aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/dev/rtwn/if_rtwn.c4
-rw-r--r--sys/dev/usb/wlan/if_rum.c4
-rw-r--r--sys/net80211/ieee80211.c4
-rw-r--r--sys/net80211/ieee80211_ddb.c15
-rw-r--r--sys/net80211/ieee80211_proto.c124
-rw-r--r--sys/net80211/ieee80211_var.h18
6 files changed, 136 insertions, 33 deletions
diff --git a/sys/dev/rtwn/if_rtwn.c b/sys/dev/rtwn/if_rtwn.c
index baf427b4aafc..4334d5700e51 100644
--- a/sys/dev/rtwn/if_rtwn.c
+++ b/sys/dev/rtwn/if_rtwn.c
@@ -614,10 +614,12 @@ rtwn_vap_delete(struct ieee80211vap *vap)
struct ieee80211com *ic = vap->iv_ic;
struct rtwn_softc *sc = ic->ic_softc;
struct rtwn_vap *uvp = RTWN_VAP(vap);
+ int i;
/* Put vap into INIT state + stop device if needed. */
ieee80211_stop(vap);
- ieee80211_draintask(ic, &vap->iv_nstate_task);
+ for (i = 0; i < NET80211_IV_NSTATE_NUM; i++)
+ ieee80211_draintask(ic, &vap->iv_nstate_task[i]);
ieee80211_draintask(ic, &ic->ic_parent_task);
RTWN_LOCK(sc);
diff --git a/sys/dev/usb/wlan/if_rum.c b/sys/dev/usb/wlan/if_rum.c
index d4efc37a783f..364f02393d8d 100644
--- a/sys/dev/usb/wlan/if_rum.c
+++ b/sys/dev/usb/wlan/if_rum.c
@@ -719,10 +719,12 @@ rum_vap_delete(struct ieee80211vap *vap)
struct rum_vap *rvp = RUM_VAP(vap);
struct ieee80211com *ic = vap->iv_ic;
struct rum_softc *sc = ic->ic_softc;
+ int i;
/* Put vap into INIT state. */
ieee80211_new_state(vap, IEEE80211_S_INIT, -1);
- ieee80211_draintask(ic, &vap->iv_nstate_task);
+ for (i = 0; i < NET80211_IV_NSTATE_NUM; i++)
+ ieee80211_draintask(ic, &vap->iv_nstate_task[i]);
RUM_LOCK(sc);
/* Cancel any unfinished Tx. */
diff --git a/sys/net80211/ieee80211.c b/sys/net80211/ieee80211.c
index 2bc85b7ac04a..a3de9fd91797 100644
--- a/sys/net80211/ieee80211.c
+++ b/sys/net80211/ieee80211.c
@@ -729,6 +729,7 @@ ieee80211_vap_detach(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
struct ifnet *ifp = vap->iv_ifp;
+ int i;
CURVNET_SET(ifp->if_vnet);
@@ -743,7 +744,8 @@ ieee80211_vap_detach(struct ieee80211vap *vap)
/*
* Flush any deferred vap tasks.
*/
- ieee80211_draintask(ic, &vap->iv_nstate_task);
+ for (i = 0; i < NET80211_IV_NSTATE_NUM; i++)
+ ieee80211_draintask(ic, &vap->iv_nstate_task[i]);
ieee80211_draintask(ic, &vap->iv_swbmiss_task);
ieee80211_draintask(ic, &vap->iv_wme_task);
ieee80211_draintask(ic, &ic->ic_parent_task);
diff --git a/sys/net80211/ieee80211_ddb.c b/sys/net80211/ieee80211_ddb.c
index 29de6d10fcc3..f028c4273ee3 100644
--- a/sys/net80211/ieee80211_ddb.c
+++ b/sys/net80211/ieee80211_ddb.c
@@ -470,8 +470,9 @@ _db_show_vap(const struct ieee80211vap *vap, int showmesh, int showprocs)
if (vap->iv_opmode == IEEE80211_M_MBSS)
db_printf("(%p)", vap->iv_mesh);
#endif
- db_printf(" state %s", ieee80211_state_name[vap->iv_state]);
- db_printf(" ifp %p(%s)", vap->iv_ifp, vap->iv_ifp->if_xname);
+ db_printf(" state %#x %s", vap->iv_state,
+ ieee80211_state_name[vap->iv_state]);
+ db_printf(" ifp %p(%s)", vap->iv_ifp, if_name(vap->iv_ifp));
db_printf("\n");
db_printf("\tic %p", vap->iv_ic);
@@ -482,6 +483,16 @@ _db_show_vap(const struct ieee80211vap *vap, int showmesh, int showprocs)
struct sysctllog *iv_sysctl; /* dynamic sysctl context */
#endif
db_printf("\n");
+
+ db_printf("\tiv_nstate %#x %s iv_nstate_b %d iv_nstate_n %d\n",
+ vap->iv_nstate, ieee80211_state_name[vap->iv_nstate], /* historic */
+ vap->iv_nstate_b, vap->iv_nstate_n);
+ for (i = 0; i < NET80211_IV_NSTATE_NUM; i++) {
+ db_printf("\t [%d] iv_nstates %#x %s _task %p _args %d\n", i,
+ vap->iv_nstates[i], ieee80211_state_name[vap->iv_nstates[i]],
+ &vap->iv_nstate_task[i], vap->iv_nstate_args[i]);
+ }
+
db_printf("\tdebug=%b\n", vap->iv_debug, IEEE80211_MSG_BITS);
db_printf("\tflags=%b\n", vap->iv_flags, IEEE80211_F_BITS);
diff --git a/sys/net80211/ieee80211_proto.c b/sys/net80211/ieee80211_proto.c
index 5ed9f2e3f50e..bc097888a1d6 100644
--- a/sys/net80211/ieee80211_proto.c
+++ b/sys/net80211/ieee80211_proto.c
@@ -340,7 +340,8 @@ ieee80211_proto_vattach(struct ieee80211vap *vap)
vap->iv_bmiss_max = IEEE80211_BMISS_MAX;
callout_init_mtx(&vap->iv_swbmiss, IEEE80211_LOCK_OBJ(ic), 0);
callout_init(&vap->iv_mgtsend, 1);
- TASK_INIT(&vap->iv_nstate_task, 0, ieee80211_newstate_cb, vap);
+ for (i = 0; i < NET80211_IV_NSTATE_NUM; i++)
+ TASK_INIT(&vap->iv_nstate_task[i], 0, ieee80211_newstate_cb, vap);
TASK_INIT(&vap->iv_swbmiss_task, 0, beacon_swmiss, vap);
TASK_INIT(&vap->iv_wme_task, 0, vap_update_wme, vap);
TASK_INIT(&vap->iv_slot_task, 0, vap_update_slot, vap);
@@ -2497,6 +2498,51 @@ wakeupwaiting(struct ieee80211vap *vap0)
}
}
+static int
+_ieee80211_newstate_get_next_empty_slot(struct ieee80211vap *vap)
+{
+ int nstate_num;
+
+ IEEE80211_LOCK_ASSERT(vap->iv_ic);
+
+ if (vap->iv_nstate_n >= NET80211_IV_NSTATE_NUM)
+ return (-1);
+
+ nstate_num = vap->iv_nstate_b + vap->iv_nstate_n;
+ nstate_num %= NET80211_IV_NSTATE_NUM;
+ vap->iv_nstate_n++;
+
+ return (nstate_num);
+}
+
+static int
+_ieee80211_newstate_get_next_pending_slot(struct ieee80211vap *vap)
+{
+ int nstate_num;
+
+ IEEE80211_LOCK_ASSERT(vap->iv_ic);
+
+ KASSERT(vap->iv_nstate_n > 0, ("%s: vap %p iv_nstate_n %d\n",
+ __func__, vap, vap->iv_nstate_n));
+
+ nstate_num = vap->iv_nstate_b;
+ vap->iv_nstate_b++;
+ if (vap->iv_nstate_b >= NET80211_IV_NSTATE_NUM)
+ vap->iv_nstate_b = 0;
+ vap->iv_nstate_n--;
+
+ return (nstate_num);
+}
+
+static int
+_ieee80211_newstate_get_npending(struct ieee80211vap *vap)
+{
+
+ IEEE80211_LOCK_ASSERT(vap->iv_ic);
+
+ return (vap->iv_nstate_n);
+}
+
/*
* Handle post state change work common to all operating modes.
*/
@@ -2506,17 +2552,25 @@ ieee80211_newstate_cb(void *xvap, int npending)
struct ieee80211vap *vap = xvap;
struct ieee80211com *ic = vap->iv_ic;
enum ieee80211_state nstate, ostate;
- int arg, rc;
+ int arg, rc, nstate_num;
+ KASSERT(npending == 1, ("%s: vap %p with npending %d != 1\n",
+ __func__, vap, npending));
IEEE80211_LOCK(ic);
- nstate = vap->iv_nstate;
- arg = vap->iv_nstate_arg;
+ nstate_num = _ieee80211_newstate_get_next_pending_slot(vap);
+
+ /*
+ * Update the historic fields for now as they are used in some
+ * drivers and reduce code changes for now.
+ */
+ vap->iv_nstate = nstate = vap->iv_nstates[nstate_num];
+ arg = vap->iv_nstate_args[nstate_num];
IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
"%s:%d: running state update %s -> %s (%d)\n",
__func__, __LINE__,
ieee80211_state_name[vap->iv_state],
- ieee80211_state_name[vap->iv_nstate],
+ ieee80211_state_name[nstate],
npending);
if (vap->iv_flags_ext & IEEE80211_FEXT_REINIT) {
@@ -2527,9 +2581,10 @@ ieee80211_newstate_cb(void *xvap, int npending)
/* Deny any state changes while we are here. */
vap->iv_nstate = IEEE80211_S_INIT;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
- "%s: %s -> %s arg %d\n", __func__,
+ "%s: %s -> %s arg %d -> %s arg %d\n", __func__,
ieee80211_state_name[vap->iv_state],
- ieee80211_state_name[vap->iv_nstate], arg);
+ ieee80211_state_name[vap->iv_nstate], 0,
+ ieee80211_state_name[nstate], arg);
vap->iv_newstate(vap, vap->iv_nstate, 0);
IEEE80211_LOCK_ASSERT(ic);
vap->iv_flags_ext &= ~(IEEE80211_FEXT_REINIT |
@@ -2670,7 +2725,7 @@ ieee80211_new_state_locked(struct ieee80211vap *vap,
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211vap *vp;
enum ieee80211_state ostate;
- int nrunning, nscanning;
+ int nrunning, nscanning, nstate_num;
IEEE80211_LOCK_ASSERT(ic);
@@ -2692,14 +2747,6 @@ ieee80211_new_state_locked(struct ieee80211vap *vap,
ieee80211_state_name[nstate],
ieee80211_state_name[vap->iv_nstate]);
return -1;
- } else if (vap->iv_state != vap->iv_nstate) {
- /* Warn if the previous state hasn't completed. */
- IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
- "%s:%d: pending %s -> %s (now to %s) transition lost\n",
- __func__, __LINE__,
- ieee80211_state_name[vap->iv_state],
- ieee80211_state_name[vap->iv_nstate],
- ieee80211_state_name[nstate]);
}
}
@@ -2722,7 +2769,16 @@ ieee80211_new_state_locked(struct ieee80211vap *vap,
nscanning++;
}
}
- ostate = vap->iv_state;
+ /*
+ * Look ahead for the "old state" at that point when the last queued
+ * state transition is run.
+ */
+ if (vap->iv_nstate_n == 0) {
+ ostate = vap->iv_state;
+ } else {
+ nstate_num = (vap->iv_nstate_b + vap->iv_nstate_n - 1) % NET80211_IV_NSTATE_NUM;
+ ostate = vap->iv_nstates[nstate_num];
+ }
IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
"%s: %s -> %s (arg %d) (nrunning %d nscanning %d)\n", __func__,
ieee80211_state_name[ostate], ieee80211_state_name[nstate], arg,
@@ -2816,11 +2872,37 @@ ieee80211_new_state_locked(struct ieee80211vap *vap,
default:
break;
}
- /* defer the state change to a thread */
- vap->iv_nstate = nstate;
- vap->iv_nstate_arg = arg;
+ /*
+ * Defer the state change to a thread.
+ * We support up-to NET80211_IV_NSTATE_NUM pending state changes
+ * using a separate task for each. Otherwise, if we enqueue
+ * more than one state change they will be folded together,
+ * npedning will be > 1 and we may run then out of sequence with
+ * other events.
+ * This is kind-of a hack after 10 years but we know how to provoke
+ * these cases now (and seen them in the wild).
+ */
+ nstate_num = _ieee80211_newstate_get_next_empty_slot(vap);
+ if (nstate_num == -1) {
+ /*
+ * This is really bad and we should just go kaboom.
+ * Instead drop it. No one checks the return code anyway.
+ */
+ ic_printf(ic, "%s:%d: pending %s -> %s (now to %s) "
+ "transition lost. %d/%d pending state changes:\n",
+ __func__, __LINE__,
+ ieee80211_state_name[vap->iv_state],
+ ieee80211_state_name[vap->iv_nstate],
+ ieee80211_state_name[nstate],
+ _ieee80211_newstate_get_npending(vap),
+ NET80211_IV_NSTATE_NUM);
+
+ return (EAGAIN);
+ }
+ vap->iv_nstates[nstate_num] = nstate;
+ vap->iv_nstate_args[nstate_num] = arg;
vap->iv_flags_ext |= IEEE80211_FEXT_STATEWAIT;
- ieee80211_runtask(ic, &vap->iv_nstate_task);
+ ieee80211_runtask(ic, &vap->iv_nstate_task[nstate_num]);
return EINPROGRESS;
}
diff --git a/sys/net80211/ieee80211_var.h b/sys/net80211/ieee80211_var.h
index 868f1886069c..aa99ccefd248 100644
--- a/sys/net80211/ieee80211_var.h
+++ b/sys/net80211/ieee80211_var.h
@@ -410,9 +410,16 @@ struct ieee80211vap {
uint32_t iv_com_state; /* com usage / detached flag */
enum ieee80211_opmode iv_opmode; /* operation mode */
enum ieee80211_state iv_state; /* state machine state */
- enum ieee80211_state iv_nstate; /* pending state */
- int iv_nstate_arg; /* pending state arg */
- struct task iv_nstate_task; /* deferred state processing */
+
+ /* Deferred state processing. */
+ enum ieee80211_state iv_nstate; /* next pending state (historic) */
+#define NET80211_IV_NSTATE_NUM 8
+ int iv_nstate_b; /* First filled slot. */
+ int iv_nstate_n; /* # of filled slots. */
+ enum ieee80211_state iv_nstates[NET80211_IV_NSTATE_NUM]; /* queued pending state(s) */
+ int iv_nstate_args[NET80211_IV_NSTATE_NUM]; /* queued pending state(s) arg */
+ struct task iv_nstate_task[NET80211_IV_NSTATE_NUM];
+
struct task iv_swbmiss_task;/* deferred iv_bmiss call */
struct callout iv_mgtsend; /* mgmt frame response timer */
/* inactivity timer settings */
@@ -604,10 +611,7 @@ struct ieee80211vap {
struct ieee80211_node * (*iv_update_bss)(struct ieee80211vap *,
struct ieee80211_node *);
-#ifdef __ILP32__
- uint32_t iv_spare0;
-#endif
- uint64_t iv_spare[5];
+ uint64_t iv_spare[36];
};
MALLOC_DECLARE(M_80211_VAP);