ChangeSet 1.1371.759.29, 2004/04/26 16:10:02-07:00, mdharm-usb@one-eyed-alien.net

[PATCH] USB: usb-storage driver changes for 2.6.x [1/4]

Patch as239b from Alan Stern:  This patch improves the interaction between
a SCSI reset, an internally generated reset, and an abort.  This improves
our error-recovery in cases where the device is hung (or almost hung) while
we're trying to auto-reset.


 drivers/usb/storage/scsiglue.c  |   19 +++++++++-------
 drivers/usb/storage/transport.c |   47 ++++++++++++++++++++++------------------
 drivers/usb/storage/usb.h       |    5 ++--
 3 files changed, 40 insertions(+), 31 deletions(-)


diff -Nru a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
--- a/drivers/usb/storage/scsiglue.c	Fri May 14 15:32:32 2004
+++ b/drivers/usb/storage/scsiglue.c	Fri May 14 15:32:32 2004
@@ -159,14 +159,18 @@
 		return FAILED;
 	}
 
-	/* Set state to ABORTING, set the ABORTING bit, and release the lock */
+	/* Set state to ABORTING and set the ABORTING bit, but only if
+	 * a device reset isn't already in progress (to avoid interfering
+	 * with the reset).  To prevent races with auto-reset, we must
+	 * stop any ongoing USB transfers while still holding the host
+	 * lock. */
 	us->sm_state = US_STATE_ABORTING;
-	set_bit(US_FLIDX_ABORTING, &us->flags);
+	if (!test_bit(US_FLIDX_RESETTING, &us->flags)) {
+		set_bit(US_FLIDX_ABORTING, &us->flags);
+		usb_stor_stop_transport(us);
+	}
 	scsi_unlock(host);
 
-	/* Stop an ongoing USB transfer */
-	usb_stor_stop_transport(us);
-
 	/* Wait for the aborted command to finish */
 	wait_for_completion(&us->notify);
 
@@ -254,18 +258,17 @@
 }
 
 /* Report a driver-initiated device reset to the SCSI layer.
- * Calling this for a SCSI-initiated reset is unnecessary but harmless. */
+ * Calling this for a SCSI-initiated reset is unnecessary but harmless.
+ * The caller must own the SCSI host lock. */
 void usb_stor_report_device_reset(struct us_data *us)
 {
 	int i;
 
-	scsi_lock(us->host);
 	scsi_report_device_reset(us->host, 0, 0);
 	if (us->flags & US_FL_SCM_MULT_TARG) {
 		for (i = 1; i < us->host->max_id; ++i)
 			scsi_report_device_reset(us->host, 0, i);
 	}
-	scsi_unlock(us->host);
 }
 
 /***********************************************************************
diff -Nru a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
--- a/drivers/usb/storage/transport.c	Fri May 14 15:32:32 2004
+++ b/drivers/usb/storage/transport.c	Fri May 14 15:32:32 2004
@@ -137,7 +137,7 @@
 	int status;
 
 	/* don't submit URBs during abort/disconnect processing */
-	if (us->flags & DONT_SUBMIT)
+	if (us->flags & ABORTING_OR_DISCONNECTING)
 		return -EIO;
 
 	/* set up data structures for the wakeup system */
@@ -172,7 +172,7 @@
 	set_bit(US_FLIDX_URB_ACTIVE, &us->flags);
 
 	/* did an abort/disconnect occur during the submission? */
-	if (us->flags & DONT_SUBMIT) {
+	if (us->flags & ABORTING_OR_DISCONNECTING) {
 
 		/* cancel the URB, if it hasn't been cancelled already */
 		if (test_and_clear_bit(US_FLIDX_URB_ACTIVE, &us->flags)) {
@@ -440,7 +440,7 @@
 	int result;
 
 	/* don't submit s-g requests during abort/disconnect processing */
-	if (us->flags & DONT_SUBMIT)
+	if (us->flags & ABORTING_OR_DISCONNECTING)
 		return USB_STOR_XFER_ERROR;
 
 	/* initialize the scatter-gather request block */
@@ -458,7 +458,7 @@
 	set_bit(US_FLIDX_SG_ACTIVE, &us->flags);
 
 	/* did an abort/disconnect occur during the submission? */
-	if (us->flags & DONT_SUBMIT) {
+	if (us->flags & ABORTING_OR_DISCONNECTING) {
 
 		/* cancel the request, if it hasn't been cancelled already */
 		if (test_and_clear_bit(US_FLIDX_SG_ACTIVE, &us->flags)) {
@@ -712,14 +712,10 @@
 
 	/* abort processing: the bulk-only transport requires a reset
 	 * following an abort */
-	Handle_Abort:
+  Handle_Abort:
 	srb->result = DID_ABORT << 16;
-	if (us->protocol == US_PR_BULK) {
-
-		/* permit the reset transfer to take place */
-		clear_bit(US_FLIDX_ABORTING, &us->flags);
+	if (us->protocol == US_PR_BULK)
 		us->transport_reset(us);
-	}
 }
 
 /* Stop the current URB transfer */
@@ -1079,20 +1075,28 @@
 {
 	int result;
 	int result2;
+	int rc = FAILED;
 
-	/* Let the SCSI layer know we are doing a reset */
+	/* Let the SCSI layer know we are doing a reset, set the
+	 * RESETTING bit, and clear the ABORTING bit so that the reset
+	 * may proceed.
+	 */
+	scsi_lock(us->host);
 	usb_stor_report_device_reset(us);
+	set_bit(US_FLIDX_RESETTING, &us->flags);
+	clear_bit(US_FLIDX_ABORTING, &us->flags);
+	scsi_unlock(us->host);
 
 	/* A 20-second timeout may seem rather long, but a LaCie
-	 *  StudioDrive USB2 device takes 16+ seconds to get going
-	 *  following a powerup or USB attach event. */
-
+	 * StudioDrive USB2 device takes 16+ seconds to get going
+	 * following a powerup or USB attach event.
+	 */
 	result = usb_stor_control_msg(us, us->send_ctrl_pipe,
 			request, requesttype, value, index, data, size,
 			20*HZ);
 	if (result < 0) {
 		US_DEBUGP("Soft reset failed: %d\n", result);
-		return FAILED;
+		goto Done;
 	}
 
 	/* long wait for reset, so unlock to allow disconnects */
@@ -1102,12 +1106,9 @@
 	down(&us->dev_semaphore);
 	if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
 		US_DEBUGP("Reset interrupted by disconnect\n");
-		return FAILED;
+		goto Done;
 	}
 
-	/* permit the clear-halt transfers to take place */
-	clear_bit(US_FLIDX_ABORTING, &us->flags);
-
 	US_DEBUGP("Soft reset: clearing bulk-in endpoint halt\n");
 	result = usb_stor_clear_halt(us, us->recv_bulk_pipe);
 
@@ -1117,10 +1118,14 @@
 	/* return a result code based on the result of the control message */
 	if (result < 0 || result2 < 0) {
 		US_DEBUGP("Soft reset failed\n");
-		return FAILED;
+		goto Done;
 	}
 	US_DEBUGP("Soft reset done\n");
-	return SUCCESS;
+	rc = SUCCESS;
+
+  Done:
+	clear_bit(US_FLIDX_RESETTING, &us->flags);
+	return rc;
 }
 
 /* This issues a CB[I] Reset to the device in question
diff -Nru a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h
--- a/drivers/usb/storage/usb.h	Fri May 14 15:32:32 2004
+++ b/drivers/usb/storage/usb.h	Fri May 14 15:32:32 2004
@@ -79,8 +79,9 @@
 #define US_FLIDX_SG_ACTIVE	19  /* 0x00080000  current_sg is in use   */
 #define US_FLIDX_ABORTING	20  /* 0x00100000  abort is in progress   */
 #define US_FLIDX_DISCONNECTING	21  /* 0x00200000  disconnect in progress */
-#define DONT_SUBMIT	((1UL << US_FLIDX_ABORTING) | \
-			 (1UL << US_FLIDX_DISCONNECTING))
+#define ABORTING_OR_DISCONNECTING	((1UL << US_FLIDX_ABORTING) | \
+					 (1UL << US_FLIDX_DISCONNECTING))
+#define US_FLIDX_RESETTING	22  /* 0x00400000  device reset in progress */
 
 
 /* processing state machine states */
