The removed patches were applied upstream.
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
PKG_NAME:=mac80211
-PKG_VERSION:=5.13.19-1
+PKG_VERSION:=5.14.13-1
PKG_RELEASE:=1
-PKG_SOURCE_URL:=@KERNEL/linux/kernel/projects/backports/stable/v5.13.19/
-PKG_HASH:=1eb761c1664d59a0a2d52847f240c9d02fc2e56fb27d862d2949bfb275187eef
+PKG_SOURCE_URL:=@KERNEL/linux/kernel/projects/backports/stable/v5.14.13/
+PKG_HASH:=042aef20caf17ef649502d5f2e744a7676abe7faed18de83c96f37bc029635fe
PKG_SOURCE:=backports-$(PKG_VERSION).tar.xz
PKG_BUILD_DIR:=$(KERNEL_BUILD_DIR)/backports-$(PKG_VERSION)
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
-@@ -2961,6 +2961,10 @@ brcmf_cfg80211_set_power_mgmt(struct wip
+@@ -2966,6 +2966,10 @@ brcmf_cfg80211_set_power_mgmt(struct wip
* preference in cfg struct to apply this to
* FW later while initializing the dongle
*/
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c
-@@ -12,6 +12,36 @@
- #include "common.h"
- #include "of.h"
+@@ -58,6 +58,36 @@ static int brcmf_of_get_country_codes(st
+ return 0;
+ }
+/* TODO: FIXME: Use DT */
+static void brcmf_of_probe_cc(struct device *dev,
void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type,
struct brcmf_mp_device *settings)
{
-@@ -43,6 +73,8 @@ void brcmf_of_probe(struct device *dev,
+@@ -90,6 +120,8 @@ void brcmf_of_probe(struct device *dev,
of_node_put(root);
}
+ brcmf_of_probe_cc(dev, settings);
+
- if (!np || bus_type != BRCMF_BUSTYPE_SDIO ||
- !of_device_is_compatible(np, "brcm,bcm4329-fmac"))
+ if (!np || !of_device_is_compatible(np, "brcm,bcm4329-fmac"))
return;
+
brcmf_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *ndev,
int idx, u8 *mac, struct station_info *sinfo)
{
-@@ -3008,6 +3065,7 @@ static s32 brcmf_inform_single_bss(struc
+@@ -3013,6 +3070,7 @@ static s32 brcmf_inform_single_bss(struc
struct brcmu_chan ch;
u16 channel;
u32 freq;
u16 notify_capability;
u16 notify_interval;
u8 *notify_ie;
-@@ -3032,6 +3090,17 @@ static s32 brcmf_inform_single_bss(struc
+@@ -3037,6 +3095,17 @@ static s32 brcmf_inform_single_bss(struc
band = NL80211_BAND_5GHZ;
freq = ieee80211_channel_to_frequency(channel, band);
bss_data.chan = ieee80211_get_channel(wiphy, freq);
bss_data.scan_width = NL80211_BSS_CHAN_WIDTH_20;
bss_data.boottime_ns = ktime_to_ns(ktime_get_boottime());
-@@ -5560,6 +5629,7 @@ static struct cfg80211_ops brcmf_cfg8021
+@@ -5565,6 +5634,7 @@ static struct cfg80211_ops brcmf_cfg8021
.leave_ibss = brcmf_cfg80211_leave_ibss,
.get_station = brcmf_cfg80211_get_station,
.dump_station = brcmf_cfg80211_dump_station,
.add_key = brcmf_cfg80211_add_key,
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
-@@ -1356,6 +1356,8 @@ int brcmf_attach(struct device *dev)
+@@ -1361,6 +1361,8 @@ int brcmf_attach(struct device *dev)
/* Link to bus module */
drvr->hdrlen = 0;
/* Attach and link in the protocol */
ret = brcmf_proto_attach(drvr);
-@@ -1438,6 +1440,12 @@ void brcmf_detach(struct device *dev)
+@@ -1443,6 +1445,12 @@ void brcmf_detach(struct device *dev)
if (drvr == NULL)
return;
static int ieee80211_ifa6_changed(struct notifier_block *nb,
unsigned long data, void *arg)
{
-@@ -1319,14 +1319,14 @@ int ieee80211_register_hw(struct ieee802
+@@ -1324,14 +1324,14 @@ int ieee80211_register_hw(struct ieee802
wiphy_unlock(hw->wiphy);
rtnl_unlock();
local->ifa6_notifier.notifier_call = ieee80211_ifa6_changed;
result = register_inet6addr_notifier(&local->ifa6_notifier);
if (result)
-@@ -1335,13 +1335,13 @@ int ieee80211_register_hw(struct ieee802
+@@ -1340,13 +1340,13 @@ int ieee80211_register_hw(struct ieee802
return 0;
fail_ifa:
#endif
wiphy_unregister(local->hw.wiphy);
-@@ -1369,10 +1369,10 @@ void ieee80211_unregister_hw(struct ieee
+@@ -1374,10 +1374,10 @@ void ieee80211_unregister_hw(struct ieee
tasklet_kill(&local->tx_pending_tasklet);
tasklet_kill(&local->tasklet);
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
-@@ -2462,7 +2462,7 @@ static int ieee80211_scan(struct wiphy *
+@@ -2487,7 +2487,7 @@ static int ieee80211_scan(struct wiphy *
* the frames sent while scanning on other channel will be
* lost)
*/
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
-@@ -2990,15 +2990,19 @@ static void mac80211_hwsim_he_capab(stru
+@@ -2992,15 +2992,19 @@ static void mac80211_hwsim_he_capab(stru
{
u16 n_iftype_data;
return;
}
-@@ -3288,6 +3292,12 @@ static int mac80211_hwsim_new_radio(stru
+@@ -3290,6 +3294,12 @@ static int mac80211_hwsim_new_radio(stru
sband->vht_cap.vht_mcs.tx_mcs_map =
sband->vht_cap.vht_mcs.rx_mcs_map;
break;
case NL80211_BAND_S1GHZ:
memcpy(&sband->s1g_cap, &hwsim_s1g_cap,
sizeof(sband->s1g_cap));
-@@ -3298,6 +3308,13 @@ static int mac80211_hwsim_new_radio(stru
+@@ -3300,6 +3310,13 @@ static int mac80211_hwsim_new_radio(stru
continue;
}
sband->ht_cap.ht_supported = true;
sband->ht_cap.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
IEEE80211_HT_CAP_GRN_FLD |
-@@ -3311,10 +3328,6 @@ static int mac80211_hwsim_new_radio(stru
+@@ -3313,10 +3330,6 @@ static int mac80211_hwsim_new_radio(stru
sband->ht_cap.mcs.rx_mask[0] = 0xff;
sband->ht_cap.mcs.rx_mask[1] = 0xff;
sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
}
}
-@@ -1206,7 +1169,7 @@ minstrel_ht_tx_status(void *priv, struct
+@@ -1183,7 +1146,7 @@ minstrel_ht_tx_status(void *priv, struct
struct ieee80211_tx_info *info = st->info;
struct minstrel_ht_sta *mi = priv_sta;
struct ieee80211_tx_rate *ar = info->status.rates;
struct minstrel_priv *mp = priv;
u32 update_interval = mp->update_interval;
bool last, update = false;
-@@ -1252,18 +1215,13 @@ minstrel_ht_tx_status(void *priv, struct
+@@ -1233,18 +1196,13 @@ minstrel_ht_tx_status(void *priv, struct
/*
* check for sudden death of spatial multiplexing,
* downgrade to a lower number of streams if necessary.
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Thu, 17 Jun 2021 17:56:54 +0200
-Subject: [PATCH] mac80211: move A-MPDU session check from minstrel_ht to
- mac80211
-
-This avoids calling back into tx handlers from within the rate control module.
-Preparation for deferring rate control until tx dequeue
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -6192,6 +6192,11 @@ enum rate_control_capabilities {
- * otherwise the NSS difference doesn't bother us.
- */
- RATE_CTRL_CAPA_VHT_EXT_NSS_BW = BIT(0),
-+ /**
-+ * @RATE_CTRL_CAPA_AMPDU_TRIGGER:
-+ * mac80211 should start A-MPDU sessions on tx
-+ */
-+ RATE_CTRL_CAPA_AMPDU_TRIGGER = BIT(1),
- };
-
- struct rate_control_ops {
---- a/net/mac80211/rc80211_minstrel_ht.c
-+++ b/net/mac80211/rc80211_minstrel_ht.c
-@@ -1140,29 +1140,6 @@ minstrel_downgrade_prob_rate(struct mins
- }
-
- static void
--minstrel_aggr_check(struct ieee80211_sta *pubsta, struct sk_buff *skb)
--{
-- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
-- struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
-- u16 tid;
--
-- if (skb_get_queue_mapping(skb) == IEEE80211_AC_VO)
-- return;
--
-- if (unlikely(!ieee80211_is_data_qos(hdr->frame_control)))
-- return;
--
-- if (unlikely(skb->protocol == cpu_to_be16(ETH_P_PAE)))
-- return;
--
-- tid = ieee80211_get_tid(hdr);
-- if (likely(sta->ampdu_mlme.tid_tx[tid]))
-- return;
--
-- ieee80211_start_tx_ba_session(pubsta, tid, 0);
--}
--
--static void
- minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
- void *priv_sta, struct ieee80211_tx_status *st)
- {
-@@ -1457,10 +1434,6 @@ minstrel_ht_get_rate(void *priv, struct
- struct minstrel_priv *mp = priv;
- u16 sample_idx;
-
-- if (!(info->flags & IEEE80211_TX_CTL_AMPDU) &&
-- !minstrel_ht_is_legacy_group(MI_RATE_GROUP(mi->max_prob_rate)))
-- minstrel_aggr_check(sta, txrc->skb);
--
- info->flags |= mi->tx_flags;
-
- #ifdef CPTCFG_MAC80211_DEBUGFS
-@@ -1866,6 +1839,7 @@ static u32 minstrel_ht_get_expected_thro
-
- static const struct rate_control_ops mac80211_minstrel_ht = {
- .name = "minstrel_ht",
-+ .capa = RATE_CTRL_CAPA_AMPDU_TRIGGER,
- .tx_status_ext = minstrel_ht_tx_status,
- .get_rate = minstrel_ht_get_rate,
- .rate_init = minstrel_ht_rate_init,
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -3951,6 +3951,29 @@ void ieee80211_txq_schedule_start(struct
- }
- EXPORT_SYMBOL(ieee80211_txq_schedule_start);
-
-+static void
-+ieee80211_aggr_check(struct ieee80211_sub_if_data *sdata,
-+ struct sta_info *sta,
-+ struct sk_buff *skb)
-+{
-+ struct rate_control_ref *ref = sdata->local->rate_ctrl;
-+ u16 tid;
-+
-+ if (!ref || !(ref->ops->capa & RATE_CTRL_CAPA_AMPDU_TRIGGER))
-+ return;
-+
-+ if (!sta || !sta->sta.ht_cap.ht_supported ||
-+ !sta->sta.wme || skb_get_queue_mapping(skb) == IEEE80211_AC_VO ||
-+ skb->protocol == sdata->control_port_protocol)
-+ return;
-+
-+ tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
-+ if (likely(sta->ampdu_mlme.tid_tx[tid]))
-+ return;
-+
-+ ieee80211_start_tx_ba_session(&sta->sta, tid, 0);
-+}
-+
- void __ieee80211_subif_start_xmit(struct sk_buff *skb,
- struct net_device *dev,
- u32 info_flags,
-@@ -3981,6 +4004,8 @@ void __ieee80211_subif_start_xmit(struct
- skb_get_hash(skb);
- }
-
-+ ieee80211_aggr_check(sdata, sta, skb);
-+
- if (sta) {
- struct ieee80211_fast_tx *fast_tx;
-
-@@ -4244,6 +4269,8 @@ static void ieee80211_8023_xmit(struct i
-
- memset(info, 0, sizeof(*info));
-
-+ ieee80211_aggr_check(sdata, sta, skb);
-+
- tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
- tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]);
- if (tid_tx) {
+++ /dev/null
-From: Ryder Lee <ryder.lee@mediatek.com>
-Date: Fri, 28 May 2021 14:05:41 +0800
-Subject: [PATCH] mac80211: call ieee80211_tx_h_rate_ctrl() when dequeue
-
-Make ieee80211_tx_h_rate_ctrl() get called on dequeue to improve
-performance since it reduces the turnaround time for rate control.
-
-Signed-off-by: Ryder Lee <ryder.lee@mediatek.com>
----
-
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -1768,8 +1768,6 @@ static int invoke_tx_handlers_early(stru
- CALL_TXH(ieee80211_tx_h_ps_buf);
- CALL_TXH(ieee80211_tx_h_check_control_port_protocol);
- CALL_TXH(ieee80211_tx_h_select_key);
-- if (!ieee80211_hw_check(&tx->local->hw, HAS_RATE_CONTROL))
-- CALL_TXH(ieee80211_tx_h_rate_ctrl);
-
- txh_done:
- if (unlikely(res == TX_DROP)) {
-@@ -1802,6 +1800,9 @@ static int invoke_tx_handlers_late(struc
- goto txh_done;
- }
-
-+ if (!ieee80211_hw_check(&tx->local->hw, HAS_RATE_CONTROL))
-+ CALL_TXH(ieee80211_tx_h_rate_ctrl);
-+
- CALL_TXH(ieee80211_tx_h_michael_mic_add);
- CALL_TXH(ieee80211_tx_h_sequence);
- CALL_TXH(ieee80211_tx_h_fragment);
-@@ -3391,15 +3392,21 @@ out:
- * Can be called while the sta lock is held. Anything that can cause packets to
- * be generated will cause deadlock!
- */
--static void ieee80211_xmit_fast_finish(struct ieee80211_sub_if_data *sdata,
-- struct sta_info *sta, u8 pn_offs,
-- struct ieee80211_key *key,
-- struct sk_buff *skb)
-+static ieee80211_tx_result
-+ieee80211_xmit_fast_finish(struct ieee80211_sub_if_data *sdata,
-+ struct sta_info *sta, u8 pn_offs,
-+ struct ieee80211_key *key,
-+ struct ieee80211_tx_data *tx)
- {
-+ struct sk_buff *skb = tx->skb;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- struct ieee80211_hdr *hdr = (void *)skb->data;
- u8 tid = IEEE80211_NUM_TIDS;
-
-+ if (!ieee80211_hw_check(&tx->local->hw, HAS_RATE_CONTROL) &&
-+ ieee80211_tx_h_rate_ctrl(tx) != TX_CONTINUE)
-+ return TX_DROP;
-+
- if (key)
- info->control.hw_key = &key->conf;
-
-@@ -3448,6 +3455,8 @@ static void ieee80211_xmit_fast_finish(s
- break;
- }
- }
-+
-+ return TX_CONTINUE;
- }
-
- static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
-@@ -3551,24 +3560,17 @@ static bool ieee80211_xmit_fast(struct i
- tx.sta = sta;
- tx.key = fast_tx->key;
-
-- if (!ieee80211_hw_check(&local->hw, HAS_RATE_CONTROL)) {
-- tx.skb = skb;
-- r = ieee80211_tx_h_rate_ctrl(&tx);
-- skb = tx.skb;
-- tx.skb = NULL;
--
-- if (r != TX_CONTINUE) {
-- if (r != TX_QUEUED)
-- kfree_skb(skb);
-- return true;
-- }
-- }
--
- if (ieee80211_queue_skb(local, sdata, sta, skb))
- return true;
-
-- ieee80211_xmit_fast_finish(sdata, sta, fast_tx->pn_offs,
-- fast_tx->key, skb);
-+ tx.skb = skb;
-+ r = ieee80211_xmit_fast_finish(sdata, sta, fast_tx->pn_offs,
-+ fast_tx->key, &tx);
-+ tx.skb = NULL;
-+ if (r == TX_DROP) {
-+ kfree_skb(skb);
-+ return true;
-+ }
-
- if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
- sdata = container_of(sdata->bss,
-@@ -3685,8 +3687,12 @@ begin:
- (tx.key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV))
- pn_offs = ieee80211_hdrlen(hdr->frame_control);
-
-- ieee80211_xmit_fast_finish(sta->sdata, sta, pn_offs,
-- tx.key, skb);
-+ r = ieee80211_xmit_fast_finish(sta->sdata, sta, pn_offs,
-+ tx.key, &tx);
-+ if (r != TX_CONTINUE) {
-+ ieee80211_free_txskb(&local->hw, skb);
-+ goto begin;
-+ }
- } else {
- if (invoke_tx_handlers_late(&tx))
- goto begin;
+++ /dev/null
-From: Ryder Lee <ryder.lee@mediatek.com>
-Date: Fri, 28 May 2021 14:05:43 +0800
-Subject: [PATCH] mac80211: add rate control support for encap offload
-
-The software rate control cannot deal with encap offload, so fix it.
-
-Signed-off-by: Ryder Lee <ryder.lee@mediatek.com>
----
-
---- a/net/mac80211/rate.c
-+++ b/net/mac80211/rate.c
-@@ -297,15 +297,11 @@ void ieee80211_check_rate_mask(struct ie
- static bool rc_no_data_or_no_ack_use_min(struct ieee80211_tx_rate_control *txrc)
- {
- struct sk_buff *skb = txrc->skb;
-- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-- __le16 fc;
--
-- fc = hdr->frame_control;
-
- return (info->flags & (IEEE80211_TX_CTL_NO_ACK |
- IEEE80211_TX_CTL_USE_MINRATE)) ||
-- !ieee80211_is_data(fc);
-+ !ieee80211_is_tx_data(skb);
- }
-
- static void rc_send_low_basicrate(struct ieee80211_tx_rate *rate,
-@@ -870,7 +866,6 @@ void ieee80211_get_tx_rates(struct ieee8
- int max_rates)
- {
- struct ieee80211_sub_if_data *sdata;
-- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- struct ieee80211_supported_band *sband;
-
-@@ -882,7 +877,7 @@ void ieee80211_get_tx_rates(struct ieee8
- sdata = vif_to_sdata(vif);
- sband = sdata->local->hw.wiphy->bands[info->band];
-
-- if (ieee80211_is_data(hdr->frame_control))
-+ if (ieee80211_is_tx_data(skb))
- rate_control_apply_mask(sdata, sta, sband, dest, max_rates);
-
- if (dest[0].idx < 0)
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -666,6 +666,7 @@ ieee80211_tx_h_rate_ctrl(struct ieee8021
- u32 len;
- struct ieee80211_tx_rate_control txrc;
- struct ieee80211_sta_rates *ratetbl = NULL;
-+ bool encap = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP;
- bool assoc = false;
-
- memset(&txrc, 0, sizeof(txrc));
-@@ -707,7 +708,7 @@ ieee80211_tx_h_rate_ctrl(struct ieee8021
- * just wants a probe response.
- */
- if (tx->sdata->vif.bss_conf.use_short_preamble &&
-- (ieee80211_is_data(hdr->frame_control) ||
-+ (ieee80211_is_tx_data(tx->skb) ||
- (tx->sta && test_sta_flag(tx->sta, WLAN_STA_SHORT_PREAMBLE))))
- txrc.short_preamble = true;
-
-@@ -729,7 +730,8 @@ ieee80211_tx_h_rate_ctrl(struct ieee8021
- "%s: Dropped data frame as no usable bitrate found while "
- "scanning and associated. Target station: "
- "%pM on %d GHz band\n",
-- tx->sdata->name, hdr->addr1,
-+ tx->sdata->name,
-+ encap ? ((struct ethhdr *)hdr)->h_dest : hdr->addr1,
- info->band ? 5 : 2))
- return TX_DROP;
-
-@@ -763,7 +765,7 @@ ieee80211_tx_h_rate_ctrl(struct ieee8021
-
- if (txrc.reported_rate.idx < 0) {
- txrc.reported_rate = tx->rate;
-- if (tx->sta && ieee80211_is_data(hdr->frame_control))
-+ if (tx->sta && ieee80211_is_tx_data(tx->skb))
- tx->sta->tx_stats.last_rate = txrc.reported_rate;
- } else if (tx->sta)
- tx->sta->tx_stats.last_rate = txrc.reported_rate;
-@@ -3675,8 +3677,16 @@ begin:
- else
- info->flags &= ~IEEE80211_TX_CTL_AMPDU;
-
-- if (info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP)
-+ if (info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) {
-+ if (!ieee80211_hw_check(&local->hw, HAS_RATE_CONTROL)) {
-+ r = ieee80211_tx_h_rate_ctrl(&tx);
-+ if (r != TX_CONTINUE) {
-+ ieee80211_free_txskb(&local->hw, skb);
-+ goto begin;
-+ }
-+ }
- goto encap_out;
-+ }
-
- if (info->control.flags & IEEE80211_TX_CTRL_FAST_XMIT) {
- struct sta_info *sta = container_of(txq->sta, struct sta_info,
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -6765,4 +6765,22 @@ struct sk_buff *ieee80211_get_fils_disco
- struct sk_buff *
- ieee80211_get_unsol_bcast_probe_resp_tmpl(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif);
-+
-+/**
-+ * ieee80211_is_tx_data - check if frame is a data frame
-+ *
-+ * The function is used to check if a frame is a data frame. Frames with
-+ * hardware encapsulation enabled are data frames.
-+ *
-+ * @skb: the frame to be transmitted.
-+ */
-+static inline bool ieee80211_is_tx_data(struct sk_buff *skb)
-+{
-+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-+ struct ieee80211_hdr *hdr = (void *) skb->data;
-+
-+ return info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP ||
-+ ieee80211_is_data(hdr->frame_control);
-+}
-+
- #endif /* MAC80211_H */
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Tue, 29 Jun 2021 13:25:09 +0200
-Subject: [PATCH] mac80211: fix starting aggregation sessions on mesh
- interfaces
-
-The logic for starting aggregation sessions was recently moved from minstrel_ht
-to mac80211, into the subif tx handler just after the sta lookup.
-Unfortunately this didn't work for mesh interfaces, since the sta lookup is
-deferred until a much later point in time on those.
-Fix this by also calling the aggregation check right after the deferred sta
-lookup.
-
-Fixes: 08a46c642001 ("mac80211: move A-MPDU session check from minstrel_ht to mac80211")
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -1146,6 +1146,29 @@ static bool ieee80211_tx_prep_agg(struct
- return queued;
- }
-
-+static void
-+ieee80211_aggr_check(struct ieee80211_sub_if_data *sdata,
-+ struct sta_info *sta,
-+ struct sk_buff *skb)
-+{
-+ struct rate_control_ref *ref = sdata->local->rate_ctrl;
-+ u16 tid;
-+
-+ if (!ref || !(ref->ops->capa & RATE_CTRL_CAPA_AMPDU_TRIGGER))
-+ return;
-+
-+ if (!sta || !sta->sta.ht_cap.ht_supported ||
-+ !sta->sta.wme || skb_get_queue_mapping(skb) == IEEE80211_AC_VO ||
-+ skb->protocol == sdata->control_port_protocol)
-+ return;
-+
-+ tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
-+ if (likely(sta->ampdu_mlme.tid_tx[tid]))
-+ return;
-+
-+ ieee80211_start_tx_ba_session(&sta->sta, tid, 0);
-+}
-+
- /*
- * initialises @tx
- * pass %NULL for the station if unknown, a valid pointer if known
-@@ -1159,6 +1182,7 @@ ieee80211_tx_prepare(struct ieee80211_su
- struct ieee80211_local *local = sdata->local;
- struct ieee80211_hdr *hdr;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-+ bool aggr_check = false;
- int tid;
-
- memset(tx, 0, sizeof(*tx));
-@@ -1187,8 +1211,10 @@ ieee80211_tx_prepare(struct ieee80211_su
- } else if (tx->sdata->control_port_protocol == tx->skb->protocol) {
- tx->sta = sta_info_get_bss(sdata, hdr->addr1);
- }
-- if (!tx->sta && !is_multicast_ether_addr(hdr->addr1))
-+ if (!tx->sta && !is_multicast_ether_addr(hdr->addr1)) {
- tx->sta = sta_info_get(sdata, hdr->addr1);
-+ aggr_check = true;
-+ }
- }
-
- if (tx->sta && ieee80211_is_data_qos(hdr->frame_control) &&
-@@ -1198,8 +1224,12 @@ ieee80211_tx_prepare(struct ieee80211_su
- struct tid_ampdu_tx *tid_tx;
-
- tid = ieee80211_get_tid(hdr);
--
- tid_tx = rcu_dereference(tx->sta->ampdu_mlme.tid_tx[tid]);
-+ if (!tid_tx && aggr_check) {
-+ ieee80211_aggr_check(sdata, tx->sta, skb);
-+ tid_tx = rcu_dereference(tx->sta->ampdu_mlme.tid_tx[tid]);
-+ }
-+
- if (tid_tx) {
- bool queued;
-
-@@ -3967,29 +3997,6 @@ void ieee80211_txq_schedule_start(struct
- }
- EXPORT_SYMBOL(ieee80211_txq_schedule_start);
-
--static void
--ieee80211_aggr_check(struct ieee80211_sub_if_data *sdata,
-- struct sta_info *sta,
-- struct sk_buff *skb)
--{
-- struct rate_control_ref *ref = sdata->local->rate_ctrl;
-- u16 tid;
--
-- if (!ref || !(ref->ops->capa & RATE_CTRL_CAPA_AMPDU_TRIGGER))
-- return;
--
-- if (!sta || !sta->sta.ht_cap.ht_supported ||
-- !sta->sta.wme || skb_get_queue_mapping(skb) == IEEE80211_AC_VO ||
-- skb->protocol == sdata->control_port_protocol)
-- return;
--
-- tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
-- if (likely(sta->ampdu_mlme.tid_tx[tid]))
-- return;
--
-- ieee80211_start_tx_ba_session(&sta->sta, tid, 0);
--}
--
- void __ieee80211_subif_start_xmit(struct sk_buff *skb,
- struct net_device *dev,
- u32 info_flags,
+++ /dev/null
-From: Johannes Berg <johannes.berg@intel.com>
-Date: Fri, 18 Jun 2021 13:41:44 +0300
-Subject: [PATCH] mac80211: rearrange struct txq_info for fewer holes
-
-We can slightly decrease the size of struct txq_info by
-rearranging some fields for fewer holes, so do that.
-
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
-Link: https://lore.kernel.org/r/iwlwifi.20210618133832.1bf019a1fe2e.Ib54622b8d6dc1a9a7dc484e573c073119450538b@changeid
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/net/mac80211/ieee80211_i.h
-+++ b/net/mac80211/ieee80211_i.h
-@@ -5,7 +5,7 @@
- * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
- * Copyright 2007-2010 Johannes Berg <johannes@sipsolutions.net>
- * Copyright 2013-2015 Intel Mobile Communications GmbH
-- * Copyright (C) 2018-2020 Intel Corporation
-+ * Copyright (C) 2018-2021 Intel Corporation
- */
-
- #ifndef IEEE80211_I_H
-@@ -843,9 +843,12 @@ struct txq_info {
- struct fq_tin tin;
- struct codel_vars def_cvars;
- struct codel_stats cstats;
-- struct sk_buff_head frags;
-- struct list_head schedule_order;
-+
- u16 schedule_round;
-+ struct list_head schedule_order;
-+
-+ struct sk_buff_head frags;
-+
- unsigned long flags;
-
- /* keep last! */
+++ /dev/null
-From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= <toke@redhat.com>
-Date: Wed, 23 Jun 2021 15:47:55 +0200
-Subject: [PATCH] mac80211: Switch to a virtual time-based airtime scheduler
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This switches the airtime scheduler in mac80211 to use a virtual
-time-based scheduler instead of the round-robin scheduler used before.
-This has a couple of advantages:
-
-- No need to sync up the round-robin scheduler in firmware/hardware with
- the round-robin airtime scheduler.
-
-- If several stations are eligible for transmission we can schedule both
- of them; no need to hard-block the scheduling rotation until the head
- of the queue has used up its quantum.
-
-- The check of whether a station is eligible for transmission becomes
- simpler (in ieee80211_txq_may_transmit()).
-
-The drawback is that scheduling becomes slightly more expensive, as we
-need to maintain an rbtree of TXQs sorted by virtual time. This means
-that ieee80211_register_airtime() becomes O(logN) in the number of
-currently scheduled TXQs because it can change the order of the
-scheduled stations. We mitigate this overhead by only resorting when a
-station changes position in the tree, and hopefully N rarely grows too
-big (it's only TXQs currently backlogged, not all associated stations),
-so it shouldn't be too big of an issue.
-
-To prevent divisions in the fast path, we maintain both station sums and
-pre-computed reciprocals of the sums. This turns the fast-path operation
-into a multiplication, with divisions only happening as the number of
-active stations change (to re-compute the current sum of all active
-station weights). To prevent this re-computation of the reciprocal from
-happening too frequently, we use a time-based notion of station
-activity, instead of updating the weight every time a station gets
-scheduled or de-scheduled. As queues can oscillate between empty and
-occupied quite frequently, this can significantly cut down on the number
-of re-computations. It also has the added benefit of making the station
-airtime calculation independent on whether the queue happened to have
-drained at the time an airtime value was accounted.
-
-Co-developed-by: Yibo Zhao <yiboz@codeaurora.org>
-Signed-off-by: Yibo Zhao <yiboz@codeaurora.org>
-Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
-Link: https://lore.kernel.org/r/20210623134755.235545-1-toke@redhat.com
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -6589,9 +6589,6 @@ static inline void ieee80211_txq_schedul
- {
- }
-
--void __ieee80211_schedule_txq(struct ieee80211_hw *hw,
-- struct ieee80211_txq *txq, bool force);
--
- /**
- * ieee80211_schedule_txq - schedule a TXQ for transmission
- *
-@@ -6604,11 +6601,7 @@ void __ieee80211_schedule_txq(struct iee
- * The driver may call this function if it has buffered packets for
- * this TXQ internally.
- */
--static inline void
--ieee80211_schedule_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
--{
-- __ieee80211_schedule_txq(hw, txq, true);
--}
-+void ieee80211_schedule_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq);
-
- /**
- * ieee80211_return_txq - return a TXQ previously acquired by ieee80211_next_txq()
-@@ -6620,12 +6613,8 @@ ieee80211_schedule_txq(struct ieee80211_
- * The driver may set force=true if it has buffered packets for this TXQ
- * internally.
- */
--static inline void
--ieee80211_return_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq,
-- bool force)
--{
-- __ieee80211_schedule_txq(hw, txq, force);
--}
-+void ieee80211_return_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq,
-+ bool force);
-
- /**
- * ieee80211_txq_may_transmit - check whether TXQ is allowed to transmit
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -1460,6 +1460,38 @@ static void sta_apply_mesh_params(struct
- #endif
- }
-
-+static void sta_apply_airtime_params(struct ieee80211_local *local,
-+ struct sta_info *sta,
-+ struct station_parameters *params)
-+{
-+ u8 ac;
-+
-+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
-+ struct airtime_sched_info *air_sched = &local->airtime[ac];
-+ struct airtime_info *air_info = &sta->airtime[ac];
-+ struct txq_info *txqi;
-+ u8 tid;
-+
-+ spin_lock_bh(&air_sched->lock);
-+ for (tid = 0; tid < IEEE80211_NUM_TIDS + 1; tid++) {
-+ if (air_info->weight == params->airtime_weight ||
-+ !sta->sta.txq[tid] ||
-+ ac != ieee80211_ac_from_tid(tid))
-+ continue;
-+
-+ airtime_weight_set(air_info, params->airtime_weight);
-+
-+ txqi = to_txq_info(sta->sta.txq[tid]);
-+ if (RB_EMPTY_NODE(&txqi->schedule_order))
-+ continue;
-+
-+ ieee80211_update_airtime_weight(local, air_sched,
-+ 0, true);
-+ }
-+ spin_unlock_bh(&air_sched->lock);
-+ }
-+}
-+
- static int sta_apply_parameters(struct ieee80211_local *local,
- struct sta_info *sta,
- struct station_parameters *params)
-@@ -1647,7 +1679,8 @@ static int sta_apply_parameters(struct i
- sta_apply_mesh_params(local, sta, params);
-
- if (params->airtime_weight)
-- sta->airtime_weight = params->airtime_weight;
-+ sta_apply_airtime_params(local, sta, params);
-+
-
- /* set the STA state after all sta info from usermode has been set */
- if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) ||
---- a/net/mac80211/debugfs.c
-+++ b/net/mac80211/debugfs.c
-@@ -216,14 +216,14 @@ static ssize_t aql_txq_limit_read(struct
- "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]);
-+ local->airtime[IEEE80211_AC_VO].aql_txq_limit_low,
-+ local->airtime[IEEE80211_AC_VO].aql_txq_limit_high,
-+ local->airtime[IEEE80211_AC_VI].aql_txq_limit_low,
-+ local->airtime[IEEE80211_AC_VI].aql_txq_limit_high,
-+ local->airtime[IEEE80211_AC_BE].aql_txq_limit_low,
-+ local->airtime[IEEE80211_AC_BE].aql_txq_limit_high,
-+ local->airtime[IEEE80211_AC_BK].aql_txq_limit_low,
-+ local->airtime[IEEE80211_AC_BK].aql_txq_limit_high);
- return simple_read_from_buffer(user_buf, count, ppos,
- buf, len);
- }
-@@ -255,11 +255,11 @@ static ssize_t aql_txq_limit_write(struc
- 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];
-+ q_limit_low_old = local->airtime[ac].aql_txq_limit_low;
-+ q_limit_high_old = local->airtime[ac].aql_txq_limit_high;
-
-- local->aql_txq_limit_low[ac] = q_limit_low;
-- local->aql_txq_limit_high[ac] = q_limit_high;
-+ local->airtime[ac].aql_txq_limit_low = q_limit_low;
-+ local->airtime[ac].aql_txq_limit_high = q_limit_high;
-
- mutex_lock(&local->sta_mtx);
- list_for_each_entry(sta, &local->sta_list, list) {
-@@ -382,6 +382,46 @@ static const struct file_operations forc
- .llseek = default_llseek,
- };
-
-+static ssize_t airtime_read(struct file *file,
-+ char __user *user_buf,
-+ size_t count,
-+ loff_t *ppos)
-+{
-+ struct ieee80211_local *local = file->private_data;
-+ char buf[200];
-+ u64 v_t[IEEE80211_NUM_ACS];
-+ u64 wt[IEEE80211_NUM_ACS];
-+ int len = 0, ac;
-+
-+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
-+ spin_lock_bh(&local->airtime[ac].lock);
-+ v_t[ac] = local->airtime[ac].v_t;
-+ wt[ac] = local->airtime[ac].weight_sum;
-+ spin_unlock_bh(&local->airtime[ac].lock);
-+ }
-+ len = scnprintf(buf, sizeof(buf),
-+ "\tVO VI BE BK\n"
-+ "Virt-t\t%-10llu %-10llu %-10llu %-10llu\n"
-+ "Weight\t%-10llu %-10llu %-10llu %-10llu\n",
-+ v_t[0],
-+ v_t[1],
-+ v_t[2],
-+ v_t[3],
-+ wt[0],
-+ wt[1],
-+ wt[2],
-+ wt[3]);
-+
-+ return simple_read_from_buffer(user_buf, count, ppos,
-+ buf, len);
-+}
-+
-+static const struct file_operations airtime_ops = {
-+ .read = airtime_read,
-+ .open = simple_open,
-+ .llseek = default_llseek,
-+};
-+
- #ifdef CONFIG_PM
- static ssize_t reset_write(struct file *file, const char __user *user_buf,
- size_t count, loff_t *ppos)
-@@ -632,7 +672,11 @@ void debugfs_hw_add(struct ieee80211_loc
- if (local->ops->wake_tx_queue)
- DEBUGFS_ADD_MODE(aqm, 0600);
-
-- DEBUGFS_ADD_MODE(airtime_flags, 0600);
-+ if (wiphy_ext_feature_isset(local->hw.wiphy,
-+ NL80211_EXT_FEATURE_AIRTIME_FAIRNESS)) {
-+ DEBUGFS_ADD_MODE(airtime, 0600);
-+ DEBUGFS_ADD_MODE(airtime_flags, 0600);
-+ }
-
- DEBUGFS_ADD(aql_txq_limit);
- debugfs_create_u32("aql_threshold", 0600,
---- a/net/mac80211/debugfs_netdev.c
-+++ b/net/mac80211/debugfs_netdev.c
-@@ -513,6 +513,34 @@ static ssize_t ieee80211_if_fmt_aqm(
- }
- IEEE80211_IF_FILE_R(aqm);
-
-+static ssize_t ieee80211_if_fmt_airtime(
-+ const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
-+{
-+ struct ieee80211_local *local = sdata->local;
-+ struct ieee80211_txq *txq = sdata->vif.txq;
-+ struct airtime_info *air_info;
-+ int len;
-+
-+ if (!txq)
-+ return 0;
-+
-+ spin_lock_bh(&local->airtime[txq->ac].lock);
-+ air_info = to_airtime_info(txq);
-+ len = scnprintf(buf,
-+ buflen,
-+ "RX: %llu us\nTX: %llu us\nWeight: %u\n"
-+ "Virt-T: %lld us\n",
-+ air_info->rx_airtime,
-+ air_info->tx_airtime,
-+ air_info->weight,
-+ air_info->v_t);
-+ spin_unlock_bh(&local->airtime[txq->ac].lock);
-+
-+ return len;
-+}
-+
-+IEEE80211_IF_FILE_R(airtime);
-+
- IEEE80211_IF_FILE(multicast_to_unicast, u.ap.multicast_to_unicast, HEX);
-
- /* IBSS attributes */
-@@ -658,8 +686,10 @@ static void add_common_files(struct ieee
-
- if (sdata->local->ops->wake_tx_queue &&
- sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
-- sdata->vif.type != NL80211_IFTYPE_NAN)
-+ sdata->vif.type != NL80211_IFTYPE_NAN) {
- DEBUGFS_ADD(aqm);
-+ DEBUGFS_ADD(airtime);
-+ }
- }
-
- static void add_sta_files(struct ieee80211_sub_if_data *sdata)
---- a/net/mac80211/debugfs_sta.c
-+++ b/net/mac80211/debugfs_sta.c
-@@ -202,7 +202,7 @@ static ssize_t sta_airtime_read(struct f
- size_t bufsz = 400;
- char *buf = kzalloc(bufsz, GFP_KERNEL), *p = buf;
- u64 rx_airtime = 0, tx_airtime = 0;
-- s64 deficit[IEEE80211_NUM_ACS];
-+ u64 v_t[IEEE80211_NUM_ACS];
- ssize_t rv;
- int ac;
-
-@@ -210,18 +210,18 @@ static ssize_t sta_airtime_read(struct f
- return -ENOMEM;
-
- for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
-- spin_lock_bh(&local->active_txq_lock[ac]);
-+ spin_lock_bh(&local->airtime[ac].lock);
- 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]);
-+ v_t[ac] = sta->airtime[ac].v_t;
-+ spin_unlock_bh(&local->airtime[ac].lock);
- }
-
- 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]);
-+ "Virt-T: VO: %lld us VI: %lld us BE: %lld us BK: %lld us\n",
-+ rx_airtime, tx_airtime, sta->airtime[0].weight,
-+ v_t[0], v_t[1], v_t[2], v_t[3]);
-
- rv = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
- kfree(buf);
-@@ -236,11 +236,11 @@ static ssize_t sta_airtime_write(struct
- int ac;
-
- for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
-- spin_lock_bh(&local->active_txq_lock[ac]);
-+ spin_lock_bh(&local->airtime[ac].lock);
- 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]);
-+ sta->airtime[ac].v_t = 0;
-+ spin_unlock_bh(&local->airtime[ac].lock);
- }
-
- return count;
-@@ -263,10 +263,10 @@ static ssize_t sta_aql_read(struct file
- return -ENOMEM;
-
- for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
-- spin_lock_bh(&local->active_txq_lock[ac]);
-+ spin_lock_bh(&local->airtime[ac].lock);
- 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]);
-+ spin_unlock_bh(&local->airtime[ac].lock);
- q_depth[ac] = atomic_read(&sta->airtime[ac].aql_tx_pending);
- }
-
---- a/net/mac80211/ieee80211_i.h
-+++ b/net/mac80211/ieee80211_i.h
-@@ -835,20 +835,16 @@ enum txq_info_flags {
- * @def_flow: used as a fallback flow when a packet destined to @tin hashes to
- * a fq_flow which is already owned by a different tin
- * @def_cvars: codel vars for @def_flow
-- * @frags: used to keep fragments created after dequeue
- * @schedule_order: used with ieee80211_local->active_txqs
-- * @schedule_round: counter to prevent infinite loops on TXQ scheduling
-+ * @frags: used to keep fragments created after dequeue
- */
- struct txq_info {
- struct fq_tin tin;
- struct codel_vars def_cvars;
- struct codel_stats cstats;
--
-- u16 schedule_round;
-- struct list_head schedule_order;
-+ struct rb_node schedule_order;
-
- struct sk_buff_head frags;
--
- unsigned long flags;
-
- /* keep last! */
-@@ -925,6 +921,8 @@ struct ieee80211_sub_if_data {
- struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS];
- struct mac80211_qos_map __rcu *qos_map;
-
-+ struct airtime_info airtime[IEEE80211_NUM_ACS];
-+
- struct work_struct csa_finalize_work;
- bool csa_block_tx; /* write-protected by sdata_lock and local->mtx */
- struct cfg80211_chan_def csa_chandef;
-@@ -1137,6 +1135,44 @@ enum mac80211_scan_state {
- SCAN_ABORT,
- };
-
-+/**
-+ * struct airtime_sched_info - state used for airtime scheduling and AQL
-+ *
-+ * @lock: spinlock that protects all the fields in this struct
-+ * @active_txqs: rbtree of currently backlogged queues, sorted by virtual time
-+ * @schedule_pos: the current position maintained while a driver walks the tree
-+ * with ieee80211_next_txq()
-+ * @active_list: list of struct airtime_info structs that were active within
-+ * the last AIRTIME_ACTIVE_DURATION (100 ms), used to compute
-+ * weight_sum
-+ * @last_weight_update: used for rate limiting walking active_list
-+ * @last_schedule_time: tracks the last time a transmission was scheduled; used
-+ * for catching up v_t if no stations are eligible for
-+ * transmission.
-+ * @v_t: global virtual time; queues with v_t < this are eligible for
-+ * transmission
-+ * @weight_sum: total sum of all active stations used for dividing airtime
-+ * @weight_sum_reciprocal: reciprocal of weight_sum (to avoid divisions in fast
-+ * path - see comment above
-+ * IEEE80211_RECIPROCAL_DIVISOR_64)
-+ * @aql_txq_limit_low: AQL limit when total outstanding airtime
-+ * is < IEEE80211_AQL_THRESHOLD
-+ * @aql_txq_limit_high: AQL limit when total outstanding airtime
-+ * is > IEEE80211_AQL_THRESHOLD
-+ */
-+struct airtime_sched_info {
-+ spinlock_t lock;
-+ struct rb_root_cached active_txqs;
-+ struct rb_node *schedule_pos;
-+ struct list_head active_list;
-+ u64 last_weight_update;
-+ u64 last_schedule_activity;
-+ u64 v_t;
-+ u64 weight_sum;
-+ u64 weight_sum_reciprocal;
-+ u32 aql_txq_limit_low;
-+ u32 aql_txq_limit_high;
-+};
- DECLARE_STATIC_KEY_FALSE(aql_disable);
-
- struct ieee80211_local {
-@@ -1150,13 +1186,8 @@ struct ieee80211_local {
- struct codel_params cparams;
-
- /* protects active_txqs and txqi->schedule_order */
-- spinlock_t active_txq_lock[IEEE80211_NUM_ACS];
-- struct list_head active_txqs[IEEE80211_NUM_ACS];
-- u16 schedule_round[IEEE80211_NUM_ACS];
--
-+ struct airtime_sched_info airtime[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;
-
-@@ -1574,6 +1605,125 @@ static inline bool txq_has_queue(struct
- return !(skb_queue_empty(&txqi->frags) && !txqi->tin.backlog_packets);
- }
-
-+static inline struct airtime_info *to_airtime_info(struct ieee80211_txq *txq)
-+{
-+ struct ieee80211_sub_if_data *sdata;
-+ struct sta_info *sta;
-+
-+ if (txq->sta) {
-+ sta = container_of(txq->sta, struct sta_info, sta);
-+ return &sta->airtime[txq->ac];
-+ }
-+
-+ sdata = vif_to_sdata(txq->vif);
-+ return &sdata->airtime[txq->ac];
-+}
-+
-+/* To avoid divisions in the fast path, we keep pre-computed reciprocals for
-+ * airtime weight calculations. There are two different weights to keep track
-+ * of: The per-station weight and the sum of weights per phy.
-+ *
-+ * For the per-station weights (kept in airtime_info below), we use 32-bit
-+ * reciprocals with a devisor of 2^19. This lets us keep the multiplications and
-+ * divisions for the station weights as 32-bit operations at the cost of a bit
-+ * of rounding error for high weights; but the choice of divisor keeps rounding
-+ * errors <10% for weights <2^15, assuming no more than 8ms of airtime is
-+ * reported at a time.
-+ *
-+ * For the per-phy sum of weights the values can get higher, so we use 64-bit
-+ * operations for those with a 32-bit divisor, which should avoid any
-+ * significant rounding errors.
-+ */
-+#define IEEE80211_RECIPROCAL_DIVISOR_64 0x100000000ULL
-+#define IEEE80211_RECIPROCAL_SHIFT_64 32
-+#define IEEE80211_RECIPROCAL_DIVISOR_32 0x80000U
-+#define IEEE80211_RECIPROCAL_SHIFT_32 19
-+
-+static inline void airtime_weight_set(struct airtime_info *air_info, u16 weight)
-+{
-+ if (air_info->weight == weight)
-+ return;
-+
-+ air_info->weight = weight;
-+ if (weight) {
-+ air_info->weight_reciprocal =
-+ IEEE80211_RECIPROCAL_DIVISOR_32 / weight;
-+ } else {
-+ air_info->weight_reciprocal = 0;
-+ }
-+}
-+
-+static inline void airtime_weight_sum_set(struct airtime_sched_info *air_sched,
-+ int weight_sum)
-+{
-+ if (air_sched->weight_sum == weight_sum)
-+ return;
-+
-+ air_sched->weight_sum = weight_sum;
-+ if (air_sched->weight_sum) {
-+ air_sched->weight_sum_reciprocal = IEEE80211_RECIPROCAL_DIVISOR_64;
-+ do_div(air_sched->weight_sum_reciprocal, air_sched->weight_sum);
-+ } else {
-+ air_sched->weight_sum_reciprocal = 0;
-+ }
-+}
-+
-+/* A problem when trying to enforce airtime fairness is that we want to divide
-+ * the airtime between the currently *active* stations. However, basing this on
-+ * the instantaneous queue state of stations doesn't work, as queues tend to
-+ * oscillate very quickly between empty and occupied, leading to the scheduler
-+ * thinking only a single station is active when deciding whether to allow
-+ * transmission (and thus not throttling correctly).
-+ *
-+ * To fix this we use a timer-based notion of activity: a station is considered
-+ * active if it has been scheduled within the last 100 ms; we keep a separate
-+ * list of all the stations considered active in this manner, and lazily update
-+ * the total weight of active stations from this list (filtering the stations in
-+ * the list by their 'last active' time).
-+ *
-+ * We add one additional safeguard to guard against stations that manage to get
-+ * scheduled every 100 ms but don't transmit a lot of data, and thus don't use
-+ * up any airtime. Such stations would be able to get priority for an extended
-+ * period of time if they do start transmitting at full capacity again, and so
-+ * we add an explicit maximum for how far behind a station is allowed to fall in
-+ * the virtual airtime domain. This limit is set to a relatively high value of
-+ * 20 ms because the main mechanism for catching up idle stations is the active
-+ * state as described above; i.e., the hard limit should only be hit in
-+ * pathological cases.
-+ */
-+#define AIRTIME_ACTIVE_DURATION (100 * NSEC_PER_MSEC)
-+#define AIRTIME_MAX_BEHIND 20000 /* 20 ms */
-+
-+static inline bool airtime_is_active(struct airtime_info *air_info, u64 now)
-+{
-+ return air_info->last_scheduled >= now - AIRTIME_ACTIVE_DURATION;
-+}
-+
-+static inline void airtime_set_active(struct airtime_sched_info *air_sched,
-+ struct airtime_info *air_info, u64 now)
-+{
-+ air_info->last_scheduled = now;
-+ air_sched->last_schedule_activity = now;
-+ list_move_tail(&air_info->list, &air_sched->active_list);
-+}
-+
-+static inline bool airtime_catchup_v_t(struct airtime_sched_info *air_sched,
-+ u64 v_t, u64 now)
-+{
-+ air_sched->v_t = v_t;
-+ return true;
-+}
-+
-+static inline void init_airtime_info(struct airtime_info *air_info,
-+ struct airtime_sched_info *air_sched)
-+{
-+ atomic_set(&air_info->aql_tx_pending, 0);
-+ air_info->aql_limit_low = air_sched->aql_txq_limit_low;
-+ air_info->aql_limit_high = air_sched->aql_txq_limit_high;
-+ airtime_weight_set(air_info, IEEE80211_DEFAULT_AIRTIME_WEIGHT);
-+ INIT_LIST_HEAD(&air_info->list);
-+}
-+
- static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr)
- {
- return ether_addr_equal(raddr, addr) ||
-@@ -1816,6 +1966,14 @@ int ieee80211_tx_control_port(struct wip
- u64 *cookie);
- int ieee80211_probe_mesh_link(struct wiphy *wiphy, struct net_device *dev,
- const u8 *buf, size_t len);
-+void ieee80211_resort_txq(struct ieee80211_hw *hw,
-+ struct ieee80211_txq *txq);
-+void ieee80211_unschedule_txq(struct ieee80211_hw *hw,
-+ struct ieee80211_txq *txq,
-+ bool purge);
-+void ieee80211_update_airtime_weight(struct ieee80211_local *local,
-+ struct airtime_sched_info *air_sched,
-+ u64 now, bool force);
-
- /* HT */
- void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
---- a/net/mac80211/iface.c
-+++ b/net/mac80211/iface.c
-@@ -2041,6 +2041,9 @@ int ieee80211_if_add(struct ieee80211_lo
- }
- }
-
-+ for (i = 0; i < IEEE80211_NUM_ACS; i++)
-+ init_airtime_info(&sdata->airtime[i], &local->airtime[i]);
-+
- ieee80211_set_default_queues(sdata);
-
- sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
---- a/net/mac80211/main.c
-+++ b/net/mac80211/main.c
-@@ -707,10 +707,13 @@ struct ieee80211_hw *ieee80211_alloc_hw_
- spin_lock_init(&local->queue_stop_reason_lock);
-
- 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] =
-+ struct airtime_sched_info *air_sched = &local->airtime[i];
-+
-+ air_sched->active_txqs = RB_ROOT_CACHED;
-+ INIT_LIST_HEAD(&air_sched->active_list);
-+ spin_lock_init(&air_sched->lock);
-+ air_sched->aql_txq_limit_low = IEEE80211_DEFAULT_AQL_TXQ_LIMIT_L;
-+ air_sched->aql_txq_limit_high =
- IEEE80211_DEFAULT_AQL_TXQ_LIMIT_H;
- }
-
---- a/net/mac80211/rx.c
-+++ b/net/mac80211/rx.c
-@@ -1563,12 +1563,8 @@ static void sta_ps_start(struct sta_info
-
- for (tid = 0; tid < IEEE80211_NUM_TIDS; tid++) {
- struct ieee80211_txq *txq = sta->sta.txq[tid];
-- struct txq_info *txqi = to_txq_info(txq);
-
-- spin_lock(&local->active_txq_lock[txq->ac]);
-- if (!list_empty(&txqi->schedule_order))
-- list_del_init(&txqi->schedule_order);
-- spin_unlock(&local->active_txq_lock[txq->ac]);
-+ ieee80211_unschedule_txq(&local->hw, txq, false);
-
- if (txq_has_queue(txq))
- set_bit(tid, &sta->txq_buffered_tids);
---- a/net/mac80211/sta_info.c
-+++ b/net/mac80211/sta_info.c
-@@ -426,15 +426,11 @@ struct sta_info *sta_info_alloc(struct i
- if (sta_prepare_rate_control(local, sta, gfp))
- goto free_txq;
-
-- sta->airtime_weight = IEEE80211_DEFAULT_AIRTIME_WEIGHT;
-
- for (i = 0; i < IEEE80211_NUM_ACS; 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];
-+ init_airtime_info(&sta->airtime[i], &local->airtime[i]);
- }
-
- for (i = 0; i < IEEE80211_NUM_TIDS; i++)
-@@ -1893,24 +1889,59 @@ void ieee80211_sta_set_buffered(struct i
- }
- EXPORT_SYMBOL(ieee80211_sta_set_buffered);
-
--void ieee80211_sta_register_airtime(struct ieee80211_sta *pubsta, u8 tid,
-- u32 tx_airtime, u32 rx_airtime)
-+void ieee80211_register_airtime(struct ieee80211_txq *txq,
-+ u32 tx_airtime, u32 rx_airtime)
- {
-- struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
-- struct ieee80211_local *local = sta->sdata->local;
-- u8 ac = ieee80211_ac_from_tid(tid);
-+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->vif);
-+ struct ieee80211_local *local = sdata->local;
-+ u64 weight_sum, weight_sum_reciprocal;
-+ struct airtime_sched_info *air_sched;
-+ struct airtime_info *air_info;
- u32 airtime = 0;
-
-- if (sta->local->airtime_flags & AIRTIME_USE_TX)
-+ air_sched = &local->airtime[txq->ac];
-+ air_info = to_airtime_info(txq);
-+
-+ if (local->airtime_flags & AIRTIME_USE_TX)
- airtime += tx_airtime;
-- if (sta->local->airtime_flags & AIRTIME_USE_RX)
-+ if (local->airtime_flags & AIRTIME_USE_RX)
- airtime += rx_airtime;
-
-- spin_lock_bh(&local->active_txq_lock[ac]);
-- sta->airtime[ac].tx_airtime += tx_airtime;
-- sta->airtime[ac].rx_airtime += rx_airtime;
-- sta->airtime[ac].deficit -= airtime;
-- spin_unlock_bh(&local->active_txq_lock[ac]);
-+ /* Weights scale so the unit weight is 256 */
-+ airtime <<= 8;
-+
-+ spin_lock_bh(&air_sched->lock);
-+
-+ air_info->tx_airtime += tx_airtime;
-+ air_info->rx_airtime += rx_airtime;
-+
-+ if (air_sched->weight_sum) {
-+ weight_sum = air_sched->weight_sum;
-+ weight_sum_reciprocal = air_sched->weight_sum_reciprocal;
-+ } else {
-+ weight_sum = air_info->weight;
-+ weight_sum_reciprocal = air_info->weight_reciprocal;
-+ }
-+
-+ /* Round the calculation of global vt */
-+ air_sched->v_t += (u64)((airtime + (weight_sum >> 1)) *
-+ weight_sum_reciprocal) >> IEEE80211_RECIPROCAL_SHIFT_64;
-+ air_info->v_t += (u32)((airtime + (air_info->weight >> 1)) *
-+ air_info->weight_reciprocal) >> IEEE80211_RECIPROCAL_SHIFT_32;
-+ ieee80211_resort_txq(&local->hw, txq);
-+
-+ spin_unlock_bh(&air_sched->lock);
-+}
-+
-+void ieee80211_sta_register_airtime(struct ieee80211_sta *pubsta, u8 tid,
-+ u32 tx_airtime, u32 rx_airtime)
-+{
-+ struct ieee80211_txq *txq = pubsta->txq[tid];
-+
-+ if (!txq)
-+ return;
-+
-+ ieee80211_register_airtime(txq, tx_airtime, rx_airtime);
- }
- EXPORT_SYMBOL(ieee80211_sta_register_airtime);
-
-@@ -2354,7 +2385,7 @@ void sta_set_sinfo(struct sta_info *sta,
- }
-
- if (!(sinfo->filled & BIT_ULL(NL80211_STA_INFO_AIRTIME_WEIGHT))) {
-- sinfo->airtime_weight = sta->airtime_weight;
-+ sinfo->airtime_weight = sta->airtime[0].weight;
- sinfo->filled |= BIT_ULL(NL80211_STA_INFO_AIRTIME_WEIGHT);
- }
-
---- a/net/mac80211/sta_info.h
-+++ b/net/mac80211/sta_info.h
-@@ -135,18 +135,25 @@ enum ieee80211_agg_stop_reason {
- #define AIRTIME_USE_TX BIT(0)
- #define AIRTIME_USE_RX BIT(1)
-
-+
- struct airtime_info {
- u64 rx_airtime;
- u64 tx_airtime;
-- s64 deficit;
-+ u64 v_t;
-+ u64 last_scheduled;
-+ struct list_head list;
- atomic_t aql_tx_pending; /* Estimated airtime for frames pending */
- u32 aql_limit_low;
- u32 aql_limit_high;
-+ u32 weight_reciprocal;
-+ u16 weight;
- };
-
- void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local,
- struct sta_info *sta, u8 ac,
- u16 tx_airtime, bool tx_completed);
-+void ieee80211_register_airtime(struct ieee80211_txq *txq,
-+ u32 tx_airtime, u32 rx_airtime);
-
- struct sta_info;
-
-@@ -515,7 +522,6 @@ struct ieee80211_fragment_cache {
- * @tid_seq: per-TID sequence numbers for sending to this STA
- * @airtime: per-AC struct airtime_info describing airtime statistics for this
- * station
-- * @airtime_weight: station weight for airtime fairness calculation purposes
- * @ampdu_mlme: A-MPDU state machine state
- * @mesh: mesh STA information
- * @debugfs_dir: debug filesystem directory dentry
-@@ -646,7 +652,6 @@ struct sta_info {
- u16 tid_seq[IEEE80211_QOS_CTL_TID_MASK + 1];
-
- struct airtime_info airtime[IEEE80211_NUM_ACS];
-- u16 airtime_weight;
-
- /*
- * Aggregation information, locked with lock.
---- a/net/mac80211/status.c
-+++ b/net/mac80211/status.c
-@@ -970,6 +970,25 @@ static void __ieee80211_tx_status(struct
- if (!(info->flags & IEEE80211_TX_CTL_INJECTED) && acked)
- ieee80211_frame_acked(sta, skb);
-
-+ } else if (wiphy_ext_feature_isset(local->hw.wiphy,
-+ NL80211_EXT_FEATURE_AIRTIME_FAIRNESS)) {
-+ struct ieee80211_sub_if_data *sdata;
-+ struct ieee80211_txq *txq;
-+ u32 airtime;
-+
-+ /* Account airtime to multicast queue */
-+ sdata = ieee80211_sdata_from_skb(local, skb);
-+
-+ if (sdata && (txq = sdata->vif.txq)) {
-+ airtime = info->status.tx_time ?:
-+ ieee80211_calc_expected_tx_airtime(hw,
-+ &sdata->vif,
-+ NULL,
-+ skb->len,
-+ false);
-+
-+ ieee80211_register_airtime(txq, airtime, 0);
-+ }
- }
-
- /* SNMP counters
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -18,6 +18,7 @@
- #include <linux/bitmap.h>
- #include <linux/rcupdate.h>
- #include <linux/export.h>
-+#include <linux/timekeeping.h>
- #include <net/net_namespace.h>
- #include <net/ieee80211_radiotap.h>
- #include <net/cfg80211.h>
-@@ -1479,7 +1480,7 @@ void ieee80211_txq_init(struct ieee80211
- codel_vars_init(&txqi->def_cvars);
- codel_stats_init(&txqi->cstats);
- __skb_queue_head_init(&txqi->frags);
-- INIT_LIST_HEAD(&txqi->schedule_order);
-+ RB_CLEAR_NODE(&txqi->schedule_order);
-
- txqi->txq.vif = &sdata->vif;
-
-@@ -1523,9 +1524,7 @@ void ieee80211_txq_purge(struct ieee8021
- ieee80211_purge_tx_queue(&local->hw, &txqi->frags);
- spin_unlock_bh(&fq->lock);
-
-- spin_lock_bh(&local->active_txq_lock[txqi->txq.ac]);
-- list_del_init(&txqi->schedule_order);
-- spin_unlock_bh(&local->active_txq_lock[txqi->txq.ac]);
-+ ieee80211_unschedule_txq(&local->hw, &txqi->txq, true);
- }
-
- void ieee80211_txq_set_params(struct ieee80211_local *local)
-@@ -3812,102 +3811,259 @@ EXPORT_SYMBOL(ieee80211_tx_dequeue);
- struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac)
- {
- struct ieee80211_local *local = hw_to_local(hw);
-+ struct airtime_sched_info *air_sched;
-+ u64 now = ktime_get_boottime_ns();
- struct ieee80211_txq *ret = NULL;
-- struct txq_info *txqi = NULL, *head = NULL;
-- bool found_eligible_txq = false;
-+ struct airtime_info *air_info;
-+ struct txq_info *txqi = NULL;
-+ struct rb_node *node;
-+ bool first = false;
-
-- spin_lock_bh(&local->active_txq_lock[ac]);
-+ air_sched = &local->airtime[ac];
-+ spin_lock_bh(&air_sched->lock);
-
-- begin:
-- txqi = list_first_entry_or_null(&local->active_txqs[ac],
-- struct txq_info,
-- schedule_order);
-- if (!txqi)
-+ node = air_sched->schedule_pos;
-+
-+begin:
-+ if (!node) {
-+ node = rb_first_cached(&air_sched->active_txqs);
-+ first = true;
-+ } else {
-+ node = rb_next(node);
-+ }
-+
-+ if (!node)
- goto out;
-
-- if (txqi == head) {
-- if (!found_eligible_txq)
-- goto out;
-- else
-- found_eligible_txq = false;
-+ txqi = container_of(node, struct txq_info, schedule_order);
-+ air_info = to_airtime_info(&txqi->txq);
-+
-+ if (air_info->v_t > air_sched->v_t &&
-+ (!first || !airtime_catchup_v_t(air_sched, air_info->v_t, now)))
-+ goto out;
-+
-+ if (!ieee80211_txq_airtime_check(hw, &txqi->txq)) {
-+ first = false;
-+ goto begin;
- }
-
-- if (!head)
-- head = txqi;
-+ air_sched->schedule_pos = node;
-+ air_sched->last_schedule_activity = now;
-+ ret = &txqi->txq;
-+out:
-+ spin_unlock_bh(&air_sched->lock);
-+ return ret;
-+}
-+EXPORT_SYMBOL(ieee80211_next_txq);
-
-- if (txqi->txq.sta) {
-- struct sta_info *sta = container_of(txqi->txq.sta,
-- struct sta_info, sta);
-- bool aql_check = ieee80211_txq_airtime_check(hw, &txqi->txq);
-- s64 deficit = sta->airtime[txqi->txq.ac].deficit;
-+static void __ieee80211_insert_txq(struct rb_root_cached *root,
-+ struct txq_info *txqi)
-+{
-+ struct rb_node **new = &root->rb_root.rb_node;
-+ struct airtime_info *old_air, *new_air;
-+ struct rb_node *parent = NULL;
-+ struct txq_info *__txqi;
-+ bool leftmost = true;
-+
-+ while (*new) {
-+ parent = *new;
-+ __txqi = rb_entry(parent, struct txq_info, schedule_order);
-+ old_air = to_airtime_info(&__txqi->txq);
-+ new_air = to_airtime_info(&txqi->txq);
-
-- if (aql_check)
-- found_eligible_txq = true;
-+ if (new_air->v_t <= old_air->v_t) {
-+ new = &parent->rb_left;
-+ } else {
-+ new = &parent->rb_right;
-+ leftmost = false;
-+ }
-+ }
-
-- 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;
-+ rb_link_node(&txqi->schedule_order, parent, new);
-+ rb_insert_color_cached(&txqi->schedule_order, root, leftmost);
-+}
-+
-+void ieee80211_resort_txq(struct ieee80211_hw *hw,
-+ struct ieee80211_txq *txq)
-+{
-+ struct airtime_info *air_info = to_airtime_info(txq);
-+ struct ieee80211_local *local = hw_to_local(hw);
-+ struct txq_info *txqi = to_txq_info(txq);
-+ struct airtime_sched_info *air_sched;
-+
-+ air_sched = &local->airtime[txq->ac];
-+
-+ lockdep_assert_held(&air_sched->lock);
-+
-+ if (!RB_EMPTY_NODE(&txqi->schedule_order)) {
-+ struct airtime_info *a_prev = NULL, *a_next = NULL;
-+ struct txq_info *t_prev, *t_next;
-+ struct rb_node *n_prev, *n_next;
-+
-+ /* Erasing a node can cause an expensive rebalancing operation,
-+ * so we check the previous and next nodes first and only remove
-+ * and re-insert if the current node is not already in the
-+ * correct position.
-+ */
-+ if ((n_prev = rb_prev(&txqi->schedule_order)) != NULL) {
-+ t_prev = container_of(n_prev, struct txq_info,
-+ schedule_order);
-+ a_prev = to_airtime_info(&t_prev->txq);
-+ }
-+
-+ if ((n_next = rb_next(&txqi->schedule_order)) != NULL) {
-+ t_next = container_of(n_next, struct txq_info,
-+ schedule_order);
-+ a_next = to_airtime_info(&t_next->txq);
- }
-+
-+ if ((!a_prev || a_prev->v_t <= air_info->v_t) &&
-+ (!a_next || a_next->v_t > air_info->v_t))
-+ return;
-+
-+ if (air_sched->schedule_pos == &txqi->schedule_order)
-+ air_sched->schedule_pos = n_prev;
-+
-+ rb_erase_cached(&txqi->schedule_order,
-+ &air_sched->active_txqs);
-+ RB_CLEAR_NODE(&txqi->schedule_order);
-+ __ieee80211_insert_txq(&air_sched->active_txqs, txqi);
- }
-+}
-+
-+void ieee80211_update_airtime_weight(struct ieee80211_local *local,
-+ struct airtime_sched_info *air_sched,
-+ u64 now, bool force)
-+{
-+ struct airtime_info *air_info, *tmp;
-+ u64 weight_sum = 0;
-+
-+ if (unlikely(!now))
-+ now = ktime_get_boottime_ns();
-+
-+ lockdep_assert_held(&air_sched->lock);
-+
-+ if (!force && (air_sched->last_weight_update <
-+ now - AIRTIME_ACTIVE_DURATION))
-+ return;
-+
-+ list_for_each_entry_safe(air_info, tmp,
-+ &air_sched->active_list, list) {
-+ if (airtime_is_active(air_info, now))
-+ weight_sum += air_info->weight;
-+ else
-+ list_del_init(&air_info->list);
-+ }
-+ airtime_weight_sum_set(air_sched, weight_sum);
-+ air_sched->last_weight_update = now;
-+}
-
-+void ieee80211_schedule_txq(struct ieee80211_hw *hw,
-+ struct ieee80211_txq *txq)
-+ __acquires(txq_lock) __releases(txq_lock)
-+{
-+ struct ieee80211_local *local = hw_to_local(hw);
-+ struct txq_info *txqi = to_txq_info(txq);
-+ struct airtime_sched_info *air_sched;
-+ u64 now = ktime_get_boottime_ns();
-+ struct airtime_info *air_info;
-+ u8 ac = txq->ac;
-+ bool was_active;
-
-- if (txqi->schedule_round == local->schedule_round[ac])
-+ air_sched = &local->airtime[ac];
-+ air_info = to_airtime_info(txq);
-+
-+ spin_lock_bh(&air_sched->lock);
-+ was_active = airtime_is_active(air_info, now);
-+ airtime_set_active(air_sched, air_info, now);
-+
-+ if (!RB_EMPTY_NODE(&txqi->schedule_order))
- goto out;
-
-- list_del_init(&txqi->schedule_order);
-- txqi->schedule_round = local->schedule_round[ac];
-- ret = &txqi->txq;
-+ /* If the station has been inactive for a while, catch up its v_t so it
-+ * doesn't get indefinite priority; see comment above the definition of
-+ * AIRTIME_MAX_BEHIND.
-+ */
-+ if ((!was_active && air_info->v_t < air_sched->v_t) ||
-+ air_info->v_t < air_sched->v_t - AIRTIME_MAX_BEHIND)
-+ air_info->v_t = air_sched->v_t;
-+
-+ ieee80211_update_airtime_weight(local, air_sched, now, !was_active);
-+ __ieee80211_insert_txq(&air_sched->active_txqs, txqi);
-
- out:
-- spin_unlock_bh(&local->active_txq_lock[ac]);
-- return ret;
-+ spin_unlock_bh(&air_sched->lock);
- }
--EXPORT_SYMBOL(ieee80211_next_txq);
-+EXPORT_SYMBOL(ieee80211_schedule_txq);
-
--void __ieee80211_schedule_txq(struct ieee80211_hw *hw,
-- struct ieee80211_txq *txq,
-- bool force)
-+static void __ieee80211_unschedule_txq(struct ieee80211_hw *hw,
-+ struct ieee80211_txq *txq,
-+ bool purge)
- {
- struct ieee80211_local *local = hw_to_local(hw);
- struct txq_info *txqi = to_txq_info(txq);
-+ struct airtime_sched_info *air_sched;
-+ struct airtime_info *air_info;
-
-- spin_lock_bh(&local->active_txq_lock[txq->ac]);
-+ air_sched = &local->airtime[txq->ac];
-+ air_info = to_airtime_info(&txqi->txq);
-
-- if (list_empty(&txqi->schedule_order) &&
-- (force || !skb_queue_empty(&txqi->frags) ||
-- txqi->tin.backlog_packets)) {
-- /* If airtime accounting is active, always enqueue STAs at the
-- * head of the list to ensure that they only get moved to the
-- * back by the airtime DRR scheduler once they have a negative
-- * deficit. A station that already has a negative deficit will
-- * get immediately moved to the back of the list on the next
-- * call to ieee80211_next_txq().
-- */
-- if (txqi->txq.sta && local->airtime_flags &&
-- wiphy_ext_feature_isset(local->hw.wiphy,
-- NL80211_EXT_FEATURE_AIRTIME_FAIRNESS))
-- list_add(&txqi->schedule_order,
-- &local->active_txqs[txq->ac]);
-- else
-- list_add_tail(&txqi->schedule_order,
-- &local->active_txqs[txq->ac]);
-+ lockdep_assert_held(&air_sched->lock);
-+
-+ if (purge) {
-+ list_del_init(&air_info->list);
-+ ieee80211_update_airtime_weight(local, air_sched, 0, true);
- }
-
-- spin_unlock_bh(&local->active_txq_lock[txq->ac]);
-+ if (RB_EMPTY_NODE(&txqi->schedule_order))
-+ return;
-+
-+ if (air_sched->schedule_pos == &txqi->schedule_order)
-+ air_sched->schedule_pos = rb_prev(&txqi->schedule_order);
-+
-+ if (!purge)
-+ airtime_set_active(air_sched, air_info,
-+ ktime_get_boottime_ns());
-+
-+ rb_erase_cached(&txqi->schedule_order,
-+ &air_sched->active_txqs);
-+ RB_CLEAR_NODE(&txqi->schedule_order);
-+}
-+
-+void ieee80211_unschedule_txq(struct ieee80211_hw *hw,
-+ struct ieee80211_txq *txq,
-+ bool purge)
-+ __acquires(txq_lock) __releases(txq_lock)
-+{
-+ struct ieee80211_local *local = hw_to_local(hw);
-+
-+ spin_lock_bh(&local->airtime[txq->ac].lock);
-+ __ieee80211_unschedule_txq(hw, txq, purge);
-+ spin_unlock_bh(&local->airtime[txq->ac].lock);
-+}
-+
-+void ieee80211_return_txq(struct ieee80211_hw *hw,
-+ struct ieee80211_txq *txq, bool force)
-+{
-+ struct ieee80211_local *local = hw_to_local(hw);
-+ struct txq_info *txqi = to_txq_info(txq);
-+
-+ spin_lock_bh(&local->airtime[txq->ac].lock);
-+
-+ if (!RB_EMPTY_NODE(&txqi->schedule_order) && !force &&
-+ !txq_has_queue(txq))
-+ __ieee80211_unschedule_txq(hw, txq, false);
-+
-+ spin_unlock_bh(&local->airtime[txq->ac].lock);
- }
--EXPORT_SYMBOL(__ieee80211_schedule_txq);
-+EXPORT_SYMBOL(ieee80211_return_txq);
-
- DEFINE_STATIC_KEY_FALSE(aql_disable);
-
- bool ieee80211_txq_airtime_check(struct ieee80211_hw *hw,
- struct ieee80211_txq *txq)
- {
-- struct sta_info *sta;
-+ struct airtime_info *air_info = to_airtime_info(txq);
- struct ieee80211_local *local = hw_to_local(hw);
-
- if (!wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL))
-@@ -3922,15 +4078,12 @@ bool ieee80211_txq_airtime_check(struct
- if (unlikely(txq->tid == IEEE80211_NUM_TIDS))
- 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)
-+ if (atomic_read(&air_info->aql_tx_pending) < air_info->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)
-+ atomic_read(&air_info->aql_tx_pending) < air_info->aql_limit_high)
- return true;
-
- return false;
-@@ -3940,60 +4093,59 @@ EXPORT_SYMBOL(ieee80211_txq_airtime_chec
- bool ieee80211_txq_may_transmit(struct ieee80211_hw *hw,
- struct ieee80211_txq *txq)
- {
-+ struct txq_info *first_txqi = NULL, *txqi = to_txq_info(txq);
- struct ieee80211_local *local = hw_to_local(hw);
-- struct txq_info *iter, *tmp, *txqi = to_txq_info(txq);
-- struct sta_info *sta;
-- u8 ac = txq->ac;
-+ struct airtime_sched_info *air_sched;
-+ struct airtime_info *air_info;
-+ struct rb_node *node = NULL;
-+ bool ret = false;
-+ u64 now;
-
-- spin_lock_bh(&local->active_txq_lock[ac]);
-
-- if (!txqi->txq.sta)
-- goto out;
-+ if (!ieee80211_txq_airtime_check(hw, txq))
-+ return false;
-+
-+ air_sched = &local->airtime[txq->ac];
-+ spin_lock_bh(&air_sched->lock);
-
-- if (list_empty(&txqi->schedule_order))
-+ if (RB_EMPTY_NODE(&txqi->schedule_order))
- goto out;
-
-- list_for_each_entry_safe(iter, tmp, &local->active_txqs[ac],
-- schedule_order) {
-- if (iter == txqi)
-- break;
-+ now = ktime_get_boottime_ns();
-
-- if (!iter->txq.sta) {
-- list_move_tail(&iter->schedule_order,
-- &local->active_txqs[ac]);
-- continue;
-- }
-- sta = container_of(iter->txq.sta, struct sta_info, sta);
-- if (sta->airtime[ac].deficit < 0)
-- sta->airtime[ac].deficit += sta->airtime_weight;
-- list_move_tail(&iter->schedule_order, &local->active_txqs[ac]);
-+ /* Like in ieee80211_next_txq(), make sure the first station in the
-+ * scheduling order is eligible for transmission to avoid starvation.
-+ */
-+ node = rb_first_cached(&air_sched->active_txqs);
-+ if (node) {
-+ first_txqi = container_of(node, struct txq_info,
-+ schedule_order);
-+ air_info = to_airtime_info(&first_txqi->txq);
-+
-+ if (air_sched->v_t < air_info->v_t)
-+ airtime_catchup_v_t(air_sched, air_info->v_t, now);
- }
-
-- sta = container_of(txqi->txq.sta, struct sta_info, sta);
-- if (sta->airtime[ac].deficit >= 0)
-- goto out;
--
-- sta->airtime[ac].deficit += sta->airtime_weight;
-- list_move_tail(&txqi->schedule_order, &local->active_txqs[ac]);
-- spin_unlock_bh(&local->active_txq_lock[ac]);
-+ air_info = to_airtime_info(&txqi->txq);
-+ if (air_info->v_t <= air_sched->v_t) {
-+ air_sched->last_schedule_activity = now;
-+ ret = true;
-+ }
-
-- return false;
- out:
-- if (!list_empty(&txqi->schedule_order))
-- list_del_init(&txqi->schedule_order);
-- spin_unlock_bh(&local->active_txq_lock[ac]);
--
-- return true;
-+ spin_unlock_bh(&air_sched->lock);
-+ return ret;
- }
- EXPORT_SYMBOL(ieee80211_txq_may_transmit);
-
- void ieee80211_txq_schedule_start(struct ieee80211_hw *hw, u8 ac)
- {
- struct ieee80211_local *local = hw_to_local(hw);
-+ struct airtime_sched_info *air_sched = &local->airtime[ac];
-
-- spin_lock_bh(&local->active_txq_lock[ac]);
-- local->schedule_round[ac]++;
-- spin_unlock_bh(&local->active_txq_lock[ac]);
-+ spin_lock_bh(&air_sched->lock);
-+ air_sched->schedule_pos = NULL;
-+ spin_unlock_bh(&air_sched->lock);
- }
- EXPORT_SYMBOL(ieee80211_txq_schedule_start);
-
+++ /dev/null
-From: Ryder Lee <ryder.lee@mediatek.com>
-Date: Fri, 18 Jun 2021 04:38:59 +0800
-Subject: [PATCH] mac80211: check per vif offload_flags in Tx path
-
-offload_flags has been introduced to indicate encap status of each interface.
-An interface can encap offload at runtime, or if it has some extra limitations
-it can simply override the flags, so it's more flexible to check offload_flags
-in Tx path.
-
-Signed-off-by: Ryder Lee <ryder.lee@mediatek.com>
-Link: https://lore.kernel.org/r/177785418cf407808bf3a44760302d0647076990.1623961575.git.ryder.lee@mediatek.com
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -3318,6 +3318,9 @@ static bool ieee80211_amsdu_aggregate(st
- if (!ieee80211_hw_check(&local->hw, TX_AMSDU))
- return false;
-
-+ if (sdata->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED)
-+ return false;
-+
- if (skb_is_gso(skb))
- return false;
-
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
-@@ -1253,6 +1253,27 @@ struct cfg80211_csa_settings {
- #define CFG80211_MAX_NUM_DIFFERENT_CHANNELS 10
+@@ -1258,6 +1258,27 @@ struct cfg80211_csa_settings {
+ };
/**
+ * struct cfg80211_color_change_settings - color change settings
* struct iface_combination_params - input parameters for interface combinations
*
* Used to pass interface combination parameters
-@@ -3991,6 +4012,8 @@ struct mgmt_frame_regs {
+@@ -4000,6 +4021,8 @@ struct mgmt_frame_regs {
* given TIDs. This callback may sleep.
*
* @set_sar_specs: Update the SAR (TX power) settings.
*/
struct cfg80211_ops {
int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
-@@ -4318,6 +4341,9 @@ struct cfg80211_ops {
+@@ -4327,6 +4350,9 @@ struct cfg80211_ops {
const u8 *peer, u8 tids);
int (*set_sar_specs)(struct wiphy *wiphy,
struct cfg80211_sar_specs *sar);
};
/*
-@@ -8209,4 +8235,70 @@ void cfg80211_update_owe_info_event(stru
+@@ -8226,4 +8252,70 @@ void cfg80211_update_owe_info_event(stru
*/
void cfg80211_bss_flush(struct wiphy *wiphy);
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
-@@ -5950,6 +5989,9 @@ enum nl80211_feature_flags {
+@@ -5953,6 +5992,9 @@ enum nl80211_feature_flags {
* frame protection for all management frames exchanged during the
* negotiation and range measurement procedure.
*
* @NUM_NL80211_EXT_FEATURES: number of extended features.
* @MAX_NL80211_EXT_FEATURES: highest extended feature index.
*/
-@@ -6014,6 +6056,7 @@ enum nl80211_ext_feature_index {
+@@ -6017,6 +6059,7 @@ enum nl80211_ext_feature_index {
NL80211_EXT_FEATURE_SECURE_LTF,
NL80211_EXT_FEATURE_SECURE_RTT,
NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE,
};
/* policy for the key attributes */
-@@ -14815,6 +14819,106 @@ bad_tid_conf:
+@@ -14823,6 +14827,106 @@ bad_tid_conf:
return ret;
}
#define NL80211_FLAG_NEED_WIPHY 0x01
#define NL80211_FLAG_NEED_NETDEV 0x02
#define NL80211_FLAG_NEED_RTNL 0x04
-@@ -15815,6 +15919,14 @@ static const struct genl_small_ops nl802
+@@ -15823,6 +15927,14 @@ static const struct genl_small_ops nl802
.internal_flags = NL80211_FLAG_NEED_WIPHY |
NL80211_FLAG_NEED_RTNL,
},
};
static struct genl_family nl80211_fam __genl_ro_after_init = {
-@@ -17446,6 +17558,51 @@ void cfg80211_ch_switch_started_notify(s
+@@ -17454,6 +17566,51 @@ void cfg80211_ch_switch_started_notify(s
}
EXPORT_SYMBOL(cfg80211_ch_switch_started_notify);
const struct cfg80211_chan_def *chandef,
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
-@@ -1358,4 +1358,17 @@ static inline int rdev_set_sar_specs(str
+@@ -1368,4 +1368,17 @@ static inline int rdev_set_sar_specs(str
return ret;
}
#endif /* __CFG80211_RDEV_OPS */
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
-@@ -3565,6 +3565,52 @@ TRACE_EVENT(rdev_set_sar_specs,
+@@ -3597,6 +3597,52 @@ TRACE_EVENT(rdev_set_sar_specs,
WIPHY_PR_ARG, __entry->type, __entry->num)
);
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
-@@ -1713,6 +1713,10 @@ enum ieee80211_offload_flags {
+@@ -1715,6 +1715,10 @@ enum ieee80211_offload_flags {
* protected by fq->lock.
* @offload_flags: 802.3 -> 802.11 enapsulation offload flags, see
* &enum ieee80211_offload_flags.
*/
struct ieee80211_vif {
enum nl80211_iftype type;
-@@ -1741,6 +1745,9 @@ struct ieee80211_vif {
+@@ -1743,6 +1747,9 @@ struct ieee80211_vif {
bool txqs_stopped[IEEE80211_NUM_ACS];
/* must be last */
u8 drv_priv[] __aligned(sizeof(void *));
};
-@@ -4992,6 +4999,16 @@ void ieee80211_csa_finish(struct ieee802
+@@ -5016,6 +5023,16 @@ void ieee80211_csa_finish(struct ieee802
bool ieee80211_beacon_cntdwn_is_complete(struct ieee80211_vif *vif);
/**
* ieee80211_proberesp_get - retrieve a Probe Response template
* @hw: pointer obtained from ieee80211_alloc_hw().
* @vif: &struct ieee80211_vif pointer from the add_interface callback.
-@@ -6756,6 +6773,18 @@ ieee80211_get_unsol_bcast_probe_resp_tmp
+@@ -6780,6 +6797,18 @@ ieee80211_get_unsol_bcast_probe_resp_tmp
struct ieee80211_vif *vif);
/**
if (err < 0)
return err;
ieee80211_bss_info_change_notify(sdata, err);
-@@ -3163,7 +3171,7 @@ static int ieee80211_set_after_csa_beaco
+@@ -3155,7 +3163,7 @@ static int ieee80211_set_after_csa_beaco
switch (sdata->vif.type) {
case NL80211_IFTYPE_AP:
err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon,
kfree(sdata->u.ap.next_beacon);
sdata->u.ap.next_beacon = NULL;
-@@ -3329,7 +3337,7 @@ static int ieee80211_set_csa_beacon(stru
+@@ -3321,7 +3329,7 @@ static int ieee80211_set_csa_beacon(stru
csa.n_counter_offsets_presp = params->n_counter_offsets_presp;
csa.count = params->count;
if (err < 0) {
kfree(sdata->u.ap.next_beacon);
return err;
-@@ -3418,6 +3426,15 @@ static int ieee80211_set_csa_beacon(stru
+@@ -3410,6 +3418,15 @@ static int ieee80211_set_csa_beacon(stru
return 0;
}
static int
__ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_csa_settings *params)
-@@ -3486,6 +3503,10 @@ __ieee80211_channel_switch(struct wiphy
+@@ -3478,6 +3495,10 @@ __ieee80211_channel_switch(struct wiphy
goto out;
}
err = ieee80211_set_csa_beacon(sdata, params, &changed);
if (err) {
ieee80211_vif_unreserve_chanctx(sdata);
-@@ -4137,6 +4158,196 @@ static int ieee80211_set_sar_specs(struc
+@@ -4129,6 +4150,196 @@ static int ieee80211_set_sar_specs(struc
return local->ops->set_sar_specs(&local->hw, sar);
}
const struct cfg80211_ops mac80211_config_ops = {
.add_virtual_intf = ieee80211_add_iface,
.del_virtual_intf = ieee80211_del_iface,
-@@ -4240,4 +4451,5 @@ const struct cfg80211_ops mac80211_confi
+@@ -4232,4 +4443,5 @@ const struct cfg80211_ops mac80211_confi
.set_tid_config = ieee80211_set_tid_config,
.reset_tid_config = ieee80211_reset_tid_config,
.set_sar_specs = ieee80211_set_sar_specs,
struct list_head assigned_chanctx_list; /* protected by chanctx_mtx */
struct list_head reserved_chanctx_list; /* protected by chanctx_mtx */
-@@ -1895,6 +1903,9 @@ void ieee80211_csa_finalize_work(struct
+@@ -1891,6 +1899,9 @@ void ieee80211_csa_finalize_work(struct
int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_csa_settings *params);
cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
-@@ -1595,6 +1596,7 @@ static void ieee80211_setup_sdata(struct
+@@ -1608,6 +1609,7 @@ static void ieee80211_setup_sdata(struct
INIT_WORK(&sdata->work, ieee80211_iface_work);
INIT_WORK(&sdata->recalc_smps, ieee80211_recalc_smps_work);
INIT_WORK(&sdata->csa_finalize_work, ieee80211_csa_finalize_work);
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
-@@ -4784,11 +4784,11 @@ static int ieee80211_beacon_add_tim(stru
+@@ -4796,11 +4796,11 @@ static int ieee80211_beacon_add_tim(stru
static void ieee80211_set_beacon_cntdwn(struct ieee80211_sub_if_data *sdata,
struct beacon_data *beacon)
{
switch (sdata->vif.type) {
case NL80211_IFTYPE_AP:
-@@ -4808,21 +4808,27 @@ static void ieee80211_set_beacon_cntdwn(
+@@ -4820,21 +4820,27 @@ static void ieee80211_set_beacon_cntdwn(
}
rcu_read_lock();
}
rcu_read_unlock();
}
-@@ -5032,6 +5038,7 @@ __ieee80211_beacon_get(struct ieee80211_
+@@ -5044,6 +5050,7 @@ __ieee80211_beacon_get(struct ieee80211_
if (offs) {
offs->tim_offset = beacon->head_len;
offs->tim_length = skb->len - beacon->head_len;
} u;
} __packed action;
} u;
-@@ -2879,6 +2925,7 @@ enum ieee80211_eid {
+@@ -2881,6 +2927,7 @@ enum ieee80211_eid {
WLAN_EID_AID_RESPONSE = 211,
WLAN_EID_S1G_BCN_COMPAT = 213,
WLAN_EID_S1G_SHORT_BCN_INTERVAL = 214,
WLAN_EID_S1G_CAPABILITIES = 217,
WLAN_EID_VENDOR_SPECIFIC = 221,
WLAN_EID_QOS_PARAMETER = 222,
-@@ -2947,6 +2994,7 @@ enum ieee80211_category {
+@@ -2950,6 +2997,7 @@ enum ieee80211_category {
WLAN_CATEGORY_FST = 18,
WLAN_CATEGORY_UNPROT_DMG = 20,
WLAN_CATEGORY_VHT = 21,
WLAN_CATEGORY_VENDOR_SPECIFIC_PROTECTED = 126,
WLAN_CATEGORY_VENDOR_SPECIFIC = 127,
};
-@@ -3020,6 +3068,20 @@ enum ieee80211_key_len {
+@@ -3023,6 +3071,20 @@ enum ieee80211_key_len {
WLAN_KEY_LEN_BIP_GMAC_256 = 32,
};
+From f5a4c24e689f54e66201f04d343bdd2e8a1d7923 Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Mon, 23 Aug 2021 20:02:39 +0200
Subject: [PATCH] mac80211: introduce individual TWT support in AP mode
fix to use ieee80211_get_he_iftype_cap() correctly]
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
+ include/net/mac80211.h | 12 +++
+ net/mac80211/driver-ops.h | 36 ++++++++
+ net/mac80211/ieee80211_i.h | 6 ++
+ net/mac80211/iface.c | 41 +++++++++
+ net/mac80211/rx.c | 73 +++++++++++++++
+ net/mac80211/s1g.c | 180 +++++++++++++++++++++++++++++++++++++
+ net/mac80211/status.c | 17 +++-
+ net/mac80211/trace.h | 67 ++++++++++++++
+ 8 files changed, 430 insertions(+), 2 deletions(-)
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
-@@ -4229,6 +4229,11 @@ struct ieee80211_ops {
+@@ -3930,6 +3930,13 @@ struct ieee80211_prep_tx_info {
+ * @set_sar_specs: Update the SAR (TX power) settings.
+ * @sta_set_decap_offload: Called to notify the driver when a station is allowed
+ * to use rx decapsulation offload
++ * @add_twt_setup: Update hw with TWT agreement parameters received from the peer.
++ * This callback allows the hw to check if requested parameters
++ * are supported and if there is enough room for a new agreement.
++ * The hw is expected to set agreement result in the req_type field of
++ * twt structure.
++ * @twt_teardown_request: Update the hw with TWT teardown request received
++ * from the peer.
+ */
+ struct ieee80211_ops {
+ void (*tx)(struct ieee80211_hw *hw,
+@@ -4253,6 +4260,11 @@ struct ieee80211_ops {
void (*sta_set_decap_offload)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta, bool enabled);
/**
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
-@@ -1429,4 +1429,40 @@ static inline void drv_sta_set_decap_off
+@@ -1447,4 +1447,40 @@ static inline void drv_sta_set_decap_off
trace_drv_return_void(local);
}
u8 needed_rx_chains;
enum ieee80211_smps_mode smps_mode;
-@@ -2088,6 +2089,11 @@ ieee80211_he_op_ie_to_bss_conf(struct ie
+@@ -2083,6 +2084,11 @@ ieee80211_he_op_ie_to_bss_conf(struct ie
/* S1G */
void ieee80211_s1g_sta_rate_init(struct sta_info *sta);
INIT_WORK(&sdata->work, ieee80211_iface_work);
return 0;
-@@ -1396,6 +1398,24 @@ static void ieee80211_if_setup_no_queue(
- dev->priv_flags |= IFF_NO_QUEUE;
+@@ -1459,6 +1461,16 @@ static void ieee80211_iface_process_skb(
+ WARN_ON(1);
+ break;
+ }
++ } else if (ieee80211_is_action(mgmt->frame_control) &&
++ mgmt->u.action.category == WLAN_CATEGORY_S1G) {
++ switch (mgmt->u.action.u.s1g.action_code) {
++ case WLAN_S1G_TWT_TEARDOWN:
++ case WLAN_S1G_TWT_SETUP:
++ ieee80211_s1g_rx_twt_action(sdata, skb);
++ break;
++ default:
++ break;
++ }
+ } else if (ieee80211_is_ext(mgmt->frame_control)) {
+ if (sdata->vif.type == NL80211_IFTYPE_STATION)
+ ieee80211_sta_rx_queued_ext(sdata, skb);
+@@ -1514,6 +1526,24 @@ static void ieee80211_iface_process_skb(
+ }
}
+static void ieee80211_iface_process_status(struct ieee80211_sub_if_data *sdata,
static void ieee80211_iface_work(struct work_struct *work)
{
struct ieee80211_sub_if_data *sdata =
-@@ -1474,6 +1494,16 @@ static void ieee80211_iface_work(struct
- WARN_ON(1);
- break;
- }
-+ } else if (ieee80211_is_action(mgmt->frame_control) &&
-+ mgmt->u.action.category == WLAN_CATEGORY_S1G) {
-+ switch (mgmt->u.action.u.s1g.action_code) {
-+ case WLAN_S1G_TWT_TEARDOWN:
-+ case WLAN_S1G_TWT_SETUP:
-+ ieee80211_s1g_rx_twt_action(sdata, skb);
-+ break;
-+ default:
-+ break;
-+ }
- } else if (ieee80211_is_ext(mgmt->frame_control)) {
- if (sdata->vif.type == NL80211_IFTYPE_STATION)
- ieee80211_sta_rx_queued_ext(sdata, skb);
-@@ -1530,6 +1560,12 @@ static void ieee80211_iface_work(struct
+@@ -1543,6 +1573,16 @@ static void ieee80211_iface_work(struct
kcov_remote_stop();
}
+ /* process status queue */
+ while ((skb = skb_dequeue(&sdata->status_queue))) {
++ kcov_remote_start_common(skb_get_kcov_handle(skb));
++
+ ieee80211_iface_process_status(sdata, skb);
+ kfree_skb(skb);
++
++ kcov_remote_stop();
+ }
+
/* then other type-dependent work */
switch (sdata->vif.type) {
case NL80211_IFTYPE_STATION:
-@@ -1593,6 +1629,7 @@ static void ieee80211_setup_sdata(struct
+@@ -1606,6 +1646,7 @@ static void ieee80211_setup_sdata(struct
}
skb_queue_head_init(&sdata->skb_queue);
INIT_WORK(&sdata->csa_finalize_work, ieee80211_csa_finalize_work);
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
-@@ -3198,6 +3198,68 @@ ieee80211_rx_h_mgmt_check(struct ieee802
+@@ -3211,6 +3211,68 @@ ieee80211_rx_h_mgmt_check(struct ieee802
return RX_CONTINUE;
}
static ieee80211_rx_result debug_noinline
ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
{
-@@ -3477,6 +3539,17 @@ ieee80211_rx_h_action(struct ieee80211_r
+@@ -3490,6 +3552,17 @@ ieee80211_rx_h_action(struct ieee80211_r
!mesh_path_sel_is_hwmp(sdata))
break;
goto queue;
rcu_read_unlock();
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
-@@ -2804,6 +2804,73 @@ DEFINE_EVENT(sta_flag_evt, drv_sta_set_d
+@@ -2825,6 +2825,73 @@ DEFINE_EVENT(sta_flag_evt, drv_sta_set_d
TP_ARGS(local, sdata, sta, enabled)
);
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
-@@ -637,21 +637,6 @@ static int wiphy_verify_combinations(str
+@@ -629,21 +629,6 @@ static int wiphy_verify_combinations(str
c->limits[j].max > 1))
return -EINVAL;
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
-@@ -3826,6 +3826,7 @@ struct mgmt_frame_regs {
+@@ -3835,6 +3835,7 @@ struct mgmt_frame_regs {
* (as advertised by the nl80211 feature flag.)
* @get_tx_power: store the current TX power into the dbm variable;
* return 0 if successful
*
* @rfkill_poll: polls the hw rfkill line, use cfg80211 reporting
* functions to adjust rfkill hw state
-@@ -4150,6 +4151,7 @@ struct cfg80211_ops {
+@@ -4159,6 +4160,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);
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
-@@ -1564,6 +1564,7 @@ enum ieee80211_smps_mode {
+@@ -1566,6 +1566,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
*
* @chandef: the channel definition to tune to
* @radar_enabled: whether radar detection is enabled
-@@ -1584,6 +1585,7 @@ enum ieee80211_smps_mode {
+@@ -1586,6 +1587,7 @@ enum ieee80211_smps_mode {
struct ieee80211_conf {
u32 flags;
int power_level, dynamic_ps_timeout;
__NL80211_ATTR_AFTER_LAST,
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
-@@ -2768,6 +2768,19 @@ static int ieee80211_get_tx_power(struct
+@@ -2760,6 +2760,19 @@ static int ieee80211_get_tx_power(struct
return 0;
}
static void ieee80211_rfkill_poll(struct wiphy *wiphy)
{
struct ieee80211_local *local = wiphy_priv(wiphy);
-@@ -4403,6 +4416,7 @@ const struct cfg80211_ops mac80211_confi
+@@ -4395,6 +4408,7 @@ const struct cfg80211_ops mac80211_confi
.set_wiphy_params = ieee80211_set_wiphy_params,
.set_tx_power = ieee80211_set_tx_power,
.get_tx_power = ieee80211_get_tx_power,
};
/* policy for the key attributes */
-@@ -3317,6 +3318,20 @@ static int nl80211_set_wiphy(struct sk_b
+@@ -3325,6 +3326,20 @@ static int nl80211_set_wiphy(struct sk_b
goto out;
}
--- a/nl80211.h
+++ b/nl80211.h
+@@ -11,7 +11,7 @@
+ * Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com>
+ * Copyright 2008 Colin McCabe <colin@cozybit.com>
+ * Copyright 2015-2017 Intel Deutschland GmbH
+- * Copyright (C) 2018-2020 Intel Corporation
++ * Copyright (C) 2018-2021 Intel Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
@@ -655,6 +655,9 @@
* When a security association was established on an 802.1X network using
* fast transition, this event should be followed by an
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
-@@ -5896,6 +5981,22 @@ enum nl80211_feature_flags {
+@@ -3613,6 +3698,8 @@ enum nl80211_mpath_info {
+ * defined
+ * @NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA: HE 6GHz band capabilities (__le16),
+ * given for all 6 GHz band channels
++ * @NL80211_BAND_IFTYPE_ATTR_VENDOR_ELEMS: vendor element capabilities that are
++ * advertised on this band/for this iftype (binary)
+ * @__NL80211_BAND_IFTYPE_ATTR_AFTER_LAST: internal use
+ */
+ enum nl80211_band_iftype_attr {
+@@ -3624,6 +3711,7 @@ enum nl80211_band_iftype_attr {
+ NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET,
+ NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE,
+ NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA,
++ NL80211_BAND_IFTYPE_ATTR_VENDOR_ELEMS,
+
+ /* keep last */
+ __NL80211_BAND_IFTYPE_ATTR_AFTER_LAST,
+@@ -5896,6 +5984,22 @@ enum nl80211_feature_flags {
* @NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP: Driver/device supports
* unsolicited broadcast probe response transmission
*
* @NUM_NL80211_EXT_FEATURES: number of extended features.
* @MAX_NL80211_EXT_FEATURES: highest extended feature index.
*/
-@@ -5956,6 +6057,11 @@ enum nl80211_ext_feature_index {
+@@ -5956,6 +6060,11 @@ enum nl80211_ext_feature_index {
NL80211_EXT_FEATURE_SAE_OFFLOAD_AP,
NL80211_EXT_FEATURE_FILS_DISCOVERY,
NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP,
/* add new features before the definition below */
NUM_NL80211_EXT_FEATURES,
-@@ -6253,11 +6359,13 @@ struct nl80211_vendor_cmd_info {
+@@ -6253,11 +6362,13 @@ struct nl80211_vendor_cmd_info {
* @NL80211_TDLS_PEER_HT: TDLS peer is HT capable.
* @NL80211_TDLS_PEER_VHT: TDLS peer is VHT capable.
* @NL80211_TDLS_PEER_WMM: TDLS peer is WMM capable.
};
/**
-@@ -6849,6 +6957,9 @@ enum nl80211_peer_measurement_ftm_capa {
+@@ -6849,6 +6960,12 @@ enum nl80211_peer_measurement_ftm_capa {
* if neither %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED nor
* %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED is set, EDCA based
* ranging will be used.
+ * @NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK: negotiate for LMR feedback. Only
+ * valid if either %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED or
+ * %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED is set.
++ * @NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR: optional. The BSS color of the
++ * responder. Only valid if %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED
++ * or %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED is set.
*
* @NUM_NL80211_PMSR_FTM_REQ_ATTR: internal
* @NL80211_PMSR_FTM_REQ_ATTR_MAX: highest attribute number
-@@ -6867,6 +6978,7 @@ enum nl80211_peer_measurement_ftm_req {
+@@ -6867,6 +6984,8 @@ enum nl80211_peer_measurement_ftm_req {
NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC,
NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED,
NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED,
+ NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK,
++ NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR,
/* keep last */
NUM_NL80211_PMSR_FTM_REQ_ATTR,
-@@ -7124,4 +7236,115 @@ enum nl80211_unsol_bcast_probe_resp_attr
+@@ -7124,4 +7243,115 @@ enum nl80211_unsol_bcast_probe_resp_attr
NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_MAX =
__NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_LAST - 1
};