From 77f46328fb83b64befd889ebce6d7fb959932509 Mon Sep 17 00:00:00 2001 From: Matthew Dharm Date: Thu, 28 Jul 2005 14:44:29 -0700 Subject: [PATCH] [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 Signed-off-by: Matthew Dharm Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/usb.c | 62 ++++++++++++++++++++++----------------- 1 file 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) * any more commands. */ US_DEBUGP("-- sending exit command to thread\n"); + set_bit(US_FLIDX_DISCONNECTING, &us->flags); up(&us->sema); /* Call the destructor routine, if it exists */ @@ -816,6 +817,36 @@ static void dissociate_dev(struct us_data *us) usb_set_intfdata(us->pusb_intf, NULL); } +/* First stage of disconnect processing: stop all commands and remove + * the host */ +static void quiesce_and_remove_host(struct us_data *us) +{ + /* Prevent new USB transfers, stop the current command, and + * interrupt a SCSI-scan or device-reset delay */ + set_bit(US_FLIDX_DISCONNECTING, &us->flags); + usb_stor_stop_transport(us); + wake_up(&us->delay_wait); + + /* It doesn't matter if the SCSI-scanning thread is still running. + * The thread will exit when it sees the DISCONNECTING flag. */ + + /* Wait for the current command to finish, then remove the host */ + down(&us->dev_semaphore); + up(&us->dev_semaphore); + scsi_remove_host(us_to_host(us)); +} + +/* Second stage of disconnect processing: deallocate all resources */ +static void release_everything(struct us_data *us) +{ + usb_stor_release_resources(us); + dissociate_dev(us); + + /* Drop our reference to the host; the SCSI core will free it + * (and "us" along with it) when the refcount becomes 0. */ + scsi_host_put(us_to_host(us)); +} + /* Thread to carry out delayed SCSI-device scanning */ static int usb_stor_scan_thread(void * __us) { @@ -956,7 +987,7 @@ static int storage_probe(struct usb_interface *intf, if (result < 0) { printk(KERN_WARNING USB_STORAGE "Unable to start the device-scanning thread\n"); - scsi_remove_host(host); + quiesce_and_remove_host(us); goto BadDevice; } atomic_inc(&total_threads); @@ -969,10 +1000,7 @@ static int storage_probe(struct usb_interface *intf, /* We come here if there are any problems */ BadDevice: US_DEBUGP("storage_probe() failed\n"); - set_bit(US_FLIDX_DISCONNECTING, &us->flags); - usb_stor_release_resources(us); - dissociate_dev(us); - scsi_host_put(host); + release_everything(us); return result; } @@ -982,28 +1010,8 @@ static void storage_disconnect(struct usb_interface *intf) struct us_data *us = usb_get_intfdata(intf); US_DEBUGP("storage_disconnect() called\n"); - - /* Prevent new USB transfers, stop the current command, and - * interrupt a SCSI-scan or device-reset delay */ - set_bit(US_FLIDX_DISCONNECTING, &us->flags); - usb_stor_stop_transport(us); - wake_up(&us->delay_wait); - - /* It doesn't matter if the SCSI-scanning thread is still running. - * The thread will exit when it sees the DISCONNECTING flag. */ - - /* Wait for the current command to finish, then remove the host */ - down(&us->dev_semaphore); - up(&us->dev_semaphore); - scsi_remove_host(us_to_host(us)); - - /* Wait for everything to become idle and release all our resources */ - usb_stor_release_resources(us); - dissociate_dev(us); - - /* Drop our reference to the host; the SCSI core will free it - * (and "us" along with it) when the refcount becomes 0. */ - scsi_host_put(us_to_host(us)); + quiesce_and_remove_host(us); + release_everything(us); } /***********************************************************************