The mac context task gets triggered by changing parameters in beacons
received from our AP while ic_state indicates that we are in RUN state.
This can happen during regular operation and while a background scan
is in progress.
When a background scan has found a new AP, we trigger a RUN -> AUTH state
change by scheduling the newstate task. Any other driver tasks which are
pending are removed from the task queue now, before the newstate task is
inserted. So a pending mac context task will be removed at this point.
However, there is a chance that a mac context task gets inserted into
the task queue again, after the newstate task.
The newstate task has been scheduled, and when it runs it will issue a
long series of firmware commands to perform the state change, which involves
waiting for command responses. While this is going on the ic_state variable
still indicates that we are in RUN state, and any beacon we receive now could
contain a changed parameter and cause a mac context task to be scheduled.
Then, after completing the switch into AUTH state the scheduled mac context
task runs and sends its command to the firmware. This results in a fatal
firmware error along with the message "could not update MAC context" from
the driver.
We can prevent this problem by making the mac context task send its
command only if we are still in RUN state when the task gets to run.
Additionally, avoid scheduling a mac context task if a pending newstate
task is going to move us out of RUN state anyway (this part is just a
tiny optimization, it doesn't fix the bug).
Beacons are usually sent every 100ms, but parameters change less often.
Whether this issue triggers ultimately depends on timing and the behaviour
of the AP.
Issue debugged by zxystd in OpenIntelWireless itlwm; patch by me.
ok?
diff 18a81a05d6e3098eadc4b5e23d6656377f87591a /usr/src
blob - e33fe39b9784c5bd4f8b28099af9f97c04e49c65
file + sys/dev/pci/if_iwm.c
--- sys/dev/pci/if_iwm.c
+++ sys/dev/pci/if_iwm.c
@@ -3223,7 +3223,8 @@ iwm_mac_ctxt_task(void *arg)
struct iwm_node *in = (void *)ic->ic_bss;
int err, s = splnet();
- if (sc->sc_flags & IWM_FLAG_SHUTDOWN) {
+ if ((sc->sc_flags & IWM_FLAG_SHUTDOWN) ||
+ ic->ic_state != IEEE80211_S_RUN) {
refcnt_rele_wake(&sc->task_refs);
splx(s);
return;
@@ -3242,7 +3243,8 @@ iwm_updateprot(struct ieee80211com *ic)
{
struct iwm_softc *sc = ic->ic_softc;
- if (ic->ic_state == IEEE80211_S_RUN)
+ if (ic->ic_state == IEEE80211_S_RUN &&
+ !task_pending(&sc->newstate_task))
iwm_add_task(sc, systq, &sc->mac_ctxt_task);
}
@@ -3251,7 +3253,8 @@ iwm_updateslot(struct ieee80211com *ic)
{
struct iwm_softc *sc = ic->ic_softc;
- if (ic->ic_state == IEEE80211_S_RUN)
+ if (ic->ic_state == IEEE80211_S_RUN &&
+ !task_pending(&sc->newstate_task))
iwm_add_task(sc, systq, &sc->mac_ctxt_task);
}
@@ -3260,7 +3263,8 @@ iwm_updateedca(struct ieee80211com *ic)
{
struct iwm_softc *sc = ic->ic_softc;
- if (ic->ic_state == IEEE80211_S_RUN)
+ if (ic->ic_state == IEEE80211_S_RUN &&
+ !task_pending(&sc->newstate_task))
iwm_add_task(sc, systq, &sc->mac_ctxt_task);
}
blob - 21bc8be34500730264ad206513951a32fcb2d702
file + sys/dev/pci/if_iwx.c
--- sys/dev/pci/if_iwx.c
+++ sys/dev/pci/if_iwx.c
@@ -2947,7 +2947,8 @@ iwx_mac_ctxt_task(void *arg)
struct iwx_node *in = (void *)ic->ic_bss;
int err, s = splnet();
- if (sc->sc_flags & IWX_FLAG_SHUTDOWN) {
+ if ((sc->sc_flags & IWX_FLAG_SHUTDOWN) ||
+ ic->ic_state != IEEE80211_S_RUN) {
refcnt_rele_wake(&sc->task_refs);
splx(s);
return;
@@ -2966,7 +2967,8 @@ iwx_updateprot(struct ieee80211com *ic)
{
struct iwx_softc *sc = ic->ic_softc;
- if (ic->ic_state == IEEE80211_S_RUN)
+ if (ic->ic_state == IEEE80211_S_RUN &&
+ !task_pending(&sc->newstate_task))
iwx_add_task(sc, systq, &sc->mac_ctxt_task);
}
@@ -2975,7 +2977,8 @@ iwx_updateslot(struct ieee80211com *ic)
{
struct iwx_softc *sc = ic->ic_softc;
- if (ic->ic_state == IEEE80211_S_RUN)
+ if (ic->ic_state == IEEE80211_S_RUN &&
+ !task_pending(&sc->newstate_task))
iwx_add_task(sc, systq, &sc->mac_ctxt_task);
}
@@ -2984,7 +2987,8 @@ iwx_updateedca(struct ieee80211com *ic)
{
struct iwx_softc *sc = ic->ic_softc;
- if (ic->ic_state == IEEE80211_S_RUN)
+ if (ic->ic_state == IEEE80211_S_RUN &&
+ !task_pending(&sc->newstate_task))
iwx_add_task(sc, systq, &sc->mac_ctxt_task);
}