mac80211: optimize skb resizing to avoid reallocation when using 802.11s + batman-adv
authorFelix Fietkau <nbd@nbd.name>
Mon, 18 Mar 2019 11:59:20 +0000 (12:59 +0100)
committerFelix Fietkau <nbd@nbd.name>
Wed, 20 Mar 2019 09:41:30 +0000 (10:41 +0100)
Signed-off-by: Felix Fietkau <nbd@nbd.name>
package/kernel/mac80211/patches/subsys/357-mac80211-optimize-skb-resizing.patch [new file with mode: 0644]

diff --git a/package/kernel/mac80211/patches/subsys/357-mac80211-optimize-skb-resizing.patch b/package/kernel/mac80211/patches/subsys/357-mac80211-optimize-skb-resizing.patch
new file mode 100644 (file)
index 0000000..8fd85fe
--- /dev/null
@@ -0,0 +1,198 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Sun, 17 Mar 2019 18:11:30 +0100
+Subject: [PATCH] mac80211: optimize skb resizing
+
+When forwarding unicast packets from ethernet to batman-adv over 802.11s
+(with forwarding disabled), the typical required headroom to transmit
+encrypted packets on mt76 is 32 (802.11) + 6 (802.11s) + 8 (CCMP) +
+2 (padding) + 6 (LLC) + 18 (batman-adv) - 14 (old ethernet header) = 58 bytes.
+
+On systems where NET_SKB_PAD is 64 this leads to a call to pskb_expand_head
+for every packet, since mac80211 also tries to allocate 16 bytes status
+headroom for radiotap headers.
+
+This patch fixes these unnecessary reallocations by only requiring the extra
+status headroom in ieee80211_tx_monitor()
+If however a reallocation happens before that call, the status headroom gets
+added there as well, in order to avoid double reallocation.
+
+The patch also cleans up the code by moving the headroom calculation to
+ieee80211_skb_resize.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -1761,6 +1761,8 @@ void ieee80211_clear_fast_xmit(struct st
+ int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev,
+                             const u8 *buf, size_t len,
+                             const u8 *dest, __be16 proto, bool unencrypted);
++int ieee80211_skb_resize(struct ieee80211_sub_if_data *sdata,
++                       struct sk_buff *skb, int hdrlen, int hdr_add);
+ /* HT */
+ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
+--- a/net/mac80211/status.c
++++ b/net/mac80211/status.c
+@@ -672,6 +672,11 @@ void ieee80211_tx_monitor(struct ieee802
+               }
+       }
++      if (ieee80211_skb_resize(NULL, skb, 0, 0)) {
++              dev_kfree_skb(skb);
++              return;
++      }
++
+       /* send frame to monitor interfaces now */
+       rtap_len = ieee80211_tx_radiotap_len(info);
+       if (WARN_ON_ONCE(skb_headroom(skb) < rtap_len)) {
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -1914,37 +1914,53 @@ static bool ieee80211_tx(struct ieee8021
+ }
+ /* device xmit handlers */
+-
+-static int ieee80211_skb_resize(struct ieee80211_sub_if_data *sdata,
+-                              struct sk_buff *skb,
+-                              int head_need, bool may_encrypt)
++int ieee80211_skb_resize(struct ieee80211_sub_if_data *sdata,
++                       struct sk_buff *skb, int hdr_len, int hdr_extra)
+ {
+       struct ieee80211_local *local = sdata->local;
++      struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_hdr *hdr;
+-      bool enc_tailroom;
+-      int tail_need = 0;
++      int head_need, head_max;
++      int tail_need, tail_max;
++      bool enc_tailroom = false;
+-      hdr = (struct ieee80211_hdr *) skb->data;
+-      enc_tailroom = may_encrypt &&
+-                     (sdata->crypto_tx_tailroom_needed_cnt ||
+-                      ieee80211_is_mgmt(hdr->frame_control));
++      if (sdata && !hdr_len &&
++          !(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT)) {
++              hdr = (struct ieee80211_hdr *) skb->data;
++              enc_tailroom = (sdata->crypto_tx_tailroom_needed_cnt ||
++                              ieee80211_is_mgmt(hdr->frame_control));
++              hdr_len += sdata->encrypt_headroom;
++      }
+-      if (enc_tailroom) {
+-              tail_need = IEEE80211_ENCRYPT_TAILROOM;
+-              tail_need -= skb_tailroom(skb);
+-              tail_need = max_t(int, tail_need, 0);
++      head_need = head_max = hdr_len;
++      tail_need = tail_max = 0;
++      if (!sdata) {
++              head_need = head_max = local->tx_headroom;
++      } else {
++              head_max += hdr_extra;
++              head_max += max_t(int, local->tx_headroom,
++                                local->hw.extra_tx_headroom);
++              head_need += local->hw.extra_tx_headroom;
++
++              tail_max = IEEE80211_ENCRYPT_TAILROOM;
++              if (enc_tailroom)
++                      tail_need = tail_max;
+       }
+       if (skb_cloned(skb) &&
+           (!ieee80211_hw_check(&local->hw, SUPPORTS_CLONED_SKBS) ||
+            !skb_clone_writable(skb, ETH_HLEN) || enc_tailroom))
+               I802_DEBUG_INC(local->tx_expand_skb_head_cloned);
+-      else if (head_need || tail_need)
++      else if (head_need > skb_headroom(skb) ||
++               tail_need > skb_tailroom(skb))
+               I802_DEBUG_INC(local->tx_expand_skb_head);
+       else
+               return 0;
+-      if (pskb_expand_head(skb, head_need, tail_need, GFP_ATOMIC)) {
++      head_max = max_t(int, 0, head_max - skb_headroom(skb));
++      tail_max = max_t(int, 0, tail_max - skb_tailroom(skb));
++
++      if (pskb_expand_head(skb, head_max, tail_max, GFP_ATOMIC)) {
+               wiphy_debug(local->hw.wiphy,
+                           "failed to reallocate TX buffer\n");
+               return -ENOMEM;
+@@ -1960,18 +1976,8 @@ void ieee80211_xmit(struct ieee80211_sub
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_hdr *hdr;
+-      int headroom;
+-      bool may_encrypt;
+-
+-      may_encrypt = !(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT);
+-
+-      headroom = local->tx_headroom;
+-      if (may_encrypt)
+-              headroom += sdata->encrypt_headroom;
+-      headroom -= skb_headroom(skb);
+-      headroom = max_t(int, 0, headroom);
+-      if (ieee80211_skb_resize(sdata, skb, headroom, may_encrypt)) {
++      if (ieee80211_skb_resize(sdata, skb, 0, 0)) {
+               ieee80211_free_txskb(&local->hw, skb);
+               return;
+       }
+@@ -2740,30 +2746,14 @@ static struct sk_buff *ieee80211_build_h
+       skb_pull(skb, skip_header_bytes);
+       padsize = ieee80211_hdr_padsize(&local->hw, hdrlen);
+-      head_need = hdrlen + encaps_len + meshhdrlen - skb_headroom(skb);
++      head_need = hdrlen + encaps_len + meshhdrlen;
+       head_need += padsize;
+-      /*
+-       * So we need to modify the skb header and hence need a copy of
+-       * that. The head_need variable above doesn't, so far, include
+-       * the needed header space that we don't need right away. If we
+-       * can, then we don't reallocate right now but only after the
+-       * frame arrives at the master device (if it does...)
+-       *
+-       * If we cannot, however, then we will reallocate to include all
+-       * the ever needed space. Also, if we need to reallocate it anyway,
+-       * make it big enough for everything we may ever need.
+-       */
+-
+-      if (head_need > 0 || skb_cloned(skb)) {
+-              head_need += sdata->encrypt_headroom;
+-              head_need += local->tx_headroom;
+-              head_need = max_t(int, 0, head_need);
+-              if (ieee80211_skb_resize(sdata, skb, head_need, true)) {
+-                      ieee80211_free_txskb(&local->hw, skb);
+-                      skb = NULL;
+-                      return ERR_PTR(-ENOMEM);
+-              }
++      if (ieee80211_skb_resize(sdata, skb, head_need,
++                               sdata->encrypt_headroom)) {
++              ieee80211_free_txskb(&local->hw, skb);
++              skb = NULL;
++              return ERR_PTR(-ENOMEM);
+       }
+       if (encaps_data)
+@@ -3377,7 +3367,6 @@ static bool ieee80211_xmit_fast(struct i
+       struct ieee80211_local *local = sdata->local;
+       u16 ethertype = (skb->data[12] << 8) | skb->data[13];
+       int extra_head = fast_tx->hdr_len - (ETH_HLEN - 2);
+-      int hw_headroom = sdata->local->hw.extra_tx_headroom;
+       struct ethhdr eth;
+       struct ieee80211_tx_info *info;
+       struct ieee80211_hdr *hdr = (void *)fast_tx->hdr;
+@@ -3429,10 +3418,7 @@ static bool ieee80211_xmit_fast(struct i
+        * as the may-encrypt argument for the resize to not account for
+        * more room than we already have in 'extra_head'
+        */
+-      if (unlikely(ieee80211_skb_resize(sdata, skb,
+-                                        max_t(int, extra_head + hw_headroom -
+-                                                   skb_headroom(skb), 0),
+-                                        false))) {
++      if (unlikely(ieee80211_skb_resize(sdata, skb, extra_head, 0))) {
+               kfree_skb(skb);
+               return true;
+       }