From: Felix Fietkau Date: Wed, 15 Feb 2023 18:40:02 +0000 (+0100) Subject: mac80211: fix mesh issues and improve performance X-Git-Tag: v23.05.0-rc1~952 X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=57db2280a2155c39f545ac712766a849cf45f62c;p=openwrt%2Fopenwrt.git mac80211: fix mesh issues and improve performance fix forwarding received mesh a-msdu packets add fast xmit support for mesh to improve performance Signed-off-by: Felix Fietkau --- diff --git a/package/kernel/mac80211/patches/subsys/317-wifi-mac80211-fix-qos-on-mesh-interfaces.patch b/package/kernel/mac80211/patches/subsys/317-wifi-mac80211-fix-qos-on-mesh-interfaces.patch new file mode 100644 index 0000000000..c60a88d2a6 --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/317-wifi-mac80211-fix-qos-on-mesh-interfaces.patch @@ -0,0 +1,35 @@ +From: Felix Fietkau +Date: Wed, 15 Feb 2023 15:11:54 +0100 +Subject: [PATCH] wifi: mac80211: fix qos on mesh interfaces + +When ieee80211_select_queue is called for mesh, the sta pointer is usually +NULL, since the nexthop is looked up much later in the tx path. +Explicitly check for unicast address in that case in order to make qos work +again. + +Fixes: 50e2ab392919 ("wifi: mac80211: fix queue selection for mesh/OCB interfaces") +Signed-off-by: Felix Fietkau +--- + +--- a/net/mac80211/wme.c ++++ b/net/mac80211/wme.c +@@ -147,6 +147,7 @@ u16 ieee80211_select_queue_80211(struct + u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, + struct sta_info *sta, struct sk_buff *skb) + { ++ const struct ethhdr *eth = (void *)skb->data; + struct mac80211_qos_map *qos_map; + bool qos; + +@@ -154,8 +155,9 @@ u16 ieee80211_select_queue(struct ieee80 + skb_get_hash(skb); + + /* all mesh/ocb stations are required to support WME */ +- if (sta && (sdata->vif.type == NL80211_IFTYPE_MESH_POINT || +- sdata->vif.type == NL80211_IFTYPE_OCB)) ++ if ((sdata->vif.type == NL80211_IFTYPE_MESH_POINT && ++ !is_multicast_ether_addr(eth->h_dest)) || ++ (sdata->vif.type == NL80211_IFTYPE_OCB && sta)) + qos = true; + else if (sta) + qos = sta->sta.wme; diff --git a/package/kernel/mac80211/patches/subsys/318-wifi-mac80211-fix-race-in-mesh-sequence-number-assig.patch b/package/kernel/mac80211/patches/subsys/318-wifi-mac80211-fix-race-in-mesh-sequence-number-assig.patch new file mode 100644 index 0000000000..05e368cd2e --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/318-wifi-mac80211-fix-race-in-mesh-sequence-number-assig.patch @@ -0,0 +1,37 @@ +From: Felix Fietkau +Date: Wed, 15 Feb 2023 15:21:37 +0100 +Subject: [PATCH] wifi: mac80211: fix race in mesh sequence number + assignment + +Since the sequence number is shared across different tx queues, it needs +to be atomic in order to avoid accidental duplicate assignment + +Signed-off-by: Felix Fietkau +--- + +--- a/net/mac80211/ieee80211_i.h ++++ b/net/mac80211/ieee80211_i.h +@@ -695,7 +695,7 @@ struct ieee80211_if_mesh { + struct mesh_stats mshstats; + struct mesh_config mshcfg; + atomic_t estab_plinks; +- u32 mesh_seqnum; ++ atomic_t mesh_seqnum; + bool accepting_plinks; + int num_gates; + struct beacon_data __rcu *beacon; +--- a/net/mac80211/mesh.c ++++ b/net/mac80211/mesh.c +@@ -752,10 +752,8 @@ unsigned int ieee80211_new_mesh_header(s + + meshhdr->ttl = sdata->u.mesh.mshcfg.dot11MeshTTL; + +- /* FIXME: racy -- TX on multiple queues can be concurrent */ +- put_unaligned(cpu_to_le32(sdata->u.mesh.mesh_seqnum), &meshhdr->seqnum); +- sdata->u.mesh.mesh_seqnum++; +- ++ put_unaligned_le32(atomic_inc_return(&sdata->u.mesh.mesh_seqnum), ++ &meshhdr->seqnum); + if (addr4or5 && !addr6) { + meshhdr->flags |= MESH_FLAGS_AE_A4; + memcpy(meshhdr->eaddr1, addr4or5, ETH_ALEN); 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 new file mode 100644 index 0000000000..4bd3d4c092 --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/319-wifi-mac80211-mesh-fast-xmit-support.patch @@ -0,0 +1,764 @@ +From: Sriram R +Date: Thu, 18 Aug 2022 12:35:42 +0530 +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. + +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. + +Co-developed-by: Felix Fietkau +Signed-off-by: Sriram R +Signed-off-by: Felix Fietkau +--- + +--- a/net/mac80211/ieee80211_i.h ++++ b/net/mac80211/ieee80211_i.h +@@ -37,6 +37,7 @@ + extern const struct cfg80211_ops mac80211_config_ops; + + struct ieee80211_local; ++struct mhdr_cache_entry; + + /* 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 { + atomic_t entries; /* Up to MAX_MESH_NEIGHBOURS */ + }; + ++/** ++ * struct mesh_hdr_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 ++ */ ++struct mesh_hdr_cache { ++ struct rhashtable rhead; ++ struct hlist_head walk_head; ++ spinlock_t walk_lock; ++}; ++ + struct ieee80211_if_mesh { + struct timer_list housekeeping_timer; + struct timer_list mesh_path_timer; +@@ -733,6 +748,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; + }; + + #ifdef CPTCFG_MAC80211_MESH +@@ -1998,6 +2014,9 @@ 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); + + /* 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( + changed = mesh_accept_plinks_update(sdata); + ieee80211_mbss_info_change_notify(sdata, changed); + ++ mesh_hdr_cache_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 { + u8 rann_snd_addr[ETH_ALEN]; + u32 rann_metric; + unsigned long last_preq_to_root; ++ unsigned long fast_xmit_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 */ ++ ++/** ++ * 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 ++ * @rhash: rhashtable pointer ++ * @mpath: The Mesh path corresponding to the Mesh DA ++ * @mppath: The 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 { ++ u8 addr_key[ETH_ALEN] __aligned(2); ++ u8 hdr[MESH_HEADER_MAX_LEN]; ++ u16 machdr_len; ++ 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; ++ 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 + 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 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 + } + + 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); ++ } + 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 + } + + 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); ++ } + 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 + return 0; + } + ++void mesh_refresh_path(struct ieee80211_sub_if_data *sdata, ++ struct mesh_path *mpath, const u8 *addr) ++{ ++ if (mpath->flags & (MESH_PATH_REQ_QUEUED | MESH_PATH_FIXED | ++ MESH_PATH_RESOLVING)) ++ return; ++ ++ if (time_after(jiffies, ++ mpath->exp_time - ++ msecs_to_jiffies(sdata->u.mesh.mshcfg.path_refresh_time)) && ++ (!addr || ether_addr_equal(sdata->vif.addr, addr))) ++ mesh_queue_preq(mpath, PREQ_Q_F_START | PREQ_Q_F_REFRESH); ++} ++ + /** + * 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 + if (!mpath || !(mpath->flags & MESH_PATH_ACTIVE)) + return -ENOENT; + +- if (time_after(jiffies, +- mpath->exp_time - +- msecs_to_jiffies(sdata->u.mesh.mshcfg.path_refresh_time)) && +- ether_addr_equal(sdata->vif.addr, hdr->addr4) && +- !(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); + + 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); + return 0; + } + +--- a/net/mac80211/mesh_pathtbl.c ++++ b/net/mac80211/mesh_pathtbl.c +@@ -14,6 +14,7 @@ + #include "wme.h" + #include "ieee80211_i.h" + #include "mesh.h" ++#include + + static void mesh_path_free_rcu(struct mesh_table *tbl, struct mesh_path *mpath); + +@@ -32,6 +33,41 @@ static const struct rhashtable_params me + .hashfn = mesh_table_hash, + }; + ++static const struct rhashtable_params mesh_hdr_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), ++ .hashfn = mesh_table_hash, ++}; ++ ++static void __mesh_hdr_cache_entry_free(void *ptr, void *tblptr) ++{ ++ struct mhdr_cache_entry *mhdr = ptr; ++ ++ kfree_rcu(mhdr, rcu); ++} ++ ++static void mesh_hdr_cache_deinit(struct ieee80211_sub_if_data *sdata) ++{ ++ struct mesh_hdr_cache *cache; ++ ++ cache = &sdata->u.mesh.hdr_cache; ++ rhashtable_free_and_destroy(&cache->rhead, ++ __mesh_hdr_cache_entry_free, NULL); ++} ++ ++static void mesh_hdr_cache_init(struct ieee80211_sub_if_data *sdata) ++{ ++ struct mesh_hdr_cache *cache; ++ ++ cache = &sdata->u.mesh.hdr_cache; ++ rhashtable_init(&cache->rhead, &mesh_hdr_rht_params); ++ INIT_HLIST_HEAD(&cache->walk_head); ++ spin_lock_init(&cache->walk_lock); ++} ++ + 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 + return new_mpath; + } + ++struct mhdr_cache_entry * ++mesh_get_cached_hdr(struct ieee80211_sub_if_data *sdata, const u8 *addr) ++{ ++ struct mesh_path *mpath, *mppath; ++ struct mhdr_cache_entry *entry; ++ struct mesh_hdr_cache *cache; ++ ++ cache = &sdata->u.mesh.hdr_cache; ++ entry = rhashtable_lookup(&cache->rhead, addr, mesh_hdr_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)) ++ return NULL; ++ ++ mesh_refresh_path(sdata, mpath, NULL); ++ if (mppath) ++ 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) ++{ ++ 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 ieee80211s_hdr *meshhdr; ++ struct sta_info *next_hop; ++ struct ieee80211_key *key; ++ struct mesh_path *mppath; ++ u16 meshhdr_len; ++ u8 pn_offs = 0; ++ int hdrlen; ++ ++ if (sdata->noack_map) ++ return; ++ ++ if (!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); ++ ++ cache = &sdata->u.mesh.hdr_cache; ++ if (atomic_read(&cache->rhead.nelems) >= MESH_HEADER_CACHE_MAX_SIZE) ++ return; ++ ++ next_hop = rcu_dereference(mpath->next_hop); ++ if (!next_hop) ++ return; ++ ++ if ((meshhdr->flags & MESH_FLAGS_AE) == MESH_FLAGS_AE_A5_A6) { ++ /* This is required to keep the mppath alive */ ++ mppath = mpp_path_lookup(sdata, meshhdr->eaddr1); ++ if (!mppath) ++ return; ++ } else if (ieee80211_has_a4(hdr->frame_control)) { ++ mppath = mpath; ++ } else { ++ return; ++ } ++ ++ /* rate limit, in case fast xmit can't be enabled */ ++ if (mppath->fast_xmit_check == jiffies) ++ return; ++ ++ mppath->fast_xmit_check = jiffies; ++ ++ key = rcu_access_pointer(next_hop->ptk[next_hop->ptk_idx]); ++ if (!key) ++ key = rcu_access_pointer(sdata->default_unicast_key); ++ ++ if (key) { ++ bool gen_iv, iv_spc; ++ ++ gen_iv = key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV; ++ iv_spc = key->conf.flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE; ++ ++ if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) || ++ (key->flags & KEY_FLAG_TAINTED)) ++ return; ++ ++ switch (key->conf.cipher) { ++ case WLAN_CIPHER_SUITE_CCMP: ++ case WLAN_CIPHER_SUITE_CCMP_256: ++ if (gen_iv) ++ pn_offs = hdrlen; ++ if (gen_iv || iv_spc) ++ hdrlen += IEEE80211_CCMP_HDR_LEN; ++ break; ++ case WLAN_CIPHER_SUITE_GCMP: ++ case WLAN_CIPHER_SUITE_GCMP_256: ++ if (gen_iv) ++ pn_offs = hdrlen; ++ if (gen_iv || iv_spc) ++ hdrlen += IEEE80211_GCMP_HDR_LEN; ++ break; ++ default: ++ return; ++ } ++ } ++ ++ if (WARN_ON_ONCE(hdrlen + meshhdr_len + sizeof(rfc1042_header) > ++ MESH_HEADER_MAX_LEN)) ++ return; ++ ++ mhdr = kzalloc(sizeof(*mhdr), GFP_ATOMIC); ++ if (!mhdr) ++ return; ++ ++ 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; ++ ++ 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); ++ } ++ ++ if (key) { ++ hdr = (struct ieee80211_hdr *)mhdr->hdr; ++ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); ++ } ++ ++ 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); ++} ++ ++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); ++} ++ ++void mesh_hdr_cache_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; ++ struct hlist_node *n; ++ ++ cache = &sdata->u.mesh.hdr_cache; ++ if (atomic_read(&cache->rhead.nelems) < MESH_HEADER_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); ++ spin_unlock_bh(&cache->walk_lock); ++} ++ ++void mesh_hdr_cache_flush(struct ieee80211_sub_if_data *sdata, const u8 *addr, ++ bool is_mpp) ++{ ++ struct mesh_hdr_cache *cache = &sdata->u.mesh.hdr_cache; ++ struct mhdr_cache_entry *entry; ++ struct hlist_node *n; ++ ++ cache = &sdata->u.mesh.hdr_cache; ++ spin_lock_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; ++ } ++ ++ 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); ++ ++out: ++ 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 + + static void __mesh_path_del(struct mesh_table *tbl, struct mesh_path *mpath) + { ++ 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); + mesh_path_free_rcu(tbl, mpath); +@@ -747,6 +990,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); + 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_ + { + mesh_table_init(&sdata->u.mesh.mesh_paths); + mesh_table_init(&sdata->u.mesh.mpp_paths); ++ mesh_hdr_cache_init(sdata); + } + + static +@@ -785,6 +1030,7 @@ void mesh_path_expire(struct ieee80211_s + + void mesh_pathtbl_unregister(struct ieee80211_sub_if_data *sdata) + { ++ mesh_hdr_cache_deinit(sdata); + mesh_table_free(&sdata->u.mesh.mesh_paths); + mesh_table_free(&sdata->u.mesh.mpp_paths); + } +--- a/net/mac80211/rx.c ++++ b/net/mac80211/rx.c +@@ -2791,6 +2791,7 @@ ieee80211_rx_mesh_data(struct ieee80211_ + if (mesh_hdr->flags & MESH_FLAGS_AE) { + struct mesh_path *mppath; + char *proxied_addr; ++ bool update = false; + + if (multicast) + proxied_addr = mesh_hdr->eaddr1; +@@ -2806,11 +2807,18 @@ ieee80211_rx_mesh_data(struct ieee80211_ + mpp_path_add(sdata, proxied_addr, eth->h_source); + } else { + spin_lock_bh(&mppath->state_lock); +- if (!ether_addr_equal(mppath->mpp, eth->h_source)) ++ if (!ether_addr_equal(mppath->mpp, eth->h_source)) { + memcpy(mppath->mpp, eth->h_source, ETH_ALEN); ++ update = true; ++ } + mppath->exp_time = jiffies; + spin_unlock_bh(&mppath->state_lock); + } ++ ++ /* flush fast xmit cache if the address path changed */ ++ if (update) ++ mesh_hdr_cache_flush(sdata, proxied_addr, true); ++ + rcu_read_unlock(); + } + +--- a/net/mac80211/tx.c ++++ b/net/mac80211/tx.c +@@ -3021,6 +3021,9 @@ void ieee80211_check_fast_xmit(struct st + if (!ieee80211_hw_check(&local->hw, SUPPORT_FAST_XMIT)) + return; + ++ if (ieee80211_vif_is_mesh(&sdata->vif)) ++ mesh_hdr_cache_flush(sdata, sta->addr, false); ++ + /* 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); + } + ++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)) ++ 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; ++} ++ + 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 + return; + } + ++ sk_pacing_shift_update(skb->sk, sdata->local->hw.tx_sk_pacing_shift); ++ + rcu_read_lock(); + ++ if (ieee80211_vif_is_mesh(&sdata->vif) && ++ 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 + skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, sta, skb)); + ieee80211_aggr_check(sdata, sta, skb); + +- sk_pacing_shift_update(skb->sk, sdata->local->hw.tx_sk_pacing_shift); +- + if (sta) { + struct ieee80211_fast_tx *fast_tx; + 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 new file mode 100644 index 0000000000..e0d4e60ed7 --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/320-wifi-mac80211-use-mesh-header-cache-to-speed-up-mesh.patch @@ -0,0 +1,70 @@ +From: Felix Fietkau +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. + +Signed-off-by: Felix Fietkau +--- + +--- 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; + } + +- if (mesh_hdr->flags & MESH_FLAGS_AE) { ++ if ((mesh_hdr->flags & MESH_FLAGS_AE) == MESH_FLAGS_AE_A5_A6) ++ entry = mesh_get_cached_hdr(sdata, mesh_hdr->eaddr1); ++ else if (!(mesh_hdr->flags & MESH_FLAGS_AE)) ++ entry = mesh_get_cached_hdr(sdata, eth->h_dest); ++ ++ 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_ + info->control.flags |= IEEE80211_TX_INTCFL_NEED_TXPROCESSING; + info->control.vif = &sdata->vif; + info->control.jiffies = jiffies; ++ fwd_skb->dev = sdata->dev; + 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_ + } + + IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_frames); +- fwd_skb->dev = sdata->dev; + ieee80211_add_pending_skb(local, fwd_skb); + + rx_accept: 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 new file mode 100644 index 0000000000..d9af8c7929 --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/321-mac80211-fix-mesh-forwarding.patch @@ -0,0 +1,32 @@ +From: Felix Fietkau +Date: Mon, 20 Feb 2023 12:50:50 +0100 +Subject: [PATCH] mac80211: fix mesh forwarding + +Linearize packets (needed for forwarding A-MSDU subframes). +Fix network header offset to fix flow dissector (and fair queueing). + +Fixes: 986e43b19ae9 ("wifi: mac80211: fix receiving A-MSDU frames on mesh interfaces") +Signed-off-by: Felix Fietkau +--- + +--- a/net/mac80211/rx.c ++++ b/net/mac80211/rx.c +@@ -2847,6 +2847,9 @@ ieee80211_rx_mesh_data(struct ieee80211_ + + if (skb_cow_head(fwd_skb, hdrlen - sizeof(struct ethhdr))) + return RX_DROP_UNUSABLE; ++ ++ if (skb_linearize(fwd_skb)) ++ return RX_DROP_UNUSABLE; + } + + fwd_hdr = skb_push(fwd_skb, hdrlen - sizeof(struct ethhdr)); +@@ -2861,7 +2864,7 @@ ieee80211_rx_mesh_data(struct ieee80211_ + hdrlen += ETH_ALEN; + else + fwd_skb->protocol = htons(fwd_skb->len - hdrlen); +- skb_set_network_header(fwd_skb, hdrlen); ++ skb_set_network_header(fwd_skb, hdrlen + 2); + + info = IEEE80211_SKB_CB(fwd_skb); + memset(info, 0, sizeof(*info)); 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 70d4e89c90..817be9e4d2 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 -@@ -1520,6 +1520,7 @@ struct ieee80211_local { +@@ -1536,6 +1536,7 @@ struct ieee80211_local { int dynamic_ps_forced_timeout; int user_power_level; /* in dBm, for all interfaces */