diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/storage/usb.c | 62 |
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 */ | ||
822 | static 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 */ | ||
840 | static 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 */ |
820 | static int usb_stor_scan_thread(void * __us) | 851 | static 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 */ |
970 | BadDevice: | 1001 | BadDevice: |
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 | /*********************************************************************** |