| From: Felix Fietkau <nbd@nbd.name> |
| Date: Mon, 20 Jun 2022 21:26:34 +0200 |
| Subject: [PATCH] mac80211: add a per-PHY AQL limit to improve fairness |
| |
| In order to maintain fairness, the amount of queueing needs to be limited |
| beyond the simple per-station AQL budget, otherwise the driver can simply |
| repeatedly do scheduling rounds until all queues that have not used their |
| AQL budget become eligble. |
| |
| To be conservative, use the high AQL limit for the first txq and add half |
| of the low AQL for each subsequent queue. |
| |
| Signed-off-by: Felix Fietkau <nbd@nbd.name> |
| --- |
| |
| --- a/net/mac80211/ieee80211_i.h |
| +++ b/net/mac80211/ieee80211_i.h |
| @@ -1215,6 +1215,7 @@ struct ieee80211_local { |
| u32 aql_txq_limit_high[IEEE80211_NUM_ACS]; |
| u32 aql_threshold; |
| atomic_t aql_total_pending_airtime; |
| + atomic_t aql_ac_pending_airtime[IEEE80211_NUM_ACS]; |
| |
| const struct ieee80211_ops *ops; |
| |
| --- a/net/mac80211/main.c |
| +++ b/net/mac80211/main.c |
| @@ -712,6 +712,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_ |
| local->aql_txq_limit_low[i] = IEEE80211_DEFAULT_AQL_TXQ_LIMIT_L; |
| local->aql_txq_limit_high[i] = |
| IEEE80211_DEFAULT_AQL_TXQ_LIMIT_H; |
| + atomic_set(&local->aql_ac_pending_airtime[i], 0); |
| } |
| |
| local->airtime_flags = AIRTIME_USE_TX | AIRTIME_USE_RX; |
| --- a/net/mac80211/sta_info.c |
| +++ b/net/mac80211/sta_info.c |
| @@ -1929,6 +1929,7 @@ void ieee80211_sta_update_pending_airtim |
| &sta->airtime[ac].aql_tx_pending); |
| |
| atomic_add(tx_airtime, &local->aql_total_pending_airtime); |
| + atomic_add(tx_airtime, &local->aql_ac_pending_airtime[ac]); |
| return; |
| } |
| |
| @@ -1940,14 +1941,17 @@ void ieee80211_sta_update_pending_airtim |
| tx_pending, 0); |
| } |
| |
| + atomic_sub(tx_airtime, &local->aql_total_pending_airtime); |
| tx_pending = atomic_sub_return(tx_airtime, |
| - &local->aql_total_pending_airtime); |
| + &local->aql_ac_pending_airtime[ac]); |
| if (WARN_ONCE(tx_pending < 0, |
| "Device %s AC %d pending airtime underflow: %u, %u", |
| wiphy_name(local->hw.wiphy), ac, tx_pending, |
| - tx_airtime)) |
| - atomic_cmpxchg(&local->aql_total_pending_airtime, |
| + tx_airtime)) { |
| + atomic_cmpxchg(&local->aql_ac_pending_airtime[ac], |
| tx_pending, 0); |
| + atomic_sub(tx_pending, &local->aql_total_pending_airtime); |
| + } |
| } |
| |
| int sta_info_move_state(struct sta_info *sta, |
| --- a/net/mac80211/tx.c |
| +++ b/net/mac80211/tx.c |
| @@ -3863,6 +3863,9 @@ struct ieee80211_txq *ieee80211_next_txq |
| |
| spin_lock_bh(&local->active_txq_lock[ac]); |
| |
| + if (!local->schedule_round[ac]) |
| + goto out; |
| + |
| begin: |
| txqi = list_first_entry_or_null(&local->active_txqs[ac], |
| struct txq_info, |
| @@ -3984,6 +3987,25 @@ bool ieee80211_txq_airtime_check(struct |
| } |
| EXPORT_SYMBOL(ieee80211_txq_airtime_check); |
| |
| +static bool |
| +ieee80211_txq_schedule_airtime_check(struct ieee80211_local *local, u8 ac) |
| +{ |
| + unsigned int num_txq = 0; |
| + struct txq_info *txq; |
| + u32 aql_limit; |
| + |
| + if (!wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) |
| + return true; |
| + |
| + list_for_each_entry(txq, &local->active_txqs[ac], schedule_order) |
| + num_txq++; |
| + |
| + aql_limit = (num_txq - 1) * local->aql_txq_limit_low[ac] / 2 + |
| + local->aql_txq_limit_high[ac]; |
| + |
| + return atomic_read(&local->aql_ac_pending_airtime[ac]) < aql_limit; |
| +} |
| + |
| bool ieee80211_txq_may_transmit(struct ieee80211_hw *hw, |
| struct ieee80211_txq *txq) |
| { |
| @@ -4000,6 +4022,9 @@ bool ieee80211_txq_may_transmit(struct i |
| if (list_empty(&txqi->schedule_order)) |
| goto out; |
| |
| + if (!ieee80211_txq_schedule_airtime_check(local, ac)) |
| + goto out; |
| + |
| list_for_each_entry_safe(iter, tmp, &local->active_txqs[ac], |
| schedule_order) { |
| if (iter == txqi) |
| @@ -4039,7 +4064,15 @@ void ieee80211_txq_schedule_start(struct |
| struct ieee80211_local *local = hw_to_local(hw); |
| |
| spin_lock_bh(&local->active_txq_lock[ac]); |
| - local->schedule_round[ac]++; |
| + |
| + if (ieee80211_txq_schedule_airtime_check(local, ac)) { |
| + local->schedule_round[ac]++; |
| + if (!local->schedule_round[ac]) |
| + local->schedule_round[ac]++; |
| + } else { |
| + local->schedule_round[ac] = 0; |
| + } |
| + |
| spin_unlock_bh(&local->active_txq_lock[ac]); |
| } |
| EXPORT_SYMBOL(ieee80211_txq_schedule_start); |