All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/4] New SCSI command timeout handler
@ 2013-06-06  9:43 Hannes Reinecke
  2013-06-06  9:43 ` [PATCH 1/4] scsi: move initialization of scmd->eh_entry Hannes Reinecke
                   ` (4 more replies)
  0 siblings, 5 replies; 19+ messages in thread
From: Hannes Reinecke @ 2013-06-06  9:43 UTC (permalink / raw)
  To: James Bottomley
  Cc: linux-scsi, Joern Engel, Ewan Milne, James Smart, Ren Mingxin,
	Roland Dreier, Bryn Reeves, Christoph Hellwig, Hannes Reinecke

Hi all,

this is the first step towards a new non-blocking
error handler. This patch implements a new command
timeout handler which will be sending command aborts
inline without engaging SCSI EH.

In addition the commands will be returned directly
if the command abort succeeded, cutting down recovery
times dramatically.

With the original scsi error recovery I got:
# time dd if=/dev/zero of=/mnt/test.blk bs=512 count=2048 oflag=sync
2048+0 records in
2048+0 records out
1048576 bytes (1.0 MB) copied, 3.72732 s, 281 kB/s

real	2m14.475s
user	0m0.000s
sys	0m0.104s

with this patchset I got:
# time dd if=/dev/zero of=/mnt/test.blk bs=512 count=2048 oflag=sync
2048+0 records in
2048+0 records out
1048576 bytes (1.0 MB) copied, 31.5151 s, 33.3 kB/s

real	0m31.519s
user	0m0.000s
sys	0m0.088s

Test was to disable RSCN on the target port, disable the
target port, and then start the 'dd' command as indicated.

As a proof-of-concept I've also enabled the new timeout
handler for virtio, so that things can be tested out
more easily.

Comments etc are welcome.

Hannes Reinecke (4):
  scsi: move initialization of scmd->eh_entry
  blk-timeout: add BLK_EH_SCHEDULED return code
  scsi: improved eh timeout handler
  virtio_scsi: use improved eh timeout handler

 drivers/scsi/scsi_error.c        | 82 ++++++++++++++++++++++++++++++++++++++++
 drivers/scsi/scsi_lib.c          |  4 +-
 drivers/scsi/scsi_scan.c         |  3 ++
 drivers/scsi/scsi_transport_fc.c |  3 +-
 drivers/scsi/virtio_scsi.c       |  8 ++++
 include/linux/blkdev.h           |  1 +
 include/scsi/scsi_cmnd.h         |  1 +
 include/scsi/scsi_device.h       |  2 +
 8 files changed, 101 insertions(+), 3 deletions(-)

-- 
1.7.12.4


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 1/4] scsi: move initialization of scmd->eh_entry
  2013-06-06  9:43 [PATCH 0/4] New SCSI command timeout handler Hannes Reinecke
@ 2013-06-06  9:43 ` Hannes Reinecke
  2013-06-06 16:14   ` Jörn Engel
  2013-06-06  9:43 ` [PATCH 2/4] blk-timeout: add BLK_EH_SCHEDULED return code Hannes Reinecke
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 19+ messages in thread
From: Hannes Reinecke @ 2013-06-06  9:43 UTC (permalink / raw)
  To: James Bottomley
  Cc: linux-scsi, Joern Engel, Ewan Milne, James Smart, Ren Mingxin,
	Roland Dreier, Bryn Reeves, Christoph Hellwig, Hannes Reinecke

The 'eh_entry' list might be used even before scsi_softirq_done()
is called. Hence we should rather initialize it together with
the other eh-related variables.

Signed-off-by: Hannes Reinecke <hare@suse.de>
---
 drivers/scsi/scsi_lib.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 86d5220..c1774de 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -317,6 +317,8 @@ static void scsi_init_cmd_errh(struct scsi_cmnd *cmd)
 	memset(cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
 	if (cmd->cmd_len == 0)
 		cmd->cmd_len = scsi_command_size(cmd->cmnd);
+	INIT_LIST_HEAD(&cmd->eh_entry);
+	cmd->eh_eflags = 0;
 }
 
 void scsi_device_unbusy(struct scsi_device *sdev)
@@ -1482,8 +1484,6 @@ static void scsi_softirq_done(struct request *rq)
 	unsigned long wait_for = (cmd->allowed + 1) * rq->timeout;
 	int disposition;
 
-	INIT_LIST_HEAD(&cmd->eh_entry);
-
 	atomic_inc(&cmd->device->iodone_cnt);
 	if (cmd->result)
 		atomic_inc(&cmd->device->ioerr_cnt);
-- 
1.7.12.4


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 2/4] blk-timeout: add BLK_EH_SCHEDULED return code
  2013-06-06  9:43 [PATCH 0/4] New SCSI command timeout handler Hannes Reinecke
  2013-06-06  9:43 ` [PATCH 1/4] scsi: move initialization of scmd->eh_entry Hannes Reinecke
@ 2013-06-06  9:43 ` Hannes Reinecke
  2013-06-06 16:24   ` Jörn Engel
  2013-06-06  9:43 ` [PATCH 3/4] scsi: improved eh timeout handler Hannes Reinecke
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 19+ messages in thread
From: Hannes Reinecke @ 2013-06-06  9:43 UTC (permalink / raw)
  To: James Bottomley
  Cc: linux-scsi, Joern Engel, Ewan Milne, James Smart, Ren Mingxin,
	Roland Dreier, Bryn Reeves, Christoph Hellwig, Hannes Reinecke

Add a 'BLK_EH_SCHEDULED' return code for blk-timeout to indicate
that a delayed error recovery has been initiated.

Signed-off-by: Hannes Reinecke <hare@suse.de>
---
 drivers/scsi/scsi_error.c | 3 +++
 include/linux/blkdev.h    | 1 +
 2 files changed, 4 insertions(+)

diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index f43de1e..96b4bb6 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -146,6 +146,9 @@ enum blk_eh_timer_return scsi_times_out(struct request *req)
 		rtn = host->hostt->eh_timed_out(scmd);
 
 	scmd->result |= DID_TIME_OUT << 16;
+	/* Check for delayed EH scheduling */
+	if (rtn == BLK_EH_SCHEDULED)
+		return BLK_EH_NOT_HANDLED;
 
 	if (unlikely(rtn == BLK_EH_NOT_HANDLED &&
 		     !scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD)))
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 2fdb4a4..d846e2b 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -238,6 +238,7 @@ enum blk_eh_timer_return {
 	BLK_EH_NOT_HANDLED,
 	BLK_EH_HANDLED,
 	BLK_EH_RESET_TIMER,
+	BLK_EH_SCHEDULED,
 };
 
 typedef enum blk_eh_timer_return (rq_timed_out_fn)(struct request *);
-- 
1.7.12.4


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 3/4] scsi: improved eh timeout handler
  2013-06-06  9:43 [PATCH 0/4] New SCSI command timeout handler Hannes Reinecke
  2013-06-06  9:43 ` [PATCH 1/4] scsi: move initialization of scmd->eh_entry Hannes Reinecke
  2013-06-06  9:43 ` [PATCH 2/4] blk-timeout: add BLK_EH_SCHEDULED return code Hannes Reinecke
@ 2013-06-06  9:43 ` Hannes Reinecke
  2013-06-06 16:23   ` Jörn Engel
                     ` (2 more replies)
  2013-06-06  9:43 ` [PATCH 4/4] virtio_scsi: use " Hannes Reinecke
  2013-06-07  6:54 ` [PATCH 0/4] New SCSI command " Ren Mingxin
  4 siblings, 3 replies; 19+ messages in thread
From: Hannes Reinecke @ 2013-06-06  9:43 UTC (permalink / raw)
  To: James Bottomley
  Cc: linux-scsi, Joern Engel, Ewan Milne, James Smart, Ren Mingxin,
	Roland Dreier, Bryn Reeves, Christoph Hellwig, Hannes Reinecke

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 'eh_abort_handler'.

If the 'eh_abort_handler' returns SUCCESS or FAST_IO_FAIL the
command will be retried if possible. If no retries are allowed
the command will be returned immediately, as we have to assume
the TMF succeeded and the command is completed with the LLDD.
For any other return code from 'eh_abort_handler' the command
will be pushed onto the existing SCSI EH handler, or aborted
with DID_TIME_OUT if that fails.

Signed-off-by: Hannes Reinecke <hare@suse.de>
---
 drivers/scsi/scsi_error.c        | 79 ++++++++++++++++++++++++++++++++++++++++
 drivers/scsi/scsi_scan.c         |  3 ++
 drivers/scsi/scsi_transport_fc.c |  3 +-
 include/scsi/scsi_cmnd.h         |  1 +
 include/scsi/scsi_device.h       |  2 +
 5 files changed, 87 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index 96b4bb6..0a6b21c 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -55,6 +55,8 @@ static void scsi_eh_done(struct scsi_cmnd *scmd);
 #define HOST_RESET_SETTLE_TIME  (10)
 
 static int scsi_eh_try_stu(struct scsi_cmnd *scmd);
+static int scsi_try_to_abort_cmd(struct scsi_host_template *hostt,
+				 struct scsi_cmnd *scmd);
 
 /* called with shost->host_lock held */
 void scsi_eh_wakeup(struct Scsi_Host *shost)
@@ -90,6 +92,83 @@ void scsi_schedule_eh(struct Scsi_Host *shost)
 EXPORT_SYMBOL_GPL(scsi_schedule_eh);
 
 /**
+ * scsi_eh_abort_handler - Handle command aborts
+ * @work:	sdev on which commands should be aborted.
+ */
+void
+scsi_eh_abort_handler(struct work_struct *work)
+{
+	struct scsi_device *sdev =
+		container_of(work, struct scsi_device, abort_work);
+	struct Scsi_Host *shost = sdev->host;
+	struct scsi_cmnd *scmd, *tmp;
+	unsigned long flags;
+	int rtn;
+
+	spin_lock_irqsave(&sdev->list_lock, flags);
+	list_for_each_entry_safe(scmd, tmp, &sdev->eh_abort_list, eh_entry) {
+		list_del_init(&scmd->eh_entry);
+		spin_unlock_irqrestore(&sdev->list_lock, flags);
+		SCSI_LOG_ERROR_RECOVERY(3,
+			scmd_printk(KERN_INFO, scmd,
+				    "aborting command %p\n", scmd));
+		rtn = scsi_try_to_abort_cmd(shost->hostt, scmd);
+		if (rtn == SUCCESS || rtn == FAST_IO_FAIL) {
+			if (((scmd->request->cmd_flags & REQ_FAILFAST_DEV) ||
+			     (scmd->request->cmd_type == REQ_TYPE_BLOCK_PC)) &&
+			    (++scmd->retries <= scmd->allowed)) {
+				SCSI_LOG_ERROR_RECOVERY(3,
+					scmd_printk(KERN_WARNING, scmd,
+						    "retry aborted command\n"));
+
+				scsi_queue_insert(scmd, SCSI_MLQUEUE_EH_RETRY);
+			} else {
+				SCSI_LOG_ERROR_RECOVERY(3,
+					scmd_printk(KERN_WARNING, scmd,
+						    "fast fail aborted command\n"));
+				scmd->result |= DID_TRANSPORT_FAILFAST << 16;
+				scsi_finish_command(scmd);
+			}
+		} else {
+			if (!scsi_eh_scmd_add(scmd, 0)) {
+				SCSI_LOG_ERROR_RECOVERY(3,
+					scmd_printk(KERN_WARNING, scmd,
+						    "terminate aborted command\n"));
+				scmd->result |= DID_TIME_OUT << 16;
+				scsi_finish_command(scmd);
+			}
+		}
+		spin_lock_irqsave(&sdev->list_lock, flags);
+	}
+	spin_unlock_irqrestore(&sdev->list_lock, flags);
+}
+
+/**
+ * scsi_abort_command - schedule a command abort
+ * @scmd:	scmd to abort.
+ *
+ * We only need to abort commands after a command timeout
+ */
+void
+scsi_abort_command(struct scsi_cmnd *scmd)
+{
+	unsigned long flags;
+	int kick_worker = 0;
+	struct scsi_device *sdev = scmd->device;
+
+	spin_lock_irqsave(&sdev->list_lock, flags);
+	if (list_empty(&sdev->eh_abort_list))
+		kick_worker = 1;
+	list_add(&scmd->eh_entry, &sdev->eh_abort_list);
+	SCSI_LOG_ERROR_RECOVERY(3,
+		scmd_printk(KERN_INFO, scmd, "adding to eh_abort_list\n"));
+	spin_unlock_irqrestore(&sdev->list_lock, flags);
+	if (kick_worker)
+		schedule_work(&sdev->abort_work);
+}
+EXPORT_SYMBOL_GPL(scsi_abort_command);
+
+/**
  * scsi_eh_scmd_add - add scsi cmd to error handling.
  * @scmd:	scmd to run eh on.
  * @eh_flag:	optional SCSI_EH flag.
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index 3e58b22..f9cc6fc 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -231,6 +231,7 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget,
 	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
 	extern void scsi_evt_thread(struct work_struct *work);
 	extern void scsi_requeue_run_queue(struct work_struct *work);
+	extern void scsi_eh_abort_handler(struct work_struct *work);
 
 	sdev = kzalloc(sizeof(*sdev) + shost->transportt->device_size,
 		       GFP_ATOMIC);
@@ -251,9 +252,11 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget,
 	INIT_LIST_HEAD(&sdev->cmd_list);
 	INIT_LIST_HEAD(&sdev->starved_entry);
 	INIT_LIST_HEAD(&sdev->event_list);
+	INIT_LIST_HEAD(&sdev->eh_abort_list);
 	spin_lock_init(&sdev->list_lock);
 	INIT_WORK(&sdev->event_work, scsi_evt_thread);
 	INIT_WORK(&sdev->requeue_work, scsi_requeue_run_queue);
+	INIT_WORK(&sdev->abort_work, scsi_eh_abort_handler);
 
 	sdev->sdev_gendev.parent = get_device(&starget->dev);
 	sdev->sdev_target = starget;
diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c
index e106c27..09237bf 100644
--- a/drivers/scsi/scsi_transport_fc.c
+++ b/drivers/scsi/scsi_transport_fc.c
@@ -2079,7 +2079,8 @@ fc_timed_out(struct scsi_cmnd *scmd)
 	if (rport->port_state == FC_PORTSTATE_BLOCKED)
 		return BLK_EH_RESET_TIMER;
 
-	return BLK_EH_NOT_HANDLED;
+	scsi_abort_command(scmd);
+	return BLK_EH_SCHEDULED;
 }
 
 /*
diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h
index de5f5d8..460e514 100644
--- a/include/scsi/scsi_cmnd.h
+++ b/include/scsi/scsi_cmnd.h
@@ -144,6 +144,7 @@ extern void scsi_put_command(struct scsi_cmnd *);
 extern void __scsi_put_command(struct Scsi_Host *, struct scsi_cmnd *,
 			       struct device *);
 extern void scsi_finish_command(struct scsi_cmnd *cmd);
+extern void scsi_abort_command(struct scsi_cmnd *cmd);
 
 extern void *scsi_kmap_atomic_sg(struct scatterlist *sg, int sg_count,
 				 size_t *offset, size_t *len);
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
index cc64587..e03d379 100644
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -80,6 +80,7 @@ struct scsi_device {
 	spinlock_t list_lock;
 	struct list_head cmd_list;	/* queue of in use SCSI Command structures */
 	struct list_head starved_entry;
+	struct list_head eh_abort_list;
 	struct scsi_cmnd *current_cmnd;	/* currently active command */
 	unsigned short queue_depth;	/* How deep of a queue we want */
 	unsigned short max_queue_depth;	/* max queue depth */
@@ -180,6 +181,7 @@ struct scsi_device {
 
 	struct execute_work	ew; /* used to get process context on put */
 	struct work_struct	requeue_work;
+	struct work_struct	abort_work;
 
 	struct scsi_dh_data	*scsi_dh_data;
 	enum scsi_device_state sdev_state;
-- 
1.7.12.4


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 4/4] virtio_scsi: use improved eh timeout handler
  2013-06-06  9:43 [PATCH 0/4] New SCSI command timeout handler Hannes Reinecke
                   ` (2 preceding siblings ...)
  2013-06-06  9:43 ` [PATCH 3/4] scsi: improved eh timeout handler Hannes Reinecke
@ 2013-06-06  9:43 ` Hannes Reinecke
  2013-06-07  6:54 ` [PATCH 0/4] New SCSI command " Ren Mingxin
  4 siblings, 0 replies; 19+ messages in thread
From: Hannes Reinecke @ 2013-06-06  9:43 UTC (permalink / raw)
  To: James Bottomley
  Cc: linux-scsi, Joern Engel, Ewan Milne, James Smart, Ren Mingxin,
	Roland Dreier, Bryn Reeves, Christoph Hellwig, Hannes Reinecke

Hook in the improved eh timeout handler for virtio.

Signed-off-by: Hannes Reinecke <hare@suse.de>
---
 drivers/scsi/virtio_scsi.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c
index 2168258..3fb588d 100644
--- a/drivers/scsi/virtio_scsi.c
+++ b/drivers/scsi/virtio_scsi.c
@@ -654,6 +654,12 @@ static int virtscsi_abort(struct scsi_cmnd *sc)
 	return virtscsi_tmf(vscsi, cmd);
 }
 
+static enum blk_eh_timer_return virtscsi_timedout(struct scsi_cmnd *scmd)
+{
+	scsi_abort_command(scmd);
+	return BLK_EH_SCHEDULED;
+}
+
 static int virtscsi_target_alloc(struct scsi_target *starget)
 {
 	struct virtio_scsi_target_state *tgt =
@@ -683,6 +689,7 @@ static struct scsi_host_template virtscsi_host_template_single = {
 	.queuecommand = virtscsi_queuecommand_single,
 	.eh_abort_handler = virtscsi_abort,
 	.eh_device_reset_handler = virtscsi_device_reset,
+	.eh_timed_out = virtscsi_timedout,
 
 	.can_queue = 1024,
 	.dma_boundary = UINT_MAX,
@@ -699,6 +706,7 @@ static struct scsi_host_template virtscsi_host_template_multi = {
 	.queuecommand = virtscsi_queuecommand_multi,
 	.eh_abort_handler = virtscsi_abort,
 	.eh_device_reset_handler = virtscsi_device_reset,
+	.eh_timed_out = virtscsi_timedout,
 
 	.can_queue = 1024,
 	.dma_boundary = UINT_MAX,
-- 
1.7.12.4


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH 1/4] scsi: move initialization of scmd->eh_entry
  2013-06-06  9:43 ` [PATCH 1/4] scsi: move initialization of scmd->eh_entry Hannes Reinecke
@ 2013-06-06 16:14   ` Jörn Engel
  0 siblings, 0 replies; 19+ messages in thread
From: Jörn Engel @ 2013-06-06 16:14 UTC (permalink / raw)
  To: Hannes Reinecke
  Cc: James Bottomley, linux-scsi, Ewan Milne, James Smart,
	Ren Mingxin, Roland Dreier, Bryn Reeves, Christoph Hellwig

On Thu, 6 June 2013 11:43:52 +0200, Hannes Reinecke wrote:
> 
> The 'eh_entry' list might be used even before scsi_softirq_done()
> is called. Hence we should rather initialize it together with
> the other eh-related variables.
> 
> Signed-off-by: Hannes Reinecke <hare@suse.de>

Tested-by: Joern Engel <joern@logfs.org>

Jörn

--
It is better to die of hunger having lived without grief and fear,
than to live with a troubled spirit amid abundance.
-- Epictetus
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 3/4] scsi: improved eh timeout handler
  2013-06-06  9:43 ` [PATCH 3/4] scsi: improved eh timeout handler Hannes Reinecke
@ 2013-06-06 16:23   ` Jörn Engel
  2013-06-06 20:39     ` Hannes Reinecke
  2013-06-07 16:21   ` Jörn Engel
  2013-06-10  0:12   ` Baruch Even
  2 siblings, 1 reply; 19+ messages in thread
From: Jörn Engel @ 2013-06-06 16:23 UTC (permalink / raw)
  To: Hannes Reinecke
  Cc: James Bottomley, linux-scsi, Ewan Milne, James Smart,
	Ren Mingxin, Roland Dreier, Bryn Reeves, Christoph Hellwig

On Thu, 6 June 2013 11:43:54 +0200, Hannes Reinecke wrote:
> 
> 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 'eh_abort_handler'.
> 
> If the 'eh_abort_handler' returns SUCCESS or FAST_IO_FAIL the
> command will be retried if possible. If no retries are allowed
> the command will be returned immediately, as we have to assume
> the TMF succeeded and the command is completed with the LLDD.
> For any other return code from 'eh_abort_handler' the command
> will be pushed onto the existing SCSI EH handler, or aborted
> with DID_TIME_OUT if that fails.
> 
> Signed-off-by: Hannes Reinecke <hare@suse.de>
> ---
>  drivers/scsi/scsi_error.c        | 79 ++++++++++++++++++++++++++++++++++++++++
>  drivers/scsi/scsi_scan.c         |  3 ++
>  drivers/scsi/scsi_transport_fc.c |  3 +-
>  include/scsi/scsi_cmnd.h         |  1 +
>  include/scsi/scsi_device.h       |  2 +
>  5 files changed, 87 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
> index 96b4bb6..0a6b21c 100644
> --- a/drivers/scsi/scsi_error.c
> +++ b/drivers/scsi/scsi_error.c
> @@ -55,6 +55,8 @@ static void scsi_eh_done(struct scsi_cmnd *scmd);
>  #define HOST_RESET_SETTLE_TIME  (10)
>  
>  static int scsi_eh_try_stu(struct scsi_cmnd *scmd);
> +static int scsi_try_to_abort_cmd(struct scsi_host_template *hostt,
> +				 struct scsi_cmnd *scmd);
>  
>  /* called with shost->host_lock held */
>  void scsi_eh_wakeup(struct Scsi_Host *shost)
> @@ -90,6 +92,83 @@ void scsi_schedule_eh(struct Scsi_Host *shost)
>  EXPORT_SYMBOL_GPL(scsi_schedule_eh);
>  
>  /**
> + * scsi_eh_abort_handler - Handle command aborts
> + * @work:	sdev on which commands should be aborted.
> + */
> +void
> +scsi_eh_abort_handler(struct work_struct *work)
> +{
> +	struct scsi_device *sdev =
> +		container_of(work, struct scsi_device, abort_work);
> +	struct Scsi_Host *shost = sdev->host;
> +	struct scsi_cmnd *scmd, *tmp;
> +	unsigned long flags;
> +	int rtn;
> +
> +	spin_lock_irqsave(&sdev->list_lock, flags);
> +	list_for_each_entry_safe(scmd, tmp, &sdev->eh_abort_list, eh_entry) {
> +		list_del_init(&scmd->eh_entry);

The _init bit is not needed.  I prefer list_del, as the poisoning
sometimes helps catching bugs.

> +		spin_unlock_irqrestore(&sdev->list_lock, flags);
> +		SCSI_LOG_ERROR_RECOVERY(3,
> +			scmd_printk(KERN_INFO, scmd,
> +				    "aborting command %p\n", scmd));
> +		rtn = scsi_try_to_abort_cmd(shost->hostt, scmd);
> +		if (rtn == SUCCESS || rtn == FAST_IO_FAIL) {
> +			if (((scmd->request->cmd_flags & REQ_FAILFAST_DEV) ||

Am I being stupid again or should this be negated?

> +			     (scmd->request->cmd_type == REQ_TYPE_BLOCK_PC)) &&
> +			    (++scmd->retries <= scmd->allowed)) {
> +				SCSI_LOG_ERROR_RECOVERY(3,
> +					scmd_printk(KERN_WARNING, scmd,
> +						    "retry aborted command\n"));
> +
> +				scsi_queue_insert(scmd, SCSI_MLQUEUE_EH_RETRY);
> +			} else {
> +				SCSI_LOG_ERROR_RECOVERY(3,
> +					scmd_printk(KERN_WARNING, scmd,
> +						    "fast fail aborted command\n"));
> +				scmd->result |= DID_TRANSPORT_FAILFAST << 16;
> +				scsi_finish_command(scmd);
> +			}
> +		} else {
> +			if (!scsi_eh_scmd_add(scmd, 0)) {
> +				SCSI_LOG_ERROR_RECOVERY(3,
> +					scmd_printk(KERN_WARNING, scmd,
> +						    "terminate aborted command\n"));
> +				scmd->result |= DID_TIME_OUT << 16;
> +				scsi_finish_command(scmd);
> +			}
> +		}
> +		spin_lock_irqsave(&sdev->list_lock, flags);
> +	}
> +	spin_unlock_irqrestore(&sdev->list_lock, flags);
> +}
> +
> +/**
> + * scsi_abort_command - schedule a command abort
> + * @scmd:	scmd to abort.
> + *
> + * We only need to abort commands after a command timeout
> + */
> +void
> +scsi_abort_command(struct scsi_cmnd *scmd)
> +{
> +	unsigned long flags;
> +	int kick_worker = 0;
> +	struct scsi_device *sdev = scmd->device;
> +
> +	spin_lock_irqsave(&sdev->list_lock, flags);
> +	if (list_empty(&sdev->eh_abort_list))
> +		kick_worker = 1;
> +	list_add(&scmd->eh_entry, &sdev->eh_abort_list);
> +	SCSI_LOG_ERROR_RECOVERY(3,
> +		scmd_printk(KERN_INFO, scmd, "adding to eh_abort_list\n"));

The printk can be moved outside the spinlock.  Who knows, maybe this
will become a scalability bottleneck before the millenium is over.

> +	spin_unlock_irqrestore(&sdev->list_lock, flags);
> +	if (kick_worker)
> +		schedule_work(&sdev->abort_work);
> +}
> +EXPORT_SYMBOL_GPL(scsi_abort_command);
> +
> +/**
>   * scsi_eh_scmd_add - add scsi cmd to error handling.
>   * @scmd:	scmd to run eh on.
>   * @eh_flag:	optional SCSI_EH flag.
> diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
> index 3e58b22..f9cc6fc 100644
> --- a/drivers/scsi/scsi_scan.c
> +++ b/drivers/scsi/scsi_scan.c
> @@ -231,6 +231,7 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget,
>  	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
>  	extern void scsi_evt_thread(struct work_struct *work);
>  	extern void scsi_requeue_run_queue(struct work_struct *work);
> +	extern void scsi_eh_abort_handler(struct work_struct *work);

Function declarations in a .c file?  Ick!

>  
>  	sdev = kzalloc(sizeof(*sdev) + shost->transportt->device_size,
>  		       GFP_ATOMIC);
> @@ -251,9 +252,11 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget,
>  	INIT_LIST_HEAD(&sdev->cmd_list);
>  	INIT_LIST_HEAD(&sdev->starved_entry);
>  	INIT_LIST_HEAD(&sdev->event_list);
> +	INIT_LIST_HEAD(&sdev->eh_abort_list);
>  	spin_lock_init(&sdev->list_lock);
>  	INIT_WORK(&sdev->event_work, scsi_evt_thread);
>  	INIT_WORK(&sdev->requeue_work, scsi_requeue_run_queue);
> +	INIT_WORK(&sdev->abort_work, scsi_eh_abort_handler);
>  
>  	sdev->sdev_gendev.parent = get_device(&starget->dev);
>  	sdev->sdev_target = starget;
> diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c
> index e106c27..09237bf 100644
> --- a/drivers/scsi/scsi_transport_fc.c
> +++ b/drivers/scsi/scsi_transport_fc.c
> @@ -2079,7 +2079,8 @@ fc_timed_out(struct scsi_cmnd *scmd)
>  	if (rport->port_state == FC_PORTSTATE_BLOCKED)
>  		return BLK_EH_RESET_TIMER;
>  
> -	return BLK_EH_NOT_HANDLED;
> +	scsi_abort_command(scmd);
> +	return BLK_EH_SCHEDULED;
>  }
>  
>  /*
> diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h
> index de5f5d8..460e514 100644
> --- a/include/scsi/scsi_cmnd.h
> +++ b/include/scsi/scsi_cmnd.h
> @@ -144,6 +144,7 @@ extern void scsi_put_command(struct scsi_cmnd *);
>  extern void __scsi_put_command(struct Scsi_Host *, struct scsi_cmnd *,
>  			       struct device *);
>  extern void scsi_finish_command(struct scsi_cmnd *cmd);
> +extern void scsi_abort_command(struct scsi_cmnd *cmd);
>  
>  extern void *scsi_kmap_atomic_sg(struct scatterlist *sg, int sg_count,
>  				 size_t *offset, size_t *len);
> diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
> index cc64587..e03d379 100644
> --- a/include/scsi/scsi_device.h
> +++ b/include/scsi/scsi_device.h
> @@ -80,6 +80,7 @@ struct scsi_device {
>  	spinlock_t list_lock;
>  	struct list_head cmd_list;	/* queue of in use SCSI Command structures */
>  	struct list_head starved_entry;
> +	struct list_head eh_abort_list;
>  	struct scsi_cmnd *current_cmnd;	/* currently active command */
>  	unsigned short queue_depth;	/* How deep of a queue we want */
>  	unsigned short max_queue_depth;	/* max queue depth */
> @@ -180,6 +181,7 @@ struct scsi_device {
>  
>  	struct execute_work	ew; /* used to get process context on put */
>  	struct work_struct	requeue_work;
> +	struct work_struct	abort_work;
>  
>  	struct scsi_dh_data	*scsi_dh_data;
>  	enum scsi_device_state sdev_state;
> -- 
> 1.7.12.4
> 

Jörn

--
You can't tell where a program is going to spend its time. Bottlenecks
occur in surprising places, so don't try to second guess and put in a
speed hack until you've proven that's where the bottleneck is.
-- Rob Pike
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 2/4] blk-timeout: add BLK_EH_SCHEDULED return code
  2013-06-06  9:43 ` [PATCH 2/4] blk-timeout: add BLK_EH_SCHEDULED return code Hannes Reinecke
@ 2013-06-06 16:24   ` Jörn Engel
  0 siblings, 0 replies; 19+ messages in thread
From: Jörn Engel @ 2013-06-06 16:24 UTC (permalink / raw)
  To: Hannes Reinecke
  Cc: James Bottomley, linux-scsi, Ewan Milne, James Smart,
	Ren Mingxin, Roland Dreier, Bryn Reeves, Christoph Hellwig

On Thu, 6 June 2013 11:43:53 +0200, Hannes Reinecke wrote:
> 
> Add a 'BLK_EH_SCHEDULED' return code for blk-timeout to indicate
> that a delayed error recovery has been initiated.
> 
> Signed-off-by: Hannes Reinecke <hare@suse.de>

Since the patch I tested diverged from your patches 2-4, this is only
Acked-by: Joern Enge <joern@logfs.org>

> ---
>  drivers/scsi/scsi_error.c | 3 +++
>  include/linux/blkdev.h    | 1 +
>  2 files changed, 4 insertions(+)
> 
> diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
> index f43de1e..96b4bb6 100644
> --- a/drivers/scsi/scsi_error.c
> +++ b/drivers/scsi/scsi_error.c
> @@ -146,6 +146,9 @@ enum blk_eh_timer_return scsi_times_out(struct request *req)
>  		rtn = host->hostt->eh_timed_out(scmd);
>  
>  	scmd->result |= DID_TIME_OUT << 16;
> +	/* Check for delayed EH scheduling */
> +	if (rtn == BLK_EH_SCHEDULED)
> +		return BLK_EH_NOT_HANDLED;
>  
>  	if (unlikely(rtn == BLK_EH_NOT_HANDLED &&
>  		     !scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD)))
> diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
> index 2fdb4a4..d846e2b 100644
> --- a/include/linux/blkdev.h
> +++ b/include/linux/blkdev.h
> @@ -238,6 +238,7 @@ enum blk_eh_timer_return {
>  	BLK_EH_NOT_HANDLED,
>  	BLK_EH_HANDLED,
>  	BLK_EH_RESET_TIMER,
> +	BLK_EH_SCHEDULED,
>  };
>  
>  typedef enum blk_eh_timer_return (rq_timed_out_fn)(struct request *);
> -- 
> 1.7.12.4
> 

Jörn

--
Geld macht nicht glücklich.
Glück macht nicht satt.
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 3/4] scsi: improved eh timeout handler
  2013-06-06 20:39     ` Hannes Reinecke
@ 2013-06-06 20:28       ` Jörn Engel
  2013-06-07  6:25         ` Ren Mingxin
  0 siblings, 1 reply; 19+ messages in thread
From: Jörn Engel @ 2013-06-06 20:28 UTC (permalink / raw)
  To: Hannes Reinecke
  Cc: James Bottomley, linux-scsi, Ewan Milne, James Smart,
	Ren Mingxin, Roland Dreier, Bryn Reeves, Christoph Hellwig

On Thu, 6 June 2013 22:39:14 +0200, Hannes Reinecke wrote:
> 
> >>+		spin_unlock_irqrestore(&sdev->list_lock, flags);
> >>+		SCSI_LOG_ERROR_RECOVERY(3,
> >>+			scmd_printk(KERN_INFO, scmd,
> >>+				    "aborting command %p\n", scmd));
> >>+		rtn = scsi_try_to_abort_cmd(shost->hostt, scmd);
> >>+		if (rtn == SUCCESS || rtn == FAST_IO_FAIL) {
> >>+			if (((scmd->request->cmd_flags & REQ_FAILFAST_DEV) ||
> >
> >Am I being stupid again or should this be negated?
> >
> Knowing you I would think the former; where do you see the negation?

If REQ_FAILFAST_DEV is set, this runs scsi_queue_insert(), which I
would expect it should run scsi_finish_command().

> >>+			     (scmd->request->cmd_type == REQ_TYPE_BLOCK_PC)) &&
> >>+			    (++scmd->retries <= scmd->allowed)) {
> >>+				SCSI_LOG_ERROR_RECOVERY(3,
> >>+					scmd_printk(KERN_WARNING, scmd,
> >>+						    "retry aborted command\n"));
> >>+
> >>+				scsi_queue_insert(scmd, SCSI_MLQUEUE_EH_RETRY);
> >>+			} else {
> >>+				SCSI_LOG_ERROR_RECOVERY(3,
> >>+					scmd_printk(KERN_WARNING, scmd,
> >>+						    "fast fail aborted command\n"));
> >>+				scmd->result |= DID_TRANSPORT_FAILFAST << 16;
> >>+				scsi_finish_command(scmd);
> >>+			}
> >>+		} else {
> >>+			if (!scsi_eh_scmd_add(scmd, 0)) {
> >>+				SCSI_LOG_ERROR_RECOVERY(3,
> >>+					scmd_printk(KERN_WARNING, scmd,
> >>+						    "terminate aborted command\n"));
> >>+				scmd->result |= DID_TIME_OUT << 16;
> >>+				scsi_finish_command(scmd);
> >>+			}
> >>+		}
> >>+		spin_lock_irqsave(&sdev->list_lock, flags);
> >>+	}
> >>+	spin_unlock_irqrestore(&sdev->list_lock, flags);
...
> >>@@ -231,6 +231,7 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget,
> >>  	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
> >>  	extern void scsi_evt_thread(struct work_struct *work);
> >>  	extern void scsi_requeue_run_queue(struct work_struct *work);
> >>+	extern void scsi_eh_abort_handler(struct work_struct *work);
> >
> >Function declarations in a .c file?  Ick!
> >
> Sing-along: We didn't start the fire, it always burning ...
> 
> I'm just following precedents here.

Fair enough.

Jörn

--
The cost of changing business rules is much more expensive for software
than for a secretary.
-- unknown
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 3/4] scsi: improved eh timeout handler
  2013-06-06 16:23   ` Jörn Engel
@ 2013-06-06 20:39     ` Hannes Reinecke
  2013-06-06 20:28       ` Jörn Engel
  0 siblings, 1 reply; 19+ messages in thread
From: Hannes Reinecke @ 2013-06-06 20:39 UTC (permalink / raw)
  To: Jörn Engel
  Cc: James Bottomley, linux-scsi, Ewan Milne, James Smart,
	Ren Mingxin, Roland Dreier, Bryn Reeves, Christoph Hellwig

On 06/06/2013 06:23 PM, Jörn Engel wrote:
> On Thu, 6 June 2013 11:43:54 +0200, Hannes Reinecke wrote:
>>
>> 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 'eh_abort_handler'.
>>
>> If the 'eh_abort_handler' returns SUCCESS or FAST_IO_FAIL the
>> command will be retried if possible. If no retries are allowed
>> the command will be returned immediately, as we have to assume
>> the TMF succeeded and the command is completed with the LLDD.
>> For any other return code from 'eh_abort_handler' the command
>> will be pushed onto the existing SCSI EH handler, or aborted
>> with DID_TIME_OUT if that fails.
>>
>> Signed-off-by: Hannes Reinecke <hare@suse.de>
>> ---
>>   drivers/scsi/scsi_error.c        | 79 ++++++++++++++++++++++++++++++++++++++++
>>   drivers/scsi/scsi_scan.c         |  3 ++
>>   drivers/scsi/scsi_transport_fc.c |  3 +-
>>   include/scsi/scsi_cmnd.h         |  1 +
>>   include/scsi/scsi_device.h       |  2 +
>>   5 files changed, 87 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
>> index 96b4bb6..0a6b21c 100644
>> --- a/drivers/scsi/scsi_error.c
>> +++ b/drivers/scsi/scsi_error.c
>> @@ -55,6 +55,8 @@ static void scsi_eh_done(struct scsi_cmnd *scmd);
>>   #define HOST_RESET_SETTLE_TIME  (10)
>>
>>   static int scsi_eh_try_stu(struct scsi_cmnd *scmd);
>> +static int scsi_try_to_abort_cmd(struct scsi_host_template *hostt,
>> +				 struct scsi_cmnd *scmd);
>>
>>   /* called with shost->host_lock held */
>>   void scsi_eh_wakeup(struct Scsi_Host *shost)
>> @@ -90,6 +92,83 @@ void scsi_schedule_eh(struct Scsi_Host *shost)
>>   EXPORT_SYMBOL_GPL(scsi_schedule_eh);
>>
>>   /**
>> + * scsi_eh_abort_handler - Handle command aborts
>> + * @work:	sdev on which commands should be aborted.
>> + */
>> +void
>> +scsi_eh_abort_handler(struct work_struct *work)
>> +{
>> +	struct scsi_device *sdev =
>> +		container_of(work, struct scsi_device, abort_work);
>> +	struct Scsi_Host *shost = sdev->host;
>> +	struct scsi_cmnd *scmd, *tmp;
>> +	unsigned long flags;
>> +	int rtn;
>> +
>> +	spin_lock_irqsave(&sdev->list_lock, flags);
>> +	list_for_each_entry_safe(scmd, tmp, &sdev->eh_abort_list, eh_entry) {
>> +		list_del_init(&scmd->eh_entry);
>
> The _init bit is not needed.  I prefer list_del, as the poisoning
> sometimes helps catching bugs.
>
Strictly speaking not.
But then I'm on the good side of programming who prefer to handle issues 
gracefully, not aborting with a kernel oops :-)

>> +		spin_unlock_irqrestore(&sdev->list_lock, flags);
>> +		SCSI_LOG_ERROR_RECOVERY(3,
>> +			scmd_printk(KERN_INFO, scmd,
>> +				    "aborting command %p\n", scmd));
>> +		rtn = scsi_try_to_abort_cmd(shost->hostt, scmd);
>> +		if (rtn == SUCCESS || rtn == FAST_IO_FAIL) {
>> +			if (((scmd->request->cmd_flags & REQ_FAILFAST_DEV) ||
>
> Am I being stupid again or should this be negated?
>
Knowing you I would think the former; where do you see the negation?

>> +			     (scmd->request->cmd_type == REQ_TYPE_BLOCK_PC)) &&
>> +			    (++scmd->retries <= scmd->allowed)) {
>> +				SCSI_LOG_ERROR_RECOVERY(3,
>> +					scmd_printk(KERN_WARNING, scmd,
>> +						    "retry aborted command\n"));
>> +
>> +				scsi_queue_insert(scmd, SCSI_MLQUEUE_EH_RETRY);
>> +			} else {
>> +				SCSI_LOG_ERROR_RECOVERY(3,
>> +					scmd_printk(KERN_WARNING, scmd,
>> +						    "fast fail aborted command\n"));
>> +				scmd->result |= DID_TRANSPORT_FAILFAST << 16;
>> +				scsi_finish_command(scmd);
>> +			}
>> +		} else {
>> +			if (!scsi_eh_scmd_add(scmd, 0)) {
>> +				SCSI_LOG_ERROR_RECOVERY(3,
>> +					scmd_printk(KERN_WARNING, scmd,
>> +						    "terminate aborted command\n"));
>> +				scmd->result |= DID_TIME_OUT << 16;
>> +				scsi_finish_command(scmd);
>> +			}
>> +		}
>> +		spin_lock_irqsave(&sdev->list_lock, flags);
>> +	}
>> +	spin_unlock_irqrestore(&sdev->list_lock, flags);
>> +}
>> +
>> +/**
>> + * scsi_abort_command - schedule a command abort
>> + * @scmd:	scmd to abort.
>> + *
>> + * We only need to abort commands after a command timeout
>> + */
>> +void
>> +scsi_abort_command(struct scsi_cmnd *scmd)
>> +{
>> +	unsigned long flags;
>> +	int kick_worker = 0;
>> +	struct scsi_device *sdev = scmd->device;
>> +
>> +	spin_lock_irqsave(&sdev->list_lock, flags);
>> +	if (list_empty(&sdev->eh_abort_list))
>> +		kick_worker = 1;
>> +	list_add(&scmd->eh_entry, &sdev->eh_abort_list);
>> +	SCSI_LOG_ERROR_RECOVERY(3,
>> +		scmd_printk(KERN_INFO, scmd, "adding to eh_abort_list\n"));
>
> The printk can be moved outside the spinlock.  Who knows, maybe this
> will become a scalability bottleneck before the millenium is over.
>
That's a debugging thing only. But yeah, you're right, it should move 
out of the spinlock.

>> +	spin_unlock_irqrestore(&sdev->list_lock, flags);
>> +	if (kick_worker)
>> +		schedule_work(&sdev->abort_work);
>> +}
>> +EXPORT_SYMBOL_GPL(scsi_abort_command);
>> +
>> +/**
>>    * scsi_eh_scmd_add - add scsi cmd to error handling.
>>    * @scmd:	scmd to run eh on.
>>    * @eh_flag:	optional SCSI_EH flag.
>> diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
>> index 3e58b22..f9cc6fc 100644
>> --- a/drivers/scsi/scsi_scan.c
>> +++ b/drivers/scsi/scsi_scan.c
>> @@ -231,6 +231,7 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget,
>>   	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
>>   	extern void scsi_evt_thread(struct work_struct *work);
>>   	extern void scsi_requeue_run_queue(struct work_struct *work);
>> +	extern void scsi_eh_abort_handler(struct work_struct *work);
>
> Function declarations in a .c file?  Ick!
>
Sing-along: We didn't start the fire, it always burning ...

I'm just following precedents here.

Thanks for the review.

Cheers,

Hannes
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 3/4] scsi: improved eh timeout handler
  2013-06-06 20:28       ` Jörn Engel
@ 2013-06-07  6:25         ` Ren Mingxin
  2013-06-07  6:42           ` Hannes Reinecke
  0 siblings, 1 reply; 19+ messages in thread
From: Ren Mingxin @ 2013-06-07  6:25 UTC (permalink / raw)
  To: Hannes Reinecke
  Cc: Jörn Engel, James Bottomley, linux-scsi, Ewan Milne,
	James Smart, Roland Dreier, Bryn Reeves, Christoph Hellwig

Hi, Hannes:

On 06/07/2013 04:28 AM, Jörn Engel wrote:
> On Thu, 6 June 2013 22:39:14 +0200, Hannes Reinecke wrote:
>>>> +		spin_unlock_irqrestore(&sdev->list_lock, flags);
>>>> +		SCSI_LOG_ERROR_RECOVERY(3,
>>>> +			scmd_printk(KERN_INFO, scmd,
>>>> +				    "aborting command %p\n", scmd));
>>>> +		rtn = scsi_try_to_abort_cmd(shost->hostt, scmd);
>>>> +		if (rtn == SUCCESS || rtn == FAST_IO_FAIL) {
>>>> +			if (((scmd->request->cmd_flags&  REQ_FAILFAST_DEV) ||
>>>
>>> Am I being stupid again or should this be negated?
>>>
>> Knowing you I would think the former; where do you see the negation?
>
> If REQ_FAILFAST_DEV is set, this runs scsi_queue_insert(), which I
> would expect it should run scsi_finish_command().

I also think (scmd->request->cmd_flags & REQ_FAILFAST_DEV) and
(scmd->request->cmd_type == REQ_TYPE_BLOCK_PC) should be negated.
I'm confused why not use !scsi_noretry_cmd(scmd) directly as your
former patch here?

>>>> +			     (scmd->request->cmd_type == REQ_TYPE_BLOCK_PC))&&
>>>> +			    (++scmd->retries<= scmd->allowed)) {
>>>> +				SCSI_LOG_ERROR_RECOVERY(3,
>>>> +					scmd_printk(KERN_WARNING, scmd,
>>>> +						    "retry aborted command\n"));
>>>> +
>>>> +				scsi_queue_insert(scmd, SCSI_MLQUEUE_EH_RETRY);
>>>> +			} else {
>>>> +				SCSI_LOG_ERROR_RECOVERY(3,
>>>> +					scmd_printk(KERN_WARNING, scmd,
>>>> +						    "fast fail aborted command\n"));
>>>> +				scmd->result |= DID_TRANSPORT_FAILFAST<<  16;
>>>> +				scsi_finish_command(scmd);
>>>> +			}
>>>> +		} else {
>>>> +			if (!scsi_eh_scmd_add(scmd, 0)) {
>>>> +				SCSI_LOG_ERROR_RECOVERY(3,
>>>> +					scmd_printk(KERN_WARNING, scmd,
>>>> +						    "terminate aborted command\n"));
>>>> +				scmd->result |= DID_TIME_OUT<<  16;
>>>> +				scsi_finish_command(scmd);
>>>> +			}
>>>> +		}
>>>> +		spin_lock_irqsave(&sdev->list_lock, flags);
>>>> +	}
>>>> +	spin_unlock_irqrestore(&sdev->list_lock, flags);
> ...
>>>> +/**
>>>> + * scsi_abort_command - schedule a command abort
>>>> + * @scmd:	scmd to abort.
>>>> + *
>>>> + * We only need to abort commands after a command timeout
>>>> + */
>>>> +void
>>>> +scsi_abort_command(struct scsi_cmnd *scmd)
>>>> +{
>>>> +	unsigned long flags;
>>>> +	int kick_worker = 0;
>>>> +	struct scsi_device *sdev = scmd->device;
>>>> +
>>>> +	spin_lock_irqsave(&sdev->list_lock, flags);
>>>> +	if (list_empty(&sdev->eh_abort_list))
>>>> +		kick_worker = 1;
>>>> +	list_add(&scmd->eh_entry,&sdev->eh_abort_list);
>>>> +	SCSI_LOG_ERROR_RECOVERY(3,
>>>> +		scmd_printk(KERN_INFO, scmd, "adding to eh_abort_list\n"));
>>>> +	spin_unlock_irqrestore(&sdev->list_lock, flags);
>>>> +	if (kick_worker)
>>>> +		schedule_work(&sdev->abort_work);
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(scsi_abort_command);

Should the name of function above be more ideographic/understandable?
For example, scsi_abort_scmd_add? I was bewildered among functions
named scsi_abort_eh_cmnd, scsi_eh_abort_cmds...

Thanks,
Ren
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 3/4] scsi: improved eh timeout handler
  2013-06-07  6:25         ` Ren Mingxin
@ 2013-06-07  6:42           ` Hannes Reinecke
  0 siblings, 0 replies; 19+ messages in thread
From: Hannes Reinecke @ 2013-06-07  6:42 UTC (permalink / raw)
  To: Ren Mingxin
  Cc: Jörn Engel, James Bottomley, linux-scsi, Ewan Milne,
	James Smart, Roland Dreier, Bryn Reeves, Christoph Hellwig

On 06/07/2013 08:25 AM, Ren Mingxin wrote:
> Hi, Hannes:
> 
> On 06/07/2013 04:28 AM, Jörn Engel wrote:
>> On Thu, 6 June 2013 22:39:14 +0200, Hannes Reinecke wrote:
>>>>> +        spin_unlock_irqrestore(&sdev->list_lock, flags);
>>>>> +        SCSI_LOG_ERROR_RECOVERY(3,
>>>>> +            scmd_printk(KERN_INFO, scmd,
>>>>> +                    "aborting command %p\n", scmd));
>>>>> +        rtn = scsi_try_to_abort_cmd(shost->hostt, scmd);
>>>>> +        if (rtn == SUCCESS || rtn == FAST_IO_FAIL) {
>>>>> +            if (((scmd->request->cmd_flags&  REQ_FAILFAST_DEV) ||
>>>>
>>>> Am I being stupid again or should this be negated?
>>>>
>>> Knowing you I would think the former; where do you see the negation?
>>
>> If REQ_FAILFAST_DEV is set, this runs scsi_queue_insert(), which I
>> would expect it should run scsi_finish_command().
> 
> I also think (scmd->request->cmd_flags & REQ_FAILFAST_DEV) and
> (scmd->request->cmd_type == REQ_TYPE_BLOCK_PC) should be negated.
Bummer. You are correct.

So far I've only encountered the 'BLOCK_PC' condition, which worked.

> I'm confused why not use !scsi_noretry_cmd(scmd) directly as your
> former patch here?
> 
Hehe. I've fallen into the trap myself.
scsi_noretry_cmd() only works if you have a status for the command,
and will only evaluate REQ_FAILFAST_DEV or BLOCK_PC if the command
has a CHECK CONDITION status.
As this is the timeout handler we do _not_ have any status, so
scsi_noretry_cmd() will always tell us that the command should be
retried.

>>>>> +                 (scmd->request->cmd_type ==
>>>>> REQ_TYPE_BLOCK_PC))&&
>>>>> +                (++scmd->retries<= scmd->allowed)) {
>>>>> +                SCSI_LOG_ERROR_RECOVERY(3,
>>>>> +                    scmd_printk(KERN_WARNING, scmd,
>>>>> +                            "retry aborted command\n"));
>>>>> +
>>>>> +                scsi_queue_insert(scmd, SCSI_MLQUEUE_EH_RETRY);
>>>>> +            } else {
>>>>> +                SCSI_LOG_ERROR_RECOVERY(3,
>>>>> +                    scmd_printk(KERN_WARNING, scmd,
>>>>> +                            "fast fail aborted command\n"));
>>>>> +                scmd->result |= DID_TRANSPORT_FAILFAST<<  16;
>>>>> +                scsi_finish_command(scmd);
>>>>> +            }
>>>>> +        } else {
>>>>> +            if (!scsi_eh_scmd_add(scmd, 0)) {
>>>>> +                SCSI_LOG_ERROR_RECOVERY(3,
>>>>> +                    scmd_printk(KERN_WARNING, scmd,
>>>>> +                            "terminate aborted command\n"));
>>>>> +                scmd->result |= DID_TIME_OUT<<  16;
>>>>> +                scsi_finish_command(scmd);
>>>>> +            }
>>>>> +        }
>>>>> +        spin_lock_irqsave(&sdev->list_lock, flags);
>>>>> +    }
>>>>> +    spin_unlock_irqrestore(&sdev->list_lock, flags);
>> ...
>>>>> +/**
>>>>> + * scsi_abort_command - schedule a command abort
>>>>> + * @scmd:    scmd to abort.
>>>>> + *
>>>>> + * We only need to abort commands after a command timeout
>>>>> + */
>>>>> +void
>>>>> +scsi_abort_command(struct scsi_cmnd *scmd)
>>>>> +{
>>>>> +    unsigned long flags;
>>>>> +    int kick_worker = 0;
>>>>> +    struct scsi_device *sdev = scmd->device;
>>>>> +
>>>>> +    spin_lock_irqsave(&sdev->list_lock, flags);
>>>>> +    if (list_empty(&sdev->eh_abort_list))
>>>>> +        kick_worker = 1;
>>>>> +    list_add(&scmd->eh_entry,&sdev->eh_abort_list);
>>>>> +    SCSI_LOG_ERROR_RECOVERY(3,
>>>>> +        scmd_printk(KERN_INFO, scmd, "adding to
>>>>> eh_abort_list\n"));
>>>>> +    spin_unlock_irqrestore(&sdev->list_lock, flags);
>>>>> +    if (kick_worker)
>>>>> +        schedule_work(&sdev->abort_work);
>>>>> +}
>>>>> +EXPORT_SYMBOL_GPL(scsi_abort_command);
> 
> Should the name of function above be more ideographic/understandable?
> For example, scsi_abort_scmd_add? I was bewildered among functions
> named scsi_abort_eh_cmnd, scsi_eh_abort_cmds...
> 
scsi_abort_scmd_add()? That's even more confusing.

scsi_abort_command() does exactly this, it aborts a command.
Yeah, the individual wrapper/callback function naming might be
improved, but this is the one function which actually does what it
advertises ...

Cheers,

Hannes
-- 
Dr. Hannes Reinecke		      zSeries & Storage
hare@suse.de			      +49 911 74053 688
SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg
GF: J. Hawn, J. Guild, F. Imendörffer, HRB 16746 (AG Nürnberg)
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 0/4] New SCSI command timeout handler
  2013-06-06  9:43 [PATCH 0/4] New SCSI command timeout handler Hannes Reinecke
                   ` (3 preceding siblings ...)
  2013-06-06  9:43 ` [PATCH 4/4] virtio_scsi: use " Hannes Reinecke
@ 2013-06-07  6:54 ` Ren Mingxin
  2013-06-07  7:31   ` Hannes Reinecke
  2013-06-07 16:02   ` Jörn Engel
  4 siblings, 2 replies; 19+ messages in thread
From: Ren Mingxin @ 2013-06-07  6:54 UTC (permalink / raw)
  To: Hannes Reinecke
  Cc: James Bottomley, linux-scsi, Joern Engel, Ewan Milne,
	James Smart, Roland Dreier, Bryn Reeves, Christoph Hellwig

Hi, Hannes:

On 06/06/2013 05:43 PM, Hannes Reinecke wrote:
> this is the first step towards a new non-blocking
> error handler. This patch implements a new command
> timeout handler which will be sending command aborts
> inline without engaging SCSI EH.
>
> In addition the commands will be returned directly
> if the command abort succeeded, cutting down recovery
> times dramatically.
>
> With the original scsi error recovery I got:
> # time dd if=/dev/zero of=/mnt/test.blk bs=512 count=2048 oflag=sync
> 2048+0 records in
> 2048+0 records out
> 1048576 bytes (1.0 MB) copied, 3.72732 s, 281 kB/s
>
> real	2m14.475s
> user	0m0.000s
> sys	0m0.104s
>
> with this patchset I got:
> # time dd if=/dev/zero of=/mnt/test.blk bs=512 count=2048 oflag=sync
> 2048+0 records in
> 2048+0 records out
> 1048576 bytes (1.0 MB) copied, 31.5151 s, 33.3 kB/s
>
> real	0m31.519s
> user	0m0.000s
> sys	0m0.088s
>
> Test was to disable RSCN on the target port, disable the
> target port, and then start the 'dd' command as indicated.
>
> As a proof-of-concept I've also enabled the new timeout
> handler for virtio, so that things can be tested out
> more easily.

So this 31.5s is tested on virtio disks, right? Much faster than your
former test via fc.

This approach may not work for some LLDDs as you said, but I wonder
whether SAS is applicable(whether there will be later patches for
SAS).

Thanks,
Ren


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 0/4] New SCSI command timeout handler
  2013-06-07  6:54 ` [PATCH 0/4] New SCSI command " Ren Mingxin
@ 2013-06-07  7:31   ` Hannes Reinecke
  2013-06-07 16:02   ` Jörn Engel
  1 sibling, 0 replies; 19+ messages in thread
From: Hannes Reinecke @ 2013-06-07  7:31 UTC (permalink / raw)
  To: Ren Mingxin
  Cc: James Bottomley, linux-scsi, Joern Engel, Ewan Milne,
	James Smart, Roland Dreier, Bryn Reeves, Christoph Hellwig

On 06/07/2013 08:54 AM, Ren Mingxin wrote:
> Hi, Hannes:
> 
> On 06/06/2013 05:43 PM, Hannes Reinecke wrote:
>> this is the first step towards a new non-blocking
>> error handler. This patch implements a new command
>> timeout handler which will be sending command aborts
>> inline without engaging SCSI EH.
>>
>> In addition the commands will be returned directly
>> if the command abort succeeded, cutting down recovery
>> times dramatically.
>>
>> With the original scsi error recovery I got:
>> # time dd if=/dev/zero of=/mnt/test.blk bs=512 count=2048 oflag=sync
>> 2048+0 records in
>> 2048+0 records out
>> 1048576 bytes (1.0 MB) copied, 3.72732 s, 281 kB/s
>>
>> real    2m14.475s
>> user    0m0.000s
>> sys    0m0.104s
>>
>> with this patchset I got:
>> # time dd if=/dev/zero of=/mnt/test.blk bs=512 count=2048 oflag=sync
>> 2048+0 records in
>> 2048+0 records out
>> 1048576 bytes (1.0 MB) copied, 31.5151 s, 33.3 kB/s
>>
>> real    0m31.519s
>> user    0m0.000s
>> sys    0m0.088s
>>
>> Test was to disable RSCN on the target port, disable the
>> target port, and then start the 'dd' command as indicated.
>>
>> As a proof-of-concept I've also enabled the new timeout
>> handler for virtio, so that things can be tested out
>> more easily.
> 
> So this 31.5s is tested on virtio disks, right? Much faster than your
> former test via fc.
> 
No. This was done on HP Proliant /QLogic FC running against HP P2000.

There is a reason why I posted this new patchset :-)

(The old patchset had a bug where it wouldn't abort the TUR send by
multipaths path-checker. Which were causing delays during
switchover. This version now does terminate them properly)
(Incidentally, it was the non-working scsi_noretry_cmd() which
caused this bug :-)

> This approach may not work for some LLDDs as you said, but I wonder
> whether SAS is applicable(whether there will be later patches for
> SAS).
> 
SAS should be applicable for this, too. I'll check.

Cheers,

Hannes
-- 
Dr. Hannes Reinecke		      zSeries & Storage
hare@suse.de			      +49 911 74053 688
SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg
GF: J. Hawn, J. Guild, F. Imendörffer, HRB 16746 (AG Nürnberg)
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 0/4] New SCSI command timeout handler
  2013-06-07  6:54 ` [PATCH 0/4] New SCSI command " Ren Mingxin
  2013-06-07  7:31   ` Hannes Reinecke
@ 2013-06-07 16:02   ` Jörn Engel
  1 sibling, 0 replies; 19+ messages in thread
From: Jörn Engel @ 2013-06-07 16:02 UTC (permalink / raw)
  To: Ren Mingxin
  Cc: Hannes Reinecke, James Bottomley, linux-scsi, Ewan Milne,
	James Smart, Roland Dreier, Bryn Reeves, Christoph Hellwig

On Fri, 7 June 2013 14:54:16 +0800, Ren Mingxin wrote:
> On 06/06/2013 05:43 PM, Hannes Reinecke wrote:
> 
> This approach may not work for some LLDDs as you said, but I wonder
> whether SAS is applicable(whether there will be later patches for
> SAS).

We have tested a derived patch on SAS and it did improve things.

Jörn

--
You ain't got no problem, Jules. I'm on the motherfucker. Go back in
there, chill them niggers out and wait for the Wolf, who should be
coming directly.
-- Marsellus Wallace
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 3/4] scsi: improved eh timeout handler
  2013-06-06  9:43 ` [PATCH 3/4] scsi: improved eh timeout handler Hannes Reinecke
  2013-06-06 16:23   ` Jörn Engel
@ 2013-06-07 16:21   ` Jörn Engel
  2013-06-10  0:12   ` Baruch Even
  2 siblings, 0 replies; 19+ messages in thread
From: Jörn Engel @ 2013-06-07 16:21 UTC (permalink / raw)
  To: Hannes Reinecke
  Cc: James Bottomley, linux-scsi, Ewan Milne, James Smart,
	Ren Mingxin, Roland Dreier, Bryn Reeves, Christoph Hellwig

On Thu, 6 June 2013 11:43:54 +0200, Hannes Reinecke wrote:
> 
> +	spin_lock_irqsave(&sdev->list_lock, flags);
> +	list_for_each_entry_safe(scmd, tmp, &sdev->eh_abort_list, eh_entry) {
> +		list_del_init(&scmd->eh_entry);
> +		spin_unlock_irqrestore(&sdev->list_lock, flags);

I never liked the list_for_each_entry_safe() loop but until this
morning couldn't really say why.  The problem is that it can finish
with commands left on the list.  And since your kick_worker checks for
list_empty, there wouldn't be any more aborts.

Thread1		list state		Thread2
		head->1->2->head
spin_lock_irqsave();
scmd = 1; tmp = 2;
list_del_init(scmd);
spin_unlock_irqrestore();
		head->2->head
					spin_lock_irqsave();
					kick_worker = 0;
					list_add();
					spin_unlock_irqrestore();
		head->3->2->head	
spin_lock_irqsave();
scmd = 2; tmp = head;
list_del_init(scmd);
spin_unlock_irqrestore();
		head->3->head

And now that I think about nasty races, you could also do
schedule_work() when the list is empty, but the worker thread is still
running.  I've had to debug similar cases and the approach is to waste
a day or five, despair and eventually get lucky half a year later when
you happen to notice the race.  Rare random crashes in the kworker
code a not my favorite sight.

> +void
> +scsi_abort_command(struct scsi_cmnd *scmd)
> +{
> +	unsigned long flags;
> +	int kick_worker = 0;
> +	struct scsi_device *sdev = scmd->device;
> +
> +	spin_lock_irqsave(&sdev->list_lock, flags);
> +	if (list_empty(&sdev->eh_abort_list))
> +		kick_worker = 1;
> +	list_add(&scmd->eh_entry, &sdev->eh_abort_list);
> +	SCSI_LOG_ERROR_RECOVERY(3,
> +		scmd_printk(KERN_INFO, scmd, "adding to eh_abort_list\n"));
> +	spin_unlock_irqrestore(&sdev->list_lock, flags);
> +	if (kick_worker)
> +		schedule_work(&sdev->abort_work);
> +}
> +EXPORT_SYMBOL_GPL(scsi_abort_command);

Jörn

--
Time? What's that? Time is only worth what you do with it.
-- Theo de Raadt
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 3/4] scsi: improved eh timeout handler
  2013-06-06  9:43 ` [PATCH 3/4] scsi: improved eh timeout handler Hannes Reinecke
  2013-06-06 16:23   ` Jörn Engel
  2013-06-07 16:21   ` Jörn Engel
@ 2013-06-10  0:12   ` Baruch Even
  2013-06-10  5:48     ` Hannes Reinecke
  2 siblings, 1 reply; 19+ messages in thread
From: Baruch Even @ 2013-06-10  0:12 UTC (permalink / raw)
  To: Hannes Reinecke
  Cc: James Bottomley, linux-scsi, Joern Engel, Ewan Milne,
	James Smart, Ren Mingxin, Roland Dreier, Bryn Reeves,
	Christoph Hellwig

On Thu, Jun 6, 2013 at 12:43 PM, Hannes Reinecke <hare@suse.de> wrote:
> 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 'eh_abort_handler'.
>
> If the 'eh_abort_handler' returns SUCCESS or FAST_IO_FAIL the
> command will be retried if possible. If no retries are allowed
> the command will be returned immediately, as we have to assume
> the TMF succeeded and the command is completed with the LLDD.
> For any other return code from 'eh_abort_handler' the command
> will be pushed onto the existing SCSI EH handler, or aborted
> with DID_TIME_OUT if that fails.
>

Why would you do a retry at this low level? We already have a bad path,
the IO may already be rerouted through another path and continuing with this
IO here may interfere with the multipath handling.


Baruch

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 3/4] scsi: improved eh timeout handler
  2013-06-10  0:12   ` Baruch Even
@ 2013-06-10  5:48     ` Hannes Reinecke
  0 siblings, 0 replies; 19+ messages in thread
From: Hannes Reinecke @ 2013-06-10  5:48 UTC (permalink / raw)
  To: Baruch Even
  Cc: James Bottomley, linux-scsi, Joern Engel, Ewan Milne,
	James Smart, Ren Mingxin, Roland Dreier, Bryn Reeves,
	Christoph Hellwig

On 06/10/2013 02:12 AM, Baruch Even wrote:
> On Thu, Jun 6, 2013 at 12:43 PM, Hannes Reinecke <hare@suse.de> wrote:
>> 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 'eh_abort_handler'.
>>
>> If the 'eh_abort_handler' returns SUCCESS or FAST_IO_FAIL the
>> command will be retried if possible. If no retries are allowed
>> the command will be returned immediately, as we have to assume
>> the TMF succeeded and the command is completed with the LLDD.
>> For any other return code from 'eh_abort_handler' the command
>> will be pushed onto the existing SCSI EH handler, or aborted
>> with DID_TIME_OUT if that fails.
>>
> 
> Why would you do a retry at this low level? We already have a bad path,
> the IO may already be rerouted through another path and continuing with this
> IO here may interfere with the multipath handling.
> 
For multipathing no retries would be set, so the command would
be returned immediately.

I checked.

Cheers,

Hannes
-- 
Dr. Hannes Reinecke		      zSeries & Storage
hare@suse.de			      +49 911 74053 688
SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg
GF: J. Hawn, J. Guild, F. Imendörffer, HRB 16746 (AG Nürnberg)
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 3/4] scsi: improved eh timeout handler
  2013-10-30  8:37 [PATCHv7 0/4] New EH " Hannes Reinecke
@ 2013-10-30  8:37 ` Hannes Reinecke
  0 siblings, 0 replies; 19+ messages in thread
From: Hannes Reinecke @ 2013-10-30  8:37 UTC (permalink / raw)
  To: James Bottomley
  Cc: Christoph Hellwig, linux-scsi, Ren Mingxin, Joern Engel,
	James Smart, Hannes Reinecke

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.

Signed-off-by: Hannes Reinecke <hare@suse.de>
Cc: Ren Mingxin <renmx@cn.fujitsu.com>
---
 drivers/scsi/scsi.c       |   9 +--
 drivers/scsi/scsi_error.c | 154 +++++++++++++++++++++++++++++++++++++++++-----
 drivers/scsi/scsi_priv.h  |   2 +
 include/scsi/scsi_cmnd.h  |   2 +
 include/scsi/scsi_host.h  |   5 ++
 5 files changed, 152 insertions(+), 20 deletions(-)

diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index fe0bcb1..d8afec8 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -297,6 +297,7 @@ struct scsi_cmnd *scsi_get_command(struct scsi_device *dev, gfp_t gfp_mask)
 
 		cmd->device = dev;
 		INIT_LIST_HEAD(&cmd->list);
+		INIT_DELAYED_WORK(&cmd->abort_work, scmd_eh_abort_handler);
 		spin_lock_irqsave(&dev->list_lock, flags);
 		list_add_tail(&cmd->list, &dev->cmd_list);
 		spin_unlock_irqrestore(&dev->list_lock, flags);
@@ -353,6 +354,8 @@ void scsi_put_command(struct scsi_cmnd *cmd)
 	list_del_init(&cmd->list);
 	spin_unlock_irqrestore(&cmd->device->list_lock, flags);
 
+	cancel_delayed_work(&cmd->abort_work);
+
 	__scsi_put_command(cmd->device->host, cmd, &sdev->sdev_gendev);
 }
 EXPORT_SYMBOL(scsi_put_command);
@@ -742,15 +745,13 @@ int scsi_dispatch_cmd(struct scsi_cmnd *cmd)
 }
 
 /**
- * scsi_done - Enqueue the finished SCSI command into the done queue.
+ * scsi_done - Invoke completion on finished SCSI command.
  * @cmd: The SCSI Command for which a low-level device driver (LLDD) gives
  * ownership back to SCSI Core -- i.e. the LLDD has finished with it.
  *
  * Description: This function is the mid-level's (SCSI Core) interrupt routine,
  * which regains ownership of the SCSI command (de facto) from a LLDD, and
- * enqueues the command to the done queue for further processing.
- *
- * This is the producer of the done queue who enqueues at the tail.
+ * calls blk_complete_request() for further processing.
  *
  * This function is interrupt context safe.
  */
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index af51a9c..8bd2615 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -53,6 +53,8 @@ static void scsi_eh_done(struct scsi_cmnd *scmd);
 #define HOST_RESET_SETTLE_TIME  (10)
 
 static int scsi_eh_try_stu(struct scsi_cmnd *scmd);
+static int scsi_try_to_abort_cmd(struct scsi_host_template *,
+				 struct scsi_cmnd *);
 
 /* called with shost->host_lock held */
 void scsi_eh_wakeup(struct Scsi_Host *shost)
@@ -100,6 +102,117 @@ static int scsi_host_eh_past_deadline(struct Scsi_Host *shost)
 }
 
 /**
+ * scmd_eh_abort_handler - Handle command aborts
+ * @work:	command to be aborted.
+ */
+void
+scmd_eh_abort_handler(struct work_struct *work)
+{
+	struct scsi_cmnd *scmd =
+		container_of(work, struct scsi_cmnd, abort_work.work);
+	struct scsi_device *sdev = scmd->device;
+	unsigned long flags;
+	int rtn;
+
+	spin_lock_irqsave(sdev->host->host_lock, flags);
+	if (scsi_host_eh_past_deadline(sdev->host)) {
+		spin_unlock_irqrestore(sdev->host->host_lock, flags);
+		SCSI_LOG_ERROR_RECOVERY(3,
+			scmd_printk(KERN_INFO, scmd,
+				    "scmd %p eh timeout, not aborting\n",
+				    scmd));
+	} else {
+		spin_unlock_irqrestore(sdev->host->host_lock, flags);
+		SCSI_LOG_ERROR_RECOVERY(3,
+			scmd_printk(KERN_INFO, scmd,
+				    "aborting command %p\n", scmd));
+		rtn = scsi_try_to_abort_cmd(sdev->host->hostt, scmd);
+		if (rtn == SUCCESS) {
+			scmd->result |= DID_TIME_OUT << 16;
+			if (!scsi_noretry_cmd(scmd) &&
+			    (++scmd->retries <= scmd->allowed)) {
+				SCSI_LOG_ERROR_RECOVERY(3,
+					scmd_printk(KERN_WARNING, scmd,
+						    "scmd %p retry "
+						    "aborted command\n", scmd));
+				scsi_queue_insert(scmd, SCSI_MLQUEUE_EH_RETRY);
+			} else {
+				SCSI_LOG_ERROR_RECOVERY(3,
+					scmd_printk(KERN_WARNING, scmd,
+						    "scmd %p finish "
+						    "aborted command\n", scmd));
+				scsi_finish_command(scmd);
+			}
+			return;
+		}
+		SCSI_LOG_ERROR_RECOVERY(3,
+			scmd_printk(KERN_INFO, scmd,
+				    "scmd %p abort failed, rtn %d\n",
+				    scmd, rtn));
+	}
+
+	if (scsi_eh_scmd_add(scmd, 0)) {
+		SCSI_LOG_ERROR_RECOVERY(3,
+			scmd_printk(KERN_WARNING, scmd,
+				    "scmd %p terminate "
+				    "aborted command\n", scmd));
+		scmd->result |= DID_TIME_OUT << 16;
+		scsi_finish_command(scmd);
+	}
+}
+
+/**
+ * scsi_abort_command - schedule a command abort
+ * @scmd:	scmd to abort.
+ *
+ * We only need to abort commands after a command timeout
+ */
+int
+scsi_abort_command(struct scsi_cmnd *scmd)
+{
+	struct scsi_device *sdev = scmd->device;
+	struct Scsi_Host *shost = sdev->host;
+	unsigned long flags;
+
+	if (scmd->eh_eflags & SCSI_EH_ABORT_SCHEDULED) {
+		/*
+		 * command abort timed out.
+		 */
+		SCSI_LOG_ERROR_RECOVERY(3,
+			scmd_printk(KERN_INFO, scmd,
+				    "scmd %p abort timeout\n", scmd));
+		cancel_delayed_work(&scmd->abort_work);
+		return FAILED;
+	}
+
+	/*
+	 * Do not try a command abort if
+	 * SCSI EH has already started.
+	 */
+	spin_lock_irqsave(shost->host_lock, flags);
+	if (scsi_host_in_recovery(shost)) {
+		spin_unlock_irqrestore(shost->host_lock, flags);
+		SCSI_LOG_ERROR_RECOVERY(3,
+			scmd_printk(KERN_INFO, scmd,
+				    "scmd %p not aborting, host in recovery\n",
+				    scmd));
+		return FAILED;
+	}
+
+	if (shost->eh_deadline && !shost->last_reset)
+		shost->last_reset = jiffies;
+	spin_unlock_irqrestore(shost->host_lock, flags);
+
+	scmd->eh_eflags |= SCSI_EH_ABORT_SCHEDULED;
+	SCSI_LOG_ERROR_RECOVERY(3,
+		scmd_printk(KERN_INFO, scmd,
+			    "scmd %p abort scheduled\n", scmd));
+	schedule_delayed_work(&scmd->abort_work, HZ / 100);
+	return SUCCESS;
+}
+EXPORT_SYMBOL_GPL(scsi_abort_command);
+
+/**
  * scsi_eh_scmd_add - add scsi cmd to error handling.
  * @scmd:	scmd to run eh on.
  * @eh_flag:	optional SCSI_EH flag.
@@ -125,6 +238,8 @@ int scsi_eh_scmd_add(struct scsi_cmnd *scmd, int eh_flag)
 		shost->last_reset = jiffies;
 
 	ret = 1;
+	if (scmd->eh_eflags & SCSI_EH_ABORT_SCHEDULED)
+		eh_flag &= ~SCSI_EH_CANCEL_CMD;
 	scmd->eh_eflags |= eh_flag;
 	list_add_tail(&scmd->eh_entry, &shost->eh_cmd_q);
 	shost->host_failed++;
@@ -161,9 +276,9 @@ enum blk_eh_timer_return scsi_times_out(struct request *req)
 	else if (host->hostt->eh_timed_out)
 		rtn = host->hostt->eh_timed_out(scmd);
 
-	/* Check for asynchronous command aborts */
-	if (rtn == BLK_EH_SCHEDULED)
-		return BLK_EH_NOT_HANDLED;
+	if (rtn == BLK_EH_NOT_HANDLED && !host->hostt->no_async_abort)
+		if (scsi_abort_command(scmd) == SUCCESS)
+			return BLK_EH_NOT_HANDLED;
 
 	scmd->result |= DID_TIME_OUT << 16;
 
@@ -1581,7 +1696,7 @@ static void scsi_eh_offline_sdevs(struct list_head *work_q,
 }
 
 /**
- * scsi_noretry_cmd - determinte if command should be failed fast
+ * scsi_noretry_cmd - determine if command should be failed fast
  * @scmd:	SCSI cmd to examine.
  */
 int scsi_noretry_cmd(struct scsi_cmnd *scmd)
@@ -1589,6 +1704,8 @@ int scsi_noretry_cmd(struct scsi_cmnd *scmd)
 	switch (host_byte(scmd->result)) {
 	case DID_OK:
 		break;
+	case DID_TIME_OUT:
+		goto check_type;
 	case DID_BUS_BUSY:
 		return (scmd->request->cmd_flags & REQ_FAILFAST_TRANSPORT);
 	case DID_PARITY:
@@ -1602,18 +1719,19 @@ int scsi_noretry_cmd(struct scsi_cmnd *scmd)
 		return (scmd->request->cmd_flags & REQ_FAILFAST_DRIVER);
 	}
 
-	switch (status_byte(scmd->result)) {
-	case CHECK_CONDITION:
-		/*
-		 * assume caller has checked sense and determinted
-		 * the check condition was retryable.
-		 */
-		if (scmd->request->cmd_flags & REQ_FAILFAST_DEV ||
-		    scmd->request->cmd_type == REQ_TYPE_BLOCK_PC)
-			return 1;
-	}
+	switch (status_byte(scmd->result) != CHECK_CONDITION)
+		return 0;
 
-	return 0;
+check_type:
+	/*
+	 * assume caller has checked sense and determined
+	 * the check condition was retryable.
+	 */
+	if (scmd->request->cmd_flags & REQ_FAILFAST_DEV ||
+	    scmd->request->cmd_type == REQ_TYPE_BLOCK_PC)
+		return 1;
+	else
+		return 0;
 }
 
 /**
@@ -1663,9 +1781,13 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd)
 		 * looks good.  drop through, and check the next byte.
 		 */
 		break;
+	case DID_ABORT:
+		if (scmd->eh_eflags & SCSI_EH_ABORT_SCHEDULED) {
+			scmd->result |= DID_TIME_OUT << 16;
+			return SUCCESS;
+		}
 	case DID_NO_CONNECT:
 	case DID_BAD_TARGET:
-	case DID_ABORT:
 		/*
 		 * note - this means that we just report the status back
 		 * to the top level driver, not that we actually think
diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h
index 8f9a0ca..f079a59 100644
--- a/drivers/scsi/scsi_priv.h
+++ b/drivers/scsi/scsi_priv.h
@@ -19,6 +19,7 @@ struct scsi_nl_hdr;
  * 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_SENSE_VALID(scmd) \
 	(((scmd)->sense_buffer[0] & 0x70) == 0x70)
@@ -66,6 +67,7 @@ extern int __init scsi_init_devinfo(void);
 extern void scsi_exit_devinfo(void);
 
 /* scsi_error.c */
+extern void scmd_eh_abort_handler(struct work_struct *work);
 extern enum blk_eh_timer_return scsi_times_out(struct request *req);
 extern int scsi_error_handler(void *host);
 extern int scsi_decide_disposition(struct scsi_cmnd *cmd);
diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h
index de5f5d8..e3137e3 100644
--- a/include/scsi/scsi_cmnd.h
+++ b/include/scsi/scsi_cmnd.h
@@ -55,6 +55,7 @@ struct scsi_cmnd {
 	struct scsi_device *device;
 	struct list_head list;  /* scsi_cmnd participates in queue lists */
 	struct list_head eh_entry; /* entry for the host eh_cmd_q */
+	struct delayed_work abort_work;
 	int eh_eflags;		/* Used by error handlr */
 
 	/*
@@ -144,6 +145,7 @@ extern void scsi_put_command(struct scsi_cmnd *);
 extern void __scsi_put_command(struct Scsi_Host *, struct scsi_cmnd *,
 			       struct device *);
 extern void scsi_finish_command(struct scsi_cmnd *cmd);
+extern enum blk_eh_timer_return scsi_abort_command(struct scsi_cmnd *cmd);
 
 extern void *scsi_kmap_atomic_sg(struct scatterlist *sg, int sg_count,
 				 size_t *offset, size_t *len);
diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h
index 5460849..85430ae 100644
--- a/include/scsi/scsi_host.h
+++ b/include/scsi/scsi_host.h
@@ -476,6 +476,11 @@ struct scsi_host_template {
 	unsigned ordered_tag:1;
 
 	/*
+	 * True if asynchronous aborts are not supported
+	 */
+	unsigned no_async_abort:1;
+
+	/*
 	 * Countdown for host blocking with no commands outstanding.
 	 */
 	unsigned int max_host_blocked;
-- 
1.8.1.4


^ permalink raw reply related	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2013-10-30  8:37 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-06-06  9:43 [PATCH 0/4] New SCSI command timeout handler Hannes Reinecke
2013-06-06  9:43 ` [PATCH 1/4] scsi: move initialization of scmd->eh_entry Hannes Reinecke
2013-06-06 16:14   ` Jörn Engel
2013-06-06  9:43 ` [PATCH 2/4] blk-timeout: add BLK_EH_SCHEDULED return code Hannes Reinecke
2013-06-06 16:24   ` Jörn Engel
2013-06-06  9:43 ` [PATCH 3/4] scsi: improved eh timeout handler Hannes Reinecke
2013-06-06 16:23   ` Jörn Engel
2013-06-06 20:39     ` Hannes Reinecke
2013-06-06 20:28       ` Jörn Engel
2013-06-07  6:25         ` Ren Mingxin
2013-06-07  6:42           ` Hannes Reinecke
2013-06-07 16:21   ` Jörn Engel
2013-06-10  0:12   ` Baruch Even
2013-06-10  5:48     ` Hannes Reinecke
2013-06-06  9:43 ` [PATCH 4/4] virtio_scsi: use " Hannes Reinecke
2013-06-07  6:54 ` [PATCH 0/4] New SCSI command " Ren Mingxin
2013-06-07  7:31   ` Hannes Reinecke
2013-06-07 16:02   ` Jörn Engel
2013-10-30  8:37 [PATCHv7 0/4] New EH " Hannes Reinecke
2013-10-30  8:37 ` [PATCH 3/4] scsi: improved eh " Hannes Reinecke

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.