diff options
author | Felix Fietkau <nbd@openwrt.org> | 2015-03-18 18:22:41 +0000 |
---|---|---|
committer | Felix Fietkau <nbd@openwrt.org> | 2015-03-18 18:22:41 +0000 |
commit | 1eb74d2c55dfcb60ed3d4a557bd360680695ffbc (patch) | |
tree | 7cd84a087ab09abdbd3ef6914d4eeb165ec9a12c /target/linux/generic/patches-3.18 | |
parent | 5d4be21553ddc62ac97b9698c656829d7e814ee1 (diff) | |
download | upstream-1eb74d2c55dfcb60ed3d4a557bd360680695ffbc.tar.gz upstream-1eb74d2c55dfcb60ed3d4a557bd360680695ffbc.tar.bz2 upstream-1eb74d2c55dfcb60ed3d4a557bd360680695ffbc.zip |
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>
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@44877 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'target/linux/generic/patches-3.18')
3 files changed, 164 insertions, 30 deletions
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 index 0000000000..ad5000e2b9 --- /dev/null +++ b/target/linux/generic/patches-3.18/680-NET-skip-GRO-for-foreign-MAC-addresses.patch @@ -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 index 2a65c0be61..0000000000 --- a/target/linux/generic/patches-3.18/680-net-skip-gro-for-otherhost-packets.patch +++ /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; - diff --git a/target/linux/generic/patches-3.18/721-phy_packets.patch b/target/linux/generic/patches-3.18/721-phy_packets.patch index 5d989741a6..aa1897cad9 100644 --- a/target/linux/generic/patches-3.18/721-phy_packets.patch +++ b/target/linux/generic/patches-3.18/721-phy_packets.patch @@ -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; |