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

Commit 92d60ad4 authored by Hemant Kumar's avatar Hemant Kumar Committed by Gerrit - the friendly Code Review server
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 9bd05f8c
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -2967,6 +2967,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_idr_lock);
	usb_disconnect(&rhdev);		/* Sets rhdev to NULL */
	mutex_unlock(&usb_bus_idr_lock);
+6 −0
Original line number Diff line number Diff line
@@ -641,6 +641,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
@@ -497,7 +497,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);