ChangeSet 1.2181.4.61, 2005/03/24 15:13:36-08:00, [EMAIL PROTECTED]
[PATCH] USB Storage: allow disconnect to complete faster
This patch started life as as476 from Alan Stern. It has been rediffed
against the tip, tho that was a few days ago.
This patch makes the disconnect() routine not wait for the control and
scanning threads to exit. This may not seem important now, but it will
become important later: We would end up with a deadlock if disconnect()
(which is called with the device locked) was waiting for the control
thread to exit, while the control thread was waiting to lock the device
so
it could do an autosuspend.
It's necessary to make sure that the host and us_data structures aren't
deallocated before the control and scanning threads are through with
them.
This is done by calling scsi_host_get and scsi_host_put at the start and
end of each thread, before signalling that the threads are running.
Since
the probe() and disconnect() routines cannot run concurrently
(guaranteed
to us by the USB core), this method will guarantee the structures are
not
deallocated too soon.
While there's nothing wrong with leaving the threads alive after
disconnect() returns, there would be a real problem if the threads were
still alive when usb_stor_exit returned! So now usb_stor_exit has to
wait
to make sure all the threads have died. Apparently the only safe way
for
one thread to signal another while exiting is to use complete_and_exit,
which we've been doing. So the patch adds a new driver-wide struct
completion, named threads_gone, and each thread signals it while
exiting.
usb_stor_exit must call wait_for_completion the appropriate number of
times, and that number is stored in a new counter named total_threads.
Signed-off-by: Alan Stern <[EMAIL PROTECTED]>
Signed-off-by: Matthew Dharm <[EMAIL PROTECTED]>
Signed-off-by: Greg Kroah-Hartman <[EMAIL PROTECTED]>
usb.c | 48 +++++++++++++++++++++++++++++++++++++++++-------
1 files changed, 41 insertions(+), 7 deletions(-)
diff -Nru a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
--- a/drivers/usb/storage/usb.c 2005-03-30 15:17:06 -08:00
+++ b/drivers/usb/storage/usb.c 2005-03-30 15:17:06 -08:00
@@ -102,6 +102,13 @@
MODULE_PARM_DESC(delay_use, "seconds to delay before using a new device");
+/* These are used to make sure the module doesn't unload before all the
+ * threads have exited.
+ */
+static atomic_t total_threads = ATOMIC_INIT(0);
+static DECLARE_COMPLETION(threads_gone);
+
+
static int storage_probe(struct usb_interface *iface,
const struct usb_device_id *id);
@@ -286,11 +293,13 @@
* so get rid of all our resources.
*/
daemonize("usb-storage");
-
current->flags |= PF_NOFREEZE;
-
unlock_kernel();
+ /* acquire a reference to the host, so it won't be deallocated
+ * until we're ready to exit */
+ scsi_host_get(host);
+
/* signal that we've started the thread */
complete(&(us->notify));
@@ -394,6 +403,8 @@
up(&(us->dev_semaphore));
} /* for (;;) */
+ scsi_host_put(host);
+
/* notify the exit routine that we're actually exiting now
*
* complete()/wait_for_completion() is similar to up()/down(),
@@ -408,7 +419,7 @@
* This is important in preemption kernels, which transfer the flow
* of execution immediately upon a complete().
*/
- complete_and_exit(&(us->notify), 0);
+ complete_and_exit(&threads_gone, 0);
}
/***********************************************************************
@@ -757,6 +768,7 @@
return p;
}
us->pid = p;
+ atomic_inc(&total_threads);
/* Wait for the thread to start */
wait_for_completion(&(us->notify));
@@ -817,6 +829,13 @@
daemonize("usb-stor-scan");
unlock_kernel();
+ /* Acquire a reference to the host, so it won't be deallocated
+ * until we're ready to exit */
+ scsi_host_get(us_to_host(us));
+
+ /* Signal that we've started the thread */
+ complete(&(us->notify));
+
printk(KERN_DEBUG
"usb-storage: device found at %d\n", us->pusb_dev->devnum);
@@ -838,9 +857,12 @@
if (!test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
scsi_scan_host(us_to_host(us));
printk(KERN_DEBUG "usb-storage: device scan complete\n");
+
+ /* Should we unbind if no devices were detected? */
}
- complete_and_exit(&us->scsi_scan_done, 0);
+ scsi_host_put(us_to_host(us));
+ complete_and_exit(&threads_gone, 0);
}
@@ -941,6 +963,10 @@
scsi_remove_host(host);
goto BadDevice;
}
+ atomic_inc(&total_threads);
+
+ /* Wait for the thread to start */
+ wait_for_completion(&(us->notify));
return 0;
@@ -967,10 +993,8 @@
usb_stor_stop_transport(us);
wake_up(&us->dev_reset_wait);
- /* Interrupt the SCSI-device-scanning thread's time delay, and
- * wait for the thread to finish */
+ /* Interrupt the SCSI-device-scanning thread's time delay */
wake_up(&us->scsi_scan_wait);
- wait_for_completion(&us->scsi_scan_done);
/* Wait for the current command to finish, then remove the host */
down(&us->dev_semaphore);
@@ -1013,6 +1037,16 @@
*/
US_DEBUGP("-- calling usb_deregister()\n");
usb_deregister(&usb_storage_driver) ;
+
+ /* Don't return until all of our control and scanning threads
+ * have exited. Since each thread signals threads_gone as its
+ * last act, we have to call wait_for_completion the right number
+ * of times.
+ */
+ while (atomic_read(&total_threads) > 0) {
+ wait_for_completion(&threads_gone);
+ atomic_dec(&total_threads);
+ }
}
module_init(usb_stor_init);
-
To unsubscribe from this list: send the line "unsubscribe bk-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at http://vger.kernel.org/majordomo-info.html