Commit bb94a406 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

usb-storage: fix freezing of the scanning thread

This patch (as1521b) fixes the interaction between usb-storage's
scanning thread and the freezer.  The current implementation has a
race: If the device is unplugged shortly after being plugged in and
just as a system sleep begins, the scanning thread may get frozen
before the khubd task.  Khubd won't be able to freeze until the
disconnect processing is complete, and the disconnect processing can't
proceed until the scanning thread finishes, so the sleep transition
will fail.

The implementation in the 3.2 kernel suffers from an additional
problem.  There the scanning thread calls set_freezable_with_signal(),
and the signals sent by the freezer will mess up the thread's I/O
delays, which are all interruptible.

The solution to both problems is the same: Replace the kernel thread
used for scanning with a delayed-work routine on the system freezable
work queue.  Freezable work queues have the nice property that you can
cancel a work item even while the work queue is frozen, and no signals
are needed.

The 3.2 version of this patch solves the problem in Bugzilla #42730.
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Acked-by: default avatarSeth Forshee <seth.forshee@canonical.com>
CC: stable <stable@vger.kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 9a9a71b7
...@@ -788,15 +788,19 @@ static void quiesce_and_remove_host(struct us_data *us) ...@@ -788,15 +788,19 @@ static void quiesce_and_remove_host(struct us_data *us)
struct Scsi_Host *host = us_to_host(us); struct Scsi_Host *host = us_to_host(us);
/* If the device is really gone, cut short reset delays */ /* If the device is really gone, cut short reset delays */
if (us->pusb_dev->state == USB_STATE_NOTATTACHED) if (us->pusb_dev->state == USB_STATE_NOTATTACHED) {
set_bit(US_FLIDX_DISCONNECTING, &us->dflags); set_bit(US_FLIDX_DISCONNECTING, &us->dflags);
wake_up(&us->delay_wait);
}
/* Prevent SCSI-scanning (if it hasn't started yet) /* Prevent SCSI scanning (if it hasn't started yet)
* and wait for the SCSI-scanning thread to stop. * or wait for the SCSI-scanning routine to stop.
*/ */
set_bit(US_FLIDX_DONT_SCAN, &us->dflags); cancel_delayed_work_sync(&us->scan_dwork);
wake_up(&us->delay_wait);
wait_for_completion(&us->scanning_done); /* Balance autopm calls if scanning was cancelled */
if (test_bit(US_FLIDX_SCAN_PENDING, &us->dflags))
usb_autopm_put_interface_no_suspend(us->pusb_intf);
/* Removing the host will perform an orderly shutdown: caches /* Removing the host will perform an orderly shutdown: caches
* synchronized, disks spun down, etc. * synchronized, disks spun down, etc.
...@@ -823,53 +827,28 @@ static void release_everything(struct us_data *us) ...@@ -823,53 +827,28 @@ static void release_everything(struct us_data *us)
scsi_host_put(us_to_host(us)); scsi_host_put(us_to_host(us));
} }
/* Thread to carry out delayed SCSI-device scanning */ /* Delayed-work routine to carry out SCSI-device scanning */
static int usb_stor_scan_thread(void * __us) static void usb_stor_scan_dwork(struct work_struct *work)
{ {
struct us_data *us = (struct us_data *)__us; struct us_data *us = container_of(work, struct us_data,
scan_dwork.work);
struct device *dev = &us->pusb_intf->dev; struct device *dev = &us->pusb_intf->dev;
dev_dbg(dev, "device found\n"); dev_dbg(dev, "starting scan\n");
set_freezable();
/* /* For bulk-only devices, determine the max LUN value */
* Wait for the timeout to expire or for a disconnect if (us->protocol == USB_PR_BULK && !(us->fflags & US_FL_SINGLE_LUN)) {
* mutex_lock(&us->dev_mutex);
* We can't freeze in this thread or we risk causing khubd to us->max_lun = usb_stor_Bulk_max_lun(us);
* fail to freeze, but we can't be non-freezable either. Nor can mutex_unlock(&us->dev_mutex);
* khubd freeze while waiting for scanning to complete as it may
* hold the device lock, causing a hang when suspending devices.
* So instead of using wait_event_freezable(), explicitly test
* for (DONT_SCAN || freezing) in interruptible wait and proceed
* if any of DONT_SCAN, freezing or timeout has happened.
*/
if (delay_use > 0) {
dev_dbg(dev, "waiting for device to settle "
"before scanning\n");
wait_event_interruptible_timeout(us->delay_wait,
test_bit(US_FLIDX_DONT_SCAN, &us->dflags) ||
freezing(current), delay_use * HZ);
} }
scsi_scan_host(us_to_host(us));
dev_dbg(dev, "scan complete\n");
/* If the device is still connected, perform the scanning */ /* Should we unbind if no devices were detected? */
if (!test_bit(US_FLIDX_DONT_SCAN, &us->dflags)) {
/* For bulk-only devices, determine the max LUN value */
if (us->protocol == USB_PR_BULK &&
!(us->fflags & US_FL_SINGLE_LUN)) {
mutex_lock(&us->dev_mutex);
us->max_lun = usb_stor_Bulk_max_lun(us);
mutex_unlock(&us->dev_mutex);
}
scsi_scan_host(us_to_host(us));
dev_dbg(dev, "scan complete\n");
/* Should we unbind if no devices were detected? */
}
usb_autopm_put_interface(us->pusb_intf); usb_autopm_put_interface(us->pusb_intf);
complete_and_exit(&us->scanning_done, 0); clear_bit(US_FLIDX_SCAN_PENDING, &us->dflags);
} }
static unsigned int usb_stor_sg_tablesize(struct usb_interface *intf) static unsigned int usb_stor_sg_tablesize(struct usb_interface *intf)
...@@ -916,7 +895,7 @@ int usb_stor_probe1(struct us_data **pus, ...@@ -916,7 +895,7 @@ int usb_stor_probe1(struct us_data **pus,
init_completion(&us->cmnd_ready); init_completion(&us->cmnd_ready);
init_completion(&(us->notify)); init_completion(&(us->notify));
init_waitqueue_head(&us->delay_wait); init_waitqueue_head(&us->delay_wait);
init_completion(&us->scanning_done); INIT_DELAYED_WORK(&us->scan_dwork, usb_stor_scan_dwork);
/* Associate the us_data structure with the USB device */ /* Associate the us_data structure with the USB device */
result = associate_dev(us, intf); result = associate_dev(us, intf);
...@@ -947,7 +926,6 @@ EXPORT_SYMBOL_GPL(usb_stor_probe1); ...@@ -947,7 +926,6 @@ EXPORT_SYMBOL_GPL(usb_stor_probe1);
/* Second part of general USB mass-storage probing */ /* Second part of general USB mass-storage probing */
int usb_stor_probe2(struct us_data *us) int usb_stor_probe2(struct us_data *us)
{ {
struct task_struct *th;
int result; int result;
struct device *dev = &us->pusb_intf->dev; struct device *dev = &us->pusb_intf->dev;
...@@ -988,20 +966,14 @@ int usb_stor_probe2(struct us_data *us) ...@@ -988,20 +966,14 @@ int usb_stor_probe2(struct us_data *us)
goto BadDevice; goto BadDevice;
} }
/* Start up the thread for delayed SCSI-device scanning */ /* Submit the delayed_work for SCSI-device scanning */
th = kthread_create(usb_stor_scan_thread, us, "usb-stor-scan");
if (IS_ERR(th)) {
dev_warn(dev,
"Unable to start the device-scanning thread\n");
complete(&us->scanning_done);
quiesce_and_remove_host(us);
result = PTR_ERR(th);
goto BadDevice;
}
usb_autopm_get_interface_no_resume(us->pusb_intf); usb_autopm_get_interface_no_resume(us->pusb_intf);
wake_up_process(th); set_bit(US_FLIDX_SCAN_PENDING, &us->dflags);
if (delay_use > 0)
dev_dbg(dev, "waiting for device to settle before scanning\n");
queue_delayed_work(system_freezable_wq, &us->scan_dwork,
delay_use * HZ);
return 0; return 0;
/* We come here if there are any problems */ /* We come here if there are any problems */
......
...@@ -47,6 +47,7 @@ ...@@ -47,6 +47,7 @@
#include <linux/blkdev.h> #include <linux/blkdev.h>
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/workqueue.h>
#include <scsi/scsi_host.h> #include <scsi/scsi_host.h>
struct us_data; struct us_data;
...@@ -72,7 +73,7 @@ struct us_unusual_dev { ...@@ -72,7 +73,7 @@ struct us_unusual_dev {
#define US_FLIDX_DISCONNECTING 3 /* disconnect in progress */ #define US_FLIDX_DISCONNECTING 3 /* disconnect in progress */
#define US_FLIDX_RESETTING 4 /* device reset in progress */ #define US_FLIDX_RESETTING 4 /* device reset in progress */
#define US_FLIDX_TIMED_OUT 5 /* SCSI midlayer timed out */ #define US_FLIDX_TIMED_OUT 5 /* SCSI midlayer timed out */
#define US_FLIDX_DONT_SCAN 6 /* don't scan (disconnect) */ #define US_FLIDX_SCAN_PENDING 6 /* scanning not yet done */
#define US_FLIDX_REDO_READ10 7 /* redo READ(10) command */ #define US_FLIDX_REDO_READ10 7 /* redo READ(10) command */
#define US_FLIDX_READ10_WORKED 8 /* previous READ(10) succeeded */ #define US_FLIDX_READ10_WORKED 8 /* previous READ(10) succeeded */
...@@ -147,8 +148,8 @@ struct us_data { ...@@ -147,8 +148,8 @@ struct us_data {
/* mutual exclusion and synchronization structures */ /* mutual exclusion and synchronization structures */
struct completion cmnd_ready; /* to sleep thread on */ struct completion cmnd_ready; /* to sleep thread on */
struct completion notify; /* thread begin/end */ struct completion notify; /* thread begin/end */
wait_queue_head_t delay_wait; /* wait during scan, reset */ wait_queue_head_t delay_wait; /* wait during reset */
struct completion scanning_done; /* wait for scan thread */ struct delayed_work scan_dwork; /* for async scanning */
/* subdriver information */ /* subdriver information */
void *extra; /* Any extra data */ void *extra; /* Any extra data */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment