On Sun, Jul 12, 2015 at 09:11:44PM +0200, Stefan Sperling wrote: > On Sun, Jul 12, 2015 at 05:57:14PM +0200, Martin Pieuchot wrote: > > > > run_newstate_cb SCAN -> INIT > > > > run_newstate_cb SCAN -> SCAN > > > > How is this possible? Why it isn't INIT -> SCAN? > > I'm not entirely sure.
This implements your suggestion to properly abort async tasks when bringing the interface down. Fixes the problem for me just as well. ok? Index: if_run.c =================================================================== RCS file: /cvs/src/sys/dev/usb/if_run.c,v retrieving revision 1.109 diff -u -p -r1.109 if_run.c --- if_run.c 12 Jun 2015 15:47:31 -0000 1.109 +++ if_run.c 15 Jul 2015 03:50:08 -0000 @@ -358,6 +358,7 @@ struct ieee80211_node *run_node_alloc(s int run_media_change(struct ifnet *); void run_next_scan(void *); void run_task(void *); +void run_cancel_async(struct run_softc *); void run_do_async(struct run_softc *, void (*)(struct run_softc *, void *), void *, int); int run_newstate(struct ieee80211com *, enum ieee80211_state, int); @@ -685,13 +686,10 @@ run_detach(struct device *self, int flag s = splusb(); - if (timeout_initialized(&sc->scan_to)) - timeout_del(&sc->scan_to); if (timeout_initialized(&sc->calib_to)) timeout_del(&sc->calib_to); - /* wait for all queued asynchronous commands to complete */ - usb_rem_wait_task(sc->sc_udev, &sc->sc_task); + run_cancel_async(sc); usbd_ref_wait(sc->sc_udev); @@ -1740,6 +1738,28 @@ run_task(void *arg) } void +run_cancel_async(struct run_softc *sc) +{ + struct run_host_cmd_ring *ring = &sc->cmdq; + struct run_host_cmd *cmd; + int s, i; + + s = splusb(); + + if (timeout_initialized(&sc->scan_to)) + timeout_del(&sc->scan_to); + usb_rem_task(sc->sc_udev, &sc->sc_task); + + for (i = 0; i < RUN_HOST_CMD_RING_COUNT; i++) { + cmd = &ring->cmd[i]; + cmd->cb = NULL; + } + ring->next = ring->cur = ring->queued = 0; + + splx(s); +} + +void run_do_async(struct run_softc *sc, void (*cb)(struct run_softc *, void *), void *arg, int len) { @@ -1751,6 +1771,11 @@ run_do_async(struct run_softc *sc, void return; s = splusb(); + if (ring->queued == RUN_HOST_CMD_RING_COUNT) { + splx(s); + printf("%s: host cmd queue overrun\n", sc->sc_dev.dv_xname); + return; /* XXX */ + } cmd = &ring->cmd[ring->cur]; cmd->cb = cb; KASSERT(len <= sizeof (cmd->data)); @@ -4504,7 +4529,9 @@ run_init(struct ifnet *ifp) } /* init host command ring */ - sc->cmdq.cur = sc->cmdq.next = sc->cmdq.queued = 0; + if (sc->cmdq.queued != 0) + panic("outstanding host commands queued"); + sc->cmdq.cur = sc->cmdq.next = 0; /* init Tx rings (4 EDCAs) */ for (qid = 0; qid < 4; qid++) { @@ -4735,13 +4762,16 @@ run_stop(struct ifnet *ifp, int disable) ifp->if_timer = 0; ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); - timeout_del(&sc->scan_to); timeout_del(&sc->calib_to); s = splusb(); + + /* Cancel asynchronous state transitions etc. */ + run_cancel_async(sc); ieee80211_new_state(ic, IEEE80211_S_INIT, -1); - /* wait for all queued asynchronous commands to complete */ + /* Wait for asynchronous state transition to INIT to complete. */ usb_wait_task(sc->sc_udev, &sc->sc_task); + splx(s); /* Disable Tx/Rx DMA. */