[SCSI] qla4xxx: Properly handle SCSI underrun while processing status IOCBs.
authorLalit Chandivade <lalit.chandivade@qlogic.com>
Tue, 7 Aug 2012 11:57:16 +0000 (07:57 -0400)
committerJames Bottomley <JBottomley@Parallels.com>
Fri, 14 Sep 2012 16:59:21 +0000 (17:59 +0100)
The current code would incorrectly return a DID_OK for a
CHECK CONDITION with Recovered error sense key causing incorrect
completion of a command when there is a dropped frame.

Signed-off-by: Lalit Chandivade <lalit.chandivade@qlogic.com>
Signed-off-by: Tej Parkash <tej.parkash@qlogic.com>
Signed-off-by: Vikas Chaudhary <vikas.chaudhary@qlogic.com>
Reviewed-by: Mike Christie <michaelc@cs.wisc.edu>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
drivers/scsi/qla4xxx/ql4_isr.c

index fc542a9bb106231ac9f4991388b2f9ee1e3d0d46..db8c8e5fe2592540b90713117205a12ddd69acf5 100644 (file)
@@ -243,56 +243,72 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha,
 
                scsi_set_resid(cmd, residual);
 
-               /*
-                * If there is scsi_status, it takes precedense over
-                * underflow condition.
-                */
-               if (scsi_status != 0) {
-                       cmd->result = DID_OK << 16 | scsi_status;
+               if (sts_entry->iscsiFlags & ISCSI_FLAG_RESIDUAL_UNDER) {
+
+                       /* Both the firmware and target reported UNDERRUN:
+                        *
+                        * MID-LAYER UNDERFLOW case:
+                        * Some kernels do not properly detect midlayer
+                        * underflow, so we manually check it and return
+                        * ERROR if the minimum required data was not
+                        * received.
+                        *
+                        * ALL OTHER cases:
+                        * Fall thru to check scsi_status
+                        */
+                       if (!scsi_status && (scsi_bufflen(cmd) - residual) <
+                           cmd->underflow) {
+                               DEBUG2(ql4_printk(KERN_INFO, ha,
+                                                 "scsi%ld:%d:%d:%d: %s: Mid-layer Data underrun, xferlen = 0x%x,residual = 0x%x\n",
+                                                  ha->host_no,
+                                                  cmd->device->channel,
+                                                  cmd->device->id,
+                                                  cmd->device->lun, __func__,
+                                                  scsi_bufflen(cmd),
+                                                  residual));
 
-                       if (scsi_status != SCSI_CHECK_CONDITION)
+                               cmd->result = DID_ERROR << 16;
                                break;
+                       }
+
+               } else if (scsi_status != SAM_STAT_TASK_SET_FULL &&
+                          scsi_status != SAM_STAT_BUSY) {
 
-                       /* Copy Sense Data into sense buffer. */
-                       qla4xxx_copy_sense(ha, sts_entry, srb);
-               } else {
                        /*
-                        * If RISC reports underrun and target does not
-                        * report it then we must have a lost frame, so
-                        * tell upper layer to retry it by reporting a
-                        * bus busy.
+                        * The firmware reports UNDERRUN, but the target does
+                        * not report it:
+                        *
+                        *   scsi_status     |    host_byte       device_byte
+                        *                   |     (19:16)          (7:0)
+                        *   =============   |    =========       ===========
+                        *   TASK_SET_FULL   |    DID_OK          scsi_status
+                        *   BUSY            |    DID_OK          scsi_status
+                        *   ALL OTHERS      |    DID_ERROR       scsi_status
+                        *
+                        *   Note: If scsi_status is task set full or busy,
+                        *   then this else if would fall thru to check the
+                        *   scsi_status and return DID_OK.
                         */
-                       if ((sts_entry->iscsiFlags &
-                            ISCSI_FLAG_RESIDUAL_UNDER) == 0) {
-                               cmd->result = DID_BUS_BUSY << 16;
-                       } else if ((scsi_bufflen(cmd) - residual) <
-                                  cmd->underflow) {
-                               /*
-                                * Handle mid-layer underflow???
-                                *
-                                * For kernels less than 2.4, the driver must
-                                * return an error if an underflow is detected.
-                                * For kernels equal-to and above 2.4, the
-                                * mid-layer will appearantly handle the
-                                * underflow by detecting the residual count --
-                                * unfortunately, we do not see where this is
-                                * actually being done.  In the interim, we
-                                * will return DID_ERROR.
-                                */
-                               DEBUG2(printk("scsi%ld:%d:%d:%d: %s: "
-                                       "Mid-layer Data underrun1, "
-                                       "xferlen = 0x%x, "
-                                       "residual = 0x%x\n", ha->host_no,
-                                       cmd->device->channel,
-                                       cmd->device->id,
-                                       cmd->device->lun, __func__,
-                                       scsi_bufflen(cmd), residual));
 
-                               cmd->result = DID_ERROR << 16;
-                       } else {
-                               cmd->result = DID_OK << 16;
-                       }
+                       DEBUG2(ql4_printk(KERN_INFO, ha,
+                                         "scsi%ld:%d:%d:%d: %s: Dropped frame(s) detected (0x%x of 0x%x bytes).\n",
+                                         ha->host_no,
+                                         cmd->device->channel,
+                                         cmd->device->id,
+                                         cmd->device->lun, __func__,
+                                         residual,
+                                         scsi_bufflen(cmd)));
+
+                       cmd->result = DID_ERROR << 16 | scsi_status;
+                       goto check_scsi_status;
                }
+
+               cmd->result = DID_OK << 16 | scsi_status;
+
+check_scsi_status:
+               if (scsi_status == SAM_STAT_CHECK_CONDITION)
+                       qla4xxx_copy_sense(ha, sts_entry, srb);
+
                break;
 
        case SCS_DEVICE_LOGGED_OUT: