kernel: replace GRO optimization patch with a new one that supports VLANs/bridges with different MAC addresses
Signed-off-by: Felix Fietkau <nbd@openwrt.org> SVN-Revision: 44877
This commit is contained in:
parent
17afb853ee
commit
b011293d0b
9 changed files with 494 additions and 92 deletions
|
@ -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
|
|
@ -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;
|
||||
|
|
@ -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_
|
||||
}
|
||||
|
||||
|
||||
|
@ -146,7 +146,7 @@
|
|||
{
|
||||
--- 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;
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
|
|
@ -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_
|
||||
}
|
||||
|
||||
|
||||
|
@ -146,7 +146,7 @@
|
|||
{
|
||||
--- 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;
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
|
|
@ -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_
|
||||
}
|
||||
|
||||
|
||||
|
@ -146,7 +146,7 @@
|
|||
{
|
||||
--- 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;
|
||||
|
|
Loading…
Reference in a new issue