]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/scsi/libfc/fc_fcp.c
Merge tag 'v2.6.38' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / drivers / scsi / libfc / fc_fcp.c
index 2924363d142bc4affc6422de1272e73a70957486..5962d1a5a674c4bedbc2bb8f7476693324d4e9ab 100644 (file)
@@ -57,6 +57,9 @@ struct kmem_cache *scsi_pkt_cachep;
 #define FC_SRB_READ            (1 << 1)
 #define FC_SRB_WRITE           (1 << 0)
 
+/* constant added to e_d_tov timeout to get rec_tov value */
+#define REC_TOV_CONST          1
+
 /*
  * The SCp.ptr should be tested and set under the scsi_pkt_queue lock
  */
@@ -96,7 +99,7 @@ static void fc_fcp_resp(struct fc_fcp_pkt *, struct fc_frame *);
 static void fc_fcp_complete_locked(struct fc_fcp_pkt *);
 static void fc_tm_done(struct fc_seq *, struct fc_frame *, void *);
 static void fc_fcp_error(struct fc_fcp_pkt *, struct fc_frame *);
-static void fc_fcp_recovery(struct fc_fcp_pkt *);
+static void fc_fcp_recovery(struct fc_fcp_pkt *, u8 code);
 static void fc_fcp_timeout(unsigned long);
 static void fc_fcp_rec(struct fc_fcp_pkt *);
 static void fc_fcp_rec_error(struct fc_fcp_pkt *, struct fc_frame *);
@@ -120,14 +123,13 @@ static void fc_fcp_srr_error(struct fc_fcp_pkt *, struct fc_frame *);
 #define FC_DATA_UNDRUN         7
 #define FC_ERROR               8
 #define FC_HRD_ERROR           9
-#define FC_CMD_RECOVERY                10
+#define FC_CRC_ERROR           10
+#define FC_TIMED_OUT           11
 
 /*
  * Error recovery timeout values.
  */
-#define FC_SCSI_ER_TIMEOUT     (10 * HZ)
 #define FC_SCSI_TM_TOV         (10 * HZ)
-#define FC_SCSI_REC_TOV                (2 * HZ)
 #define FC_HOST_RESET_TIMEOUT  (30 * HZ)
 #define FC_CAN_QUEUE_PERIOD    (60 * HZ)
 
@@ -438,6 +440,7 @@ static void fc_fcp_recv_data(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
        void *buf;
        struct scatterlist *sg;
        u32 nents;
+       u8 host_bcode = FC_COMPLETE;
 
        fh = fc_frame_header_get(fp);
        offset = ntohl(fh->fh_parm_offset);
@@ -446,13 +449,16 @@ static void fc_fcp_recv_data(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
        buf = fc_frame_payload_get(fp, 0);
 
        /*
-        * if this I/O is ddped then clear it
-        * and initiate recovery since data
-        * frames are expected to be placed
-        * directly in that case.
+        * if this I/O is ddped then clear it and initiate recovery since data
+        * frames are expected to be placed directly in that case.
+        *
+        * Indicate error to scsi-ml because something went wrong with the
+        * ddp handling to get us here.
         */
        if (fsp->xfer_ddp != FC_XID_UNKNOWN) {
                fc_fcp_ddp_done(fsp);
+               FC_FCP_DBG(fsp, "DDP I/O in fc_fcp_recv_data set ERROR\n");
+               host_bcode = FC_ERROR;
                goto err;
        }
        if (offset + len > fsp->data_len) {
@@ -462,6 +468,9 @@ static void fc_fcp_recv_data(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
                        goto crc_err;
                FC_FCP_DBG(fsp, "data received past end. len %zx offset %zx "
                           "data_len %x\n", len, offset, fsp->data_len);
+
+               /* Data is corrupted indicate scsi-ml should retry */
+               host_bcode = FC_DATA_OVRRUN;
                goto err;
        }
        if (offset != fsp->xfer_len)
@@ -498,8 +507,10 @@ crc_err:
                         * If so, we need to retry the entire operation.
                         * Otherwise, ignore it.
                         */
-                       if (fsp->state & FC_SRB_DISCONTIG)
+                       if (fsp->state & FC_SRB_DISCONTIG) {
+                               host_bcode = FC_CRC_ERROR;
                                goto err;
+                       }
                        return;
                }
        }
@@ -517,7 +528,7 @@ crc_err:
                fc_fcp_complete_locked(fsp);
        return;
 err:
-       fc_fcp_recovery(fsp);
+       fc_fcp_recovery(fsp, host_bcode);
 }
 
 /**
@@ -962,7 +973,13 @@ static void fc_fcp_complete_locked(struct fc_fcp_pkt *fsp)
                }
                lport->tt.exch_done(seq);
        }
-       fc_io_compl(fsp);
+       /*
+        * Some resets driven by SCSI are not I/Os and do not have
+        * SCSI commands associated with the requests. We should not
+        * call I/O completion if we do not have a SCSI command.
+        */
+       if (fsp->cmd)
+               fc_io_compl(fsp);
 }
 
 /**
@@ -1072,6 +1089,21 @@ static int fc_fcp_pkt_send(struct fc_lport *lport, struct fc_fcp_pkt *fsp)
        return rc;
 }
 
+/**
+ * get_fsp_rec_tov() - Helper function to get REC_TOV
+ * @fsp: the FCP packet
+ */
+static inline unsigned int get_fsp_rec_tov(struct fc_fcp_pkt *fsp)
+{
+       struct fc_rport *rport;
+       struct fc_rport_libfc_priv *rpriv;
+
+       rport = fsp->rport;
+       rpriv = rport->dd_data;
+
+       return rpriv->e_d_tov + REC_TOV_CONST;
+}
+
 /**
  * fc_fcp_cmd_send() - Send a FCP command
  * @lport: The local port to send the command on
@@ -1089,6 +1121,7 @@ static int fc_fcp_cmd_send(struct fc_lport *lport, struct fc_fcp_pkt *fsp,
        struct fc_rport_libfc_priv *rpriv;
        const size_t len = sizeof(fsp->cdb_cmd);
        int rc = 0;
+       unsigned int rec_tov;
 
        if (fc_fcp_lock_pkt(fsp))
                return 0;
@@ -1119,10 +1152,13 @@ static int fc_fcp_cmd_send(struct fc_lport *lport, struct fc_fcp_pkt *fsp,
        fsp->seq_ptr = seq;
        fc_fcp_pkt_hold(fsp);   /* hold for fc_fcp_pkt_destroy */
 
+       rec_tov = get_fsp_rec_tov(fsp);
+
        setup_timer(&fsp->timer, fc_fcp_timeout, (unsigned long)fsp);
-       fc_fcp_timer_set(fsp,
-                        (fsp->tgt_flags & FC_RP_FLAGS_REC_SUPPORTED) ?
-                        FC_SCSI_REC_TOV : FC_SCSI_ER_TIMEOUT);
+
+       if (rpriv->flags & FC_RP_FLAGS_REC_SUPPORTED)
+               fc_fcp_timer_set(fsp, rec_tov);
+
 unlock:
        fc_fcp_unlock_pkt(fsp);
        return rc;
@@ -1197,13 +1233,16 @@ static void fc_lun_reset_send(unsigned long data)
 {
        struct fc_fcp_pkt *fsp = (struct fc_fcp_pkt *)data;
        struct fc_lport *lport = fsp->lp;
+       unsigned int rec_tov;
+
        if (lport->tt.fcp_cmd_send(lport, fsp, fc_tm_done)) {
                if (fsp->recov_retry++ >= FC_MAX_RECOV_RETRY)
                        return;
                if (fc_fcp_lock_pkt(fsp))
                        return;
+               rec_tov = get_fsp_rec_tov(fsp);
                setup_timer(&fsp->timer, fc_lun_reset_send, (unsigned long)fsp);
-               fc_fcp_timer_set(fsp, FC_SCSI_REC_TOV);
+               fc_fcp_timer_set(fsp, rec_tov);
                fc_fcp_unlock_pkt(fsp);
        }
 }
@@ -1211,7 +1250,7 @@ static void fc_lun_reset_send(unsigned long data)
 /**
  * fc_lun_reset() - Send a LUN RESET command to a device
  *                 and wait for the reply
- * @lport: The local port to sent the comand on
+ * @lport: The local port to sent the command on
  * @fsp:   The FCP packet that identifies the LUN to be reset
  * @id:           The SCSI command ID
  * @lun:   The LUN ID to be reset
@@ -1282,27 +1321,27 @@ static void fc_tm_done(struct fc_seq *seq, struct fc_frame *fp, void *arg)
                 *
                 * scsi-eh will escalate for when either happens.
                 */
-               return;
+               goto out;
        }
 
        if (fc_fcp_lock_pkt(fsp))
-               return;
+               goto out;
 
        /*
         * raced with eh timeout handler.
         */
-       if (!fsp->seq_ptr || !fsp->wait_for_comp) {
-               spin_unlock_bh(&fsp->scsi_pkt_lock);
-               return;
-       }
+       if (!fsp->seq_ptr || !fsp->wait_for_comp)
+               goto out_unlock;
 
        fh = fc_frame_header_get(fp);
        if (fh->fh_type != FC_TYPE_BLS)
                fc_fcp_resp(fsp, fp);
        fsp->seq_ptr = NULL;
        fsp->lp->tt.exch_done(seq);
-       fc_frame_free(fp);
+out_unlock:
        fc_fcp_unlock_pkt(fsp);
+out:
+       fc_frame_free(fp);
 }
 
 /**
@@ -1341,13 +1380,10 @@ static void fc_fcp_timeout(unsigned long data)
 
        if (rpriv->flags & FC_RP_FLAGS_REC_SUPPORTED)
                fc_fcp_rec(fsp);
-       else if (time_after_eq(fsp->last_pkt_time + (FC_SCSI_ER_TIMEOUT / 2),
-                              jiffies))
-               fc_fcp_timer_set(fsp, FC_SCSI_ER_TIMEOUT);
        else if (fsp->state & FC_SRB_RCV_STATUS)
                fc_fcp_complete_locked(fsp);
        else
-               fc_fcp_recovery(fsp);
+               fc_fcp_recovery(fsp, FC_TIMED_OUT);
        fsp->state &= ~FC_SRB_FCP_PROCESSING_TMO;
 unlock:
        fc_fcp_unlock_pkt(fsp);
@@ -1373,6 +1409,7 @@ static void fc_fcp_rec(struct fc_fcp_pkt *fsp)
                fc_fcp_complete_locked(fsp);
                return;
        }
+
        fp = fc_fcp_frame_alloc(lport, sizeof(struct fc_els_rec));
        if (!fp)
                goto retry;
@@ -1383,15 +1420,15 @@ static void fc_fcp_rec(struct fc_fcp_pkt *fsp)
                       FC_FCTL_REQ, 0);
        if (lport->tt.elsct_send(lport, rport->port_id, fp, ELS_REC,
                                 fc_fcp_rec_resp, fsp,
-                                jiffies_to_msecs(FC_SCSI_REC_TOV))) {
+                                2 * lport->r_a_tov)) {
                fc_fcp_pkt_hold(fsp);           /* hold while REC outstanding */
                return;
        }
 retry:
        if (fsp->recov_retry++ < FC_MAX_RECOV_RETRY)
-               fc_fcp_timer_set(fsp, FC_SCSI_REC_TOV);
+               fc_fcp_timer_set(fsp, get_fsp_rec_tov(fsp));
        else
-               fc_fcp_recovery(fsp);
+               fc_fcp_recovery(fsp, FC_TIMED_OUT);
 }
 
 /**
@@ -1445,7 +1482,6 @@ static void fc_fcp_rec_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
                         * making progress.
                         */
                        rpriv->flags &= ~FC_RP_FLAGS_REC_SUPPORTED;
-                       fc_fcp_timer_set(fsp, FC_SCSI_ER_TIMEOUT);
                        break;
                case ELS_RJT_LOGIC:
                case ELS_RJT_UNAB:
@@ -1460,7 +1496,7 @@ static void fc_fcp_rec_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
                                fc_fcp_retry_cmd(fsp);
                                break;
                        }
-                       fc_fcp_recovery(fsp);
+                       fc_fcp_recovery(fsp, FC_ERROR);
                        break;
                }
        } else if (opcode == ELS_LS_ACC) {
@@ -1498,12 +1534,12 @@ static void fc_fcp_rec_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
                        }
                        fc_fcp_srr(fsp, r_ctl, offset);
                } else if (e_stat & ESB_ST_SEQ_INIT) {
-
+                       unsigned int rec_tov = get_fsp_rec_tov(fsp);
                        /*
                         * The remote port has the initiative, so just
                         * keep waiting for it to complete.
                         */
-                       fc_fcp_timer_set(fsp, FC_SCSI_REC_TOV);
+                       fc_fcp_timer_set(fsp, rec_tov);
                } else {
 
                        /*
@@ -1575,7 +1611,7 @@ static void fc_fcp_rec_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
                if (fsp->recov_retry++ < FC_MAX_RECOV_RETRY)
                        fc_fcp_rec(fsp);
                else
-                       fc_fcp_recovery(fsp);
+                       fc_fcp_recovery(fsp, FC_ERROR);
                break;
        }
        fc_fcp_unlock_pkt(fsp);
@@ -1587,9 +1623,9 @@ out:
  * fc_fcp_recovery() - Handler for fcp_pkt recovery
  * @fsp: The FCP pkt that needs to be aborted
  */
-static void fc_fcp_recovery(struct fc_fcp_pkt *fsp)
+static void fc_fcp_recovery(struct fc_fcp_pkt *fsp, u8 code)
 {
-       fsp->status_code = FC_CMD_RECOVERY;
+       fsp->status_code = code;
        fsp->cdb_status = 0;
        fsp->io_status = 0;
        /*
@@ -1616,6 +1652,7 @@ static void fc_fcp_srr(struct fc_fcp_pkt *fsp, enum fc_rctl r_ctl, u32 offset)
        struct fcp_srr *srr;
        struct fc_frame *fp;
        u8 cdb_op;
+       unsigned int rec_tov;
 
        rport = fsp->rport;
        rpriv = rport->dd_data;
@@ -1640,8 +1677,9 @@ static void fc_fcp_srr(struct fc_fcp_pkt *fsp, enum fc_rctl r_ctl, u32 offset)
                       rpriv->local_port->port_id, FC_TYPE_FCP,
                       FC_FCTL_REQ, 0);
 
+       rec_tov = get_fsp_rec_tov(fsp);
        seq = lport->tt.exch_seq_send(lport, fp, fc_fcp_srr_resp, NULL,
-                                     fsp, jiffies_to_msecs(FC_SCSI_REC_TOV));
+                                     fsp, jiffies_to_msecs(rec_tov));
        if (!seq)
                goto retry;
 
@@ -1665,6 +1703,7 @@ static void fc_fcp_srr_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
 {
        struct fc_fcp_pkt *fsp = arg;
        struct fc_frame_header *fh;
+       unsigned int rec_tov;
 
        if (IS_ERR(fp)) {
                fc_fcp_srr_error(fsp, fp);
@@ -1691,11 +1730,12 @@ static void fc_fcp_srr_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
        switch (fc_frame_payload_op(fp)) {
        case ELS_LS_ACC:
                fsp->recov_retry = 0;
-               fc_fcp_timer_set(fsp, FC_SCSI_REC_TOV);
+               rec_tov = get_fsp_rec_tov(fsp);
+               fc_fcp_timer_set(fsp, rec_tov);
                break;
        case ELS_LS_RJT:
        default:
-               fc_fcp_recovery(fsp);
+               fc_fcp_recovery(fsp, FC_ERROR);
                break;
        }
        fc_fcp_unlock_pkt(fsp);
@@ -1721,7 +1761,7 @@ static void fc_fcp_srr_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
                if (fsp->recov_retry++ < FC_MAX_RECOV_RETRY)
                        fc_fcp_rec(fsp);
                else
-                       fc_fcp_recovery(fsp);
+                       fc_fcp_recovery(fsp, FC_TIMED_OUT);
                break;
        case -FC_EX_CLOSED:                     /* e.g., link failure */
                /* fall through */
@@ -1820,19 +1860,17 @@ static int fc_queuecommand_lck(struct scsi_cmnd *sc_cmd, void (*done)(struct scs
        if (sc_cmd->sc_data_direction == DMA_FROM_DEVICE) {
                fsp->req_flags = FC_SRB_READ;
                stats->InputRequests++;
-               stats->InputMegabytes = fsp->data_len;
+               stats->InputBytes += fsp->data_len;
        } else if (sc_cmd->sc_data_direction == DMA_TO_DEVICE) {
                fsp->req_flags = FC_SRB_WRITE;
                stats->OutputRequests++;
-               stats->OutputMegabytes = fsp->data_len;
+               stats->OutputBytes += fsp->data_len;
        } else {
                fsp->req_flags = 0;
                stats->ControlRequests++;
        }
        put_cpu();
 
-       fsp->tgt_flags = rpriv->flags;
-
        init_timer(&fsp->timer);
        fsp->timer.data = (unsigned long)fsp;
 
@@ -1946,18 +1984,29 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp)
                break;
        case FC_CMD_ABORTED:
                FC_FCP_DBG(fsp, "Returning DID_ERROR to scsi-ml "
-                          "due to FC_CMD_ABORTED\n");
+                         "due to FC_CMD_ABORTED\n");
                sc_cmd->result = (DID_ERROR << 16) | fsp->io_status;
                break;
-       case FC_CMD_RECOVERY:
-               sc_cmd->result = (DID_BUS_BUSY << 16) | fsp->io_status;
-               break;
        case FC_CMD_RESET:
+               FC_FCP_DBG(fsp, "Returning DID_RESET to scsi-ml "
+                          "due to FC_CMD_RESET\n");
                sc_cmd->result = (DID_RESET << 16);
                break;
        case FC_HRD_ERROR:
+               FC_FCP_DBG(fsp, "Returning DID_NO_CONNECT to scsi-ml "
+                          "due to FC_HRD_ERROR\n");
                sc_cmd->result = (DID_NO_CONNECT << 16);
                break;
+       case FC_CRC_ERROR:
+               FC_FCP_DBG(fsp, "Returning DID_PARITY to scsi-ml "
+                          "due to FC_CRC_ERROR\n");
+               sc_cmd->result = (DID_PARITY << 16);
+               break;
+       case FC_TIMED_OUT:
+               FC_FCP_DBG(fsp, "Returning DID_BUS_BUSY to scsi-ml "
+                          "due to FC_TIMED_OUT\n");
+               sc_cmd->result = (DID_BUS_BUSY << 16) | fsp->io_status;
+               break;
        default:
                FC_FCP_DBG(fsp, "Returning DID_ERROR to scsi-ml "
                           "due to unknown error\n");
@@ -2004,7 +2053,7 @@ int fc_eh_abort(struct scsi_cmnd *sc_cmd)
        fsp = CMD_SP(sc_cmd);
        if (!fsp) {
                /* command completed while scsi eh was setting up */
-               spin_unlock_irqrestore(lport->host->host_lock, flags);
+               spin_unlock_irqrestore(&si->scsi_queue_lock, flags);
                return SUCCESS;
        }
        /* grab a ref so the fsp and sc_cmd cannot be relased from under us */