aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/core
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2006-08-11 16:52:39 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2006-09-27 14:58:54 -0400
commitd5cbad4b8b37acfde3e63d31b92561b87288ad0f (patch)
tree98869f0db85e1f257c5ddd13a2afda934626153a /drivers/usb/core
parent455b25fb209c8241e2163b491228b28667d82c1c (diff)
usbcore: khubd and busy-port handling
We don't want khubd to start interfering in the device-resume process merely because the PORT_STATUS_C_SUSPEND feature happens to be set. Ports need to be marked as busy while a resume is taking place. In addition, so long as ports are marked as busy, khubd won't be able to clear their various status-change features. On an interrupt-driven root hub this could lead to an interrupt storm. Root hub IRQs should not be re-enabled until the busy_bits value is equal to 0. This patch (as765) fixes these two potential problems. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/core')
-rw-r--r--drivers/usb/core/hub.c11
-rw-r--r--drivers/usb/core/hub.h3
2 files changed, 12 insertions, 2 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 3924dd080be..bdf5be09965 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1658,6 +1658,8 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
1658 1658
1659 // dev_dbg(hub->intfdev, "resume port %d\n", port1); 1659 // dev_dbg(hub->intfdev, "resume port %d\n", port1);
1660 1660
1661 set_bit(port1, hub->busy_bits);
1662
1661 /* see 7.1.7.7; affects power usage, but not budgeting */ 1663 /* see 7.1.7.7; affects power usage, but not budgeting */
1662 status = clear_port_feature(hub->hdev, 1664 status = clear_port_feature(hub->hdev,
1663 port1, USB_PORT_FEAT_SUSPEND); 1665 port1, USB_PORT_FEAT_SUSPEND);
@@ -1707,6 +1709,10 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
1707 if (status < 0) 1709 if (status < 0)
1708 hub_port_logical_disconnect(hub, port1); 1710 hub_port_logical_disconnect(hub, port1);
1709 1711
1712 clear_bit(port1, hub->busy_bits);
1713 if (!hub->hdev->parent && !hub->busy_bits[0])
1714 usb_enable_root_hub_irq(hub->hdev->bus);
1715
1710 return status; 1716 return status;
1711} 1717}
1712 1718
@@ -2690,7 +2696,7 @@ static void hub_events(void)
2690 2696
2691 /* If this is a root hub, tell the HCD it's okay to 2697 /* If this is a root hub, tell the HCD it's okay to
2692 * re-enable port-change interrupts now. */ 2698 * re-enable port-change interrupts now. */
2693 if (!hdev->parent) 2699 if (!hdev->parent && !hub->busy_bits[0])
2694 usb_enable_root_hub_irq(hdev->bus); 2700 usb_enable_root_hub_irq(hdev->bus);
2695 2701
2696loop: 2702loop:
@@ -2865,6 +2871,9 @@ int usb_reset_device(struct usb_device *udev)
2865 break; 2871 break;
2866 } 2872 }
2867 clear_bit(port1, parent_hub->busy_bits); 2873 clear_bit(port1, parent_hub->busy_bits);
2874 if (!parent_hdev->parent && !parent_hub->busy_bits[0])
2875 usb_enable_root_hub_irq(parent_hdev->bus);
2876
2868 if (ret < 0) 2877 if (ret < 0)
2869 goto re_enumerate; 2878 goto re_enumerate;
2870 2879
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index 29d5f45a845..0f8e82a4d48 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -212,7 +212,8 @@ struct usb_hub {
212 unsigned long event_bits[1]; /* status change bitmask */ 212 unsigned long event_bits[1]; /* status change bitmask */
213 unsigned long change_bits[1]; /* ports with logical connect 213 unsigned long change_bits[1]; /* ports with logical connect
214 status change */ 214 status change */
215 unsigned long busy_bits[1]; /* ports being reset */ 215 unsigned long busy_bits[1]; /* ports being reset or
216 resumed */
216#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */ 217#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
217#error event_bits[] is too short! 218#error event_bits[] is too short!
218#endif 219#endif