[PATCH] wifi: mac80211: scale the airtime queue limit by the station's weight

Julius Bairaktaris <[email protected]>
Newsgroups org.kernel.vger.linux-wireless
Message-ID <[email protected]>
The airtime queue limit is the same for every station, so on a driver
that pushes a whole scheduling selection into the hardware, every station
is allowed the same airtime in flight and the deficit round robin can
only decide the order in which they reach that ceiling, not how much of
the medium each one gets. Setting NL80211_ATTR_AIRTIME_WEIGHT then has
no effect on the airtime a station receives.

Measured on an IPQ8074 access point with two stations on one radio, a 1x1
VHT80 client and a 2x2 HE160 client, both saturated from the access point
at once, BE aql_txq_limit at 500/1000 us, three interleaved runs per
weight with the association verified unchanged across every run. Without
this change the slow station takes 44.4, 43.9 and 44.0 per cent of the
medium at equal weights, 44.0, 43.5 and 44.4 at 1024:256, and 43.1, 43.4
and 42.8 at 256:1024. The weight moves the split by less than the spread
within one setting.

Scale the per-station limit by the station's weight. A station weighted
above the default is allowed proportionally more airtime in flight and
therefore takes proportionally more of the medium. A network that never
sets a weight is unaffected, since every station keeps the default.

The same pair then reads 42.1, 43.3 and 42.8 per cent at equal weights,
78.0, 78.9 and 77.2 at 1024:256, and 19.6, 18.3 and 20.0 at 256:1024: a
4:1 weight produces a 3.6:1 airtime ratio and 1:4 produces 4.2:1. It is
not free, and over the pair the aggregate is 557 Mbit/s at equal weights,
925 when the faster station is favoured and 337 when the slower one is.

The low limit is consulted without reference to the radio total, so the
scaled value is bounded by aql_threshold. Without that bound a weight of
65535 grants one station 1.3 seconds of standing airtime on the default
low limit, which no total accounts for. The bound reaches the high limit
as well, where it changes nothing: that check already requires the radio
total to sit below the same threshold.

The bound never reduces a limit below the value configured for it, so an
access point that raises aql_txq_limit above aql_threshold keeps what it
asked for at every weight, and scaling a weight up cannot scale a limit
down.

That bound is also the ceiling on what a weight can express. At the
default low limit of 5000 us it takes effect near a weight of 1229, so
ratios beyond roughly five to one do not resolve: a weight of 65535
against 256 asks for 256 to 1 and delivers 4.9 to 1.

This changes nothing where the limit does not bind. A station whose
pending airtime never approaches the limit is not held back by it at
either weight, mac80211 holds no backlog to arbitrate, and the share each
station gets is decided by the hardware's own scheduling.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Julius Bairaktaris <[email protected]>
---
The measurements were taken on ath11k with the series "wifi: ath11k:
airtime queue limits, fairness and a driver TXQ scheduler", posted
separately to linux-wireless.

 net/mac80211/tx.c | 25 +++++++++++++++++++++++--
 1 file changed, 23 insertions(+), 2 deletions(-)

diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 3a1e2c9e1565..11350133d637 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -4223,6 +4223,25 @@ EXPORT_SYMBOL(__ieee80211_schedule_txq);
 
 DEFINE_STATIC_KEY_FALSE(aql_disable);
 
+/* The airtime a station may keep in flight scales with its weight. The low
+ * limit is consulted unconditionally, so the scaled value is bounded by the
+ * radio-wide threshold rather than by the weight alone, and never below the
+ * limit that was configured.
+ */
+static u32 ieee80211_aql_sta_limit(struct ieee80211_local *local,
+				   struct sta_info *sta, u32 limit)
+{
+	u32 scaled;
+
+	if (sta->airtime_weight == IEEE80211_DEFAULT_AIRTIME_WEIGHT)
+		return limit;
+
+	scaled = mult_frac(limit, sta->airtime_weight,
+			   IEEE80211_DEFAULT_AIRTIME_WEIGHT);
+
+	return min(scaled, max(limit, local->aql_threshold));
+}
+
 bool ieee80211_txq_airtime_check(struct ieee80211_hw *hw,
 				 struct ieee80211_txq *txq)
 {
@@ -4244,13 +4263,15 @@ bool ieee80211_txq_airtime_check(struct ieee80211_hw *hw,
 
 	sta = container_of(txq->sta, struct sta_info, sta);
 	if (atomic_read(&sta->airtime[txq->ac].aql_tx_pending) <
-	    sta->airtime[txq->ac].aql_limit_low)
+	    ieee80211_aql_sta_limit(local, sta,
+				    sta->airtime[txq->ac].aql_limit_low))
 		return true;
 
 	if (atomic_read(&local->aql_total_pending_airtime) <
 	    local->aql_threshold &&
 	    atomic_read(&sta->airtime[txq->ac].aql_tx_pending) <
-	    sta->airtime[txq->ac].aql_limit_high)
+	    ieee80211_aql_sta_limit(local, sta,
+				    sta->airtime[txq->ac].aql_limit_high))
 		return true;
 
 	return false;
-- 
2.53.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.