summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndiry Xu <andiry.xu@amd.com>2010-12-27 17:39:02 +0800
committerGreg Kroah-Hartman <gregkh@suse.de>2011-02-17 15:15:22 -0800
commit09302e3286eb76d3d85d117e28bb0c3950163278 (patch)
tree39c7a10f926dcbf968e1145f43ff8a5a80cfb7e9
parent97347262562763e32da386600b3b9e69f7dd724c (diff)
downloadkernel-common-09302e3286eb76d3d85d117e28bb0c3950163278.tar.gz
kernel-common-09302e3286eb76d3d85d117e28bb0c3950163278.tar.bz2
kernel-common-09302e3286eb76d3d85d117e28bb0c3950163278.zip
xHCI: synchronize irq in xhci_suspend()
commit 0029227f1bc30b6c809ae751f9e7af6cef900997 upstream. 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: Matthew Garrett <mjg@redhat.com> Signed-off-by: Andiry Xu <andiry.xu@amd.com> Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--drivers/usb/core/hcd-pci.c7
-rw-r--r--drivers/usb/host/xhci.c46
-rw-r--r--include/linux/usb/hcd.h1
3 files changed, 22 insertions, 32 deletions
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index 3799573bd385..4de52dcd360a 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -406,7 +406,12 @@ static int suspend_common(struct device *dev, bool do_wakeup)
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
* there will be no DMA activity. Now we can shut down the upstream
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 45e4a3108cc3..d48edfa5043a 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -226,7 +226,8 @@ static int xhci_setup_msi(struct xhci_hcd *xhci)
static int xhci_setup_msix(struct xhci_hcd *xhci)
{
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.
@@ -265,6 +266,7 @@ static int xhci_setup_msix(struct xhci_hcd *xhci)
goto disable_msix;
}
+ hcd->msix_enabled = 1;
return ret;
disable_msix:
@@ -280,7 +282,8 @@ free_entries:
/* Free any IRQs and disable MSI-X */
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);
@@ -292,6 +295,7 @@ static void xhci_cleanup_msix(struct xhci_hcd *xhci)
pci_disable_msi(pdev);
}
+ hcd->msix_enabled = 0;
return;
}
@@ -647,6 +651,7 @@ int xhci_suspend(struct xhci_hcd *xhci)
int rc = 0;
struct usb_hcd *hcd = xhci_to_hcd(xhci);
u32 command;
+ int i;
spin_lock_irq(&xhci->lock);
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
@@ -677,10 +682,15 @@ int xhci_suspend(struct xhci_hcd *xhci)
spin_unlock_irq(&xhci->lock);
return -ETIMEDOUT;
}
- /* step 5: remove core well power */
- xhci_cleanup_msix(xhci);
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;
}
@@ -694,7 +704,6 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
{
u32 command, temp = 0;
struct usb_hcd *hcd = xhci_to_hcd(xhci);
- struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
int old_state, retval;
old_state = hcd->state;
@@ -729,9 +738,8 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
xhci_dbg(xhci, "Stop HCD\n");
xhci_halt(xhci);
xhci_reset(xhci);
- if (hibernated)
- xhci_cleanup_msix(xhci);
spin_unlock_irq(&xhci->lock);
+ xhci_cleanup_msix(xhci);
#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING
/* Tell the event ring poll function not to reschedule */
@@ -765,30 +773,6 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
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 */
command = xhci_readl(xhci, &xhci->op_regs->command);
command |= CMD_RUN;
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index 0b6e751ea0b1..6c37d782c4ea 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -112,6 +112,7 @@ struct usb_hcd {
/* Flags that get set only during HCD registration or removal. */
unsigned rh_registered:1;/* is root hub registered? */
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
* support the new root-hub polling mechanism. */