I managed to trigger the following uvm fault by continously switching an
iwm(4) client between two APs on different channels, i.e. a loop that runs:
ifconfig iwm0 chan X; sleep 10; ifconfig iwm chan Y; sleep 10;

 uvm_fault(0xffffffff819614a0, 0x7, 0, 2) -> e
 kernel: page fault trap, code=0
 Stopped at        softclock+0x32: movq %rdx,0x8(%rax)

This diff seems to fix the problem. It cancels mira timeouts from
interrupt context where a state change is scheduled, instead of
cancelling mira timeouts from the process context which performs
the actual state change.

Also, cancel mira timeouts when the interface is brought down in
iwm_stop() because this code path does not go through iwm_newstate().

ok?

Note that unless we are in RUN state the timeouts have not been initialized
so timeout_del() inside mira_cancel_timeouts() would panic if we called it.
This can't be done in a different way since mira state can only be
initialized once the node for our AP (ic->ic_bss) is available.

Index: if_iwm.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_iwm.c,v
retrieving revision 1.156
diff -u -p -r1.156 if_iwm.c
--- if_iwm.c    12 Jan 2017 18:06:57 -0000      1.156
+++ if_iwm.c    17 Jan 2017 18:59:20 -0000
@@ -5496,10 +5496,8 @@ iwm_newstate_task(void *psc)
        if (ostate == IEEE80211_S_SCAN && nstate != ostate)
                iwm_led_blink_stop(sc);
 
-       if (ostate == IEEE80211_S_RUN && nstate != ostate) {
+       if (ostate == IEEE80211_S_RUN && nstate != ostate)
                iwm_disable_beacon_filter(sc);
-               ieee80211_mira_cancel_timeouts(&in->in_mn);
-       }
 
        /* Reset the device if moving out of AUTH, ASSOC, or RUN. */
        /* XXX Is there a way to switch states without a full reset? */
@@ -5632,8 +5630,11 @@ iwm_newstate(struct ieee80211com *ic, en
 {
        struct ifnet *ifp = IC2IFP(ic);
        struct iwm_softc *sc = ifp->if_softc;
+       struct iwm_node *in = (void *)ic->ic_bss;
 
        timeout_del(&sc->sc_calib_to);
+       if (ic->ic_state == IEEE80211_S_RUN)
+               ieee80211_mira_cancel_timeouts(&in->in_mn);
 
        sc->ns_nstate = nstate;
        sc->ns_arg = arg;
@@ -6116,6 +6117,8 @@ iwm_stop(struct ifnet *ifp, int disable)
        ifq_clr_oactive(&ifp->if_snd);
 
        in->in_phyctxt = NULL;
+       if (ic->ic_state == IEEE80211_S_RUN)
+               ieee80211_mira_cancel_timeouts(&in->in_mn);
 
        task_del(systq, &sc->init_task);
        task_del(sc->sc_nswq, &sc->newstate_task);

Reply via email to