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. */