Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9e40f5c3 authored by Hemant Kumar's avatar Hemant Kumar Committed by Ajay Agarwal
Browse files

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: default avatarHemant Kumar <hemantk@codeaurora.org>
parent 4b85ad60
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -2869,6 +2869,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);
+6 −0
Original line number Diff line number Diff line
@@ -606,6 +606,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.
+1 −1
Original line number Diff line number Diff line
@@ -474,7 +474,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);