mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-13 14:24:11 +08:00
scsi: make asynchronous aborts mandatory
There hasn't been any reports for HBAs where asynchronous abort would not work, so we should make it mandatory and remove the fallback. Signed-off-by: Hannes Reinecke <hare@suse.de> Reviewed-by: Johannes Thumshirn <jthumshirn@suse.de> Reviewed-by: Bart Van Assche <bart.vanassche@sandisk.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
parent
2171b6d08b
commit
a06586325f
@ -70,7 +70,7 @@ with the command.
|
|||||||
scmd is requeued to blk queue.
|
scmd is requeued to blk queue.
|
||||||
|
|
||||||
- otherwise
|
- otherwise
|
||||||
scsi_eh_scmd_add(scmd, 0) is invoked for the command. See
|
scsi_eh_scmd_add(scmd) is invoked for the command. See
|
||||||
[1-3] for details of this function.
|
[1-3] for details of this function.
|
||||||
|
|
||||||
|
|
||||||
@ -103,9 +103,7 @@ function
|
|||||||
eh_timed_out() callback did not handle the command.
|
eh_timed_out() callback did not handle the command.
|
||||||
Step #2 is taken.
|
Step #2 is taken.
|
||||||
|
|
||||||
2. If the host supports asynchronous completion (as indicated by the
|
2. scsi_abort_command() is invoked to schedule an asynchrous abort.
|
||||||
no_async_abort setting in the host template) scsi_abort_command()
|
|
||||||
is invoked to schedule an asynchrous abort.
|
|
||||||
Asynchronous abort are not invoked for commands which the
|
Asynchronous abort are not invoked for commands which the
|
||||||
SCSI_EH_ABORT_SCHEDULED flag is set (this indicates that the command
|
SCSI_EH_ABORT_SCHEDULED flag is set (this indicates that the command
|
||||||
already had been aborted once, and this is a retry which failed),
|
already had been aborted once, and this is a retry which failed),
|
||||||
@ -127,16 +125,13 @@ function
|
|||||||
|
|
||||||
scmds enter EH via scsi_eh_scmd_add(), which does the following.
|
scmds enter EH via scsi_eh_scmd_add(), which does the following.
|
||||||
|
|
||||||
1. Turns on scmd->eh_eflags as requested. It's 0 for error
|
1. Links scmd->eh_entry to shost->eh_cmd_q
|
||||||
completions and SCSI_EH_CANCEL_CMD for timeouts.
|
|
||||||
|
|
||||||
2. Links scmd->eh_entry to shost->eh_cmd_q
|
2. Sets SHOST_RECOVERY bit in shost->shost_state
|
||||||
|
|
||||||
3. Sets SHOST_RECOVERY bit in shost->shost_state
|
3. Increments shost->host_failed
|
||||||
|
|
||||||
4. Increments shost->host_failed
|
4. Wakes up SCSI EH thread if shost->host_busy == shost->host_failed
|
||||||
|
|
||||||
5. Wakes up SCSI EH thread if shost->host_busy == shost->host_failed
|
|
||||||
|
|
||||||
As can be seen above, once any scmd is added to shost->eh_cmd_q,
|
As can be seen above, once any scmd is added to shost->eh_cmd_q,
|
||||||
SHOST_RECOVERY shost_state bit is turned on. This prevents any new
|
SHOST_RECOVERY shost_state bit is turned on. This prevents any new
|
||||||
@ -252,7 +247,6 @@ scmd->allowed.
|
|||||||
|
|
||||||
1. Error completion / time out
|
1. Error completion / time out
|
||||||
ACTION: scsi_eh_scmd_add() is invoked for scmd
|
ACTION: scsi_eh_scmd_add() is invoked for scmd
|
||||||
- set scmd->eh_eflags
|
|
||||||
- add scmd to shost->eh_cmd_q
|
- add scmd to shost->eh_cmd_q
|
||||||
- set SHOST_RECOVERY
|
- set SHOST_RECOVERY
|
||||||
- shost->host_failed++
|
- shost->host_failed++
|
||||||
|
@ -162,7 +162,7 @@ scmd_eh_abort_handler(struct work_struct *work)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scsi_eh_scmd_add(scmd, 0);
|
scsi_eh_scmd_add(scmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -221,9 +221,8 @@ static void scsi_eh_reset(struct scsi_cmnd *scmd)
|
|||||||
/**
|
/**
|
||||||
* scsi_eh_scmd_add - add scsi cmd to error handling.
|
* scsi_eh_scmd_add - add scsi cmd to error handling.
|
||||||
* @scmd: scmd to run eh on.
|
* @scmd: scmd to run eh on.
|
||||||
* @eh_flag: optional SCSI_EH flag.
|
|
||||||
*/
|
*/
|
||||||
void scsi_eh_scmd_add(struct scsi_cmnd *scmd, int eh_flag)
|
void scsi_eh_scmd_add(struct scsi_cmnd *scmd)
|
||||||
{
|
{
|
||||||
struct Scsi_Host *shost = scmd->device->host;
|
struct Scsi_Host *shost = scmd->device->host;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
@ -239,9 +238,6 @@ void scsi_eh_scmd_add(struct scsi_cmnd *scmd, int eh_flag)
|
|||||||
if (shost->eh_deadline != -1 && !shost->last_reset)
|
if (shost->eh_deadline != -1 && !shost->last_reset)
|
||||||
shost->last_reset = jiffies;
|
shost->last_reset = jiffies;
|
||||||
|
|
||||||
if (scmd->eh_eflags & SCSI_EH_ABORT_SCHEDULED)
|
|
||||||
eh_flag &= ~SCSI_EH_CANCEL_CMD;
|
|
||||||
scmd->eh_eflags |= eh_flag;
|
|
||||||
scsi_eh_reset(scmd);
|
scsi_eh_reset(scmd);
|
||||||
list_add_tail(&scmd->eh_entry, &shost->eh_cmd_q);
|
list_add_tail(&scmd->eh_entry, &shost->eh_cmd_q);
|
||||||
shost->host_failed++;
|
shost->host_failed++;
|
||||||
@ -275,10 +271,9 @@ enum blk_eh_timer_return scsi_times_out(struct request *req)
|
|||||||
rtn = host->hostt->eh_timed_out(scmd);
|
rtn = host->hostt->eh_timed_out(scmd);
|
||||||
|
|
||||||
if (rtn == BLK_EH_NOT_HANDLED) {
|
if (rtn == BLK_EH_NOT_HANDLED) {
|
||||||
if (host->hostt->no_async_abort ||
|
if (scsi_abort_command(scmd) != SUCCESS) {
|
||||||
scsi_abort_command(scmd) != SUCCESS) {
|
|
||||||
set_host_byte(scmd, DID_TIME_OUT);
|
set_host_byte(scmd, DID_TIME_OUT);
|
||||||
scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD);
|
scsi_eh_scmd_add(scmd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,7 +326,7 @@ static inline void scsi_eh_prt_fail_stats(struct Scsi_Host *shost,
|
|||||||
list_for_each_entry(scmd, work_q, eh_entry) {
|
list_for_each_entry(scmd, work_q, eh_entry) {
|
||||||
if (scmd->device == sdev) {
|
if (scmd->device == sdev) {
|
||||||
++total_failures;
|
++total_failures;
|
||||||
if (scmd->eh_eflags & SCSI_EH_CANCEL_CMD)
|
if (scmd->eh_eflags & SCSI_EH_ABORT_SCHEDULED)
|
||||||
++cmd_cancel;
|
++cmd_cancel;
|
||||||
else
|
else
|
||||||
++cmd_failed;
|
++cmd_failed;
|
||||||
@ -1154,8 +1149,7 @@ int scsi_eh_get_sense(struct list_head *work_q,
|
|||||||
* should not get sense.
|
* should not get sense.
|
||||||
*/
|
*/
|
||||||
list_for_each_entry_safe(scmd, next, work_q, eh_entry) {
|
list_for_each_entry_safe(scmd, next, work_q, eh_entry) {
|
||||||
if ((scmd->eh_eflags & SCSI_EH_CANCEL_CMD) ||
|
if ((scmd->eh_eflags & SCSI_EH_ABORT_SCHEDULED) ||
|
||||||
(scmd->eh_eflags & SCSI_EH_ABORT_SCHEDULED) ||
|
|
||||||
SCSI_SENSE_VALID(scmd))
|
SCSI_SENSE_VALID(scmd))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -1295,61 +1289,6 @@ static int scsi_eh_test_devices(struct list_head *cmd_list,
|
|||||||
return list_empty(work_q);
|
return list_empty(work_q);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* scsi_eh_abort_cmds - abort pending commands.
|
|
||||||
* @work_q: &list_head for pending commands.
|
|
||||||
* @done_q: &list_head for processed commands.
|
|
||||||
*
|
|
||||||
* Decription:
|
|
||||||
* Try and see whether or not it makes sense to try and abort the
|
|
||||||
* running command. This only works out to be the case if we have one
|
|
||||||
* command that has timed out. If the command simply failed, it makes
|
|
||||||
* no sense to try and abort the command, since as far as the shost
|
|
||||||
* adapter is concerned, it isn't running.
|
|
||||||
*/
|
|
||||||
static int scsi_eh_abort_cmds(struct list_head *work_q,
|
|
||||||
struct list_head *done_q)
|
|
||||||
{
|
|
||||||
struct scsi_cmnd *scmd, *next;
|
|
||||||
LIST_HEAD(check_list);
|
|
||||||
int rtn;
|
|
||||||
struct Scsi_Host *shost;
|
|
||||||
|
|
||||||
list_for_each_entry_safe(scmd, next, work_q, eh_entry) {
|
|
||||||
if (!(scmd->eh_eflags & SCSI_EH_CANCEL_CMD))
|
|
||||||
continue;
|
|
||||||
shost = scmd->device->host;
|
|
||||||
if (scsi_host_eh_past_deadline(shost)) {
|
|
||||||
list_splice_init(&check_list, work_q);
|
|
||||||
SCSI_LOG_ERROR_RECOVERY(3,
|
|
||||||
scmd_printk(KERN_INFO, scmd,
|
|
||||||
"%s: skip aborting cmd, past eh deadline\n",
|
|
||||||
current->comm));
|
|
||||||
return list_empty(work_q);
|
|
||||||
}
|
|
||||||
SCSI_LOG_ERROR_RECOVERY(3,
|
|
||||||
scmd_printk(KERN_INFO, scmd,
|
|
||||||
"%s: aborting cmd\n", current->comm));
|
|
||||||
rtn = scsi_try_to_abort_cmd(shost->hostt, scmd);
|
|
||||||
if (rtn == FAILED) {
|
|
||||||
SCSI_LOG_ERROR_RECOVERY(3,
|
|
||||||
scmd_printk(KERN_INFO, scmd,
|
|
||||||
"%s: aborting cmd failed\n",
|
|
||||||
current->comm));
|
|
||||||
list_splice_init(&check_list, work_q);
|
|
||||||
return list_empty(work_q);
|
|
||||||
}
|
|
||||||
scmd->eh_eflags &= ~SCSI_EH_CANCEL_CMD;
|
|
||||||
if (rtn == FAST_IO_FAIL)
|
|
||||||
scsi_eh_finish_cmd(scmd, done_q);
|
|
||||||
else
|
|
||||||
list_move_tail(&scmd->eh_entry, &check_list);
|
|
||||||
}
|
|
||||||
|
|
||||||
return scsi_eh_test_devices(&check_list, work_q, done_q, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* scsi_eh_try_stu - Send START_UNIT to device.
|
* scsi_eh_try_stu - Send START_UNIT to device.
|
||||||
* @scmd: &scsi_cmnd to send START_UNIT
|
* @scmd: &scsi_cmnd to send START_UNIT
|
||||||
@ -1692,11 +1631,6 @@ static void scsi_eh_offline_sdevs(struct list_head *work_q,
|
|||||||
sdev_printk(KERN_INFO, scmd->device, "Device offlined - "
|
sdev_printk(KERN_INFO, scmd->device, "Device offlined - "
|
||||||
"not ready after error recovery\n");
|
"not ready after error recovery\n");
|
||||||
scsi_device_set_state(scmd->device, SDEV_OFFLINE);
|
scsi_device_set_state(scmd->device, SDEV_OFFLINE);
|
||||||
if (scmd->eh_eflags & SCSI_EH_CANCEL_CMD) {
|
|
||||||
/*
|
|
||||||
* FIXME: Handle lost cmds.
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
scsi_eh_finish_cmd(scmd, done_q);
|
scsi_eh_finish_cmd(scmd, done_q);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@ -2140,8 +2074,7 @@ static void scsi_unjam_host(struct Scsi_Host *shost)
|
|||||||
SCSI_LOG_ERROR_RECOVERY(1, scsi_eh_prt_fail_stats(shost, &eh_work_q));
|
SCSI_LOG_ERROR_RECOVERY(1, scsi_eh_prt_fail_stats(shost, &eh_work_q));
|
||||||
|
|
||||||
if (!scsi_eh_get_sense(&eh_work_q, &eh_done_q))
|
if (!scsi_eh_get_sense(&eh_work_q, &eh_done_q))
|
||||||
if (!scsi_eh_abort_cmds(&eh_work_q, &eh_done_q))
|
scsi_eh_ready_devs(shost, &eh_work_q, &eh_done_q);
|
||||||
scsi_eh_ready_devs(shost, &eh_work_q, &eh_done_q);
|
|
||||||
|
|
||||||
spin_lock_irqsave(shost->host_lock, flags);
|
spin_lock_irqsave(shost->host_lock, flags);
|
||||||
if (shost->eh_deadline != -1)
|
if (shost->eh_deadline != -1)
|
||||||
|
@ -1593,7 +1593,7 @@ static void scsi_softirq_done(struct request *rq)
|
|||||||
scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY);
|
scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
scsi_eh_scmd_add(cmd, 0);
|
scsi_eh_scmd_add(cmd);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,6 @@ struct scsi_nl_hdr;
|
|||||||
/*
|
/*
|
||||||
* Scsi Error Handler Flags
|
* Scsi Error Handler Flags
|
||||||
*/
|
*/
|
||||||
#define SCSI_EH_CANCEL_CMD 0x0001 /* Cancel this cmd */
|
|
||||||
#define SCSI_EH_ABORT_SCHEDULED 0x0002 /* Abort has been scheduled */
|
#define SCSI_EH_ABORT_SCHEDULED 0x0002 /* Abort has been scheduled */
|
||||||
|
|
||||||
#define SCSI_SENSE_VALID(scmd) \
|
#define SCSI_SENSE_VALID(scmd) \
|
||||||
@ -72,7 +71,7 @@ extern enum blk_eh_timer_return scsi_times_out(struct request *req);
|
|||||||
extern int scsi_error_handler(void *host);
|
extern int scsi_error_handler(void *host);
|
||||||
extern int scsi_decide_disposition(struct scsi_cmnd *cmd);
|
extern int scsi_decide_disposition(struct scsi_cmnd *cmd);
|
||||||
extern void scsi_eh_wakeup(struct Scsi_Host *shost);
|
extern void scsi_eh_wakeup(struct Scsi_Host *shost);
|
||||||
extern void scsi_eh_scmd_add(struct scsi_cmnd *, int);
|
extern void scsi_eh_scmd_add(struct scsi_cmnd *);
|
||||||
void scsi_eh_ready_devs(struct Scsi_Host *shost,
|
void scsi_eh_ready_devs(struct Scsi_Host *shost,
|
||||||
struct list_head *work_q,
|
struct list_head *work_q,
|
||||||
struct list_head *done_q);
|
struct list_head *done_q);
|
||||||
|
@ -451,11 +451,6 @@ struct scsi_host_template {
|
|||||||
/* True if the controller does not support WRITE SAME */
|
/* True if the controller does not support WRITE SAME */
|
||||||
unsigned no_write_same:1;
|
unsigned no_write_same:1;
|
||||||
|
|
||||||
/*
|
|
||||||
* True if asynchronous aborts are not supported
|
|
||||||
*/
|
|
||||||
unsigned no_async_abort:1;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Countdown for host blocking with no commands outstanding.
|
* Countdown for host blocking with no commands outstanding.
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user