aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/storage/usb.c
diff options
context:
space:
mode:
authorMatthew Dharm <mdharm-usb@one-eyed-alien.net>2005-07-28 17:44:29 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2005-09-08 19:22:54 -0400
commit77f46328fb83b64befd889ebce6d7fb959932509 (patch)
treec06b5e60d8463e548b49c73799b2327ecc4f09de /drivers/usb/storage/usb.c
parent0f64e078139109d1902e5b1274c23cec9a9ad12e (diff)
[PATCH] USB Storage: close a race condition in disconnect near probe
This patch started life as as533, and has been re-diffed against the current tree. Disconnect processing in usb-storage naturally divides into two parts: one to quiesce the driver (make sure no commands are executing or queued) and remove the host, and the other to deallocate all the USB and non-USB resources. This patch creates two subroutines to handle those two parts. Mostly it's just code movement, but there is one significant change. If the scsi-scanning thread fails to initialize but the host has successfully been added, we need to quiesce the driver before removing the host. After all, it's possible that scanning could have been initiated from somewhere else, such as userspace -- very low probability, but it's easily handled by calling the new subroutine. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Matthew Dharm <mdharm-usb@one-eyed-alien.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/storage/usb.c')
-rw-r--r--drivers/usb/storage/usb.c62
1 files changed, 35 insertions, 27 deletions
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index 77e7fc258aa2..255771151399 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -786,6 +786,7 @@ static void usb_stor_release_resources(struct us_data *us)
786 * any more commands. 786 * any more commands.
787 */ 787 */
788 US_DEBUGP("-- sending exit command to thread\n"); 788 US_DEBUGP("-- sending exit command to thread\n");
789 set_bit(US_FLIDX_DISCONNECTING, &us->flags);
789 up(&us->sema); 790 up(&us->sema);
790 791
791 /* Call the destructor routine, if it exists */ 792 /* Call the destructor routine, if it exists */
@@ -816,6 +817,36 @@ static void dissociate_dev(struct us_data *us)
816 usb_set_intfdata(us->pusb_intf, NULL); 817 usb_set_intfdata(us->pusb_intf, NULL);
817} 818}
818 819
820/* First stage of disconnect processing: stop all commands and remove
821 * the host */
822static void quiesce_and_remove_host(struct us_data *us)
823{
824 /* Prevent new USB transfers, stop the current command, and
825 * interrupt a SCSI-scan or device-reset delay */
826 set_bit(US_FLIDX_DISCONNECTING, &us->flags);
827 usb_stor_stop_transport(us);
828 wake_up(&us->delay_wait);
829
830 /* It doesn't matter if the SCSI-scanning thread is still running.
831 * The thread will exit when it sees the DISCONNECTING flag. */
832
833 /* Wait for the current command to finish, then remove the host */
834 down(&us->dev_semaphore);
835 up(&us->dev_semaphore);
836 scsi_remove_host(us_to_host(us));
837}
838
839/* Second stage of disconnect processing: deallocate all resources */
840static void release_everything(struct us_data *us)
841{
842 usb_stor_release_resources(us);
843 dissociate_dev(us);
844
845 /* Drop our reference to the host; the SCSI core will free it
846 * (and "us" along with it) when the refcount becomes 0. */
847 scsi_host_put(us_to_host(us));
848}
849
819/* Thread to carry out delayed SCSI-device scanning */ 850/* Thread to carry out delayed SCSI-device scanning */
820static int usb_stor_scan_thread(void * __us) 851static int usb_stor_scan_thread(void * __us)
821{ 852{
@@ -956,7 +987,7 @@ static int storage_probe(struct usb_interface *intf,
956 if (result < 0) { 987 if (result < 0) {
957 printk(KERN_WARNING USB_STORAGE 988 printk(KERN_WARNING USB_STORAGE
958 "Unable to start the device-scanning thread\n"); 989 "Unable to start the device-scanning thread\n");
959 scsi_remove_host(host); 990 quiesce_and_remove_host(us);
960 goto BadDevice; 991 goto BadDevice;
961 } 992 }
962 atomic_inc(&total_threads); 993 atomic_inc(&total_threads);
@@ -969,10 +1000,7 @@ static int storage_probe(struct usb_interface *intf,
969 /* We come here if there are any problems */ 1000 /* We come here if there are any problems */
970BadDevice: 1001BadDevice:
971 US_DEBUGP("storage_probe() failed\n"); 1002 US_DEBUGP("storage_probe() failed\n");
972 set_bit(US_FLIDX_DISCONNECTING, &us->flags); 1003 release_everything(us);
973 usb_stor_release_resources(us);
974 dissociate_dev(us);
975 scsi_host_put(host);
976 return result; 1004 return result;
977} 1005}
978 1006
@@ -982,28 +1010,8 @@ static void storage_disconnect(struct usb_interface *intf)
982 struct us_data *us = usb_get_intfdata(intf); 1010 struct us_data *us = usb_get_intfdata(intf);
983 1011
984 US_DEBUGP("storage_disconnect() called\n"); 1012 US_DEBUGP("storage_disconnect() called\n");
985 1013 quiesce_and_remove_host(us);
986 /* Prevent new USB transfers, stop the current command, and 1014 release_everything(us);
987 * interrupt a SCSI-scan or device-reset delay */
988 set_bit(US_FLIDX_DISCONNECTING, &us->flags);
989 usb_stor_stop_transport(us);
990 wake_up(&us->delay_wait);
991
992 /* It doesn't matter if the SCSI-scanning thread is still running.
993 * The thread will exit when it sees the DISCONNECTING flag. */
994
995 /* Wait for the current command to finish, then remove the host */
996 down(&us->dev_semaphore);
997 up(&us->dev_semaphore);
998 scsi_remove_host(us_to_host(us));
999
1000 /* Wait for everything to become idle and release all our resources */
1001 usb_stor_release_resources(us);
1002 dissociate_dev(us);
1003
1004 /* Drop our reference to the host; the SCSI core will free it
1005 * (and "us" along with it) when the refcount becomes 0. */
1006 scsi_host_put(us_to_host(us));
1007} 1015}
1008 1016
1009/*********************************************************************** 1017/***********************************************************************