Commit 23d10a9e authored by Takamasa Ohtake's avatar Takamasa Ohtake Committed by Greg Kroah-Hartman

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: default avatarTakamasa Ohtake <ohtake-txa@necst.nec.co.jp>
Signed-off-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent ee269d98
...@@ -555,7 +555,7 @@ static void start_hnp(struct ohci_hcd *ohci); ...@@ -555,7 +555,7 @@ static void start_hnp(struct ohci_hcd *ohci);
#define tick_before(t1,t2) ((s16)(((s16)(t1))-((s16)(t2))) < 0) #define tick_before(t1,t2) ((s16)(((s16)(t1))-((s16)(t2))) < 0)
/* called from some task, normally khubd */ /* 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]; __hc32 __iomem *portstat = &ohci->regs->roothub.portstatus [port];
u32 temp; u32 temp;
...@@ -570,6 +570,9 @@ static inline void root_port_reset (struct ohci_hcd *ohci, unsigned port) ...@@ -570,6 +570,9 @@ static inline void root_port_reset (struct ohci_hcd *ohci, unsigned port)
/* spin until any current reset finishes */ /* spin until any current reset finishes */
for (;;) { for (;;) {
temp = ohci_readl (ohci, portstat); temp = ohci_readl (ohci, portstat);
/* handle e.g. CardBus eject */
if (temp == ~(u32)0)
return -ESHUTDOWN;
if (!(temp & RH_PS_PRS)) if (!(temp & RH_PS_PRS))
break; break;
udelay (500); udelay (500);
...@@ -586,6 +589,8 @@ static inline void root_port_reset (struct ohci_hcd *ohci, unsigned port) ...@@ -586,6 +589,8 @@ static inline void root_port_reset (struct ohci_hcd *ohci, unsigned port)
now = ohci_readl(ohci, &ohci->regs->fmnumber); now = ohci_readl(ohci, &ohci->regs->fmnumber);
} while (tick_before(now, reset_done)); } while (tick_before(now, reset_done));
/* caller synchronizes using PRSC */ /* caller synchronizes using PRSC */
return 0;
} }
static int ohci_hub_control ( static int ohci_hub_control (
...@@ -702,7 +707,7 @@ static int ohci_hub_control ( ...@@ -702,7 +707,7 @@ static int ohci_hub_control (
&ohci->regs->roothub.portstatus [wIndex]); &ohci->regs->roothub.portstatus [wIndex]);
break; break;
case USB_PORT_FEAT_RESET: case USB_PORT_FEAT_RESET:
root_port_reset (ohci, wIndex); retval = root_port_reset (ohci, wIndex);
break; break;
default: default:
goto error; goto error;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment