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 */