This revised patch (as893b) improves the method used by the hub driver to
release its private data structure.  The current code is non-robust,
relying on a memory region not getting reused by another driver after
it has been freed.

Signed-off-by: Alan Stern <[EMAIL PROTECTED]>

---

It turned out that making khubd responsible doing the final usb_put_intf() 
wasn't such a good idea.  It caused a problem with dummy_hcd.  Doing 
things this way should be better; now all device references held by the 
hub driver will be dropped before hub_disconnect() returns.


Index: usb-2.6/drivers/usb/core/hub.c
===================================================================
--- usb-2.6.orig/drivers/usb/core/hub.c
+++ usb-2.6/drivers/usb/core/hub.c
@@ -49,6 +49,9 @@ struct usb_hub {
        int                     error;          /* last reported error */
        int                     nerrors;        /* track consecutive errors */
 
+       struct completion       *finish;        /* signal that khubd is done
+                                                       using this hub */
+
        struct list_head        event_list;     /* hubs w/data or errs ready */
        unsigned long           event_bits[1];  /* status change bitmask */
        unsigned long           change_bits[1]; /* ports with logical connect
@@ -67,12 +70,15 @@ struct usb_hub {
        unsigned                limited_power:1;
        unsigned                quiescing:1;
        unsigned                activating:1;
+       unsigned                disconnected:1;
 
        unsigned                has_indicators:1;
        u8                      indicator[USB_MAXCHILDREN];
        struct delayed_work     leds;
 };
 
+/* Sentinel value for hub->finish to indicate khubd is using the hub */
+#define        HUB_IN_USE(hub)         ((struct completion *) (hub))
 
 /* Protect struct usb_device->state and ->children members
  * Note: Both are also protected by ->dev.sem, except that ->state can
@@ -324,7 +330,7 @@ static void kick_khubd(struct usb_hub *h
        to_usb_interface(hub->intfdev)->pm_usage_cnt = 1;
 
        spin_lock_irqsave(&hub_event_lock, flags);
-       if (list_empty(&hub->event_list)) {
+       if (!hub->disconnected & list_empty(&hub->event_list)) {
                list_add_tail(&hub->event_list, &hub_event_list);
                wake_up(&khubd_wait);
        }
@@ -333,6 +339,7 @@ static void kick_khubd(struct usb_hub *h
 
 void usb_kick_khubd(struct usb_device *hdev)
 {
+       /* FIXME: What if hdev isn't bound to the hub driver? */
        kick_khubd(hdev_to_hub(hdev));
 }
 
@@ -931,6 +938,21 @@ static void hub_disconnect(struct usb_in
 {
        struct usb_hub *hub = usb_get_intfdata (intf);
        struct usb_device *hdev;
+       struct completion wait_for_khubd;
+
+       /* Take the hub off the event list */
+       spin_lock_irq(&hub_event_lock);
+       hub->disconnected = 1;
+       list_del_init(&hub->event_list);
+
+       /* Wait for khubd to finish using the hub */
+       if (hub->finish == HUB_IN_USE(hub)) {
+               hub->finish = &wait_for_khubd;
+               init_completion(&wait_for_khubd);
+       }
+       spin_unlock_irq(&hub_event_lock);
+       if (hub->finish)
+               wait_for_completion(&wait_for_khubd);
 
        /* Disconnect all children and quiesce the hub */
        hub->error = 0;
@@ -945,10 +967,6 @@ static void hub_disconnect(struct usb_in
        usb_free_urb(hub->urb);
        hub->urb = NULL;
 
-       spin_lock_irq(&hub_event_lock);
-       list_del_init(&hub->event_list);
-       spin_unlock_irq(&hub_event_lock);
-
        kfree(hub->descriptor);
        hub->descriptor = NULL;
 
@@ -2600,19 +2618,19 @@ static void hub_events(void)
         * safe since we delete the hub from the event list.
         * Not the most efficient, but avoids deadlocks.
         */
+       spin_lock_irq(&hub_event_lock);
        while (1) {
 
                /* Grab the first entry at the beginning of the list */
-               spin_lock_irq(&hub_event_lock);
-               if (list_empty(&hub_event_list)) {
-                       spin_unlock_irq(&hub_event_lock);
+               if (list_empty(&hub_event_list))
                        break;
-               }
-
                tmp = hub_event_list.next;
                list_del_init(tmp);
 
                hub = list_entry(tmp, struct usb_hub, event_list);
+               hub->finish = HUB_IN_USE(hub);
+               spin_unlock_irq(&hub_event_lock);
+
                hdev = hub->hdev;
                intf = to_usb_interface(hub->intfdev);
                hub_dev = &intf->dev;
@@ -2625,13 +2643,10 @@ static void hub_events(void)
                                (u16) hub->change_bits[0],
                                (u16) hub->event_bits[0]);
 
-               usb_get_intf(intf);
-               spin_unlock_irq(&hub_event_lock);
-
                /* Lock the device, then check to see if we were
                 * disconnected while waiting for the lock to succeed. */
                usb_lock_device(hdev);
-               if (hub != usb_get_intfdata(intf))
+               if (unlikely(hub->disconnected))
                        goto loop;
 
                /* If the hub has died, clean up after it */
@@ -2794,9 +2809,17 @@ loop_autopm:
                        usb_autopm_enable(intf);
 loop:
                usb_unlock_device(hdev);
-               usb_put_intf(intf);
 
+               /* If hub_disconnect() is waiting for us to finish using
+                * the hub, tell it we are now done.
+                */
+               spin_lock_irq(&hub_event_lock);
+               if (hub->finish == HUB_IN_USE(hub))
+                       hub->finish = NULL;
+               else
+                       complete(hub->finish);
         } /* end while (1) */
+       spin_unlock_irq(&hub_event_lock);
 }
 
 static int hub_thread(void *__unused)


-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys-and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to