new diff.  integrated matthew's comments and task thread
loop and:

* put the hub explore task on the task queue, don't run it
directly (except in usb_attach()).  add comments about why.
* make sure high speed hubs explore before companion low/
full speed hubs
* handle config_pending_{inrc,decr} correctly
* stop interrupts from starting an explore if the first explore
hasn't happened yet
* instead of using both "discover" and "explore" for the same
idea, just use "explore"
* usb_hub_tasks and usb_device_tasks are bad names.  use
usb_explore_tasks for the explore tasks and usb_tasks for
the "generic" task tailq.

I've tested this on several machines with several different
usb configurations at boot (and of course plugging/unplugging
devices at random after boot).  afaics, everything is working
properly.  bonus: device attachment order is more predictible.

-- 
[email protected]
SDF Public Access UNIX System - http://sdf.lonestar.org

Index: uhub.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/uhub.c,v
retrieving revision 1.52
diff -u -p uhub.c
--- uhub.c      13 Nov 2009 18:06:57 -0000      1.52
+++ uhub.c      22 Aug 2010 21:28:17 -0000
@@ -586,5 +586,5 @@ uhub_intr(usbd_xfer_handle xfer, usbd_private_handle a
        if (status == USBD_STALLED)
                usbd_clear_endpoint_stall_async(sc->sc_ipipe);
        else if (status == USBD_NORMAL_COMPLETION)
-               usb_needs_explore(sc->sc_hub);
+               usb_needs_explore(sc->sc_hub, 0);
 }
Index: usb.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/usb.c,v
retrieving revision 1.62
diff -u -p usb.c
--- usb.c       9 Nov 2009 17:53:39 -0000       1.62
+++ usb.c       22 Aug 2010 21:28:17 -0000
@@ -81,8 +81,7 @@ extern int    ehcidebug;
 #endif
 /*
  * 0  - do usual exploration
- * 1  - do not use timeout exploration
- * >1 - do no exploration
+ * !0 - do no exploration
  */
 int    usb_noexplore = 0;
 #else
@@ -95,18 +94,20 @@ struct usb_softc {
        usbd_bus_handle  sc_bus;        /* USB controller */
        struct usbd_port sc_port;       /* dummy port for root hub */
 
-       struct proc     *sc_event_thread;
+       struct usb_task  sc_explore_task;
 
        char             sc_dying;
 };
 
-TAILQ_HEAD(, usb_task) usb_all_tasks;
+TAILQ_HEAD(, usb_task) usb_explore_tasks;
+TAILQ_HEAD(, usb_task) usb_tasks;
 
-volatile int threads_pending = 0;
+int usb_run_tasks;
+int explore_pending;
 
-void   usb_discover(void *);
-void   usb_create_event_thread(void *);
-void   usb_event_thread(void *);
+void   usb_explore(void *);
+void   usb_first_explore(void *);
+void   usb_create_task_thread(void *);
 void   usb_task_thread(void *);
 struct proc *usb_task_thread_proc = NULL;
 
@@ -161,7 +162,7 @@ usb_attach(struct device *parent, struct device *self,
        int speed;
        struct usb_event ue;
 
-       DPRINTF(("usbd_attach\n"));
+       DPRINTF(("usb_attach\n"));
 
        usbd_init();
        sc->sc_bus = aux;
@@ -189,6 +190,11 @@ usb_attach(struct device *parent, struct device *self,
        if (cold)
                sc->sc_bus->use_polling++;
 
+       /* Don't let hub interrupts cause explore until ready. */
+       sc->sc_bus->config_pending = 1;
+
+       usb_init_task(&sc->sc_explore_task, usb_explore, sc);
+
        ue.u.ue_ctrlr.ue_bus = sc->sc_dev.dv_unit;
        usb_add_event(USB_EVENT_CTRLR_ATTACH, &ue);
 
@@ -229,31 +235,40 @@ usb_attach(struct device *parent, struct device *self,
        if (cold)
                sc->sc_bus->use_polling--;
 
-       config_pending_incr();
-       kthread_create_deferred(usb_create_event_thread, sc);
+       if (!sc->sc_dying) {
+               config_pending_incr();
+               kthread_create_deferred(usb_first_explore, sc);
+       }
 }
 
+/*
+ * Called by usbd_init when first usb is attached.
+ */
 void
-usb_create_event_thread(void *arg)
+usb_begin_tasks(void)
 {
-       struct usb_softc *sc = arg;
-       static int created = 0;
+       TAILQ_INIT(&usb_explore_tasks);
+       TAILQ_INIT(&usb_tasks);
+       usb_run_tasks = 1;
+       kthread_create_deferred(usb_create_task_thread, NULL);
+}
 
-       if (sc->sc_bus->usbrev == USBREV_2_0)
-               threads_pending++;
+/*
+ * Called by usbd_finish when last usb is detached.
+ */
+void
+usb_end_tasks(void)
+{
+       usb_run_tasks = 0;
+       wakeup(&usb_run_tasks);
+}
 
-       if (kthread_create(usb_event_thread, sc, &sc->sc_event_thread,
-           "%s", sc->sc_dev.dv_xname))
-               panic("unable to create event thread for %s",
-                   sc->sc_dev.dv_xname);
-
-       if (!created) {
-               created = 1;
-               TAILQ_INIT(&usb_all_tasks);
-               if (kthread_create(usb_task_thread, NULL,
-                   &usb_task_thread_proc, "usbtask"))
-                       panic("unable to create usb task thread");
-       }
+void
+usb_create_task_thread(void *arg)
+{
+       if (kthread_create(usb_task_thread, NULL,
+           &usb_task_thread_proc, "usbtask"))
+               panic("unable to create usb task thread");
 }
 
 /*
@@ -266,15 +281,17 @@ usb_add_task(usbd_device_handle dev, struct usb_task *
 {
        int s;
 
+       DPRINTFN(2,("%s: task=%p onqueue=%d\n", __func__, task, task->onqueue));
+
        s = splusb();
        if (!task->onqueue) {
-               DPRINTFN(2,("usb_add_task: task=%p\n", task));
-               TAILQ_INSERT_TAIL(&usb_all_tasks, task, next);
+               if (task->fun == usb_explore)
+                       TAILQ_INSERT_TAIL(&usb_explore_tasks, task, next);
+               else
+                       TAILQ_INSERT_TAIL(&usb_tasks, task, next);
                task->onqueue = 1;
-       } else {
-               DPRINTFN(3,("usb_add_task: task=%p on q\n", task));
        }
-       wakeup(&usb_all_tasks);
+       wakeup(&usb_run_tasks);
        splx(s);
 }
 
@@ -283,63 +300,45 @@ usb_rem_task(usbd_device_handle dev, struct usb_task *
 {
        int s;
 
+       DPRINTFN(2,("%s: task=%p onqueue=%d\n", __func__, task, task->onqueue));
+
        s = splusb();
        if (task->onqueue) {
-               TAILQ_REMOVE(&usb_all_tasks, task, next);
+               if (task->fun == usb_explore)
+                       TAILQ_REMOVE(&usb_explore_tasks, task, next);
+               else
+                       TAILQ_REMOVE(&usb_tasks, task, next);
                task->onqueue = 0;
        }
        splx(s);
 }
 
 void
-usb_event_thread(void *arg)
+usb_first_explore(void *arg)
 {
        struct usb_softc *sc = arg;
        int pwrdly;
 
-       DPRINTF(("usb_event_thread: start\n"));
+       if (sc->sc_bus->usbrev == USBREV_2_0)
+               explore_pending++;
 
        /* Wait for power to come good. */
        pwrdly = sc->sc_bus->root_hub->hub->hubdesc.bPwrOn2PwrGood * 
            UHD_PWRON_FACTOR + USB_EXTRA_POWER_UP_TIME;
        usb_delay_ms(sc->sc_bus, pwrdly);
 
-       /* USB1 threads wait for USB2 threads to finish their first probe. */
-       while (sc->sc_bus->usbrev != USBREV_2_0 && threads_pending)
-               (void)tsleep((void *)&threads_pending, PWAIT, "config", 0);
+       /* USB1 waits for USB2 to finish their first probe. */
+       while (sc->sc_bus->usbrev != USBREV_2_0 && explore_pending)
+               (void)tsleep((void *)&explore_pending, PWAIT, "config", 0);
 
-       /* Make sure first discover does something. */
-       sc->sc_bus->needs_explore = 1;
-       usb_discover(sc);
-       config_pending_decr();
+       /* Add first explore task to the queue. */
+       usb_needs_explore(sc->sc_bus->root_hub, 1);
 
        /* Wake up any companions waiting for handover before their probes. */
        if (sc->sc_bus->usbrev == USBREV_2_0) {
-               threads_pending--;
-               wakeup((void *)&threads_pending);
+               explore_pending--;
+               wakeup((void *)&explore_pending);
        }
-
-       while (!sc->sc_dying) {
-#ifdef USB_DEBUG
-               if (usb_noexplore < 2)
-#endif
-               usb_discover(sc);
-#ifdef USB_DEBUG
-               (void)tsleep(&sc->sc_bus->needs_explore, PWAIT, "usbevt",
-                   usb_noexplore ? 0 : hz * 60);
-#else
-               (void)tsleep(&sc->sc_bus->needs_explore, PWAIT, "usbevt",
-                   hz * 60);
-#endif
-               DPRINTFN(2,("usb_event_thread: woke up\n"));
-       }
-       sc->sc_event_thread = NULL;
-
-       /* In case parent is waiting for us to exit. */
-       wakeup(sc);
-
-       DPRINTF(("usb_event_thread: exit\n"));
-       kthread_exit(0);
 }
 
 void
@@ -351,21 +350,23 @@ usb_task_thread(void *arg)
        DPRINTF(("usb_task_thread: start\n"));
 
        s = splusb();
-       for (;;) {
-               task = TAILQ_FIRST(&usb_all_tasks);
-               if (task == NULL) {
-                       tsleep(&usb_all_tasks, PWAIT, "usbtsk", 0);
-                       task = TAILQ_FIRST(&usb_all_tasks);
+       while (usb_run_tasks) {
+               if ((task = TAILQ_FIRST(&usb_explore_tasks)) != NULL)
+                       TAILQ_REMOVE(&usb_explore_tasks, task, next);
+               else if ((task = TAILQ_FIRST(&usb_tasks)) != NULL)
+                       TAILQ_REMOVE(&usb_tasks, task, next);
+               else {
+                       tsleep(&usb_run_tasks, PWAIT, "usbtsk", 0);
+                       continue;
                }
-               DPRINTFN(2,("usb_task_thread: woke up task=%p\n", task));
-               if (task != NULL) {
-                       TAILQ_REMOVE(&usb_all_tasks, task, next);
-                       task->onqueue = 0;
-                       splx(s);
-                       task->fun(task->arg);
-                       s = splusb();
-               }
+               task->onqueue = 0;
+               splx(s);
+               task->fun(task->arg);
+               s = splusb();
        }
+       splx(s);
+
+       kthread_exit(0);
 }
 
 int
@@ -662,34 +663,46 @@ usbkqfilter(dev_t dev, struct knote *kn)
        return (0);
 }
 
-/* Explore device tree from the root. */
+/*
+ * Explore device tree from the root.  We need mutual exclusion to this
+ * hub while traversing the device tree, but this is guaranteed since this
+ * function is only called from the task thread, with one exception:
+ * usb_attach() calls this function, but there shouldn't be anything else
+ * trying to explore this hub at that time.
+ */
 void
-usb_discover(void *v)
+usb_explore(void *v)
 {
        struct usb_softc *sc = v;
 
-       DPRINTFN(2,("usb_discover\n"));
+       DPRINTFN(2,("%s: %s\n", __func__, sc->sc_dev.dv_xname));
 #ifdef USB_DEBUG
-       if (usb_noexplore > 1)
+       if (usb_noexplore)
                return;
 #endif
-       /*
-        * We need mutual exclusion while traversing the device tree,
-        * but this is guaranteed since this function is only called
-        * from the event thread for the controller.
-        */
-       while (sc->sc_bus->needs_explore && !sc->sc_dying) {
-               sc->sc_bus->needs_explore = 0;
+
+       if (!sc->sc_dying)
                sc->sc_bus->root_hub->hub->explore(sc->sc_bus->root_hub);
+
+       if (sc->sc_bus->config_pending) {
+               DPRINTF(("%s: %s: first explore done\n", __func__,
+                   sc->sc_dev.dv_xname));
+               config_pending_decr();
+               sc->sc_bus->config_pending = 0;
        }
 }
 
 void
-usb_needs_explore(usbd_device_handle dev)
+usb_needs_explore(usbd_device_handle dev, int first_explore)
 {
-       DPRINTFN(2,("usb_needs_explore\n"));
-       dev->bus->needs_explore = 1;
-       wakeup(&dev->bus->needs_explore);
+       DPRINTFN(3,("%s: %s\n", dev->bus->usbctl->sc_dev.dv_xname, __func__));
+
+       if (dev->bus->config_pending && !first_explore) {
+               DPRINTF(("%s: %s: not exploring before first explore\n",
+                   __func__, dev->bus->usbctl->sc_dev.dv_xname));
+               return;
+       }
+       usb_add_task(dev, &dev->bus->usbctl->sc_explore_task);
 }
 
 void
@@ -697,8 +710,7 @@ usb_needs_reattach(usbd_device_handle dev)
 {
        DPRINTFN(2,("usb_needs_reattach\n"));
        dev->powersrc->reattach = 1;
-       dev->bus->needs_explore = 1;
-       wakeup(&dev->bus->needs_explore);
+       usb_needs_explore(dev, 0);
 }
 
 /* Called at splusb() */
@@ -822,14 +834,7 @@ usb_detach(struct device *self, int flags)
        if (sc->sc_port.device != NULL)
                usb_disconnect_port(&sc->sc_port, self);
 
-       /* Kill off event thread. */
-       if (sc->sc_event_thread != NULL) {
-               wakeup(&sc->sc_bus->needs_explore);
-               if (tsleep(sc, PWAIT, "usbdet", hz * 60))
-                       printf("%s: event thread didn't die\n",
-                              sc->sc_dev.dv_xname);
-               DPRINTF(("usb_detach: event thread dead\n"));
-       }
+       usb_rem_task(sc->sc_bus->root_hub, &sc->sc_explore_task);
 
        usbd_finish();
 
Index: usbdi.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/usbdi.c,v
retrieving revision 1.38
diff -u -p usbdi.c
--- usbdi.c     5 Mar 2010 17:28:54 -0000       1.38
+++ usbdi.c     22 Aug 2010 21:28:17 -0000
@@ -71,15 +71,18 @@ struct rwlock usbpalock;
 void
 usbd_init(void)
 {
-       if (usbd_nbuses == 0)
+       if (usbd_nbuses == 0) {
                rw_init(&usbpalock, "usbpalock");
+               usb_begin_tasks();
+       }
        usbd_nbuses++;
 }
 
 void
 usbd_finish(void)
 {
-       --usbd_nbuses;
+       if (--usbd_nbuses == 0)
+               usb_end_tasks();
 }
 
 static __inline int
Index: usbdivar.h
===================================================================
RCS file: /cvs/src/sys/dev/usb/usbdivar.h,v
retrieving revision 1.37
diff -u -p usbdivar.h
--- usbdivar.h  12 Nov 2009 20:16:37 -0000      1.37
+++ usbdivar.h  22 Aug 2010 21:28:17 -0000
@@ -103,7 +103,7 @@ struct usbd_bus {
        /* Filled by usb driver */
        struct usbd_device     *root_hub;
        usbd_device_handle      devices[USB_MAX_DEVICES];
-       char                    needs_explore;/* a hub a signalled a change */
+       int                     config_pending;
        char                    use_polling;
        struct usb_softc       *usbctl;
        struct usb_device_stats stats;
@@ -216,6 +216,8 @@ struct usbd_xfer {
 
 void usbd_init(void);
 void usbd_finish(void);
+void usb_begin_tasks(void);
+void usb_end_tasks(void);
 
 #ifdef USB_DEBUG
 void usbd_dump_iface(struct usbd_interface *iface);
@@ -247,7 +249,7 @@ void                usb_transfer_complete(usbd_xfer_handle 
xfer);
 void           usb_disconnect_port(struct usbd_port *up, struct device *);
 
 /* Routines from usb.c */
-void           usb_needs_explore(usbd_device_handle);
+void           usb_needs_explore(usbd_device_handle, int);
 void           usb_needs_reattach(usbd_device_handle);
 void           usb_schedsoftintr(struct usbd_bus *);

Reply via email to