diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2005-11-25 09:59:46 +1100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2005-11-29 21:39:23 -0800 |
commit | 8de98402652c01839ae321be6cb3054cf5735d83 (patch) | |
tree | 6d4856ddb60be0dcd361441f0794596709553dd1 /drivers/usb/core | |
parent | d3420ba4930d61f4ec4abc046765de274182b4ed (diff) | |
download | op-kernel-dev-8de98402652c01839ae321be6cb3054cf5735d83.zip op-kernel-dev-8de98402652c01839ae321be6cb3054cf5735d83.tar.gz |
[PATCH] USB: Fix USB suspend/resume crasher (#2)
This patch closes the IRQ race and makes various other OHCI & EHCI code
path safer vs. suspend/resume.
I've been able to (finally !) successfully suspend and resume various
Mac models, with or without USB mouse plugged, or plugging while asleep,
or unplugging while asleep etc... all without a crash.
Alan, please verify the UHCI bit I did, I only verified that it builds.
It's very simple so I wouldn't expect any issue there. If you aren't
confident, then just drop the hunks that change uhci-hcd.c
I also made the patch a little bit more "safer" by making sure the store
to the interrupt register that disables interrupts is not posted before
I set the flag and drop the spinlock.
Without this patch, you cannot reliably sleep/wakeup any recent Mac, and
I suspect PCs have some more sneaky issues too (they don't frankly crash
with machine checks because x86 tend to silently swallow PCI errors but
that won't last afaik, at least PCI Express will blow up in those
situations, but the USB code may still misbehave).
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/core')
-rw-r--r-- | drivers/usb/core/hcd-pci.c | 3 | ||||
-rw-r--r-- | drivers/usb/core/hcd.c | 15 | ||||
-rw-r--r-- | drivers/usb/core/hcd.h | 7 |
3 files changed, 18 insertions, 7 deletions
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 5131d88e..29b5b2a 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -219,6 +219,7 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message) goto done; } } + synchronize_irq(dev->irq); /* FIXME until the generic PM interfaces change a lot more, this * can't use PCI D1 and D2 states. For example, the confusion @@ -392,7 +393,7 @@ int usb_hcd_pci_resume (struct pci_dev *dev) dev->dev.power.power_state = PMSG_ON; - hcd->saw_irq = 0; + clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); if (hcd->driver->resume) { retval = hcd->driver->resume(hcd); diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 5e5f65a..da24c31 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1315,11 +1315,12 @@ static int hcd_unlink_urb (struct urb *urb, int status) * finish unlinking the initial failed usb_set_address() * or device descriptor fetch. */ - if (!hcd->saw_irq && hcd->self.root_hub != urb->dev) { + if (!test_bit(HCD_FLAG_SAW_IRQ, &hcd->flags) + && hcd->self.root_hub != urb->dev) { dev_warn (hcd->self.controller, "Unlink after no-IRQ? " "Controller is probably using the wrong IRQ." "\n"); - hcd->saw_irq = 1; + set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); } urb->status = status; @@ -1649,13 +1650,15 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs * r) struct usb_hcd *hcd = __hcd; int start = hcd->state; - if (start == HC_STATE_HALT) + if (unlikely(start == HC_STATE_HALT || + !test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) return IRQ_NONE; if (hcd->driver->irq (hcd, r) == IRQ_NONE) return IRQ_NONE; - hcd->saw_irq = 1; - if (hcd->state == HC_STATE_HALT) + set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); + + if (unlikely(hcd->state == HC_STATE_HALT)) usb_hc_died (hcd); return IRQ_HANDLED; } @@ -1768,6 +1771,8 @@ int usb_add_hcd(struct usb_hcd *hcd, dev_info(hcd->self.controller, "%s\n", hcd->product_desc); + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + /* till now HC has been in an indeterminate state ... */ if (hcd->driver->reset && (retval = hcd->driver->reset(hcd)) < 0) { dev_err(hcd->self.controller, "can't reset\n"); diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index 24a62a2..c8a1b35 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -72,7 +72,12 @@ struct usb_hcd { /* usb_bus.hcpriv points to this */ * hardware info/state */ const struct hc_driver *driver; /* hw-specific hooks */ - unsigned saw_irq : 1; + + /* Flags that need to be manipulated atomically */ + unsigned long flags; +#define HCD_FLAG_HW_ACCESSIBLE 0x00000001 +#define HCD_FLAG_SAW_IRQ 0x00000002 + unsigned can_wakeup:1; /* hw supports wakeup? */ unsigned remote_wakeup:1;/* sw should use wakeup? */ unsigned rh_registered:1;/* is root hub registered? */ |