mac80211: backport airtime queue limits support
authorFelix Fietkau <nbd@nbd.name>
Sat, 18 Jan 2020 17:44:00 +0000 (18:44 +0100)
committerFelix Fietkau <nbd@nbd.name>
Wed, 29 Jan 2020 11:24:57 +0000 (12:24 +0100)
Signed-off-by: Felix Fietkau <nbd@nbd.name>
package/kernel/mac80211/patches/subsys/306-mac80211-move-store-skb-ack-code-to-its-own-function.patch [new file with mode: 0644]
package/kernel/mac80211/patches/subsys/307-mac80211-Shrink-the-size-of-ack_frame_id-to-make-roo.patch [new file with mode: 0644]
package/kernel/mac80211/patches/subsys/308-mac80211-Add-new-sta_info-getter-by-sta-vif-addrs.patch [new file with mode: 0644]
package/kernel/mac80211/patches/subsys/309-mac80211-Import-airtime-calculation-code-from-mt76.patch [new file with mode: 0644]
package/kernel/mac80211/patches/subsys/310-mac80211-Implement-Airtime-based-Queue-Limit-AQL.patch [new file with mode: 0644]
package/kernel/mac80211/patches/subsys/311-mac80211-Use-Airtime-based-Queue-Limits-AQL-on-packe.patch [new file with mode: 0644]
package/kernel/mac80211/patches/subsys/312-mac80211-airtime-Fix-an-off-by-one-in-ieee80211_calc.patch [new file with mode: 0644]
package/kernel/mac80211/patches/subsys/313-mac80211-Turn-AQL-into-an-NL80211_EXT_FEATURE.patch [new file with mode: 0644]
package/kernel/mac80211/patches/subsys/353-mac80211-use-more-bits-for-ack_frame_id.patch [new file with mode: 0644]
package/kernel/mac80211/patches/subsys/500-mac80211_configure_antenna_gain.patch

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 (file)
index 0000000..3289aae
--- /dev/null
@@ -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 (file)
index 0000000..8b3e666
--- /dev/null
@@ -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 (file)
index 0000000..2c4b978
--- /dev/null
@@ -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 (file)
index 0000000..7bfe299
--- /dev/null
@@ -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 (file)
index 0000000..4eb5eb6
--- /dev/null
@@ -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 (file)
index 0000000..3069d62
--- /dev/null
@@ -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 (file)
index 0000000..4ff496c
--- /dev/null
@@ -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 (file)
index 0000000..3803b37
--- /dev/null
@@ -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 (file)
index 0000000..ea9168e
--- /dev/null
@@ -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) {
index 768cca6046422990fbf1564dde04fda826a7cafa..dcdfe20dc850e0f6df2548e5d3d58af46dc2fdd7 100644 (file)
@@ -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 */