[PATCH 3/4] wifi: mac80211: notify driver on airtime weight changes

From: Andrei-Alexandru Bleortu

Date: Sat Oct 03 2026 - 11:59:02 EST


From: Felix Fietkau <nbd@xxxxxxxx>

Add a new sta_set_airtime_weight driver op so drivers can program the
airtime fairness weight into hardware. The op is called when the weight
is updated via sta_apply_parameters, and on station upload if a
non-default weight is already set.

Signed-off-by: Felix Fietkau <nbd@xxxxxxxx>

Signed-off-by: Andrei-Alexandru Bleortu <me@xxxxxxxxxxxx>
---
include/net/mac80211.h | 5 +++++
net/mac80211/cfg.c | 6 +++++-
net/mac80211/driver-ops.c | 4 ++++
net/mac80211/driver-ops.h | 19 +++++++++++++++++++
net/mac80211/sta_info.c | 7 ++++++-
net/mac80211/trace.h | 27 +++++++++++++++++++++++++++
6 files changed, 66 insertions(+), 2 deletions(-)

diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index cb9f8e14b..aab98d92e 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -4694,6 +4694,8 @@ struct ieee80211_prep_tx_info {
* @set_sar_specs: Update the SAR (TX power) settings.
* @sta_set_decap_offload: Called to notify the driver when a station is allowed
* to use rx decapsulation offload
+ * @sta_set_airtime_weight: Called to notify the driver of a change to a
+ * station's airtime fairness weight, so it can be programmed in hardware.
* @add_twt_setup: Update hw with TWT agreement parameters received from the peer.
* This callback allows the hw to check if requested parameters
* are supported and if there is enough room for a new agreement.
@@ -5108,6 +5110,9 @@ struct ieee80211_ops {
void (*sta_set_decap_offload)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta, bool enabled);
+ void (*sta_set_airtime_weight)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, u16 weight);
void (*add_twt_setup)(struct ieee80211_hw *hw,
struct ieee80211_sta *sta,
struct ieee80211_twt_setup *twt);
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index bd1a857c8..28f99a8f8 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -2616,8 +2616,12 @@ static int sta_apply_parameters(struct ieee80211_local *local,
if (ieee80211_vif_is_mesh(&sdata->vif))
sta_apply_mesh_params(local, sta, params);

- if (params->airtime_weight)
+ if (params->airtime_weight) {
sta->airtime_weight = params->airtime_weight;
+ if (sta->uploaded)
+ drv_sta_set_airtime_weight(local, sdata, &sta->sta,
+ sta->airtime_weight);
+ }

if (params->nmi_mac) {
struct ieee80211_sub_if_data *nmi =
diff --git a/net/mac80211/driver-ops.c b/net/mac80211/driver-ops.c
index 49753b73a..083c26517 100644
--- a/net/mac80211/driver-ops.c
+++ b/net/mac80211/driver-ops.c
@@ -157,6 +157,10 @@ int drv_sta_state(struct ieee80211_local *local,
sta->uploaded = true;
if (rcu_access_pointer(sta->sta.rates))
drv_sta_rate_tbl_update(local, sdata, &sta->sta);
+ if (sta->airtime_weight != IEEE80211_DEFAULT_AIRTIME_WEIGHT)
+ drv_sta_set_airtime_weight(local, sdata,
+ &sta->sta,
+ sta->airtime_weight);
}
} else if (old_state == IEEE80211_STA_ASSOC &&
new_state == IEEE80211_STA_AUTH) {
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index f1c0b87fd..bd5c9d1ed 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -1633,6 +1633,25 @@ static inline void drv_sta_set_decap_offload(struct ieee80211_local *local,
trace_drv_return_void(local);
}

+static inline void drv_sta_set_airtime_weight(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta,
+ u16 weight)
+{
+ sdata = get_bss_sdata(sdata);
+
+ might_sleep();
+ lockdep_assert_wiphy(local->hw.wiphy);
+ if (!check_sdata_in_driver(sdata))
+ return;
+
+ trace_drv_sta_set_airtime_weight(local, sdata, sta, weight);
+ if (local->ops->sta_set_airtime_weight)
+ local->ops->sta_set_airtime_weight(&local->hw, &sdata->vif, sta,
+ weight);
+ trace_drv_return_void(local);
+}
+
static inline void drv_add_twt_setup(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_sta *sta,
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index e81cd1551..2ce3ef905 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -872,8 +872,13 @@ static int sta_info_insert_drv_state(struct ieee80211_local *local,
* Drivers using legacy sta_add/sta_remove callbacks only
* get uploaded set to true after sta_add is called.
*/
- if (!local->ops->sta_add)
+ if (!local->ops->sta_add) {
sta->uploaded = true;
+ if (sta->airtime_weight != IEEE80211_DEFAULT_AIRTIME_WEIGHT)
+ drv_sta_set_airtime_weight(local, sdata,
+ &sta->sta,
+ sta->airtime_weight);
+ }
return 0;
}

diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index 562a4964a..3ae7e78de 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -2462,6 +2462,33 @@ DEFINE_EVENT(sta_flag_evt, drv_sta_set_decap_offload,
TP_ARGS(local, sdata, sta, enabled)
);

+TRACE_EVENT(drv_sta_set_airtime_weight,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta, u16 weight),
+
+ TP_ARGS(local, sdata, sta, weight),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ STA_ENTRY
+ __field(u16, weight)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ STA_ASSIGN;
+ __entry->weight = weight;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " weight:%u",
+ LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->weight
+ )
+);
+
TRACE_EVENT(drv_add_twt_setup,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sta *sta,