git: 401434a46c10 - main - iwm: add phy/mac context tasks; clean up task stop; add net80211 MAC methods
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 16 Oct 2026 03:54:18 UTC
The branch main has been updated by adrian:
URL: https://cgit.FreeBSD.org/src/commit/?id=401434a46c1067dd5fea8ba5e4886efb66c47908
commit 401434a46c1067dd5fea8ba5e4886efb66c47908
Author: Adrian Chadd <adrian@FreeBSD.org>
AuthorDate: 2026-10-16 03:53:19 +0000
Commit: Adrian Chadd <adrian@FreeBSD.org>
CommitDate: 2026-10-16 03:53:22 +0000
iwm: add phy/mac context tasks; clean up task stop; add net80211 MAC methods
* Add phy/mac tasks, the phy task is currently a placeholder
* Add in the MAC methods from net80211 that are called whenever
the VAP mode needs updating
* Mark the hardware as STOPPED during shutdown so scheduled tasks
don't run
The fun side of this work:
* detaching net80211 causes the parent method to be called which leads
to iwm_stop() being called, and at least one of the entry paths
into iwm_stop() has IWM_LOCK held, and we can't do a taskqueue_drain()
or taskqueue_drain_all() call with any lock held as it will sleep.
* the net80211 detach path is calling at least one of the MAC methods
during detach, and since the taskqueue is being deleted /before/
the net80211 detach path, we have to make sure nothing is actually
going to do taskqueue work after that!
* STOPPED in FreeBSD is MOSTLY doing the same as SHUTDOWN in OpenBSD.
It should be fine enough for this.
Locally tested:
* 3160/7260/8260/9260 STA mode - create, ctrl-C wpa_supplicant; create,
destroy; create, unload; create, destroy, unload. The create+unload paths
generate interesting net80211 tear down states which were causing
issues here.
Differential Revision: https://reviews.freebsd.org/D60607
---
sys/dev/iwm/if_iwm.c | 169 +++++++++++++++++++++++++++++++++++++++++++++++-
sys/dev/iwm/if_iwmvar.h | 6 ++
2 files changed, 174 insertions(+), 1 deletion(-)
diff --git a/sys/dev/iwm/if_iwm.c b/sys/dev/iwm/if_iwm.c
index ae00187d7819..fef762024c6f 100644
--- a/sys/dev/iwm/if_iwm.c
+++ b/sys/dev/iwm/if_iwm.c
@@ -2149,6 +2149,69 @@ iwm_setup_ht_rates(struct iwm_softc *sc)
/* XXX TODO: MCS8..15 if >1 antenna and mimo enabled */
}
+static void
+iwm_vap_update_slot(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct iwm_softc *sc = ic->ic_softc;
+
+ IWM_LOCK_ASSERT_UNLOCKED(sc);
+
+ IWM_LOCK(sc);
+ if ((sc->sc_flags & IWM_FLAG_STOPPED) == 0) {
+ taskqueue_enqueue(sc->sc_tq, &sc->mac_ctxt_task);
+ }
+ IWM_UNLOCK(sc);
+}
+
+static void
+iwm_vap_update_preamble(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct iwm_softc *sc = ic->ic_softc;
+
+ IWM_LOCK_ASSERT_UNLOCKED(sc);
+
+ IWM_LOCK(sc);
+ if ((sc->sc_flags & IWM_FLAG_STOPPED) == 0) {
+ taskqueue_enqueue(sc->sc_tq, &sc->mac_ctxt_task);
+ }
+ IWM_UNLOCK(sc);
+}
+
+static int
+iwm_vap_update_wme(struct ieee80211vap *vap,
+ const struct wmeParams *wme_params)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct iwm_softc *sc = ic->ic_softc;
+
+ IWM_LOCK_ASSERT_UNLOCKED(sc);
+
+ IWM_LOCK(sc);
+ if ((sc->sc_flags & IWM_FLAG_STOPPED) == 0) {
+ taskqueue_enqueue(sc->sc_tq, &sc->mac_ctxt_task);
+ }
+ IWM_UNLOCK(sc);
+
+ return (0);
+}
+
+static void
+iwm_vap_update_erp_protmode(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct iwm_softc *sc = ic->ic_softc;
+
+ IWM_LOCK_ASSERT_UNLOCKED(sc);
+
+ IWM_LOCK(sc);
+ if ((sc->sc_flags & IWM_FLAG_STOPPED) == 0) {
+ taskqueue_enqueue(sc->sc_tq, &sc->mac_ctxt_task);
+ }
+ IWM_UNLOCK(sc);
+}
+
static void
iwm_set_hw_address_family_8000(struct iwm_softc *sc, struct iwm_nvm_data *data,
const uint16_t *mac_override, const uint16_t *nvm_hw)
@@ -5209,9 +5272,20 @@ iwm_start(struct iwm_softc *sc)
static void
iwm_stop(struct iwm_softc *sc)
{
+ IWM_LOCK_ASSERT_LOCKED(sc);
sc->sc_flags &= ~IWM_FLAG_HW_INITED;
sc->sc_flags |= IWM_FLAG_STOPPED;
+
+ /*
+ * Attempt to cancel; but if we can't, then don't call drain as
+ * that can't be done with the lock held.
+ */
+ if (sc->sc_tq != NULL) {
+ taskqueue_cancel(sc->sc_tq, &sc->mac_ctxt_task, NULL);
+ taskqueue_cancel(sc->sc_tq, &sc->phy_ctxt_task, NULL);
+ }
+
sc->sc_generation++;
iwm_led_blink_stop(sc);
sc->sc_tx_timer = 0;
@@ -5249,6 +5323,8 @@ iwm_parent(struct ieee80211com *ic)
int startall = 0;
int rfkill = 0;
+ IWM_LOCK_ASSERT_UNLOCKED(sc);
+
IWM_LOCK(sc);
if (ic->ic_nrunning > 0) {
if (!(sc->sc_flags & IWM_FLAG_HW_INITED)) {
@@ -5273,6 +5349,8 @@ iwm_rftoggle_task(void *arg, int npending __unused)
struct ieee80211com *ic = &sc->sc_ic;
int rfkill;
+ IWM_LOCK_ASSERT_UNLOCKED(sc);
+
IWM_LOCK(sc);
rfkill = iwm_check_rfkill(sc);
IWM_UNLOCK(sc);
@@ -5289,6 +5367,68 @@ iwm_rftoggle_task(void *arg, int npending __unused)
}
}
+/*
+ * Deferred MAC related task updates.
+ *
+ * This is scheduled whenever something in the STA MAC
+ * config has changed. This driver currently only supports
+ * a single STA interface, so that's the assumption made
+ * here.
+ */
+static void
+iwm_mac_ctxt_task(void *arg, int npending __unused)
+{
+ struct iwm_softc *sc = arg;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ const struct iwm_vap *iv;
+ int error;
+
+ IWM_LOCK_ASSERT_UNLOCKED(sc);
+
+ if (vap == NULL)
+ return;
+
+ iv = IWM_VAP(vap);
+
+ IWM_LOCK(sc);
+
+ /* Don't scheduled work if the driver is stopped */
+ if (sc->sc_flags & IWM_FLAG_STOPPED) {
+ IWM_UNLOCK(sc);
+ return;
+ }
+
+ /*
+ * Only trigger an update if we've already programmed in a
+ * MAC context.
+ */
+ if (iv->is_uploaded) {
+ if ((error = iwm_mac_ctxt_changed(sc, vap)) != 0) {
+ device_printf(sc->sc_dev,
+ "%s: failed to update MAC\n", __func__);
+ }
+ }
+ IWM_UNLOCK(sc);
+}
+
+/*
+ * Deferred PHY related task updates.
+ *
+ * This is scheduled whenever something in the STA PHY
+ * config has changed. This driver currently only supports
+ * a single STA interface, so that's the assumption made
+ * here.
+ */
+static void
+iwm_phy_ctxt_task(void *arg, int npending __unused)
+{
+ struct iwm_softc *sc = arg;
+ /* TODO */
+
+ device_printf(sc->sc_dev, "%s: called\n", __func__);
+}
+
/*
* The interrupt side of things
*/
@@ -6326,6 +6466,14 @@ iwm_attach(device_t dev)
TASK_INIT(&sc->sc_es_task, 0, iwm_endscan_cb, sc);
TASK_INIT(&sc->sc_rftoggle_task, 0, iwm_rftoggle_task, sc);
+ /*
+ * Note: these would be per-ctx, but right now the driver
+ * only knows about a single VAP. This mirrors what
+ * OpenBSD iwm does to make porting/updating easier.
+ */
+ TASK_INIT(&sc->mac_ctxt_task, 0, iwm_mac_ctxt_task, sc);
+ TASK_INIT(&sc->phy_ctxt_task, 0, iwm_phy_ctxt_task, sc);
+
sc->sc_tq = taskqueue_create("iwm_taskq", M_WAITOK,
taskqueue_thread_enqueue, &sc->sc_tq);
error = taskqueue_start_threads(&sc->sc_tq, 1, 0, "iwm_taskq");
@@ -6711,6 +6859,11 @@ iwm_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit,
ivp->iv_newstate = vap->iv_newstate;
vap->iv_newstate = iwm_newstate;
+ vap->iv_updateslot = iwm_vap_update_slot;
+ vap->iv_erp_protmode_update = iwm_vap_update_erp_protmode;
+ vap->iv_preamble_update = iwm_vap_update_preamble;
+ vap->iv_wme_update = iwm_vap_update_wme;
+
ivp->id = IWM_DEFAULT_MACID;
ivp->color = IWM_DEFAULT_COLOR;
@@ -6905,15 +7058,29 @@ iwm_detach_local(struct iwm_softc *sc, int do_net80211)
IWM_LOCK_ASSERT_UNLOCKED(sc);
- if (!sc->sc_attached)
+ IWM_LOCK(sc);
+ if (!sc->sc_attached) {
+ IWM_UNLOCK(sc);
return 0;
+ }
sc->sc_attached = 0;
+ sc->sc_flags |= IWM_FLAG_STOPPED;
+ IWM_UNLOCK(sc);
+
if (do_net80211) {
ieee80211_draintask(&sc->sc_ic, &sc->sc_es_task);
}
iwm_stop_device(sc);
+
+ while (taskqueue_cancel(sc->sc_tq, &sc->mac_ctxt_task, NULL) != 0)
+ taskqueue_drain(sc->sc_tq, &sc->mac_ctxt_task);
+ while (taskqueue_cancel(sc->sc_tq, &sc->phy_ctxt_task, NULL) != 0)
+ taskqueue_drain(sc->sc_tq, &sc->phy_ctxt_task);
+
taskqueue_drain_all(sc->sc_tq);
taskqueue_free(sc->sc_tq);
+ sc->sc_tq = NULL;
+
if (do_net80211) {
IWM_LOCK(sc);
iwm_xmit_queue_drain(sc);
diff --git a/sys/dev/iwm/if_iwmvar.h b/sys/dev/iwm/if_iwmvar.h
index 0d61aee37d8e..4497bd91c8a1 100644
--- a/sys/dev/iwm/if_iwmvar.h
+++ b/sys/dev/iwm/if_iwmvar.h
@@ -439,6 +439,12 @@ struct iwm_softc {
struct task init_task;
+ /* Task for ERP/HT prot/slot-time/EDCA updates. */
+ struct task mac_ctxt_task;
+
+ /* Task for HT 20/40 MHz channel width updates. */
+ struct task phy_ctxt_task;
+
struct resource *sc_irq;
struct resource *sc_mem;
bus_space_tag_t sc_st;