Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ipv6: introduce and uses route look hints for list input.

When doing RX batch packet processing, we currently always repeat
the route lookup for each ingress packet. When no custom rules are
in place, and there aren't routes depending on source addresses,
we know that packets with the same destination address will use
the same dst.

This change tries to avoid per packet route lookup caching
the destination address of the latest successful lookup, and
reusing it for the next packet when the above conditions are
in place. Ingress traffic for most servers should fit.

The measured performance delta under UDP flood vs a recvmmsg
receiver is as follow:

vanilla patched delta
Kpps Kpps %
1431 1674 +17

In the worst-case scenario - each packet has a different
destination address - the performance delta is within noise
range.

v3 -> v4:
- support hints for SUBFLOW build, too (David A.)
- several style fixes (Eric)

v2 -> v3:
- add fib6_has_custom_rules() helpers (David A.)
- add ip6_extract_route_hint() helper (Edward C.)
- use hint directly in ip6_list_rcv_finish() (Willem)

v1 -> v2:
- fix build issue with !CONFIG_IPV6_MULTIPLE_TABLES
- fix potential race when fib6_has_custom_rules is set
while processing a packet batch

Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Reviewed-by: David Ahern <dsahern@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Paolo Abeni and committed by
David S. Miller
197dbf24 b9b33e7c

+24 -2
+24 -2
net/ipv6/ip6_input.c
··· 86 86 } 87 87 } 88 88 89 + static bool ip6_can_use_hint(const struct sk_buff *skb, 90 + const struct sk_buff *hint) 91 + { 92 + return hint && !skb_dst(skb) && 93 + ipv6_addr_equal(&ipv6_hdr(hint)->daddr, &ipv6_hdr(skb)->daddr); 94 + } 95 + 96 + static struct sk_buff *ip6_extract_route_hint(const struct net *net, 97 + struct sk_buff *skb) 98 + { 99 + if (fib6_routes_require_src(net) || fib6_has_custom_rules(net)) 100 + return NULL; 101 + 102 + return skb; 103 + } 104 + 89 105 static void ip6_list_rcv_finish(struct net *net, struct sock *sk, 90 106 struct list_head *head) 91 107 { 108 + struct sk_buff *skb, *next, *hint = NULL; 92 109 struct dst_entry *curr_dst = NULL; 93 - struct sk_buff *skb, *next; 94 110 struct list_head sublist; 95 111 96 112 INIT_LIST_HEAD(&sublist); ··· 120 104 skb = l3mdev_ip6_rcv(skb); 121 105 if (!skb) 122 106 continue; 123 - ip6_rcv_finish_core(net, sk, skb); 107 + 108 + if (ip6_can_use_hint(skb, hint)) 109 + skb_dst_copy(skb, hint); 110 + else 111 + ip6_rcv_finish_core(net, sk, skb); 124 112 dst = skb_dst(skb); 125 113 if (curr_dst != dst) { 114 + hint = ip6_extract_route_hint(net, skb); 115 + 126 116 /* dispatch old sublist */ 127 117 if (!list_empty(&sublist)) 128 118 ip6_sublist_rcv_finish(&sublist);