On Sun, Sep 27, 2015 at 08:00:19PM +0200, Stefan Sperling wrote:
> This is yet another attempt at improving the iwm(4) newstate task.
This diff has been working nicely for me, with many suspend/resume cycles.
Never had a problem connecting to several wifis.
Any objections? Any Oks?
> The goal is to simplify things by only queuing one state transition
> at a time. The newstate task now always transitions to the most
> recently requested state, rather than hopping along with every request.
>
> This allows us get rid of the silly newstate generation counter, and
> allows us to simply cancel any outstanding transition when the interface
> goes down.
>
> The old code was queuing *additional* work from iwm_stop(). Which meant,
> for example, that every time upon resume, a task ran only to discover that
> it is no longer relevant.
>
> This probably needs some testing to shake out bugs.
> Test reports are very much appreciated!
>
> This change might also fix semi-frequent firmware errors during association.
> But not all -- I've found that running with IWM_DEBUG cranked up produces
> sufficient printfs to make firmware commands time out more often.
> It all seems very sensitive to timing which is hard to get completely
> right with tasks involved.
>
> Index: if_iwm.c
> ===================================================================
> RCS file: /cvs/src/sys/dev/pci/if_iwm.c,v
> retrieving revision 1.51
> diff -u -p -r1.51 if_iwm.c
> --- if_iwm.c 27 Sep 2015 16:53:38 -0000 1.51
> +++ if_iwm.c 27 Sep 2015 17:33:58 -0000
> @@ -195,14 +195,6 @@ const struct iwm_rate {
> #define IWM_RIDX_IS_CCK(_i_) ((_i_) < IWM_RIDX_OFDM)
> #define IWM_RIDX_IS_OFDM(_i_) ((_i_) >= IWM_RIDX_OFDM)
>
> -struct iwm_newstate_state {
> - struct task ns_wk;
> - struct ieee80211com *ns_ic;
> - enum ieee80211_state ns_nstate;
> - int ns_arg;
> - int ns_generation;
> -};
> -
> int iwm_store_cscheme(struct iwm_softc *, uint8_t *, size_t);
> int iwm_firmware_store_section(struct iwm_softc *, enum iwm_ucode_type,
> uint8_t *, size_t);
> @@ -406,7 +398,7 @@ struct ieee80211_node *iwm_node_alloc(st
> void iwm_calib_timeout(void *);
> void iwm_setrates(struct iwm_node *);
> int iwm_media_change(struct ifnet *);
> -void iwm_newstate_cb(void *);
> +void iwm_newstate_task(void *);
> int iwm_newstate(struct ieee80211com *, enum ieee80211_state, int);
> void iwm_endscan_cb(void *);
> int iwm_init_hw(struct iwm_softc *);
> @@ -5263,43 +5255,29 @@ iwm_media_change(struct ifnet *ifp)
> }
>
> void
> -iwm_newstate_cb(void *wk)
> +iwm_newstate_task(void *psc)
> {
> - struct iwm_newstate_state *iwmns = (void *)wk;
> - struct ieee80211com *ic = iwmns->ns_ic;
> - enum ieee80211_state nstate = iwmns->ns_nstate;
> - int generation = iwmns->ns_generation;
> + struct iwm_softc *sc = (struct iwm_softc *)psc;
> + struct ieee80211com *ic = &sc->sc_ic;
> + enum ieee80211_state nstate = sc->ns_nstate;
> + enum ieee80211_state ostate = ic->ic_state;
> struct iwm_node *in;
> - int arg = iwmns->ns_arg;
> - struct ifnet *ifp = IC2IFP(ic);
> - struct iwm_softc *sc = ifp->if_softc;
> + int arg = sc->ns_arg;
> int error;
>
> - free(iwmns, M_DEVBUF, sizeof(*iwmns));
> -
> - DPRINTF(("Prepare to switch state %s->%s\n",
> - ieee80211_state_name[ic->ic_state],
> - ieee80211_state_name[nstate]));
> - if (sc->sc_generation != generation) {
> - DPRINTF(("newstate_cb: someone pulled the plug meanwhile\n"));
> - if (nstate == IEEE80211_S_INIT) {
> - DPRINTF(("newstate_cb: nstate == IEEE80211_S_INIT:
> calling sc_newstate()\n"));
> - sc->sc_newstate(ic, nstate, arg);
> - }
> - return;
> - }
> -
> DPRINTF(("switching state %s->%s\n",
> - ieee80211_state_name[ic->ic_state],
> + ieee80211_state_name[ostate],
> ieee80211_state_name[nstate]));
>
> - if (ic->ic_state == IEEE80211_S_SCAN && nstate != ic->ic_state)
> + if (ostate == IEEE80211_S_SCAN && nstate != ostate)
> iwm_led_blink_stop(sc);
>
> /* disable beacon filtering if we're hopping out of RUN */
> - if (ic->ic_state == IEEE80211_S_RUN && nstate != ic->ic_state) {
> + if (ostate == IEEE80211_S_RUN && nstate != ostate)
> iwm_mvm_disable_beacon_filter(sc, (void *)ic->ic_bss);
>
> + /* Reset the device if moving out of AUTH, ASSOC, or RUN. */
> + if (ostate > IEEE80211_S_SCAN && nstate < ostate) {
> if (((in = (void *)ic->ic_bss) != NULL))
> in->in_assoc = 0;
> iwm_release(sc, NULL);
> @@ -5393,25 +5371,15 @@ iwm_newstate_cb(void *wk)
> int
> iwm_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
> {
> - struct iwm_newstate_state *iwmns;
> struct ifnet *ifp = IC2IFP(ic);
> struct iwm_softc *sc = ifp->if_softc;
>
> timeout_del(&sc->sc_calib_to);
>
> - iwmns = malloc(sizeof(*iwmns), M_DEVBUF, M_NOWAIT);
> - if (!iwmns) {
> - DPRINTF(("%s: allocating state cb mem failed\n", DEVNAME(sc)));
> - return ENOMEM;
> - }
> -
> - iwmns->ns_ic = ic;
> - iwmns->ns_nstate = nstate;
> - iwmns->ns_arg = arg;
> - iwmns->ns_generation = sc->sc_generation;
> + sc->ns_nstate = nstate;
> + sc->ns_arg = arg;
>
> - task_set(&iwmns->ns_wk, iwm_newstate_cb, iwmns);
> - task_add(sc->sc_nswq, &iwmns->ns_wk);
> + task_add(sc->sc_nswq, &sc->newstate_task);
>
> return 0;
> }
> @@ -5676,8 +5644,10 @@ iwm_stop(struct ifnet *ifp, int disable)
> ic->ic_scan_lock = IEEE80211_SCAN_UNLOCKED;
> ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
>
> - if (ic->ic_state != IEEE80211_S_INIT)
> - ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
> + task_del(systq, &sc->init_task);
> + task_del(sc->sc_nswq, &sc->newstate_task);
> + task_del(sc->sc_eswq, &sc->sc_eswk);
> + sc->sc_newstate(ic, IEEE80211_S_INIT, -1);
>
> timeout_del(&sc->sc_calib_to);
> iwm_led_blink_stop(sc);
> @@ -6614,6 +6584,7 @@ iwm_attach(struct device *parent, struct
> timeout_set(&sc->sc_calib_to, iwm_calib_timeout, sc);
> timeout_set(&sc->sc_led_blink_to, iwm_led_blink_timeout, sc);
> task_set(&sc->init_task, iwm_init_task, sc);
> + task_set(&sc->newstate_task, iwm_newstate_task, sc);
>
> /*
> * We cannot read the MAC address without loading the
> @@ -6687,8 +6658,7 @@ iwm_wakeup(struct iwm_softc *sc)
> reg = pci_conf_read(sc->sc_pct, sc->sc_pcitag, 0x40);
> pci_conf_write(sc->sc_pct, sc->sc_pcitag, 0x40, reg & ~0xff00);
>
> - iwm_init_task(sc);
> -
> + task_add(systq, &sc->init_task);
> }
>
> int
> Index: if_iwmvar.h
> ===================================================================
> RCS file: /cvs/src/sys/dev/pci/if_iwmvar.h,v
> retrieving revision 1.9
> diff -u -p -r1.9 if_iwmvar.h
> --- if_iwmvar.h 15 Jun 2015 08:06:12 -0000 1.9
> +++ if_iwmvar.h 27 Sep 2015 17:09:03 -0000
> @@ -364,6 +364,9 @@ struct iwm_softc {
> struct timeout sc_led_blink_to;
>
> struct task init_task;
> + struct task newstate_task;
> + enum ieee80211_state ns_nstate;
> + int ns_arg;
>
> bus_space_tag_t sc_st;
> bus_space_handle_t sc_sh;