git: 38075f7d5c87 - main - net80211: remove direct use of ni->ni_txrate, add indirection methods

From: Adrian Chadd <adrian_at_FreeBSD.org>
Date: Wed, 26 Feb 2025 19:31:34 UTC
The branch main has been updated by adrian:

URL: https://cgit.FreeBSD.org/src/commit/?id=38075f7d5c87176f7b317c368712444a2f450a5e

commit 38075f7d5c87176f7b317c368712444a2f450a5e
Author:     Adrian Chadd <adrian@FreeBSD.org>
AuthorDate: 2025-01-02 03:52:25 +0000
Commit:     Adrian Chadd <adrian@FreeBSD.org>
CommitDate: 2025-02-26 19:29:09 +0000

    net80211: remove direct use of ni->ni_txrate, add indirection methods
    
    The summary:
    
    * Refactor ni_txrate access into ieee80211_node_get_txrate_dot11rate()
      and ieee80211_node_set_txrate_dot11rate(). These wrap the ni->ni_txrate
      access and will eventually be able to do runtime sanity checks and
      fallback where necessary.
    
    * Refactor ieee80211_node_get_txrate_kbit() from the ioctl code which
      sets isi_txmbps (which is in 0.5Mbit/s units.)  This new routine
      returns the TX rate in kbit/s units.
    
    * Also use ieee80211_node_get_txrate_kbit() in various places in the
      code where the dot11rate was turned into a Mbit/sec value, which was
      very wrong for HT (but also only used for logging, so it didn't
      have an effect on normal runtime.)
    
    * Mb -> Mbit/s
    
    The long version:
    
    The current ni->ni_txrate value is what net80211's phy code
    calls a 'dot11rate'.  Inside the ieee80211_phy.c tables you'll
    find a bunch of tables which represent:
    
    * for legacy rates its in 1/2 mbit units.
    * for turbo (Atheros 40MHz OFDM) it's the non-turbo rates, but the
      turbo rate speed in kbit/sec.
    * for 802.11n rates its the MCS, starting at 0x80.
    
    However there are a couple of catches with this:
    
    * Basic rates are represented in the pre-11n rates using the high bit
      (IEEE80211_RATE_BASIC)
    * 11n rates are also represented using the high bit (IEEE80211_RATE_MCS)
    
    Now, ni->ni_txrate will clear the IEEE80211_RATE_BASIC flag before
    storing it, so if the high bit exists it must be an 802.11n rate.
    However, there's still a bunch of code everywhere that purposefully
    filters that out.
    
    The goals of this commit:
    
    * Provide an easy API to migrate existing drivers and other consumers
      to - ieee80211_node_get_txrate_dot11rate() is defined as "will return
      the normal legacy or HT rate" so all the existing code can work.
    * Lay the ground work for extending ni_txrate (and a rate representation
      in general) that can represent legacy, HT, VHT, EHT, HE, etc rates.
    * Create a central place where ni_txrate is updated from rate control,
      drivers that will update ni_txrate itself, and consumers,
      so we can provide some basic runtime checks / logging as VHT, EHT, HE,
      etc rates are eventually added.
    
    For example, a VHT driver will eventually receive VHT rates, but an
    existing HT driver will not, so the API should log and return a
    sensible default when something like a VHT rate shows up on a HT only
    device.
    
    The rate control code currently returns a rix, and sets ni_txrate to the
    dot11rate.  Drivers can choose either.  However, choosing the rix is
    risky because you need to know if it's the ni_rates or ni_htrates, which
    requires a lot of duplicate work that lines up consistently at all
    layers (see the AMRR code for an example.)
    
    Differential Revision: https://reviews.freebsd.org/D48601
    Reviewed by:    bz, thj
---
 sys/net80211/ieee80211.c              |  2 +-
 sys/net80211/ieee80211_adhoc.c        |  5 ++-
 sys/net80211/ieee80211_amrr.c         | 14 +++++---
 sys/net80211/ieee80211_hostap.c       |  7 ++--
 sys/net80211/ieee80211_ioctl.c        | 20 ++---------
 sys/net80211/ieee80211_mesh.c         |  2 +-
 sys/net80211/ieee80211_node.c         | 65 +++++++++++++++++++++++++++++++++++
 sys/net80211/ieee80211_node.h         |  6 ++++
 sys/net80211/ieee80211_ratectl_none.c |  6 ++--
 sys/net80211/ieee80211_rssadapt.c     | 16 +++++----
 sys/net80211/ieee80211_scan_sta.c     |  2 +-
 sys/net80211/ieee80211_sta.c          |  7 ++--
 sys/net80211/ieee80211_superg.c       | 10 +++---
 13 files changed, 112 insertions(+), 50 deletions(-)

diff --git a/sys/net80211/ieee80211.c b/sys/net80211/ieee80211.c
index 49d313e5077d..eb796462d3d1 100644
--- a/sys/net80211/ieee80211.c
+++ b/sys/net80211/ieee80211.c
@@ -2306,7 +2306,7 @@ ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr)
 		 * In station mode report the current transmit rate.
 		 */
 		imr->ifm_active |= ieee80211_rate2media(ic,
-			vap->iv_bss->ni_txrate, mode);
+		    ieee80211_node_get_txrate_dot11rate(vap->iv_bss), mode);
 	} else
 		imr->ifm_active |= IFM_AUTO;
 	if (imr->ifm_status & IFM_ACTIVE)
diff --git a/sys/net80211/ieee80211_adhoc.c b/sys/net80211/ieee80211_adhoc.c
index d252b75899a2..77e5a2d99904 100644
--- a/sys/net80211/ieee80211_adhoc.c
+++ b/sys/net80211/ieee80211_adhoc.c
@@ -235,10 +235,9 @@ adhoc_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
 				    ether_sprintf(ni->ni_bssid));
 				ieee80211_print_essid(vap->iv_bss->ni_essid,
 				    ni->ni_esslen);
-				/* XXX MCS/HT */
-				printf(" channel %d start %uMb\n",
+				printf(" channel %d start %uMbit/s\n",
 				    ieee80211_chan2ieee(ic, ic->ic_curchan),
-				    IEEE80211_RATE2MBS(ni->ni_txrate));
+				    ieee80211_node_get_txrate_kbit(ni) / 1000);
 			}
 #endif
 			break;
diff --git a/sys/net80211/ieee80211_amrr.c b/sys/net80211/ieee80211_amrr.c
index 386a3de92a74..d366bf71e367 100644
--- a/sys/net80211/ieee80211_amrr.c
+++ b/sys/net80211/ieee80211_amrr.c
@@ -207,7 +207,7 @@ amrr_node_init(struct ieee80211_node *ni)
 		rate |= IEEE80211_RATE_MCS;
 
 	/* Assign initial rate from the rateset */
-	ni->ni_txrate = rate;
+	ieee80211_node_set_txrate_dot11rate(ni, rate);
 	amn->amn_ticks = ticks;
 
 	/* XXX TODO: we really need a rate-to-string method */
@@ -321,7 +321,8 @@ amrr_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg __unused)
 	/* XXX should return -1 here, but drivers may not expect this... */
 	if (!amn)
 	{
-		ni->ni_txrate = ni->ni_rates.rs_rates[0];
+		ieee80211_node_set_txrate_dot11rate(ni,
+		    ni->ni_rates.rs_rates[0]);
 		return 0;
 	}
 
@@ -338,13 +339,16 @@ amrr_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg __unused)
 	if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval) {
 		rix = amrr_update(amrr, amn, ni);
 		if (rix != amn->amn_rix) {
+			uint8_t dot11Rate;
 			/* update public rate */
-			ni->ni_txrate = rs->rs_rates[rix];
+			dot11Rate = rs->rs_rates[rix];
 			/* XXX strip basic rate flag from txrate, if non-11n */
 			if (ieee80211_ht_check_tx_ht(ni))
-				ni->ni_txrate |= IEEE80211_RATE_MCS;
+				dot11Rate |= IEEE80211_RATE_MCS;
 			else
-				ni->ni_txrate &= IEEE80211_RATE_VAL;
+				dot11Rate &= IEEE80211_RATE_VAL;
+			ieee80211_node_set_txrate_dot11rate(ni, dot11Rate);
+
 			amn->amn_rix = rix;
 		}
 		amn->amn_ticks = ticks;
diff --git a/sys/net80211/ieee80211_hostap.c b/sys/net80211/ieee80211_hostap.c
index 1dce9a6b5923..76c419f5bdf8 100644
--- a/sys/net80211/ieee80211_hostap.c
+++ b/sys/net80211/ieee80211_hostap.c
@@ -63,8 +63,6 @@
 #include <net80211/ieee80211_vht.h>
 #include <net80211/ieee80211_sta.h> /* for parse_wmeie */
 
-#define	IEEE80211_RATE2MBS(r)	(((r) & IEEE80211_RATE_VAL) / 2)
-
 static	void hostap_vattach(struct ieee80211vap *);
 static	int hostap_newstate(struct ieee80211vap *, enum ieee80211_state, int);
 static	int hostap_input(struct ieee80211_node *ni, struct mbuf *m,
@@ -311,10 +309,9 @@ hostap_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
 				    ether_sprintf(ni->ni_bssid));
 				ieee80211_print_essid(ni->ni_essid,
 				    ni->ni_esslen);
-				/* XXX MCS/HT */
-				printf(" channel %d start %uMb\n",
+				printf(" channel %d start %uMbit/s\n",
 				    ieee80211_chan2ieee(ic, ic->ic_curchan),
-				    IEEE80211_RATE2MBS(ni->ni_txrate));
+				    ieee80211_node_get_txrate_kbit(ni) / 1000);
 			}
 #endif
 			break;
diff --git a/sys/net80211/ieee80211_ioctl.c b/sys/net80211/ieee80211_ioctl.c
index 3b57e7d8cd8e..24ec9659dc2d 100644
--- a/sys/net80211/ieee80211_ioctl.c
+++ b/sys/net80211/ieee80211_ioctl.c
@@ -406,23 +406,9 @@ get_sta_info(void *arg, struct ieee80211_node *ni)
 	if (si->isi_nrates > 15)
 		si->isi_nrates = 15;
 	memcpy(si->isi_rates, ni->ni_rates.rs_rates, si->isi_nrates);
-	si->isi_txrate = ni->ni_txrate;
-	if (si->isi_txrate & IEEE80211_RATE_MCS) {
-		const struct ieee80211_mcs_rates *mcs =
-		    &ieee80211_htrates[ni->ni_txrate &~ IEEE80211_RATE_MCS];
-		if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) {
-			if (ni->ni_flags & IEEE80211_NODE_SGI40)
-				si->isi_txmbps = mcs->ht40_rate_800ns;
-			else
-				si->isi_txmbps = mcs->ht40_rate_400ns;
-		} else {
-			if (ni->ni_flags & IEEE80211_NODE_SGI20)
-				si->isi_txmbps = mcs->ht20_rate_800ns;
-			else
-				si->isi_txmbps = mcs->ht20_rate_400ns;
-		}
-	} else
-		si->isi_txmbps = si->isi_txrate;
+	si->isi_txrate = ieee80211_node_get_txrate_dot11rate(ni);
+	/* Note: txmbps is in 1/2Mbit/s units */
+	si->isi_txmbps = ieee80211_node_get_txrate_kbit(ni) / 500;
 	si->isi_associd = ni->ni_associd;
 	si->isi_txpower = ni->ni_txpower;
 	si->isi_vlan = ni->ni_vlan;
diff --git a/sys/net80211/ieee80211_mesh.c b/sys/net80211/ieee80211_mesh.c
index c52122ebeb13..9f81bc3643aa 100644
--- a/sys/net80211/ieee80211_mesh.c
+++ b/sys/net80211/ieee80211_mesh.c
@@ -3298,7 +3298,7 @@ mesh_airtime_calc(struct ieee80211_node *ni)
 	uint64_t res;
 
 	/* Time to transmit a frame */
-	rate = ni->ni_txrate;
+	rate = ieee80211_node_get_txrate_dot11rate(ni);
 	overhead = ieee80211_compute_duration(ic->ic_rt,
 	    ifp->if_mtu + IEEE80211_MESH_MAXOVERHEAD, rate, 0) << M_BITS;
 	/* Error rate in percentage */
diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c
index f2fc3d21a361..f5b05ed769c9 100644
--- a/sys/net80211/ieee80211_node.c
+++ b/sys/net80211/ieee80211_node.c
@@ -3137,3 +3137,68 @@ ieee80211_getsignal(struct ieee80211vap *vap, int8_t *rssi, int8_t *noise)
 	if (vap->iv_opmode != IEEE80211_M_STA)
 		*rssi = ieee80211_getrssi(vap);
 }
+
+uint8_t
+ieee80211_node_get_txrate_dot11rate(struct ieee80211_node *ni)
+{
+	return (ni->ni_txrate);
+}
+
+void
+ieee80211_node_set_txrate_dot11rate(struct ieee80211_node *ni,
+    uint8_t dot11Rate)
+{
+	ni->ni_txrate = dot11Rate;
+}
+
+void
+ieee80211_node_set_txrate_ht_mcsrate(struct ieee80211_node *ni,
+    uint8_t mcs)
+{
+	KASSERT(mcs <= 76, ("%s: MCS is not 0..76 (%d)", __func__, mcs));
+	if (mcs > 76) {
+		ic_printf(ni->ni_ic, "%s: invalid MCS (%d)\n", __func__, mcs);
+		return;
+	}
+	ni->ni_txrate = IEEE80211_RATE_MCS | mcs;
+}
+
+
+/*
+ * @brief Fetch the transmit rate for the given node in kbit/s.
+ *
+ * This currently only works for CCK, OFDM and HT rates.
+ *
+ * @param ni	struct ieee80211_node * to lookup
+ * @returns	current transmit rate in kbit/s
+ */
+uint32_t
+ieee80211_node_get_txrate_kbit(struct ieee80211_node *ni)
+{
+	uint32_t mbps;
+
+	if (ni->ni_txrate & IEEE80211_RATE_MCS) {
+		const struct ieee80211_mcs_rates *mcs =
+		    &ieee80211_htrates[ni->ni_txrate & ~IEEE80211_RATE_MCS];
+
+		if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) {
+			/* Note: these are in 1/2Mbit/s units */
+			if (ni->ni_flags & IEEE80211_NODE_SGI40)
+				mbps = mcs->ht40_rate_800ns;
+			else
+				mbps = mcs->ht40_rate_400ns;
+		} else {
+			if (ni->ni_flags & IEEE80211_NODE_SGI20)
+				mbps = mcs->ht20_rate_800ns;
+			else
+				mbps = mcs->ht20_rate_400ns;
+		}
+	} else
+		/* Note: CCK/OFDM dot11rate entries are in 1/2Mbit/s units */
+		mbps = ni->ni_txrate;
+
+	/*
+	 * Note; 'mbps' in 1/2 Mbit/s units so *500 to make it in kbit/s units.
+	 */
+	return (mbps * 500);
+}
diff --git a/sys/net80211/ieee80211_node.h b/sys/net80211/ieee80211_node.h
index 0039c743544c..722f3d54a00b 100644
--- a/sys/net80211/ieee80211_node.h
+++ b/sys/net80211/ieee80211_node.h
@@ -498,4 +498,10 @@ void	ieee80211_node_join(struct ieee80211_node *,int);
 void	ieee80211_node_leave(struct ieee80211_node *);
 int8_t	ieee80211_getrssi(struct ieee80211vap *);
 void	ieee80211_getsignal(struct ieee80211vap *, int8_t *, int8_t *);
+
+uint8_t	ieee80211_node_get_txrate_dot11rate(struct ieee80211_node *);
+void	ieee80211_node_set_txrate_dot11rate(struct ieee80211_node *, uint8_t);
+void	ieee80211_node_set_txrate_ht_mcsrate(struct ieee80211_node *, uint8_t);
+uint32_t	ieee80211_node_get_txrate_kbit(struct ieee80211_node *);
+
 #endif /* _NET80211_IEEE80211_NODE_H_ */
diff --git a/sys/net80211/ieee80211_ratectl_none.c b/sys/net80211/ieee80211_ratectl_none.c
index 4ab122fbab92..47f88cbd3a79 100644
--- a/sys/net80211/ieee80211_ratectl_none.c
+++ b/sys/net80211/ieee80211_ratectl_none.c
@@ -62,7 +62,8 @@ none_deinit(struct ieee80211vap *vap)
 static void
 none_node_init(struct ieee80211_node *ni)
 {
-	ni->ni_txrate = ni->ni_rates.rs_rates[0] & IEEE80211_RATE_VAL;
+	ieee80211_node_set_txrate_dot11rate(ni,
+	    ni->ni_rates.rs_rates[0] & IEEE80211_RATE_VAL);
 }
 
 static void
@@ -75,7 +76,8 @@ none_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg __unused)
 {
 	int rix = 0;
 
-	ni->ni_txrate = ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL;
+	ieee80211_node_set_txrate_dot11rate(ni,
+	    ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL);
 	return rix;
 }
 
diff --git a/sys/net80211/ieee80211_rssadapt.c b/sys/net80211/ieee80211_rssadapt.c
index 6c77727d7526..43118aad53c3 100644
--- a/sys/net80211/ieee80211_rssadapt.c
+++ b/sys/net80211/ieee80211_rssadapt.c
@@ -203,11 +203,13 @@ rssadapt_node_init(struct ieee80211_node *ni)
 	     ra->ra_rix > 0 && (rs->rs_rates[ra->ra_rix] & IEEE80211_RATE_VAL) > 72;
 	     ra->ra_rix--)
 		;
-	ni->ni_txrate = rs->rs_rates[ra->ra_rix] & IEEE80211_RATE_VAL;
+	ieee80211_node_set_txrate_dot11rate(ni,
+	    rs->rs_rates[ra->ra_rix] & IEEE80211_RATE_VAL);
 	ra->ra_ticks = ticks;
 
 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
-	    "RSSADAPT initial rate %d", ni->ni_txrate);
+	    "RSSADAPT initial rate %d Mbit/s",
+	    ieee80211_node_get_txrate_kbit(ni) / 1000);
 }
 
 static void
@@ -244,7 +246,8 @@ rssadapt_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg)
 	/* XXX should return -1 here, but drivers may not expect this... */
 	if (!ra)
 	{
-		ni->ni_txrate = ni->ni_rates.rs_rates[0];
+		ieee80211_node_set_txrate_dot11rate(ni,
+		    ni->ni_rates.rs_rates[0]);
 		return 0;
 	}
 
@@ -263,12 +266,13 @@ rssadapt_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg)
 			break;
 	if (rix != ra->ra_rix) {
 		/* update public rate */
-		ni->ni_txrate = ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL;
+		ieee80211_node_set_txrate_dot11rate(ni,
+		    ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL);
 		ra->ra_rix = rix;
 
 		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
-		    "RSSADAPT new rate %d (pktlen %d rssi %d)",
-		    ni->ni_txrate, pktlen, rssi);
+		    "RSSADAPT new rate %d Mbit/s (pktlen %d rssi %d)",
+		    ieee80211_node_get_txrate_kbit(ni) / 1000, pktlen, rssi);
 	}
 	return rix;
 }
diff --git a/sys/net80211/ieee80211_scan_sta.c b/sys/net80211/ieee80211_scan_sta.c
index a8159fca6d80..69347a5d99cf 100644
--- a/sys/net80211/ieee80211_scan_sta.c
+++ b/sys/net80211/ieee80211_scan_sta.c
@@ -1365,7 +1365,7 @@ sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
 	/* NB: the most up to date rssi is in the node, not the scan cache */
 	curRssi = ic->ic_node_getrssi(ni);
 	if (ucastRate == IEEE80211_FIXED_RATE_NONE) {
-		curRate = ni->ni_txrate;
+		curRate = ieee80211_node_get_txrate_dot11rate(ni);
 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_ROAM,
 		    "%s: currssi %d currate %u roamrssi %d roamrate %u\n",
 		    __func__, curRssi, curRate, roamRssi, roamRate);
diff --git a/sys/net80211/ieee80211_sta.c b/sys/net80211/ieee80211_sta.c
index 9e4e1013fd2f..96603f339710 100644
--- a/sys/net80211/ieee80211_sta.c
+++ b/sys/net80211/ieee80211_sta.c
@@ -64,8 +64,6 @@
 #include <net80211/ieee80211_sta.h>
 #include <net80211/ieee80211_vht.h>
 
-#define	IEEE80211_RATE2MBS(r)	(((r) & IEEE80211_RATE_VAL) / 2)
-
 static	void sta_vattach(struct ieee80211vap *);
 static	void sta_beacon_miss(struct ieee80211vap *);
 static	int sta_newstate(struct ieee80211vap *, enum ieee80211_state, int);
@@ -417,10 +415,9 @@ sta_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
 				    ether_sprintf(ni->ni_bssid));
 				ieee80211_print_essid(vap->iv_bss->ni_essid,
 				    ni->ni_esslen);
-				/* XXX MCS/HT */
-				printf(" channel %d start %uMb\n",
+				printf(" channel %d start %uMbit/s\n",
 				    ieee80211_chan2ieee(ic, ic->ic_curchan),
-				    IEEE80211_RATE2MBS(ni->ni_txrate));
+				    ieee80211_node_get_txrate_kbit(ni) / 1000);
 			}
 #endif
 			ieee80211_scan_assoc_success(vap, ni->ni_macaddr);
diff --git a/sys/net80211/ieee80211_superg.c b/sys/net80211/ieee80211_superg.c
index 815fb356891c..ee3b70d5ed9f 100644
--- a/sys/net80211/ieee80211_superg.c
+++ b/sys/net80211/ieee80211_superg.c
@@ -725,6 +725,7 @@ ff_approx_txtime(struct ieee80211_node *ni,
 	struct ieee80211vap *vap = ni->ni_vap;
 	uint32_t framelen;
 	uint32_t frame_time;
+	uint8_t dot11rate;
 
 	/*
 	 * Approximate the frame length to be transmitted. A swag to add
@@ -746,15 +747,16 @@ ff_approx_txtime(struct ieee80211_node *ni,
 	 * For now, we assume non-shortgi, 20MHz, just because I want to
 	 * at least test 802.11n.
 	 */
-	if (ni->ni_txrate & IEEE80211_RATE_MCS)
+	dot11rate = ieee80211_node_get_txrate_dot11rate(ni);
+	if (dot11rate & IEEE80211_RATE_MCS)
 		frame_time = ieee80211_compute_duration_ht(framelen,
-		    ni->ni_txrate,
-		    IEEE80211_HT_RC_2_STREAMS(ni->ni_txrate),
+		    dot11rate,
+		    IEEE80211_HT_RC_2_STREAMS(dot11rate),
 		    0, /* isht40 */
 		    0); /* isshortgi */
 	else
 		frame_time = ieee80211_compute_duration(ic->ic_rt, framelen,
-			    ni->ni_txrate, 0);
+			    dot11rate, 0);
 	return (frame_time);
 }