diff options
author | Hannes Reinecke <hare@suse.de> | 2013-11-11 07:44:54 -0500 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2013-12-19 10:39:02 -0500 |
commit | e494f6a728394ab0df194342549ee20e6f0752df (patch) | |
tree | 2cbae9eea944540b2777e40f1787a90b78123334 /drivers/scsi/hosts.c | |
parent | 2451079bc2ae1334058be8babd44be03ecfa7041 (diff) |
[SCSI] improved eh timeout handler
When a command runs into a timeout we need to send an 'ABORT TASK'
TMF. This is typically done by the 'eh_abort_handler' LLDD callback.
Conceptually, however, this function is a normal SCSI command, so
there is no need to enter the error handler.
This patch implements a new scsi_abort_command() function which
invokes an asynchronous function scsi_eh_abort_handler() to
abort the commands via the usual 'eh_abort_handler'.
If abort succeeds the command is either retried or terminated,
depending on the number of allowed retries. However, 'eh_eflags'
records the abort, so if the retry would fail again the
command is pushed onto the error handler without trying to
abort it (again); it'll be cleared up from SCSI EH.
[hare: smatch detected stray switch fixed]
Signed-off-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers/scsi/hosts.c')
-rw-r--r-- | drivers/scsi/hosts.c | 14 |
1 files changed, 13 insertions, 1 deletions
diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index f2c5005f312a..c3ab093dd8a7 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c | |||
@@ -169,6 +169,7 @@ void scsi_remove_host(struct Scsi_Host *shost) | |||
169 | spin_unlock_irqrestore(shost->host_lock, flags); | 169 | spin_unlock_irqrestore(shost->host_lock, flags); |
170 | 170 | ||
171 | scsi_autopm_get_host(shost); | 171 | scsi_autopm_get_host(shost); |
172 | flush_workqueue(shost->tmf_work_q); | ||
172 | scsi_forget_host(shost); | 173 | scsi_forget_host(shost); |
173 | mutex_unlock(&shost->scan_mutex); | 174 | mutex_unlock(&shost->scan_mutex); |
174 | scsi_proc_host_rm(shost); | 175 | scsi_proc_host_rm(shost); |
@@ -294,6 +295,8 @@ static void scsi_host_dev_release(struct device *dev) | |||
294 | 295 | ||
295 | scsi_proc_hostdir_rm(shost->hostt); | 296 | scsi_proc_hostdir_rm(shost->hostt); |
296 | 297 | ||
298 | if (shost->tmf_work_q) | ||
299 | destroy_workqueue(shost->tmf_work_q); | ||
297 | if (shost->ehandler) | 300 | if (shost->ehandler) |
298 | kthread_stop(shost->ehandler); | 301 | kthread_stop(shost->ehandler); |
299 | if (shost->work_q) | 302 | if (shost->work_q) |
@@ -360,7 +363,6 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) | |||
360 | INIT_LIST_HEAD(&shost->eh_cmd_q); | 363 | INIT_LIST_HEAD(&shost->eh_cmd_q); |
361 | INIT_LIST_HEAD(&shost->starved_list); | 364 | INIT_LIST_HEAD(&shost->starved_list); |
362 | init_waitqueue_head(&shost->host_wait); | 365 | init_waitqueue_head(&shost->host_wait); |
363 | |||
364 | mutex_init(&shost->scan_mutex); | 366 | mutex_init(&shost->scan_mutex); |
365 | 367 | ||
366 | /* | 368 | /* |
@@ -444,9 +446,19 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) | |||
444 | goto fail_kfree; | 446 | goto fail_kfree; |
445 | } | 447 | } |
446 | 448 | ||
449 | shost->tmf_work_q = alloc_workqueue("scsi_tmf_%d", | ||
450 | WQ_UNBOUND | WQ_MEM_RECLAIM, | ||
451 | 1, shost->host_no); | ||
452 | if (!shost->tmf_work_q) { | ||
453 | printk(KERN_WARNING "scsi%d: failed to create tmf workq\n", | ||
454 | shost->host_no); | ||
455 | goto fail_kthread; | ||
456 | } | ||
447 | scsi_proc_hostdir_add(shost->hostt); | 457 | scsi_proc_hostdir_add(shost->hostt); |
448 | return shost; | 458 | return shost; |
449 | 459 | ||
460 | fail_kthread: | ||
461 | kthread_stop(shost->ehandler); | ||
450 | fail_kfree: | 462 | fail_kfree: |
451 | kfree(shost); | 463 | kfree(shost); |
452 | return NULL; | 464 | return NULL; |