|  | 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(-) | 
|  |  | 
|  | --- a/include/linux/netdevice.h | 
|  | +++ b/include/linux/netdevice.h | 
|  | @@ -1936,6 +1936,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 | 
|  | @@ -826,6 +826,7 @@ struct sk_buff { | 
|  | __u8			decrypted:1; | 
|  | #endif | 
|  | __u8			scm_io_uring:1; | 
|  | +	__u8			gro_skip:1; | 
|  |  | 
|  | #ifdef CONFIG_NET_SCHED | 
|  | __u16			tc_index;	/* traffic control index */ | 
|  | --- a/net/core/dev.c | 
|  | +++ b/net/core/dev.c | 
|  | @@ -5544,6 +5544,9 @@ static enum gro_result dev_gro_receive(s | 
|  | int same_flow; | 
|  | int grow; | 
|  |  | 
|  | +	if (skb->gro_skip) | 
|  | +		goto normal; | 
|  | + | 
|  | if (netif_elide_gro(skb->dev)) | 
|  | goto normal; | 
|  |  | 
|  | @@ -7487,6 +7490,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 *upper_priv, void *upper_info, | 
|  | @@ -7537,6 +7582,7 @@ static int __netdev_upper_dev_link(struc | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | +	netdev_update_addr_mask(dev); | 
|  | ret = call_netdevice_notifiers_info(NETDEV_CHANGEUPPER, | 
|  | &changeupper_info.info); | 
|  | ret = notifier_to_errno(ret); | 
|  | @@ -7630,6 +7676,7 @@ void netdev_upper_dev_unlink(struct net_ | 
|  |  | 
|  | __netdev_adjacent_dev_unlink_neighbour(dev, upper_dev); | 
|  |  | 
|  | +	netdev_update_addr_mask(dev); | 
|  | call_netdevice_notifiers_info(NETDEV_CHANGEUPPER, | 
|  | &changeupper_info.info); | 
|  |  | 
|  | @@ -8360,6 +8407,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/net/ethernet/eth.c | 
|  | +++ b/net/ethernet/eth.c | 
|  | @@ -143,6 +143,18 @@ u32 eth_get_headlen(const struct net_dev | 
|  | } | 
|  | 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 | 
|  | @@ -174,6 +186,10 @@ __be16 eth_type_trans(struct sk_buff *sk | 
|  | } else { | 
|  | skb->pkt_type = PACKET_OTHERHOST; | 
|  | } | 
|  | + | 
|  | +		if (eth_check_local_mask(eth->h_dest, dev->dev_addr, | 
|  | +					 dev->local_addr_mask)) | 
|  | +			skb->gro_skip = 1; | 
|  | } | 
|  |  | 
|  | /* |