aboutsummaryrefslogtreecommitdiffstats
path: root/package
diff options
context:
space:
mode:
authorFelix Fietkau <nbd@nbd.name>2020-01-18 18:44:00 +0100
committerFelix Fietkau <nbd@nbd.name>2020-01-29 12:24:57 +0100
commitea5078014d3f8a6f662e41cf9dec23028ca303e3 (patch)
tree1f0d92c9c26dda6f4665c70b98e900703c147928 /package
parente0ab33ea496f371a0683b18d5555d651f8df1f5e (diff)
downloadupstream-ea5078014d3f8a6f662e41cf9dec23028ca303e3.tar.gz
upstream-ea5078014d3f8a6f662e41cf9dec23028ca303e3.tar.bz2
upstream-ea5078014d3f8a6f662e41cf9dec23028ca303e3.zip
mac80211: backport airtime queue limits support
Signed-off-by: Felix Fietkau <nbd@nbd.name>
Diffstat (limited to 'package')
-rw-r--r--package/kernel/mac80211/patches/subsys/306-mac80211-move-store-skb-ack-code-to-its-own-function.patch78
-rw-r--r--package/kernel/mac80211/patches/subsys/307-mac80211-Shrink-the-size-of-ack_frame_id-to-make-roo.patch67
-rw-r--r--package/kernel/mac80211/patches/subsys/308-mac80211-Add-new-sta_info-getter-by-sta-vif-addrs.patch78
-rw-r--r--package/kernel/mac80211/patches/subsys/309-mac80211-Import-airtime-calculation-code-from-mt76.patch690
-rw-r--r--package/kernel/mac80211/patches/subsys/310-mac80211-Implement-Airtime-based-Queue-Limit-AQL.patch446
-rw-r--r--package/kernel/mac80211/patches/subsys/311-mac80211-Use-Airtime-based-Queue-Limits-AQL-on-packe.patch146
-rw-r--r--package/kernel/mac80211/patches/subsys/312-mac80211-airtime-Fix-an-off-by-one-in-ieee80211_calc.patch31
-rw-r--r--package/kernel/mac80211/patches/subsys/313-mac80211-Turn-AQL-into-an-NL80211_EXT_FEATURE.patch253
-rw-r--r--package/kernel/mac80211/patches/subsys/353-mac80211-use-more-bits-for-ack_frame_id.patch70
-rw-r--r--package/kernel/mac80211/patches/subsys/500-mac80211_configure_antenna_gain.patch10
10 files changed, 1864 insertions, 5 deletions
diff --git a/package/kernel/mac80211/patches/subsys/306-mac80211-move-store-skb-ack-code-to-its-own-function.patch b/package/kernel/mac80211/patches/subsys/306-mac80211-move-store-skb-ack-code-to-its-own-function.patch
new file mode 100644
index 0000000000..3289aae151
--- /dev/null
+++ b/package/kernel/mac80211/patches/subsys/306-mac80211-move-store-skb-ack-code-to-its-own-function.patch
@@ -0,0 +1,78 @@
+From: John Crispin <john@phrozen.org>
+Date: Tue, 29 Oct 2019 10:13:02 +0100
+Subject: [PATCH] mac80211: move store skb ack code to its own function
+
+This patch moves the code handling SKBTX_WIFI_STATUS inside the TX path
+into an extra function. This allows us to reuse it inside the 802.11 encap
+offloading datapath.
+
+Signed-off-by: John Crispin <john@phrozen.org>
+Link: https://lore.kernel.org/r/20191029091304.7330-2-john@phrozen.org
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -2430,6 +2430,33 @@ static int ieee80211_lookup_ra_sta(struc
+ return 0;
+ }
+
++static int ieee80211_store_ack_skb(struct ieee80211_local *local,
++ struct sk_buff *skb,
++ u32 *info_flags)
++{
++ struct sk_buff *ack_skb = skb_clone_sk(skb);
++ u16 info_id = 0;
++
++ if (ack_skb) {
++ unsigned long flags;
++ int id;
++
++ spin_lock_irqsave(&local->ack_status_lock, flags);
++ id = idr_alloc(&local->ack_status_frames, ack_skb,
++ 1, 0x10000, GFP_ATOMIC);
++ spin_unlock_irqrestore(&local->ack_status_lock, flags);
++
++ if (id >= 0) {
++ info_id = id;
++ *info_flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
++ } else {
++ kfree_skb(ack_skb);
++ }
++ }
++
++ return info_id;
++}
++
+ /**
+ * ieee80211_build_hdr - build 802.11 header in the given frame
+ * @sdata: virtual interface to build the header for
+@@ -2723,26 +2750,8 @@ static struct sk_buff *ieee80211_build_h
+ }
+
+ if (unlikely(!multicast && skb->sk &&
+- skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)) {
+- struct sk_buff *ack_skb = skb_clone_sk(skb);
+-
+- if (ack_skb) {
+- unsigned long flags;
+- int id;
+-
+- spin_lock_irqsave(&local->ack_status_lock, flags);
+- id = idr_alloc(&local->ack_status_frames, ack_skb,
+- 1, 0x10000, GFP_ATOMIC);
+- spin_unlock_irqrestore(&local->ack_status_lock, flags);
+-
+- if (id >= 0) {
+- info_id = id;
+- info_flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
+- } else {
+- kfree_skb(ack_skb);
+- }
+- }
+- }
++ skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS))
++ info_id = ieee80211_store_ack_skb(local, skb, &info_flags);
+
+ /*
+ * If the skb is shared we need to obtain our own copy.
diff --git a/package/kernel/mac80211/patches/subsys/307-mac80211-Shrink-the-size-of-ack_frame_id-to-make-roo.patch b/package/kernel/mac80211/patches/subsys/307-mac80211-Shrink-the-size-of-ack_frame_id-to-make-roo.patch
new file mode 100644
index 0000000000..8b3e666731
--- /dev/null
+++ b/package/kernel/mac80211/patches/subsys/307-mac80211-Shrink-the-size-of-ack_frame_id-to-make-roo.patch
@@ -0,0 +1,67 @@
+From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= <toke@redhat.com>
+Date: Wed, 23 Oct 2019 11:59:00 +0200
+Subject: [PATCH] mac80211: Shrink the size of ack_frame_id to make room for
+ tx_time_est
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+To implement airtime queue limiting, we need to keep a running account of
+the estimated airtime of all skbs queued into the device. Do to this
+correctly, we need to store the airtime estimate into the skb so we can
+decrease the outstanding balance when the skb is freed. This means that the
+time estimate must be stored somewhere that will survive for the lifetime
+of the skb.
+
+To get this, decrease the size of the ack_frame_id field to 6 bits, and
+lower the size of the ID space accordingly. This leaves 10 bits for use for
+tx_time_est, which is enough to store a maximum of 4096 us, if we shift the
+values so they become units of 4us.
+
+Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
+Link: https://lore.kernel.org/r/157182474063.150713.16132669599100802716.stgit@toke.dk
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -967,6 +967,7 @@ ieee80211_rate_get_vht_nss(const struct
+ * @band: the band to transmit on (use for checking for races)
+ * @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_est: TX time estimate in units of 4us, used internally
+ * @control: union part for control data
+ * @control.rates: TX rates array to try
+ * @control.rts_cts_rate_idx: rate for RTS or CTS
+@@ -1007,7 +1008,8 @@ struct ieee80211_tx_info {
+
+ u8 hw_queue;
+
+- u16 ack_frame_id;
++ u16 ack_frame_id:6;
++ u16 tx_time_est:10;
+
+ union {
+ struct {
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -3427,7 +3427,7 @@ int ieee80211_attach_ack_skb(struct ieee
+
+ spin_lock_irqsave(&local->ack_status_lock, spin_flags);
+ id = idr_alloc(&local->ack_status_frames, ack_skb,
+- 1, 0x10000, GFP_ATOMIC);
++ 1, 0x40, GFP_ATOMIC);
+ spin_unlock_irqrestore(&local->ack_status_lock, spin_flags);
+
+ if (id < 0) {
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -2443,7 +2443,7 @@ static int ieee80211_store_ack_skb(struc
+
+ spin_lock_irqsave(&local->ack_status_lock, flags);
+ id = idr_alloc(&local->ack_status_frames, ack_skb,
+- 1, 0x10000, GFP_ATOMIC);
++ 1, 0x40, GFP_ATOMIC);
+ spin_unlock_irqrestore(&local->ack_status_lock, flags);
+
+ if (id >= 0) {
diff --git a/package/kernel/mac80211/patches/subsys/308-mac80211-Add-new-sta_info-getter-by-sta-vif-addrs.patch b/package/kernel/mac80211/patches/subsys/308-mac80211-Add-new-sta_info-getter-by-sta-vif-addrs.patch
new file mode 100644
index 0000000000..2c4b978230
--- /dev/null
+++ b/package/kernel/mac80211/patches/subsys/308-mac80211-Add-new-sta_info-getter-by-sta-vif-addrs.patch
@@ -0,0 +1,78 @@
+From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= <toke@redhat.com>
+Date: Tue, 12 Nov 2019 14:08:35 +0100
+Subject: [PATCH] mac80211: Add new sta_info getter by sta/vif addrs
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+In ieee80211_tx_status() we don't have an sdata struct when looking up the
+destination sta. Instead, we just do a lookup by the vif addr that is the
+source of the packet being completed. Factor this out into a new sta_info
+getter helper, since we need to use it for accounting AQL as well.
+
+Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
+Link: https://lore.kernel.org/r/20191112130835.382062-1-toke@redhat.com
+[remove internal rcu_read_lock(), document instead]
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/net/mac80211/sta_info.c
++++ b/net/mac80211/sta_info.c
+@@ -210,6 +210,20 @@ struct sta_info *sta_info_get_bss(struct
+ return NULL;
+ }
+
++struct sta_info *sta_info_get_by_addrs(struct ieee80211_local *local,
++ const u8 *sta_addr, const u8 *vif_addr)
++{
++ struct rhlist_head *tmp;
++ struct sta_info *sta;
++
++ for_each_sta_info(local, sta_addr, sta, tmp) {
++ if (ether_addr_equal(vif_addr, sta->sdata->vif.addr))
++ return sta;
++ }
++
++ return NULL;
++}
++
+ struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata,
+ int idx)
+ {
+--- a/net/mac80211/sta_info.h
++++ b/net/mac80211/sta_info.h
+@@ -725,6 +725,10 @@ struct sta_info *sta_info_get(struct iee
+ struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata,
+ const u8 *addr);
+
++/* user must hold sta_mtx or be in RCU critical section */
++struct sta_info *sta_info_get_by_addrs(struct ieee80211_local *local,
++ const u8 *sta_addr, const u8 *vif_addr);
++
+ #define for_each_sta_info(local, _addr, _sta, _tmp) \
+ rhl_for_each_entry_rcu(_sta, _tmp, \
+ sta_info_hash_lookup(local, _addr), hash_node)
+--- a/net/mac80211/status.c
++++ b/net/mac80211/status.c
+@@ -1085,19 +1085,13 @@ void ieee80211_tx_status(struct ieee8021
+ .skb = skb,
+ .info = IEEE80211_SKB_CB(skb),
+ };
+- struct rhlist_head *tmp;
+ struct sta_info *sta;
+
+ rcu_read_lock();
+
+- for_each_sta_info(local, hdr->addr1, sta, tmp) {
+- /* skip wrong virtual interface */
+- if (!ether_addr_equal(hdr->addr2, sta->sdata->vif.addr))
+- continue;
+-
++ sta = sta_info_get_by_addrs(local, hdr->addr1, hdr->addr2);
++ if (sta)
+ status.sta = &sta->sta;
+- break;
+- }
+
+ __ieee80211_tx_status(hw, &status);
+ rcu_read_unlock();
diff --git a/package/kernel/mac80211/patches/subsys/309-mac80211-Import-airtime-calculation-code-from-mt76.patch b/package/kernel/mac80211/patches/subsys/309-mac80211-Import-airtime-calculation-code-from-mt76.patch
new file mode 100644
index 0000000000..7bfe299cf4
--- /dev/null
+++ b/package/kernel/mac80211/patches/subsys/309-mac80211-Import-airtime-calculation-code-from-mt76.patch
@@ -0,0 +1,690 @@
+From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= <toke@redhat.com>
+Date: Mon, 18 Nov 2019 22:06:08 -0800
+Subject: [PATCH] mac80211: Import airtime calculation code from mt76
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Felix recently added code to calculate airtime of packets to the mt76
+driver. Import this into mac80211 so we can use it for airtime queue limit
+calculations.
+
+The airtime.c file is copied verbatim from the mt76 driver, and adjusted to
+be usable in mac80211. This involves:
+
+- Switching to mac80211 data structures.
+- Adding support for 160 MHz channels and HE mode.
+- Moving the symbol and duration calculations around a bit to avoid
+ rounding with the higher rates and longer symbol times used for HE rates.
+
+The per-rate TX rate calculation is also split out to its own function so
+it can be used directly for the AQL calculations later.
+
+Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
+Link: https://lore.kernel.org/r/20191119060610.76681-3-kyan@google.com
+[fix HE_GROUP_IDX() to use 3 * bw, since there are 3 _gi values]
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+ create mode 100644 net/mac80211/airtime.c
+
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -6417,4 +6417,33 @@ void ieee80211_nan_func_match(struct iee
+ struct cfg80211_nan_match_params *match,
+ gfp_t gfp);
+
++/**
++ * ieee80211_calc_rx_airtime - calculate estimated transmission airtime for RX.
++ *
++ * This function calculates the estimated airtime usage of a frame based on the
++ * rate information in the RX status struct and the frame length.
++ *
++ * @hw: pointer as obtained from ieee80211_alloc_hw()
++ * @status: &struct ieee80211_rx_status containing the transmission rate
++ * information.
++ * @len: frame length in bytes
++ */
++u32 ieee80211_calc_rx_airtime(struct ieee80211_hw *hw,
++ struct ieee80211_rx_status *status,
++ int len);
++
++/**
++ * ieee80211_calc_tx_airtime - calculate estimated transmission airtime for TX.
++ *
++ * This function calculates the estimated airtime usage of a frame based on the
++ * rate information in the TX info struct and the frame length.
++ *
++ * @hw: pointer as obtained from ieee80211_alloc_hw()
++ * @info: &struct ieee80211_tx_info of the frame.
++ * @len: frame length in bytes
++ */
++u32 ieee80211_calc_tx_airtime(struct ieee80211_hw *hw,
++ struct ieee80211_tx_info *info,
++ int len);
++
+ #endif /* MAC80211_H */
+--- a/net/mac80211/Makefile
++++ b/net/mac80211/Makefile
+@@ -31,7 +31,8 @@ mac80211-y := \
+ chan.o \
+ trace.o mlme.o \
+ tdls.o \
+- ocb.o
++ ocb.o \
++ airtime.o
+
+ mac80211-$(CPTCFG_MAC80211_LEDS) += led.o
+ mac80211-$(CPTCFG_MAC80211_DEBUGFS) += \
+--- /dev/null
++++ b/net/mac80211/airtime.c
+@@ -0,0 +1,597 @@
++// SPDX-License-Identifier: ISC
++/*
++ * Copyright (C) 2019 Felix Fietkau <nbd@nbd.name>
++ */
++
++#include <net/mac80211.h>
++#include "ieee80211_i.h"
++#include "sta_info.h"
++
++#define AVG_PKT_SIZE 1024
++
++/* Number of bits for an average sized packet */
++#define MCS_NBITS (AVG_PKT_SIZE << 3)
++
++/* Number of kilo-symbols (symbols * 1024) for a packet with (bps) bits per
++ * symbol. We use k-symbols to avoid rounding in the _TIME macros below.
++ */
++#define MCS_N_KSYMS(bps) DIV_ROUND_UP(MCS_NBITS << 10, (bps))
++
++/* Transmission time (in 1024 * usec) for a packet containing (ksyms) * 1024
++ * symbols.
++ */
++#define MCS_SYMBOL_TIME(sgi, ksyms) \
++ (sgi ? \
++ ((ksyms) * 4 * 18) / 20 : /* 3.6 us per sym */ \
++ ((ksyms) * 4) /* 4.0 us per sym */ \
++ )
++
++/* Transmit duration for the raw data part of an average sized packet */
++#define MCS_DURATION(streams, sgi, bps) \
++ ((u32)MCS_SYMBOL_TIME(sgi, MCS_N_KSYMS((streams) * (bps))))
++
++#define MCS_DURATION_S(shift, streams, sgi, bps) \
++ ((u16)((MCS_DURATION(streams, sgi, bps) >> shift)))
++
++/* These should match the values in enum nl80211_he_gi */
++#define HE_GI_08 0
++#define HE_GI_16 1
++#define HE_GI_32 2
++
++/* Transmission time (1024 usec) for a packet containing (ksyms) * k-symbols */
++#define HE_SYMBOL_TIME(gi, ksyms) \
++ (gi == HE_GI_08 ? \
++ ((ksyms) * 16 * 17) / 20 : /* 13.6 us per sym */ \
++ (gi == HE_GI_16 ? \
++ ((ksyms) * 16 * 18) / 20 : /* 14.4 us per sym */ \
++ ((ksyms) * 16) /* 16.0 us per sym */ \
++ ))
++
++/* Transmit duration for the raw data part of an average sized packet */
++#define HE_DURATION(streams, gi, bps) \
++ ((u32)HE_SYMBOL_TIME(gi, MCS_N_KSYMS((streams) * (bps))))
++
++#define HE_DURATION_S(shift, streams, gi, bps) \
++ (HE_DURATION(streams, gi, bps) >> shift)
++
++#define BW_20 0
++#define BW_40 1
++#define BW_80 2
++#define BW_160 3
++
++/*
++ * Define group sort order: HT40 -> SGI -> #streams
++ */
++#define IEEE80211_MAX_STREAMS 4
++#define IEEE80211_HT_STREAM_GROUPS 4 /* BW(=2) * SGI(=2) */
++#define IEEE80211_VHT_STREAM_GROUPS 8 /* BW(=4) * SGI(=2) */
++
++#define IEEE80211_HE_MAX_STREAMS 8
++#define IEEE80211_HE_STREAM_GROUPS 12 /* BW(=4) * GI(=3) */
++
++#define IEEE80211_HT_GROUPS_NB (IEEE80211_MAX_STREAMS * \
++ IEEE80211_HT_STREAM_GROUPS)
++#define IEEE80211_VHT_GROUPS_NB (IEEE80211_MAX_STREAMS * \
++ IEEE80211_VHT_STREAM_GROUPS)
++#define IEEE80211_HE_GROUPS_NB (IEEE80211_HE_MAX_STREAMS * \
++ IEEE80211_HE_STREAM_GROUPS)
++#define IEEE80211_GROUPS_NB (IEEE80211_HT_GROUPS_NB + \
++ IEEE80211_VHT_GROUPS_NB + \
++ IEEE80211_HE_GROUPS_NB)
++
++#define IEEE80211_HT_GROUP_0 0
++#define IEEE80211_VHT_GROUP_0 (IEEE80211_HT_GROUP_0 + IEEE80211_HT_GROUPS_NB)
++#define IEEE80211_HE_GROUP_0 (IEEE80211_VHT_GROUP_0 + IEEE80211_VHT_GROUPS_NB)
++
++#define MCS_GROUP_RATES 12
++
++#define HT_GROUP_IDX(_streams, _sgi, _ht40) \
++ IEEE80211_HT_GROUP_0 + \
++ IEEE80211_MAX_STREAMS * 2 * _ht40 + \
++ IEEE80211_MAX_STREAMS * _sgi + \
++ _streams - 1
++
++#define _MAX(a, b) (((a)>(b))?(a):(b))
++
++#define GROUP_SHIFT(duration) \
++ _MAX(0, 16 - __builtin_clz(duration))
++
++/* MCS rate information for an MCS group */
++#define __MCS_GROUP(_streams, _sgi, _ht40, _s) \
++ [HT_GROUP_IDX(_streams, _sgi, _ht40)] = { \
++ .shift = _s, \
++ .duration = { \
++ MCS_DURATION_S(_s, _streams, _sgi, _ht40 ? 54 : 26), \
++ MCS_DURATION_S(_s, _streams, _sgi, _ht40 ? 108 : 52), \
++ MCS_DURATION_S(_s, _streams, _sgi, _ht40 ? 162 : 78), \
++ MCS_DURATION_S(_s, _streams, _sgi, _ht40 ? 216 : 104), \
++ MCS_DURATION_S(_s, _streams, _sgi, _ht40 ? 324 : 156), \
++ MCS_DURATION_S(_s, _streams, _sgi, _ht40 ? 432 : 208), \
++ MCS_DURATION_S(_s, _streams, _sgi, _ht40 ? 486 : 234), \
++ MCS_DURATION_S(_s, _streams, _sgi, _ht40 ? 540 : 260) \
++ } \
++}
++
++#define MCS_GROUP_SHIFT(_streams, _sgi, _ht40) \
++ GROUP_SHIFT(MCS_DURATION(_streams, _sgi, _ht40 ? 54 : 26))
++
++#define MCS_GROUP(_streams, _sgi, _ht40) \
++ __MCS_GROUP(_streams, _sgi, _ht40, \
++ MCS_GROUP_SHIFT(_streams, _sgi, _ht40))
++
++#define VHT_GROUP_IDX(_streams, _sgi, _bw) \
++ (IEEE80211_VHT_GROUP_0 + \
++ IEEE80211_MAX_STREAMS * 2 * (_bw) + \
++ IEEE80211_MAX_STREAMS * (_sgi) + \
++ (_streams) - 1)
++
++#define BW2VBPS(_bw, r4, r3, r2, r1) \
++ (_bw == BW_160 ? r4 : _bw == BW_80 ? r3 : _bw == BW_40 ? r2 : r1)
++
++#define __VHT_GROUP(_streams, _sgi, _bw, _s) \
++ [VHT_GROUP_IDX(_streams, _sgi, _bw)] = { \
++ .shift = _s, \
++ .duration = { \
++ MCS_DURATION_S(_s, _streams, _sgi, \
++ BW2VBPS(_bw, 234, 117, 54, 26)), \
++ MCS_DURATION_S(_s, _streams, _sgi, \
++ BW2VBPS(_bw, 468, 234, 108, 52)), \
++ MCS_DURATION_S(_s, _streams, _sgi, \
++ BW2VBPS(_bw, 702, 351, 162, 78)), \
++ MCS_DURATION_S(_s, _streams, _sgi, \
++ BW2VBPS(_bw, 936, 468, 216, 104)), \
++ MCS_DURATION_S(_s, _streams, _sgi, \
++ BW2VBPS(_bw, 1404, 702, 324, 156)), \
++ MCS_DURATION_S(_s, _streams, _sgi, \
++ BW2VBPS(_bw, 1872, 936, 432, 208)), \
++ MCS_DURATION_S(_s, _streams, _sgi, \
++ BW2VBPS(_bw, 2106, 1053, 486, 234)), \
++ MCS_DURATION_S(_s, _streams, _sgi, \
++ BW2VBPS(_bw, 2340, 1170, 540, 260)), \
++ MCS_DURATION_S(_s, _streams, _sgi, \
++ BW2VBPS(_bw, 2808, 1404, 648, 312)), \
++ MCS_DURATION_S(_s, _streams, _sgi, \
++ BW2VBPS(_bw, 3120, 1560, 720, 346)) \
++ } \
++}
++
++#define VHT_GROUP_SHIFT(_streams, _sgi, _bw) \
++ GROUP_SHIFT(MCS_DURATION(_streams, _sgi, \
++ BW2VBPS(_bw, 243, 117, 54, 26)))
++
++#define VHT_GROUP(_streams, _sgi, _bw) \
++ __VHT_GROUP(_streams, _sgi, _bw, \
++ VHT_GROUP_SHIFT(_streams, _sgi, _bw))
++
++
++#define HE_GROUP_IDX(_streams, _gi, _bw) \
++ (IEEE80211_HE_GROUP_0 + \
++ IEEE80211_HE_MAX_STREAMS * 3 * (_bw) + \
++ IEEE80211_HE_MAX_STREAMS * (_gi) + \
++ (_streams) - 1)
++
++#define __HE_GROUP(_streams, _gi, _bw, _s) \
++ [HE_GROUP_IDX(_streams, _gi, _bw)] = { \
++ .shift = _s, \
++ .duration = { \
++ HE_DURATION_S(_s, _streams, _gi, \
++ BW2VBPS(_bw, 979, 489, 230, 115)), \
++ HE_DURATION_S(_s, _streams, _gi, \
++ BW2VBPS(_bw, 1958, 979, 475, 230)), \
++ HE_DURATION_S(_s, _streams, _gi, \
++ BW2VBPS(_bw, 2937, 1468, 705, 345)), \
++ HE_DURATION_S(_s, _streams, _gi, \
++ BW2VBPS(_bw, 3916, 1958, 936, 475)), \
++ HE_DURATION_S(_s, _streams, _gi, \
++ BW2VBPS(_bw, 5875, 2937, 1411, 705)), \
++ HE_DURATION_S(_s, _streams, _gi, \
++ BW2VBPS(_bw, 7833, 3916, 1872, 936)), \
++ HE_DURATION_S(_s, _streams, _gi, \
++ BW2VBPS(_bw, 8827, 4406, 2102, 1051)), \
++ HE_DURATION_S(_s, _streams, _gi, \
++ BW2VBPS(_bw, 9806, 4896, 2347, 1166)), \
++ HE_DURATION_S(_s, _streams, _gi, \
++ BW2VBPS(_bw, 11764, 5875, 2808, 1411)), \
++ HE_DURATION_S(_s, _streams, _gi, \
++ BW2VBPS(_bw, 13060, 6523, 3124, 1555)), \
++ HE_DURATION_S(_s, _streams, _gi, \
++ BW2VBPS(_bw, 14702, 7344, 3513, 1756)), \
++ HE_DURATION_S(_s, _streams, _gi, \
++ BW2VBPS(_bw, 16329, 8164, 3902, 1944)) \
++ } \
++}
++
++#define HE_GROUP_SHIFT(_streams, _gi, _bw) \
++ GROUP_SHIFT(HE_DURATION(_streams, _gi, \
++ BW2VBPS(_bw, 979, 489, 230, 115)))
++
++#define HE_GROUP(_streams, _gi, _bw) \
++ __HE_GROUP(_streams, _gi, _bw, \
++ HE_GROUP_SHIFT(_streams, _gi, _bw))
++struct mcs_group {
++ u8 shift;
++ u16 duration[MCS_GROUP_RATES];
++};
++
++static const struct mcs_group airtime_mcs_groups[] = {
++ MCS_GROUP(1, 0, BW_20),
++ MCS_GROUP(2, 0, BW_20),
++ MCS_GROUP(3, 0, BW_20),
++ MCS_GROUP(4, 0, BW_20),
++
++ MCS_GROUP(1, 1, BW_20),
++ MCS_GROUP(2, 1, BW_20),
++ MCS_GROUP(3, 1, BW_20),
++ MCS_GROUP(4, 1, BW_20),
++
++ MCS_GROUP(1, 0, BW_40),
++ MCS_GROUP(2, 0, BW_40),
++ MCS_GROUP(3, 0, BW_40),
++ MCS_GROUP(4, 0, BW_40),
++
++ MCS_GROUP(1, 1, BW_40),
++ MCS_GROUP(2, 1, BW_40),
++ MCS_GROUP(3, 1, BW_40),
++ MCS_GROUP(4, 1, BW_40),
++
++ VHT_GROUP(1, 0, BW_20),
++ VHT_GROUP(2, 0, BW_20),
++ VHT_GROUP(3, 0, BW_20),
++ VHT_GROUP(4, 0, BW_20),
++
++ VHT_GROUP(1, 1, BW_20),
++ VHT_GROUP(2, 1, BW_20),
++ VHT_GROUP(3, 1, BW_20),
++ VHT_GROUP(4, 1, BW_20),
++
++ VHT_GROUP(1, 0, BW_40),
++ VHT_GROUP(2, 0, BW_40),
++ VHT_GROUP(3, 0, BW_40),
++ VHT_GROUP(4, 0, BW_40),
++
++ VHT_GROUP(1, 1, BW_40),
++ VHT_GROUP(2, 1, BW_40),
++ VHT_GROUP(3, 1, BW_40),
++ VHT_GROUP(4, 1, BW_40),
++
++ VHT_GROUP(1, 0, BW_80),
++ VHT_GROUP(2, 0, BW_80),
++ VHT_GROUP(3, 0, BW_80),
++ VHT_GROUP(4, 0, BW_80),
++
++ VHT_GROUP(1, 1, BW_80),
++ VHT_GROUP(2, 1, BW_80),
++ VHT_GROUP(3, 1, BW_80),
++ VHT_GROUP(4, 1, BW_80),
++
++ VHT_GROUP(1, 0, BW_160),
++ VHT_GROUP(2, 0, BW_160),
++ VHT_GROUP(3, 0, BW_160),
++ VHT_GROUP(4, 0, BW_160),
++
++ VHT_GROUP(1, 1, BW_160),
++ VHT_GROUP(2, 1, BW_160),
++ VHT_GROUP(3, 1, BW_160),
++ VHT_GROUP(4, 1, BW_160),
++
++ HE_GROUP(1, HE_GI_08, BW_20),
++ HE_GROUP(2, HE_GI_08, BW_20),
++ HE_GROUP(3, HE_GI_08, BW_20),
++ HE_GROUP(4, HE_GI_08, BW_20),
++ HE_GROUP(5, HE_GI_08, BW_20),
++ HE_GROUP(6, HE_GI_08, BW_20),
++ HE_GROUP(7, HE_GI_08, BW_20),
++ HE_GROUP(8, HE_GI_08, BW_20),
++
++ HE_GROUP(1, HE_GI_16, BW_20),
++ HE_GROUP(2, HE_GI_16, BW_20),
++ HE_GROUP(3, HE_GI_16, BW_20),
++ HE_GROUP(4, HE_GI_16, BW_20),
++ HE_GROUP(5, HE_GI_16, BW_20),
++ HE_GROUP(6, HE_GI_16, BW_20),
++ HE_GROUP(7, HE_GI_16, BW_20),
++ HE_GROUP(8, HE_GI_16, BW_20),
++
++ HE_GROUP(1, HE_GI_32, BW_20),
++ HE_GROUP(2, HE_GI_32, BW_20),
++ HE_GROUP(3, HE_GI_32, BW_20),
++ HE_GROUP(4, HE_GI_32, BW_20),
++ HE_GROUP(5, HE_GI_32, BW_20),
++ HE_GROUP(6, HE_GI_32, BW_20),
++ HE_GROUP(7, HE_GI_32, BW_20),
++ HE_GROUP(8, HE_GI_32, BW_20),
++
++ HE_GROUP(1, HE_GI_08, BW_40),
++ HE_GROUP(2, HE_GI_08, BW_40),
++ HE_GROUP(3, HE_GI_08, BW_40),
++ HE_GROUP(4, HE_GI_08, BW_40),
++ HE_GROUP(5, HE_GI_08, BW_40),
++ HE_GROUP(6, HE_GI_08, BW_40),
++ HE_GROUP(7, HE_GI_08, BW_40),
++ HE_GROUP(8, HE_GI_08, BW_40),
++
++ HE_GROUP(1, HE_GI_16, BW_40),
++ HE_GROUP(2, HE_GI_16, BW_40),
++ HE_GROUP(3, HE_GI_16, BW_40),
++ HE_GROUP(4, HE_GI_16, BW_40),
++ HE_GROUP(5, HE_GI_16, BW_40),
++ HE_GROUP(6, HE_GI_16, BW_40),
++ HE_GROUP(7, HE_GI_16, BW_40),
++ HE_GROUP(8, HE_GI_16, BW_40),
++
++ HE_GROUP(1, HE_GI_32, BW_40),
++ HE_GROUP(2, HE_GI_32, BW_40),
++ HE_GROUP(3, HE_GI_32, BW_40),
++ HE_GROUP(4, HE_GI_32, BW_40),
++ HE_GROUP(5, HE_GI_32, BW_40),
++ HE_GROUP(6, HE_GI_32, BW_40),
++ HE_GROUP(7, HE_GI_32, BW_40),
++ HE_GROUP(8, HE_GI_32, BW_40),
++
++ HE_GROUP(1, HE_GI_08, BW_80),
++ HE_GROUP(2, HE_GI_08, BW_80),
++ HE_GROUP(3, HE_GI_08, BW_80),
++ HE_GROUP(4, HE_GI_08, BW_80),
++ HE_GROUP(5, HE_GI_08, BW_80),
++ HE_GROUP(6, HE_GI_08, BW_80),
++ HE_GROUP(7, HE_GI_08, BW_80),
++ HE_GROUP(8, HE_GI_08, BW_80),
++
++ HE_GROUP(1, HE_GI_16, BW_80),
++ HE_GROUP(2, HE_GI_16, BW_80),
++ HE_GROUP(3, HE_GI_16, BW_80),
++ HE_GROUP(4, HE_GI_16, BW_80),
++ HE_GROUP(5, HE_GI_16, BW_80),
++ HE_GROUP(6, HE_GI_16, BW_80),
++ HE_GROUP(7, HE_GI_16, BW_80),
++ HE_GROUP(8, HE_GI_16, BW_80),
++
++ HE_GROUP(1, HE_GI_32, BW_80),
++ HE_GROUP(2, HE_GI_32, BW_80),
++ HE_GROUP(3, HE_GI_32, BW_80),
++ HE_GROUP(4, HE_GI_32, BW_80),
++ HE_GROUP(5, HE_GI_32, BW_80),
++ HE_GROUP(6, HE_GI_32, BW_80),
++ HE_GROUP(7, HE_GI_32, BW_80),
++ HE_GROUP(8, HE_GI_32, BW_80),
++
++ HE_GROUP(1, HE_GI_08, BW_160),
++ HE_GROUP(2, HE_GI_08, BW_160),
++ HE_GROUP(3, HE_GI_08, BW_160),
++ HE_GROUP(4, HE_GI_08, BW_160),
++ HE_GROUP(5, HE_GI_08, BW_160),
++ HE_GROUP(6, HE_GI_08, BW_160),
++ HE_GROUP(7, HE_GI_08, BW_160),
++ HE_GROUP(8, HE_GI_08, BW_160),
++
++ HE_GROUP(1, HE_GI_16, BW_160),
++ HE_GROUP(2, HE_GI_16, BW_160),
++ HE_GROUP(3, HE_GI_16, BW_160),
++ HE_GROUP(4, HE_GI_16, BW_160),
++ HE_GROUP(5, HE_GI_16, BW_160),
++ HE_GROUP(6, HE_GI_16, BW_160),
++ HE_GROUP(7, HE_GI_16, BW_160),
++ HE_GROUP(8, HE_GI_16, BW_160),
++
++ HE_GROUP(1, HE_GI_32, BW_160),
++ HE_GROUP(2, HE_GI_32, BW_160),
++ HE_GROUP(3, HE_GI_32, BW_160),
++ HE_GROUP(4, HE_GI_32, BW_160),
++ HE_GROUP(5, HE_GI_32, BW_160),
++ HE_GROUP(6, HE_GI_32, BW_160),
++ HE_GROUP(7, HE_GI_32, BW_160),
++ HE_GROUP(8, HE_GI_32, BW_160),
++};
++
++static u32
++ieee80211_calc_legacy_rate_duration(u16 bitrate, bool short_pre,
++ bool cck, int len)
++{
++ u32 duration;
++
++ if (cck) {
++ duration = 144 + 48; /* preamble + PLCP */
++ if (short_pre)
++ duration >>= 1;
++
++ duration += 10; /* SIFS */
++ } else {
++ duration = 20 + 16; /* premable + SIFS */
++ }
++
++ len <<= 3;
++ duration += (len * 10) / bitrate;
++
++ return duration;
++}
++
++u32 ieee80211_calc_rx_airtime(struct ieee80211_hw *hw,
++ struct ieee80211_rx_status *status,
++ int len)
++{
++ struct ieee80211_supported_band *sband;
++ const struct ieee80211_rate *rate;
++ bool sgi = status->enc_flags & RX_ENC_FLAG_SHORT_GI;
++ bool sp = status->enc_flags & RX_ENC_FLAG_SHORTPRE;
++ int bw, streams;
++ int group, idx;
++ u32 duration;
++ bool cck;
++
++ switch (status->bw) {
++ case RATE_INFO_BW_20:
++ bw = BW_20;
++ break;
++ case RATE_INFO_BW_40:
++ bw = BW_40;
++ break;
++ case RATE_INFO_BW_80:
++ bw = BW_80;
++ break;
++ case RATE_INFO_BW_160:
++ bw = BW_160;
++ break;
++ default:
++ WARN_ON_ONCE(1);
++ return 0;
++ }
++
++ switch (status->encoding) {
++ case RX_ENC_LEGACY:
++ if (WARN_ON_ONCE(status->band > NL80211_BAND_5GHZ))
++ return 0;
++
++ sband = hw->wiphy->bands[status->band];
++ if (!sband || status->rate_idx > sband->n_bitrates)
++ return 0;
++
++ rate = &sband->bitrates[status->rate_idx];
++ cck = rate->flags & IEEE80211_RATE_MANDATORY_B;
++
++ return ieee80211_calc_legacy_rate_duration(rate->bitrate, sp,
++ cck, len);
++
++ case RX_ENC_VHT:
++ streams = status->nss;
++ idx = status->rate_idx;
++ group = VHT_GROUP_IDX(streams, sgi, bw);
++ break;
++ case RX_ENC_HT:
++ streams = ((status->rate_idx >> 3) & 3) + 1;
++ idx = status->rate_idx & 7;
++ group = HT_GROUP_IDX(streams, sgi, bw);
++ break;
++ case RX_ENC_HE:
++ streams = status->nss;
++ idx = status->rate_idx;
++ group = HE_GROUP_IDX(streams, status->he_gi, bw);
++ break;
++ default:
++ WARN_ON_ONCE(1);
++ return 0;
++ }
++
++ if (WARN_ON_ONCE((status->encoding != RX_ENC_HE && streams > 4) ||
++ (status->encoding == RX_ENC_HE && streams > 8)))
++ return 0;
++
++ duration = airtime_mcs_groups[group].duration[idx];
++ duration <<= airtime_mcs_groups[group].shift;
++ duration *= len;
++ duration /= AVG_PKT_SIZE;
++ duration /= 1024;
++
++ duration += 36 + (streams << 2);
++
++ return duration;
++}
++EXPORT_SYMBOL_GPL(ieee80211_calc_rx_airtime);
++
++static u32 ieee80211_calc_tx_airtime_rate(struct ieee80211_hw *hw,
++ struct ieee80211_tx_rate *rate,
++ u8 band, int len)
++{
++ struct ieee80211_rx_status stat = {
++ .band = band,
++ };
++
++ if (rate->idx < 0 || !rate->count)
++ return 0;
++
++ if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH)
++ stat.bw = RATE_INFO_BW_80;
++ else if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
++ stat.bw = RATE_INFO_BW_40;
++ else
++ stat.bw = RATE_INFO_BW_20;
++
++ stat.enc_flags = 0;
++ if (rate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)
++ stat.enc_flags |= RX_ENC_FLAG_SHORTPRE;
++ if (rate->flags & IEEE80211_TX_RC_SHORT_GI)
++ stat.enc_flags |= RX_ENC_FLAG_SHORT_GI;
++
++ stat.rate_idx = rate->idx;
++ if (rate->flags & IEEE80211_TX_RC_VHT_MCS) {
++ stat.encoding = RX_ENC_VHT;
++ stat.rate_idx = ieee80211_rate_get_vht_mcs(rate);
++ stat.nss = ieee80211_rate_get_vht_nss(rate);
++ } else if (rate->flags & IEEE80211_TX_RC_MCS) {
++ stat.encoding = RX_ENC_HT;
++ } else {
++ stat.encoding = RX_ENC_LEGACY;
++ }
++
++ return ieee80211_calc_rx_airtime(hw, &stat, len);
++}
++
++u32 ieee80211_calc_tx_airtime(struct ieee80211_hw *hw,
++ struct ieee80211_tx_info *info,
++ int len)
++{
++ u32 duration = 0;
++ int i;
++
++ for (i = 0; i < ARRAY_SIZE(info->status.rates); i++) {
++ struct ieee80211_tx_rate *rate = &info->status.rates[i];
++ u32 cur_duration;
++
++ cur_duration = ieee80211_calc_tx_airtime_rate(hw, rate,
++ info->band, len);
++ if (!cur_duration)
++ break;
++
++ duration += cur_duration * rate->count;
++ }
++
++ return duration;
++}
++EXPORT_SYMBOL_GPL(ieee80211_calc_tx_airtime);
++
++u32 ieee80211_calc_expected_tx_airtime(struct ieee80211_hw *hw,
++ struct ieee80211_vif *vif,
++ struct ieee80211_sta *pubsta,
++ int len)
++{
++ struct ieee80211_supported_band *sband;
++ struct ieee80211_chanctx_conf *conf;
++ int rateidx, shift = 0;
++ bool cck, short_pream;
++ u32 basic_rates;
++ u8 band = 0;
++ u16 rate;
++
++ len += 38; /* Ethernet header length */
++
++ conf = rcu_dereference(vif->chanctx_conf);
++ if (conf) {
++ band = conf->def.chan->band;
++ shift = ieee80211_chandef_get_shift(&conf->def);
++ }
++
++ if (pubsta) {
++ struct sta_info *sta = container_of(pubsta, struct sta_info,
++ sta);
++
++ return ieee80211_calc_tx_airtime_rate(hw,
++ &sta->tx_stats.last_rate,
++ band, len);
++ }
++
++ if (!conf)
++ return 0;
++
++ /* No station to get latest rate from, so calculate the worst-case
++ * duration using the lowest configured basic rate.
++ */
++ sband = hw->wiphy->bands[band];
++
++ basic_rates = vif->bss_conf.basic_rates;
++ short_pream = vif->bss_conf.use_short_preamble;
++
++ rateidx = basic_rates ? ffs(basic_rates) - 1 : 0;
++ rate = sband->bitrates[rateidx].bitrate << shift;
++ cck = sband->bitrates[rateidx].flags & IEEE80211_RATE_MANDATORY_B;
++
++ return ieee80211_calc_legacy_rate_duration(rate, short_pream, cck, len);
++}
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -2253,6 +2253,10 @@ const char *ieee80211_get_reason_code_st
+
+ extern const struct ethtool_ops ieee80211_ethtool_ops;
+
++u32 ieee80211_calc_expected_tx_airtime(struct ieee80211_hw *hw,
++ struct ieee80211_vif *vif,
++ struct ieee80211_sta *pubsta,
++ int len);
+ #ifdef CPTCFG_MAC80211_NOINLINE
+ #define debug_noinline noinline
+ #else
diff --git a/package/kernel/mac80211/patches/subsys/310-mac80211-Implement-Airtime-based-Queue-Limit-AQL.patch b/package/kernel/mac80211/patches/subsys/310-mac80211-Implement-Airtime-based-Queue-Limit-AQL.patch
new file mode 100644
index 0000000000..4eb5eb63a2
--- /dev/null
+++ b/package/kernel/mac80211/patches/subsys/310-mac80211-Implement-Airtime-based-Queue-Limit-AQL.patch
@@ -0,0 +1,446 @@
+From: Kan Yan <kyan@google.com>
+Date: Mon, 18 Nov 2019 22:06:09 -0800
+Subject: [PATCH] mac80211: Implement Airtime-based Queue Limit (AQL)
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+In order for the Fq_CoDel algorithm integrated in mac80211 layer to operate
+effectively to control excessive queueing latency, the CoDel algorithm
+requires an accurate measure of how long packets stays in the queue, AKA
+sojourn time. The sojourn time measured at the mac80211 layer doesn't
+include queueing latency in the lower layer (firmware/hardware) and CoDel
+expects lower layer to have a short queue. However, most 802.11ac chipsets
+offload tasks such TX aggregation to firmware or hardware, thus have a deep
+lower layer queue.
+
+Without a mechanism to control the lower layer queue size, packets only
+stay in mac80211 layer transiently before being sent to firmware queue.
+As a result, the sojourn time measured by CoDel in the mac80211 layer is
+almost always lower than the CoDel latency target, hence CoDel does little
+to control the latency, even when the lower layer queue causes excessive
+latency.
+
+The Byte Queue Limits (BQL) mechanism is commonly used to address the
+similar issue with wired network interface. However, this method cannot be
+applied directly to the wireless network interface. "Bytes" is not a
+suitable measure of queue depth in the wireless network, as the data rate
+can vary dramatically from station to station in the same network, from a
+few Mbps to over Gbps.
+
+This patch implements an Airtime-based Queue Limit (AQL) to make CoDel work
+effectively with wireless drivers that utilized firmware/hardware
+offloading. AQL allows each txq to release just enough packets to the lower
+layer to form 1-2 large aggregations to keep hardware fully utilized and
+retains the rest of the frames in mac80211 layer to be controlled by the
+CoDel algorithm.
+
+Signed-off-by: Kan Yan <kyan@google.com>
+[ Toke: Keep API to set pending airtime internal, fix nits in commit msg ]
+Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
+Link: https://lore.kernel.org/r/20191119060610.76681-4-kyan@google.com
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -2603,6 +2603,13 @@ enum wiphy_params_flags {
+
+ #define IEEE80211_DEFAULT_AIRTIME_WEIGHT 256
+
++/* The per TXQ device queue limit in airtime */
++#define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_L 5000
++#define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_H 12000
++
++/* The per interface airtime threshold to switch to lower queue limit */
++#define IEEE80211_AQL_THRESHOLD 24000
++
+ /**
+ * struct cfg80211_pmksa - PMK Security Association
+ *
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -5559,6 +5559,18 @@ void ieee80211_sta_register_airtime(stru
+ u32 tx_airtime, u32 rx_airtime);
+
+ /**
++ * ieee80211_txq_airtime_check - check if a txq can send frame to device
++ *
++ * @hw: pointer obtained from ieee80211_alloc_hw()
++ * @txq: pointer obtained from station or virtual interface
++ *
++ * Return true if the AQL's airtime limit has not been reached and the txq can
++ * continue to send more packets to the device. Otherwise return false.
++ */
++bool
++ieee80211_txq_airtime_check(struct ieee80211_hw *hw, struct ieee80211_txq *txq);
++
++/**
+ * ieee80211_iter_keys - iterate keys programmed into the device
+ * @hw: pointer obtained from ieee80211_alloc_hw()
+ * @vif: virtual interface to iterate, may be %NULL for all
+--- a/net/mac80211/debugfs.c
++++ b/net/mac80211/debugfs.c
+@@ -148,6 +148,87 @@ static const struct file_operations aqm_
+ .llseek = default_llseek,
+ };
+
++static ssize_t aql_txq_limit_read(struct file *file,
++ char __user *user_buf,
++ size_t count,
++ loff_t *ppos)
++{
++ struct ieee80211_local *local = file->private_data;
++ char buf[400];
++ int len = 0;
++
++ len = scnprintf(buf, sizeof(buf),
++ "AC AQL limit low AQL limit high\n"
++ "VO %u %u\n"
++ "VI %u %u\n"
++ "BE %u %u\n"
++ "BK %u %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],
++ local->aql_txq_limit_high[IEEE80211_AC_VI],
++ 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]);
++ return simple_read_from_buffer(user_buf, count, ppos,
++ buf, len);
++}
++
++static ssize_t aql_txq_limit_write(struct file *file,
++ const char __user *user_buf,
++ size_t count,
++ loff_t *ppos)
++{
++ struct ieee80211_local *local = file->private_data;
++ char buf[100];
++ size_t len;
++ u32 ac, q_limit_low, q_limit_high, q_limit_low_old, q_limit_high_old;
++ struct sta_info *sta;
++
++ if (count > sizeof(buf))
++ return -EINVAL;
++
++ if (copy_from_user(buf, user_buf, count))
++ return -EFAULT;
++
++ buf[sizeof(buf) - 1] = 0;
++ len = strlen(buf);
++ if (len > 0 && buf[len - 1] == '\n')
++ buf[len - 1] = 0;
++
++ if (sscanf(buf, "%u %u %u", &ac, &q_limit_low, &q_limit_high) != 3)
++ return -EINVAL;
++
++ if (ac >= IEEE80211_NUM_ACS)
++ return -EINVAL;
++
++ q_limit_low_old = local->aql_txq_limit_low[ac];
++ q_limit_high_old = local->aql_txq_limit_high[ac];
++
++ local->aql_txq_limit_low[ac] = q_limit_low;
++ local->aql_txq_limit_high[ac] = q_limit_high;
++
++ mutex_lock(&local->sta_mtx);
++ list_for_each_entry(sta, &local->sta_list, list) {
++ /* If a sta has customized queue limits, keep it */
++ if (sta->airtime[ac].aql_limit_low == q_limit_low_old &&
++ sta->airtime[ac].aql_limit_high == q_limit_high_old) {
++ sta->airtime[ac].aql_limit_low = q_limit_low;
++ sta->airtime[ac].aql_limit_high = q_limit_high;
++ }
++ }
++ mutex_unlock(&local->sta_mtx);
++ return count;
++}
++
++static const struct file_operations aql_txq_limit_ops = {
++ .write = aql_txq_limit_write,
++ .read = aql_txq_limit_read,
++ .open = simple_open,
++ .llseek = default_llseek,
++};
++
+ static ssize_t force_tx_status_read(struct file *file,
+ char __user *user_buf,
+ size_t count,
+@@ -441,6 +522,10 @@ void debugfs_hw_add(struct ieee80211_loc
+ debugfs_create_u16("airtime_flags", 0600,
+ phyd, &local->airtime_flags);
+
++ DEBUGFS_ADD(aql_txq_limit);
++ debugfs_create_u32("aql_threshold", 0600,
++ phyd, &local->aql_threshold);
++
+ statsd = debugfs_create_dir("statistics", phyd);
+
+ /* if the dir failed, don't put all the other things into the root! */
+--- a/net/mac80211/debugfs_sta.c
++++ b/net/mac80211/debugfs_sta.c
+@@ -197,10 +197,12 @@ static ssize_t sta_airtime_read(struct f
+ {
+ struct sta_info *sta = file->private_data;
+ struct ieee80211_local *local = sta->sdata->local;
+- size_t bufsz = 200;
++ size_t bufsz = 400;
+ char *buf = kzalloc(bufsz, GFP_KERNEL), *p = buf;
+ u64 rx_airtime = 0, tx_airtime = 0;
+ s64 deficit[IEEE80211_NUM_ACS];
++ u32 q_depth[IEEE80211_NUM_ACS];
++ u32 q_limit_l[IEEE80211_NUM_ACS], q_limit_h[IEEE80211_NUM_ACS];
+ ssize_t rv;
+ int ac;
+
+@@ -212,19 +214,22 @@ static ssize_t sta_airtime_read(struct f
+ rx_airtime += sta->airtime[ac].rx_airtime;
+ tx_airtime += sta->airtime[ac].tx_airtime;
+ deficit[ac] = sta->airtime[ac].deficit;
++ q_limit_l[ac] = sta->airtime[ac].aql_limit_low;
++ q_limit_h[ac] = sta->airtime[ac].aql_limit_high;
+ spin_unlock_bh(&local->active_txq_lock[ac]);
++ q_depth[ac] = atomic_read(&sta->airtime[ac].aql_tx_pending);
+ }
+
+ p += scnprintf(p, bufsz + buf - p,
+ "RX: %llu us\nTX: %llu us\nWeight: %u\n"
+- "Deficit: VO: %lld us VI: %lld us BE: %lld us BK: %lld us\n",
+- rx_airtime,
+- tx_airtime,
+- sta->airtime_weight,
+- deficit[0],
+- deficit[1],
+- deficit[2],
+- deficit[3]);
++ "Deficit: VO: %lld us VI: %lld us BE: %lld us BK: %lld us\n"
++ "Q depth: VO: %u us VI: %u us BE: %u us BK: %u us\n"
++ "Q limit[low/high]: VO: %u/%u VI: %u/%u BE: %u/%u BK: %u/%u\n",
++ rx_airtime, tx_airtime, sta->airtime_weight,
++ deficit[0], deficit[1], deficit[2], deficit[3],
++ q_depth[0], q_depth[1], q_depth[2], q_depth[3],
++ q_limit_l[0], q_limit_h[0], q_limit_l[1], q_limit_h[1],
++ q_limit_l[2], q_limit_h[2], q_limit_l[3], q_limit_h[3]),
+
+ rv = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
+ kfree(buf);
+@@ -236,7 +241,25 @@ static ssize_t sta_airtime_write(struct
+ {
+ struct sta_info *sta = file->private_data;
+ struct ieee80211_local *local = sta->sdata->local;
+- int ac;
++ u32 ac, q_limit_l, q_limit_h;
++ char _buf[100] = {}, *buf = _buf;
++
++ if (count > sizeof(_buf))
++ return -EINVAL;
++
++ if (copy_from_user(buf, userbuf, count))
++ return -EFAULT;
++
++ buf[sizeof(_buf) - 1] = '\0';
++ if (sscanf(buf, "queue limit %u %u %u", &ac, &q_limit_l, &q_limit_h)
++ != 3)
++ return -EINVAL;
++
++ if (ac >= IEEE80211_NUM_ACS)
++ return -EINVAL;
++
++ sta->airtime[ac].aql_limit_low = q_limit_l;
++ sta->airtime[ac].aql_limit_high = q_limit_h;
+
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+ spin_lock_bh(&local->active_txq_lock[ac]);
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -1142,6 +1142,10 @@ struct ieee80211_local {
+ u16 schedule_round[IEEE80211_NUM_ACS];
+
+ u16 airtime_flags;
++ 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;
+
+ const struct ieee80211_ops *ops;
+
+--- a/net/mac80211/main.c
++++ b/net/mac80211/main.c
+@@ -669,8 +669,16 @@ struct ieee80211_hw *ieee80211_alloc_hw_
+ for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+ INIT_LIST_HEAD(&local->active_txqs[i]);
+ spin_lock_init(&local->active_txq_lock[i]);
++ local->aql_txq_limit_low[i] = IEEE80211_DEFAULT_AQL_TXQ_LIMIT_L;
++ local->aql_txq_limit_high[i] =
++ IEEE80211_DEFAULT_AQL_TXQ_LIMIT_H;
+ }
+- local->airtime_flags = AIRTIME_USE_TX | AIRTIME_USE_RX;
++
++ local->airtime_flags = AIRTIME_USE_TX |
++ AIRTIME_USE_RX |
++ AIRTIME_USE_AQL;
++ local->aql_threshold = IEEE80211_AQL_THRESHOLD;
++ atomic_set(&local->aql_total_pending_airtime, 0);
+
+ INIT_LIST_HEAD(&local->chanctx_list);
+ mutex_init(&local->chanctx_mtx);
+--- a/net/mac80211/sta_info.c
++++ b/net/mac80211/sta_info.c
+@@ -411,6 +411,9 @@ struct sta_info *sta_info_alloc(struct i
+ skb_queue_head_init(&sta->ps_tx_buf[i]);
+ skb_queue_head_init(&sta->tx_filtered[i]);
+ sta->airtime[i].deficit = sta->airtime_weight;
++ atomic_set(&sta->airtime[i].aql_tx_pending, 0);
++ sta->airtime[i].aql_limit_low = local->aql_txq_limit_low[i];
++ sta->airtime[i].aql_limit_high = local->aql_txq_limit_high[i];
+ }
+
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++)
+@@ -1908,6 +1911,41 @@ void ieee80211_sta_register_airtime(stru
+ }
+ EXPORT_SYMBOL(ieee80211_sta_register_airtime);
+
++void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local,
++ struct sta_info *sta, u8 ac,
++ u16 tx_airtime, bool tx_completed)
++{
++ int tx_pending;
++
++ if (!tx_completed) {
++ if (sta)
++ atomic_add(tx_airtime,
++ &sta->airtime[ac].aql_tx_pending);
++
++ atomic_add(tx_airtime, &local->aql_total_pending_airtime);
++ return;
++ }
++
++ if (sta) {
++ tx_pending = atomic_sub_return(tx_airtime,
++ &sta->airtime[ac].aql_tx_pending);
++ if (WARN_ONCE(tx_pending < 0,
++ "STA %pM AC %d txq pending airtime underflow: %u, %u",
++ sta->addr, ac, tx_pending, tx_airtime))
++ atomic_cmpxchg(&sta->airtime[ac].aql_tx_pending,
++ tx_pending, 0);
++ }
++
++ tx_pending = atomic_sub_return(tx_airtime,
++ &local->aql_total_pending_airtime);
++ 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_pending, 0);
++}
++
+ int sta_info_move_state(struct sta_info *sta,
+ enum ieee80211_sta_state new_state)
+ {
+--- a/net/mac80211/sta_info.h
++++ b/net/mac80211/sta_info.h
+@@ -127,13 +127,21 @@ enum ieee80211_agg_stop_reason {
+ /* Debugfs flags to enable/disable use of RX/TX airtime in scheduler */
+ #define AIRTIME_USE_TX BIT(0)
+ #define AIRTIME_USE_RX BIT(1)
++#define AIRTIME_USE_AQL BIT(2)
+
+ struct airtime_info {
+ u64 rx_airtime;
+ u64 tx_airtime;
+ s64 deficit;
++ atomic_t aql_tx_pending; /* Estimated airtime for frames pending */
++ u32 aql_limit_low;
++ u32 aql_limit_high;
+ };
+
++void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local,
++ struct sta_info *sta, u8 ac,
++ u16 tx_airtime, bool tx_completed);
++
+ struct sta_info;
+
+ /**
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -3667,7 +3667,8 @@ struct ieee80211_txq *ieee80211_next_txq
+ {
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_txq *ret = NULL;
+- struct txq_info *txqi = NULL;
++ struct txq_info *txqi = NULL, *head = NULL;
++ bool found_eligible_txq = false;
+
+ spin_lock_bh(&local->active_txq_lock[ac]);
+
+@@ -3678,13 +3679,30 @@ struct ieee80211_txq *ieee80211_next_txq
+ if (!txqi)
+ goto out;
+
++ if (txqi == head) {
++ if (!found_eligible_txq)
++ goto out;
++ else
++ found_eligible_txq = false;
++ }
++
++ if (!head)
++ head = txqi;
++
+ if (txqi->txq.sta) {
+ struct sta_info *sta = container_of(txqi->txq.sta,
+- struct sta_info, sta);
++ struct sta_info, sta);
++ bool aql_check = ieee80211_txq_airtime_check(hw, &txqi->txq);
++ s64 deficit = sta->airtime[txqi->txq.ac].deficit;
+
+- if (sta->airtime[txqi->txq.ac].deficit < 0) {
++ if (aql_check)
++ found_eligible_txq = true;
++
++ if (deficit < 0)
+ sta->airtime[txqi->txq.ac].deficit +=
+ sta->airtime_weight;
++
++ if (deficit < 0 || !aql_check) {
+ list_move_tail(&txqi->schedule_order,
+ &local->active_txqs[txqi->txq.ac]);
+ goto begin;
+@@ -3738,6 +3756,33 @@ void __ieee80211_schedule_txq(struct iee
+ }
+ EXPORT_SYMBOL(__ieee80211_schedule_txq);
+
++bool ieee80211_txq_airtime_check(struct ieee80211_hw *hw,
++ struct ieee80211_txq *txq)
++{
++ struct sta_info *sta;
++ struct ieee80211_local *local = hw_to_local(hw);
++
++ if (!(local->airtime_flags & AIRTIME_USE_AQL))
++ return true;
++
++ if (!txq->sta)
++ return true;
++
++ 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)
++ 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)
++ return true;
++
++ return false;
++}
++EXPORT_SYMBOL(ieee80211_txq_airtime_check);
++
+ bool ieee80211_txq_may_transmit(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq)
+ {
diff --git a/package/kernel/mac80211/patches/subsys/311-mac80211-Use-Airtime-based-Queue-Limits-AQL-on-packe.patch b/package/kernel/mac80211/patches/subsys/311-mac80211-Use-Airtime-based-Queue-Limits-AQL-on-packe.patch
new file mode 100644
index 0000000000..3069d624f5
--- /dev/null
+++ b/package/kernel/mac80211/patches/subsys/311-mac80211-Use-Airtime-based-Queue-Limits-AQL-on-packe.patch
@@ -0,0 +1,146 @@
+From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= <toke@redhat.com>
+Date: Mon, 18 Nov 2019 22:06:10 -0800
+Subject: [PATCH] mac80211: Use Airtime-based Queue Limits (AQL) on packet
+ dequeue
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The previous commit added the ability to throttle stations when they queue
+too much airtime in the hardware. This commit enables the functionality by
+calculating the expected airtime usage of each packet that is dequeued from
+the TXQs in mac80211, and accounting that as pending airtime.
+
+The estimated airtime for each skb is stored in the tx_info, so we can
+subtract the same amount from the running total when the skb is freed or
+recycled. The throttling mechanism relies on this accounting to be
+accurate (i.e., that we are not freeing skbs without subtracting any
+airtime they were accounted for), so we put the subtraction into
+ieee80211_report_used_skb(). As an optimisation, we also subtract the
+airtime on regular TX completion, zeroing out the value stored in the
+packet afterwards, to avoid having to do an expensive lookup of the station
+from the packet data on every packet.
+
+This patch does *not* include any mechanism to wake a throttled TXQ again,
+on the assumption that this will happen anyway as a side effect of whatever
+freed the skb (most commonly a TX completion).
+
+Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
+Link: https://lore.kernel.org/r/20191119060610.76681-5-kyan@google.com
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -1060,6 +1060,22 @@ struct ieee80211_tx_info {
+ };
+ };
+
++static inline u16
++ieee80211_info_set_tx_time_est(struct ieee80211_tx_info *info, u16 tx_time_est)
++{
++ /* We only have 10 bits in tx_time_est, so store airtime
++ * in increments of 4us and clamp the maximum to 2**12-1
++ */
++ info->tx_time_est = min_t(u16, tx_time_est, 4095) >> 2;
++ return info->tx_time_est << 2;
++}
++
++static inline u16
++ieee80211_info_get_tx_time_est(struct ieee80211_tx_info *info)
++{
++ return info->tx_time_est << 2;
++}
++
+ /**
+ * struct ieee80211_tx_status - extended tx staus info for rate control
+ *
+--- a/net/mac80211/status.c
++++ b/net/mac80211/status.c
+@@ -670,12 +670,26 @@ static void ieee80211_report_used_skb(st
+ struct sk_buff *skb, bool dropped)
+ {
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
++ u16 tx_time_est = ieee80211_info_get_tx_time_est(info);
+ struct ieee80211_hdr *hdr = (void *)skb->data;
+ bool acked = info->flags & IEEE80211_TX_STAT_ACK;
+
+ if (dropped)
+ acked = false;
+
++ if (tx_time_est) {
++ struct sta_info *sta;
++
++ rcu_read_lock();
++
++ sta = sta_info_get_by_addrs(local, hdr->addr1, hdr->addr2);
++ ieee80211_sta_update_pending_airtime(local, sta,
++ skb_get_queue_mapping(skb),
++ tx_time_est,
++ true);
++ rcu_read_unlock();
++ }
++
+ if (info->flags & IEEE80211_TX_INTFL_MLME_CONN_TX) {
+ struct ieee80211_sub_if_data *sdata;
+
+@@ -885,6 +899,7 @@ static void __ieee80211_tx_status(struct
+ struct ieee80211_bar *bar;
+ int shift = 0;
+ int tid = IEEE80211_NUM_TIDS;
++ u16 tx_time_est;
+
+ rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count);
+
+@@ -996,6 +1011,17 @@ static void __ieee80211_tx_status(struct
+ ieee80211_sta_register_airtime(&sta->sta, tid,
+ info->status.tx_time, 0);
+
++ if ((tx_time_est = ieee80211_info_get_tx_time_est(info)) > 0) {
++ /* Do this here to avoid the expensive lookup of the sta
++ * in ieee80211_report_used_skb().
++ */
++ ieee80211_sta_update_pending_airtime(local, sta,
++ skb_get_queue_mapping(skb),
++ tx_time_est,
++ true);
++ ieee80211_info_set_tx_time_est(info, 0);
++ }
++
+ if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
+ if (acked) {
+ if (sta->status_stats.lost_packets)
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -3544,6 +3544,9 @@ struct sk_buff *ieee80211_tx_dequeue(str
+
+ WARN_ON_ONCE(softirq_count() == 0);
+
++ if (!ieee80211_txq_airtime_check(hw, txq))
++ return NULL;
++
+ begin:
+ spin_lock_bh(&fq->lock);
+
+@@ -3654,6 +3657,21 @@ begin:
+ }
+
+ IEEE80211_SKB_CB(skb)->control.vif = vif;
++
++ if (local->airtime_flags & AIRTIME_USE_AQL) {
++ u32 airtime;
++
++ airtime = ieee80211_calc_expected_tx_airtime(hw, vif, txq->sta,
++ skb->len);
++ if (airtime) {
++ airtime = ieee80211_info_set_tx_time_est(info, airtime);
++ ieee80211_sta_update_pending_airtime(local, tx.sta,
++ txq->ac,
++ airtime,
++ false);
++ }
++ }
++
+ return skb;
+
+ out:
diff --git a/package/kernel/mac80211/patches/subsys/312-mac80211-airtime-Fix-an-off-by-one-in-ieee80211_calc.patch b/package/kernel/mac80211/patches/subsys/312-mac80211-airtime-Fix-an-off-by-one-in-ieee80211_calc.patch
new file mode 100644
index 0000000000..4ff496c12e
--- /dev/null
+++ b/package/kernel/mac80211/patches/subsys/312-mac80211-airtime-Fix-an-off-by-one-in-ieee80211_calc.patch
@@ -0,0 +1,31 @@
+From: Dan Carpenter <dan.carpenter@oracle.com>
+Date: Tue, 26 Nov 2019 15:09:39 +0300
+Subject: [PATCH] mac80211: airtime: Fix an off by one in
+ ieee80211_calc_rx_airtime()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This code was copied from mt76 and inherited an off by one bug from
+there. The > should be >= so that we don't read one element beyond
+the end of the array.
+
+Fixes: db3e1c40cf2f ("mac80211: Import airtime calculation code from mt76")
+Reported-by: Toke Høiland-Jørgensen <toke@redhat.com>
+Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
+Acked-by: Toke Høiland-Jørgensen <toke@redhat.com>
+Link: https://lore.kernel.org/r/20191126120910.ftr4t7me3by32aiz@kili.mountain
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/net/mac80211/airtime.c
++++ b/net/mac80211/airtime.c
+@@ -442,7 +442,7 @@ u32 ieee80211_calc_rx_airtime(struct iee
+ return 0;
+
+ sband = hw->wiphy->bands[status->band];
+- if (!sband || status->rate_idx > sband->n_bitrates)
++ if (!sband || status->rate_idx >= sband->n_bitrates)
+ return 0;
+
+ rate = &sband->bitrates[status->rate_idx];
diff --git a/package/kernel/mac80211/patches/subsys/313-mac80211-Turn-AQL-into-an-NL80211_EXT_FEATURE.patch b/package/kernel/mac80211/patches/subsys/313-mac80211-Turn-AQL-into-an-NL80211_EXT_FEATURE.patch
new file mode 100644
index 0000000000..3803b37922
--- /dev/null
+++ b/package/kernel/mac80211/patches/subsys/313-mac80211-Turn-AQL-into-an-NL80211_EXT_FEATURE.patch
@@ -0,0 +1,253 @@
+From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= <toke@redhat.com>
+Date: Thu, 12 Dec 2019 12:14:37 +0100
+Subject: [PATCH] mac80211: Turn AQL into an NL80211_EXT_FEATURE
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Instead of just having an airtime flag in debugfs, turn AQL into a proper
+NL80211_EXT_FEATURE, so drivers can turn it on when they are ready, and so
+we also expose the presence of the feature to userspace.
+
+This also has the effect of flipping the default, so drivers have to opt in
+to using AQL instead of getting it by default with TXQs. To keep
+functionality the same as pre-patch, we set this feature for ath10k (which
+is where it is needed the most).
+
+While we're at it, split out the debugfs interface so AQL gets its own
+per-station debugfs file instead of using the 'airtime' file.
+
+[Johannes:]
+This effectively disables AQL for iwlwifi, where it fixes a number of
+issues:
+ * TSO in iwlwifi is causing underflows and associated warnings in AQL
+ * HE (802.11ax) rates aren't reported properly so at HE rates, AQL could
+ never have a valid estimate (it'd use 6 Mbps instead of up to 2400!)
+
+Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
+Link: https://lore.kernel.org/r/20191212111437.224294-1-toke@redhat.com
+Fixes: 3ace10f5b5ad ("mac80211: Implement Airtime-based Queue Limit (AQL)")
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/drivers/net/wireless/ath/ath10k/mac.c
++++ b/drivers/net/wireless/ath/ath10k/mac.c
+@@ -8868,6 +8868,7 @@ int ath10k_mac_register(struct ath10k *a
+ wiphy_ext_feature_set(ar->hw->wiphy, NL80211_EXT_FEATURE_VHT_IBSS);
+ wiphy_ext_feature_set(ar->hw->wiphy,
+ NL80211_EXT_FEATURE_SET_SCAN_DWELL);
++ wiphy_ext_feature_set(ar->hw->wiphy, NL80211_EXT_FEATURE_AQL);
+
+ if (test_bit(WMI_SERVICE_TX_DATA_ACK_RSSI, ar->wmi.svc_map) ||
+ test_bit(WMI_SERVICE_HTT_MGMT_TX_COMP_VALID_FLAGS, ar->wmi.svc_map))
+--- a/include/uapi/linux/nl80211.h
++++ b/include/uapi/linux/nl80211.h
+@@ -5484,6 +5484,10 @@ enum nl80211_feature_flags {
+ * @NL80211_EXT_FEATURE_SAE_OFFLOAD: Device wants to do SAE authentication in
+ * station mode (SAE password is passed as part of the connect command).
+ *
++ * @NL80211_EXT_FEATURE_AQL: The driver supports the Airtime Queue Limit (AQL)
++ * feature, which prevents bufferbloat by using the expected transmission
++ * time to limit the amount of data buffered in the hardware.
++ *
+ * @NUM_NL80211_EXT_FEATURES: number of extended features.
+ * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
+ */
+@@ -5529,6 +5533,8 @@ enum nl80211_ext_feature_index {
+ NL80211_EXT_FEATURE_EXT_KEY_ID,
+ NL80211_EXT_FEATURE_STA_TX_PWR,
+ NL80211_EXT_FEATURE_SAE_OFFLOAD,
++ NL80211_EXT_FEATURE_VLAN_OFFLOAD,
++ NL80211_EXT_FEATURE_AQL,
+
+ /* add new features before the definition below */
+ NUM_NL80211_EXT_FEATURES,
+--- a/net/mac80211/debugfs_sta.c
++++ b/net/mac80211/debugfs_sta.c
+@@ -201,8 +201,6 @@ static ssize_t sta_airtime_read(struct f
+ char *buf = kzalloc(bufsz, GFP_KERNEL), *p = buf;
+ u64 rx_airtime = 0, tx_airtime = 0;
+ s64 deficit[IEEE80211_NUM_ACS];
+- u32 q_depth[IEEE80211_NUM_ACS];
+- u32 q_limit_l[IEEE80211_NUM_ACS], q_limit_h[IEEE80211_NUM_ACS];
+ ssize_t rv;
+ int ac;
+
+@@ -214,6 +212,56 @@ static ssize_t sta_airtime_read(struct f
+ rx_airtime += sta->airtime[ac].rx_airtime;
+ tx_airtime += sta->airtime[ac].tx_airtime;
+ deficit[ac] = sta->airtime[ac].deficit;
++ spin_unlock_bh(&local->active_txq_lock[ac]);
++ }
++
++ p += scnprintf(p, bufsz + buf - p,
++ "RX: %llu us\nTX: %llu us\nWeight: %u\n"
++ "Deficit: VO: %lld us VI: %lld us BE: %lld us BK: %lld us\n",
++ rx_airtime, tx_airtime, sta->airtime_weight,
++ deficit[0], deficit[1], deficit[2], deficit[3]);
++
++ rv = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
++ kfree(buf);
++ return rv;
++}
++
++static ssize_t sta_airtime_write(struct file *file, const char __user *userbuf,
++ size_t count, loff_t *ppos)
++{
++ struct sta_info *sta = file->private_data;
++ struct ieee80211_local *local = sta->sdata->local;
++ int ac;
++
++ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
++ spin_lock_bh(&local->active_txq_lock[ac]);
++ sta->airtime[ac].rx_airtime = 0;
++ sta->airtime[ac].tx_airtime = 0;
++ sta->airtime[ac].deficit = sta->airtime_weight;
++ spin_unlock_bh(&local->active_txq_lock[ac]);
++ }
++
++ return count;
++}
++STA_OPS_RW(airtime);
++
++static ssize_t sta_aql_read(struct file *file, char __user *userbuf,
++ size_t count, loff_t *ppos)
++{
++ struct sta_info *sta = file->private_data;
++ struct ieee80211_local *local = sta->sdata->local;
++ size_t bufsz = 400;
++ char *buf = kzalloc(bufsz, GFP_KERNEL), *p = buf;
++ u32 q_depth[IEEE80211_NUM_ACS];
++ u32 q_limit_l[IEEE80211_NUM_ACS], q_limit_h[IEEE80211_NUM_ACS];
++ ssize_t rv;
++ int ac;
++
++ if (!buf)
++ return -ENOMEM;
++
++ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
++ spin_lock_bh(&local->active_txq_lock[ac]);
+ q_limit_l[ac] = sta->airtime[ac].aql_limit_low;
+ q_limit_h[ac] = sta->airtime[ac].aql_limit_high;
+ spin_unlock_bh(&local->active_txq_lock[ac]);
+@@ -221,12 +269,8 @@ static ssize_t sta_airtime_read(struct f
+ }
+
+ p += scnprintf(p, bufsz + buf - p,
+- "RX: %llu us\nTX: %llu us\nWeight: %u\n"
+- "Deficit: VO: %lld us VI: %lld us BE: %lld us BK: %lld us\n"
+ "Q depth: VO: %u us VI: %u us BE: %u us BK: %u us\n"
+ "Q limit[low/high]: VO: %u/%u VI: %u/%u BE: %u/%u BK: %u/%u\n",
+- rx_airtime, tx_airtime, sta->airtime_weight,
+- deficit[0], deficit[1], deficit[2], deficit[3],
+ q_depth[0], q_depth[1], q_depth[2], q_depth[3],
+ q_limit_l[0], q_limit_h[0], q_limit_l[1], q_limit_h[1],
+ q_limit_l[2], q_limit_h[2], q_limit_l[3], q_limit_h[3]),
+@@ -236,11 +280,10 @@ static ssize_t sta_airtime_read(struct f
+ return rv;
+ }
+
+-static ssize_t sta_airtime_write(struct file *file, const char __user *userbuf,
++static ssize_t sta_aql_write(struct file *file, const char __user *userbuf,
+ size_t count, loff_t *ppos)
+ {
+ struct sta_info *sta = file->private_data;
+- struct ieee80211_local *local = sta->sdata->local;
+ u32 ac, q_limit_l, q_limit_h;
+ char _buf[100] = {}, *buf = _buf;
+
+@@ -251,7 +294,7 @@ static ssize_t sta_airtime_write(struct
+ return -EFAULT;
+
+ buf[sizeof(_buf) - 1] = '\0';
+- if (sscanf(buf, "queue limit %u %u %u", &ac, &q_limit_l, &q_limit_h)
++ if (sscanf(buf, "limit %u %u %u", &ac, &q_limit_l, &q_limit_h)
+ != 3)
+ return -EINVAL;
+
+@@ -261,17 +304,10 @@ static ssize_t sta_airtime_write(struct
+ sta->airtime[ac].aql_limit_low = q_limit_l;
+ sta->airtime[ac].aql_limit_high = q_limit_h;
+
+- for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+- spin_lock_bh(&local->active_txq_lock[ac]);
+- sta->airtime[ac].rx_airtime = 0;
+- sta->airtime[ac].tx_airtime = 0;
+- sta->airtime[ac].deficit = sta->airtime_weight;
+- spin_unlock_bh(&local->active_txq_lock[ac]);
+- }
+-
+ return count;
+ }
+-STA_OPS_RW(airtime);
++STA_OPS_RW(aql);
++
+
+ static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+@@ -1001,6 +1037,10 @@ void ieee80211_sta_debugfs_add(struct st
+ NL80211_EXT_FEATURE_AIRTIME_FAIRNESS))
+ DEBUGFS_ADD(airtime);
+
++ if (wiphy_ext_feature_isset(local->hw.wiphy,
++ NL80211_EXT_FEATURE_AQL))
++ DEBUGFS_ADD(aql);
++
+ if (sizeof(sta->driver_buffered_tids) == sizeof(u32))
+ debugfs_create_x32("driver_buffered_tids", 0400,
+ sta->debugfs_dir,
+--- a/net/mac80211/main.c
++++ b/net/mac80211/main.c
+@@ -674,9 +674,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_
+ IEEE80211_DEFAULT_AQL_TXQ_LIMIT_H;
+ }
+
+- local->airtime_flags = AIRTIME_USE_TX |
+- AIRTIME_USE_RX |
+- AIRTIME_USE_AQL;
++ local->airtime_flags = AIRTIME_USE_TX | AIRTIME_USE_RX;
+ local->aql_threshold = IEEE80211_AQL_THRESHOLD;
+ atomic_set(&local->aql_total_pending_airtime, 0);
+
+--- a/net/mac80211/sta_info.c
++++ b/net/mac80211/sta_info.c
+@@ -1917,6 +1917,9 @@ void ieee80211_sta_update_pending_airtim
+ {
+ int tx_pending;
+
++ if (!wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL))
++ return;
++
+ if (!tx_completed) {
+ if (sta)
+ atomic_add(tx_airtime,
+--- a/net/mac80211/sta_info.h
++++ b/net/mac80211/sta_info.h
+@@ -127,7 +127,6 @@ enum ieee80211_agg_stop_reason {
+ /* Debugfs flags to enable/disable use of RX/TX airtime in scheduler */
+ #define AIRTIME_USE_TX BIT(0)
+ #define AIRTIME_USE_RX BIT(1)
+-#define AIRTIME_USE_AQL BIT(2)
+
+ struct airtime_info {
+ u64 rx_airtime;
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -3658,7 +3658,7 @@ begin:
+
+ IEEE80211_SKB_CB(skb)->control.vif = vif;
+
+- if (local->airtime_flags & AIRTIME_USE_AQL) {
++ if (wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) {
+ u32 airtime;
+
+ airtime = ieee80211_calc_expected_tx_airtime(hw, vif, txq->sta,
+@@ -3780,7 +3780,7 @@ bool ieee80211_txq_airtime_check(struct
+ struct sta_info *sta;
+ struct ieee80211_local *local = hw_to_local(hw);
+
+- if (!(local->airtime_flags & AIRTIME_USE_AQL))
++ if (!wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL))
+ return true;
+
+ if (!txq->sta)
diff --git a/package/kernel/mac80211/patches/subsys/353-mac80211-use-more-bits-for-ack_frame_id.patch b/package/kernel/mac80211/patches/subsys/353-mac80211-use-more-bits-for-ack_frame_id.patch
new file mode 100644
index 0000000000..ea9168e9be
--- /dev/null
+++ b/package/kernel/mac80211/patches/subsys/353-mac80211-use-more-bits-for-ack_frame_id.patch
@@ -0,0 +1,70 @@
+From: Johannes Berg <johannes.berg@intel.com>
+Date: Wed, 15 Jan 2020 12:25:50 +0100
+Subject: [PATCH] mac80211: use more bits for ack_frame_id
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+It turns out that this wasn't a good idea, I hit a test failure in
+hwsim due to this. That particular failure was easily worked around,
+but it raised questions: if an AP needs to, for example, send action
+frames to each connected station, the current limit is nowhere near
+enough (especially if those stations are sleeping and the frames are
+queued for a while.)
+
+Shuffle around some bits to make more room for ack_frame_id to allow
+up to 8192 queued up frames, that's enough for queueing 4 frames to
+each connected station, even at the maximum of 2007 stations on a
+single AP.
+
+We take the bits from band (which currently only 2 but I leave 3 in
+case we add another band) and from the hw_queue, which can only need
+4 since it has a limit of 16 queues.
+
+Fixes: 6912daed05e1 ("mac80211: Shrink the size of ack_frame_id to make room for tx_time_est")
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Acked-by: Toke Høiland-Jørgensen <toke@redhat.com>
+---
+
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -1004,12 +1004,11 @@ ieee80211_rate_get_vht_nss(const struct
+ struct ieee80211_tx_info {
+ /* common information */
+ u32 flags;
+- u8 band;
+-
+- u8 hw_queue;
+-
+- u16 ack_frame_id:6;
+- u16 tx_time_est:10;
++ u32 band:3,
++ ack_frame_id:13,
++ hw_queue:4,
++ tx_time_est:10;
++ /* 2 free bits */
+
+ union {
+ struct {
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -3427,7 +3427,7 @@ int ieee80211_attach_ack_skb(struct ieee
+
+ spin_lock_irqsave(&local->ack_status_lock, spin_flags);
+ id = idr_alloc(&local->ack_status_frames, ack_skb,
+- 1, 0x40, GFP_ATOMIC);
++ 1, 0x2000, GFP_ATOMIC);
+ spin_unlock_irqrestore(&local->ack_status_lock, spin_flags);
+
+ if (id < 0) {
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -2443,7 +2443,7 @@ static int ieee80211_store_ack_skb(struc
+
+ spin_lock_irqsave(&local->ack_status_lock, flags);
+ id = idr_alloc(&local->ack_status_frames, ack_skb,
+- 1, 0x40, GFP_ATOMIC);
++ 1, 0x2000, GFP_ATOMIC);
+ spin_unlock_irqrestore(&local->ack_status_lock, flags);
+
+ if (id >= 0) {
diff --git a/package/kernel/mac80211/patches/subsys/500-mac80211_configure_antenna_gain.patch b/package/kernel/mac80211/patches/subsys/500-mac80211_configure_antenna_gain.patch
index 768cca6046..dcdfe20dc8 100644
--- a/package/kernel/mac80211/patches/subsys/500-mac80211_configure_antenna_gain.patch
+++ b/package/kernel/mac80211/patches/subsys/500-mac80211_configure_antenna_gain.patch
@@ -1,6 +1,6 @@
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
-@@ -3447,6 +3447,7 @@ struct cfg80211_update_owe_info {
+@@ -3454,6 +3454,7 @@ struct cfg80211_update_owe_info {
* (as advertised by the nl80211 feature flag.)
* @get_tx_power: store the current TX power into the dbm variable;
* return 0 if successful
@@ -8,7 +8,7 @@
*
* @set_wds_peer: set the WDS peer for a WDS interface
*
-@@ -3759,6 +3760,7 @@ struct cfg80211_ops {
+@@ -3766,6 +3767,7 @@ struct cfg80211_ops {
enum nl80211_tx_power_setting type, int mbm);
int (*get_tx_power)(struct wiphy *wiphy, struct wireless_dev *wdev,
int *dbm);
@@ -18,7 +18,7 @@
const u8 *addr);
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
-@@ -1484,6 +1484,7 @@ enum ieee80211_smps_mode {
+@@ -1501,6 +1501,7 @@ enum ieee80211_smps_mode {
*
* @power_level: requested transmit power (in dBm), backward compatibility
* value only that is set to the minimum of all interfaces
@@ -26,7 +26,7 @@
*
* @chandef: the channel definition to tune to
* @radar_enabled: whether radar detection is enabled
-@@ -1504,6 +1505,7 @@ enum ieee80211_smps_mode {
+@@ -1521,6 +1522,7 @@ enum ieee80211_smps_mode {
struct ieee80211_conf {
u32 flags;
int power_level, dynamic_ps_timeout;
@@ -87,7 +87,7 @@
CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd)
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
-@@ -1372,6 +1372,7 @@ struct ieee80211_local {
+@@ -1376,6 +1376,7 @@ struct ieee80211_local {
int dynamic_ps_forced_timeout;
int user_power_level; /* in dBm, for all interfaces */