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: 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> Cc: stable@kernel.org
This commit is contained in:
@@ -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)
|
|||||||
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)
|
|||||||
goto disable_msix;
|
goto disable_msix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hcd->msix_enabled = 1;
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
disable_msix:
|
disable_msix:
|
||||||
@@ -280,7 +282,8 @@ free_entries:
|
|||||||
/* 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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
{
|
{
|
||||||
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)
|
|||||||
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)
|
|||||||
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 {
|
|||||||
/* 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. */
|
||||||
|
Reference in New Issue
Block a user