summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorStuart Hayes <stuart_hayes@dell.com>2012-07-22 20:27:23 +0100
committerWilly Tarreau <w@1wt.eu>2012-10-07 23:37:12 +0200
commitdad11ff11f3c2e70e54e9d92a914694427cc5bb1 (patch)
tree86b96a7cafb5342f61904d610b77b0842030c07d /kernel
parent3ca9407c5c2c246ed54197568e9559a33219387a (diff)
usb: Fix deadlock in hid_reset when Dell iDRAC is reset
This was fixed upstream by commit e22bee782b3b00bd4534ae9b1c5fb2e8e6573c5c ('workqueue: implement concurrency managed dynamic worker pool'), but that is far too large a change for stable. When Dell iDRAC is reset, the iDRAC's USB keyboard/mouse device stops responding but is not actually disconnected. This causes usbhid to hid hid_io_error(), and you get a chain of calls like... hid_reset() usb_reset_device() usb_reset_and_verify_device() usb_ep0_reinit() usb_disble_endpoint() usb_hcd_disable_endpoint() ehci_endpoint_disable() Along the way, as a result of an error/timeout with a USB transaction, ehci_clear_tt_buffer() calls usb_hub_clear_tt_buffer() (to clear a failed transaction out of the transaction translator in the hub), which schedules hub_tt_work() to be run (using keventd), and then sets qh->clearing_tt=1 so that nobody will mess with that qh until the TT is cleared. But run_workqueue() never happens for the keventd workqueue on that CPU, so hub_tt_work() never gets run. And qh->clearing_tt never gets changed back to 0. This causes ehci_endpoint_disable() to get stuck in a loop waiting for qh->clearing_tt to go to 0. Part of the problem is hid_reset() is itself running on keventd. So when that thread gets a timeout trying to talk to the HID device, it schedules clear_work (to run hub_tt_work) to run, and then gets stuck in ehci_endpoint_disable waiting for it to run. However, clear_work never gets run because the workqueue for that CPU is still waiting for hid_reset to finish. A much less invasive patch for earlier kernels is to just schedule clear_work on khubd if the usb code needs to clear the TT and it sees that it is already running on keventd. Khubd isn't used by default because it can get blocked by device enumeration sometimes, but I think it should be ok for a backup for unusual cases like this just to prevent deadlock. Signed-off-by: Stuart Hayes <stuart_hayes@dell.com> Signed-off-by: Shyam Iyer <shyam_iyer@dell.com> [bwh: Use current_is_keventd() rather than checking current->{flags,comm}] Signed-off-by: Ben Hutchings <ben@decadent.org.uk> Signed-off-by: Willy Tarreau <w@1wt.eu>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/workqueue.c1
1 files changed, 1 insertions, 0 deletions
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 67e526b6ae81..b617e0c06cc9 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -772,6 +772,7 @@ int current_is_keventd(void)
return ret;
}
+EXPORT_SYMBOL_GPL(current_is_keventd);
static struct cpu_workqueue_struct *
init_cpu_workqueue(struct workqueue_struct *wq, int cpu)