| From: Felix Fietkau <nbd@nbd.name> |
| Date: Fri, 9 Feb 2024 19:43:40 +0100 |
| Subject: [PATCH] mac80211: add AQL support for broadcast packets |
| |
| Excessive broadcast traffic with little competing unicast traffic can easily |
| flood hardware queues, leading to throughput issues. Additionally, filling |
| the hardware queues with too many packets breaks FQ for broadcast data. |
| Fix this by enabling AQL for broadcast packets. |
| |
| Signed-off-by: Felix Fietkau <nbd@nbd.name> |
| --- |
| |
| --- a/include/net/cfg80211.h |
| +++ b/include/net/cfg80211.h |
| @@ -3324,6 +3324,7 @@ enum wiphy_params_flags { |
| /* The per TXQ device queue limit in airtime */ |
| #define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_L 5000 |
| #define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_H 12000 |
| +#define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_BC 50000 |
| |
| /* The per interface airtime threshold to switch to lower queue limit */ |
| #define IEEE80211_AQL_THRESHOLD 24000 |
| --- a/net/mac80211/debugfs.c |
| +++ b/net/mac80211/debugfs.c |
| @@ -215,11 +215,13 @@ static ssize_t aql_pending_read(struct f |
| "VI %u us\n" |
| "BE %u us\n" |
| "BK %u us\n" |
| + "BC/MC %u us\n" |
| "total %u us\n", |
| atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_VO]), |
| atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_VI]), |
| atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_BE]), |
| atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_BK]), |
| + atomic_read(&local->aql_bc_pending_airtime), |
| atomic_read(&local->aql_total_pending_airtime)); |
| return simple_read_from_buffer(user_buf, count, ppos, |
| buf, len); |
| @@ -245,7 +247,8 @@ static ssize_t aql_txq_limit_read(struct |
| "VO %u %u\n" |
| "VI %u %u\n" |
| "BE %u %u\n" |
| - "BK %u %u\n", |
| + "BK %u %u\n" |
| + "BC/MC %u\n", |
| local->aql_txq_limit_low[IEEE80211_AC_VO], |
| local->aql_txq_limit_high[IEEE80211_AC_VO], |
| local->aql_txq_limit_low[IEEE80211_AC_VI], |
| @@ -253,7 +256,8 @@ static ssize_t aql_txq_limit_read(struct |
| local->aql_txq_limit_low[IEEE80211_AC_BE], |
| local->aql_txq_limit_high[IEEE80211_AC_BE], |
| local->aql_txq_limit_low[IEEE80211_AC_BK], |
| - local->aql_txq_limit_high[IEEE80211_AC_BK]); |
| + local->aql_txq_limit_high[IEEE80211_AC_BK], |
| + local->aql_txq_limit_bc); |
| return simple_read_from_buffer(user_buf, count, ppos, |
| buf, len); |
| } |
| @@ -279,6 +283,11 @@ static ssize_t aql_txq_limit_write(struc |
| else |
| buf[count] = '\0'; |
| |
| + if (sscanf(buf, "mcast %u", &q_limit_low) == 1) { |
| + local->aql_txq_limit_bc = q_limit_low; |
| + return count; |
| + } |
| + |
| if (sscanf(buf, "%u %u %u", &ac, &q_limit_low, &q_limit_high) != 3) |
| return -EINVAL; |
| |
| --- a/net/mac80211/ieee80211_i.h |
| +++ b/net/mac80211/ieee80211_i.h |
| @@ -1328,10 +1328,12 @@ struct ieee80211_local { |
| spinlock_t handle_wake_tx_queue_lock; |
| |
| u16 airtime_flags; |
| + u32 aql_txq_limit_bc; |
| u32 aql_txq_limit_low[IEEE80211_NUM_ACS]; |
| u32 aql_txq_limit_high[IEEE80211_NUM_ACS]; |
| u32 aql_threshold; |
| atomic_t aql_total_pending_airtime; |
| + atomic_t aql_bc_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 |
| @@ -788,6 +788,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_ |
| spin_lock_init(&local->rx_path_lock); |
| spin_lock_init(&local->queue_stop_reason_lock); |
| |
| + local->aql_txq_limit_bc = IEEE80211_DEFAULT_AQL_TXQ_LIMIT_BC; |
| for (i = 0; i < IEEE80211_NUM_ACS; i++) { |
| INIT_LIST_HEAD(&local->active_txqs[i]); |
| spin_lock_init(&local->active_txq_lock[i]); |
| --- a/net/mac80211/sta_info.c |
| +++ b/net/mac80211/sta_info.c |
| @@ -2343,13 +2343,28 @@ EXPORT_SYMBOL(ieee80211_sta_recalc_aggre |
| |
| void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local, |
| struct sta_info *sta, u8 ac, |
| - u16 tx_airtime, bool tx_completed) |
| + u16 tx_airtime, bool tx_completed, |
| + bool mcast) |
| { |
| int tx_pending; |
| |
| if (!wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) |
| return; |
| |
| + if (mcast) { |
| + if (!tx_completed) { |
| + atomic_add(tx_airtime, &local->aql_bc_pending_airtime); |
| + return; |
| + } |
| + |
| + tx_pending = atomic_sub_return(tx_airtime, |
| + &local->aql_bc_pending_airtime); |
| + if (tx_pending < 0) |
| + atomic_cmpxchg(&local->aql_bc_pending_airtime, |
| + tx_pending, 0); |
| + return; |
| + } |
| + |
| if (!tx_completed) { |
| if (sta) |
| atomic_add(tx_airtime, |
| --- a/net/mac80211/tx.c |
| +++ b/net/mac80211/tx.c |
| @@ -2536,7 +2536,7 @@ static u16 ieee80211_store_ack_skb(struc |
| |
| spin_lock_irqsave(&local->ack_status_lock, flags); |
| id = idr_alloc(&local->ack_status_frames, ack_skb, |
| - 1, 0x2000, GFP_ATOMIC); |
| + 1, 0x1000, GFP_ATOMIC); |
| spin_unlock_irqrestore(&local->ack_status_lock, flags); |
| |
| if (id >= 0) { |
| @@ -3958,20 +3958,20 @@ begin: |
| encap_out: |
| IEEE80211_SKB_CB(skb)->control.vif = vif; |
| |
| - if (tx.sta && |
| - wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) { |
| - bool ampdu = txq->ac != IEEE80211_AC_VO; |
| + if (wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) { |
| + bool ampdu = txq->sta && txq->ac != IEEE80211_AC_VO; |
| u32 airtime; |
| |
| airtime = ieee80211_calc_expected_tx_airtime(hw, vif, txq->sta, |
| skb->len, ampdu); |
| - if (airtime) { |
| - airtime = ieee80211_info_set_tx_time_est(info, airtime); |
| - ieee80211_sta_update_pending_airtime(local, tx.sta, |
| - txq->ac, |
| - airtime, |
| - false); |
| - } |
| + if (!airtime) |
| + return skb; |
| + |
| + airtime = ieee80211_info_set_tx_time_est(info, airtime); |
| + info->tx_time_mc = !tx.sta; |
| + ieee80211_sta_update_pending_airtime(local, tx.sta, txq->ac, |
| + airtime, false, |
| + info->tx_time_mc); |
| } |
| |
| return skb; |
| @@ -4026,6 +4026,7 @@ struct ieee80211_txq *ieee80211_next_txq |
| struct ieee80211_txq *ret = NULL; |
| struct txq_info *txqi = NULL, *head = NULL; |
| bool found_eligible_txq = false; |
| + bool aql_check; |
| |
| spin_lock_bh(&local->active_txq_lock[ac]); |
| |
| @@ -4049,26 +4050,26 @@ struct ieee80211_txq *ieee80211_next_txq |
| if (!head) |
| head = txqi; |
| |
| + aql_check = ieee80211_txq_airtime_check(hw, &txqi->txq); |
| + if (aql_check) |
| + found_eligible_txq = true; |
| + |
| if (txqi->txq.sta) { |
| struct sta_info *sta = container_of(txqi->txq.sta, |
| struct sta_info, sta); |
| - bool aql_check = ieee80211_txq_airtime_check(hw, &txqi->txq); |
| - s32 deficit = ieee80211_sta_deficit(sta, txqi->txq.ac); |
| - |
| - if (aql_check) |
| - found_eligible_txq = true; |
| - |
| - if (deficit < 0) |
| + if (ieee80211_sta_deficit(sta, txqi->txq.ac) < 0) { |
| sta->airtime[txqi->txq.ac].deficit += |
| sta->airtime_weight << AIRTIME_QUANTUM_SHIFT; |
| - |
| - if (deficit < 0 || !aql_check) { |
| - list_move_tail(&txqi->schedule_order, |
| - &local->active_txqs[txqi->txq.ac]); |
| - goto begin; |
| + aql_check = false; |
| } |
| } |
| |
| + if (!aql_check) { |
| + list_move_tail(&txqi->schedule_order, |
| + &local->active_txqs[txqi->txq.ac]); |
| + goto begin; |
| + } |
| + |
| if (txqi->schedule_round == local->schedule_round[ac]) |
| goto out; |
| |
| @@ -4133,7 +4134,8 @@ bool ieee80211_txq_airtime_check(struct |
| return true; |
| |
| if (!txq->sta) |
| - return true; |
| + return atomic_read(&local->aql_bc_pending_airtime) < |
| + local->aql_txq_limit_bc; |
| |
| if (unlikely(txq->tid == IEEE80211_NUM_TIDS)) |
| return true; |
| @@ -4182,15 +4184,15 @@ bool ieee80211_txq_may_transmit(struct i |
| |
| spin_lock_bh(&local->active_txq_lock[ac]); |
| |
| - if (!txqi->txq.sta) |
| - goto out; |
| - |
| if (list_empty(&txqi->schedule_order)) |
| goto out; |
| |
| if (!ieee80211_txq_schedule_airtime_check(local, ac)) |
| goto out; |
| |
| + if (!txqi->txq.sta) |
| + goto out; |
| + |
| list_for_each_entry_safe(iter, tmp, &local->active_txqs[ac], |
| schedule_order) { |
| if (iter == txqi) |
| --- a/include/net/mac80211.h |
| +++ b/include/net/mac80211.h |
| @@ -1116,6 +1116,7 @@ ieee80211_rate_get_vht_nss(const struct |
| * link the frame will be transmitted on |
| * @hw_queue: HW queue to put the frame on, skb_get_queue_mapping() gives the AC |
| * @ack_frame_id: internal frame ID for TX status, used internally |
| + * @tx_time_mc: TX time is for a multicast packet |
| * @tx_time_est: TX time estimate in units of 4us, used internally |
| * @control: union part for control data |
| * @control.rates: TX rates array to try |
| @@ -1155,8 +1156,9 @@ struct ieee80211_tx_info { |
| /* common information */ |
| u32 flags; |
| u32 band:3, |
| - ack_frame_id:13, |
| + ack_frame_id:12, |
| hw_queue:4, |
| + tx_time_mc:1, |
| tx_time_est:10; |
| /* 2 free bits */ |
| |
| --- a/net/mac80211/sta_info.h |
| +++ b/net/mac80211/sta_info.h |
| @@ -147,7 +147,8 @@ struct airtime_info { |
| |
| void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local, |
| struct sta_info *sta, u8 ac, |
| - u16 tx_airtime, bool tx_completed); |
| + u16 tx_airtime, bool tx_completed, |
| + bool mcast); |
| |
| struct sta_info; |
| |
| --- a/net/mac80211/status.c |
| +++ b/net/mac80211/status.c |
| @@ -716,7 +716,7 @@ static void ieee80211_report_used_skb(st |
| ieee80211_sta_update_pending_airtime(local, sta, |
| skb_get_queue_mapping(skb), |
| tx_time_est, |
| - true); |
| + true, info->tx_time_mc); |
| rcu_read_unlock(); |
| } |
| |
| @@ -1127,10 +1127,11 @@ void ieee80211_tx_status_ext(struct ieee |
| /* Do this here to avoid the expensive lookup of the sta |
| * in ieee80211_report_used_skb(). |
| */ |
| + bool mcast = IEEE80211_SKB_CB(skb)->tx_time_mc; |
| ieee80211_sta_update_pending_airtime(local, sta, |
| skb_get_queue_mapping(skb), |
| tx_time_est, |
| - true); |
| + true, mcast); |
| ieee80211_info_set_tx_time_est(IEEE80211_SKB_CB(skb), 0); |
| } |
| |