diff options
Diffstat (limited to 'drivers/usb/storage/usb.c')
-rw-r--r-- | drivers/usb/storage/usb.c | 77 |
1 files changed, 36 insertions, 41 deletions
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 6b14f8d253f1..6bfd99dd57aa 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c | |||
@@ -320,16 +320,17 @@ static int usb_stor_control_thread(void * __us) | |||
320 | /* lock the device pointers */ | 320 | /* lock the device pointers */ |
321 | mutex_lock(&(us->dev_mutex)); | 321 | mutex_lock(&(us->dev_mutex)); |
322 | 322 | ||
323 | /* if the device has disconnected, we are free to exit */ | 323 | /* lock access to the state */ |
324 | if (test_bit(US_FLIDX_DISCONNECTING, &us->dflags)) { | 324 | scsi_lock(host); |
325 | US_DEBUGP("-- exiting\n"); | 325 | |
326 | /* When we are called with no command pending, we're done */ | ||
327 | if (us->srb == NULL) { | ||
328 | scsi_unlock(host); | ||
326 | mutex_unlock(&us->dev_mutex); | 329 | mutex_unlock(&us->dev_mutex); |
330 | US_DEBUGP("-- exiting\n"); | ||
327 | break; | 331 | break; |
328 | } | 332 | } |
329 | 333 | ||
330 | /* lock access to the state */ | ||
331 | scsi_lock(host); | ||
332 | |||
333 | /* has the command timed out *already* ? */ | 334 | /* has the command timed out *already* ? */ |
334 | if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) { | 335 | if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) { |
335 | us->srb->result = DID_ABORT << 16; | 336 | us->srb->result = DID_ABORT << 16; |
@@ -384,12 +385,8 @@ static int usb_stor_control_thread(void * __us) | |||
384 | /* lock access to the state */ | 385 | /* lock access to the state */ |
385 | scsi_lock(host); | 386 | scsi_lock(host); |
386 | 387 | ||
387 | /* did the command already complete because of a disconnect? */ | ||
388 | if (!us->srb) | ||
389 | ; /* nothing to do */ | ||
390 | |||
391 | /* indicate that the command is done */ | 388 | /* indicate that the command is done */ |
392 | else if (us->srb->result != DID_ABORT << 16) { | 389 | if (us->srb->result != DID_ABORT << 16) { |
393 | US_DEBUGP("scsi cmd done, result=0x%x\n", | 390 | US_DEBUGP("scsi cmd done, result=0x%x\n", |
394 | us->srb->result); | 391 | us->srb->result); |
395 | us->srb->scsi_done(us->srb); | 392 | us->srb->scsi_done(us->srb); |
@@ -820,11 +817,10 @@ static void usb_stor_release_resources(struct us_data *us) | |||
820 | US_DEBUGP("-- %s\n", __func__); | 817 | US_DEBUGP("-- %s\n", __func__); |
821 | 818 | ||
822 | /* Tell the control thread to exit. The SCSI host must | 819 | /* Tell the control thread to exit. The SCSI host must |
823 | * already have been removed so it won't try to queue | 820 | * already have been removed and the DISCONNECTING flag set |
824 | * any more commands. | 821 | * so that we won't accept any more commands. |
825 | */ | 822 | */ |
826 | US_DEBUGP("-- sending exit command to thread\n"); | 823 | US_DEBUGP("-- sending exit command to thread\n"); |
827 | set_bit(US_FLIDX_DISCONNECTING, &us->dflags); | ||
828 | complete(&us->cmnd_ready); | 824 | complete(&us->cmnd_ready); |
829 | if (us->ctl_thread) | 825 | if (us->ctl_thread) |
830 | kthread_stop(us->ctl_thread); | 826 | kthread_stop(us->ctl_thread); |
@@ -859,39 +855,36 @@ static void dissociate_dev(struct us_data *us) | |||
859 | usb_set_intfdata(us->pusb_intf, NULL); | 855 | usb_set_intfdata(us->pusb_intf, NULL); |
860 | } | 856 | } |
861 | 857 | ||
862 | /* First stage of disconnect processing: stop all commands and remove | 858 | /* First stage of disconnect processing: stop SCSI scanning, |
863 | * the host */ | 859 | * remove the host, and stop accepting new commands |
860 | */ | ||
864 | static void quiesce_and_remove_host(struct us_data *us) | 861 | static void quiesce_and_remove_host(struct us_data *us) |
865 | { | 862 | { |
866 | struct Scsi_Host *host = us_to_host(us); | 863 | struct Scsi_Host *host = us_to_host(us); |
867 | 864 | ||
868 | /* Prevent new USB transfers, stop the current command, and | 865 | /* If the device is really gone, cut short reset delays */ |
869 | * interrupt a SCSI-scan or device-reset delay */ | 866 | if (us->pusb_dev->state == USB_STATE_NOTATTACHED) |
870 | scsi_lock(host); | 867 | set_bit(US_FLIDX_DISCONNECTING, &us->dflags); |
871 | set_bit(US_FLIDX_DISCONNECTING, &us->dflags); | ||
872 | scsi_unlock(host); | ||
873 | usb_stor_stop_transport(us); | ||
874 | wake_up(&us->delay_wait); | ||
875 | 868 | ||
876 | /* queuecommand won't accept any new commands and the control | 869 | /* Prevent SCSI-scanning (if it hasn't started yet) |
877 | * thread won't execute a previously-queued command. If there | 870 | * and wait for the SCSI-scanning thread to stop. |
878 | * is such a command pending, complete it with an error. */ | 871 | */ |
879 | mutex_lock(&us->dev_mutex); | 872 | set_bit(US_FLIDX_DONT_SCAN, &us->dflags); |
880 | if (us->srb) { | 873 | wake_up(&us->delay_wait); |
881 | us->srb->result = DID_NO_CONNECT << 16; | 874 | wait_for_completion(&us->scanning_done); |
882 | scsi_lock(host); | ||
883 | us->srb->scsi_done(us->srb); | ||
884 | us->srb = NULL; | ||
885 | complete(&us->notify); /* in case of an abort */ | ||
886 | scsi_unlock(host); | ||
887 | } | ||
888 | mutex_unlock(&us->dev_mutex); | ||
889 | 875 | ||
890 | /* Now we own no commands so it's safe to remove the SCSI host */ | 876 | /* Removing the host will perform an orderly shutdown: caches |
877 | * synchronized, disks spun down, etc. | ||
878 | */ | ||
891 | scsi_remove_host(host); | 879 | scsi_remove_host(host); |
892 | 880 | ||
893 | /* Wait for the SCSI-scanning thread to stop */ | 881 | /* Prevent any new commands from being accepted and cut short |
894 | wait_for_completion(&us->scanning_done); | 882 | * reset delays. |
883 | */ | ||
884 | scsi_lock(host); | ||
885 | set_bit(US_FLIDX_DISCONNECTING, &us->dflags); | ||
886 | scsi_unlock(host); | ||
887 | wake_up(&us->delay_wait); | ||
895 | } | 888 | } |
896 | 889 | ||
897 | /* Second stage of disconnect processing: deallocate all resources */ | 890 | /* Second stage of disconnect processing: deallocate all resources */ |
@@ -919,12 +912,12 @@ static int usb_stor_scan_thread(void * __us) | |||
919 | printk(KERN_DEBUG "usb-storage: waiting for device " | 912 | printk(KERN_DEBUG "usb-storage: waiting for device " |
920 | "to settle before scanning\n"); | 913 | "to settle before scanning\n"); |
921 | wait_event_freezable_timeout(us->delay_wait, | 914 | wait_event_freezable_timeout(us->delay_wait, |
922 | test_bit(US_FLIDX_DISCONNECTING, &us->dflags), | 915 | test_bit(US_FLIDX_DONT_SCAN, &us->dflags), |
923 | delay_use * HZ); | 916 | delay_use * HZ); |
924 | } | 917 | } |
925 | 918 | ||
926 | /* If the device is still connected, perform the scanning */ | 919 | /* If the device is still connected, perform the scanning */ |
927 | if (!test_bit(US_FLIDX_DISCONNECTING, &us->dflags)) { | 920 | if (!test_bit(US_FLIDX_DONT_SCAN, &us->dflags)) { |
928 | 921 | ||
929 | /* For bulk-only devices, determine the max LUN value */ | 922 | /* For bulk-only devices, determine the max LUN value */ |
930 | if (us->protocol == US_PR_BULK && | 923 | if (us->protocol == US_PR_BULK && |
@@ -1023,6 +1016,7 @@ static int storage_probe(struct usb_interface *intf, | |||
1023 | if (IS_ERR(th)) { | 1016 | if (IS_ERR(th)) { |
1024 | printk(KERN_WARNING USB_STORAGE | 1017 | printk(KERN_WARNING USB_STORAGE |
1025 | "Unable to start the device-scanning thread\n"); | 1018 | "Unable to start the device-scanning thread\n"); |
1019 | complete(&us->scanning_done); | ||
1026 | quiesce_and_remove_host(us); | 1020 | quiesce_and_remove_host(us); |
1027 | result = PTR_ERR(th); | 1021 | result = PTR_ERR(th); |
1028 | goto BadDevice; | 1022 | goto BadDevice; |
@@ -1065,6 +1059,7 @@ static struct usb_driver usb_storage_driver = { | |||
1065 | .pre_reset = storage_pre_reset, | 1059 | .pre_reset = storage_pre_reset, |
1066 | .post_reset = storage_post_reset, | 1060 | .post_reset = storage_post_reset, |
1067 | .id_table = storage_usb_ids, | 1061 | .id_table = storage_usb_ids, |
1062 | .soft_unbind = 1, | ||
1068 | }; | 1063 | }; |
1069 | 1064 | ||
1070 | static int __init usb_stor_init(void) | 1065 | static int __init usb_stor_init(void) |