Skip to contentSkip to conversation

scsi: make asynchronous aborts mandatory

RFC PATCH In review Hannes Reinecke 2 participants 7 messages Nov 4, 2014 – Nov 5, 2014

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

cover scsi: make asynchronous aborts mandatory

Hi all,

asynchronous abort have been activated since v3.14, and we haven't received any failure reports so far. So we should make asynchronous aborts mandatory and remove the no_async_aborts flag from the SCSI host structure.
With this we can also cleanup the failure path in SCSI EH.

Hannes Reinecke (3):
scsi: make scsi_eh_scmd_add() always succeed
scsi: make eh_eflags persistent
scsi: make asynchronous aborts mandatory

 Documentation/scsi/scsi_eh.txt |  31 +++++------
 drivers/scsi/scsi_error.c      | 113 +++++++----------------------------------
 drivers/scsi/scsi_lib.c        |   4 +-
 drivers/scsi/scsi_priv.h       |   3 +-
 include/scsi/scsi_eh.h         |   1 +
 include/scsi/scsi_host.h       |   5 --
 6 files changed, 34 insertions(+), 123 deletions(-)
Signature · 1 line
1.8.5.2
#1 lore raw

PATCH 1/3 scsi: make scsi_eh_scmd_add() always succeed

BLK_EH_HANDLED does not work with scsi commands, as we need to release the associated buffers correctly.
And scsi_eh_scmd_add() currently only will fail if no error handler thread is started (which will never be the case) or if the state machine encounters an illegal transition. As state machine transitions don't have any real meaning we can also force setting of the new state and
make scsi_dh_scmd_add() a void function.
With that we'll never have to resort to return BLK_EH_HANDLED.

---
 drivers/scsi/scsi_error.c | 30 +++++++++---------------------
 drivers/scsi/scsi_lib.c   |  4 ++--
 drivers/scsi/scsi_priv.h  |  2 +-
 3 files changed, 12 insertions(+), 24 deletions(-)
drivers/scsi/scsi_error.c +9 -21
index fa7b5ec..c71d61f 100644
@@ -163,14 +163,7 @@ scmd_eh_abort_handler(struct work_struct *work)
 		}
 	}
 
-	if (!scsi_eh_scmd_add(scmd, 0)) {
-		SCSI_LOG_ERROR_RECOVERY(3,
-			scmd_printk(KERN_WARNING, scmd,
-				    "scmd %p terminate "
-				    "aborted command\n", scmd));
-		set_host_byte(scmd, DID_TIME_OUT);
-		scsi_finish_command(scmd);
-	}
+	scsi_eh_scmd_add(scmd, 0);
 }
 
 /**
@@ -228,37 +221,33 @@ scsi_abort_command(struct scsi_cmnd *scmd)
  * scsi_eh_scmd_add - add scsi cmd to error handling.
  * @scmd:	scmd to run eh on.
  * @eh_flag:	optional SCSI_EH flag.
- *
- * Return value:
- *	0 on failure.
  */
-int scsi_eh_scmd_add(struct scsi_cmnd *scmd, int eh_flag)
+void scsi_eh_scmd_add(struct scsi_cmnd *scmd, int eh_flag)
 {
 	struct Scsi_Host *shost = scmd->device->host;
 	unsigned long flags;
-	int ret = 0;
 
-	if (!shost->ehandler)
-		return 0;
+	WARN_ON(!shost->ehandler);
 
 	spin_lock_irqsave(shost->host_lock, flags);
+	/*
+	 * Force SHOST_RECOVERY even for illegal state transitions;
+	 * we cannot handle failed commands otherwise.
+	 */
 	if (scsi_host_set_state(shost, SHOST_RECOVERY))
 		if (scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY))
-			goto out_unlock;
+			shost->shost_state = SHOST_RECOVERY;
 
 	if (shost->eh_deadline != -1 && !shost->last_reset)
 		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++;
 	scsi_eh_wakeup(shost);
- out_unlock:
 	spin_unlock_irqrestore(shost->host_lock, flags);
-	return ret;
 }
 
 /**
@@ -294,8 +283,7 @@ enum blk_eh_timer_return scsi_times_out(struct request *req)
 			return BLK_EH_NOT_HANDLED;
 
 		set_host_byte(scmd, DID_TIME_OUT);
-		if (!scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD))
-			rtn = BLK_EH_HANDLED;
+		scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD);
 	}
 
 	return rtn;
drivers/scsi/scsi_lib.c +2 -2
index fc0a8a0..26f7dcf 100644
@@ -1647,8 +1647,8 @@ static void scsi_softirq_done(struct request *rq)
 			scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY);
 			break;
 		default:
-			if (!scsi_eh_scmd_add(cmd, 0))
-				scsi_finish_command(cmd);
+			scsi_eh_scmd_add(cmd, 0);
+			break;
 	}
 }
 
drivers/scsi/scsi_priv.h +1 -1
index 12b8e1b..b2d1a2b 100644
@@ -72,7 +72,7 @@ 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);
 extern void scsi_eh_wakeup(struct Scsi_Host *shost);
-extern int scsi_eh_scmd_add(struct scsi_cmnd *, int);
+extern void scsi_eh_scmd_add(struct scsi_cmnd *, int);
 void scsi_eh_ready_devs(struct Scsi_Host *shost,
 			struct list_head *work_q,
 			struct list_head *done_q);
Signature · 1 line
1.8.5.2
#2 lore raw

Re: [PATCH 1/3] scsi: make scsi_eh_scmd_add() always succeed

Quoted 2 lines · Hannes Reinecke

BLK_EH_HANDLED does not work with scsi commands, as we need to release the associated buffers correctly.

Which particular error do you see? The ->eh_timed_out routines can return BLK_EH_HANDLED, and libata, iscsi and fc actually do, so we'll need to fix these issues either way.

Quoted 3 lines

And scsi_eh_scmd_add() currently only will fail if no error handler thread is started (which will never be the case)

Yes.

Quoted 6 lines

or if the state machine encounters an illegal transition. As state machine transitions don't have any real meaning we can also force setting of the new state and make scsi_dh_scmd_add() a void function.
With that we'll never have to resort to return BLK_EH_HANDLED.

Which kind of transition did you see rejected? Neither a CREATED or DEL_(RECOVERY) state should be possible when invoking the error handler, so I'd rather see asserts here than overriding the state machine. te RECOVERY state from a RUNNING or existing RECOVERY state, which leaves CREATED, CANCEL, DEL, CANCEL_RECOVERY or DEL_RECOVERY.

#3 lore raw

Re: [PATCH 1/3] scsi: make scsi_eh_scmd_add() always succeed

Quoted 8 lines · Christoph Hellwig
Quoted 2 lines · Hannes Reinecke

BLK_EH_HANDLED does not work with scsi commands, as we need to release the associated buffers correctly.

Which particular error do you see? The ->eh_timed_out routines can return BLK_EH_HANDLED, and libata, iscsi and fc actually do, so we'll need to fix these issues either way.

Hmm. Looking closer it seem that you are correct. However, the main point still stands; there is no way scsi_eh_scmd_add() can legitimately fail.

Quoted 13 lines
Quoted 3 lines

And scsi_eh_scmd_add() currently only will fail if no error handler thread is started (which will never be the case)

Yes.

Quoted 6 lines

or if the state machine encounters an illegal transition. As state machine transitions don't have any real meaning we can also force setting of the new state and make scsi_dh_scmd_add() a void function.
With that we'll never have to resort to return BLK_EH_HANDLED.

Quoted 6 lines

Which kind of transition did you see rejected? Neither a CREATED or DEL_(RECOVERY) state should be possible when invoking the error handler, so I'd rather see asserts here than overriding the state machine. te RECOVERY state from a RUNNING or existing RECOVERY state, which leaves CREATED, CANCEL, DEL, CANCEL_RECOVERY or DEL_RECOVERY.

I did not see any rejection so far.
However, we're calling scsi_host_set_state(), which _might_ fail. And we need to have a way of either
a) make sure that it cannot fail prior to calling it
or
b) handling the failure.
So I'm fine with using asserts here.

I'll be updating the patch (and description)

Cheers,

Hannes

Signature · 8 lines
Dr. Hannes Reinecke		      zSeries & Storage
[email protected]			      +49 911 74053 688
SUSE LINUX GmbH, Maxfeldstr. 5, 90409 Nürnberg
GF: J. Hawn, J. Guild, F. Imendörffer, HRB 21284 (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
#4 lore raw

PATCH 2/3 scsi: make eh_eflags persistent

In reply to Hannes Reinecke

To detect if a failed command has been retried we must not clear scmd->eh_eflags when EH finishes.
The flag should be persistent throughout the lifetime of the command.

---
 Documentation/scsi/scsi_eh.txt | 3 ---
 drivers/scsi/scsi_error.c      | 4 ++--
 include/scsi/scsi_eh.h         | 1 +
 3 files changed, 3 insertions(+), 5 deletions(-)
Documentation/scsi/scsi_eh.txt +0 -3
index a0c8511..011c03d 100644
@@ -264,7 +264,6 @@ scmd->allowed.
  3. scmd recovered
     ACTION: scsi_eh_finish_cmd() is invoked to EH-finish scmd
 	- shost->host_failed--
-	- clear scmd->eh_eflags
 	- scsi_setup_cmd_retry()
 	- move from local eh_work_q to local eh_done_q
     LOCKING: none
@@ -452,8 +451,6 @@ except for #1 must be implemented by eh_strategy_handler().
 
  - shost->host_failed is zero.
 
- - Each scmd's eh_eflags field is cleared.
-
  - Each scmd is in such a state that scsi_setup_cmd_retry() on the
    scmd doesn't make any difference.
 
drivers/scsi/scsi_error.c +2 -2
index c71d61f..27cb30d 100644
@@ -183,7 +183,6 @@ scsi_abort_command(struct scsi_cmnd *scmd)
 		/*
 		 * Retry after abort failed, escalate to next level.
 		 */
-		scmd->eh_eflags &= ~SCSI_EH_ABORT_SCHEDULED;
 		SCSI_LOG_ERROR_RECOVERY(3,
 			scmd_printk(KERN_INFO, scmd,
 				    "scmd %p previous abort failed\n", scmd));
@@ -926,6 +925,7 @@ void scsi_eh_prep_cmnd(struct scsi_cmnd *scmd, struct scsi_eh_save *ses,
 	ses->result = scmd->result;
 	ses->underflow = scmd->underflow;
 	ses->prot_op = scmd->prot_op;
+	ses->eh_eflags = scmd->eh_eflags;
 
 	scmd->prot_op = SCSI_PROT_NORMAL;
 	scmd->eh_eflags = 0;
@@ -989,6 +989,7 @@ void scsi_eh_restore_cmnd(struct scsi_cmnd* scmd, struct scsi_eh_save *ses)
 	scmd->result = ses->result;
 	scmd->underflow = ses->underflow;
 	scmd->prot_op = ses->prot_op;
+	scmd->eh_eflags = ses->eh_eflags;
 }
 EXPORT_SYMBOL(scsi_eh_restore_cmnd);
 
@@ -1122,7 +1123,6 @@ static int scsi_eh_action(struct scsi_cmnd *scmd, int rtn)
 void scsi_eh_finish_cmd(struct scsi_cmnd *scmd, struct list_head *done_q)
 {
 	scmd->device->host->host_failed--;
-	scmd->eh_eflags = 0;
 	list_move_tail(&scmd->eh_entry, done_q);
 }
 EXPORT_SYMBOL(scsi_eh_finish_cmd);
include/scsi/scsi_eh.h +1 -0
index 2562481..4973844 100644
@@ -78,6 +78,7 @@ extern int scsi_reset_provider(struct scsi_device *, int);
 struct scsi_eh_save {
 	/* saved state */
 	int result;
+	int eh_eflags;
 	enum dma_data_direction data_direction;
 	unsigned underflow;
 	unsigned char cmd_len;
Signature · 1 line
1.8.5.2
#5 lore raw

PATCH 3/3 scsi: make asynchronous aborts mandatory

In reply to Hannes Reinecke

There hasn't been any reports for HBAs where asynchronous abort would not work, so we should make it mandatory and remove the fallback.

---
 Documentation/scsi/scsi_eh.txt | 28 +++++++-------
 drivers/scsi/scsi_error.c      | 85 +++++-------------------------------------
 drivers/scsi/scsi_lib.c        |  2 +-
 drivers/scsi/scsi_priv.h       |  3 +-
 include/scsi/scsi_host.h       |  5 ---
 5 files changed, 24 insertions(+), 99 deletions(-)
Documentation/scsi/scsi_eh.txt +13 -15
index 011c03d..cbb07f2 100644
@@ -70,7 +70,7 @@ with the command.
 	scmd is requeued to blk queue.
 
  - otherwise
-	scsi_eh_scmd_add(scmd, 0) is invoked for the command.  See
+	scsi_eh_scmd_add(scmd) is invoked for the command.  See
 	[1-3] for details of this function.
 
 
@@ -103,13 +103,15 @@ function
         eh_timed_out() callback did not handle the command.
 	Step #2 is taken.
 
- 2. If the host supports asynchronous completion (as indicated by the
-    no_async_abort setting in the host template) scsi_abort_command()
-    is invoked to schedule an asynchrous abort. If that fails
-    Step #3 is taken.
+ 2. scsi_abort_command() is invoked to schedule an asynchrous abort
+    (Seee [1-3] for more information).
+    Asynchronous abort are not invoked for commands which have
+    SCSI_EH_ABORT_SCHEDULED set (this indicates that the command
+    already had been aborted once, and this is a retry which failed),
+    or when the EH deadline is expired. In these case Step #3 is taken.
 
- 2. scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD) is invoked for the
-    command.  See [1-3] for more information.
+ 3. scsi_eh_scmd_add(scmd) is invoked for the
+    command.  See [1-4] for more information.
 
 [1-3] Asynchronous command aborts
 
@@ -124,16 +126,13 @@ function
 
  scmds enter EH via scsi_eh_scmd_add(), which does the following.
 
- 1. Turns on scmd->eh_eflags as requested.  It's 0 for error
-    completions and SCSI_EH_CANCEL_CMD for timeouts.
+ 1. Links scmd->eh_entry to shost->eh_cmd_q
 
- 2. Links scmd->eh_entry to shost->eh_cmd_q
+ 2. Sets SHOST_RECOVERY bit in shost->shost_state
 
- 3. Sets SHOST_RECOVERY bit in shost->shost_state
+ 3. Increments shost->host_failed
 
- 4. Increments shost->host_failed
-
- 5. Wakes up SCSI EH thread if shost->host_busy == shost->host_failed
+ 4. Wakes up SCSI EH thread if shost->host_busy == shost->host_failed
 
  As can be seen above, once any scmd is added to shost->eh_cmd_q,
 SHOST_RECOVERY shost_state bit is turned on.  This prevents any new
@@ -249,7 +248,6 @@ scmd->allowed.
 
  1. Error completion / time out
     ACTION: scsi_eh_scmd_add() is invoked for scmd
-	- set scmd->eh_eflags
 	- add scmd to shost->eh_cmd_q
 	- set SHOST_RECOVERY
 	- shost->host_failed++
drivers/scsi/scsi_error.c +9 -76

140 lines not on this page · raw message

drivers/scsi/scsi_lib.c +1 -1
index 26f7dcf..9cbe33b 100644
@@ -1647,7 +1647,7 @@ static void scsi_softirq_done(struct request *rq)
 			scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY);
 			break;
 		default:
-			scsi_eh_scmd_add(cmd, 0);
+			scsi_eh_scmd_add(cmd);
 			break;
 	}
 }
drivers/scsi/scsi_priv.h +1 -2
index b2d1a2b..1248e72 100644
@@ -18,7 +18,6 @@ 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) \
@@ -72,7 +71,7 @@ extern enum blk_eh_timer_return scsi_times_out(struct request *req);
 extern int scsi_error_handler(void *host);
 extern int scsi_decide_disposition(struct scsi_cmnd *cmd);
 extern void scsi_eh_wakeup(struct Scsi_Host *shost);
-extern void scsi_eh_scmd_add(struct scsi_cmnd *, int);
+extern void scsi_eh_scmd_add(struct scsi_cmnd *);
 void scsi_eh_ready_devs(struct Scsi_Host *shost,
 			struct list_head *work_q,
 			struct list_head *done_q);
include/scsi/scsi_host.h +0 -5
index 5e36248..4f4db75 100644
@@ -460,11 +460,6 @@ struct scsi_host_template {
 	unsigned no_write_same:1;
 
 	/*
-	 * True if asynchronous aborts are not supported
-	 */
-	unsigned no_async_abort:1;
-
-	/*
 	 * Countdown for host blocking with no commands outstanding.
 	 */
 	unsigned int max_host_blocked;
Signature · 1 line
1.8.5.2
#6 lore raw

This leaves a stale comment reference to scsi_eh_abort_cmds in libsas. Also while grepping for that I found out that sas aborts don't work anymore with the async abort code due to this beautiful code in sas_eh_abort_handler:

	if (current != host->ehandler)
		return FAILED;

so you'll need another patch to fix that up. In the longer run we really should both kill sas_eh_abort_handler and bring SAS EH more in line with the rest of the world.