kernel: replace GRO optimization patch with a new one that supports VLANs/bridges...
authorFelix Fietkau <nbd@openwrt.org>
Wed, 18 Mar 2015 18:22:41 +0000 (18:22 +0000)
committerFelix Fietkau <nbd@openwrt.org>
Wed, 18 Mar 2015 18:22:41 +0000 (18:22 +0000)
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
SVN-Revision: 44877

target/linux/generic/patches-3.18/680-NET-skip-GRO-for-foreign-MAC-addresses.patch [new file with mode: 0644]
target/linux/generic/patches-3.18/680-net-skip-gro-for-otherhost-packets.patch [deleted file]
target/linux/generic/patches-3.18/721-phy_packets.patch
target/linux/generic/patches-3.19/680-NET-skip-GRO-for-foreign-MAC-addresses.patch [new file with mode: 0644]
target/linux/generic/patches-3.19/680-net-skip-gro-for-otherhost-packets.patch [deleted file]
target/linux/generic/patches-3.19/721-phy_packets.patch
target/linux/generic/patches-4.0/680-NET-skip-GRO-for-foreign-MAC-addresses.patch [new file with mode: 0644]
target/linux/generic/patches-4.0/680-net-skip-gro-for-otherhost-packets.patch [deleted file]
target/linux/generic/patches-4.0/721-phy_packets.patch

diff --git a/target/linux/generic/patches-3.18/680-NET-skip-GRO-for-foreign-MAC-addresses.patch b/target/linux/generic/patches-3.18/680-NET-skip-GRO-for-foreign-MAC-addresses.patch
new file mode 100644 (file)
index 0000000..ad5000e
--- /dev/null
@@ -0,0 +1,160 @@
+Subject: NET: skip GRO for foreign MAC addresses
+
+For network drivers using napi_gro_receive, packets are run through GRO,
+even when the destination MAC address does not match, and they're supposed
+to be delivered to another host behind a different bridge port.
+
+This can be very expensive, because for drivers without TSO or scatter-
+gather, this can only be undone by copying the skb and checksumming it
+again.
+
+To be able to track foreign MAC addresses in an inexpensive way, create
+a mask of changed bits in MAC addresses of upper devices. This allows
+handling VLANs and bridge devices with different addresses (as long as
+they are not too different).
+
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+
+--- a/net/core/dev.c
++++ b/net/core/dev.c
+@@ -4003,6 +4003,9 @@ static enum gro_result dev_gro_receive(s
+       enum gro_result ret;
+       int grow;
++      if (skb->gro_skip)
++              goto normal;
++
+       if (!(skb->dev->features & NETIF_F_GRO))
+               goto normal;
+@@ -5066,6 +5069,48 @@ static void __netdev_adjacent_dev_unlink
+                                          &upper_dev->adj_list.lower);
+ }
++static void __netdev_addr_mask(unsigned char *mask, const unsigned char *addr,
++                             struct net_device *dev)
++{
++      int i;
++
++      for (i = 0; i < dev->addr_len; i++)
++              mask[i] |= addr[i] ^ dev->dev_addr[i];
++}
++
++static void __netdev_upper_mask(unsigned char *mask, struct net_device *dev,
++                              struct net_device *lower)
++{
++      struct net_device *cur;
++      struct list_head *iter;
++
++      netdev_for_each_upper_dev_rcu(dev, cur, iter) {
++              __netdev_addr_mask(mask, cur->dev_addr, lower);
++              __netdev_upper_mask(mask, cur, lower);
++      }
++}
++
++static void __netdev_update_addr_mask(struct net_device *dev)
++{
++      unsigned char mask[MAX_ADDR_LEN];
++      struct net_device *cur;
++      struct list_head *iter;
++
++      memset(mask, 0, sizeof(mask));
++      __netdev_upper_mask(mask, dev, dev);
++      memcpy(dev->local_addr_mask, mask, dev->addr_len);
++
++      netdev_for_each_lower_dev(dev, cur, iter)
++              __netdev_update_addr_mask(cur);
++}
++
++static void netdev_update_addr_mask(struct net_device *dev)
++{
++      rcu_read_lock();
++      __netdev_update_addr_mask(dev);
++      rcu_read_unlock();
++}
++
+ static int __netdev_upper_dev_link(struct net_device *dev,
+                                  struct net_device *upper_dev, bool master,
+                                  void *private)
+@@ -5126,6 +5171,7 @@ static int __netdev_upper_dev_link(struc
+                       goto rollback_lower_mesh;
+       }
++      netdev_update_addr_mask(dev);
+       call_netdevice_notifiers(NETDEV_CHANGEUPPER, dev);
+       return 0;
+@@ -5243,6 +5289,7 @@ void netdev_upper_dev_unlink(struct net_
+       list_for_each_entry(i, &upper_dev->all_adj_list.upper, list)
+               __netdev_adjacent_dev_unlink(dev, i->dev);
++      netdev_update_addr_mask(dev);
+       call_netdevice_notifiers(NETDEV_CHANGEUPPER, dev);
+ }
+ EXPORT_SYMBOL(netdev_upper_dev_unlink);
+@@ -5762,6 +5809,7 @@ int dev_set_mac_address(struct net_devic
+       if (err)
+               return err;
+       dev->addr_assign_type = NET_ADDR_SET;
++      netdev_update_addr_mask(dev);
+       call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
+       add_device_randomness(dev->dev_addr, dev->addr_len);
+       return 0;
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -1548,6 +1548,8 @@ struct net_device {
+       struct netdev_hw_addr_list      mc;
+       struct netdev_hw_addr_list      dev_addrs;
++      unsigned char           local_addr_mask[MAX_ADDR_LEN];
++
+ #ifdef CONFIG_SYSFS
+       struct kset             *queues_kset;
+ #endif
+--- a/include/linux/skbuff.h
++++ b/include/linux/skbuff.h
+@@ -597,7 +597,8 @@ struct sk_buff {
+ #endif
+       __u8                    ipvs_property:1;
+       __u8                    inner_protocol_type:1;
+-      /* 4 or 6 bit hole */
++      __u8                    gro_skip:1;
++      /* 3 or 5 bit hole */
+ #ifdef CONFIG_NET_SCHED
+       __u16                   tc_index;       /* traffic control index */
+--- a/net/ethernet/eth.c
++++ b/net/ethernet/eth.c
+@@ -172,6 +172,18 @@ u32 eth_get_headlen(void *data, unsigned
+ }
+ EXPORT_SYMBOL(eth_get_headlen);
++static inline bool
++eth_check_local_mask(const void *addr1, const void *addr2, const void *mask)
++{
++      const u16 *a1 = addr1;
++      const u16 *a2 = addr2;
++      const u16 *m = mask;
++
++      return (((a1[0] ^ a2[0]) & ~m[0]) |
++              ((a1[1] ^ a2[1]) & ~m[1]) |
++              ((a1[2] ^ a2[2]) & ~m[2]));
++}
++
+ /**
+  * eth_type_trans - determine the packet's protocol ID.
+  * @skb: received socket data
+@@ -199,8 +211,12 @@ __be16 eth_type_trans(struct sk_buff *sk
+                       skb->pkt_type = PACKET_MULTICAST;
+       }
+       else if (unlikely(!ether_addr_equal_64bits(eth->h_dest,
+-                                                 dev->dev_addr)))
++                                                 dev->dev_addr))) {
+               skb->pkt_type = PACKET_OTHERHOST;
++              if (eth_check_local_mask(eth->h_dest, dev->dev_addr,
++                                       dev->local_addr_mask))
++                      skb->gro_skip = 1;
++      }
+       /*
+        * Some variants of DSA tagging don't have an ethertype field
diff --git a/target/linux/generic/patches-3.18/680-net-skip-gro-for-otherhost-packets.patch b/target/linux/generic/patches-3.18/680-net-skip-gro-for-otherhost-packets.patch
deleted file mode 100644 (file)
index 2a65c0b..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-Subject: NET: skip GRO for otherhost packets
-
-For network drivers using napi_gro_receive, packets are run through GRO,
-even when the destination MAC address does not match, and they're supposed
-to be delivered to another host behind a different bridge port.
-
-This can be very expensive, because for drivers without TSO or scatter-
-gather, this can only be undone by copying the skb and checksumming it
-again.
-
-Fix this by leaving skbs with PACKET_OTHERHOST untouched.
-
-Signed-off-by: Felix Fietkau <nbd@openwrt.org>
-
---- a/net/core/dev.c
-+++ b/net/core/dev.c
-@@ -4003,6 +4003,9 @@ static enum gro_result dev_gro_receive(s
-       enum gro_result ret;
-       int grow;
-+      if (skb->pkt_type == PACKET_OTHERHOST)
-+              goto normal;
-+
-       if (!(skb->dev->features & NETIF_F_GRO))
-               goto normal;
index 5d989741a6a1fbd42138f8ee405fbb56d8958195..aa1897cad9656c1c2bf6d95339f164c11e5891ab 100644 (file)
@@ -28,7 +28,7 @@
        const struct header_ops *header_ops;
  
        unsigned int            flags;
-@@ -1577,6 +1584,10 @@ struct net_device {
+@@ -1579,6 +1586,10 @@ struct net_device {
        void                    *ax25_ptr;
        struct wireless_dev     *ieee80211_ptr;
  
@@ -41,7 +41,7 @@
   */
 --- a/include/linux/skbuff.h
 +++ b/include/linux/skbuff.h
-@@ -2052,6 +2052,10 @@ static inline int pskb_trim(struct sk_bu
+@@ -2053,6 +2053,10 @@ static inline int pskb_trim(struct sk_bu
        return (len < skb->len) ? __pskb_trim(skb, len) : 0;
  }
  
@@ -52,7 +52,7 @@
  /**
   *    pskb_trim_unique - remove end from a paged unique (not cloned) buffer
   *    @skb: buffer to alter
-@@ -2178,16 +2182,6 @@ static inline struct sk_buff *dev_alloc_
+@@ -2179,16 +2183,6 @@ static inline struct sk_buff *dev_alloc_
  }
  
  
  {
 --- a/net/ethernet/eth.c
 +++ b/net/ethernet/eth.c
-@@ -188,6 +188,12 @@ __be16 eth_type_trans(struct sk_buff *sk
+@@ -200,6 +200,12 @@ __be16 eth_type_trans(struct sk_buff *sk
        const struct ethhdr *eth;
  
        skb->dev = dev;
diff --git a/target/linux/generic/patches-3.19/680-NET-skip-GRO-for-foreign-MAC-addresses.patch b/target/linux/generic/patches-3.19/680-NET-skip-GRO-for-foreign-MAC-addresses.patch
new file mode 100644 (file)
index 0000000..511f0e9
--- /dev/null
@@ -0,0 +1,160 @@
+Subject: NET: skip GRO for foreign MAC addresses
+
+For network drivers using napi_gro_receive, packets are run through GRO,
+even when the destination MAC address does not match, and they're supposed
+to be delivered to another host behind a different bridge port.
+
+This can be very expensive, because for drivers without TSO or scatter-
+gather, this can only be undone by copying the skb and checksumming it
+again.
+
+To be able to track foreign MAC addresses in an inexpensive way, create
+a mask of changed bits in MAC addresses of upper devices. This allows
+handling VLANs and bridge devices with different addresses (as long as
+they are not too different).
+
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+
+--- a/net/core/dev.c
++++ b/net/core/dev.c
+@@ -3967,6 +3967,9 @@ static enum gro_result dev_gro_receive(s
+       enum gro_result ret;
+       int grow;
++      if (skb->gro_skip)
++              goto normal;
++
+       if (!(skb->dev->features & NETIF_F_GRO))
+               goto normal;
+@@ -5113,6 +5116,48 @@ static void __netdev_adjacent_dev_unlink
+                                          &upper_dev->adj_list.lower);
+ }
++static void __netdev_addr_mask(unsigned char *mask, const unsigned char *addr,
++                             struct net_device *dev)
++{
++      int i;
++
++      for (i = 0; i < dev->addr_len; i++)
++              mask[i] |= addr[i] ^ dev->dev_addr[i];
++}
++
++static void __netdev_upper_mask(unsigned char *mask, struct net_device *dev,
++                              struct net_device *lower)
++{
++      struct net_device *cur;
++      struct list_head *iter;
++
++      netdev_for_each_upper_dev_rcu(dev, cur, iter) {
++              __netdev_addr_mask(mask, cur->dev_addr, lower);
++              __netdev_upper_mask(mask, cur, lower);
++      }
++}
++
++static void __netdev_update_addr_mask(struct net_device *dev)
++{
++      unsigned char mask[MAX_ADDR_LEN];
++      struct net_device *cur;
++      struct list_head *iter;
++
++      memset(mask, 0, sizeof(mask));
++      __netdev_upper_mask(mask, dev, dev);
++      memcpy(dev->local_addr_mask, mask, dev->addr_len);
++
++      netdev_for_each_lower_dev(dev, cur, iter)
++              __netdev_update_addr_mask(cur);
++}
++
++static void netdev_update_addr_mask(struct net_device *dev)
++{
++      rcu_read_lock();
++      __netdev_update_addr_mask(dev);
++      rcu_read_unlock();
++}
++
+ static int __netdev_upper_dev_link(struct net_device *dev,
+                                  struct net_device *upper_dev, bool master,
+                                  void *private)
+@@ -5173,6 +5218,7 @@ static int __netdev_upper_dev_link(struc
+                       goto rollback_lower_mesh;
+       }
++      netdev_update_addr_mask(dev);
+       call_netdevice_notifiers(NETDEV_CHANGEUPPER, dev);
+       return 0;
+@@ -5290,6 +5336,7 @@ void netdev_upper_dev_unlink(struct net_
+       list_for_each_entry(i, &upper_dev->all_adj_list.upper, list)
+               __netdev_adjacent_dev_unlink(dev, i->dev);
++      netdev_update_addr_mask(dev);
+       call_netdevice_notifiers(NETDEV_CHANGEUPPER, dev);
+ }
+ EXPORT_SYMBOL(netdev_upper_dev_unlink);
+@@ -5809,6 +5856,7 @@ int dev_set_mac_address(struct net_devic
+       if (err)
+               return err;
+       dev->addr_assign_type = NET_ADDR_SET;
++      netdev_update_addr_mask(dev);
+       call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
+       add_device_randomness(dev->dev_addr, dev->addr_len);
+       return 0;
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -1583,6 +1583,8 @@ struct net_device {
+       struct netdev_hw_addr_list      mc;
+       struct netdev_hw_addr_list      dev_addrs;
++      unsigned char           local_addr_mask[MAX_ADDR_LEN];
++
+ #ifdef CONFIG_SYSFS
+       struct kset             *queues_kset;
+ #endif
+--- a/include/linux/skbuff.h
++++ b/include/linux/skbuff.h
+@@ -605,7 +605,8 @@ struct sk_buff {
+       __u8                    ipvs_property:1;
+       __u8                    inner_protocol_type:1;
+       __u8                    remcsum_offload:1;
+-      /* 3 or 5 bit hole */
++      __u8                    gro_skip:1;
++      /* 2 or 4 bit hole */
+ #ifdef CONFIG_NET_SCHED
+       __u16                   tc_index;       /* traffic control index */
+--- a/net/ethernet/eth.c
++++ b/net/ethernet/eth.c
+@@ -172,6 +172,18 @@ u32 eth_get_headlen(void *data, unsigned
+ }
+ EXPORT_SYMBOL(eth_get_headlen);
++static inline bool
++eth_check_local_mask(const void *addr1, const void *addr2, const void *mask)
++{
++      const u16 *a1 = addr1;
++      const u16 *a2 = addr2;
++      const u16 *m = mask;
++
++      return (((a1[0] ^ a2[0]) & ~m[0]) |
++              ((a1[1] ^ a2[1]) & ~m[1]) |
++              ((a1[2] ^ a2[2]) & ~m[2]));
++}
++
+ /**
+  * eth_type_trans - determine the packet's protocol ID.
+  * @skb: received socket data
+@@ -199,8 +211,12 @@ __be16 eth_type_trans(struct sk_buff *sk
+                       skb->pkt_type = PACKET_MULTICAST;
+       }
+       else if (unlikely(!ether_addr_equal_64bits(eth->h_dest,
+-                                                 dev->dev_addr)))
++                                                 dev->dev_addr))) {
+               skb->pkt_type = PACKET_OTHERHOST;
++              if (eth_check_local_mask(eth->h_dest, dev->dev_addr,
++                                       dev->local_addr_mask))
++                      skb->gro_skip = 1;
++      }
+       /*
+        * Some variants of DSA tagging don't have an ethertype field
diff --git a/target/linux/generic/patches-3.19/680-net-skip-gro-for-otherhost-packets.patch b/target/linux/generic/patches-3.19/680-net-skip-gro-for-otherhost-packets.patch
deleted file mode 100644 (file)
index 292d021..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-Subject: NET: skip GRO for otherhost packets
-
-For network drivers using napi_gro_receive, packets are run through GRO,
-even when the destination MAC address does not match, and they're supposed
-to be delivered to another host behind a different bridge port.
-
-This can be very expensive, because for drivers without TSO or scatter-
-gather, this can only be undone by copying the skb and checksumming it
-again.
-
-Fix this by leaving skbs with PACKET_OTHERHOST untouched.
-
-Signed-off-by: Felix Fietkau <nbd@openwrt.org>
-
---- a/net/core/dev.c
-+++ b/net/core/dev.c
-@@ -3967,6 +3967,9 @@ static enum gro_result dev_gro_receive(s
-       enum gro_result ret;
-       int grow;
-+      if (skb->pkt_type == PACKET_OTHERHOST)
-+              goto normal;
-+
-       if (!(skb->dev->features & NETIF_F_GRO))
-               goto normal;
index 68ba8fc30646be8ac11c6b5af9f0280ea40b0e36..e1317a234c42444572a58f48b7cb93ed3a073b82 100644 (file)
@@ -28,7 +28,7 @@
        const struct header_ops *header_ops;
  
        unsigned int            flags;
-@@ -1613,6 +1620,10 @@ struct net_device {
+@@ -1615,6 +1622,10 @@ struct net_device {
        struct wireless_dev     *ieee80211_ptr;
        struct wpan_dev         *ieee802154_ptr;
  
@@ -41,7 +41,7 @@
   */
 --- a/include/linux/skbuff.h
 +++ b/include/linux/skbuff.h
-@@ -2042,6 +2042,10 @@ static inline int pskb_trim(struct sk_bu
+@@ -2043,6 +2043,10 @@ static inline int pskb_trim(struct sk_bu
        return (len < skb->len) ? __pskb_trim(skb, len) : 0;
  }
  
@@ -52,7 +52,7 @@
  /**
   *    pskb_trim_unique - remove end from a paged unique (not cloned) buffer
   *    @skb: buffer to alter
-@@ -2168,16 +2172,6 @@ static inline struct sk_buff *dev_alloc_
+@@ -2169,16 +2173,6 @@ static inline struct sk_buff *dev_alloc_
  }
  
  
  
  #include <net/protocol.h>
  #include <net/dst.h>
-@@ -548,6 +549,22 @@ struct sk_buff *__napi_alloc_skb(struct
+@@ -548,6 +549,22 @@ struct sk_buff *__napi_alloc_skb(struct 
  }
  EXPORT_SYMBOL(__napi_alloc_skb);
  
  {
 --- a/net/ethernet/eth.c
 +++ b/net/ethernet/eth.c
-@@ -188,6 +188,12 @@ __be16 eth_type_trans(struct sk_buff *sk
+@@ -200,6 +200,12 @@ __be16 eth_type_trans(struct sk_buff *sk
        const struct ethhdr *eth;
  
        skb->dev = dev;
diff --git a/target/linux/generic/patches-4.0/680-NET-skip-GRO-for-foreign-MAC-addresses.patch b/target/linux/generic/patches-4.0/680-NET-skip-GRO-for-foreign-MAC-addresses.patch
new file mode 100644 (file)
index 0000000..a192960
--- /dev/null
@@ -0,0 +1,160 @@
+Subject: NET: skip GRO for foreign MAC addresses
+
+For network drivers using napi_gro_receive, packets are run through GRO,
+even when the destination MAC address does not match, and they're supposed
+to be delivered to another host behind a different bridge port.
+
+This can be very expensive, because for drivers without TSO or scatter-
+gather, this can only be undone by copying the skb and checksumming it
+again.
+
+To be able to track foreign MAC addresses in an inexpensive way, create
+a mask of changed bits in MAC addresses of upper devices. This allows
+handling VLANs and bridge devices with different addresses (as long as
+they are not too different).
+
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+
+--- a/net/core/dev.c
++++ b/net/core/dev.c
+@@ -4005,6 +4005,9 @@ static enum gro_result dev_gro_receive(s
+       enum gro_result ret;
+       int grow;
++      if (skb->gro_skip)
++              goto normal;
++
+       if (!(skb->dev->features & NETIF_F_GRO))
+               goto normal;
+@@ -5152,6 +5155,48 @@ static void __netdev_adjacent_dev_unlink
+                                          &upper_dev->adj_list.lower);
+ }
++static void __netdev_addr_mask(unsigned char *mask, const unsigned char *addr,
++                             struct net_device *dev)
++{
++      int i;
++
++      for (i = 0; i < dev->addr_len; i++)
++              mask[i] |= addr[i] ^ dev->dev_addr[i];
++}
++
++static void __netdev_upper_mask(unsigned char *mask, struct net_device *dev,
++                              struct net_device *lower)
++{
++      struct net_device *cur;
++      struct list_head *iter;
++
++      netdev_for_each_upper_dev_rcu(dev, cur, iter) {
++              __netdev_addr_mask(mask, cur->dev_addr, lower);
++              __netdev_upper_mask(mask, cur, lower);
++      }
++}
++
++static void __netdev_update_addr_mask(struct net_device *dev)
++{
++      unsigned char mask[MAX_ADDR_LEN];
++      struct net_device *cur;
++      struct list_head *iter;
++
++      memset(mask, 0, sizeof(mask));
++      __netdev_upper_mask(mask, dev, dev);
++      memcpy(dev->local_addr_mask, mask, dev->addr_len);
++
++      netdev_for_each_lower_dev(dev, cur, iter)
++              __netdev_update_addr_mask(cur);
++}
++
++static void netdev_update_addr_mask(struct net_device *dev)
++{
++      rcu_read_lock();
++      __netdev_update_addr_mask(dev);
++      rcu_read_unlock();
++}
++
+ static int __netdev_upper_dev_link(struct net_device *dev,
+                                  struct net_device *upper_dev, bool master,
+                                  void *private)
+@@ -5212,6 +5257,7 @@ static int __netdev_upper_dev_link(struc
+                       goto rollback_lower_mesh;
+       }
++      netdev_update_addr_mask(dev);
+       call_netdevice_notifiers(NETDEV_CHANGEUPPER, dev);
+       return 0;
+@@ -5329,6 +5375,7 @@ void netdev_upper_dev_unlink(struct net_
+       list_for_each_entry(i, &upper_dev->all_adj_list.upper, list)
+               __netdev_adjacent_dev_unlink(dev, i->dev);
++      netdev_update_addr_mask(dev);
+       call_netdevice_notifiers(NETDEV_CHANGEUPPER, dev);
+ }
+ EXPORT_SYMBOL(netdev_upper_dev_unlink);
+@@ -5868,6 +5915,7 @@ int dev_set_mac_address(struct net_devic
+       if (err)
+               return err;
+       dev->addr_assign_type = NET_ADDR_SET;
++      netdev_update_addr_mask(dev);
+       call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
+       add_device_randomness(dev->dev_addr, dev->addr_len);
+       return 0;
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -1589,6 +1589,8 @@ struct net_device {
+       struct netdev_hw_addr_list      mc;
+       struct netdev_hw_addr_list      dev_addrs;
++      unsigned char           local_addr_mask[MAX_ADDR_LEN];
++
+ #ifdef CONFIG_SYSFS
+       struct kset             *queues_kset;
+ #endif
+--- a/include/linux/skbuff.h
++++ b/include/linux/skbuff.h
+@@ -609,7 +609,8 @@ struct sk_buff {
+       __u8                    ipvs_property:1;
+       __u8                    inner_protocol_type:1;
+       __u8                    remcsum_offload:1;
+-      /* 3 or 5 bit hole */
++      __u8                    gro_skip:1;
++      /* 2 or 4 bit hole */
+ #ifdef CONFIG_NET_SCHED
+       __u16                   tc_index;       /* traffic control index */
+--- a/net/ethernet/eth.c
++++ b/net/ethernet/eth.c
+@@ -172,6 +172,18 @@ u32 eth_get_headlen(void *data, unsigned
+ }
+ EXPORT_SYMBOL(eth_get_headlen);
++static inline bool
++eth_check_local_mask(const void *addr1, const void *addr2, const void *mask)
++{
++      const u16 *a1 = addr1;
++      const u16 *a2 = addr2;
++      const u16 *m = mask;
++
++      return (((a1[0] ^ a2[0]) & ~m[0]) |
++              ((a1[1] ^ a2[1]) & ~m[1]) |
++              ((a1[2] ^ a2[2]) & ~m[2]));
++}
++
+ /**
+  * eth_type_trans - determine the packet's protocol ID.
+  * @skb: received socket data
+@@ -199,8 +211,12 @@ __be16 eth_type_trans(struct sk_buff *sk
+                       skb->pkt_type = PACKET_MULTICAST;
+       }
+       else if (unlikely(!ether_addr_equal_64bits(eth->h_dest,
+-                                                 dev->dev_addr)))
++                                                 dev->dev_addr))) {
+               skb->pkt_type = PACKET_OTHERHOST;
++              if (eth_check_local_mask(eth->h_dest, dev->dev_addr,
++                                       dev->local_addr_mask))
++                      skb->gro_skip = 1;
++      }
+       /*
+        * Some variants of DSA tagging don't have an ethertype field
diff --git a/target/linux/generic/patches-4.0/680-net-skip-gro-for-otherhost-packets.patch b/target/linux/generic/patches-4.0/680-net-skip-gro-for-otherhost-packets.patch
deleted file mode 100644 (file)
index 62e0bde..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-Subject: NET: skip GRO for otherhost packets
-
-For network drivers using napi_gro_receive, packets are run through GRO,
-even when the destination MAC address does not match, and they're supposed
-to be delivered to another host behind a different bridge port.
-
-This can be very expensive, because for drivers without TSO or scatter-
-gather, this can only be undone by copying the skb and checksumming it
-again.
-
-Fix this by leaving skbs with PACKET_OTHERHOST untouched.
-
-Signed-off-by: Felix Fietkau <nbd@openwrt.org>
-
---- a/net/core/dev.c
-+++ b/net/core/dev.c
-@@ -4005,6 +4005,9 @@ static enum gro_result dev_gro_receive(s
-       enum gro_result ret;
-       int grow;
-+      if (skb->pkt_type == PACKET_OTHERHOST)
-+              goto normal;
-+
-       if (!(skb->dev->features & NETIF_F_GRO))
-               goto normal;
index 30dde5e935c8dc3087890cb086a0448a1ef95c31..1ace035a1bf69101acec2055deb476f009fa0d09 100644 (file)
@@ -28,7 +28,7 @@
        const struct header_ops *header_ops;
  
        unsigned int            flags;
-@@ -1619,6 +1626,10 @@ struct net_device {
+@@ -1621,6 +1628,10 @@ struct net_device {
        struct wireless_dev     *ieee80211_ptr;
        struct wpan_dev         *ieee802154_ptr;
  
@@ -41,7 +41,7 @@
   */
 --- a/include/linux/skbuff.h
 +++ b/include/linux/skbuff.h
-@@ -2049,6 +2049,10 @@ static inline int pskb_trim(struct sk_bu
+@@ -2050,6 +2050,10 @@ static inline int pskb_trim(struct sk_bu
        return (len < skb->len) ? __pskb_trim(skb, len) : 0;
  }
  
@@ -52,7 +52,7 @@
  /**
   *    pskb_trim_unique - remove end from a paged unique (not cloned) buffer
   *    @skb: buffer to alter
-@@ -2175,16 +2179,6 @@ static inline struct sk_buff *dev_alloc_
+@@ -2176,16 +2180,6 @@ static inline struct sk_buff *dev_alloc_
  }
  
  
  
  #include <net/protocol.h>
  #include <net/dst.h>
-@@ -550,6 +551,22 @@ struct sk_buff *__napi_alloc_skb(struct
+@@ -550,6 +551,22 @@ struct sk_buff *__napi_alloc_skb(struct 
  }
  EXPORT_SYMBOL(__napi_alloc_skb);
  
  {
 --- a/net/ethernet/eth.c
 +++ b/net/ethernet/eth.c
-@@ -188,6 +188,12 @@ __be16 eth_type_trans(struct sk_buff *sk
+@@ -200,6 +200,12 @@ __be16 eth_type_trans(struct sk_buff *sk
        const struct ethhdr *eth;
  
        skb->dev = dev;