[SCSI] qla4xxx: Properly handle SCSI underrun while processing status IOCBs.

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>
This commit is contained in:
Lalit Chandivade 2012-08-07 07:57:16 -04:00 committed by James Bottomley
parent 80c53e649d
commit 24c1420094

View File

@ -243,56 +243,72 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha,
scsi_set_resid(cmd, residual); scsi_set_resid(cmd, residual);
/* if (sts_entry->iscsiFlags & ISCSI_FLAG_RESIDUAL_UNDER) {
* If there is scsi_status, it takes precedense over
* underflow condition.
*/
if (scsi_status != 0) {
cmd->result = DID_OK << 16 | scsi_status;
if (scsi_status != SCSI_CHECK_CONDITION) /* Both the firmware and target reported UNDERRUN:
break; *
* MID-LAYER UNDERFLOW case:
/* Copy Sense Data into sense buffer. */ * Some kernels do not properly detect midlayer
qla4xxx_copy_sense(ha, sts_entry, srb); * underflow, so we manually check it and return
} else { * ERROR if the minimum required data was not
/* * received.
* If RISC reports underrun and target does not *
* report it then we must have a lost frame, so * ALL OTHER cases:
* tell upper layer to retry it by reporting a * Fall thru to check scsi_status
* bus busy.
*/ */
if ((sts_entry->iscsiFlags & if (!scsi_status && (scsi_bufflen(cmd) - residual) <
ISCSI_FLAG_RESIDUAL_UNDER) == 0) { cmd->underflow) {
cmd->result = DID_BUS_BUSY << 16; DEBUG2(ql4_printk(KERN_INFO, ha,
} else if ((scsi_bufflen(cmd) - residual) < "scsi%ld:%d:%d:%d: %s: Mid-layer Data underrun, xferlen = 0x%x,residual = 0x%x\n",
cmd->underflow) { ha->host_no,
/* cmd->device->channel,
* Handle mid-layer underflow??? cmd->device->id,
* cmd->device->lun, __func__,
* For kernels less than 2.4, the driver must scsi_bufflen(cmd),
* return an error if an underflow is detected. residual));
* 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; cmd->result = DID_ERROR << 16;
} else { break;
cmd->result = DID_OK << 16;
} }
} else if (scsi_status != SAM_STAT_TASK_SET_FULL &&
scsi_status != SAM_STAT_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.
*/
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; break;
case SCS_DEVICE_LOGGED_OUT: case SCS_DEVICE_LOGGED_OUT: