From: Takamasa Ohtake Date: Thu, 7 Dec 2006 01:04:15 +0000 (-0800) Subject: USB: ohci handles hardware faults during root port resets X-Git-Tag: v2.6.20-rc2~83^2~12 X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=23d10a9e376d6a9cd4afd4e27e5e403864f6729b;p=linux-2.6 USB: ohci handles hardware faults during root port resets I have found a problem where the root_port_reset() goes into an infinite loop and stalls the kernel. This happens when a hardware fault inside the machine occurs during a small timing window. In case of USB device connection, if a USB device responds to hcd_submit_urb(), and later the controller fails before root_port_reset(), root_port_reset() will loop infinitely because ohci_readl() will always return "-1". Such a failure can include ejecting a CardBus OHCI controller. The probability of this problem is low, but it will increase if PnP type usage is frequent. The attached patch can solve this problem and I believe that it is better to fix this problem. Signed-off-by: Takamasa Ohtake Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index f9748b67e5..216c9c9d4d 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -555,7 +555,7 @@ static void start_hnp(struct ohci_hcd *ohci); #define tick_before(t1,t2) ((s16)(((s16)(t1))-((s16)(t2))) < 0) /* called from some task, normally khubd */ -static inline void root_port_reset (struct ohci_hcd *ohci, unsigned port) +static inline int root_port_reset (struct ohci_hcd *ohci, unsigned port) { __hc32 __iomem *portstat = &ohci->regs->roothub.portstatus [port]; u32 temp; @@ -570,6 +570,9 @@ static inline void root_port_reset (struct ohci_hcd *ohci, unsigned port) /* spin until any current reset finishes */ for (;;) { temp = ohci_readl (ohci, portstat); + /* handle e.g. CardBus eject */ + if (temp == ~(u32)0) + return -ESHUTDOWN; if (!(temp & RH_PS_PRS)) break; udelay (500); @@ -586,6 +589,8 @@ static inline void root_port_reset (struct ohci_hcd *ohci, unsigned port) now = ohci_readl(ohci, &ohci->regs->fmnumber); } while (tick_before(now, reset_done)); /* caller synchronizes using PRSC */ + + return 0; } static int ohci_hub_control ( @@ -702,7 +707,7 @@ static int ohci_hub_control ( &ohci->regs->roothub.portstatus [wIndex]); break; case USB_PORT_FEAT_RESET: - root_port_reset (ohci, wIndex); + retval = root_port_reset (ohci, wIndex); break; default: goto error;