|
@@ -3,14 +3,14 @@
|
|
*
|
|
*
|
|
* SCSI error/timeout handling
|
|
* SCSI error/timeout handling
|
|
* Initial versions: Eric Youngdale. Based upon conversations with
|
|
* Initial versions: Eric Youngdale. Based upon conversations with
|
|
- * Leonard Zubkoff and David Miller at Linux Expo,
|
|
|
|
|
|
+ * Leonard Zubkoff and David Miller at Linux Expo,
|
|
* ideas originating from all over the place.
|
|
* ideas originating from all over the place.
|
|
*
|
|
*
|
|
* Restructured scsi_unjam_host and associated functions.
|
|
* Restructured scsi_unjam_host and associated functions.
|
|
* September 04, 2002 Mike Anderson (andmike@us.ibm.com)
|
|
* September 04, 2002 Mike Anderson (andmike@us.ibm.com)
|
|
*
|
|
*
|
|
* Forward port of Russell King's (rmk@arm.linux.org.uk) changes and
|
|
* Forward port of Russell King's (rmk@arm.linux.org.uk) changes and
|
|
- * minor cleanups.
|
|
|
|
|
|
+ * minor cleanups.
|
|
* September 30, 2002 Mike Anderson (andmike@us.ibm.com)
|
|
* September 30, 2002 Mike Anderson (andmike@us.ibm.com)
|
|
*/
|
|
*/
|
|
|
|
|
|
@@ -129,14 +129,15 @@ enum blk_eh_timer_return scsi_times_out(struct request *req)
|
|
{
|
|
{
|
|
struct scsi_cmnd *scmd = req->special;
|
|
struct scsi_cmnd *scmd = req->special;
|
|
enum blk_eh_timer_return rtn = BLK_EH_NOT_HANDLED;
|
|
enum blk_eh_timer_return rtn = BLK_EH_NOT_HANDLED;
|
|
|
|
+ struct Scsi_Host *host = scmd->device->host;
|
|
|
|
|
|
trace_scsi_dispatch_cmd_timeout(scmd);
|
|
trace_scsi_dispatch_cmd_timeout(scmd);
|
|
scsi_log_completion(scmd, TIMEOUT_ERROR);
|
|
scsi_log_completion(scmd, TIMEOUT_ERROR);
|
|
|
|
|
|
- if (scmd->device->host->transportt->eh_timed_out)
|
|
|
|
- rtn = scmd->device->host->transportt->eh_timed_out(scmd);
|
|
|
|
- else if (scmd->device->host->hostt->eh_timed_out)
|
|
|
|
- rtn = scmd->device->host->hostt->eh_timed_out(scmd);
|
|
|
|
|
|
+ if (host->transportt->eh_timed_out)
|
|
|
|
+ rtn = host->transportt->eh_timed_out(scmd);
|
|
|
|
+ else if (host->hostt->eh_timed_out)
|
|
|
|
+ rtn = host->hostt->eh_timed_out(scmd);
|
|
|
|
|
|
if (unlikely(rtn == BLK_EH_NOT_HANDLED &&
|
|
if (unlikely(rtn == BLK_EH_NOT_HANDLED &&
|
|
!scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD))) {
|
|
!scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD))) {
|
|
@@ -195,7 +196,7 @@ static inline void scsi_eh_prt_fail_stats(struct Scsi_Host *shost,
|
|
++total_failures;
|
|
++total_failures;
|
|
if (scmd->eh_eflags & SCSI_EH_CANCEL_CMD)
|
|
if (scmd->eh_eflags & SCSI_EH_CANCEL_CMD)
|
|
++cmd_cancel;
|
|
++cmd_cancel;
|
|
- else
|
|
|
|
|
|
+ else
|
|
++cmd_failed;
|
|
++cmd_failed;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -214,7 +215,7 @@ static inline void scsi_eh_prt_fail_stats(struct Scsi_Host *shost,
|
|
|
|
|
|
SCSI_LOG_ERROR_RECOVERY(2, printk("Total of %d commands on %d"
|
|
SCSI_LOG_ERROR_RECOVERY(2, printk("Total of %d commands on %d"
|
|
" devices require eh work\n",
|
|
" devices require eh work\n",
|
|
- total_failures, devices_failed));
|
|
|
|
|
|
+ total_failures, devices_failed));
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
|
|
@@ -294,7 +295,7 @@ static int scsi_check_sense(struct scsi_cmnd *scmd)
|
|
return NEEDS_RETRY;
|
|
return NEEDS_RETRY;
|
|
}
|
|
}
|
|
/*
|
|
/*
|
|
- * if the device is in the process of becoming ready, we
|
|
|
|
|
|
+ * if the device is in the process of becoming ready, we
|
|
* should retry.
|
|
* should retry.
|
|
*/
|
|
*/
|
|
if ((sshdr.asc == 0x04) && (sshdr.ascq == 0x01))
|
|
if ((sshdr.asc == 0x04) && (sshdr.ascq == 0x01))
|
|
@@ -488,7 +489,7 @@ static int scsi_eh_completed_normally(struct scsi_cmnd *scmd)
|
|
*/
|
|
*/
|
|
static void scsi_eh_done(struct scsi_cmnd *scmd)
|
|
static void scsi_eh_done(struct scsi_cmnd *scmd)
|
|
{
|
|
{
|
|
- struct completion *eh_action;
|
|
|
|
|
|
+ struct completion *eh_action;
|
|
|
|
|
|
SCSI_LOG_ERROR_RECOVERY(3,
|
|
SCSI_LOG_ERROR_RECOVERY(3,
|
|
printk("%s scmd: %p result: %x\n",
|
|
printk("%s scmd: %p result: %x\n",
|
|
@@ -507,22 +508,23 @@ static int scsi_try_host_reset(struct scsi_cmnd *scmd)
|
|
{
|
|
{
|
|
unsigned long flags;
|
|
unsigned long flags;
|
|
int rtn;
|
|
int rtn;
|
|
|
|
+ struct Scsi_Host *host = scmd->device->host;
|
|
|
|
+ struct scsi_host_template *hostt = host->hostt;
|
|
|
|
|
|
SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Host RST\n",
|
|
SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Host RST\n",
|
|
__func__));
|
|
__func__));
|
|
|
|
|
|
- if (!scmd->device->host->hostt->eh_host_reset_handler)
|
|
|
|
|
|
+ if (!hostt->eh_host_reset_handler)
|
|
return FAILED;
|
|
return FAILED;
|
|
|
|
|
|
- rtn = scmd->device->host->hostt->eh_host_reset_handler(scmd);
|
|
|
|
|
|
+ rtn = hostt->eh_host_reset_handler(scmd);
|
|
|
|
|
|
if (rtn == SUCCESS) {
|
|
if (rtn == SUCCESS) {
|
|
- if (!scmd->device->host->hostt->skip_settle_delay)
|
|
|
|
|
|
+ if (!hostt->skip_settle_delay)
|
|
ssleep(HOST_RESET_SETTLE_TIME);
|
|
ssleep(HOST_RESET_SETTLE_TIME);
|
|
- spin_lock_irqsave(scmd->device->host->host_lock, flags);
|
|
|
|
- scsi_report_bus_reset(scmd->device->host,
|
|
|
|
- scmd_channel(scmd));
|
|
|
|
- spin_unlock_irqrestore(scmd->device->host->host_lock, flags);
|
|
|
|
|
|
+ spin_lock_irqsave(host->host_lock, flags);
|
|
|
|
+ scsi_report_bus_reset(host, scmd_channel(scmd));
|
|
|
|
+ spin_unlock_irqrestore(host->host_lock, flags);
|
|
}
|
|
}
|
|
|
|
|
|
return rtn;
|
|
return rtn;
|
|
@@ -536,22 +538,23 @@ static int scsi_try_bus_reset(struct scsi_cmnd *scmd)
|
|
{
|
|
{
|
|
unsigned long flags;
|
|
unsigned long flags;
|
|
int rtn;
|
|
int rtn;
|
|
|
|
+ struct Scsi_Host *host = scmd->device->host;
|
|
|
|
+ struct scsi_host_template *hostt = host->hostt;
|
|
|
|
|
|
SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Bus RST\n",
|
|
SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Bus RST\n",
|
|
__func__));
|
|
__func__));
|
|
|
|
|
|
- if (!scmd->device->host->hostt->eh_bus_reset_handler)
|
|
|
|
|
|
+ if (!hostt->eh_bus_reset_handler)
|
|
return FAILED;
|
|
return FAILED;
|
|
|
|
|
|
- rtn = scmd->device->host->hostt->eh_bus_reset_handler(scmd);
|
|
|
|
|
|
+ rtn = hostt->eh_bus_reset_handler(scmd);
|
|
|
|
|
|
if (rtn == SUCCESS) {
|
|
if (rtn == SUCCESS) {
|
|
- if (!scmd->device->host->hostt->skip_settle_delay)
|
|
|
|
|
|
+ if (!hostt->skip_settle_delay)
|
|
ssleep(BUS_RESET_SETTLE_TIME);
|
|
ssleep(BUS_RESET_SETTLE_TIME);
|
|
- spin_lock_irqsave(scmd->device->host->host_lock, flags);
|
|
|
|
- scsi_report_bus_reset(scmd->device->host,
|
|
|
|
- scmd_channel(scmd));
|
|
|
|
- spin_unlock_irqrestore(scmd->device->host->host_lock, flags);
|
|
|
|
|
|
+ spin_lock_irqsave(host->host_lock, flags);
|
|
|
|
+ scsi_report_bus_reset(host, scmd_channel(scmd));
|
|
|
|
+ spin_unlock_irqrestore(host->host_lock, flags);
|
|
}
|
|
}
|
|
|
|
|
|
return rtn;
|
|
return rtn;
|
|
@@ -577,16 +580,18 @@ static int scsi_try_target_reset(struct scsi_cmnd *scmd)
|
|
{
|
|
{
|
|
unsigned long flags;
|
|
unsigned long flags;
|
|
int rtn;
|
|
int rtn;
|
|
|
|
+ struct Scsi_Host *host = scmd->device->host;
|
|
|
|
+ struct scsi_host_template *hostt = host->hostt;
|
|
|
|
|
|
- if (!scmd->device->host->hostt->eh_target_reset_handler)
|
|
|
|
|
|
+ if (!hostt->eh_target_reset_handler)
|
|
return FAILED;
|
|
return FAILED;
|
|
|
|
|
|
- rtn = scmd->device->host->hostt->eh_target_reset_handler(scmd);
|
|
|
|
|
|
+ rtn = hostt->eh_target_reset_handler(scmd);
|
|
if (rtn == SUCCESS) {
|
|
if (rtn == SUCCESS) {
|
|
- spin_lock_irqsave(scmd->device->host->host_lock, flags);
|
|
|
|
|
|
+ spin_lock_irqsave(host->host_lock, flags);
|
|
__starget_for_each_device(scsi_target(scmd->device), NULL,
|
|
__starget_for_each_device(scsi_target(scmd->device), NULL,
|
|
__scsi_report_device_reset);
|
|
__scsi_report_device_reset);
|
|
- spin_unlock_irqrestore(scmd->device->host->host_lock, flags);
|
|
|
|
|
|
+ spin_unlock_irqrestore(host->host_lock, flags);
|
|
}
|
|
}
|
|
|
|
|
|
return rtn;
|
|
return rtn;
|
|
@@ -605,27 +610,28 @@ static int scsi_try_target_reset(struct scsi_cmnd *scmd)
|
|
static int scsi_try_bus_device_reset(struct scsi_cmnd *scmd)
|
|
static int scsi_try_bus_device_reset(struct scsi_cmnd *scmd)
|
|
{
|
|
{
|
|
int rtn;
|
|
int rtn;
|
|
|
|
+ struct scsi_host_template *hostt = scmd->device->host->hostt;
|
|
|
|
|
|
- if (!scmd->device->host->hostt->eh_device_reset_handler)
|
|
|
|
|
|
+ if (!hostt->eh_device_reset_handler)
|
|
return FAILED;
|
|
return FAILED;
|
|
|
|
|
|
- rtn = scmd->device->host->hostt->eh_device_reset_handler(scmd);
|
|
|
|
|
|
+ rtn = hostt->eh_device_reset_handler(scmd);
|
|
if (rtn == SUCCESS)
|
|
if (rtn == SUCCESS)
|
|
__scsi_report_device_reset(scmd->device, NULL);
|
|
__scsi_report_device_reset(scmd->device, NULL);
|
|
return rtn;
|
|
return rtn;
|
|
}
|
|
}
|
|
|
|
|
|
-static int scsi_try_to_abort_cmd(struct scsi_cmnd *scmd)
|
|
|
|
|
|
+static int scsi_try_to_abort_cmd(struct scsi_host_template *hostt, struct scsi_cmnd *scmd)
|
|
{
|
|
{
|
|
- if (!scmd->device->host->hostt->eh_abort_handler)
|
|
|
|
|
|
+ if (!hostt->eh_abort_handler)
|
|
return FAILED;
|
|
return FAILED;
|
|
|
|
|
|
- return scmd->device->host->hostt->eh_abort_handler(scmd);
|
|
|
|
|
|
+ return hostt->eh_abort_handler(scmd);
|
|
}
|
|
}
|
|
|
|
|
|
static void scsi_abort_eh_cmnd(struct scsi_cmnd *scmd)
|
|
static void scsi_abort_eh_cmnd(struct scsi_cmnd *scmd)
|
|
{
|
|
{
|
|
- if (scsi_try_to_abort_cmd(scmd) != SUCCESS)
|
|
|
|
|
|
+ if (scsi_try_to_abort_cmd(scmd->device->host->hostt, scmd) != SUCCESS)
|
|
if (scsi_try_bus_device_reset(scmd) != SUCCESS)
|
|
if (scsi_try_bus_device_reset(scmd) != SUCCESS)
|
|
if (scsi_try_target_reset(scmd) != SUCCESS)
|
|
if (scsi_try_target_reset(scmd) != SUCCESS)
|
|
if (scsi_try_bus_reset(scmd) != SUCCESS)
|
|
if (scsi_try_bus_reset(scmd) != SUCCESS)
|
|
@@ -846,7 +852,7 @@ EXPORT_SYMBOL(scsi_eh_finish_cmd);
|
|
*
|
|
*
|
|
* Description:
|
|
* Description:
|
|
* See if we need to request sense information. if so, then get it
|
|
* See if we need to request sense information. if so, then get it
|
|
- * now, so we have a better idea of what to do.
|
|
|
|
|
|
+ * now, so we have a better idea of what to do.
|
|
*
|
|
*
|
|
* Notes:
|
|
* Notes:
|
|
* This has the unfortunate side effect that if a shost adapter does
|
|
* This has the unfortunate side effect that if a shost adapter does
|
|
@@ -958,7 +964,7 @@ static int scsi_eh_abort_cmds(struct list_head *work_q,
|
|
SCSI_LOG_ERROR_RECOVERY(3, printk("%s: aborting cmd:"
|
|
SCSI_LOG_ERROR_RECOVERY(3, printk("%s: aborting cmd:"
|
|
"0x%p\n", current->comm,
|
|
"0x%p\n", current->comm,
|
|
scmd));
|
|
scmd));
|
|
- rtn = scsi_try_to_abort_cmd(scmd);
|
|
|
|
|
|
+ rtn = scsi_try_to_abort_cmd(scmd->device->host->hostt, scmd);
|
|
if (rtn == SUCCESS || rtn == FAST_IO_FAIL) {
|
|
if (rtn == SUCCESS || rtn == FAST_IO_FAIL) {
|
|
scmd->eh_eflags &= ~SCSI_EH_CANCEL_CMD;
|
|
scmd->eh_eflags &= ~SCSI_EH_CANCEL_CMD;
|
|
if (!scsi_device_online(scmd->device) ||
|
|
if (!scsi_device_online(scmd->device) ||
|
|
@@ -966,7 +972,6 @@ static int scsi_eh_abort_cmds(struct list_head *work_q,
|
|
!scsi_eh_tur(scmd)) {
|
|
!scsi_eh_tur(scmd)) {
|
|
scsi_eh_finish_cmd(scmd, done_q);
|
|
scsi_eh_finish_cmd(scmd, done_q);
|
|
}
|
|
}
|
|
-
|
|
|
|
} else
|
|
} else
|
|
SCSI_LOG_ERROR_RECOVERY(3, printk("%s: aborting"
|
|
SCSI_LOG_ERROR_RECOVERY(3, printk("%s: aborting"
|
|
" cmd failed:"
|
|
" cmd failed:"
|
|
@@ -1010,7 +1015,7 @@ static int scsi_eh_try_stu(struct scsi_cmnd *scmd)
|
|
*
|
|
*
|
|
* Notes:
|
|
* Notes:
|
|
* If commands are failing due to not ready, initializing command required,
|
|
* If commands are failing due to not ready, initializing command required,
|
|
- * try revalidating the device, which will end up sending a start unit.
|
|
|
|
|
|
+ * try revalidating the device, which will end up sending a start unit.
|
|
*/
|
|
*/
|
|
static int scsi_eh_stu(struct Scsi_Host *shost,
|
|
static int scsi_eh_stu(struct Scsi_Host *shost,
|
|
struct list_head *work_q,
|
|
struct list_head *work_q,
|
|
@@ -1064,7 +1069,7 @@ static int scsi_eh_stu(struct Scsi_Host *shost,
|
|
* Try a bus device reset. Still, look to see whether we have multiple
|
|
* Try a bus device reset. Still, look to see whether we have multiple
|
|
* devices that are jammed or not - if we have multiple devices, it
|
|
* devices that are jammed or not - if we have multiple devices, it
|
|
* makes no sense to try bus_device_reset - we really would need to try
|
|
* makes no sense to try bus_device_reset - we really would need to try
|
|
- * a bus_reset instead.
|
|
|
|
|
|
+ * a bus_reset instead.
|
|
*/
|
|
*/
|
|
static int scsi_eh_bus_device_reset(struct Scsi_Host *shost,
|
|
static int scsi_eh_bus_device_reset(struct Scsi_Host *shost,
|
|
struct list_head *work_q,
|
|
struct list_head *work_q,
|
|
@@ -1164,7 +1169,7 @@ static int scsi_eh_target_reset(struct Scsi_Host *shost,
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * scsi_eh_bus_reset - send a bus reset
|
|
|
|
|
|
+ * scsi_eh_bus_reset - send a bus reset
|
|
* @shost: &scsi host being recovered.
|
|
* @shost: &scsi host being recovered.
|
|
* @work_q: &list_head for pending commands.
|
|
* @work_q: &list_head for pending commands.
|
|
* @done_q: &list_head for processed commands.
|
|
* @done_q: &list_head for processed commands.
|
|
@@ -1181,7 +1186,7 @@ static int scsi_eh_bus_reset(struct Scsi_Host *shost,
|
|
* we really want to loop over the various channels, and do this on
|
|
* we really want to loop over the various channels, and do this on
|
|
* a channel by channel basis. we should also check to see if any
|
|
* a channel by channel basis. we should also check to see if any
|
|
* of the failed commands are on soft_reset devices, and if so, skip
|
|
* of the failed commands are on soft_reset devices, and if so, skip
|
|
- * the reset.
|
|
|
|
|
|
+ * the reset.
|
|
*/
|
|
*/
|
|
|
|
|
|
for (channel = 0; channel <= shost->max_channel; channel++) {
|
|
for (channel = 0; channel <= shost->max_channel; channel++) {
|
|
@@ -1223,7 +1228,7 @@ static int scsi_eh_bus_reset(struct Scsi_Host *shost,
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * scsi_eh_host_reset - send a host reset
|
|
|
|
|
|
+ * scsi_eh_host_reset - send a host reset
|
|
* @work_q: list_head for processed commands.
|
|
* @work_q: list_head for processed commands.
|
|
* @done_q: list_head for processed commands.
|
|
* @done_q: list_head for processed commands.
|
|
*/
|
|
*/
|
|
@@ -1376,7 +1381,7 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd)
|
|
return SUCCESS;
|
|
return SUCCESS;
|
|
/*
|
|
/*
|
|
* when the low level driver returns did_soft_error,
|
|
* when the low level driver returns did_soft_error,
|
|
- * it is responsible for keeping an internal retry counter
|
|
|
|
|
|
+ * it is responsible for keeping an internal retry counter
|
|
* in order to avoid endless loops (db)
|
|
* in order to avoid endless loops (db)
|
|
*
|
|
*
|
|
* actually this is a bug in this function here. we should
|
|
* actually this is a bug in this function here. we should
|
|
@@ -1414,7 +1419,6 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd)
|
|
*/
|
|
*/
|
|
break;
|
|
break;
|
|
/* fallthrough */
|
|
/* fallthrough */
|
|
-
|
|
|
|
case DID_BUS_BUSY:
|
|
case DID_BUS_BUSY:
|
|
case DID_PARITY:
|
|
case DID_PARITY:
|
|
goto maybe_retry;
|
|
goto maybe_retry;
|
|
@@ -1982,7 +1986,7 @@ int scsi_normalize_sense(const u8 *sense_buffer, int sb_len,
|
|
if (sb_len > 7)
|
|
if (sb_len > 7)
|
|
sshdr->additional_length = sense_buffer[7];
|
|
sshdr->additional_length = sense_buffer[7];
|
|
} else {
|
|
} else {
|
|
- /*
|
|
|
|
|
|
+ /*
|
|
* fixed format
|
|
* fixed format
|
|
*/
|
|
*/
|
|
if (sb_len > 2)
|
|
if (sb_len > 2)
|