Commit f0680904 authored by Mathias Nyman's avatar Mathias Nyman Committed by Greg Kroah-Hartman

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 fe190ed0 ("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 4ac53087 ("usb: xhci: plat: Create both
HCDs before adding them")

Fixes: fe190ed0 ("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: default avatarJack Pham <jackp@codeaurora.org>
Tested-by: default avatarJack Pham <jackp@codeaurora.org>
Tested-by: default avatarPeter Chen <peter.chen@nxp.com>
Signed-off-by: default avatarMathias Nyman <mathias.nyman@linux.intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent f6501f49
...@@ -325,14 +325,16 @@ static int xhci_histb_remove(struct platform_device *dev) ...@@ -325,14 +325,16 @@ static int xhci_histb_remove(struct platform_device *dev)
struct xhci_hcd_histb *histb = platform_get_drvdata(dev); struct xhci_hcd_histb *histb = platform_get_drvdata(dev);
struct usb_hcd *hcd = histb->hcd; struct usb_hcd *hcd = histb->hcd;
struct xhci_hcd *xhci = hcd_to_xhci(hcd); struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct usb_hcd *shared_hcd = xhci->shared_hcd;
xhci->xhc_state |= XHCI_STATE_REMOVING; xhci->xhc_state |= XHCI_STATE_REMOVING;
usb_remove_hcd(xhci->shared_hcd); usb_remove_hcd(shared_hcd);
xhci->shared_hcd = NULL;
device_wakeup_disable(&dev->dev); device_wakeup_disable(&dev->dev);
usb_remove_hcd(hcd); usb_remove_hcd(hcd);
usb_put_hcd(xhci->shared_hcd); usb_put_hcd(shared_hcd);
xhci_histb_host_disable(histb); xhci_histb_host_disable(histb);
usb_put_hcd(hcd); usb_put_hcd(hcd);
......
...@@ -590,12 +590,14 @@ static int xhci_mtk_remove(struct platform_device *dev) ...@@ -590,12 +590,14 @@ static int xhci_mtk_remove(struct platform_device *dev)
struct xhci_hcd_mtk *mtk = platform_get_drvdata(dev); struct xhci_hcd_mtk *mtk = platform_get_drvdata(dev);
struct usb_hcd *hcd = mtk->hcd; struct usb_hcd *hcd = mtk->hcd;
struct xhci_hcd *xhci = hcd_to_xhci(hcd); struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct usb_hcd *shared_hcd = xhci->shared_hcd;
usb_remove_hcd(xhci->shared_hcd); usb_remove_hcd(shared_hcd);
xhci->shared_hcd = NULL;
device_init_wakeup(&dev->dev, false); device_init_wakeup(&dev->dev, false);
usb_remove_hcd(hcd); usb_remove_hcd(hcd);
usb_put_hcd(xhci->shared_hcd); usb_put_hcd(shared_hcd);
usb_put_hcd(hcd); usb_put_hcd(hcd);
xhci_mtk_sch_exit(mtk); xhci_mtk_sch_exit(mtk);
xhci_mtk_clks_disable(mtk); xhci_mtk_clks_disable(mtk);
......
...@@ -380,6 +380,7 @@ static void xhci_pci_remove(struct pci_dev *dev) ...@@ -380,6 +380,7 @@ static void xhci_pci_remove(struct pci_dev *dev)
if (xhci->shared_hcd) { if (xhci->shared_hcd) {
usb_remove_hcd(xhci->shared_hcd); usb_remove_hcd(xhci->shared_hcd);
usb_put_hcd(xhci->shared_hcd); usb_put_hcd(xhci->shared_hcd);
xhci->shared_hcd = NULL;
} }
/* Workaround for spurious wakeups at shutdown with HSW */ /* Workaround for spurious wakeups at shutdown with HSW */
......
...@@ -362,14 +362,16 @@ static int xhci_plat_remove(struct platform_device *dev) ...@@ -362,14 +362,16 @@ static int xhci_plat_remove(struct platform_device *dev)
struct xhci_hcd *xhci = hcd_to_xhci(hcd); struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct clk *clk = xhci->clk; struct clk *clk = xhci->clk;
struct clk *reg_clk = xhci->reg_clk; struct clk *reg_clk = xhci->reg_clk;
struct usb_hcd *shared_hcd = xhci->shared_hcd;
xhci->xhc_state |= XHCI_STATE_REMOVING; xhci->xhc_state |= XHCI_STATE_REMOVING;
usb_remove_hcd(xhci->shared_hcd); usb_remove_hcd(shared_hcd);
xhci->shared_hcd = NULL;
usb_phy_shutdown(hcd->usb_phy); usb_phy_shutdown(hcd->usb_phy);
usb_remove_hcd(hcd); usb_remove_hcd(hcd);
usb_put_hcd(xhci->shared_hcd); usb_put_hcd(shared_hcd);
clk_disable_unprepare(clk); clk_disable_unprepare(clk);
clk_disable_unprepare(reg_clk); clk_disable_unprepare(reg_clk);
......
...@@ -1303,6 +1303,7 @@ static int tegra_xusb_remove(struct platform_device *pdev) ...@@ -1303,6 +1303,7 @@ static int tegra_xusb_remove(struct platform_device *pdev)
usb_remove_hcd(xhci->shared_hcd); usb_remove_hcd(xhci->shared_hcd);
usb_put_hcd(xhci->shared_hcd); usb_put_hcd(xhci->shared_hcd);
xhci->shared_hcd = NULL;
usb_remove_hcd(tegra->hcd); usb_remove_hcd(tegra->hcd);
usb_put_hcd(tegra->hcd); usb_put_hcd(tegra->hcd);
......
...@@ -719,8 +719,6 @@ static void xhci_stop(struct usb_hcd *hcd) ...@@ -719,8 +719,6 @@ static void xhci_stop(struct usb_hcd *hcd)
/* Only halt host and free memory after both hcds are removed */ /* Only halt host and free memory after both hcds are removed */
if (!usb_hcd_is_primary_hcd(hcd)) { if (!usb_hcd_is_primary_hcd(hcd)) {
/* usb core will free this hcd shortly, unset pointer */
xhci->shared_hcd = NULL;
mutex_unlock(&xhci->mutex); mutex_unlock(&xhci->mutex);
return; return;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment