mac80211: fix mesh fast tx cache issues
authorFelix Fietkau <nbd@nbd.name>
Fri, 30 Jun 2023 11:18:31 +0000 (13:18 +0200)
committerFelix Fietkau <nbd@nbd.name>
Fri, 30 Jun 2023 11:18:55 +0000 (13:18 +0200)
Split the cache by tx type in order to avoid packet drop issues

Signed-off-by: Felix Fietkau <nbd@nbd.name>
package/kernel/mac80211/patches/subsys/338-mac80211-split-mesh-fast-tx-cache-into-local-proxied.patch [new file with mode: 0644]

diff --git a/package/kernel/mac80211/patches/subsys/338-mac80211-split-mesh-fast-tx-cache-into-local-proxied.patch b/package/kernel/mac80211/patches/subsys/338-mac80211-split-mesh-fast-tx-cache-into-local-proxied.patch
new file mode 100644 (file)
index 0000000..f7391a5
--- /dev/null
@@ -0,0 +1,219 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 30 Jun 2023 13:11:51 +0200
+Subject: [PATCH] mac80211: split mesh fast tx cache into
+ local/proxied/forwarded
+
+Depending on the origin of the packets (and their SA), 802.11 + mesh headers
+could be filled in differently. In order to properly deal with that, add a
+new field to the lookup key, indicating the type (local, proxied or
+forwarded). This can fix spurious packet drop issues that depend on the order
+in which nodes/hosts communicate with each other.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/mac80211/mesh.c
++++ b/net/mac80211/mesh.c
+@@ -703,6 +703,9 @@ bool ieee80211_mesh_xmit_fast(struct iee
+                             struct sk_buff *skb, u32 ctrl_flags)
+ {
+       struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
++      struct ieee80211_mesh_fast_tx_key key = {
++              .type = MESH_FAST_TX_TYPE_LOCAL
++      };
+       struct ieee80211_mesh_fast_tx *entry;
+       struct ieee80211s_hdr *meshhdr;
+       u8 sa[ETH_ALEN] __aligned(2);
+@@ -738,7 +741,10 @@ bool ieee80211_mesh_xmit_fast(struct iee
+                       return false;
+       }
+-      entry = mesh_fast_tx_get(sdata, skb->data);
++      ether_addr_copy(key.addr, skb->data);
++      if (!ether_addr_equal(skb->data + ETH_ALEN, sdata->vif.addr))
++              key.type = MESH_FAST_TX_TYPE_PROXIED;
++      entry = mesh_fast_tx_get(sdata, &key);
+       if (!entry)
+               return false;
+--- a/net/mac80211/mesh.h
++++ b/net/mac80211/mesh.h
+@@ -133,9 +133,33 @@ struct mesh_path {
+ #define MESH_FAST_TX_CACHE_TIMEOUT            8000 /* msecs */
+ /**
++ * enum ieee80211_mesh_fast_tx_type - cached mesh fast tx entry type
++ *
++ * @MESH_FAST_TX_TYPE_LOCAL: tx from the local vif address as SA
++ * @MESH_FAST_TX_TYPE_PROXIED: local tx with a different SA (e.g. bridged)
++ * @MESH_FAST_TX_TYPE_FORWARDED: forwarded from a different mesh point
++ */
++enum ieee80211_mesh_fast_tx_type {
++      MESH_FAST_TX_TYPE_LOCAL,
++      MESH_FAST_TX_TYPE_PROXIED,
++      MESH_FAST_TX_TYPE_FORWARDED,
++};
++
++/**
++ * struct ieee80211_mesh_fast_tx_key - cached mesh fast tx entry key
++ *
++ * @addr: The Ethernet DA for this entry
++ * @type: cache entry type
++ */
++struct ieee80211_mesh_fast_tx_key {
++      u8 addr[ETH_ALEN] __aligned(2);
++      enum ieee80211_mesh_fast_tx_type type;
++};
++
++/**
+  * struct ieee80211_mesh_fast_tx - cached mesh fast tx entry
+  * @rhash: rhashtable pointer
+- * @addr_key: The Ethernet DA which is the key for this entry
++ * @key: the lookup key for this cache entry
+  * @fast_tx: base fast_tx data
+  * @hdr: cached mesh and rfc1042 headers
+  * @hdrlen: length of mesh + rfc1042
+@@ -146,7 +170,7 @@ struct mesh_path {
+  */
+ struct ieee80211_mesh_fast_tx {
+       struct rhash_head rhash;
+-      u8 addr_key[ETH_ALEN] __aligned(2);
++      struct ieee80211_mesh_fast_tx_key key;
+       struct ieee80211_fast_tx fast_tx;
+       u8 hdr[sizeof(struct ieee80211s_hdr) + sizeof(rfc1042_header)];
+@@ -329,7 +353,8 @@ void mesh_path_tx_root_frame(struct ieee
+ bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt);
+ struct ieee80211_mesh_fast_tx *
+-mesh_fast_tx_get(struct ieee80211_sub_if_data *sdata, const u8 *addr);
++mesh_fast_tx_get(struct ieee80211_sub_if_data *sdata,
++               struct ieee80211_mesh_fast_tx_key *key);
+ 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,
+--- a/net/mac80211/mesh_pathtbl.c
++++ b/net/mac80211/mesh_pathtbl.c
+@@ -36,8 +36,8 @@ static const struct rhashtable_params me
+ static const struct rhashtable_params fast_tx_rht_params = {
+       .nelem_hint = 10,
+       .automatic_shrinking = true,
+-      .key_len = ETH_ALEN,
+-      .key_offset = offsetof(struct ieee80211_mesh_fast_tx, addr_key),
++      .key_len = sizeof(struct ieee80211_mesh_fast_tx_key),
++      .key_offset = offsetof(struct ieee80211_mesh_fast_tx, key),
+       .head_offset = offsetof(struct ieee80211_mesh_fast_tx, rhash),
+       .hashfn = mesh_table_hash,
+ };
+@@ -426,20 +426,21 @@ static void mesh_fast_tx_entry_free(stru
+ }
+ struct ieee80211_mesh_fast_tx *
+-mesh_fast_tx_get(struct ieee80211_sub_if_data *sdata, const u8 *addr)
++mesh_fast_tx_get(struct ieee80211_sub_if_data *sdata,
++               struct ieee80211_mesh_fast_tx_key *key)
+ {
+       struct ieee80211_mesh_fast_tx *entry;
+       struct mesh_tx_cache *cache;
+       cache = &sdata->u.mesh.tx_cache;
+-      entry = rhashtable_lookup(&cache->rht, addr, fast_tx_rht_params);
++      entry = rhashtable_lookup(&cache->rht, key, 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->rht, addr, fast_tx_rht_params);
++              entry = rhashtable_lookup(&cache->rht, key, fast_tx_rht_params);
+               if (entry)
+                   mesh_fast_tx_entry_free(cache, entry);
+               spin_unlock_bh(&cache->walk_lock);
+@@ -484,18 +485,24 @@ void mesh_fast_tx_cache(struct ieee80211
+       if (!sta)
+               return;
++      build.key.type = MESH_FAST_TX_TYPE_LOCAL;
+       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;
+               build.mppath = mppath;
++              if (!ether_addr_equal(meshhdr->eaddr2, sdata->vif.addr))
++                      build.key.type = MESH_FAST_TX_TYPE_PROXIED;
+       } else if (ieee80211_has_a4(hdr->frame_control)) {
+               mppath = mpath;
+       } else {
+               return;
+       }
++      if (!ether_addr_equal(hdr->addr4, sdata->vif.addr))
++              build.key.type = MESH_FAST_TX_TYPE_FORWARDED;
++
+       /* rate limit, in case fast xmit can't be enabled */
+       if (mppath->fast_tx_check == jiffies)
+               return;
+@@ -542,7 +549,7 @@ void mesh_fast_tx_cache(struct ieee80211
+               }
+       }
+-      memcpy(build.addr_key, mppath->dst, ETH_ALEN);
++      memcpy(build.key.addr, mppath->dst, ETH_ALEN);
+       build.timestamp = jiffies;
+       build.fast_tx.band = info->band;
+       build.fast_tx.da_offs = offsetof(struct ieee80211_hdr, addr3);
+@@ -644,13 +651,19 @@ void mesh_fast_tx_flush_addr(struct ieee
+                            const u8 *addr)
+ {
+       struct mesh_tx_cache *cache = &sdata->u.mesh.tx_cache;
++      struct ieee80211_mesh_fast_tx_key key = {};
+       struct ieee80211_mesh_fast_tx *entry;
++      int i;
++      ether_addr_copy(key.addr, addr);
+       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);
++      for (i = MESH_FAST_TX_TYPE_LOCAL; i < MESH_FAST_TX_TYPE_FORWARDED; i++) {
++              key.type = i;
++              entry = rhashtable_lookup(&cache->rht, &key, fast_tx_rht_params);
++              if (entry)
++                      mesh_fast_tx_entry_free(cache, entry);
++      }
+       spin_unlock_bh(&cache->walk_lock);
+ }
+--- a/net/mac80211/rx.c
++++ b/net/mac80211/rx.c
+@@ -2726,7 +2726,10 @@ ieee80211_rx_mesh_fast_forward(struct ie
+                              struct sk_buff *skb, int hdrlen)
+ {
+       struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+-      struct ieee80211_mesh_fast_tx *entry = NULL;
++      struct ieee80211_mesh_fast_tx_key key = {
++              .type = MESH_FAST_TX_TYPE_FORWARDED
++      };
++      struct ieee80211_mesh_fast_tx *entry;
+       struct ieee80211s_hdr *mesh_hdr;
+       struct tid_ampdu_tx *tid_tx;
+       struct sta_info *sta;
+@@ -2735,9 +2738,13 @@ ieee80211_rx_mesh_fast_forward(struct ie
+       mesh_hdr = (struct ieee80211s_hdr *)(skb->data + sizeof(eth));
+       if ((mesh_hdr->flags & MESH_FLAGS_AE) == MESH_FLAGS_AE_A5_A6)
+-              entry = mesh_fast_tx_get(sdata, mesh_hdr->eaddr1);
++              ether_addr_copy(key.addr, mesh_hdr->eaddr1);
+       else if (!(mesh_hdr->flags & MESH_FLAGS_AE))
+-              entry = mesh_fast_tx_get(sdata, skb->data);
++              ether_addr_copy(key.addr, skb->data);
++      else
++              return false;
++
++      entry = mesh_fast_tx_get(sdata, &key);
+       if (!entry)
+               return false;