git: 9592f563c36b - main - LinuxKPI: 802.11: split (*bss_info_changed) up for more modern drivers

From: Bjoern A. Zeeb <bz_at_FreeBSD.org>
Date: Thu, 05 Mar 2026 13:44:35 UTC
The branch main has been updated by bz:

URL: https://cgit.FreeBSD.org/src/commit/?id=9592f563c36bd207d98f1b3a13839b88d5760d97

commit 9592f563c36bd207d98f1b3a13839b88d5760d97
Author:     Bjoern A. Zeeb <bz@FreeBSD.org>
AuthorDate: 2026-01-03 20:10:00 +0000
Commit:     Bjoern A. Zeeb <bz@FreeBSD.org>
CommitDate: 2026-03-05 13:44:02 +0000

    LinuxKPI: 802.11: split (*bss_info_changed) up for more modern drivers
    
    With the advent of MLO some of the updates (*bss_info_changed) would
    have done are not per-link.  This had (*vif_cfg_changed) and
    (*link_conf_changed) introduced which are used by iwlwifi, rtw89,
    select mt76 drivers, and ath12k currently it seems.
    A driver normally only supports on or the other set.
    
    Factor out the call to (*bss_info_changed) into an internal function.
    There split the options up depending on whether they are for the
    vif or a link and leave a fallback to (*bss_info_changed) for older
    drivers.
    
    Add the mac80211 ops implementations for the two new calls along with
    a currently unused backup option for (*bss_info_changed) for each
    as I assume we will eventually call the directly rather than from the
    internal wrapper function.
    
    Sponsored by:   The FreeBSD Foundation
    MFC after:      3 days
---
 sys/compat/linuxkpi/common/src/linux_80211.c       | 75 ++++++++++++++++++----
 sys/compat/linuxkpi/common/src/linux_80211.h       |  4 ++
 .../linuxkpi/common/src/linux_80211_macops.c       | 70 ++++++++++++++++++--
 3 files changed, 128 insertions(+), 21 deletions(-)

diff --git a/sys/compat/linuxkpi/common/src/linux_80211.c b/sys/compat/linuxkpi/common/src/linux_80211.c
index 799ecb245661..c003075ae601 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211.c
+++ b/sys/compat/linuxkpi/common/src/linux_80211.c
@@ -2242,6 +2242,53 @@ lkpi_remove_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 	free(lchanctx, M_LKPI80211);
 }
 
+/* -------------------------------------------------------------------------- */
+
+/* Any other options belong here? Check more drivers. */
+#define	BSS_CHANGED_VIF_CFG_BITS					\
+    (BSS_CHANGED_SSID | BSS_CHANGED_IDLE | BSS_CHANGED_PS | BSS_CHANGED_ASSOC | \
+    BSS_CHANGED_ARP_FILTER | BSS_CHANGED_MLD_VALID_LINKS | BSS_CHANGED_MLD_TTLM)
+
+static void
+lkpi_bss_info_change(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+    enum ieee80211_bss_changed bss_changed)
+{
+	struct lkpi_vif *lvif;
+	enum ieee80211_bss_changed vif_cfg_bits, link_info_bits;
+
+	if (ieee80211_vif_is_mld(vif)) {
+		TODO("This likely needs a subset only; split up into 3 parts.");
+	}
+
+	/* Nothing to do? */
+	if (bss_changed == 0)
+		return;
+
+	/*
+	 * If the vif is not known to the driver there is nothing to notifiy for.
+	 * We MUST NOT check for !lvif_bss_synched here (the reasonable it seems)
+	 * as we need to execute the update(s) or we will have follow-up issues.
+	 */
+	lvif = VIF_TO_LVIF(vif);
+	if (!lvif->added_to_drv)
+		return;
+
+	/*
+	 * With the advent of MLO bss_conf got split up into vif and link
+	 * change notfications, while historically it was one.
+	 * We now need to support all possible models.
+	 */
+	vif_cfg_bits = bss_changed & BSS_CHANGED_VIF_CFG_BITS;
+	if (vif_cfg_bits != 0)
+		lkpi_80211_mo_vif_cfg_changed(hw, vif, vif_cfg_bits, false);
+
+	link_info_bits = bss_changed & ~(BSS_CHANGED_VIF_CFG_BITS);
+	if (link_info_bits != 0)
+		lkpi_80211_mo_link_info_changed(hw, vif, &vif->bss_conf,
+		    link_info_bits, 0, false);
+
+	lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
+}
 
 /* -------------------------------------------------------------------------- */
 
@@ -2457,7 +2504,7 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int
 
 	/* RATES */
 	IMPROVE("bss info: not all needs to come now and rates are missing");
-	lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
+	lkpi_bss_info_change(hw, vif, bss_changed);
 
 	/*
 	 * Given ni and lsta are 1:1 from alloc to free we can assert that
@@ -2791,7 +2838,7 @@ lkpi_sta_assoc_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int
 	}
 
 	bss_changed |= lkpi_update_dtim_tsf(vif, ni, vap, __func__, __LINE__);
-	lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
+	lkpi_bss_info_change(hw, vif, bss_changed);
 
 	/* - change_chanctx (if needed)
 	 * - event_callback
@@ -2851,7 +2898,7 @@ lkpi_sta_assoc_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int
 
 	bss_changed = 0;
 	bss_changed |= lkpi_update_dtim_tsf(vif, ni, vap, __func__, __LINE__);
-	lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
+	lkpi_bss_info_change(hw, vif, bss_changed);
 
 	/* Prepare_multicast && configure_filter. */
 	lkpi_update_mcast_filter(vap->iv_ic);
@@ -3289,7 +3336,7 @@ lkpi_sta_auth_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int
 	/* XXX BSS_CHANGED_???? */
 	vif->bss_conf.dtim_period = 0; /* go back to 0. */
 	bss_changed |= BSS_CHANGED_BEACON_INFO;
-	lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
+	lkpi_bss_info_change(hw, vif, bss_changed);
 
 	LKPI_80211_LVIF_LOCK(lvif);
 	/* Remove ni reference for this cache of lsta. */
@@ -3653,7 +3700,7 @@ lkpi_wme_update(struct lkpi_hw *lhw, struct ieee80211vap *vap, bool planned)
 	struct chanAccParams chp;
 	struct wmeParams wmeparr[WME_NUM_AC];
 	struct ieee80211_tx_queue_params txqp;
-	enum ieee80211_bss_changed changed;
+	enum ieee80211_bss_changed bss_changed;
 	int error;
 	uint16_t ac;
 
@@ -3704,11 +3751,11 @@ lkpi_wme_update(struct lkpi_hw *lhw, struct ieee80211vap *vap, bool planned)
 			ic_printf(ic, "%s: conf_tx ac %u failed %d\n",
 			    __func__, ac, error);
 	}
-	changed = BSS_CHANGED_QOS;
+	bss_changed = BSS_CHANGED_QOS;
 	if (!planned)
-		lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, changed);
+		lkpi_bss_info_change(hw, vif, bss_changed);
 
-	return (changed);
+	return (bss_changed);
 }
 #endif
 
@@ -3774,7 +3821,7 @@ lkpi_iv_sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
 	 * locking, see if queue_work() is fast enough.
 	 */
 	bss_changed = lkpi_update_dtim_tsf(vif, ni, ni->ni_vap, __func__, __LINE__);
-	lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
+	lkpi_bss_info_change(hw, vif, bss_changed);
 }
 
 /*
@@ -3820,7 +3867,7 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
 	struct ieee80211vap *vap;
 	struct ieee80211_vif *vif;
 	struct ieee80211_tx_queue_params txqp;
-	enum ieee80211_bss_changed changed;
+	enum ieee80211_bss_changed bss_changed;
 	struct sysctl_oid *node;
 	size_t len;
 	int error, i;
@@ -3937,8 +3984,8 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
 	LKPI_80211_LHW_LVIF_UNLOCK(lhw);
 
 	/* Set bss_info. */
-	changed = 0;
-	lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, changed);
+	bss_changed = 0;
+	lkpi_bss_info_change(hw, vif, bss_changed);
 
 	/* Configure tx queues (conf_tx), default WME & send BSS_CHANGED_QOS. */
 	IMPROVE("Hardcoded values; to fix see 802.11-2016, 9.4.2.29 EDCA Parameter Set element");
@@ -3956,8 +4003,8 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
 			    __func__, ac, error);
 	}
 	wiphy_unlock(hw->wiphy);
-	changed = BSS_CHANGED_QOS;
-	lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, changed);
+	bss_changed = BSS_CHANGED_QOS;
+	lkpi_bss_info_change(hw, vif, bss_changed);
 
 	/* Force MC init. */
 	lkpi_update_mcast_filter(ic);
diff --git a/sys/compat/linuxkpi/common/src/linux_80211.h b/sys/compat/linuxkpi/common/src/linux_80211.h
index 3d25ab4a857b..8ae5c3d13d2d 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211.h
+++ b/sys/compat/linuxkpi/common/src/linux_80211.h
@@ -469,6 +469,10 @@ void lkpi_80211_mo_change_chanctx(struct ieee80211_hw *,
     struct ieee80211_chanctx_conf *, uint32_t);
 void lkpi_80211_mo_remove_chanctx(struct ieee80211_hw *,
     struct ieee80211_chanctx_conf *);
+void lkpi_80211_mo_vif_cfg_changed(struct ieee80211_hw *, struct ieee80211_vif *,
+    uint64_t, bool);
+void lkpi_80211_mo_link_info_changed(struct ieee80211_hw *, struct ieee80211_vif *,
+    struct ieee80211_bss_conf *, uint64_t, uint8_t, bool);
 void lkpi_80211_mo_bss_info_changed(struct ieee80211_hw *, struct ieee80211_vif *,
     struct ieee80211_bss_conf *, uint64_t);
 int lkpi_80211_mo_conf_tx(struct ieee80211_hw *, struct ieee80211_vif *,
diff --git a/sys/compat/linuxkpi/common/src/linux_80211_macops.c b/sys/compat/linuxkpi/common/src/linux_80211_macops.c
index 634cffddea9e..42067e36c953 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211_macops.c
+++ b/sys/compat/linuxkpi/common/src/linux_80211_macops.c
@@ -546,24 +546,80 @@ lkpi_80211_mo_remove_chanctx(struct ieee80211_hw *hw,
 }
 
 void
-lkpi_80211_mo_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-    struct ieee80211_bss_conf *conf, uint64_t changed)
+lkpi_80211_mo_vif_cfg_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+    uint64_t vif_cfg_bits, bool fallback)
+{
+	struct lkpi_hw *lhw;
+
+	might_sleep();
+	/* XXX-FINISH all callers for lockdep_assert_wiphy(hw->wiphy); */
+
+	lhw = HW_TO_LHW(hw);
+	if (lhw->ops->vif_cfg_changed == NULL &&
+	    lhw->ops->bss_info_changed == NULL)
+		return;
+
+	if (vif_cfg_bits == 0)
+		return;
+
+	LKPI_80211_TRACE_MO("hw %p vif %p vif_cfg_bits %#jx", hw, vif, (uintmax_t)vif_cfg_bits);
+	if (lhw->ops->link_info_changed != NULL)
+		lhw->ops->vif_cfg_changed(hw, vif, vif_cfg_bits);
+	else if (fallback)
+		lhw->ops->bss_info_changed(hw, vif, &vif->bss_conf, vif_cfg_bits);
+}
+
+void
+lkpi_80211_mo_link_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+    struct ieee80211_bss_conf *conf, uint64_t link_info_bits, uint8_t link_id,
+    bool fallback)
 {
 	struct lkpi_hw *lhw;
 
+	might_sleep();
+	/* XXX-FINISH all callers for lockdep_assert_wiphy(hw->wiphy); */
+
 	lhw = HW_TO_LHW(hw);
 	if (lhw->ops->link_info_changed == NULL &&
 	    lhw->ops->bss_info_changed == NULL)
 		return;
 
-	if (changed == 0)
+	if (link_info_bits == 0)
 		return;
 
-	LKPI_80211_TRACE_MO("hw %p vif %p conf %p changed %#jx", hw, vif, conf, (uintmax_t)changed);
+	if (!ieee80211_vif_link_active(vif, link_id))
+		return;
+
+	LKPI_80211_TRACE_MO("hw %p vif %p conf %p link_info_bits %#jx", hw, vif, conf, (uintmax_t)link_info_bits);
 	if (lhw->ops->link_info_changed != NULL)
-		lhw->ops->link_info_changed(hw, vif, conf, changed);
-	else
-		lhw->ops->bss_info_changed(hw, vif, conf, changed);
+		lhw->ops->link_info_changed(hw, vif, conf, link_info_bits);
+	else if (fallback)
+		lhw->ops->bss_info_changed(hw, vif, conf, link_info_bits);
+}
+
+/*
+ * This is basically obsolete but one caller.
+ * The functionality is now split between lkpi_80211_mo_link_info_changed() and
+ * lkpi_80211_mo_vif_cfg_changed().  Those functions have a flag whether to call
+ * the (*bss_info_changed) fallback or not.  See lkpi_bss_info_change().
+ */
+void
+lkpi_80211_mo_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+    struct ieee80211_bss_conf *conf, uint64_t bss_changed)
+{
+	struct lkpi_hw *lhw;
+
+	/* XXX-FINISH all callers for lockdep_assert_wiphy(hw->wiphy); */
+
+	lhw = HW_TO_LHW(hw);
+	if (lhw->ops->bss_info_changed == NULL)
+		return;
+
+	if (bss_changed == 0)
+		return;
+
+	LKPI_80211_TRACE_MO("hw %p vif %p conf %p changed %#jx", hw, vif, conf, (uintmax_t)bss_changed);
+	lhw->ops->bss_info_changed(hw, vif, conf, bss_changed);
 }
 
 int