aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2006-06-19 14:50:15 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2006-07-12 19:03:21 -0400
commiteecd11ed47c8c9bd8e7e3dff35baccae18dd0d84 (patch)
tree7d91cf5b80a59ff84cdd2bf5f4ae96e82e026276 /drivers/usb
parent3dd2ae81f70f191f5b6751d18fdfe61dbafda7e8 (diff)
[PATCH] usb-storage: fix race between reset and disconnect
My recent patch converting usb-storage to use usb_reset_composite_device() added a bug, a race between reset and disconnect. It was necessary to drop the private lock while executing a reset, and if a disconnect occurs at that time it will cause a crash. This patch (as722) fixes the problem by explicitly checking for an early termination after executing each command. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/storage/usb.c22
1 files changed, 14 insertions, 8 deletions
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index 1185acac4b21..04c3bec81201 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -373,8 +373,12 @@ static int usb_stor_control_thread(void * __us)
373 /* lock access to the state */ 373 /* lock access to the state */
374 scsi_lock(host); 374 scsi_lock(host);
375 375
376 /* did the command already complete because of a disconnect? */
377 if (!us->srb)
378 ; /* nothing to do */
379
376 /* indicate that the command is done */ 380 /* indicate that the command is done */
377 if (us->srb->result != DID_ABORT << 16) { 381 else if (us->srb->result != DID_ABORT << 16) {
378 US_DEBUGP("scsi cmd done, result=0x%x\n", 382 US_DEBUGP("scsi cmd done, result=0x%x\n",
379 us->srb->result); 383 us->srb->result);
380 us->srb->scsi_done(us->srb); 384 us->srb->scsi_done(us->srb);
@@ -836,32 +840,34 @@ static void dissociate_dev(struct us_data *us)
836 * the host */ 840 * the host */
837static void quiesce_and_remove_host(struct us_data *us) 841static void quiesce_and_remove_host(struct us_data *us)
838{ 842{
843 struct Scsi_Host *host = us_to_host(us);
844
839 /* Prevent new USB transfers, stop the current command, and 845 /* Prevent new USB transfers, stop the current command, and
840 * interrupt a SCSI-scan or device-reset delay */ 846 * interrupt a SCSI-scan or device-reset delay */
847 scsi_lock(host);
841 set_bit(US_FLIDX_DISCONNECTING, &us->flags); 848 set_bit(US_FLIDX_DISCONNECTING, &us->flags);
849 scsi_unlock(host);
842 usb_stor_stop_transport(us); 850 usb_stor_stop_transport(us);
843 wake_up(&us->delay_wait); 851 wake_up(&us->delay_wait);
844 852
845 /* It doesn't matter if the SCSI-scanning thread is still running. 853 /* It doesn't matter if the SCSI-scanning thread is still running.
846 * The thread will exit when it sees the DISCONNECTING flag. */ 854 * The thread will exit when it sees the DISCONNECTING flag. */
847 855
848 /* Wait for the current command to finish, then remove the host */
849 mutex_lock(&us->dev_mutex);
850 mutex_unlock(&us->dev_mutex);
851
852 /* queuecommand won't accept any new commands and the control 856 /* queuecommand won't accept any new commands and the control
853 * thread won't execute a previously-queued command. If there 857 * thread won't execute a previously-queued command. If there
854 * is such a command pending, complete it with an error. */ 858 * is such a command pending, complete it with an error. */
859 mutex_lock(&us->dev_mutex);
855 if (us->srb) { 860 if (us->srb) {
856 us->srb->result = DID_NO_CONNECT << 16; 861 us->srb->result = DID_NO_CONNECT << 16;
857 scsi_lock(us_to_host(us)); 862 scsi_lock(host);
858 us->srb->scsi_done(us->srb); 863 us->srb->scsi_done(us->srb);
859 us->srb = NULL; 864 us->srb = NULL;
860 scsi_unlock(us_to_host(us)); 865 scsi_unlock(host);
861 } 866 }
867 mutex_unlock(&us->dev_mutex);
862 868
863 /* Now we own no commands so it's safe to remove the SCSI host */ 869 /* Now we own no commands so it's safe to remove the SCSI host */
864 scsi_remove_host(us_to_host(us)); 870 scsi_remove_host(host);
865} 871}
866 872
867/* Second stage of disconnect processing: deallocate all resources */ 873/* Second stage of disconnect processing: deallocate all resources */