aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/generic/pending-4.9/680-NET-skip-GRO-for-foreign-MAC-addresses.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/generic/pending-4.9/680-NET-skip-GRO-for-foreign-MAC-addresses.patch')
-rw-r--r--target/linux/generic/pending-4.9/680-NET-skip-GRO-for-foreign-MAC-addresses.patch158
1 files changed, 158 insertions, 0 deletions
diff --git a/target/linux/generic/pending-4.9/680-NET-skip-GRO-for-foreign-MAC-addresses.patch b/target/linux/generic/pending-4.9/680-NET-skip-GRO-for-foreign-MAC-addresses.patch
new file mode 100644
index 0000000000..e886d70dcc
--- /dev/null
+++ b/target/linux/generic/pending-4.9/680-NET-skip-GRO-for-foreign-MAC-addresses.patch
@@ -0,0 +1,158 @@
+From: Felix Fietkau <nbd@nbd.name>
+Subject: net: replace GRO optimization patch with a new one that supports VLANs/bridges with different MAC addresses
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ include/linux/netdevice.h | 2 ++
+ include/linux/skbuff.h | 3 ++-
+ net/core/dev.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++
+ net/ethernet/eth.c | 18 +++++++++++++++++-
+ 4 files changed, 69 insertions(+), 2 deletions(-)
+
+diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
+index 780e7171f548..6b738c662bc1 100644
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -1749,6 +1749,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
+diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
+index 5f3343ae25ef..3a04baab9b28 100644
+--- a/include/linux/skbuff.h
++++ b/include/linux/skbuff.h
+@@ -742,7 +742,8 @@ struct sk_buff {
+ #ifdef CONFIG_NET_SWITCHDEV
+ __u8 offload_fwd_mark:1;
+ #endif
+- /* 2, 4 or 5 bit hole */
++ __u8 gro_skip:1;
++ /* 1, 3 or 4 bit hole */
+
+ #ifdef CONFIG_NET_SCHED
+ __u16 tc_index; /* traffic control index */
+diff --git a/net/core/dev.c b/net/core/dev.c
+index 2e04fd188081..c7c96308bc84 100644
+--- a/net/core/dev.c
++++ b/net/core/dev.c
+@@ -4512,6 +4512,9 @@ static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff
+ enum gro_result ret;
+ int grow;
+
++ if (skb->gro_skip)
++ goto normal;
++
+ if (!(skb->dev->features & NETIF_F_GRO))
+ goto normal;
+
+@@ -5789,6 +5792,48 @@ static void __netdev_adjacent_dev_unlink_neighbour(struct net_device *dev,
+ &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 *upper_priv, void *upper_info)
+@@ -5987,6 +6032,8 @@ void netdev_upper_dev_unlink(struct net_device *dev,
+ list_for_each_entry(i, &upper_dev->all_adj_list.upper, list)
+ __netdev_adjacent_dev_unlink(dev, i->dev, i->ref_nr);
+
++ netdev_update_addr_mask(dev);
++ netdev_update_addr_mask(dev);
+ call_netdevice_notifiers_info(NETDEV_CHANGEUPPER, dev,
+ &changeupper_info.info);
+ }
+@@ -6587,6 +6634,7 @@ int dev_set_mac_address(struct net_device *dev, struct sockaddr *sa)
+ 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;
+diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c
+index fbf1de965a9a..6a6d90b9a880 100644
+--- a/net/ethernet/eth.c
++++ b/net/ethernet/eth.c
+@@ -143,6 +143,18 @@ u32 eth_get_headlen(void *data, unsigned int len)
+ }
+ 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
+@@ -171,8 +183,12 @@ __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev)
+ 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
+--
+2.11.0
+