Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <ohtake-txa@necst.nec.co.jp>
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

Takamasa Ohtake and committed by
Greg Kroah-Hartman
23d10a9e ee269d98

+7 -2
+7 -2
drivers/usb/host/ohci-hub.c
··· 555 555 #define tick_before(t1,t2) ((s16)(((s16)(t1))-((s16)(t2))) < 0) 556 556 557 557 /* called from some task, normally khubd */ 558 - static inline void root_port_reset (struct ohci_hcd *ohci, unsigned port) 558 + static inline int root_port_reset (struct ohci_hcd *ohci, unsigned port) 559 559 { 560 560 __hc32 __iomem *portstat = &ohci->regs->roothub.portstatus [port]; 561 561 u32 temp; ··· 570 570 /* spin until any current reset finishes */ 571 571 for (;;) { 572 572 temp = ohci_readl (ohci, portstat); 573 + /* handle e.g. CardBus eject */ 574 + if (temp == ~(u32)0) 575 + return -ESHUTDOWN; 573 576 if (!(temp & RH_PS_PRS)) 574 577 break; 575 578 udelay (500); ··· 589 586 now = ohci_readl(ohci, &ohci->regs->fmnumber); 590 587 } while (tick_before(now, reset_done)); 591 588 /* caller synchronizes using PRSC */ 589 + 590 + return 0; 592 591 } 593 592 594 593 static int ohci_hub_control ( ··· 707 702 &ohci->regs->roothub.portstatus [wIndex]); 708 703 break; 709 704 case USB_PORT_FEAT_RESET: 710 - root_port_reset (ohci, wIndex); 705 + retval = root_port_reset (ohci, wIndex); 711 706 break; 712 707 default: 713 708 goto error;