Dear, All.

I have found a problem where the root_port_reset() goes into an infinite
loop and stall the kernel.

This happens when an 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 machine fails before root_port_reset(),
root_port_reset() will loop infinitely because ohci_readl() will always
return "-1".

The probability of this problem is low, but it will increase if PnP
processing becomes frequent.  The attached patch can solve this problem
and I believe that it is better to fix this problem.


  [in the case of the USB device connection] 

   hub_events()
       |
   hub_port_connect_change()
       |
    <omission>
       |
   usb_submit_urb()
       |
   hcd_submit_urb() <---- If the H/W machine trouble has already occured,
       |                  hcd_submit_urb() returns error code(ESHUTDOWN).
       |                  And the functions after this are not called.
    <omission>
       |
       | <------- If the H/W machine trouble occurs between hcd_submit_urb()
       |          and root_port_reset(),root_port_reset() will go into
       |          endless loop because ohci_readl() will always return "-1".
       |
   root_port_reset()

 The patch that fixed this problem is as follows.


Signed-off-by:Takamasa Ohtake <[EMAIL PROTECTED]>

Index: linux-2.6.15.4/drivers/usb/host/ohci-hub.c
===================================================================
--- linux-2.6.15.4/drivers/usb/host/ohci-hub.c.orig     2006-02-28 
14:05:22.716664344 +0900
+++ linux-2.6.15.4/drivers/usb/host/ohci-hub.c  2006-02-28 14:05:22.699666928 
+0900
@@ -475,7 +475,7 @@ static void start_hnp(struct ohci_hcd *o
 #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;
@@ -490,6 +490,8 @@ static inline void root_port_reset (stru
                /* spin until any current reset finishes */
                for (;;) {
                        temp = ohci_readl (ohci, portstat);
+                       if (temp == ~(u32)0)
+                               return -ESHUTDOWN;
                        if (!(temp & RH_PS_PRS))
                                break;
                        udelay (500);
@@ -506,6 +508,8 @@ static inline void root_port_reset (stru
                now = ohci_readl(ohci, &ohci->regs->fmnumber);
        } while (tick_before(now, reset_done));
        /* caller synchronizes using PRSC */
+
+       return 0;
 }
 
 static int ohci_hub_control (
@@ -625,7 +629,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;
^---

 --------------------------------------------------
  Takamasa Ohtake  | Servers Software Division
                   | NEC System Technologies, Ltd.
                   | [EMAIL PROTECTED]
 --------------------------------------------------


-------------------------------------------------------
This SF.Net email is sponsored by xPML, a groundbreaking scripting language
that extends applications into web and mobile media. Attend the live webcast
and join the prime developer group breaking into this new coding territory!
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=110944&bid=241720&dat=121642
_______________________________________________
linux-usb-devel@lists.sourceforge.net
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to