From e494f6a728394ab0df194342549ee20e6f0752df Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 11 Nov 2013 13:44:54 +0100 Subject: [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 Signed-off-by: James Bottomley --- drivers/scsi/hosts.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'drivers/scsi/hosts.c') 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) spin_unlock_irqrestore(shost->host_lock, flags); scsi_autopm_get_host(shost); + flush_workqueue(shost->tmf_work_q); scsi_forget_host(shost); mutex_unlock(&shost->scan_mutex); scsi_proc_host_rm(shost); @@ -294,6 +295,8 @@ static void scsi_host_dev_release(struct device *dev) scsi_proc_hostdir_rm(shost->hostt); + if (shost->tmf_work_q) + destroy_workqueue(shost->tmf_work_q); if (shost->ehandler) kthread_stop(shost->ehandler); if (shost->work_q) @@ -360,7 +363,6 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) INIT_LIST_HEAD(&shost->eh_cmd_q); INIT_LIST_HEAD(&shost->starved_list); init_waitqueue_head(&shost->host_wait); - mutex_init(&shost->scan_mutex); /* @@ -444,9 +446,19 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) goto fail_kfree; } + shost->tmf_work_q = alloc_workqueue("scsi_tmf_%d", + WQ_UNBOUND | WQ_MEM_RECLAIM, + 1, shost->host_no); + if (!shost->tmf_work_q) { + printk(KERN_WARNING "scsi%d: failed to create tmf workq\n", + shost->host_no); + goto fail_kthread; + } scsi_proc_hostdir_add(shost->hostt); return shost; + fail_kthread: + kthread_stop(shost->ehandler); fail_kfree: kfree(shost); return NULL; -- cgit v1.2.1 From bb3b621a33d60fc2baddf31597ade01243e00a2c Mon Sep 17 00:00:00 2001 From: Ren Mingxin Date: Mon, 11 Nov 2013 13:44:56 +0100 Subject: [SCSI] Set the minimum valid value of 'eh_deadline' as 0 The former minimum valid value of 'eh_deadline' is 1s, which means the earliest occasion to shorten EH is 1 second later since a command is failed or timed out. But if we want to skip EH steps ASAP, we have to wait until the first EH step is finished. If the duration of the first EH step is long, this waiting time is excruciating. So, it is necessary to accept 0 as the minimum valid value for 'eh_deadline'. According to my test, with Hannes' patchset 'New EH command timeout handler' as well, the minimum IO time is improved from 73s (eh_deadline = 1) to 43s(eh_deadline = 0) when commands are timed out by disabling RSCN and target port. Signed-off-by: Ren Mingxin Signed-off-by: Hannes Reinecke Signed-off-by: James Bottomley --- drivers/scsi/hosts.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'drivers/scsi/hosts.c') diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index c3ab093dd8a7..f28ea070d3df 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -319,11 +319,11 @@ static void scsi_host_dev_release(struct device *dev) kfree(shost); } -static unsigned int shost_eh_deadline; +static int shost_eh_deadline = -1; -module_param_named(eh_deadline, shost_eh_deadline, uint, S_IRUGO|S_IWUSR); +module_param_named(eh_deadline, shost_eh_deadline, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(eh_deadline, - "SCSI EH timeout in seconds (should be between 1 and 2^32-1)"); + "SCSI EH timeout in seconds (should be between 0 and 2^31-1)"); static struct device_type scsi_host_type = { .name = "scsi_host", @@ -396,9 +396,18 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) shost->unchecked_isa_dma = sht->unchecked_isa_dma; shost->use_clustering = sht->use_clustering; shost->ordered_tag = sht->ordered_tag; - shost->eh_deadline = shost_eh_deadline * HZ; shost->no_write_same = sht->no_write_same; + if (shost_eh_deadline == -1) + shost->eh_deadline = -1; + else if ((ulong) shost_eh_deadline * HZ > INT_MAX) { + shost_printk(KERN_WARNING, shost, + "eh_deadline %u too large, setting to %u\n", + shost_eh_deadline, INT_MAX / HZ); + shost->eh_deadline = INT_MAX; + } else + shost->eh_deadline = shost_eh_deadline * HZ; + if (sht->supported_mode == MODE_UNKNOWN) /* means we didn't set it ... default to INITIATOR */ shost->active_mode = MODE_INITIATOR; -- cgit v1.2.1