developer | 1de5e6c | 2022-06-27 11:23:25 +0800 | [diff] [blame] | 1 | From: Felix Fietkau <nbd@nbd.name> |
| 2 | Date: Mon, 20 Jun 2022 21:26:34 +0200 |
| 3 | Subject: [PATCH] mac80211: add a per-PHY AQL limit to improve fairness |
| 4 | |
| 5 | In order to maintain fairness, the amount of queueing needs to be limited |
| 6 | beyond the simple per-station AQL budget, otherwise the driver can simply |
| 7 | repeatedly do scheduling rounds until all queues that have not used their |
| 8 | AQL budget become eligble. |
| 9 | |
| 10 | To be conservative, use the high AQL limit for the first txq and add half |
| 11 | of the low AQL for each subsequent queue. |
| 12 | |
| 13 | Signed-off-by: Felix Fietkau <nbd@nbd.name> |
| 14 | --- |
| 15 | |
| 16 | --- a/net/mac80211/ieee80211_i.h |
| 17 | +++ b/net/mac80211/ieee80211_i.h |
developer | e126222 | 2022-10-25 12:20:54 +0800 | [diff] [blame^] | 18 | @@ -1215,6 +1215,7 @@ struct ieee80211_local { |
developer | 1de5e6c | 2022-06-27 11:23:25 +0800 | [diff] [blame] | 19 | u32 aql_txq_limit_high[IEEE80211_NUM_ACS]; |
| 20 | u32 aql_threshold; |
| 21 | atomic_t aql_total_pending_airtime; |
| 22 | + atomic_t aql_ac_pending_airtime[IEEE80211_NUM_ACS]; |
| 23 | |
| 24 | const struct ieee80211_ops *ops; |
| 25 | |
| 26 | --- a/net/mac80211/main.c |
| 27 | +++ b/net/mac80211/main.c |
| 28 | @@ -712,6 +712,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_ |
| 29 | local->aql_txq_limit_low[i] = IEEE80211_DEFAULT_AQL_TXQ_LIMIT_L; |
| 30 | local->aql_txq_limit_high[i] = |
| 31 | IEEE80211_DEFAULT_AQL_TXQ_LIMIT_H; |
| 32 | + atomic_set(&local->aql_ac_pending_airtime[i], 0); |
| 33 | } |
| 34 | |
| 35 | local->airtime_flags = AIRTIME_USE_TX | AIRTIME_USE_RX; |
| 36 | --- a/net/mac80211/sta_info.c |
| 37 | +++ b/net/mac80211/sta_info.c |
| 38 | @@ -1929,6 +1929,7 @@ void ieee80211_sta_update_pending_airtim |
| 39 | &sta->airtime[ac].aql_tx_pending); |
| 40 | |
| 41 | atomic_add(tx_airtime, &local->aql_total_pending_airtime); |
| 42 | + atomic_add(tx_airtime, &local->aql_ac_pending_airtime[ac]); |
| 43 | return; |
| 44 | } |
| 45 | |
| 46 | @@ -1940,14 +1941,17 @@ void ieee80211_sta_update_pending_airtim |
| 47 | tx_pending, 0); |
| 48 | } |
| 49 | |
| 50 | + atomic_sub(tx_airtime, &local->aql_total_pending_airtime); |
| 51 | tx_pending = atomic_sub_return(tx_airtime, |
| 52 | - &local->aql_total_pending_airtime); |
| 53 | + &local->aql_ac_pending_airtime[ac]); |
| 54 | if (WARN_ONCE(tx_pending < 0, |
| 55 | "Device %s AC %d pending airtime underflow: %u, %u", |
| 56 | wiphy_name(local->hw.wiphy), ac, tx_pending, |
| 57 | - tx_airtime)) |
| 58 | - atomic_cmpxchg(&local->aql_total_pending_airtime, |
| 59 | + tx_airtime)) { |
| 60 | + atomic_cmpxchg(&local->aql_ac_pending_airtime[ac], |
| 61 | tx_pending, 0); |
| 62 | + atomic_sub(tx_pending, &local->aql_total_pending_airtime); |
| 63 | + } |
| 64 | } |
| 65 | |
| 66 | int sta_info_move_state(struct sta_info *sta, |
| 67 | --- a/net/mac80211/tx.c |
| 68 | +++ b/net/mac80211/tx.c |
| 69 | @@ -3863,6 +3863,9 @@ struct ieee80211_txq *ieee80211_next_txq |
| 70 | |
| 71 | spin_lock_bh(&local->active_txq_lock[ac]); |
| 72 | |
| 73 | + if (!local->schedule_round[ac]) |
| 74 | + goto out; |
| 75 | + |
| 76 | begin: |
| 77 | txqi = list_first_entry_or_null(&local->active_txqs[ac], |
| 78 | struct txq_info, |
| 79 | @@ -3984,6 +3987,25 @@ bool ieee80211_txq_airtime_check(struct |
| 80 | } |
| 81 | EXPORT_SYMBOL(ieee80211_txq_airtime_check); |
| 82 | |
| 83 | +static bool |
| 84 | +ieee80211_txq_schedule_airtime_check(struct ieee80211_local *local, u8 ac) |
| 85 | +{ |
| 86 | + unsigned int num_txq = 0; |
| 87 | + struct txq_info *txq; |
| 88 | + u32 aql_limit; |
| 89 | + |
| 90 | + if (!wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) |
| 91 | + return true; |
| 92 | + |
| 93 | + list_for_each_entry(txq, &local->active_txqs[ac], schedule_order) |
| 94 | + num_txq++; |
| 95 | + |
| 96 | + aql_limit = (num_txq - 1) * local->aql_txq_limit_low[ac] / 2 + |
| 97 | + local->aql_txq_limit_high[ac]; |
| 98 | + |
| 99 | + return atomic_read(&local->aql_ac_pending_airtime[ac]) < aql_limit; |
| 100 | +} |
| 101 | + |
| 102 | bool ieee80211_txq_may_transmit(struct ieee80211_hw *hw, |
| 103 | struct ieee80211_txq *txq) |
| 104 | { |
| 105 | @@ -4000,6 +4022,9 @@ bool ieee80211_txq_may_transmit(struct i |
| 106 | if (list_empty(&txqi->schedule_order)) |
| 107 | goto out; |
| 108 | |
| 109 | + if (!ieee80211_txq_schedule_airtime_check(local, ac)) |
| 110 | + goto out; |
| 111 | + |
| 112 | list_for_each_entry_safe(iter, tmp, &local->active_txqs[ac], |
| 113 | schedule_order) { |
| 114 | if (iter == txqi) |
| 115 | @@ -4039,7 +4064,15 @@ void ieee80211_txq_schedule_start(struct |
| 116 | struct ieee80211_local *local = hw_to_local(hw); |
| 117 | |
| 118 | spin_lock_bh(&local->active_txq_lock[ac]); |
| 119 | - local->schedule_round[ac]++; |
| 120 | + |
| 121 | + if (ieee80211_txq_schedule_airtime_check(local, ac)) { |
| 122 | + local->schedule_round[ac]++; |
| 123 | + if (!local->schedule_round[ac]) |
| 124 | + local->schedule_round[ac]++; |
| 125 | + } else { |
| 126 | + local->schedule_round[ac] = 0; |
| 127 | + } |
| 128 | + |
| 129 | spin_unlock_bh(&local->active_txq_lock[ac]); |
| 130 | } |
| 131 | EXPORT_SYMBOL(ieee80211_txq_schedule_start); |