Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

xhci: Fix leaking USB3 shared_hcd at xhci removal

Ensure that the shared_hcd pointer is valid when calling usb_put_hcd()

The shared_hcd is removed and freed in xhci by first calling
usb_remove_hcd(xhci->shared_hcd), and later
usb_put_hcd(xhci->shared_hcd)

Afer commit fe190ed0d602 ("xhci: Do not halt the host until both HCD have
disconnected their devices.") the shared_hcd was never properly put as
xhci->shared_hcd was set to NULL before usb_put_hcd(xhci->shared_hcd) was
called.

shared_hcd (USB3) is removed before primary hcd (USB2).
While removing the primary hcd we might need to handle xhci interrupts
to cleanly remove last USB2 devices, therefore we need to set
xhci->shared_hcd to NULL before removing the primary hcd to let xhci
interrupt handler know shared_hcd is no longer available.

xhci-plat.c, xhci-histb.c and xhci-mtk first create both their hcd's before
adding them. so to keep the correct reverse removal order use a temporary
shared_hcd variable for them.
For more details see commit 4ac53087d6d4 ("usb: xhci: plat: Create both
HCDs before adding them")

Fixes: fe190ed0d602 ("xhci: Do not halt the host until both HCD have disconnected their devices.")
Cc: Joel Stanley <joel@jms.id.au>
Cc: Chunfeng Yun <chunfeng.yun@mediatek.com>
Cc: Thierry Reding <treding@nvidia.com>
Cc: Jianguo Sun <sunjianguo1@huawei.com>
Cc: <stable@vger.kernel.org>
Reported-by: Jack Pham <jackp@codeaurora.org>
Tested-by: Jack Pham <jackp@codeaurora.org>
Tested-by: Peter Chen <peter.chen@nxp.com>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Mathias Nyman and committed by
Greg Kroah-Hartman
f0680904 f6501f49

+14 -8
+4 -2
drivers/usb/host/xhci-histb.c
··· 325 325 struct xhci_hcd_histb *histb = platform_get_drvdata(dev); 326 326 struct usb_hcd *hcd = histb->hcd; 327 327 struct xhci_hcd *xhci = hcd_to_xhci(hcd); 328 + struct usb_hcd *shared_hcd = xhci->shared_hcd; 328 329 329 330 xhci->xhc_state |= XHCI_STATE_REMOVING; 330 331 331 - usb_remove_hcd(xhci->shared_hcd); 332 + usb_remove_hcd(shared_hcd); 333 + xhci->shared_hcd = NULL; 332 334 device_wakeup_disable(&dev->dev); 333 335 334 336 usb_remove_hcd(hcd); 335 - usb_put_hcd(xhci->shared_hcd); 337 + usb_put_hcd(shared_hcd); 336 338 337 339 xhci_histb_host_disable(histb); 338 340 usb_put_hcd(hcd);
+4 -2
drivers/usb/host/xhci-mtk.c
··· 590 590 struct xhci_hcd_mtk *mtk = platform_get_drvdata(dev); 591 591 struct usb_hcd *hcd = mtk->hcd; 592 592 struct xhci_hcd *xhci = hcd_to_xhci(hcd); 593 + struct usb_hcd *shared_hcd = xhci->shared_hcd; 593 594 594 - usb_remove_hcd(xhci->shared_hcd); 595 + usb_remove_hcd(shared_hcd); 596 + xhci->shared_hcd = NULL; 595 597 device_init_wakeup(&dev->dev, false); 596 598 597 599 usb_remove_hcd(hcd); 598 - usb_put_hcd(xhci->shared_hcd); 600 + usb_put_hcd(shared_hcd); 599 601 usb_put_hcd(hcd); 600 602 xhci_mtk_sch_exit(mtk); 601 603 xhci_mtk_clks_disable(mtk);
+1
drivers/usb/host/xhci-pci.c
··· 380 380 if (xhci->shared_hcd) { 381 381 usb_remove_hcd(xhci->shared_hcd); 382 382 usb_put_hcd(xhci->shared_hcd); 383 + xhci->shared_hcd = NULL; 383 384 } 384 385 385 386 /* Workaround for spurious wakeups at shutdown with HSW */
+4 -2
drivers/usb/host/xhci-plat.c
··· 362 362 struct xhci_hcd *xhci = hcd_to_xhci(hcd); 363 363 struct clk *clk = xhci->clk; 364 364 struct clk *reg_clk = xhci->reg_clk; 365 + struct usb_hcd *shared_hcd = xhci->shared_hcd; 365 366 366 367 xhci->xhc_state |= XHCI_STATE_REMOVING; 367 368 368 - usb_remove_hcd(xhci->shared_hcd); 369 + usb_remove_hcd(shared_hcd); 370 + xhci->shared_hcd = NULL; 369 371 usb_phy_shutdown(hcd->usb_phy); 370 372 371 373 usb_remove_hcd(hcd); 372 - usb_put_hcd(xhci->shared_hcd); 374 + usb_put_hcd(shared_hcd); 373 375 374 376 clk_disable_unprepare(clk); 375 377 clk_disable_unprepare(reg_clk);
+1
drivers/usb/host/xhci-tegra.c
··· 1303 1303 1304 1304 usb_remove_hcd(xhci->shared_hcd); 1305 1305 usb_put_hcd(xhci->shared_hcd); 1306 + xhci->shared_hcd = NULL; 1306 1307 usb_remove_hcd(tegra->hcd); 1307 1308 usb_put_hcd(tegra->hcd); 1308 1309
-2
drivers/usb/host/xhci.c
··· 719 719 720 720 /* Only halt host and free memory after both hcds are removed */ 721 721 if (!usb_hcd_is_primary_hcd(hcd)) { 722 - /* usb core will free this hcd shortly, unset pointer */ 723 - xhci->shared_hcd = NULL; 724 722 mutex_unlock(&xhci->mutex); 725 723 return; 726 724 }