提交 0029227f 编写于 作者: A Andiry Xu 提交者: Sarah Sharp

xHCI: synchronize irq in xhci_suspend()

Synchronize the interrupts instead of free them in xhci_suspend(). This will
prevent a double free when the host is suspended and then the card removed.

Set the flag hcd->msix_enabled when using MSI-X, and check the flag in
suspend_common(). MSI-X synchronization will be handled by xhci_suspend(),
and MSI/INTx will be synchronized in suspend_common().

This patch should be queued for the 2.6.37 stable tree.
Reported-by: NMatthew Garrett <mjg@redhat.com>
Signed-off-by: NAndiry Xu <andiry.xu@amd.com>
Signed-off-by: NSarah Sharp <sarah.a.sharp@linux.intel.com>
Cc: stable@kernel.org
上级 7111ebc9
...@@ -405,7 +405,12 @@ static int suspend_common(struct device *dev, bool do_wakeup) ...@@ -405,7 +405,12 @@ static int suspend_common(struct device *dev, bool do_wakeup)
return retval; return retval;
} }
synchronize_irq(pci_dev->irq); /* If MSI-X is enabled, the driver will have synchronized all vectors
* in pci_suspend(). If MSI or legacy PCI is enabled, that will be
* synchronized here.
*/
if (!hcd->msix_enabled)
synchronize_irq(pci_dev->irq);
/* Downstream ports from this root hub should already be quiesced, so /* Downstream ports from this root hub should already be quiesced, so
* there will be no DMA activity. Now we can shut down the upstream * there will be no DMA activity. Now we can shut down the upstream
......
...@@ -226,7 +226,8 @@ static int xhci_setup_msi(struct xhci_hcd *xhci) ...@@ -226,7 +226,8 @@ static int xhci_setup_msi(struct xhci_hcd *xhci)
static int xhci_setup_msix(struct xhci_hcd *xhci) static int xhci_setup_msix(struct xhci_hcd *xhci)
{ {
int i, ret = 0; int i, ret = 0;
struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); struct usb_hcd *hcd = xhci_to_hcd(xhci);
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
/* /*
* calculate number of msi-x vectors supported. * calculate number of msi-x vectors supported.
...@@ -265,6 +266,7 @@ static int xhci_setup_msix(struct xhci_hcd *xhci) ...@@ -265,6 +266,7 @@ static int xhci_setup_msix(struct xhci_hcd *xhci)
goto disable_msix; goto disable_msix;
} }
hcd->msix_enabled = 1;
return ret; return ret;
disable_msix: disable_msix:
...@@ -280,7 +282,8 @@ static int xhci_setup_msix(struct xhci_hcd *xhci) ...@@ -280,7 +282,8 @@ static int xhci_setup_msix(struct xhci_hcd *xhci)
/* Free any IRQs and disable MSI-X */ /* Free any IRQs and disable MSI-X */
static void xhci_cleanup_msix(struct xhci_hcd *xhci) static void xhci_cleanup_msix(struct xhci_hcd *xhci)
{ {
struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); struct usb_hcd *hcd = xhci_to_hcd(xhci);
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
xhci_free_irq(xhci); xhci_free_irq(xhci);
...@@ -292,6 +295,7 @@ static void xhci_cleanup_msix(struct xhci_hcd *xhci) ...@@ -292,6 +295,7 @@ static void xhci_cleanup_msix(struct xhci_hcd *xhci)
pci_disable_msi(pdev); pci_disable_msi(pdev);
} }
hcd->msix_enabled = 0;
return; return;
} }
...@@ -647,6 +651,7 @@ int xhci_suspend(struct xhci_hcd *xhci) ...@@ -647,6 +651,7 @@ int xhci_suspend(struct xhci_hcd *xhci)
int rc = 0; int rc = 0;
struct usb_hcd *hcd = xhci_to_hcd(xhci); struct usb_hcd *hcd = xhci_to_hcd(xhci);
u32 command; u32 command;
int i;
spin_lock_irq(&xhci->lock); spin_lock_irq(&xhci->lock);
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
...@@ -677,10 +682,15 @@ int xhci_suspend(struct xhci_hcd *xhci) ...@@ -677,10 +682,15 @@ int xhci_suspend(struct xhci_hcd *xhci)
spin_unlock_irq(&xhci->lock); spin_unlock_irq(&xhci->lock);
return -ETIMEDOUT; return -ETIMEDOUT;
} }
/* step 5: remove core well power */
xhci_cleanup_msix(xhci);
spin_unlock_irq(&xhci->lock); spin_unlock_irq(&xhci->lock);
/* step 5: remove core well power */
/* synchronize irq when using MSI-X */
if (xhci->msix_entries) {
for (i = 0; i < xhci->msix_count; i++)
synchronize_irq(xhci->msix_entries[i].vector);
}
return rc; return rc;
} }
...@@ -694,7 +704,6 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) ...@@ -694,7 +704,6 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
{ {
u32 command, temp = 0; u32 command, temp = 0;
struct usb_hcd *hcd = xhci_to_hcd(xhci); struct usb_hcd *hcd = xhci_to_hcd(xhci);
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
int old_state, retval; int old_state, retval;
old_state = hcd->state; old_state = hcd->state;
...@@ -729,9 +738,8 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) ...@@ -729,9 +738,8 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
xhci_dbg(xhci, "Stop HCD\n"); xhci_dbg(xhci, "Stop HCD\n");
xhci_halt(xhci); xhci_halt(xhci);
xhci_reset(xhci); xhci_reset(xhci);
if (hibernated)
xhci_cleanup_msix(xhci);
spin_unlock_irq(&xhci->lock); spin_unlock_irq(&xhci->lock);
xhci_cleanup_msix(xhci);
#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING #ifdef CONFIG_USB_XHCI_HCD_DEBUGGING
/* Tell the event ring poll function not to reschedule */ /* Tell the event ring poll function not to reschedule */
...@@ -765,30 +773,6 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) ...@@ -765,30 +773,6 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
return retval; return retval;
} }
spin_unlock_irq(&xhci->lock);
/* Re-setup MSI-X */
if (hcd->irq)
free_irq(hcd->irq, hcd);
hcd->irq = -1;
retval = xhci_setup_msix(xhci);
if (retval)
/* fall back to msi*/
retval = xhci_setup_msi(xhci);
if (retval) {
/* fall back to legacy interrupt*/
retval = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED,
hcd->irq_descr, hcd);
if (retval) {
xhci_err(xhci, "request interrupt %d failed\n",
pdev->irq);
return retval;
}
hcd->irq = pdev->irq;
}
spin_lock_irq(&xhci->lock);
/* step 4: set Run/Stop bit */ /* step 4: set Run/Stop bit */
command = xhci_readl(xhci, &xhci->op_regs->command); command = xhci_readl(xhci, &xhci->op_regs->command);
command |= CMD_RUN; command |= CMD_RUN;
......
...@@ -112,6 +112,7 @@ struct usb_hcd { ...@@ -112,6 +112,7 @@ struct usb_hcd {
/* Flags that get set only during HCD registration or removal. */ /* Flags that get set only during HCD registration or removal. */
unsigned rh_registered:1;/* is root hub registered? */ unsigned rh_registered:1;/* is root hub registered? */
unsigned rh_pollable:1; /* may we poll the root hub? */ unsigned rh_pollable:1; /* may we poll the root hub? */
unsigned msix_enabled:1; /* driver has MSI-X enabled? */
/* The next flag is a stopgap, to be removed when all the HCDs /* The next flag is a stopgap, to be removed when all the HCDs
* support the new root-hub polling mechanism. */ * support the new root-hub polling mechanism. */
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册