[openwrt/openwrt] mac80211: rework mesh fast xmit implementation

LEDE Commits lede-commits at lists.infradead.org
Sun Feb 26 14:45:37 PST 2023


nbd pushed a commit to openwrt/openwrt.git, branch master:
https://git.openwrt.org/2f96580c5282cd138d2bb858f7f27f539c8a63e1

commit 2f96580c5282cd138d2bb858f7f27f539c8a63e1
Author: Felix Fietkau <nbd at nbd.name>
AuthorDate: Sun Feb 26 13:36:58 2023 +0100

    mac80211: rework mesh fast xmit implementation
    
    Refactor in order to make use of generic fast xmit functions
    Fix issues with mesh SA/DA addressing
    
    Signed-off-by: Felix Fietkau <nbd at nbd.name>
---
 .../319-wifi-mac80211-mesh-fast-xmit-support.patch | 791 +++++++++++----------
 ...11-use-mesh-header-cache-to-speed-up-mesh.patch | 140 +++-
 .../subsys/321-mac80211-fix-mesh-forwarding.patch  |   4 +-
 .../500-mac80211_configure_antenna_gain.patch      |   2 +-
 4 files changed, 506 insertions(+), 431 deletions(-)

diff --git a/package/kernel/mac80211/patches/subsys/319-wifi-mac80211-mesh-fast-xmit-support.patch b/package/kernel/mac80211/patches/subsys/319-wifi-mac80211-mesh-fast-xmit-support.patch
index f18e1bb377..b802960103 100644
--- a/package/kernel/mac80211/patches/subsys/319-wifi-mac80211-mesh-fast-xmit-support.patch
+++ b/package/kernel/mac80211/patches/subsys/319-wifi-mac80211-mesh-fast-xmit-support.patch
@@ -1,33 +1,32 @@
-From: Sriram R <quic_srirrama at quicinc.com>
-Date: Thu, 18 Aug 2022 12:35:42 +0530
+From: Felix Fietkau <nbd at 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 at quicinc.com>
 
-Co-developed-by: Felix Fietkau <nbd at nbd.name>
-Signed-off-by: Sriram R <quic_srirrama at quicinc.com>
+Signed-off-by: Ryder Lee <ryder.lee at mediatek.com>
 Signed-off-by: Felix Fietkau <nbd at nbd.name>
 ---
 
@@ -37,24 +36,23 @@ Signed-off-by: Felix Fietkau <nbd at nbd.name>
  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,102 +60,209 @@ Signed-off-by: Felix Fietkau <nbd at nbd.name>
  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 *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,18 @@ 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_mpp(struct ieee80211_sub_if_data *sdata,
-+			      const u8 *addr);
-+void mesh_hdr_cache_flush_mpath(struct mesh_path *mpath);
-+void mesh_hdr_cache_flush_sta(struct ieee80211_sub_if_data *sdata,
-+			      struct sta_info *sta);
-+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
@@ -189,7 +294,7 @@ Signed-off-by: Felix Fietkau <nbd at nbd.name>
  			mesh_path_activate(mpath);
  			spin_unlock_bh(&mpath->state_lock);
 +			if (flush_mpath)
-+				mesh_hdr_cache_flush_mpath(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);
@@ -210,24 +315,15 @@ Signed-off-by: Felix Fietkau <nbd at nbd.name>
  			mesh_path_activate(mpath);
  			spin_unlock_bh(&mpath->state_lock);
 +			if (flush_mpath)
-+				mesh_hdr_cache_flush_mpath(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);
-@@ -977,7 +986,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 +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 |
@@ -244,7 +340,7 @@ Signed-off-by: Felix Fietkau <nbd at nbd.name>
  /**
   * 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 +1265,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;
  
@@ -255,18 +351,15 @@ Signed-off-by: Felix Fietkau <nbd at nbd.name>
 -	    !(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;
  	}
  
@@ -284,37 +377,37 @@ Signed-off-by: Felix Fietkau <nbd at nbd.name>
  	.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);
 +}
@@ -322,40 +415,40 @@ Signed-off-by: Felix Fietkau <nbd at nbd.name>
  static inline bool mpath_expired(struct mesh_path *mpath)
  {
  	return (mpath->flags & MESH_PATH_ACTIVE) &&
-@@ -381,6 +417,254 @@ struct mesh_path *mesh_path_new(struct i
+@@ -381,6 +417,243 @@ struct mesh_path *mesh_path_new(struct i
  	return new_mpath;
  }
  
-+static void mesh_hdr_cache_entry_free(struct mesh_hdr_cache *cache,
-+				      struct mhdr_cache_entry *entry)
++static void mesh_fast_tx_entry_free(struct mesh_tx_cache *cache,
++				    struct ieee80211_mesh_fast_tx *entry)
 +{
 +	hlist_del_rcu(&entry->walk_list);
-+	rhashtable_remove_fast(&cache->rhead, &entry->rhash, mesh_hdr_rht_params);
-+	kfree_rcu(entry, rcu);
++	rhashtable_remove_fast(&cache->rht, &entry->rhash, fast_tx_rht_params);
++	kfree_rcu(entry, fast_tx.rcu_head);
 +}
 +
-+struct mhdr_cache_entry *
-+mesh_get_cached_hdr(struct ieee80211_sub_if_data *sdata, const u8 *addr)
++struct ieee80211_mesh_fast_tx *
++mesh_fast_tx_get(struct ieee80211_sub_if_data *sdata, const u8 *addr)
 +{
-+	struct mhdr_cache_entry *entry;
-+	struct mesh_hdr_cache *cache;
++	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;
 +
 +	if (!(entry->mpath->flags & MESH_PATH_ACTIVE) ||
 +	    mpath_expired(entry->mpath)) {
 +		spin_lock_bh(&cache->walk_lock);
-+		entry = rhashtable_lookup(&cache->rhead, addr, mesh_hdr_rht_params);
++		entry = rhashtable_lookup(&cache->rht, addr, fast_tx_rht_params);
 +		if (entry)
-+		    mesh_hdr_cache_entry_free(cache, entry);
++		    mesh_fast_tx_entry_free(cache, entry);
 +		spin_unlock_bh(&cache->walk_lock);
 +		return NULL;
 +	}
 +
-+	mesh_refresh_path(sdata, entry->mpath, NULL);
++	mesh_path_refresh(sdata, entry->mpath, NULL);
 +	if (entry->mppath)
 +		entry->mppath->exp_time = jiffies;
 +	entry->timestamp = jiffies;
@@ -363,33 +456,30 @@ Signed-off-by: Felix Fietkau <nbd at nbd.name>
 +	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 *sta;
++	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;
 +
 +	sta = rcu_dereference(mpath->next_hop);
@@ -401,6 +491,7 @@ Signed-off-by: Felix Fietkau <nbd at nbd.name>
 +		mppath = mpp_path_lookup(sdata, meshhdr->eaddr1);
 +		if (!mppath)
 +			return;
++		build.mppath = mppath;
 +	} else if (ieee80211_has_a4(hdr->frame_control)) {
 +		mppath = mpath;
 +	} else {
@@ -408,10 +499,10 @@ Signed-off-by: Felix Fietkau <nbd at nbd.name>
 +	}
 +
 +	/* 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;
 +
 +	/*
 +	 * Same use of the sta lock as in ieee80211_check_fast_xmit, in order
@@ -421,6 +512,7 @@ Signed-off-by: Felix Fietkau <nbd at nbd.name>
 +	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;
@@ -436,60 +528,50 @@ Signed-off-by: Felix Fietkau <nbd at nbd.name>
 +		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:
 +			goto unlock_sta;
 +		}
 +	}
 +
-+	if (WARN_ON_ONCE(hdrlen + meshhdr_len + sizeof(rfc1042_header) >
-+			 MESH_HEADER_MAX_LEN))
-+		goto unlock_sta;
-+
-+	mhdr = kzalloc(sizeof(*mhdr), GFP_ATOMIC);
-+	if (!mhdr)
-+		goto unlock_sta;
++	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);
++
++	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);
-+	mhdr->mpath = mpath;
-+	if (meshhdr->flags & MESH_FLAGS_AE)
-+		mhdr->mppath = mppath;
-+	mhdr->key = key;
-+	mhdr->timestamp = jiffies;
-+	mhdr->band = info->band;
-+	mhdr->pn_offs = pn_offs;
-+
-+	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);
-+	}
++	qc = ieee80211_get_qos_ctl(hdr);
++	qc[1] |= IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT >> 8;
 +
-+	if (key) {
-+		hdr = (struct ieee80211_hdr *)mhdr->hdr;
-+		hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
-+	}
++	entry = kmemdup(&build, sizeof(build), GFP_ATOMIC);
++	if (!entry)
++		goto unlock_sta;
 +
 +	spin_lock_bh(&cache->walk_lock);
-+	old_mhdr = rhashtable_lookup_get_insert_fast(&cache->rhead,
-+						     &mhdr->rhash,
-+						     mesh_hdr_rht_params);
-+	if (unlikely(IS_ERR(old_mhdr))) {
-+		kfree(mhdr);
++	prev = rhashtable_lookup_get_insert_fast(&cache->rht,
++						 &entry->rhash,
++						 fast_tx_rht_params);
++	if (unlikely(IS_ERR(prev))) {
++		kfree(entry);
 +		goto unlock_cache;
 +	}
 +
@@ -497,14 +579,14 @@ Signed-off-by: Felix Fietkau <nbd at nbd.name>
 +	 * replace any previous entry in the hash table, in case we're
 +	 * replacing it with a different type (e.g. mpath -> mpp)
 +	 */
-+	if (unlikely(old_mhdr)) {
-+		rhashtable_replace_fast(&cache->rhead, &old_mhdr->rhash,
-+					&mhdr->rhash, mesh_hdr_rht_params);
-+		hlist_del_rcu(&old_mhdr->walk_list);
-+		kfree_rcu(old_mhdr, rcu);
++	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);
 +	}
 +
-+	hlist_add_head(&mhdr->walk_list, &cache->walk_head);
++	hlist_add_head(&entry->walk_list, &cache->walk_head);
 +
 +unlock_cache:
 +	spin_unlock_bh(&cache->walk_lock);
@@ -512,112 +594,112 @@ Signed-off-by: Felix Fietkau <nbd at nbd.name>
 +	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_mpath(struct mesh_path *mpath)
++void mesh_fast_tx_flush_mpath(struct mesh_path *mpath)
 +{
 +	struct ieee80211_sub_if_data *sdata = mpath->sdata;
-+	struct mesh_hdr_cache *cache = &sdata->u.mesh.hdr_cache;
-+	struct mhdr_cache_entry *entry;
++	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_hdr_cache_entry_free(cache, entry);
++			mesh_fast_tx_entry_free(cache, entry);
 +	spin_unlock_bh(&cache->walk_lock);
 +}
 +
-+void mesh_hdr_cache_flush_sta(struct ieee80211_sub_if_data *sdata,
-+			      struct sta_info *sta)
++void mesh_fast_tx_flush_sta(struct ieee80211_sub_if_data *sdata,
++			    struct sta_info *sta)
 +{
-+	struct mesh_hdr_cache *cache = &sdata->u.mesh.hdr_cache;
-+	struct mhdr_cache_entry *entry;
++	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 (rcu_access_pointer(entry->mpath->next_hop) == sta)
-+			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_mpp(struct ieee80211_sub_if_data *sdata,
-+			      const u8 *addr)
++void mesh_fast_tx_flush_addr(struct ieee80211_sub_if_data *sdata,
++			     const u8 *addr)
 +{
-+	struct mesh_hdr_cache *cache = &sdata->u.mesh.hdr_cache;
-+	struct mhdr_cache_entry *entry;
++	struct mesh_tx_cache *cache = &sdata->u.mesh.tx_cache;
++	struct ieee80211_mesh_fast_tx *entry;
 +
-+	cache = &sdata->u.mesh.hdr_cache;
++	cache = &sdata->u.mesh.tx_cache;
 +	spin_lock_bh(&cache->walk_lock);
-+	entry = rhashtable_lookup(&cache->rhead, addr, mesh_hdr_rht_params);
++	entry = rhashtable_lookup(&cache->rht, addr, fast_tx_rht_params);
 +	if (entry)
-+		mesh_hdr_cache_entry_free(cache, 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)
-@@ -464,6 +748,8 @@ int mpp_path_add(struct ieee80211_sub_if
+@@ -464,6 +737,8 @@ int mpp_path_add(struct ieee80211_sub_if
  
  	if (ret)
  		kfree(new_mpath);
 +	else
-+		mesh_hdr_cache_flush_mpp(sdata, dst);
++		mesh_fast_tx_flush_addr(sdata, dst);
  
  	sdata->u.mesh.mpp_paths_generation++;
  	return ret;
-@@ -523,6 +809,10 @@ static void __mesh_path_del(struct mesh_
+@@ -523,6 +798,10 @@ static void __mesh_path_del(struct mesh_
  {
  	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_hdr_cache_flush_mpp(mpath->sdata, mpath->dst);
++		mesh_fast_tx_flush_addr(mpath->sdata, mpath->dst);
 +	else
-+		mesh_hdr_cache_flush_mpath(mpath);
++		mesh_fast_tx_flush_mpath(mpath);
  	mesh_path_free_rcu(tbl, mpath);
  }
  
-@@ -747,6 +1037,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(mpath);
++	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 +1049,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 +1077,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);
  }
@@ -646,7 +728,7 @@ Signed-off-by: Felix Fietkau <nbd at nbd.name>
 +
 +		/* flush fast xmit cache if the address path changed */
 +		if (update)
-+			mesh_hdr_cache_flush_mpp(sdata, proxied_addr);
++			mesh_fast_tx_flush_addr(sdata, proxied_addr);
 +
  		rcu_read_unlock();
  	}
@@ -658,175 +740,105 @@ Signed-off-by: Felix Fietkau <nbd at nbd.name>
  		return;
  
 +	if (ieee80211_vif_is_mesh(&sdata->vif))
-+		mesh_hdr_cache_flush_sta(sdata, sta);
++		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,162 @@ 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 tid_ampdu_tx *tid_tx;
-+	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;
-+	tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]);
-+	if (tid_tx) {
-+		if (tid_tx->timeout)
-+			tid_tx->last_tx = jiffies;
-+		info->flags |= IEEE80211_TX_CTL_AMPDU;
-+	}
-+
-+	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 (!ieee80211_hw_check(&local->hw, SUPPORT_FAST_XMIT))
-+		return false;
-+
-+	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_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(&eth, 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(&eth, 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 +4403,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;
  	}
  
@@ -835,13 +847,14 @@ Signed-off-by: Felix Fietkau <nbd at nbd.name>
  	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 +4420,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/package/kernel/mac80211/patches/subsys/320-wifi-mac80211-use-mesh-header-cache-to-speed-up-mesh.patch b/package/kernel/mac80211/patches/subsys/320-wifi-mac80211-use-mesh-header-cache-to-speed-up-mesh.patch
index e0d4e60ed7..3b0bae67a5 100644
--- a/package/kernel/mac80211/patches/subsys/320-wifi-mac80211-use-mesh-header-cache-to-speed-up-mesh.patch
+++ b/package/kernel/mac80211/patches/subsys/320-wifi-mac80211-use-mesh-header-cache-to-speed-up-mesh.patch
@@ -3,40 +3,92 @@ Date: Thu, 16 Feb 2023 11:07:30 +0100
 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 at 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(&eth, 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 @@ Signed-off-by: Felix Fietkau <nbd at nbd.name>
  	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 @@ Signed-off-by: Felix Fietkau <nbd at nbd.name>
  	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/package/kernel/mac80211/patches/subsys/321-mac80211-fix-mesh-forwarding.patch b/package/kernel/mac80211/patches/subsys/321-mac80211-fix-mesh-forwarding.patch
index d9af8c7929..e2b268ae4c 100644
--- a/package/kernel/mac80211/patches/subsys/321-mac80211-fix-mesh-forwarding.patch
+++ b/package/kernel/mac80211/patches/subsys/321-mac80211-fix-mesh-forwarding.patch
@@ -11,7 +11,7 @@ Signed-off-by: Felix Fietkau <nbd at nbd.name>
 
 --- 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 @@ Signed-off-by: Felix Fietkau <nbd at nbd.name>
  	}
  
  	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/package/kernel/mac80211/patches/subsys/500-mac80211_configure_antenna_gain.patch b/package/kernel/mac80211/patches/subsys/500-mac80211_configure_antenna_gain.patch
index 817be9e4d2..80ffb493b8 100644
--- a/package/kernel/mac80211/patches/subsys/500-mac80211_configure_antenna_gain.patch
+++ b/package/kernel/mac80211/patches/subsys/500-mac80211_configure_antenna_gain.patch
@@ -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 */




More information about the lede-commits mailing list