[rdkb][common][bsp][Refactor and sync wifi from openwrt]
[Description]
e44e196 [MAC80211][misc][Remvoe start_disabled from netifd mac80211 script]
c4869e7 [MAC80211][core][Add sta-assisted DFS state update mechansim in core]
4b4782e [MAC80211][core][Remove using start disable in core]
27a2b88 [MAC80211][hostapd][Add sta-assisted DFS state update mechanism in hostapd]
86ee2ed [MAC80211][hostapd][Fix sending wrong VHT operation IE in CSA while using ZWDFS]
6070281 [MAC80211][hostapd][Remove sending start disable to core]
84c0326 [mac80211][mt76][Update firmware bin for mt7981]
0f34f54 [MAC80211][netifd][Fix netifd set AP sequence is out of order]
[Release-log]
Change-Id: Ib6575ac415af785dd5f4b728b7ef070eeb357b12
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/319-wifi-mac80211-mesh-fast-xmit-support.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/319-wifi-mac80211-mesh-fast-xmit-support.patch
index 4bd3d4c..b802960 100644
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/319-wifi-mac80211-mesh-fast-xmit-support.patch
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/319-wifi-mac80211-mesh-fast-xmit-support.patch
@@ -1,33 +1,32 @@
-From: Sriram R <quic_srirrama@quicinc.com>
-Date: Thu, 18 Aug 2022 12:35:42 +0530
+From: Felix Fietkau <nbd@nbd.name>
+Date: Sun, 26 Feb 2023 13:53:08 +0100
Subject: [PATCH] wifi: mac80211: mesh fast xmit support
-Currently fast xmit is supported in AP, STA and other device types where
-the destination doesn't change for the lifetime of its association by
-caching the static parts of the header that can be reused directly for
-every Tx such as addresses and updates only mutable header fields such as
-PN.
-This technique is not directly applicable for a Mesh device type due
-to the dynamic nature of the topology and protocol. The header is built
-based on the destination mesh device which is proxying a certain external
-device and based on the Mesh destination the next hop changes.
-And the RA/A1 which is the next hop for reaching the destination can
-vary during runtime as per the best route based on airtime. To accommodate
-these changes and to come up with a solution to avoid overhead during header
-generation, the headers comprising the MAC, Mesh and LLC part are cached
-whenever data for a certain external destination is sent.
-This cached header is reused every time a data is sent to that external
-destination.
+Previously, fast xmit only worked on interface types where initially a
+sta lookup is performed, and a cached header can be attached to the sta,
+requiring only some fields to be updated at runtime.
+
+This technique is not directly applicable for a mesh device type due
+to the dynamic nature of the topology and protocol. There are more
+addresses that need to be filled, and there is an extra header with a
+dynamic length based on the addressing mode.
+
+Change the code to cache entries contain a copy of the mesh subframe header +
+bridge tunnel header, as well as an embedded struct ieee80211_fast_tx, which
+contains the information for building the 802.11 header.
+
+Add a mesh specific early fast xmit call, which looks up a cached entry and
+adds only the mesh subframe header, before passing it over to the generic
+fast xmit code.
To ensure the changes in network are reflected in these cached headers,
flush affected cached entries on path changes, as well as other conditions
that currently trigger a fast xmit check in other modes (key changes etc.)
-In order to keep the cache small, use a short timeout for expiring cache
-entries.
+This code is loosely based on a previous implementation by:
+Sriram R <quic_srirrama@quicinc.com>
-Co-developed-by: Felix Fietkau <nbd@nbd.name>
-Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
+Signed-off-by: Ryder Lee <ryder.lee@mediatek.com>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
@@ -37,24 +36,23 @@
extern const struct cfg80211_ops mac80211_config_ops;
struct ieee80211_local;
-+struct mhdr_cache_entry;
++struct ieee80211_mesh_fast_tx;
/* Maximum number of broadcast/multicast frames to buffer when some of the
* associated stations are using power saving. */
-@@ -655,6 +656,20 @@ struct mesh_table {
+@@ -655,6 +656,19 @@ struct mesh_table {
atomic_t entries; /* Up to MAX_MESH_NEIGHBOURS */
};
+/**
-+ * struct mesh_hdr_cache - mesh fast xmit header cache
++ * struct mesh_tx_cache - mesh fast xmit header cache
+ *
-+ * @rhead: hash table containing struct mhdr_cache_entry, using skb DA as key
-+ * @walk_head: linked list containing all mhdr_cache_entry objects
-+ * @walk_lock: lock protecting walk_head and rhead
-+ * @enabled: indicates if header cache is initialized
++ * @rht: hash table containing struct ieee80211_mesh_fast_tx, using skb DA as key
++ * @walk_head: linked list containing all ieee80211_mesh_fast_tx objects
++ * @walk_lock: lock protecting walk_head and rht
+ */
-+struct mesh_hdr_cache {
-+ struct rhashtable rhead;
++struct mesh_tx_cache {
++ struct rhashtable rht;
+ struct hlist_head walk_head;
+ spinlock_t walk_lock;
+};
@@ -62,145 +60,270 @@
struct ieee80211_if_mesh {
struct timer_list housekeeping_timer;
struct timer_list mesh_path_timer;
-@@ -733,6 +748,7 @@ struct ieee80211_if_mesh {
+@@ -733,6 +747,7 @@ struct ieee80211_if_mesh {
struct mesh_table mpp_paths; /* Store paths for MPP&MAP */
int mesh_paths_generation;
int mpp_paths_generation;
-+ struct mesh_hdr_cache hdr_cache;
++ struct mesh_tx_cache tx_cache;
};
#ifdef CPTCFG_MAC80211_MESH
-@@ -1998,6 +2014,9 @@ int ieee80211_tx_control_port(struct wip
+@@ -1998,6 +2013,11 @@ int ieee80211_tx_control_port(struct wip
int link_id, u64 *cookie);
int ieee80211_probe_mesh_link(struct wiphy *wiphy, struct net_device *dev,
const u8 *buf, size_t len);
-+void __ieee80211_mesh_xmit_fast(struct ieee80211_sub_if_data *sdata,
-+ struct mhdr_cache_entry *entry,
-+ struct sk_buff *skb);
++void __ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
++ struct sta_info *sta,
++ struct ieee80211_fast_tx *fast_tx,
++ struct sk_buff *skb, bool ampdu,
++ const u8 *da, const u8 *sa);
/* HT */
void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
-@@ -780,6 +780,8 @@ static void ieee80211_mesh_housekeeping(
+@@ -10,6 +10,7 @@
+ #include <asm/unaligned.h>
+ #include "ieee80211_i.h"
+ #include "mesh.h"
++#include "wme.h"
+ #include "driver-ops.h"
+
+ static int mesh_allocated;
+@@ -698,6 +699,102 @@ ieee80211_mesh_update_bss_params(struct
+ __le32_to_cpu(he_oper->he_oper_params);
+ }
+
++bool ieee80211_mesh_xmit_fast(struct ieee80211_sub_if_data *sdata,
++ struct sk_buff *skb, u32 ctrl_flags)
++{
++ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
++ struct ieee80211_mesh_fast_tx *entry;
++ struct ieee80211s_hdr *meshhdr;
++ u8 sa[ETH_ALEN] __aligned(2);
++ struct tid_ampdu_tx *tid_tx;
++ struct sta_info *sta;
++ bool copy_sa = false;
++ u16 ethertype;
++ u8 tid;
++
++ if (ctrl_flags & IEEE80211_TX_CTRL_SKIP_MPATH_LOOKUP)
++ return false;
++
++ if (ifmsh->mshcfg.dot11MeshNolearn)
++ return false;
++
++ /* Add support for these cases later */
++ if (ifmsh->ps_peers_light_sleep || ifmsh->ps_peers_deep_sleep)
++ return false;
++
++ if (is_multicast_ether_addr(skb->data))
++ return false;
++
++ ethertype = (skb->data[12] << 8) | skb->data[13];
++ if (ethertype < ETH_P_802_3_MIN)
++ return false;
++
++ if (skb->sk && skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)
++ return false;
++
++ if (skb->ip_summed == CHECKSUM_PARTIAL) {
++ skb_set_transport_header(skb, skb_checksum_start_offset(skb));
++ if (skb_checksum_help(skb))
++ return false;
++ }
++
++ entry = mesh_fast_tx_get(sdata, skb->data);
++ if (!entry)
++ return false;
++
++ if (skb_headroom(skb) + 2 * ETH_ALEN < entry->hdrlen +
++ entry->fast_tx.hdr_len)
++ return false;
++
++ sta = rcu_dereference(entry->mpath->next_hop);
++ if (!sta)
++ return false;
++
++ tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
++ tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]);
++ if (tid_tx) {
++ if (!test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state))
++ return false;
++ if (tid_tx->timeout)
++ tid_tx->last_tx = jiffies;
++ }
++
++ /* If the skb is shared we need to obtain our own copy */
++ if (skb_shared(skb)) {
++ struct sk_buff *oskb = skb;
++
++ skb = skb_clone(skb, GFP_ATOMIC);
++ if (!skb)
++ return false;
++
++ kfree_skb(oskb);
++ }
++
++ skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, sta, skb));
++
++ meshhdr = (struct ieee80211s_hdr *)entry->hdr;
++ if ((meshhdr->flags & MESH_FLAGS_AE) == MESH_FLAGS_AE_A5_A6) {
++ /* preserve SA from eth header for 6-addr frames */
++ ether_addr_copy(sa, skb->data + ETH_ALEN);
++ copy_sa = true;
++ }
++
++ memcpy(skb_push(skb, entry->hdrlen - 2 * ETH_ALEN), entry->hdr,
++ entry->hdrlen);
++
++ meshhdr = (struct ieee80211s_hdr *)skb->data;
++ put_unaligned_le32(atomic_inc_return(&sdata->u.mesh.mesh_seqnum),
++ &meshhdr->seqnum);
++ meshhdr->ttl = sdata->u.mesh.mshcfg.dot11MeshTTL;
++ if (copy_sa)
++ ether_addr_copy(meshhdr->eaddr2, sa);
++
++ __ieee80211_xmit_fast(sdata, sta, &entry->fast_tx, skb, tid_tx,
++ entry->mpath->dst, sdata->vif.addr);
++
++ return true;
++}
++
+ /**
+ * ieee80211_fill_mesh_addresses - fill addresses of a locally originated mesh frame
+ * @hdr: 802.11 frame header
+@@ -780,6 +877,8 @@ static void ieee80211_mesh_housekeeping(
changed = mesh_accept_plinks_update(sdata);
ieee80211_mbss_info_change_notify(sdata, changed);
-+ mesh_hdr_cache_gc(sdata);
++ mesh_fast_tx_gc(sdata);
+
mod_timer(&ifmsh->housekeeping_timer,
round_jiffies(jiffies +
IEEE80211_MESH_HOUSEKEEPING_INTERVAL));
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
-@@ -122,11 +122,49 @@ struct mesh_path {
+@@ -122,11 +122,41 @@ struct mesh_path {
u8 rann_snd_addr[ETH_ALEN];
u32 rann_metric;
unsigned long last_preq_to_root;
-+ unsigned long fast_xmit_check;
++ unsigned long fast_tx_check;
bool is_root;
bool is_gate;
u32 path_change_count;
};
-+#define MESH_HEADER_CACHE_MAX_SIZE 512
-+#define MESH_HEADER_CACHE_THRESHOLD_SIZE 384
-+#define MESH_HEADER_CACHE_TIMEOUT 8000 /* msecs */
-+#define MESH_HEADER_MAX_LEN 68 /* mac+mesh+rfc1042 hdr */
++#define MESH_FAST_TX_CACHE_MAX_SIZE 512
++#define MESH_FAST_TX_CACHE_THRESHOLD_SIZE 384
++#define MESH_FAST_TX_CACHE_TIMEOUT 8000 /* msecs */
+
+/**
-+ * struct mhdr_cache_entry - Cached Mesh header entry
-+ * @addr_key: The Ethernet DA which is the key for this entry
-+ * @hdr: The cached header
-+ * @machdr_len: Total length of the mac header
-+ * @hdrlen: Length of this header entry
-+ * @key: Key corresponding to the nexthop stored in the header
-+ * @pn_offs: Offset to PN which is updated for every xmit
-+ * @band: band used for tx
-+ * @walk_list: list containing all the cached header entries
++ * struct ieee80211_mesh_fast_tx - cached mesh fast tx entry
+ * @rhash: rhashtable pointer
-+ * @mpath: The Mesh path corresponding to the Mesh DA
-+ * @mppath: The MPP entry corresponding to this DA
++ * @addr_key: The Ethernet DA which is the key for this entry
++ * @fast_tx: base fast_tx data
++ * @hdr: cached mesh and rfc1042 headers
++ * @hdrlen: length of mesh + rfc1042
++ * @walk_list: list containing all the fast tx entries
++ * @mpath: mesh path corresponding to the Mesh DA
++ * @mppath: MPP entry corresponding to this DA
+ * @timestamp: Last used time of this entry
-+ * @rcu: rcu to free this entry
-+ * @path_change_count: Stored path change value corresponding to the mpath
+ */
-+struct mhdr_cache_entry {
++struct ieee80211_mesh_fast_tx {
++ struct rhash_head rhash;
+ u8 addr_key[ETH_ALEN] __aligned(2);
-+ u8 hdr[MESH_HEADER_MAX_LEN];
-+ u16 machdr_len;
++
++ struct ieee80211_fast_tx fast_tx;
++ u8 hdr[sizeof(struct ieee80211s_hdr) + sizeof(rfc1042_header)];
+ u16 hdrlen;
-+ u8 pn_offs;
-+ u8 band;
-+ struct ieee80211_key __rcu *key;
-+ struct hlist_node walk_list;
-+ struct rhash_head rhash;
++
+ struct mesh_path *mpath, *mppath;
++ struct hlist_node walk_list;
+ unsigned long timestamp;
-+ struct rcu_head rcu;
+};
+
/* Recent multicast cache */
/* RMC_BUCKETS must be a power of 2, maximum 256 */
#define RMC_BUCKETS 256
-@@ -298,6 +336,15 @@ void mesh_path_discard_frame(struct ieee
+@@ -298,6 +328,20 @@ void mesh_path_discard_frame(struct ieee
void mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata);
bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt);
-+struct mhdr_cache_entry *
-+mesh_get_cached_hdr(struct ieee80211_sub_if_data *sdata, const u8 *addr);
-+void mesh_cache_hdr(struct ieee80211_sub_if_data *sdata,
-+ struct sk_buff *skb, struct mesh_path *mpath);
-+void mesh_hdr_cache_gc(struct ieee80211_sub_if_data *sdata);
-+void mesh_hdr_cache_flush(struct ieee80211_sub_if_data *sdata, const u8 *addr,
-+ bool is_mpp);
-+void mesh_refresh_path(struct ieee80211_sub_if_data *sdata,
++struct ieee80211_mesh_fast_tx *
++mesh_fast_tx_get(struct ieee80211_sub_if_data *sdata, const u8 *addr);
++bool ieee80211_mesh_xmit_fast(struct ieee80211_sub_if_data *sdata,
++ struct sk_buff *skb, u32 ctrl_flags);
++void mesh_fast_tx_cache(struct ieee80211_sub_if_data *sdata,
++ struct sk_buff *skb, struct mesh_path *mpath);
++void mesh_fast_tx_gc(struct ieee80211_sub_if_data *sdata);
++void mesh_fast_tx_flush_addr(struct ieee80211_sub_if_data *sdata,
++ const u8 *addr);
++void mesh_fast_tx_flush_mpath(struct mesh_path *mpath);
++void mesh_fast_tx_flush_sta(struct ieee80211_sub_if_data *sdata,
++ struct sta_info *sta);
++void mesh_path_refresh(struct ieee80211_sub_if_data *sdata,
+ struct mesh_path *mpath, const u8 *addr);
#ifdef CPTCFG_MAC80211_MESH
static inline
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
-@@ -491,8 +491,11 @@ static u32 hwmp_route_info_get(struct ie
+@@ -394,6 +394,7 @@ static u32 hwmp_route_info_get(struct ie
+ u32 orig_sn, orig_metric;
+ unsigned long orig_lifetime, exp_time;
+ u32 last_hop_metric, new_metric;
++ bool flush_mpath = false;
+ bool process = true;
+ u8 hopcount;
+
+@@ -491,8 +492,10 @@ static u32 hwmp_route_info_get(struct ie
}
if (fresh_info) {
- if (rcu_access_pointer(mpath->next_hop) != sta)
+ if (rcu_access_pointer(mpath->next_hop) != sta) {
mpath->path_change_count++;
-+ mesh_hdr_cache_flush(mpath->sdata, mpath->dst,
-+ false);
++ flush_mpath = true;
+ }
mesh_path_assign_nexthop(mpath, sta);
mpath->flags |= MESH_PATH_SN_VALID;
mpath->metric = new_metric;
-@@ -539,8 +542,11 @@ static u32 hwmp_route_info_get(struct ie
+@@ -502,6 +505,8 @@ static u32 hwmp_route_info_get(struct ie
+ mpath->hop_count = hopcount;
+ mesh_path_activate(mpath);
+ spin_unlock_bh(&mpath->state_lock);
++ if (flush_mpath)
++ mesh_fast_tx_flush_mpath(mpath);
+ ewma_mesh_fail_avg_init(&sta->mesh->fail_avg);
+ /* init it at a low value - 0 start is tricky */
+ ewma_mesh_fail_avg_add(&sta->mesh->fail_avg, 1);
+@@ -539,8 +544,10 @@ static u32 hwmp_route_info_get(struct ie
}
if (fresh_info) {
- if (rcu_access_pointer(mpath->next_hop) != sta)
+ if (rcu_access_pointer(mpath->next_hop) != sta) {
mpath->path_change_count++;
-+ mesh_hdr_cache_flush(mpath->sdata, mpath->dst,
-+ false);
++ flush_mpath = true;
+ }
mesh_path_assign_nexthop(mpath, sta);
mpath->metric = last_hop_metric;
mpath->exp_time = time_after(mpath->exp_time, exp_time)
-@@ -977,7 +983,7 @@ free:
- * Locking: the function must be called from within a rcu read lock block.
- *
- */
--static void mesh_queue_preq(struct mesh_path *mpath, u8 flags)
-+void mesh_queue_preq(struct mesh_path *mpath, u8 flags)
- {
- struct ieee80211_sub_if_data *sdata = mpath->sdata;
- struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
-@@ -1215,6 +1221,20 @@ static int mesh_nexthop_lookup_nolearn(s
+@@ -548,6 +555,8 @@ static u32 hwmp_route_info_get(struct ie
+ mpath->hop_count = 1;
+ mesh_path_activate(mpath);
+ spin_unlock_bh(&mpath->state_lock);
++ if (flush_mpath)
++ mesh_fast_tx_flush_mpath(mpath);
+ ewma_mesh_fail_avg_init(&sta->mesh->fail_avg);
+ /* init it at a low value - 0 start is tricky */
+ ewma_mesh_fail_avg_add(&sta->mesh->fail_avg, 1);
+@@ -1215,6 +1224,20 @@ static int mesh_nexthop_lookup_nolearn(s
return 0;
}
-+void mesh_refresh_path(struct ieee80211_sub_if_data *sdata,
++void mesh_path_refresh(struct ieee80211_sub_if_data *sdata,
+ struct mesh_path *mpath, const u8 *addr)
+{
+ if (mpath->flags & (MESH_PATH_REQ_QUEUED | MESH_PATH_FIXED |
@@ -217,7 +340,7 @@
/**
* mesh_nexthop_lookup - put the appropriate next hop on a mesh frame. Calling
* this function is considered "using" the associated mpath, so preempt a path
-@@ -1242,19 +1262,18 @@ int mesh_nexthop_lookup(struct ieee80211
+@@ -1242,19 +1265,15 @@ int mesh_nexthop_lookup(struct ieee80211
if (!mpath || !(mpath->flags & MESH_PATH_ACTIVE))
return -ENOENT;
@@ -228,18 +351,15 @@
- !(mpath->flags & MESH_PATH_RESOLVING) &&
- !(mpath->flags & MESH_PATH_FIXED))
- mesh_queue_preq(mpath, PREQ_Q_F_START | PREQ_Q_F_REFRESH);
-+ mesh_refresh_path(sdata, mpath, hdr->addr4);
++ mesh_path_refresh(sdata, mpath, hdr->addr4);
next_hop = rcu_dereference(mpath->next_hop);
if (next_hop) {
memcpy(hdr->addr1, next_hop->sta.addr, ETH_ALEN);
memcpy(hdr->addr2, sdata->vif.addr, ETH_ALEN);
ieee80211_mps_set_frame_flags(sdata, next_hop, hdr);
-+ /* Cache the whole header so as to use next time rather than resolving
-+ * and building it every time
-+ */
+ if (ieee80211_hw_check(&sdata->local->hw, SUPPORT_FAST_XMIT))
-+ mesh_cache_hdr(sdata, skb, mpath);
++ mesh_fast_tx_cache(sdata, skb, mpath);
return 0;
}
@@ -257,37 +377,37 @@
.hashfn = mesh_table_hash,
};
-+static const struct rhashtable_params mesh_hdr_rht_params = {
++static const struct rhashtable_params fast_tx_rht_params = {
+ .nelem_hint = 10,
+ .automatic_shrinking = true,
-+ .key_len = ETH_ALEN,
-+ .key_offset = offsetof(struct mhdr_cache_entry, addr_key),
-+ .head_offset = offsetof(struct mhdr_cache_entry, rhash),
++ .key_len = ETH_ALEN,
++ .key_offset = offsetof(struct ieee80211_mesh_fast_tx, addr_key),
++ .head_offset = offsetof(struct ieee80211_mesh_fast_tx, rhash),
+ .hashfn = mesh_table_hash,
+};
+
-+static void __mesh_hdr_cache_entry_free(void *ptr, void *tblptr)
++static void __mesh_fast_tx_entry_free(void *ptr, void *tblptr)
+{
-+ struct mhdr_cache_entry *mhdr = ptr;
++ struct ieee80211_mesh_fast_tx *entry = ptr;
+
-+ kfree_rcu(mhdr, rcu);
++ kfree_rcu(entry, fast_tx.rcu_head);
+}
+
-+static void mesh_hdr_cache_deinit(struct ieee80211_sub_if_data *sdata)
++static void mesh_fast_tx_deinit(struct ieee80211_sub_if_data *sdata)
+{
-+ struct mesh_hdr_cache *cache;
++ struct mesh_tx_cache *cache;
+
-+ cache = &sdata->u.mesh.hdr_cache;
-+ rhashtable_free_and_destroy(&cache->rhead,
-+ __mesh_hdr_cache_entry_free, NULL);
++ cache = &sdata->u.mesh.tx_cache;
++ rhashtable_free_and_destroy(&cache->rht,
++ __mesh_fast_tx_entry_free, NULL);
+}
+
-+static void mesh_hdr_cache_init(struct ieee80211_sub_if_data *sdata)
++static void mesh_fast_tx_init(struct ieee80211_sub_if_data *sdata)
+{
-+ struct mesh_hdr_cache *cache;
++ struct mesh_tx_cache *cache;
+
-+ cache = &sdata->u.mesh.hdr_cache;
-+ rhashtable_init(&cache->rhead, &mesh_hdr_rht_params);
++ cache = &sdata->u.mesh.tx_cache;
++ rhashtable_init(&cache->rht, &fast_tx_rht_params);
+ INIT_HLIST_HEAD(&cache->walk_head);
+ spin_lock_init(&cache->walk_lock);
+}
@@ -295,66 +415,75 @@
static inline bool mpath_expired(struct mesh_path *mpath)
{
return (mpath->flags & MESH_PATH_ACTIVE) &&
-@@ -381,6 +417,211 @@ struct mesh_path *mesh_path_new(struct i
+@@ -381,6 +417,243 @@ struct mesh_path *mesh_path_new(struct i
return new_mpath;
}
-+struct mhdr_cache_entry *
-+mesh_get_cached_hdr(struct ieee80211_sub_if_data *sdata, const u8 *addr)
++static void mesh_fast_tx_entry_free(struct mesh_tx_cache *cache,
++ struct ieee80211_mesh_fast_tx *entry)
+{
-+ struct mesh_path *mpath, *mppath;
-+ struct mhdr_cache_entry *entry;
-+ struct mesh_hdr_cache *cache;
++ hlist_del_rcu(&entry->walk_list);
++ rhashtable_remove_fast(&cache->rht, &entry->rhash, fast_tx_rht_params);
++ kfree_rcu(entry, fast_tx.rcu_head);
++}
++
++struct ieee80211_mesh_fast_tx *
++mesh_fast_tx_get(struct ieee80211_sub_if_data *sdata, const u8 *addr)
++{
++ struct ieee80211_mesh_fast_tx *entry;
++ struct mesh_tx_cache *cache;
+
-+ cache = &sdata->u.mesh.hdr_cache;
-+ entry = rhashtable_lookup(&cache->rhead, addr, mesh_hdr_rht_params);
++ cache = &sdata->u.mesh.tx_cache;
++ entry = rhashtable_lookup(&cache->rht, addr, fast_tx_rht_params);
+ if (!entry)
+ return NULL;
+
-+ mpath = rcu_dereference(entry->mpath);
-+ mppath = rcu_dereference(entry->mppath);
-+ if (!(mpath->flags & MESH_PATH_ACTIVE) || mpath_expired(mpath))
++ if (!(entry->mpath->flags & MESH_PATH_ACTIVE) ||
++ mpath_expired(entry->mpath)) {
++ spin_lock_bh(&cache->walk_lock);
++ entry = rhashtable_lookup(&cache->rht, addr, fast_tx_rht_params);
++ if (entry)
++ mesh_fast_tx_entry_free(cache, entry);
++ spin_unlock_bh(&cache->walk_lock);
+ return NULL;
++ }
+
-+ mesh_refresh_path(sdata, mpath, NULL);
-+ if (mppath)
-+ mppath->exp_time = jiffies;
++ mesh_path_refresh(sdata, entry->mpath, NULL);
++ if (entry->mppath)
++ entry->mppath->exp_time = jiffies;
+ entry->timestamp = jiffies;
+
+ return entry;
+}
+
-+void mesh_cache_hdr(struct ieee80211_sub_if_data *sdata,
-+ struct sk_buff *skb, struct mesh_path *mpath)
++void mesh_fast_tx_cache(struct ieee80211_sub_if_data *sdata,
++ struct sk_buff *skb, struct mesh_path *mpath)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-+ struct mesh_hdr_cache *cache;
-+ struct mhdr_cache_entry *mhdr, *old_mhdr;
++ struct ieee80211_mesh_fast_tx *entry, *prev;
++ struct ieee80211_mesh_fast_tx build = {};
+ struct ieee80211s_hdr *meshhdr;
-+ struct sta_info *next_hop;
++ struct mesh_tx_cache *cache;
+ struct ieee80211_key *key;
+ struct mesh_path *mppath;
-+ u16 meshhdr_len;
-+ u8 pn_offs = 0;
-+ int hdrlen;
-+
-+ if (sdata->noack_map)
-+ return;
++ struct sta_info *sta;
++ u8 *qc;
+
-+ if (!ieee80211_is_data_qos(hdr->frame_control))
++ if (sdata->noack_map ||
++ !ieee80211_is_data_qos(hdr->frame_control))
+ return;
+
-+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
-+ meshhdr = (struct ieee80211s_hdr *)(skb->data + hdrlen);
-+ meshhdr_len = ieee80211_get_mesh_hdrlen(meshhdr);
++ build.fast_tx.hdr_len = ieee80211_hdrlen(hdr->frame_control);
++ meshhdr = (struct ieee80211s_hdr *)(skb->data + build.fast_tx.hdr_len);
++ build.hdrlen = ieee80211_get_mesh_hdrlen(meshhdr);
+
-+ cache = &sdata->u.mesh.hdr_cache;
-+ if (atomic_read(&cache->rhead.nelems) >= MESH_HEADER_CACHE_MAX_SIZE)
++ cache = &sdata->u.mesh.tx_cache;
++ if (atomic_read(&cache->rht.nelems) >= MESH_FAST_TX_CACHE_MAX_SIZE)
+ return;
+
-+ next_hop = rcu_dereference(mpath->next_hop);
-+ if (!next_hop)
++ sta = rcu_dereference(mpath->next_hop);
++ if (!sta)
+ return;
+
+ if ((meshhdr->flags & MESH_FLAGS_AE) == MESH_FLAGS_AE_A5_A6) {
@@ -362,6 +491,7 @@
+ mppath = mpp_path_lookup(sdata, meshhdr->eaddr1);
+ if (!mppath)
+ return;
++ build.mppath = mppath;
+ } else if (ieee80211_has_a4(hdr->frame_control)) {
+ mppath = mpath;
+ } else {
@@ -369,14 +499,20 @@
+ }
+
+ /* rate limit, in case fast xmit can't be enabled */
-+ if (mppath->fast_xmit_check == jiffies)
++ if (mppath->fast_tx_check == jiffies)
+ return;
+
-+ mppath->fast_xmit_check = jiffies;
++ mppath->fast_tx_check = jiffies;
+
-+ key = rcu_access_pointer(next_hop->ptk[next_hop->ptk_idx]);
++ /*
++ * Same use of the sta lock as in ieee80211_check_fast_xmit, in order
++ * to protect against concurrent sta key updates.
++ */
++ spin_lock_bh(&sta->lock);
++ key = rcu_access_pointer(sta->ptk[sta->ptk_idx]);
+ if (!key)
+ key = rcu_access_pointer(sdata->default_unicast_key);
++ build.fast_tx.key = key;
+
+ if (key) {
+ bool gen_iv, iv_spc;
@@ -386,157 +522,184 @@
+
+ if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) ||
+ (key->flags & KEY_FLAG_TAINTED))
-+ return;
++ goto unlock_sta;
+
+ switch (key->conf.cipher) {
+ case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_CCMP_256:
+ if (gen_iv)
-+ pn_offs = hdrlen;
++ build.fast_tx.pn_offs = build.fast_tx.hdr_len;
+ if (gen_iv || iv_spc)
-+ hdrlen += IEEE80211_CCMP_HDR_LEN;
++ build.fast_tx.hdr_len += IEEE80211_CCMP_HDR_LEN;
+ break;
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ if (gen_iv)
-+ pn_offs = hdrlen;
++ build.fast_tx.pn_offs = build.fast_tx.hdr_len;
+ if (gen_iv || iv_spc)
-+ hdrlen += IEEE80211_GCMP_HDR_LEN;
++ build.fast_tx.hdr_len += IEEE80211_GCMP_HDR_LEN;
+ break;
+ default:
-+ return;
++ goto unlock_sta;
+ }
+ }
+
-+ if (WARN_ON_ONCE(hdrlen + meshhdr_len + sizeof(rfc1042_header) >
-+ MESH_HEADER_MAX_LEN))
-+ return;
++ memcpy(build.addr_key, mppath->dst, ETH_ALEN);
++ build.timestamp = jiffies;
++ build.fast_tx.band = info->band;
++ build.fast_tx.da_offs = offsetof(struct ieee80211_hdr, addr3);
++ build.fast_tx.sa_offs = offsetof(struct ieee80211_hdr, addr4);
++ build.mpath = mpath;
++ memcpy(build.hdr, meshhdr, build.hdrlen);
++ memcpy(build.hdr + build.hdrlen, rfc1042_header, sizeof(rfc1042_header));
++ build.hdrlen += sizeof(rfc1042_header);
++ memcpy(build.fast_tx.hdr, hdr, build.fast_tx.hdr_len);
+
-+ mhdr = kzalloc(sizeof(*mhdr), GFP_ATOMIC);
-+ if (!mhdr)
-+ return;
++ hdr = (struct ieee80211_hdr *)build.fast_tx.hdr;
++ if (build.fast_tx.key)
++ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+
-+ memcpy(mhdr->addr_key, mppath->dst, ETH_ALEN);
-+ mhdr->machdr_len = hdrlen;
-+ mhdr->hdrlen = mhdr->machdr_len + meshhdr_len + sizeof(rfc1042_header);
-+ rcu_assign_pointer(mhdr->mpath, mpath);
-+ if (meshhdr->flags & MESH_FLAGS_AE)
-+ rcu_assign_pointer(mhdr->mppath, mppath);
-+ rcu_assign_pointer(mhdr->key, key);
-+ mhdr->timestamp = jiffies;
-+ mhdr->band = info->band;
-+ mhdr->pn_offs = pn_offs;
++ qc = ieee80211_get_qos_ctl(hdr);
++ qc[1] |= IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT >> 8;
+
-+ if (pn_offs) {
-+ memcpy(mhdr->hdr, skb->data, pn_offs);
-+ memcpy(mhdr->hdr + mhdr->machdr_len, skb->data + pn_offs,
-+ mhdr->hdrlen - mhdr->machdr_len);
-+ } else {
-+ memcpy(mhdr->hdr, skb->data, mhdr->hdrlen);
++ entry = kmemdup(&build, sizeof(build), GFP_ATOMIC);
++ if (!entry)
++ goto unlock_sta;
++
++ spin_lock_bh(&cache->walk_lock);
++ prev = rhashtable_lookup_get_insert_fast(&cache->rht,
++ &entry->rhash,
++ fast_tx_rht_params);
++ if (unlikely(IS_ERR(prev))) {
++ kfree(entry);
++ goto unlock_cache;
+ }
+
-+ if (key) {
-+ hdr = (struct ieee80211_hdr *)mhdr->hdr;
-+ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
++ /*
++ * replace any previous entry in the hash table, in case we're
++ * replacing it with a different type (e.g. mpath -> mpp)
++ */
++ if (unlikely(prev)) {
++ rhashtable_replace_fast(&cache->rht, &prev->rhash,
++ &entry->rhash, fast_tx_rht_params);
++ hlist_del_rcu(&prev->walk_list);
++ kfree_rcu(prev, fast_tx.rcu_head);
+ }
+
-+ spin_lock_bh(&cache->walk_lock);
-+ old_mhdr = rhashtable_lookup_get_insert_fast(&cache->rhead,
-+ &mhdr->rhash,
-+ mesh_hdr_rht_params);
-+ if (likely(!old_mhdr))
-+ hlist_add_head(&mhdr->walk_list, &cache->walk_head);
-+ else
-+ kfree(mhdr);
-+ spin_unlock_bh(&cache->walk_lock);
-+}
++ hlist_add_head(&entry->walk_list, &cache->walk_head);
+
-+static void mesh_hdr_cache_entry_free(struct mesh_hdr_cache *cache,
-+ struct mhdr_cache_entry *entry)
-+{
-+ hlist_del_rcu(&entry->walk_list);
-+ rhashtable_remove_fast(&cache->rhead, &entry->rhash, mesh_hdr_rht_params);
-+ kfree_rcu(entry, rcu);
++unlock_cache:
++ spin_unlock_bh(&cache->walk_lock);
++unlock_sta:
++ spin_unlock_bh(&sta->lock);
+}
+
-+void mesh_hdr_cache_gc(struct ieee80211_sub_if_data *sdata)
++void mesh_fast_tx_gc(struct ieee80211_sub_if_data *sdata)
+{
-+ unsigned long timeout = msecs_to_jiffies(MESH_HEADER_CACHE_TIMEOUT);
-+ struct mesh_hdr_cache *cache;
-+ struct mhdr_cache_entry *entry;
++ unsigned long timeout = msecs_to_jiffies(MESH_FAST_TX_CACHE_TIMEOUT);
++ struct mesh_tx_cache *cache;
++ struct ieee80211_mesh_fast_tx *entry;
+ struct hlist_node *n;
+
-+ cache = &sdata->u.mesh.hdr_cache;
-+ if (atomic_read(&cache->rhead.nelems) < MESH_HEADER_CACHE_THRESHOLD_SIZE)
++ cache = &sdata->u.mesh.tx_cache;
++ if (atomic_read(&cache->rht.nelems) < MESH_FAST_TX_CACHE_THRESHOLD_SIZE)
+ return;
+
+ spin_lock_bh(&cache->walk_lock);
+ hlist_for_each_entry_safe(entry, n, &cache->walk_head, walk_list)
+ if (!time_is_after_jiffies(entry->timestamp + timeout))
-+ mesh_hdr_cache_entry_free(cache, entry);
++ mesh_fast_tx_entry_free(cache, entry);
+ spin_unlock_bh(&cache->walk_lock);
+}
+
-+void mesh_hdr_cache_flush(struct ieee80211_sub_if_data *sdata, const u8 *addr,
-+ bool is_mpp)
++void mesh_fast_tx_flush_mpath(struct mesh_path *mpath)
+{
-+ struct mesh_hdr_cache *cache = &sdata->u.mesh.hdr_cache;
-+ struct mhdr_cache_entry *entry;
++ struct ieee80211_sub_if_data *sdata = mpath->sdata;
++ struct mesh_tx_cache *cache = &sdata->u.mesh.tx_cache;
++ struct ieee80211_mesh_fast_tx *entry;
+ struct hlist_node *n;
+
-+ cache = &sdata->u.mesh.hdr_cache;
++ cache = &sdata->u.mesh.tx_cache;
+ spin_lock_bh(&cache->walk_lock);
++ hlist_for_each_entry_safe(entry, n, &cache->walk_head, walk_list)
++ if (entry->mpath == mpath)
++ mesh_fast_tx_entry_free(cache, entry);
++ spin_unlock_bh(&cache->walk_lock);
++}
+
-+ /* Only one header per mpp address is expected in the header cache */
-+ if (is_mpp) {
-+ entry = rhashtable_lookup(&cache->rhead, addr,
-+ mesh_hdr_rht_params);
-+ if (entry)
-+ mesh_hdr_cache_entry_free(cache, entry);
-+ goto out;
-+ }
++void mesh_fast_tx_flush_sta(struct ieee80211_sub_if_data *sdata,
++ struct sta_info *sta)
++{
++ struct mesh_tx_cache *cache = &sdata->u.mesh.tx_cache;
++ struct ieee80211_mesh_fast_tx *entry;
++ struct hlist_node *n;
+
++ cache = &sdata->u.mesh.tx_cache;
++ spin_lock_bh(&cache->walk_lock);
+ hlist_for_each_entry_safe(entry, n, &cache->walk_head, walk_list)
-+ if (ether_addr_equal(entry->mpath->dst, addr))
-+ mesh_hdr_cache_entry_free(cache, entry);
++ if (rcu_access_pointer(entry->mpath->next_hop) == sta)
++ mesh_fast_tx_entry_free(cache, entry);
++ spin_unlock_bh(&cache->walk_lock);
++}
++
++void mesh_fast_tx_flush_addr(struct ieee80211_sub_if_data *sdata,
++ const u8 *addr)
++{
++ struct mesh_tx_cache *cache = &sdata->u.mesh.tx_cache;
++ struct ieee80211_mesh_fast_tx *entry;
+
-+out:
++ cache = &sdata->u.mesh.tx_cache;
++ spin_lock_bh(&cache->walk_lock);
++ entry = rhashtable_lookup(&cache->rht, addr, fast_tx_rht_params);
++ if (entry)
++ mesh_fast_tx_entry_free(cache, entry);
+ spin_unlock_bh(&cache->walk_lock);
+}
+
/**
* mesh_path_add - allocate and add a new path to the mesh path table
* @dst: destination address of the path (ETH_ALEN length)
-@@ -521,6 +762,8 @@ static void mesh_path_free_rcu(struct me
+@@ -464,6 +737,8 @@ int mpp_path_add(struct ieee80211_sub_if
+
+ if (ret)
+ kfree(new_mpath);
++ else
++ mesh_fast_tx_flush_addr(sdata, dst);
- static void __mesh_path_del(struct mesh_table *tbl, struct mesh_path *mpath)
+ sdata->u.mesh.mpp_paths_generation++;
+ return ret;
+@@ -523,6 +798,10 @@ static void __mesh_path_del(struct mesh_
{
-+ mesh_hdr_cache_flush(mpath->sdata, mpath->dst,
-+ tbl == &mpath->sdata->u.mesh.mpp_paths);
hlist_del_rcu(&mpath->walk_list);
rhashtable_remove_fast(&tbl->rhead, &mpath->rhash, mesh_rht_params);
++ if (tbl == &mpath->sdata->u.mesh.mpp_paths)
++ mesh_fast_tx_flush_addr(mpath->sdata, mpath->dst);
++ else
++ mesh_fast_tx_flush_mpath(mpath);
mesh_path_free_rcu(tbl, mpath);
-@@ -747,6 +990,7 @@ void mesh_path_fix_nexthop(struct mesh_p
+ }
+
+@@ -747,6 +1026,7 @@ void mesh_path_fix_nexthop(struct mesh_p
mpath->exp_time = 0;
mpath->flags = MESH_PATH_FIXED | MESH_PATH_SN_VALID;
mesh_path_activate(mpath);
-+ mesh_hdr_cache_flush(mpath->sdata, mpath->dst, false);
++ mesh_fast_tx_flush_mpath(mpath);
spin_unlock_bh(&mpath->state_lock);
ewma_mesh_fail_avg_init(&next_hop->mesh->fail_avg);
/* init it at a low value - 0 start is tricky */
-@@ -758,6 +1002,7 @@ void mesh_pathtbl_init(struct ieee80211_
+@@ -758,6 +1038,7 @@ void mesh_pathtbl_init(struct ieee80211_
{
mesh_table_init(&sdata->u.mesh.mesh_paths);
mesh_table_init(&sdata->u.mesh.mpp_paths);
-+ mesh_hdr_cache_init(sdata);
++ mesh_fast_tx_init(sdata);
}
static
-@@ -785,6 +1030,7 @@ void mesh_path_expire(struct ieee80211_s
+@@ -785,6 +1066,7 @@ void mesh_path_expire(struct ieee80211_s
void mesh_pathtbl_unregister(struct ieee80211_sub_if_data *sdata)
{
-+ mesh_hdr_cache_deinit(sdata);
++ mesh_fast_tx_deinit(sdata);
mesh_table_free(&sdata->u.mesh.mesh_paths);
mesh_table_free(&sdata->u.mesh.mpp_paths);
}
@@ -565,7 +728,7 @@
+
+ /* flush fast xmit cache if the address path changed */
+ if (update)
-+ mesh_hdr_cache_flush(sdata, proxied_addr, true);
++ mesh_fast_tx_flush_addr(sdata, proxied_addr);
+
rcu_read_unlock();
}
@@ -577,168 +740,105 @@
return;
+ if (ieee80211_vif_is_mesh(&sdata->vif))
-+ mesh_hdr_cache_flush(sdata, sta->addr, false);
++ mesh_fast_tx_flush_sta(sdata, sta);
+
/* Locking here protects both the pointer itself, and against concurrent
* invocations winning data access races to, e.g., the key pointer that
* is used.
-@@ -3723,6 +3726,155 @@ free:
- kfree_skb(skb);
- }
+@@ -3402,6 +3405,9 @@ static bool ieee80211_amsdu_aggregate(st
+ if (sdata->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED)
+ return false;
-+void __ieee80211_mesh_xmit_fast(struct ieee80211_sub_if_data *sdata,
-+ struct mhdr_cache_entry *entry,
-+ struct sk_buff *skb)
-+{
-+ struct ieee80211_local *local = sdata->local;
-+ struct ieee80211_tx_data tx = {};
-+ struct ieee80211_tx_info *info;
-+ struct ieee80211_key *key;
-+ struct ieee80211_hdr *hdr;
-+ struct mesh_path *mpath;
-+ ieee80211_tx_result r;
-+ struct sta_info *sta;
-+ u8 tid;
-+
-+ if (!IS_ENABLED(CPTCFG_MAC80211_MESH))
-+ return;
-+
-+ info = IEEE80211_SKB_CB(skb);
-+ memset(info, 0, sizeof(*info));
-+ info->band = entry->band;
-+ info->control.vif = &sdata->vif;
-+ info->flags = IEEE80211_TX_CTL_FIRST_FRAGMENT |
-+ IEEE80211_TX_CTL_DONTFRAG;
-+
-+ info->control.flags = IEEE80211_TX_CTRL_FAST_XMIT;
-+
-+#ifdef CONFIG_MAC80211_DEBUGFS
-+ if (local->force_tx_status)
-+ info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
-+#endif
-+
-+ mpath = entry->mpath;
-+ key = entry->key;
-+ sta = rcu_dereference(mpath->next_hop);
-+
-+ __skb_queue_head_init(&tx.skbs);
-+
-+ tx.flags = IEEE80211_TX_UNICAST;
-+ tx.local = local;
-+ tx.sdata = sdata;
-+ tx.sta = sta;
-+ tx.key = key;
-+ tx.skb = skb;
-+
-+ hdr = (struct ieee80211_hdr *)skb->data;
-+ tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
-+ *ieee80211_get_qos_ctl(hdr) = tid;
-+
-+ ieee80211_aggr_check(sdata, sta, skb);
-+
-+ if (ieee80211_queue_skb(local, sdata, sta, skb))
-+ return;
-+
-+ r = ieee80211_xmit_fast_finish(sdata, sta, entry->pn_offs, key, &tx);
-+ if (r == TX_DROP) {
-+ kfree_skb(skb);
-+ return;
-+ }
-+
-+ __skb_queue_tail(&tx.skbs, skb);
-+ ieee80211_tx_frags(local, &sdata->vif, sta, &tx.skbs, false);
-+}
-+
-+
-+static bool ieee80211_mesh_xmit_fast(struct ieee80211_sub_if_data *sdata,
-+ struct sk_buff *skb, u32 ctrl_flags)
-+{
-+ struct ieee80211_local *local = sdata->local;
-+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
-+ struct mhdr_cache_entry *entry;
-+ struct ieee80211s_hdr *meshhdr;
-+ u8 sa[ETH_ALEN] __aligned(2);
-+ struct sta_info *sta;
-+ bool copy_sa = false;
-+ u16 ethertype;
-+
-+ if (ctrl_flags & IEEE80211_TX_CTRL_SKIP_MPATH_LOOKUP)
-+ return false;
-+
-+ if (ifmsh->mshcfg.dot11MeshNolearn)
-+ return false;
-+
-+ if (!ieee80211_hw_check(&local->hw, SUPPORT_FAST_XMIT))
-+ return false;
-+
-+ /* Add support for these cases later */
-+ if (ifmsh->ps_peers_light_sleep || ifmsh->ps_peers_deep_sleep)
-+ return false;
-+
-+ if (is_multicast_ether_addr(skb->data))
-+ return false;
-+
-+ ethertype = (skb->data[12] << 8) | skb->data[13];
-+ if (ethertype < ETH_P_802_3_MIN)
-+ return false;
-+
-+ if (skb->sk && skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)
-+ return false;
-+
-+ if (skb->ip_summed == CHECKSUM_PARTIAL) {
-+ skb_set_transport_header(skb, skb_checksum_start_offset(skb));
-+ if (skb_checksum_help(skb))
-+ return false;
-+ }
-+
-+ entry = mesh_get_cached_hdr(sdata, skb->data);
-+ if (!entry)
-+ return false;
-+
-+ /* Avoid extra work in this path */
-+ if (skb_headroom(skb) < (entry->hdrlen - ETH_HLEN + 2))
++ if (ieee80211_vif_is_mesh(&sdata->vif))
+ return false;
+
-+ /* If the skb is shared we need to obtain our own copy */
-+ if (skb_shared(skb)) {
-+ struct sk_buff *oskb = skb;
-+
-+ skb = skb_clone(skb, GFP_ATOMIC);
-+ if (!skb)
-+ return false;
-+
-+ kfree_skb(oskb);
-+ }
-+
-+ sta = rcu_dereference(entry->mpath->next_hop);
-+ skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, sta, skb));
-+
-+ meshhdr = (struct ieee80211s_hdr *)(entry->hdr + entry->machdr_len);
-+ if ((meshhdr->flags & MESH_FLAGS_AE) == MESH_FLAGS_AE_A5_A6) {
-+ /* preserve SA from eth header for 6-addr frames */
-+ ether_addr_copy(sa, skb->data + ETH_ALEN);
-+ copy_sa = true;
-+ }
-+
-+ memcpy(skb_push(skb, entry->hdrlen - 2 * ETH_ALEN), entry->hdr,
-+ entry->hdrlen);
-+
-+ meshhdr = (struct ieee80211s_hdr *)(skb->data + entry->machdr_len);
-+ put_unaligned_le32(atomic_inc_return(&sdata->u.mesh.mesh_seqnum),
-+ &meshhdr->seqnum);
-+ meshhdr->ttl = sdata->u.mesh.mshcfg.dot11MeshTTL;
-+ if (copy_sa)
-+ ether_addr_copy(meshhdr->eaddr2, sa);
-+
-+ __ieee80211_mesh_xmit_fast(sdata, entry, skb);
-+
-+ return true;
-+}
+ if (skb_is_gso(skb))
+ return false;
+
+@@ -3634,10 +3640,11 @@ free:
+ return NULL;
+ }
+
+-static void __ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
+- struct sta_info *sta,
+- struct ieee80211_fast_tx *fast_tx,
+- struct sk_buff *skb, u8 tid, bool ampdu)
++void __ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
++ struct sta_info *sta,
++ struct ieee80211_fast_tx *fast_tx,
++ struct sk_buff *skb, bool ampdu,
++ const u8 *da, const u8 *sa)
+ {
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_hdr *hdr = (void *)fast_tx->hdr;
+@@ -3645,8 +3652,6 @@ static void __ieee80211_xmit_fast(struct
+ struct ieee80211_tx_data tx;
+ ieee80211_tx_result r;
+ int hw_headroom = sdata->local->hw.extra_tx_headroom;
+- int extra_head = fast_tx->hdr_len - (ETH_HLEN - 2);
+- struct ethhdr eth;
+
+ skb = skb_share_check(skb, GFP_ATOMIC);
+ if (unlikely(!skb))
+@@ -3661,16 +3666,15 @@ static void __ieee80211_xmit_fast(struct
+ * more room than we already have in 'extra_head'
+ */
+ if (unlikely(ieee80211_skb_resize(sdata, skb,
+- max_t(int, extra_head + hw_headroom -
++ max_t(int, fast_tx->hdr_len + hw_headroom -
+ skb_headroom(skb), 0),
+ ENCRYPT_NO)))
+ goto free;
+
+- memcpy(ð, skb->data, ETH_HLEN - 2);
+- hdr = skb_push(skb, extra_head);
++ hdr = skb_push(skb, fast_tx->hdr_len);
+ memcpy(skb->data, fast_tx->hdr, fast_tx->hdr_len);
+- memcpy(skb->data + fast_tx->da_offs, eth.h_dest, ETH_ALEN);
+- memcpy(skb->data + fast_tx->sa_offs, eth.h_source, ETH_ALEN);
++ memcpy(skb->data + fast_tx->da_offs, da, ETH_ALEN);
++ memcpy(skb->data + fast_tx->sa_offs, sa, ETH_ALEN);
+
+ info = IEEE80211_SKB_CB(skb);
+ memset(info, 0, sizeof(*info));
+@@ -3689,7 +3693,7 @@ static void __ieee80211_xmit_fast(struct
+ #endif
+
+ if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) {
+- tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
++ u8 tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
+ *ieee80211_get_qos_ctl(hdr) = tid;
+ }
+
+@@ -3732,6 +3736,7 @@ static bool ieee80211_xmit_fast(struct i
+ struct ieee80211_hdr *hdr = (void *)fast_tx->hdr;
+ struct tid_ampdu_tx *tid_tx = NULL;
+ struct sk_buff *next;
++ struct ethhdr eth;
+ u8 tid = IEEE80211_NUM_TIDS;
+
+ /* control port protocol needs a lot of special handling */
+@@ -3757,14 +3762,18 @@ static bool ieee80211_xmit_fast(struct i
+ }
+ }
+
++ memcpy(ð, skb->data, ETH_HLEN - 2);
+
- static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
- struct sta_info *sta,
- struct ieee80211_fast_tx *fast_tx,
-@@ -4244,8 +4396,14 @@ void __ieee80211_subif_start_xmit(struct
+ /* after this point (skb is modified) we cannot return false */
++ skb_pull(skb, ETH_HLEN - 2);
+ skb = ieee80211_tx_skb_fixup(skb, ieee80211_sdata_netdev_features(sdata));
+ if (!skb)
+ return true;
+
+ skb_list_walk_safe(skb, skb, next) {
+ skb_mark_not_on_list(skb);
+- __ieee80211_xmit_fast(sdata, sta, fast_tx, skb, tid, tid_tx);
++ __ieee80211_xmit_fast(sdata, sta, fast_tx, skb, tid_tx,
++ eth.h_dest, eth.h_source);
+ }
+
+ return true;
+@@ -4244,8 +4253,15 @@ void __ieee80211_subif_start_xmit(struct
return;
}
@@ -747,13 +847,14 @@
rcu_read_lock();
+ if (ieee80211_vif_is_mesh(&sdata->vif) &&
++ ieee80211_hw_check(&local->hw, SUPPORT_FAST_XMIT) &&
+ ieee80211_mesh_xmit_fast(sdata, skb, ctrl_flags))
+ goto out;
+
if (ieee80211_lookup_ra_sta(sdata, skb, &sta))
goto out_free;
-@@ -4255,8 +4413,6 @@ void __ieee80211_subif_start_xmit(struct
+@@ -4255,8 +4271,6 @@ void __ieee80211_subif_start_xmit(struct
skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, sta, skb));
ieee80211_aggr_check(sdata, sta, skb);
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/320-wifi-mac80211-use-mesh-header-cache-to-speed-up-mesh.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/320-wifi-mac80211-use-mesh-header-cache-to-speed-up-mesh.patch
index e0d4e60..3b0bae6 100644
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/320-wifi-mac80211-use-mesh-header-cache-to-speed-up-mesh.patch
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/320-wifi-mac80211-use-mesh-header-cache-to-speed-up-mesh.patch
@@ -3,40 +3,92 @@
Subject: [PATCH] wifi: mac80211: use mesh header cache to speed up mesh
forwarding
-Use it to look up the next hop address + sta pointer + key and call
-__ieee80211_mesh_xmit_fast to queue the tx frame.
-
Significantly reduces mesh forwarding path CPU usage and enables the
-use of iTXQ.
+direct use of iTXQ.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
-@@ -2731,6 +2731,7 @@ ieee80211_rx_mesh_data(struct ieee80211_
- struct ieee80211_hdr hdr = {
- .frame_control = cpu_to_le16(fc)
- };
-+ struct mhdr_cache_entry *entry = NULL;
- struct ieee80211_hdr *fwd_hdr;
- struct ieee80211s_hdr *mesh_hdr;
- struct ieee80211_tx_info *info;
-@@ -2788,7 +2789,12 @@ ieee80211_rx_mesh_data(struct ieee80211_
- return RX_DROP_MONITOR;
+@@ -2720,6 +2720,65 @@ ieee80211_deliver_skb(struct ieee80211_r
}
+ }
-- if (mesh_hdr->flags & MESH_FLAGS_AE) {
++#ifdef CPTCFG_MAC80211_MESH
++static bool
++ieee80211_rx_mesh_fast_forward(struct ieee80211_sub_if_data *sdata,
++ struct sk_buff *skb, int hdrlen)
++{
++ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
++ struct ieee80211_mesh_fast_tx *entry = NULL;
++ struct ieee80211s_hdr *mesh_hdr;
++ struct tid_ampdu_tx *tid_tx;
++ struct sta_info *sta;
++ struct ethhdr eth;
++ u8 tid;
++
++ mesh_hdr = (struct ieee80211s_hdr *)(skb->data + sizeof(eth));
+ if ((mesh_hdr->flags & MESH_FLAGS_AE) == MESH_FLAGS_AE_A5_A6)
-+ entry = mesh_get_cached_hdr(sdata, mesh_hdr->eaddr1);
++ entry = mesh_fast_tx_get(sdata, mesh_hdr->eaddr1);
+ else if (!(mesh_hdr->flags & MESH_FLAGS_AE))
-+ entry = mesh_get_cached_hdr(sdata, eth->h_dest);
++ entry = mesh_fast_tx_get(sdata, skb->data);
++ if (!entry)
++ return false;
++
++ sta = rcu_dereference(entry->mpath->next_hop);
++ if (!sta)
++ return false;
++
++ if (skb_linearize(skb))
++ return false;
++
++ tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
++ tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]);
++ if (tid_tx) {
++ if (!test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state))
++ return false;
++
++ if (tid_tx->timeout)
++ tid_tx->last_tx = jiffies;
++ }
++
++ ieee80211_aggr_check(sdata, sta, skb);
++
++ if (ieee80211_get_8023_tunnel_proto(skb->data + hdrlen,
++ &skb->protocol))
++ hdrlen += ETH_ALEN;
++ else
++ skb->protocol = htons(skb->len - hdrlen);
++ skb_set_network_header(skb, hdrlen + 2);
++
++ skb->dev = sdata->dev;
++ memcpy(ð, skb->data, ETH_HLEN - 2);
++ skb_pull(skb, sizeof(eth));
++ __ieee80211_xmit_fast(sdata, sta, &entry->fast_tx, skb, tid_tx,
++ eth.h_dest, eth.h_source);
++ IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_unicast);
++ IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_frames);
+
-+ if (!entry && (mesh_hdr->flags & MESH_FLAGS_AE)) {
- struct mesh_path *mppath;
- char *proxied_addr;
- bool update = false;
-@@ -2862,11 +2868,23 @@ ieee80211_rx_mesh_data(struct ieee80211_
++ return true;
++}
++#endif
++
+ static ieee80211_rx_result
+ ieee80211_rx_mesh_data(struct ieee80211_sub_if_data *sdata, struct sta_info *sta,
+ struct sk_buff *skb)
+@@ -2824,6 +2883,10 @@ ieee80211_rx_mesh_data(struct ieee80211_
+
+ skb_set_queue_mapping(skb, ieee802_1d_to_ac[skb->priority]);
+
++ if (!multicast &&
++ ieee80211_rx_mesh_fast_forward(sdata, skb, mesh_hdrlen))
++ return RX_QUEUED;
++
+ ieee80211_fill_mesh_addresses(&hdr, &hdr.frame_control,
+ eth->h_dest, eth->h_source);
+ hdrlen = ieee80211_hdrlen(hdr.frame_control);
+@@ -2862,6 +2925,7 @@ ieee80211_rx_mesh_data(struct ieee80211_
info->control.flags |= IEEE80211_TX_INTCFL_NEED_TXPROCESSING;
info->control.vif = &sdata->vif;
info->control.jiffies = jiffies;
@@ -44,23 +96,7 @@
if (multicast) {
IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_mcast);
memcpy(fwd_hdr->addr2, sdata->vif.addr, ETH_ALEN);
- /* update power mode indication when forwarding */
- ieee80211_mps_set_frame_flags(sdata, NULL, fwd_hdr);
-+ } else if (entry) {
-+ struct ieee80211_hdr *ehdr = (struct ieee80211_hdr *)entry->hdr;
-+
-+ ether_addr_copy(fwd_hdr->addr1, ehdr->addr1);
-+ ether_addr_copy(fwd_hdr->addr2, sdata->vif.addr);
-+ IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_unicast);
-+ IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_frames);
-+ qos[0] = fwd_skb->priority;
-+ qos[1] = ieee80211_get_qos_ctl(ehdr)[1];
-+ __ieee80211_mesh_xmit_fast(sdata, entry, fwd_skb);
-+ return RX_QUEUED;
- } else if (!mesh_nexthop_lookup(sdata, fwd_skb)) {
- /* mesh power mode flags updated in mesh_nexthop_lookup */
- IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_unicast);
-@@ -2883,7 +2901,6 @@ ieee80211_rx_mesh_data(struct ieee80211_
+@@ -2883,7 +2947,6 @@ ieee80211_rx_mesh_data(struct ieee80211_
}
IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_frames);
@@ -68,3 +104,29 @@
ieee80211_add_pending_skb(local, fwd_skb);
rx_accept:
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -2018,6 +2018,8 @@ void __ieee80211_xmit_fast(struct ieee80
+ struct ieee80211_fast_tx *fast_tx,
+ struct sk_buff *skb, bool ampdu,
+ const u8 *da, const u8 *sa);
++void ieee80211_aggr_check(struct ieee80211_sub_if_data *sdata,
++ struct sta_info *sta, struct sk_buff *skb);
+
+ /* HT */
+ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -1191,10 +1191,8 @@ 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)
++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;
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/321-mac80211-fix-mesh-forwarding.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/321-mac80211-fix-mesh-forwarding.patch
index d9af8c7..e2b268a 100644
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/321-mac80211-fix-mesh-forwarding.patch
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/321-mac80211-fix-mesh-forwarding.patch
@@ -11,7 +11,7 @@
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
-@@ -2847,6 +2847,9 @@ ieee80211_rx_mesh_data(struct ieee80211_
+@@ -2904,6 +2904,9 @@ ieee80211_rx_mesh_data(struct ieee80211_
if (skb_cow_head(fwd_skb, hdrlen - sizeof(struct ethhdr)))
return RX_DROP_UNUSABLE;
@@ -21,7 +21,7 @@
}
fwd_hdr = skb_push(fwd_skb, hdrlen - sizeof(struct ethhdr));
-@@ -2861,7 +2864,7 @@ ieee80211_rx_mesh_data(struct ieee80211_
+@@ -2918,7 +2921,7 @@ ieee80211_rx_mesh_data(struct ieee80211_
hdrlen += ETH_ALEN;
else
fwd_skb->protocol = htons(fwd_skb->len - hdrlen);
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/322-wifi-mac80211-fix-mesh-path-discovery-based-on-unica.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/322-wifi-mac80211-fix-mesh-path-discovery-based-on-unica.patch
new file mode 100644
index 0000000..292a89e
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/322-wifi-mac80211-fix-mesh-path-discovery-based-on-unica.patch
@@ -0,0 +1,52 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Sun, 26 Feb 2023 20:30:20 +0100
+Subject: [PATCH] wifi: mac80211: fix mesh path discovery based on unicast
+ packets
+
+If a packet has reached its intended destination, it was bumped to the code
+that accepts it, without first checking if a mesh_path needs to be created
+based on the discovered source.
+Fix this by moving the destination address check further down
+
+Fixes: 986e43b19ae9 ("wifi: mac80211: fix receiving A-MSDU frames on mesh interfaces")
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/mac80211/rx.c
++++ b/net/mac80211/rx.c
+@@ -2824,17 +2824,6 @@ ieee80211_rx_mesh_data(struct ieee80211_
+ mesh_rmc_check(sdata, eth->h_source, mesh_hdr))
+ return RX_DROP_MONITOR;
+
+- /* Frame has reached destination. Don't forward */
+- if (ether_addr_equal(sdata->vif.addr, eth->h_dest))
+- goto rx_accept;
+-
+- if (!ifmsh->mshcfg.dot11MeshForwarding) {
+- if (is_multicast_ether_addr(eth->h_dest))
+- goto rx_accept;
+-
+- return RX_DROP_MONITOR;
+- }
+-
+ /* forward packet */
+ if (sdata->crypto_tx_tailroom_needed_cnt)
+ tailroom = IEEE80211_ENCRYPT_TAILROOM;
+@@ -2881,6 +2870,17 @@ ieee80211_rx_mesh_data(struct ieee80211_
+ rcu_read_unlock();
+ }
+
++ /* Frame has reached destination. Don't forward */
++ if (ether_addr_equal(sdata->vif.addr, eth->h_dest))
++ goto rx_accept;
++
++ if (!ifmsh->mshcfg.dot11MeshForwarding) {
++ if (is_multicast_ether_addr(eth->h_dest))
++ goto rx_accept;
++
++ return RX_DROP_MONITOR;
++ }
++
+ skb_set_queue_mapping(skb, ieee802_1d_to_ac[skb->priority]);
+
+ if (!multicast &&
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/500-mac80211_configure_antenna_gain.patch b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/500-mac80211_configure_antenna_gain.patch
index 817be9e..80ffb49 100644
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/500-mac80211_configure_antenna_gain.patch
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/500-mac80211_configure_antenna_gain.patch
@@ -87,7 +87,7 @@
CFG80211_TESTMODE_DUMP(ieee80211_testmode_dump)
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
-@@ -1536,6 +1536,7 @@ struct ieee80211_local {
+@@ -1535,6 +1535,7 @@ struct ieee80211_local {
int dynamic_ps_forced_timeout;
int user_power_level; /* in dBm, for all interfaces */
diff --git a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/subsys.inc b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/subsys.inc
index 45bcf93..db2b20a 100644
--- a/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/subsys.inc
+++ b/recipes-wifi/linux-mac80211/files/patches-6.x/subsys/subsys.inc
@@ -26,6 +26,7 @@
file://319-wifi-mac80211-mesh-fast-xmit-support.patch \
file://320-wifi-mac80211-use-mesh-header-cache-to-speed-up-mesh.patch \
file://321-mac80211-fix-mesh-forwarding.patch \
+ file://322-wifi-mac80211-fix-mesh-path-discovery-based-on-unica.patch \
file://400-allow-ibss-mixed.patch \
file://500-mac80211_configure_antenna_gain.patch \
file://782-net-next-1-of-net-pass-the-dst-buffer-to-of_get_mac_address.patch \
diff --git a/recipes-wifi/linux-mac80211/files/patches/subsys/mtk-0013-mac80211-mtk-check-the-control-channel-before-downgr.patch b/recipes-wifi/linux-mac80211/files/patches/subsys/mtk-0012-mac80211-mtk-check-the-control-channel-before-downgr.patch
similarity index 91%
rename from recipes-wifi/linux-mac80211/files/patches/subsys/mtk-0013-mac80211-mtk-check-the-control-channel-before-downgr.patch
rename to recipes-wifi/linux-mac80211/files/patches/subsys/mtk-0012-mac80211-mtk-check-the-control-channel-before-downgr.patch
index cf2f1b5..f5903f0 100644
--- a/recipes-wifi/linux-mac80211/files/patches/subsys/mtk-0013-mac80211-mtk-check-the-control-channel-before-downgr.patch
+++ b/recipes-wifi/linux-mac80211/files/patches/subsys/mtk-0012-mac80211-mtk-check-the-control-channel-before-downgr.patch
@@ -1,7 +1,7 @@
-From 0d87b80d522117ea4c074ff3f7767f76fdd3df2d Mon Sep 17 00:00:00 2001
+From 750e991ce9cf04af982bb11e6058c133d205d879 Mon Sep 17 00:00:00 2001
From: mtk31095 <michael-cy.lee@mediatek.com>
Date: Fri, 16 Dec 2022 10:37:53 +0800
-Subject: [PATCH 912/915] mac80211: mtk: check the control channel before
+Subject: [PATCH 12/14] mac80211: mtk: check the control channel before
downgrading the bandwidth
Signed-off-by: mtk31095 <michael-cy.lee@mediatek.com>
@@ -51,5 +51,5 @@
ifmgd->flags |= ieee80211_chandef_downgrade(&chandef);
ret = ieee80211_vif_use_channel(sdata, &chandef,
--
-2.36.1
+2.18.0
diff --git a/recipes-wifi/linux-mac80211/files/patches/subsys/mtk-0012-mac80211-mtk-fix-the-issue-of-AP-and-STA-starting-on.patch b/recipes-wifi/linux-mac80211/files/patches/subsys/mtk-0012-mac80211-mtk-fix-the-issue-of-AP-and-STA-starting-on.patch
deleted file mode 100644
index b089f6a..0000000
--- a/recipes-wifi/linux-mac80211/files/patches/subsys/mtk-0012-mac80211-mtk-fix-the-issue-of-AP-and-STA-starting-on.patch
+++ /dev/null
@@ -1,258 +0,0 @@
-From 7aebd936d9c3b2f1d1bbd2d9e9996b67fde989a1 Mon Sep 17 00:00:00 2001
-From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
-Date: Wed, 5 Oct 2022 19:13:43 +0800
-Subject: [PATCH 911/915] mac80211: mtk: fix the issue of AP and STA starting
- on DFS channel concurrently
-
-Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
----
- include/net/cfg80211.h | 21 +++++++++++++++++
- include/uapi/linux/nl80211.h | 2 +-
- net/mac80211/cfg.c | 44 ++++++++++++++++++++++++++++++++++++
- net/mac80211/chan.c | 2 +-
- net/wireless/chan.c | 6 ++---
- net/wireless/nl80211.c | 8 +++++++
- net/wireless/rdev-ops.h | 16 +++++++++++++
- net/wireless/trace.h | 15 ++++++++++++
- 8 files changed, 109 insertions(+), 5 deletions(-)
-
-diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
-index b97ddbd..c4c0926 100644
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -800,6 +800,24 @@ cfg80211_chandef_identical(const struct cfg80211_chan_def *chandef1,
- chandef1->center_freq2 == chandef2->center_freq2);
- }
-
-+/**
-+ * cfg80211_chan_fully_overlap - check if two channel are fully overlapped
-+ * @chandef1: first channel definition
-+ * @chandef2: second channel definition
-+ *
-+ * Return: %true if the channels are valid and fully overlapped, %false otherwise.
-+ */
-+static inline bool
-+cfg80211_chan_fully_overlap(const struct cfg80211_chan_def *chandef1,
-+ const struct cfg80211_chan_def *chandef2)
-+{
-+ return (chandef1->center_freq1 != 0 &&
-+ chandef1->center_freq1 == chandef2->center_freq1 &&
-+ chandef1->width == chandef2->width &&
-+ chandef1->freq1_offset == chandef2->freq1_offset &&
-+ chandef1->center_freq2 == chandef2->center_freq2);
-+}
-+
- /**
- * cfg80211_chandef_is_edmg - check if chandef represents an EDMG channel
- *
-@@ -4402,6 +4420,8 @@ struct cfg80211_ops {
- int (*set_radar_background)(struct wiphy *wiphy,
- struct cfg80211_chan_def *chandef);
- void (*skip_cac)(struct wireless_dev *wdev);
-+ void (*check_cac_skip)(struct wiphy *wiphy,
-+ struct cfg80211_chan_def *chandef);
- };
-
- /*
-@@ -5555,6 +5575,7 @@ struct wireless_dev {
- struct work_struct pmsr_free_wk;
-
- unsigned long unprot_beacon_reported;
-+ bool start_disabled;
- };
-
- static inline u8 *wdev_address(struct wireless_dev *wdev)
-diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
-index e674aa7..ada8288 100644
---- a/include/uapi/linux/nl80211.h
-+++ b/include/uapi/linux/nl80211.h
-@@ -3129,7 +3129,7 @@ enum nl80211_attrs {
- NL80211_ATTR_WIPHY_ANTENNA_GAIN,
-
- /* add attributes here, update the policy in nl80211.c */
--
-+ NL80211_ATTR_START_DISABLED = 999,
- __NL80211_ATTR_AFTER_LAST,
- NUM_NL80211_ATTR = __NL80211_ATTR_AFTER_LAST,
- NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1
-diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
-index 0a6257d..a7b6284 100644
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -4519,6 +4519,49 @@ ieee80211_skip_cac(struct wireless_dev *wdev)
- }
- }
-
-+static void
-+ieee80211_check_cac_skip(struct wiphy *wiphy,
-+ struct cfg80211_chan_def *chandef)
-+{
-+ struct ieee80211_local *local = wiphy_priv(wiphy);
-+ struct ieee80211_sub_if_data *s1;
-+ struct ieee80211_sub_if_data *s2;
-+ struct ieee80211_sub_if_data *sdata_sta;
-+ struct ieee80211_if_managed *ifmgd;
-+ struct ieee80211_channel *chan;
-+ struct wireless_dev *wdev;
-+ unsigned int cac_time_ms;
-+
-+ mutex_lock(&local->mtx);
-+ /* Bypass AP's cac if there is a STA associated to the same DFS channel */
-+ list_for_each_entry(s1, &local->interfaces, list) {
-+ ifmgd = &s1->u.mgd;
-+
-+ if (s1->vif.type == NL80211_IFTYPE_STATION && ifmgd->associated)
-+ sdata_sta = s1;
-+ else
-+ continue;
-+
-+ list_for_each_entry(s2, &local->interfaces, list) {
-+ wdev = &s2->wdev;
-+ chan = wdev->chandef.chan;
-+ if (chan) {
-+ if (!(chan->flags & IEEE80211_CHAN_RADAR))
-+ continue;
-+
-+ if (wdev->identifier != sdata_sta->wdev.identifier &&
-+ chan->dfs_state == NL80211_DFS_USABLE && wdev->cac_started &&
-+ cfg80211_chan_fully_overlap(&sdata_sta->vif.bss_conf.chandef,
-+ &s2->vif.bss_conf.chandef)) {
-+ ieee80211_skip_cac(wdev);
-+ sdata_info(s2, "Skip CAC on the associated STA's chan\n");
-+ }
-+ }
-+ }
-+ }
-+ mutex_unlock(&local->mtx);
-+}
-+
- const struct cfg80211_ops mac80211_config_ops = {
- .add_virtual_intf = ieee80211_add_iface,
- .del_virtual_intf = ieee80211_del_iface,
-@@ -4626,4 +4669,5 @@ const struct cfg80211_ops mac80211_config_ops = {
- .color_change = ieee80211_color_change,
- .set_radar_background = ieee80211_set_radar_background,
- .skip_cac = ieee80211_skip_cac,
-+ .check_cac_skip = ieee80211_check_cac_skip,
- };
-diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
-index 63e15f5..5e57e4a 100644
---- a/net/mac80211/chan.c
-+++ b/net/mac80211/chan.c
-@@ -505,7 +505,7 @@ bool ieee80211_is_radar_required(struct ieee80211_local *local)
-
- rcu_read_lock();
- list_for_each_entry_rcu(sdata, &local->interfaces, list) {
-- if (sdata->radar_required) {
-+ if (sdata->radar_required && sdata->wdev.cac_started) {
- rcu_read_unlock();
- return true;
- }
-diff --git a/net/wireless/chan.c b/net/wireless/chan.c
-index 5f50ac4..067ed79 100644
---- a/net/wireless/chan.c
-+++ b/net/wireless/chan.c
-@@ -664,13 +664,13 @@ bool cfg80211_beaconing_iface_active(struct wireless_dev *wdev)
- switch (wdev->iftype) {
- case NL80211_IFTYPE_AP:
- case NL80211_IFTYPE_P2P_GO:
-- active = wdev->beacon_interval != 0;
-+ active = wdev->beacon_interval != 0 || wdev->start_disabled;
- break;
- case NL80211_IFTYPE_ADHOC:
-- active = wdev->ssid_len != 0;
-+ active = wdev->ssid_len != 0 || wdev->start_disabled;
- break;
- case NL80211_IFTYPE_MESH_POINT:
-- active = wdev->mesh_id_len != 0;
-+ active = wdev->mesh_id_len != 0 || wdev->start_disabled;
- break;
- case NL80211_IFTYPE_STATION:
- case NL80211_IFTYPE_OCB:
-diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
-index a20aba5..8dc928d 100644
---- a/net/wireless/nl80211.c
-+++ b/net/wireless/nl80211.c
-@@ -803,6 +803,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
- NLA_POLICY_NESTED(nl80211_mbssid_config_policy),
- [NL80211_ATTR_MBSSID_ELEMS] = { .type = NLA_NESTED },
- [NL80211_ATTR_RADAR_BACKGROUND] = { .type = NLA_FLAG },
-+ [NL80211_ATTR_START_DISABLED] = { .type = NLA_FLAG },
- [NL80211_ATTR_WIPHY_ANTENNA_GAIN] = { .type = NLA_U32 },
- };
-
-@@ -5547,6 +5548,12 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
-
- memset(¶ms, 0, sizeof(params));
-
-+ if (info->attrs[NL80211_ATTR_START_DISABLED]) {
-+ wdev->start_disabled = nla_get_flag(info->attrs[NL80211_ATTR_START_DISABLED]);
-+ err = 0;
-+ goto out;
-+ }
-+
- /* these are required for START_AP */
- if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] ||
- !info->attrs[NL80211_ATTR_DTIM_PERIOD] ||
-@@ -9393,6 +9400,7 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
- wdev->cac_started = true;
- wdev->cac_start_time = jiffies;
- wdev->cac_time_ms = cac_time_ms;
-+ err = rdev_check_cac_skip(rdev, &wdev->chandef);
- }
- unlock:
- wiphy_unlock(wiphy);
-diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
-index 26f4604..c38aea1 100644
---- a/net/wireless/rdev-ops.h
-+++ b/net/wireless/rdev-ops.h
-@@ -1412,4 +1412,20 @@ rdev_skip_cac(struct cfg80211_registered_device *rdev,
- return 0;
- }
-
-+static inline int
-+rdev_check_cac_skip(struct cfg80211_registered_device *rdev,
-+ struct cfg80211_chan_def *chandef)
-+{
-+ struct wiphy *wiphy = &rdev->wiphy;
-+
-+ if (!rdev->ops->check_cac_skip)
-+ return -EOPNOTSUPP;
-+
-+ trace_rdev_check_cac_skip(wiphy, chandef);
-+ rdev->ops->check_cac_skip(wiphy, chandef);
-+ trace_rdev_return_void(wiphy);
-+
-+ return 0;
-+}
-+
- #endif /* __CFG80211_RDEV_OPS */
-diff --git a/net/wireless/trace.h b/net/wireless/trace.h
-index eadabfa..a7b0c82 100644
---- a/net/wireless/trace.h
-+++ b/net/wireless/trace.h
-@@ -3677,6 +3677,21 @@ TRACE_EVENT(rdev_skip_cac,
- TP_printk(WDEV_PR_FMT, WDEV_PR_ARG)
- );
-
-+TRACE_EVENT(rdev_check_cac_skip,
-+ TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef),
-+
-+ TP_ARGS(wiphy, chandef),
-+
-+ TP_STRUCT__entry(WIPHY_ENTRY
-+ CHAN_DEF_ENTRY),
-+
-+ TP_fast_assign(WIPHY_ASSIGN;
-+ CHAN_DEF_ASSIGN(chandef)),
-+
-+ TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT,
-+ WIPHY_PR_ARG, CHAN_DEF_PR_ARG)
-+);
-+
- #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
-
- #undef TRACE_INCLUDE_PATH
---
-2.36.1
-
diff --git a/recipes-wifi/linux-mac80211/files/patches/subsys/mtk-0014-mac80211-mtk-fix-tx-amsdu-aggregation.patch b/recipes-wifi/linux-mac80211/files/patches/subsys/mtk-0013-mac80211-mtk-fix-tx-amsdu-aggregation.patch
similarity index 93%
rename from recipes-wifi/linux-mac80211/files/patches/subsys/mtk-0014-mac80211-mtk-fix-tx-amsdu-aggregation.patch
rename to recipes-wifi/linux-mac80211/files/patches/subsys/mtk-0013-mac80211-mtk-fix-tx-amsdu-aggregation.patch
index 371958d..da8632a 100644
--- a/recipes-wifi/linux-mac80211/files/patches/subsys/mtk-0014-mac80211-mtk-fix-tx-amsdu-aggregation.patch
+++ b/recipes-wifi/linux-mac80211/files/patches/subsys/mtk-0013-mac80211-mtk-fix-tx-amsdu-aggregation.patch
@@ -1,7 +1,7 @@
-From 491811bea8080d41f28438947d51c712ccf91d0e Mon Sep 17 00:00:00 2001
+From b4f6a720d5a0aeaeb2772ce99cf89f7bede959bf Mon Sep 17 00:00:00 2001
From: TomLiu <tomml.liu@mediatek.com>
Date: Wed, 14 Dec 2022 00:26:50 -0800
-Subject: [PATCH 913/915] mac80211: mtk: fix tx amsdu aggregation
+Subject: [PATCH 13/14] mac80211: mtk: fix tx amsdu aggregation
---
include/net/mac80211.h | 7 +++++++
@@ -51,5 +51,5 @@
capab |= u16_encode_bits(tid, IEEE80211_ADDBA_PARAM_TID_MASK);
capab |= u16_encode_bits(agg_size, IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK);
--
-2.36.1
+2.18.0
diff --git a/recipes-wifi/linux-mac80211/files/patches/subsys/mtk-0014-mac80211-mtk-add-sta-assisted-DFS-state-update-mecha.patch b/recipes-wifi/linux-mac80211/files/patches/subsys/mtk-0014-mac80211-mtk-add-sta-assisted-DFS-state-update-mecha.patch
new file mode 100644
index 0000000..2006b27
--- /dev/null
+++ b/recipes-wifi/linux-mac80211/files/patches/subsys/mtk-0014-mac80211-mtk-add-sta-assisted-DFS-state-update-mecha.patch
@@ -0,0 +1,176 @@
+From 8fdded8b5d47f1ab5fb7ab1fd2a4247cd81edad0 Mon Sep 17 00:00:00 2001
+From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+Date: Mon, 20 Feb 2023 14:25:24 +0800
+Subject: [PATCH] mac80211: mtk: add sta-assisted DFS state update mechanism
+
+Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
+---
+ include/net/cfg80211.h | 14 +++++++++
+ include/uapi/linux/nl80211.h | 6 ++++
+ net/mac80211/mlme.c | 11 +++++++
+ net/wireless/chan.c | 60 ++++++++++++++++++++++++++++++++++++
+ 4 files changed, 91 insertions(+)
+
+diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
+index b97ddbd..02ad2b2 100644
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -7641,6 +7641,20 @@ void cfg80211_cac_event(struct net_device *netdev,
+ const struct cfg80211_chan_def *chandef,
+ enum nl80211_radar_event event, gfp_t gfp);
+
++/**
++ * cfg80211_sta_update_dfs_state - Update channel's DFS state during STA channel switch,
++ * association, and disassociation
++ * @wdev: the wireless device
++ * @bss_chandef: the current BSS channel definition
++ * @csa_chandef: the CSA channel definition
++ * @associated: whether STA is during association or disassociation process
++ *
++ */
++void cfg80211_sta_update_dfs_state(struct wireless_dev *wdev,
++ const struct cfg80211_chan_def *bss_chandef,
++ const struct cfg80211_chan_def *csa_chandef,
++ bool associated);
++
+ /**
+ * cfg80211_background_cac_abort - Channel Availability Check offchan abort event
+ * @wiphy: the wiphy
+diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
+index e674aa7..3e348a5 100644
+--- a/include/uapi/linux/nl80211.h
++++ b/include/uapi/linux/nl80211.h
+@@ -6294,6 +6294,10 @@ enum nl80211_smps_mode {
+ * applicable for ETSI dfs domain where pre-CAC is valid for ever.
+ * @NL80211_RADAR_CAC_STARTED: Channel Availability Check has been started,
+ * should be generated by HW if NL80211_EXT_FEATURE_DFS_OFFLOAD is enabled.
++ * @NL80211_RADAR_STA_CAC_SKIPPED: STA set the DFS state to available
++ * when receiving CSA/assoc resp
++ * @NL80211_RADAR_STA_CAC_EXPIRED: STA set the DFS state to usable
++ * when STA is disconnected or leaving the channel
+ */
+ enum nl80211_radar_event {
+ NL80211_RADAR_DETECTED,
+@@ -6302,6 +6306,8 @@ enum nl80211_radar_event {
+ NL80211_RADAR_NOP_FINISHED,
+ NL80211_RADAR_PRE_CAC_EXPIRED,
+ NL80211_RADAR_CAC_STARTED,
++ NL80211_RADAR_STA_CAC_SKIPPED,
++ NL80211_RADAR_STA_CAC_EXPIRED,
+ };
+
+ /**
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index 8ee325a..48053e4 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -1442,6 +1442,10 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
+ IEEE80211_QUEUE_STOP_REASON_CSA);
+ mutex_unlock(&local->mtx);
+
++ cfg80211_sta_update_dfs_state(&sdata->wdev,
++ &sdata->vif.bss_conf.chandef,
++ &sdata->csa_chandef,
++ sdata->vif.bss_conf.assoc);
+ cfg80211_ch_switch_started_notify(sdata->dev, &csa_ie.chandef,
+ csa_ie.count, csa_ie.mode);
+
+@@ -2420,6 +2424,10 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
+ cancel_delayed_work_sync(&ifmgd->tx_tspec_wk);
+
+ sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
++
++ cfg80211_sta_update_dfs_state(&sdata->wdev,
++ &sdata->vif.bss_conf.chandef,
++ NULL, sdata->vif.bss_conf.assoc);
+ }
+
+ static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata)
+@@ -3782,6 +3790,9 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
+ event.u.mlme.status = MLME_SUCCESS;
+ drv_event_callback(sdata->local, sdata, &event);
+ sdata_info(sdata, "associated\n");
++ cfg80211_sta_update_dfs_state(&sdata->wdev,
++ &sdata->vif.bss_conf.chandef,
++ NULL, sdata->vif.bss_conf.assoc);
+
+ /*
+ * destroy assoc_data afterwards, as otherwise an idle
+diff --git a/net/wireless/chan.c b/net/wireless/chan.c
+index 5f50ac4..0309758 100644
+--- a/net/wireless/chan.c
++++ b/net/wireless/chan.c
+@@ -14,6 +14,7 @@
+ #include <net/cfg80211.h>
+ #include "core.h"
+ #include "rdev-ops.h"
++#include "nl80211.h"
+
+ static bool cfg80211_valid_60g_freq(u32 freq)
+ {
+@@ -1386,3 +1387,62 @@ bool cfg80211_any_usable_channels(struct wiphy *wiphy,
+ return false;
+ }
+ EXPORT_SYMBOL(cfg80211_any_usable_channels);
++
++static void cfg80211_sta_radar_notify(struct wiphy *wiphy,
++ const struct cfg80211_chan_def *chandef,
++ enum nl80211_radar_event event)
++{
++ struct wireless_dev *wdev;
++
++ list_for_each_entry(wdev, &wiphy->wdev_list, list) {
++ if (cfg80211_chandef_dfs_required(wiphy, chandef, wdev->iftype) > 0) {
++ nl80211_radar_notify(wiphy_to_rdev(wiphy), chandef,
++ event, wdev->netdev, GFP_KERNEL);
++ return;
++ }
++ }
++}
++
++void cfg80211_sta_update_dfs_state(struct wireless_dev *wdev,
++ const struct cfg80211_chan_def *bss_chandef,
++ const struct cfg80211_chan_def *csa_chandef,
++ bool associated)
++{
++ bool csa_active = !!csa_chandef;
++ enum nl80211_dfs_state dfs_state = NL80211_DFS_USABLE;
++ enum nl80211_radar_event event = NL80211_RADAR_STA_CAC_EXPIRED;
++
++ if (!bss_chandef)
++ return;
++
++ /* assume csa channel is cac completed */
++ if (csa_active &&
++ (cfg80211_chandef_dfs_usable(wdev->wiphy, csa_chandef) ||
++ cfg80211_chandef_dfs_available(wdev->wiphy, csa_chandef))) {
++ cfg80211_set_dfs_state(wdev->wiphy, csa_chandef, NL80211_DFS_AVAILABLE);
++ cfg80211_sta_radar_notify(wdev->wiphy, csa_chandef,
++ NL80211_RADAR_STA_CAC_SKIPPED);
++ netdev_info(wdev->netdev, "Set CSA channel's DFS state to available\n");
++ }
++
++ /* avoid updating the dfs state during nop */
++ if (!cfg80211_chandef_dfs_usable(wdev->wiphy, bss_chandef) &&
++ !cfg80211_chandef_dfs_available(wdev->wiphy, bss_chandef))
++ return;
++
++ if (associated && !csa_active) {
++ dfs_state = NL80211_DFS_AVAILABLE;
++ event = NL80211_RADAR_STA_CAC_SKIPPED;
++ }
++
++ cfg80211_set_dfs_state(wdev->wiphy, bss_chandef, dfs_state);
++ cfg80211_sta_radar_notify(wdev->wiphy, bss_chandef, event);
++
++ if (csa_active)
++ netdev_info(wdev->netdev, "Set origin channel's DFS state to usable\n");
++ else
++ netdev_info(wdev->netdev, "Set BSS channel's DFS state to %s due to %s\n",
++ (dfs_state == NL80211_DFS_USABLE) ? "usable" : "available",
++ associated ? "association" : "disassociation");
++}
++EXPORT_SYMBOL(cfg80211_sta_update_dfs_state);
+--
+2.18.0
+
diff --git a/recipes-wifi/linux-mac80211/files/patches/subsys/subsys.inc b/recipes-wifi/linux-mac80211/files/patches/subsys/subsys.inc
index 86e3ccb..ae088f6 100644
--- a/recipes-wifi/linux-mac80211/files/patches/subsys/subsys.inc
+++ b/recipes-wifi/linux-mac80211/files/patches/subsys/subsys.inc
@@ -60,9 +60,9 @@
file://mtk-0009-mac80211-mtk-remove-timerout-handle-for-ax210-iot-is.patch \
file://mtk-0010-cfg80211-mtk-implement-DFS-status-show-cac-and-nop-s.patch \
file://mtk-0011-mac80211-mtk-Set-TWT-Information-Frame-Disabled-bit-.patch \
- file://mtk-0012-mac80211-mtk-fix-the-issue-of-AP-and-STA-starting-on.patch \
- file://mtk-0013-mac80211-mtk-check-the-control-channel-before-downgr.patch \
- file://mtk-0014-mac80211-mtk-fix-tx-amsdu-aggregation.patch \
+ file://mtk-0012-mac80211-mtk-check-the-control-channel-before-downgr.patch \
+ file://mtk-0013-mac80211-mtk-fix-tx-amsdu-aggregation.patch \
+ file://mtk-0014-mac80211-mtk-add-sta-assisted-DFS-state-update-mecha.patch \
file://mtk-9900-mac80211-mtk-mask-kernel-version-limitation-and-fil.patch \
file://mtk-9901-mac80211-mtk-add-fill-receive-path-ops-to-get-wed-i.patch \
file://mtk-9902-mac80211-mtk-register-.ndo_setup_tc-to-support-wifi2.patch \