aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/core
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2005-11-24 17:59:46 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2005-11-30 00:39:23 -0500
commit8de98402652c01839ae321be6cb3054cf5735d83 (patch)
tree6d4856ddb60be0dcd361441f0794596709553dd1 /drivers/usb/core
parentd3420ba4930d61f4ec4abc046765de274182b4ed (diff)
[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.c3
-rw-r--r--drivers/usb/core/hcd.c15
-rw-r--r--drivers/usb/core/hcd.h7
3 files changed, 18 insertions, 7 deletions
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index 5131d88e8c5b..29b5b2a6e183 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)
219 goto done; 219 goto done;
220 } 220 }
221 } 221 }
222 synchronize_irq(dev->irq);
222 223
223 /* FIXME until the generic PM interfaces change a lot more, this 224 /* FIXME until the generic PM interfaces change a lot more, this
224 * can't use PCI D1 and D2 states. For example, the confusion 225 * 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)
392 393
393 dev->dev.power.power_state = PMSG_ON; 394 dev->dev.power.power_state = PMSG_ON;
394 395
395 hcd->saw_irq = 0; 396 clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
396 397
397 if (hcd->driver->resume) { 398 if (hcd->driver->resume) {
398 retval = hcd->driver->resume(hcd); 399 retval = hcd->driver->resume(hcd);
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 5e5f65a475ab..da24c31ee00d 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)
1315 * finish unlinking the initial failed usb_set_address() 1315 * finish unlinking the initial failed usb_set_address()
1316 * or device descriptor fetch. 1316 * or device descriptor fetch.
1317 */ 1317 */
1318 if (!hcd->saw_irq && hcd->self.root_hub != urb->dev) { 1318 if (!test_bit(HCD_FLAG_SAW_IRQ, &hcd->flags)
1319 && hcd->self.root_hub != urb->dev) {
1319 dev_warn (hcd->self.controller, "Unlink after no-IRQ? " 1320 dev_warn (hcd->self.controller, "Unlink after no-IRQ? "
1320 "Controller is probably using the wrong IRQ." 1321 "Controller is probably using the wrong IRQ."
1321 "\n"); 1322 "\n");
1322 hcd->saw_irq = 1; 1323 set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
1323 } 1324 }
1324 1325
1325 urb->status = status; 1326 urb->status = status;
@@ -1649,13 +1650,15 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs * r)
1649 struct usb_hcd *hcd = __hcd; 1650 struct usb_hcd *hcd = __hcd;
1650 int start = hcd->state; 1651 int start = hcd->state;
1651 1652
1652 if (start == HC_STATE_HALT) 1653 if (unlikely(start == HC_STATE_HALT ||
1654 !test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)))
1653 return IRQ_NONE; 1655 return IRQ_NONE;
1654 if (hcd->driver->irq (hcd, r) == IRQ_NONE) 1656 if (hcd->driver->irq (hcd, r) == IRQ_NONE)
1655 return IRQ_NONE; 1657 return IRQ_NONE;
1656 1658
1657 hcd->saw_irq = 1; 1659 set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
1658 if (hcd->state == HC_STATE_HALT) 1660
1661 if (unlikely(hcd->state == HC_STATE_HALT))
1659 usb_hc_died (hcd); 1662 usb_hc_died (hcd);
1660 return IRQ_HANDLED; 1663 return IRQ_HANDLED;
1661} 1664}
@@ -1768,6 +1771,8 @@ int usb_add_hcd(struct usb_hcd *hcd,
1768 1771
1769 dev_info(hcd->self.controller, "%s\n", hcd->product_desc); 1772 dev_info(hcd->self.controller, "%s\n", hcd->product_desc);
1770 1773
1774 set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
1775
1771 /* till now HC has been in an indeterminate state ... */ 1776 /* till now HC has been in an indeterminate state ... */
1772 if (hcd->driver->reset && (retval = hcd->driver->reset(hcd)) < 0) { 1777 if (hcd->driver->reset && (retval = hcd->driver->reset(hcd)) < 0) {
1773 dev_err(hcd->self.controller, "can't reset\n"); 1778 dev_err(hcd->self.controller, "can't reset\n");
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
index 24a62a2ff86d..c8a1b350e2cf 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 */
72 * hardware info/state 72 * hardware info/state
73 */ 73 */
74 const struct hc_driver *driver; /* hw-specific hooks */ 74 const struct hc_driver *driver; /* hw-specific hooks */
75 unsigned saw_irq : 1; 75
76 /* Flags that need to be manipulated atomically */
77 unsigned long flags;
78#define HCD_FLAG_HW_ACCESSIBLE 0x00000001
79#define HCD_FLAG_SAW_IRQ 0x00000002
80
76 unsigned can_wakeup:1; /* hw supports wakeup? */ 81 unsigned can_wakeup:1; /* hw supports wakeup? */
77 unsigned remote_wakeup:1;/* sw should use wakeup? */ 82 unsigned remote_wakeup:1;/* sw should use wakeup? */
78 unsigned rh_registered:1;/* is root hub registered? */ 83 unsigned rh_registered:1;/* is root hub registered? */