Skip to contentSkip to conversation

New EH command timeout handler

PATCH Superseded Hannes Reinecke 4 participants 20 messages Sep 2, 2013 – Sep 4, 2013

Subsystems (5)

v5 of 11 Superseded Latest is v11 →

lore
Share & tools
lore
#0 lore raw

v5 cover New EH command timeout handler

Hi all,

this patchset implements a new SCSI EH command timeout handler which will be sending command aborts inline without actually engaging SCSI EH.
SCSI EH will only be invoked if command abort fails.

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

With the original SCSI EH I got:

# time dd if=/dev/zero of=/dev/dm-2 bs=4k count=4k oflag=direct
4096+0 records in
4096+0 records out
16777216 bytes (17 MB) copied, 142.652 s, 118 kB/s

real 2m22.657s
user 0m0.013s
sys 0m0.145s

With this patchset I got:

# time dd if=/dev/zero of=/dev/dm-2 bs=4k count=4k oflag=direct
4096+0 records in
4096+0 records out
16777216 bytes (17 MB) copied, 52.1579 s, 322 kB/s

real 0m52.163s
user 0m0.012s
sys 0m0.145s

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

Changes to the original version:
- Use a private list in scsi_eh_abort_handler to avoid list starvation (pointed out by Joern Engel)
- Terminate command aborts when the first abort fails
- Do not attempt command aborts if the host is already in recovery or if the device is removed.
- Flush abort workqueue if the device is removed.

Changes to v2:
- Removed eh_entry initialisation
- Convert to per-command workqueue

Changes to v3:
- Use delayed_work
- Enable new eh timeout handler for virtio, SAS, and FC
- Modify logging messages to include scmd pointer

Changes to v4:
- Remove stubs when enabling new eh timeout handler for other drivers

Hannes Reinecke (9):
scsi: Fix erratic device offline during EH
blk-timeout: add BLK_EH_SCHEDULED return code
scsi: improved eh timeout handler
virtio_scsi: Enable new EH timeout handler
libsas: Enable new EH timeout handler
mptsas: Enable new EH timeout handler
mpt2sas: Enable new EH timeout handler
mpt3sas: Enable new EH timeout handler
scsi_transport_fc: Enable new EH timeout handler

 drivers/message/fusion/mptsas.c      |   3 +-
 drivers/scsi/libsas/sas_scsi_host.c  |   2 +-
 drivers/scsi/mpt2sas/mpt2sas_scsih.c |   3 +-
 drivers/scsi/mpt3sas/mpt3sas_scsih.c |   1 +
 drivers/scsi/scsi.c                  |   3 +
 drivers/scsi/scsi_error.c            | 178 ++++++++++++++++++++++++++++++-----
 drivers/scsi/scsi_priv.h             |   2 +
 drivers/scsi/scsi_transport_fc.c     |   2 +-
 drivers/scsi/virtio_scsi.c           |   2 +
 include/linux/blkdev.h               |   1 +
 include/scsi/scsi_cmnd.h             |   2 +
 11 files changed, 173 insertions(+), 26 deletions(-)
Signature · 1 line
1.7.12.4
#1 lore raw

PATCH v5 1/9 scsi: Fix erratic device offline during EH

Commit 18a4d0a22ed6c54b67af7718c305cd010f09ddf8
(Handle disk devices which can not process medium access commands) was introduced to offline any device which cannot process medium access commands.
However, commit 3eef6257de48ff84a5d98ca533685df8a3beaeb8 (Reduce error recovery time by reducing use of TURs) reduced the number of TURs by sending it only on the first failing command, which might or might not be a medium access command. So in combination this results in an erratic device offlining during EH; if the command where the TUR was sent upon happens to be a medium access command the device will be set offline, if not everything proceeds as normal.

So instead of checking the EH command in the ->eh_action callback we should rather call ->eh_action when we're about to finish the command _and_ have sent a TUR previously. This should then set the device offline as advertised.

Cc Martin K. Petersen <[email protected]>
Cc Ewan Milne <[email protected]>
---
 drivers/scsi/scsi_error.c | 28 +++++++++++++++++++---------
 1 file changed, 19 insertions(+), 9 deletions(-)
drivers/scsi/scsi_error.c +19 -9
index abf0916..c88cb7e 100644
@@ -941,12 +941,6 @@ retry:
 
 	scsi_eh_restore_cmnd(scmd, &ses);
 
-	if (scmd->request->cmd_type != REQ_TYPE_BLOCK_PC) {
-		struct scsi_driver *sdrv = scsi_cmd_to_driver(scmd);
-		if (sdrv->eh_action)
-			rtn = sdrv->eh_action(scmd, cmnd, cmnd_size, rtn);
-	}
-
 	return rtn;
 }
 
@@ -964,6 +958,18 @@ static int scsi_request_sense(struct scsi_cmnd *scmd)
 	return scsi_send_eh_cmnd(scmd, NULL, 0, scmd->device->eh_timeout, ~0);
 }
 
+static int scsi_eh_action(struct scsi_cmnd *scmd, int rtn)
+{
+	static unsigned char tur_command[6] = {TEST_UNIT_READY, 0, 0, 0, 0, 0};
+
+	if (scmd->request->cmd_type != REQ_TYPE_BLOCK_PC) {
+		struct scsi_driver *sdrv = scsi_cmd_to_driver(scmd);
+		if (sdrv->eh_action)
+			rtn = sdrv->eh_action(scmd, tur_command, 6, rtn);
+	}
+	return rtn;
+}
+
 /**
  * scsi_eh_finish_cmd - Handle a cmd that eh is finished with.
  * @scmd:	Original SCSI cmd that eh has finished.
@@ -1142,7 +1148,9 @@ static int scsi_eh_test_devices(struct list_head *cmd_list,
 
 		list_for_each_entry_safe(scmd, next, cmd_list, eh_entry)
 			if (scmd->device == sdev) {
-				if (finish_cmds)
+				if (finish_cmds &&
+				    (try_stu ||
+				     scsi_eh_action(scmd, SUCCESS) == SUCCESS))
 					scsi_eh_finish_cmd(scmd, done_q);
 				else
 					list_move_tail(&scmd->eh_entry, work_q);
@@ -1281,7 +1289,8 @@ static int scsi_eh_stu(struct Scsi_Host *shost,
 			    !scsi_eh_tur(stu_scmd)) {
 				list_for_each_entry_safe(scmd, next,
 							  work_q, eh_entry) {
-					if (scmd->device == sdev)
+					if (scmd->device == sdev &&
+					    scsi_eh_action(scmd, SUCCESS) == SUCCESS)
 						scsi_eh_finish_cmd(scmd, done_q);
 				}
 			}
@@ -1348,7 +1357,8 @@ static int scsi_eh_bus_device_reset(struct Scsi_Host *shost,
 			    !scsi_eh_tur(bdr_scmd)) {
 				list_for_each_entry_safe(scmd, next,
 							 work_q, eh_entry) {
-					if (scmd->device == sdev)
+					if (scmd->device == sdev &&
+					    scsi_eh_action(scmd, rtn) != FAILED)
 						scsi_eh_finish_cmd(scmd,
 								   done_q);
 				}
Signature · 1 line
1.7.12.4
#2 lore raw

PATCH v5 2/9 blk-timeout: add BLK_EH_SCHEDULED return code

In reply to Hannes Reinecke

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

---
 drivers/scsi/scsi_error.c | 4 ++++
 include/linux/blkdev.h    | 1 +
 2 files changed, 5 insertions(+)
drivers/scsi/scsi_error.c +4 -0
index c88cb7e..b7dd774 100644
@@ -161,6 +161,10 @@ 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;
+
 	scmd->result |= DID_TIME_OUT << 16;
 
 	if (unlikely(rtn == BLK_EH_NOT_HANDLED &&
include/linux/blkdev.h +1 -0
index 2fdb4a4..d846e2b 100644
@@ -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 *);
Signature · 1 line
1.7.12.4
#3 lore raw

PATCH v5 3/9 scsi: improved eh timeout handler

In reply to 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.

---
 drivers/scsi/scsi.c       |   3 +
 drivers/scsi/scsi_error.c | 146 +++++++++++++++++++++++++++++++++++++++++-----
 drivers/scsi/scsi_priv.h  |   2 +
 include/scsi/scsi_cmnd.h  |   2 +
 4 files changed, 140 insertions(+), 13 deletions(-)
drivers/scsi/scsi.c +3 -0
index fe0bcb1..2b04a57 100644
@@ -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);
drivers/scsi/scsi_error.c +133 -13

191 lines not on this page · raw message

drivers/scsi/scsi_priv.h +2 -0
index 8f9a0ca..f079a59 100644
@@ -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);
include/scsi/scsi_cmnd.h +2 -0
index de5f5d8..e3137e3 100644
@@ -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);
Signature · 1 line
1.7.12.4
#4 lore raw

Re: [PATCH 3/9] scsi: improved eh timeout handler

drivers/scsi/scsi.c void scsi_put_command(struct scsi_cmnd *cmd) · line 361

Quoted 9 lines · Hannes Reinecke
drivers/scsi/scsi.c +2 -0
@@ -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);

Is this approach safe ? Is it e.g. possible that the abort work starts just before the cancel_delayed_work() call and continues to run after scsi_put_command() has finished ? In drivers/scsi/libfc/fc_exch.c a similar issue is solved by holding an additional reference as long as delayed work (fc_exch.timeout_work) is queued.

Quoted 44 lines
drivers/scsi/scsi_error.c +44 -0
+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));
+	}

drivers/scsi/scsi_error.c static int scsi_host_eh_past_deadline(struct Scsi_Host *shost) · line 160

Quoted 10 lines
drivers/scsi/scsi_error.c +10 -0
+
+	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);
+	}
+}

This patch adds several new calls to LLD EH handlers. Is it guaranteed that these will only be invoked before scsi_remove_host() has finished ? For more background information, see also "[PATCH] Make scsi_remove_host() wait until error handling finished" (http://thread.gmane.org/gmane.linux.scsi/82572/focus=82779).

Thanks,

Bart.

#5 lore raw

Re: [PATCH 3/9] scsi: improved eh timeout handler

Quoted 5 lines · Bart Van Assche
Quoted 4 lines · Hannes Reinecke
drivers/scsi/scsi.c +0 -0
@@ -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);
 
Quoted 13 lines

drivers/scsi/scsi.c void scsi_put_command(struct scsi_cmnd *cmd) · line 361

Quoted 5 lines
drivers/scsi/scsi.c +2 -0
+    cancel_delayed_work(&cmd->abort_work);
+
      __scsi_put_command(cmd->device->host, cmd, &sdev->sdev_gendev);
  }
  EXPORT_SYMBOL(scsi_put_command);

Is this approach safe ? Is it e.g. possible that the abort work starts just before the cancel_delayed_work() call and continues to run after scsi_put_command() has finished ? In drivers/scsi/libfc/fc_exch.c a similar issue is solved by holding an additional reference as long as delayed work (fc_exch.timeout_work) is queued.

I have been thinking of this, and in fact my original approach had 'cancel_delayed_work_sync' here. However, this didn't work as scsi_put_command() might end up being called from an softirq context.

From my understanding the workqueue stuff guarantees that either
a) the workqueue item is still queued
-> cancel_delayed_work will be in fact synchronous, as it'll
cancel queue item from the queue
b) the workqueue item is running
-> cancel_delayed_work is essentially a no-op, as the function
is running and will be terminated anyway.

IE from the API perspective the transition between 'queued' and 'running' is atomic, and no in-between states are visible.

So case a) is obviously safe, and for case b) the abort function is already running. But then the abort function has been called from the block timeout handler, which did a blk_mark_rq_complete() prior to calling the handler. So any completion coming in after that will be ignored, and scsi_put_command() won't be called.

Hence we should be safe here.

Quoted 52 lines
Quoted 52 lines
drivers/scsi/scsi_error.c +52 -0
+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);
Quoted 9 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+    }
+}

This patch adds several new calls to LLD EH handlers. Is it guaranteed that these will only be invoked before scsi_remove_host() has finished ? For more background information, see also "[PATCH] Make scsi_remove_host() wait until error handling finished"
(http://thread.gmane.org/gmane.linux.scsi/82572/focus=82779).

Well, that depends how scsi_remove_host() handles outstanding commands. What happens if you call scsi_remove_host() and there is still I/O in flight? I would assume that any HBA would have to kill any outstanding I/O prior to calling scsi_remove_host() (FC most certainly does this).
Which would mean that it'll have to wait for scsi_put_command() to be called for all outstanding commands. And as scsi_put_command() will be called only _after_ our routine runs (see the reasoning above) we should be safe.

Cheers,

Hannes

Signature · 8 lines
Dr. Hannes Reinecke		      zSeries & Storage
[email protected]			      +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 [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#6 lore raw

Re: [PATCH 3/9] scsi: improved eh timeout handler

Quoted 34 lines · Hannes Reinecke
Quoted 5 lines · Bart Van Assche
Quoted 4 lines · Hannes Reinecke
drivers/scsi/scsi.c +0 -0
@@ -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);
 
Quoted 13 lines

drivers/scsi/scsi.c void scsi_put_command(struct scsi_cmnd *cmd) · line 361

Quoted 5 lines
drivers/scsi/scsi.c +2 -0
+    cancel_delayed_work(&cmd->abort_work);
+
       __scsi_put_command(cmd->device->host, cmd, &sdev->sdev_gendev);
   }
   EXPORT_SYMBOL(scsi_put_command);

Is this approach safe ? Is it e.g. possible that the abort work starts just before the cancel_delayed_work() call and continues to run after scsi_put_command() has finished ? In drivers/scsi/libfc/fc_exch.c a similar issue is solved by holding an additional reference as long as delayed work (fc_exch.timeout_work) is queued.

I have been thinking of this, and in fact my original approach had 'cancel_delayed_work_sync' here. However, this didn't work as scsi_put_command() might end up being called from an softirq context.

Quoted 1 line

From my understanding the workqueue stuff guarantees that either

a) the workqueue item is still queued
-> cancel_delayed_work will be in fact synchronous, as it'll
cancel queue item from the queue
b) the workqueue item is running
-> cancel_delayed_work is essentially a no-op, as the function
is running and will be terminated anyway.

IE from the API perspective the transition between 'queued' and 'running' is atomic, and no in-between states are visible.

Quoted 7 lines

So case a) is obviously safe, and for case b) the abort function is already running. But then the abort function has been called from the block timeout handler, which did a blk_mark_rq_complete() prior to calling the handler. So any completion coming in after that will be ignored, and scsi_put_command() won't be called.

Hence we should be safe here.

Hello Hannes,

I think that you have just explained why that cancel_delayed_work() call in scsi_put_command() is not necessary at all ... In case of normal command completion, scsi_put_command() will be reached with no scmd_eh_abort_handler() call queued since there was no timeout. If the command timed out scsi_put_command() will be called after the abort_work has already been dequeued since work items are dequeued before being executed.

Bart.

#7 lore raw

Re: [PATCH 3/9] scsi: improved eh timeout handler

In reply to Hannes Reinecke
Quoted 7 lines · Hannes Reinecke
Quoted 6 lines · Bart Van Assche

This patch adds several new calls to LLD EH handlers. Is it guaranteed that these will only be invoked before scsi_remove_host() has finished ? For more background information, see also "[PATCH] Make scsi_remove_host() wait until error handling finished"
(http://thread.gmane.org/gmane.linux.scsi/82572/focus=82779).

Quoted 9 lines

Well, that depends how scsi_remove_host() handles outstanding commands. What happens if you call scsi_remove_host() and there is still I/O in flight? I would assume that any HBA would have to kill any outstanding I/O prior to calling scsi_remove_host() (FC most certainly does this).
Which would mean that it'll have to wait for scsi_put_command() to be called for all outstanding commands. And as scsi_put_command() will be called only _after_ our routine runs (see the reasoning above) we should be safe.

Hello Hannes,

Since fc_remove_host() has to be invoked before scsi_remove_host() and since fc_remove_host() changes the port state into FC_PORTSTATE_DELETED this means that fc_remote_port_chkready() will return DID_NO_CONNECT while scsi_remove_host() is in progress. I think this prevents that the SYNCHRONIZE CACHE command submitted by sd_remove() reaches SCSI disks over FC since sd_remove() is invoked from inside scsi_remove_host(). The SRP transport patch I had posted recently makes sure that the SYNCHRONIZE CACHE command submitted by sd_remove() reaches the target SCSI disk. This makes me wonder what the correct behavior is for SCSI transport drivers - disabling I/O before scsi_remove_host() starts or ensuring that I/O is still possible while scsi_remove_host() is in progress ? I think the call chain is as follows: scsi_remove_host() -> device_del() -> bus_remove_device() -> device_release_driver() -> __device_release_driver() -> sd_remove() -> sd_shutdown() -> sd_sync_cache().

Bart.

#8 lore raw

Re: [PATCH 3/9] scsi: improved eh timeout handler

Quoted 20 lines · Bart Van Assche
Quoted 7 lines · Hannes Reinecke
Quoted 6 lines · Bart Van Assche

This patch adds several new calls to LLD EH handlers. Is it guaranteed that these will only be invoked before scsi_remove_host() has finished ? For more background information, see also "[PATCH] Make scsi_remove_host() wait until error handling finished"
(http://thread.gmane.org/gmane.linux.scsi/82572/focus=82779).

Quoted 9 lines

Well, that depends how scsi_remove_host() handles outstanding commands. What happens if you call scsi_remove_host() and there is still I/O in flight? I would assume that any HBA would have to kill any outstanding I/O prior to calling scsi_remove_host() (FC most certainly does this).
Which would mean that it'll have to wait for scsi_put_command() to be called for all outstanding commands. And as scsi_put_command() will be called only _after_ our routine runs (see the reasoning above) we should be safe.

Hello Hannes,

Quoted 17 lines

Since fc_remove_host() has to be invoked before scsi_remove_host() and since fc_remove_host() changes the port state into FC_PORTSTATE_DELETED this means that fc_remote_port_chkready() will return DID_NO_CONNECT while scsi_remove_host() is in progress. I think this prevents that the SYNCHRONIZE CACHE command submitted by sd_remove() reaches SCSI disks over FC since sd_remove() is invoked from inside scsi_remove_host(). The SRP transport patch I had posted recently makes sure that the SYNCHRONIZE CACHE command submitted by sd_remove() reaches the target SCSI disk. This makes me wonder what the correct behavior is for SCSI transport drivers - disabling I/O before scsi_remove_host() starts or ensuring that I/O is still possible while scsi_remove_host() is in progress ? I think the call chain is as follows: scsi_remove_host() -> device_del() -> bus_remove_device() -> device_release_driver() -> __device_release_driver() -> sd_remove() -> sd_shutdown() -> sd_sync_cache().

The actual call chain is
scsi_remove_host() -> scsi_forget_host() -> __scsi_remove_device() -> device_del() etc.

What's important here is that __scsi_remove_device() sets the state 'SDEV_DEL' and calls blk_cleanup_queue().

So after __scsi_remove_device() no further I/O is possible. However, prior to setting SDEV_DEL I/O should be perfectly okay, so one would assume that the SYNCHRONIZE CACHE command should be sent to the device. USB most certainly does it.

A safe practice, however, would be to ensure that no _other_ I/O can be sent to the device, ie that all I/O coming in via the request queue or ioctl should be short-circuited and never hit the device at all. That's what fc_remote_port_chkready() does.

Cheers,

Hannes

Signature · 8 lines
Dr. Hannes Reinecke		      zSeries & Storage
[email protected]			      +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 [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#9 lore raw

PATCH v5 4/9 virtio_scsi: Enable new EH timeout handler

In reply to Hannes Reinecke
---
 drivers/scsi/virtio_scsi.c | 2 ++
 1 file changed, 2 insertions(+)
drivers/scsi/virtio_scsi.c +2 -0
index 74b88ef..5fca1ca 100644
@@ -683,6 +683,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 = scsi_abort_command,
 
 	.can_queue = 1024,
 	.dma_boundary = UINT_MAX,
@@ -699,6 +700,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 = scsi_abort_command,
 
 	.can_queue = 1024,
 	.dma_boundary = UINT_MAX,
Signature · 1 line
1.7.12.4
#10 lore raw

PATCH v5 5/9 libsas: Enable new EH timeout handler

In reply to Hannes Reinecke
---
 drivers/scsi/libsas/sas_scsi_host.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/scsi/libsas/sas_scsi_host.c +1 -1
index da3aee1..6eb2f0c 100644
@@ -864,7 +864,7 @@ enum blk_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd)
 {
 	scmd_printk(KERN_DEBUG, cmd, "command %p timed out\n", cmd);
 
-	return BLK_EH_NOT_HANDLED;
+	return scsi_abort_command(cmd);
 }
 
 int sas_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
Signature · 1 line
1.7.12.4
#11 lore raw

PATCH v5 6/9 mptsas: Enable new EH timeout handler

In reply to Hannes Reinecke
---
 drivers/message/fusion/mptsas.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/message/fusion/mptsas.c +2 -1
index dd239bd..d0aed77 100644
@@ -1986,7 +1986,8 @@ static struct scsi_host_template mptsas_driver_template = {
 	.slave_configure		= mptsas_slave_configure,
 	.target_destroy			= mptsas_target_destroy,
 	.slave_destroy			= mptscsih_slave_destroy,
-	.change_queue_depth 		= mptscsih_change_queue_depth,
+	.change_queue_depth		= mptscsih_change_queue_depth,
+	.eh_timed_out			= scsi_abort_command,
 	.eh_abort_handler		= mptscsih_abort,
 	.eh_device_reset_handler	= mptscsih_dev_reset,
 	.eh_host_reset_handler		= mptscsih_host_reset,
Signature · 1 line
1.7.12.4
#12 lore raw

PATCH v5 7/9 mpt2sas: Enable new EH timeout handler

In reply to Hannes Reinecke
---
 drivers/scsi/mpt2sas/mpt2sas_scsih.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/scsi/mpt2sas/mpt2sas_scsih.c +2 -1
index 389d792..9ede2c2 100644
@@ -7628,8 +7628,9 @@ static struct scsi_host_template scsih_driver_template = {
 	.slave_destroy			= _scsih_slave_destroy,
 	.scan_finished			= _scsih_scan_finished,
 	.scan_start			= _scsih_scan_start,
-	.change_queue_depth 		= _scsih_change_queue_depth,
+	.change_queue_depth		= _scsih_change_queue_depth,
 	.change_queue_type		= _scsih_change_queue_type,
+	.eh_timed_out			= scsi_abort_command,
 	.eh_abort_handler		= _scsih_abort,
 	.eh_device_reset_handler	= _scsih_dev_reset,
 	.eh_target_reset_handler	= _scsih_target_reset,
Signature · 1 line
1.7.12.4
#13 lore raw

PATCH v5 8/9 mpt3sas: Enable new EH timeout handler

In reply to Hannes Reinecke
---
 drivers/scsi/mpt3sas/mpt3sas_scsih.c | 1 +
 1 file changed, 1 insertion(+)
drivers/scsi/mpt3sas/mpt3sas_scsih.c +1 -0
index a961fe1..b893383 100644
@@ -7237,6 +7237,7 @@ static struct scsi_host_template scsih_driver_template = {
 	.scan_start			= _scsih_scan_start,
 	.change_queue_depth		= _scsih_change_queue_depth,
 	.change_queue_type		= _scsih_change_queue_type,
+	.eh_timed_out			= scsi_abort_command,
 	.eh_abort_handler		= _scsih_abort,
 	.eh_device_reset_handler	= _scsih_dev_reset,
 	.eh_target_reset_handler	= _scsih_target_reset,
Signature · 1 line
1.7.12.4
#14 lore raw

PATCH v5 9/9 scsi_transport_fc: Enable new EH timeout handler

In reply to Hannes Reinecke
---
 drivers/scsi/scsi_transport_fc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/scsi/scsi_transport_fc.c +1 -1
index 4628fd5..012c801 100644
@@ -2079,7 +2079,7 @@ fc_timed_out(struct scsi_cmnd *scmd)
 	if (rport->port_state == FC_PORTSTATE_BLOCKED)
 		return BLK_EH_RESET_TIMER;
 
-	return BLK_EH_NOT_HANDLED;
+	return scsi_abort_command(scmd);
 }
 
 /*
Signature · 1 line
1.7.12.4
#15 lore raw
In reply to Hannes Reinecke

One thing I'm still wondering is why we can't enable this globally, and if there is a reason it should be documented.

As far as I can tell the actual calling context of the eh_abort_handler doesn't change, so an LLDD would have to rely on some very specific side effects of being in EH to break, and we never guaranteed such specifics.

#16 lore raw
Quoted 8 lines · Christoph Hellwig

One thing I'm still wondering is why we can't enable this globally, and if there is a reason it should be documented.

As far as I can tell the actual calling context of the eh_abort_handler doesn't change, so an LLDD would have to rely on some very specific side effects of being in EH to break, and we never guaranteed such specifics.

I don't mind. Having talked to the various SCSI folks everyone agreed that calling abort asynchronously shouldn't do any harm. At least as far as the SCSI spec goes.

As for documentation: I didn't document it it currently as with my implementation it's pretty much an optional thing.
But if we were to enable it globally it surely should be documented.

So if there is a consensus I surely can enable it globally and update the documentation.

James?

Cheers,

Hannes

Signature · 8 lines
Dr. Hannes Reinecke		      zSeries & Storage
[email protected]			      +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 [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#17 lore raw
Quoted 10 lines · Hannes Reinecke

I don't mind. Having talked to the various SCSI folks everyone agreed that calling abort asynchronously shouldn't do any harm. At least as far as the SCSI spec goes.

As for documentation: I didn't document it it currently as with my implementation it's pretty much an optional thing.
But if we were to enable it globally it surely should be documented.

So if there is a consensus I surely can enable it globally and update the documentation.

I would defintively love enabling it globally. As a fallback we should probably keep the old code for a while and prepare a way to disable the async code by a flag in the host template. If no reason to enable it shows up after a year ore two we can remove the old code entirely.

I.e change the tail of scsi_times_out into something like:

	if (rtn == BLK_EH_NOT_HANDLED && !shost->hostt->no_async_abort) {
		if (scsi_abort_command(scmd))
			return BLK_EH_NOT_HANDLED;
	}

	scmd->result |= DID_TIME_OUT << 16;
	...

That way we don't even need the new BLK_EH_SCHEDULED return value and have all the handling in scsi_error.c

#18 lore raw
Quoted 28 lines · Christoph Hellwig
Quoted 10 lines · Hannes Reinecke

I don't mind. Having talked to the various SCSI folks everyone agreed that calling abort asynchronously shouldn't do any harm. At least as far as the SCSI spec goes.

As for documentation: I didn't document it it currently as with my implementation it's pretty much an optional thing.
But if we were to enable it globally it surely should be documented.

So if there is a consensus I surely can enable it globally and update the documentation.

I would defintively love enabling it globally. As a fallback we should probably keep the old code for a while and prepare a way to disable the async code by a flag in the host template. If no reason to enable it shows up after a year ore two we can remove the old code entirely.

I.e change the tail of scsi_times_out into something like:

	if (rtn == BLK_EH_NOT_HANDLED && !shost->hostt->no_async_abort) {
		if (scsi_abort_command(scmd))
			return BLK_EH_NOT_HANDLED;
	}

	scmd->result |= DID_TIME_OUT << 16;
	...
Quoted 3 lines

That way we don't even need the new BLK_EH_SCHEDULED return value and have all the handling in scsi_error.c

Right. Will be doing it.

Cheers,

Hannes

Signature · 8 lines
Dr. Hannes Reinecke		      zSeries & Storage
[email protected]			      +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 [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#19 lore raw
In reply to Christoph Hellwig
Quoted 2 lines · Christoph Hellwig

I would defintively love enabling it globally.

Agreed. I carry a private patch based on an older version of Hannes' patchset doing just that. Istr there was some problem exactly once on a VM with a virtualized hba. We hit problems with virtualized ethernet devices fairly regularly, so a single case on scsi may simply be some other latent bug.

On real hardware there have never been problems other than the scsi aborts themselves.

Jörn

--
Do not stop an army on its way home.
-- Sun Tzu
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html