OpenWrt – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | Subject: NET: skip GRO for foreign MAC addresses |
2 | |||
3 | For network drivers using napi_gro_receive, packets are run through GRO, |
||
4 | even when the destination MAC address does not match, and they're supposed |
||
5 | to be delivered to another host behind a different bridge port. |
||
6 | |||
7 | This can be very expensive, because for drivers without TSO or scatter- |
||
8 | gather, this can only be undone by copying the skb and checksumming it |
||
9 | again. |
||
10 | |||
11 | To be able to track foreign MAC addresses in an inexpensive way, create |
||
12 | a mask of changed bits in MAC addresses of upper devices. This allows |
||
13 | handling VLANs and bridge devices with different addresses (as long as |
||
14 | they are not too different). |
||
15 | |||
16 | Signed-off-by: Felix Fietkau <nbd@nbd.name> |
||
17 | |||
18 | --- a/net/core/dev.c |
||
19 | +++ b/net/core/dev.c |
||
20 | @@ -4028,6 +4028,9 @@ static enum gro_result dev_gro_receive(s |
||
21 | enum gro_result ret; |
||
22 | int grow; |
||
23 | |||
24 | + if (skb->gro_skip) |
||
25 | + goto normal; |
||
26 | + |
||
27 | if (!(skb->dev->features & NETIF_F_GRO)) |
||
28 | goto normal; |
||
29 | |||
30 | @@ -5103,6 +5106,48 @@ static void __netdev_adjacent_dev_unlink |
||
31 | &upper_dev->adj_list.lower); |
||
32 | } |
||
33 | |||
34 | +static void __netdev_addr_mask(unsigned char *mask, const unsigned char *addr, |
||
35 | + struct net_device *dev) |
||
36 | +{ |
||
37 | + int i; |
||
38 | + |
||
39 | + for (i = 0; i < dev->addr_len; i++) |
||
40 | + mask[i] |= addr[i] ^ dev->dev_addr[i]; |
||
41 | +} |
||
42 | + |
||
43 | +static void __netdev_upper_mask(unsigned char *mask, struct net_device *dev, |
||
44 | + struct net_device *lower) |
||
45 | +{ |
||
46 | + struct net_device *cur; |
||
47 | + struct list_head *iter; |
||
48 | + |
||
49 | + netdev_for_each_upper_dev_rcu(dev, cur, iter) { |
||
50 | + __netdev_addr_mask(mask, cur->dev_addr, lower); |
||
51 | + __netdev_upper_mask(mask, cur, lower); |
||
52 | + } |
||
53 | +} |
||
54 | + |
||
55 | +static void __netdev_update_addr_mask(struct net_device *dev) |
||
56 | +{ |
||
57 | + unsigned char mask[MAX_ADDR_LEN]; |
||
58 | + struct net_device *cur; |
||
59 | + struct list_head *iter; |
||
60 | + |
||
61 | + memset(mask, 0, sizeof(mask)); |
||
62 | + __netdev_upper_mask(mask, dev, dev); |
||
63 | + memcpy(dev->local_addr_mask, mask, dev->addr_len); |
||
64 | + |
||
65 | + netdev_for_each_lower_dev(dev, cur, iter) |
||
66 | + __netdev_update_addr_mask(cur); |
||
67 | +} |
||
68 | + |
||
69 | +static void netdev_update_addr_mask(struct net_device *dev) |
||
70 | +{ |
||
71 | + rcu_read_lock(); |
||
72 | + __netdev_update_addr_mask(dev); |
||
73 | + rcu_read_unlock(); |
||
74 | +} |
||
75 | + |
||
76 | static int __netdev_upper_dev_link(struct net_device *dev, |
||
77 | struct net_device *upper_dev, bool master, |
||
78 | void *private) |
||
79 | @@ -5163,6 +5208,7 @@ static int __netdev_upper_dev_link(struc |
||
80 | goto rollback_lower_mesh; |
||
81 | } |
||
82 | |||
83 | + netdev_update_addr_mask(dev); |
||
84 | call_netdevice_notifiers(NETDEV_CHANGEUPPER, dev); |
||
85 | return 0; |
||
86 | |||
87 | @@ -5280,6 +5326,7 @@ void netdev_upper_dev_unlink(struct net_ |
||
88 | list_for_each_entry(i, &upper_dev->all_adj_list.upper, list) |
||
89 | __netdev_adjacent_dev_unlink(dev, i->dev, i->ref_nr); |
||
90 | |||
91 | + netdev_update_addr_mask(dev); |
||
92 | call_netdevice_notifiers(NETDEV_CHANGEUPPER, dev); |
||
93 | } |
||
94 | EXPORT_SYMBOL(netdev_upper_dev_unlink); |
||
95 | @@ -5799,6 +5846,7 @@ int dev_set_mac_address(struct net_devic |
||
96 | if (err) |
||
97 | return err; |
||
98 | dev->addr_assign_type = NET_ADDR_SET; |
||
99 | + netdev_update_addr_mask(dev); |
||
100 | call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); |
||
101 | add_device_randomness(dev->dev_addr, dev->addr_len); |
||
102 | return 0; |
||
103 | --- a/include/linux/netdevice.h |
||
104 | +++ b/include/linux/netdevice.h |
||
105 | @@ -1556,6 +1556,8 @@ struct net_device { |
||
106 | struct netdev_hw_addr_list mc; |
||
107 | struct netdev_hw_addr_list dev_addrs; |
||
108 | |||
109 | + unsigned char local_addr_mask[MAX_ADDR_LEN]; |
||
110 | + |
||
111 | #ifdef CONFIG_SYSFS |
||
112 | struct kset *queues_kset; |
||
113 | #endif |
||
114 | --- a/include/linux/skbuff.h |
||
115 | +++ b/include/linux/skbuff.h |
||
116 | @@ -598,7 +598,8 @@ struct sk_buff { |
||
117 | __u8 ipvs_property:1; |
||
118 | |||
119 | __u8 inner_protocol_type:1; |
||
120 | - /* 4 or 6 bit hole */ |
||
121 | + __u8 gro_skip:1; |
||
122 | + /* 3 or 5 bit hole */ |
||
123 | |||
124 | #ifdef CONFIG_NET_SCHED |
||
125 | __u16 tc_index; /* traffic control index */ |
||
126 | --- a/net/ethernet/eth.c |
||
127 | +++ b/net/ethernet/eth.c |
||
128 | @@ -172,6 +172,18 @@ u32 eth_get_headlen(void *data, unsigned |
||
129 | } |
||
130 | EXPORT_SYMBOL(eth_get_headlen); |
||
131 | |||
132 | +static inline bool |
||
133 | +eth_check_local_mask(const void *addr1, const void *addr2, const void *mask) |
||
134 | +{ |
||
135 | + const u16 *a1 = addr1; |
||
136 | + const u16 *a2 = addr2; |
||
137 | + const u16 *m = mask; |
||
138 | + |
||
139 | + return (((a1[0] ^ a2[0]) & ~m[0]) | |
||
140 | + ((a1[1] ^ a2[1]) & ~m[1]) | |
||
141 | + ((a1[2] ^ a2[2]) & ~m[2])); |
||
142 | +} |
||
143 | + |
||
144 | /** |
||
145 | * eth_type_trans - determine the packet's protocol ID. |
||
146 | * @skb: received socket data |
||
147 | @@ -199,8 +211,12 @@ __be16 eth_type_trans(struct sk_buff *sk |
||
148 | skb->pkt_type = PACKET_MULTICAST; |
||
149 | } |
||
150 | else if (unlikely(!ether_addr_equal_64bits(eth->h_dest, |
||
151 | - dev->dev_addr))) |
||
152 | + dev->dev_addr))) { |
||
153 | skb->pkt_type = PACKET_OTHERHOST; |
||
154 | + if (eth_check_local_mask(eth->h_dest, dev->dev_addr, |
||
155 | + dev->local_addr_mask)) |
||
156 | + skb->gro_skip = 1; |
||
157 | + } |
||
158 | |||
159 | /* |
||
160 | * Some variants of DSA tagging don't have an ethertype field |