Browse Source

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>
Takamasa Ohtake 18 năm trước cách đây
mục cha
commit
23d10a9e37
1 tập tin đã thay đổi với 7 bổ sung2 xóa
  1. 7 2
      drivers/usb/host/ohci-hub.c

+ 7 - 2
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;