[SCSI] libiscsi: fix data corruption when target has to resend data-in packets
[safe/jmp/linux-2.6] / drivers / scsi / libiscsi.c
index 8b4e412..f9539af 100644 (file)
@@ -633,6 +633,40 @@ out:
        __iscsi_put_task(task);
 }
 
+/**
+ * iscsi_data_in_rsp - SCSI Data-In Response processing
+ * @conn: iscsi connection
+ * @hdr:  iscsi pdu
+ * @task: scsi command task
+ **/
+static void
+iscsi_data_in_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+                 struct iscsi_task *task)
+{
+       struct iscsi_data_rsp *rhdr = (struct iscsi_data_rsp *)hdr;
+       struct scsi_cmnd *sc = task->sc;
+
+       if (!(rhdr->flags & ISCSI_FLAG_DATA_STATUS))
+               return;
+
+       sc->result = (DID_OK << 16) | rhdr->cmd_status;
+       conn->exp_statsn = be32_to_cpu(rhdr->statsn) + 1;
+       if (rhdr->flags & (ISCSI_FLAG_DATA_UNDERFLOW |
+                          ISCSI_FLAG_DATA_OVERFLOW)) {
+               int res_count = be32_to_cpu(rhdr->residual_count);
+
+               if (res_count > 0 &&
+                   (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW ||
+                    res_count <= scsi_in(sc)->length))
+                       scsi_in(sc)->resid = res_count;
+               else
+                       sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status;
+       }
+
+       conn->scsirsp_pdus_cnt++;
+       __iscsi_put_task(task);
+}
+
 static void iscsi_tmf_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
 {
        struct iscsi_tm_rsp *tmf = (struct iscsi_tm_rsp *)hdr;
@@ -818,12 +852,7 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
                iscsi_scsi_cmd_rsp(conn, hdr, task, data, datalen);
                break;
        case ISCSI_OP_SCSI_DATA_IN:
-               if (hdr->flags & ISCSI_FLAG_DATA_STATUS) {
-                       conn->scsirsp_pdus_cnt++;
-                       iscsi_update_cmdsn(session,
-                                          (struct iscsi_nopin*) hdr);
-                       __iscsi_put_task(task);
-               }
+               iscsi_data_in_rsp(conn, hdr, task);
                break;
        case ISCSI_OP_LOGOUT_RSP:
                iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr);
@@ -1194,15 +1223,13 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
                switch (session->state) {
                case ISCSI_STATE_IN_RECOVERY:
                        reason = FAILURE_SESSION_IN_RECOVERY;
-                       sc->result = DID_IMM_RETRY << 16;
-                       break;
+                       goto reject;
                case ISCSI_STATE_LOGGING_OUT:
                        reason = FAILURE_SESSION_LOGGING_OUT;
-                       sc->result = DID_IMM_RETRY << 16;
-                       break;
+                       goto reject;
                case ISCSI_STATE_RECOVERY_FAILED:
                        reason = FAILURE_SESSION_RECOVERY_TIMEOUT;
-                       sc->result = DID_NO_CONNECT << 16;
+                       sc->result = DID_TRANSPORT_FAILFAST << 16;
                        break;
                case ISCSI_STATE_TERMINATE:
                        reason = FAILURE_SESSION_TERMINATE;
@@ -1267,7 +1294,7 @@ reject:
        spin_unlock(&session->lock);
        debug_scsi("cmd 0x%x rejected (%d)\n", sc->cmnd[0], reason);
        spin_lock(host->host_lock);
-       return SCSI_MLQUEUE_HOST_BUSY;
+       return SCSI_MLQUEUE_TARGET_BUSY;
 
 fault:
        spin_unlock(&session->lock);
@@ -1456,7 +1483,7 @@ static void fail_all_commands(struct iscsi_conn *conn, unsigned lun,
                if (lun == task->sc->device->lun || lun == -1) {
                        debug_scsi("failing in progress sc %p itt 0x%x\n",
                                   task->sc, task->itt);
-                       fail_command(conn, task, DID_BUS_BUSY << 16);
+                       fail_command(conn, task, error << 16);
                }
        }
 }
@@ -1476,12 +1503,12 @@ static void iscsi_start_tx(struct iscsi_conn *conn)
                scsi_queue_work(conn->session->host, &conn->xmitwork);
 }
 
-static enum scsi_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd)
+static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd)
 {
        struct iscsi_cls_session *cls_session;
        struct iscsi_session *session;
        struct iscsi_conn *conn;
-       enum scsi_eh_timer_return rc = EH_NOT_HANDLED;
+       enum blk_eh_timer_return rc = BLK_EH_NOT_HANDLED;
 
        cls_session = starget_to_session(scsi_target(scmd->device));
        session = cls_session->dd_data;
@@ -1494,14 +1521,14 @@ static enum scsi_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd)
                 * We are probably in the middle of iscsi recovery so let
                 * that complete and handle the error.
                 */
-               rc = EH_RESET_TIMER;
+               rc = BLK_EH_RESET_TIMER;
                goto done;
        }
 
        conn = session->leadconn;
        if (!conn) {
                /* In the middle of shuting down */
-               rc = EH_RESET_TIMER;
+               rc = BLK_EH_RESET_TIMER;
                goto done;
        }
 
@@ -1513,20 +1540,21 @@ static enum scsi_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd)
         */
        if (time_before_eq(conn->last_recv + (conn->recv_timeout * HZ) +
                            (conn->ping_timeout * HZ), jiffies))
-               rc = EH_RESET_TIMER;
+               rc = BLK_EH_RESET_TIMER;
        /*
         * if we are about to check the transport then give the command
         * more time
         */
        if (time_before_eq(conn->last_recv + (conn->recv_timeout * HZ),
                           jiffies))
-               rc = EH_RESET_TIMER;
+               rc = BLK_EH_RESET_TIMER;
        /* if in the middle of checking the transport then give us more time */
        if (conn->ping_task)
-               rc = EH_RESET_TIMER;
+               rc = BLK_EH_RESET_TIMER;
 done:
        spin_unlock(&session->lock);
-       debug_scsi("return %s\n", rc == EH_RESET_TIMER ? "timer reset" : "nh");
+       debug_scsi("return %s\n", rc == BLK_EH_RESET_TIMER ?
+                                       "timer reset" : "nh");
        return rc;
 }
 
@@ -1857,6 +1885,9 @@ EXPORT_SYMBOL_GPL(iscsi_pool_free);
  */
 int iscsi_host_add(struct Scsi_Host *shost, struct device *pdev)
 {
+       if (!shost->can_queue)
+               shost->can_queue = ISCSI_DEF_XMIT_CMDS_MAX;
+
        return scsi_add_host(shost, pdev);
 }
 EXPORT_SYMBOL_GPL(iscsi_host_add);
@@ -1942,6 +1973,9 @@ iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost,
        struct iscsi_session *session;
        struct iscsi_cls_session *cls_session;
        int cmd_i, scsi_cmds, total_cmds = cmds_max;
+
+       if (!total_cmds)
+               total_cmds = ISCSI_DEF_XMIT_CMDS_MAX;
        /*
         * The iscsi layer needs some tasks for nop handling and tmfs,
         * so the cmds_max must at least be greater than ISCSI_MGMT_CMDS_MAX
@@ -2328,8 +2362,10 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
         * flush queues.
         */
        spin_lock_bh(&session->lock);
-       fail_all_commands(conn, -1,
-                       STOP_CONN_RECOVER ? DID_BUS_BUSY : DID_ERROR);
+       if (STOP_CONN_RECOVER)
+               fail_all_commands(conn, -1, DID_TRANSPORT_DISRUPTED);
+       else
+               fail_all_commands(conn, -1, DID_ERROR);
        flush_control_queues(session, conn);
        spin_unlock_bh(&session->lock);
        mutex_unlock(&session->eh_mutex);