diff options
author | Hemant Kumar <hemantk@codeaurora.org> | 2016-10-03 11:32:10 -0700 |
---|---|---|
committer | Hemant Kumar <hemantk@codeaurora.org> | 2016-10-06 10:47:24 -0700 |
commit | 20caa4fd629eb30220ef6c4973802123ecd8a01a (patch) | |
tree | b7320912d42ad3a655d00474d083da291404058b | |
parent | 0b3886773c113fe0ad5748d8f5a9d1648fafdee2 (diff) |
usb: host: Flush hub workqueue before stopping controller
when xhci platform driver is removed with a usb device
connected there is a possibility of race between xhci
platform driver remove context and device disconnect hub
event work context. This results into
use after free: xhci_mem_clean() called from xhci_plat_remove()
freeing the xhci virtual device when a structure member is
being accessed by xhci_alloc_virt_device() after virtual device
allocation as part of connect hub event.
deadlock: Upon device disconnect if stop endpoint command remains
pending xhci_mem_cleanup() frees xhci virtual device which also
frees up endpoint stop_cmd_timer. udev->dev lock is held until
cancelled urb is not given back to class driver. In a different
context xhci platform remove calls usb_disconnect() on children of
root hub which tries to acquire the same udev->dev lock this results
into a dead lock condition.
Fix issues by making sure hub events are flushed before xhci is
stopped.
Change-Id: I86d414bca17464d1dff3346ec668d8b3efec1652
Signed-off-by: Hemant Kumar <hemantk@codeaurora.org>
-rw-r--r-- | drivers/usb/core/hcd.c | 3 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 6 | ||||
-rw-r--r-- | include/linux/usb/hcd.h | 2 |
3 files changed, 10 insertions, 1 deletions
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 3df80c73b74a..ac0eb0939ecf 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2990,6 +2990,9 @@ void usb_remove_hcd(struct usb_hcd *hcd) cancel_work_sync(&hcd->wakeup_work); #endif + /* handle any pending hub events before XHCI stops */ + usb_flush_hub_wq(); + mutex_lock(&usb_bus_list_lock); usb_disconnect(&rhdev); /* Sets rhdev to NULL */ mutex_unlock(&usb_bus_list_lock); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 84df093639ac..269c1ee2da44 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -610,6 +610,12 @@ void usb_kick_hub_wq(struct usb_device *hdev) kick_hub_wq(hub); } +void usb_flush_hub_wq(void) +{ + flush_workqueue(hub_wq); +} +EXPORT_SYMBOL(usb_flush_hub_wq); + /* * Let the USB core know that a USB 3.0 device has sent a Function Wake Device * Notification, which indicates it had initiated remote wakeup. diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 3740366d9fc5..cef429cf3dce 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -502,7 +502,7 @@ extern void usb_hc_died(struct usb_hcd *hcd); extern void usb_hcd_poll_rh_status(struct usb_hcd *hcd); extern void usb_wakeup_notification(struct usb_device *hdev, unsigned int portnum); - +extern void usb_flush_hub_wq(void); extern void usb_hcd_start_port_resume(struct usb_bus *bus, int portnum); extern void usb_hcd_end_port_resume(struct usb_bus *bus, int portnum); |