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

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'ipv6-tcp-no-longer-rebuild-fl6-at-each-transmit'

Eric Dumazet says:

====================
ipv6: tcp: no longer rebuild fl6 at each transmit

TCP v6 spends a good amount of time rebuilding a fresh fl6 at each
transmit in inet6_csk_xmit()/inet6_csk_route_socket().

TCP v4 caches the information in inet->cork.fl.u.ip4 instead.

This series changes TCP v6 to behave the same, saving cpu cycles
and reducing cache line misses and stack use.
====================

Link: https://patch.msgid.link/20260206173426.1638518-1-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+82 -72
+4
include/linux/ipv6.h
··· 230 230 struct ipv6_pinfo { 231 231 /* Used in tx path (inet6_csk_route_socket(), ip6_xmit()) */ 232 232 struct in6_addr saddr; 233 + union { 234 + struct in6_addr daddr; 235 + struct in6_addr final; 236 + }; 233 237 __be32 flow_label; 234 238 u32 dst_cookie; 235 239 struct ipv6_txoptions __rcu *opt;
+3 -1
include/net/inet6_connection_sock.h
··· 18 18 struct sock; 19 19 struct sockaddr; 20 20 21 - struct dst_entry *inet6_csk_route_req(const struct sock *sk, struct flowi6 *fl6, 21 + struct dst_entry *inet6_csk_route_req(const struct sock *sk, 22 + struct dst_entry *dst, 23 + struct flowi6 *fl6, 22 24 const struct request_sock *req, u8 proto); 23 25 24 26 int inet6_csk_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl);
+2 -2
net/ipv6/af_inet6.c
··· 825 825 { 826 826 struct ipv6_pinfo *np = inet6_sk(sk); 827 827 struct inet_sock *inet = inet_sk(sk); 828 - struct in6_addr *final_p, final; 828 + struct in6_addr *final_p; 829 829 struct dst_entry *dst; 830 830 struct flowi6 *fl6; 831 831 ··· 847 847 security_sk_classify_flow(sk, flowi6_to_flowi_common(fl6)); 848 848 849 849 rcu_read_lock(); 850 - final_p = fl6_update_dst(fl6, rcu_dereference(np->opt), &final); 850 + final_p = fl6_update_dst(fl6, rcu_dereference(np->opt), &np->final); 851 851 rcu_read_unlock(); 852 852 853 853 dst = ip6_dst_lookup_flow(sock_net(sk), sk, fl6, final_p);
+11 -10
net/ipv6/datagram.c
··· 72 72 int ip6_datagram_dst_update(struct sock *sk, bool fix_sk_saddr) 73 73 { 74 74 struct ip6_flowlabel *flowlabel = NULL; 75 - struct in6_addr *final_p, final; 76 - struct ipv6_txoptions *opt; 77 - struct dst_entry *dst; 78 75 struct inet_sock *inet = inet_sk(sk); 79 76 struct ipv6_pinfo *np = inet6_sk(sk); 80 - struct flowi6 fl6; 77 + struct ipv6_txoptions *opt; 78 + struct in6_addr *final_p; 79 + struct dst_entry *dst; 80 + struct flowi6 *fl6; 81 81 int err = 0; 82 82 83 83 if (inet6_test_bit(SNDFLOW, sk) && ··· 86 86 if (IS_ERR(flowlabel)) 87 87 return -EINVAL; 88 88 } 89 - ip6_datagram_flow_key_init(&fl6, sk); 89 + fl6 = &inet_sk(sk)->cork.fl.u.ip6; 90 + ip6_datagram_flow_key_init(fl6, sk); 90 91 91 92 rcu_read_lock(); 92 93 opt = flowlabel ? flowlabel->opt : rcu_dereference(np->opt); 93 - final_p = fl6_update_dst(&fl6, opt, &final); 94 + final_p = fl6_update_dst(fl6, opt, &np->final); 94 95 rcu_read_unlock(); 95 96 96 - dst = ip6_dst_lookup_flow(sock_net(sk), sk, &fl6, final_p); 97 + dst = ip6_dst_lookup_flow(sock_net(sk), sk, fl6, final_p); 97 98 if (IS_ERR(dst)) { 98 99 err = PTR_ERR(dst); 99 100 goto out; ··· 102 101 103 102 if (fix_sk_saddr) { 104 103 if (ipv6_addr_any(&np->saddr)) 105 - np->saddr = fl6.saddr; 104 + np->saddr = fl6->saddr; 106 105 107 106 if (ipv6_addr_any(&sk->sk_v6_rcv_saddr)) { 108 - sk->sk_v6_rcv_saddr = fl6.saddr; 107 + sk->sk_v6_rcv_saddr = fl6->saddr; 109 108 inet->inet_rcv_saddr = LOOPBACK4_IPV6; 110 109 if (sk->sk_prot->rehash) 111 110 sk->sk_prot->rehash(sk); 112 111 } 113 112 } 114 113 115 - ip6_sk_dst_store_flow(sk, dst, &fl6); 114 + ip6_sk_dst_store_flow(sk, dst, fl6); 116 115 117 116 out: 118 117 fl6_sock_release(flowlabel);
+30 -27
net/ipv6/inet6_connection_sock.c
··· 25 25 #include <net/sock_reuseport.h> 26 26 27 27 struct dst_entry *inet6_csk_route_req(const struct sock *sk, 28 + struct dst_entry *dst, 28 29 struct flowi6 *fl6, 29 30 const struct request_sock *req, 30 31 u8 proto) ··· 33 32 const struct inet_request_sock *ireq = inet_rsk(req); 34 33 const struct ipv6_pinfo *np = inet6_sk(sk); 35 34 struct in6_addr *final_p, final; 36 - struct dst_entry *dst; 37 35 38 36 memset(fl6, 0, sizeof(*fl6)); 39 37 fl6->flowi6_proto = proto; ··· 48 48 fl6->flowi6_uid = sk_uid(sk); 49 49 security_req_classify_flow(req, flowi6_to_flowi_common(fl6)); 50 50 51 - dst = ip6_dst_lookup_flow(sock_net(sk), sk, fl6, final_p); 52 - if (IS_ERR(dst)) 53 - return NULL; 54 - 51 + if (!dst) { 52 + dst = ip6_dst_lookup_flow(sock_net(sk), sk, fl6, final_p); 53 + if (IS_ERR(dst)) 54 + return NULL; 55 + } 55 56 return dst; 56 57 } 57 58 ··· 61 60 { 62 61 struct inet_sock *inet = inet_sk(sk); 63 62 struct ipv6_pinfo *np = inet6_sk(sk); 64 - struct in6_addr *final_p, final; 63 + struct in6_addr *final_p; 65 64 struct dst_entry *dst; 66 65 67 66 memset(fl6, 0, sizeof(*fl6)); ··· 78 77 security_sk_classify_flow(sk, flowi6_to_flowi_common(fl6)); 79 78 80 79 rcu_read_lock(); 81 - final_p = fl6_update_dst(fl6, rcu_dereference(np->opt), &final); 80 + final_p = fl6_update_dst(fl6, rcu_dereference(np->opt), &np->final); 82 81 rcu_read_unlock(); 83 82 84 - dst = __sk_dst_check(sk, np->dst_cookie); 85 - if (!dst) { 86 - dst = ip6_dst_lookup_flow(sock_net(sk), sk, fl6, final_p); 83 + dst = ip6_dst_lookup_flow(sock_net(sk), sk, fl6, final_p); 87 84 88 - if (!IS_ERR(dst)) 89 - ip6_dst_store(sk, dst, false, false); 90 - } 85 + if (!IS_ERR(dst)) 86 + ip6_dst_store(sk, dst, false, false); 87 + 91 88 return dst; 92 89 } 93 90 94 91 int inet6_csk_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl_unused) 95 92 { 93 + struct flowi6 *fl6 = &inet_sk(sk)->cork.fl.u.ip6; 96 94 struct ipv6_pinfo *np = inet6_sk(sk); 97 - struct flowi6 fl6; 98 95 struct dst_entry *dst; 99 96 int res; 100 97 101 - dst = inet6_csk_route_socket(sk, &fl6); 102 - if (IS_ERR(dst)) { 103 - WRITE_ONCE(sk->sk_err_soft, -PTR_ERR(dst)); 104 - sk->sk_route_caps = 0; 105 - kfree_skb(skb); 106 - return PTR_ERR(dst); 98 + dst = __sk_dst_check(sk, np->dst_cookie); 99 + if (unlikely(!dst)) { 100 + dst = inet6_csk_route_socket(sk, fl6); 101 + if (IS_ERR(dst)) { 102 + WRITE_ONCE(sk->sk_err_soft, -PTR_ERR(dst)); 103 + sk->sk_route_caps = 0; 104 + kfree_skb(skb); 105 + return PTR_ERR(dst); 106 + } 107 + /* Restore final destination back after routing done */ 108 + fl6->daddr = sk->sk_v6_daddr; 107 109 } 108 110 109 111 rcu_read_lock(); 110 112 skb_dst_set_noref(skb, dst); 111 113 112 - /* Restore final destination back after routing done */ 113 - fl6.daddr = sk->sk_v6_daddr; 114 - 115 - res = ip6_xmit(sk, skb, &fl6, sk->sk_mark, rcu_dereference(np->opt), 114 + res = ip6_xmit(sk, skb, fl6, sk->sk_mark, rcu_dereference(np->opt), 116 115 np->tclass, READ_ONCE(sk->sk_priority)); 117 116 rcu_read_unlock(); 118 117 return res; ··· 121 120 122 121 struct dst_entry *inet6_csk_update_pmtu(struct sock *sk, u32 mtu) 123 122 { 124 - struct flowi6 fl6; 125 - struct dst_entry *dst = inet6_csk_route_socket(sk, &fl6); 123 + struct flowi6 *fl6 = &inet_sk(sk)->cork.fl.u.ip6; 124 + struct dst_entry *dst; 125 + 126 + dst = inet6_csk_route_socket(sk, fl6); 126 127 127 128 if (IS_ERR(dst)) 128 129 return NULL; 129 130 dst->ops->update_pmtu(dst, sk, NULL, mtu, true); 130 131 131 - dst = inet6_csk_route_socket(sk, &fl6); 132 + dst = inet6_csk_route_socket(sk, fl6); 132 133 return IS_ERR(dst) ? NULL : dst; 133 134 }
+32 -32
net/ipv6/tcp_ipv6.c
··· 138 138 { 139 139 struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr; 140 140 struct inet_connection_sock *icsk = inet_csk(sk); 141 - struct in6_addr *saddr = NULL, *final_p, final; 142 141 struct inet_timewait_death_row *tcp_death_row; 143 142 struct ipv6_pinfo *np = tcp_inet6_sk(sk); 143 + struct in6_addr *saddr = NULL, *final_p; 144 144 struct inet_sock *inet = inet_sk(sk); 145 145 struct tcp_sock *tp = tcp_sk(sk); 146 146 struct net *net = sock_net(sk); 147 147 struct ipv6_txoptions *opt; 148 148 struct dst_entry *dst; 149 - struct flowi6 fl6; 149 + struct flowi6 *fl6; 150 150 int addr_type; 151 151 int err; 152 152 ··· 156 156 if (usin->sin6_family != AF_INET6) 157 157 return -EAFNOSUPPORT; 158 158 159 - memset(&fl6, 0, sizeof(fl6)); 159 + fl6 = &inet_sk(sk)->cork.fl.u.ip6; 160 + memset(fl6, 0, sizeof(*fl6)); 160 161 161 162 if (inet6_test_bit(SNDFLOW, sk)) { 162 - fl6.flowlabel = usin->sin6_flowinfo&IPV6_FLOWINFO_MASK; 163 - IP6_ECN_flow_init(fl6.flowlabel); 164 - if (fl6.flowlabel&IPV6_FLOWLABEL_MASK) { 163 + fl6->flowlabel = usin->sin6_flowinfo & IPV6_FLOWINFO_MASK; 164 + IP6_ECN_flow_init(fl6->flowlabel); 165 + if (fl6->flowlabel & IPV6_FLOWLABEL_MASK) { 165 166 struct ip6_flowlabel *flowlabel; 166 - flowlabel = fl6_sock_lookup(sk, fl6.flowlabel); 167 + flowlabel = fl6_sock_lookup(sk, fl6->flowlabel); 167 168 if (IS_ERR(flowlabel)) 168 169 return -EINVAL; 169 170 fl6_sock_release(flowlabel); ··· 213 212 } 214 213 215 214 sk->sk_v6_daddr = usin->sin6_addr; 216 - np->flow_label = fl6.flowlabel; 215 + np->flow_label = fl6->flowlabel; 217 216 218 217 /* 219 218 * TCP over IPv4 ··· 261 260 if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr)) 262 261 saddr = &sk->sk_v6_rcv_saddr; 263 262 264 - fl6.flowi6_proto = IPPROTO_TCP; 265 - fl6.daddr = sk->sk_v6_daddr; 266 - fl6.saddr = saddr ? *saddr : np->saddr; 267 - fl6.flowlabel = ip6_make_flowinfo(np->tclass, np->flow_label); 268 - fl6.flowi6_oif = sk->sk_bound_dev_if; 269 - fl6.flowi6_mark = sk->sk_mark; 270 - fl6.fl6_dport = usin->sin6_port; 271 - fl6.fl6_sport = inet->inet_sport; 272 - if (IS_ENABLED(CONFIG_IP_ROUTE_MULTIPATH) && !fl6.fl6_sport) 273 - fl6.flowi6_flags = FLOWI_FLAG_ANY_SPORT; 274 - fl6.flowi6_uid = sk_uid(sk); 263 + fl6->flowi6_proto = IPPROTO_TCP; 264 + fl6->daddr = sk->sk_v6_daddr; 265 + fl6->saddr = saddr ? *saddr : np->saddr; 266 + fl6->flowlabel = ip6_make_flowinfo(np->tclass, np->flow_label); 267 + fl6->flowi6_oif = sk->sk_bound_dev_if; 268 + fl6->flowi6_mark = sk->sk_mark; 269 + fl6->fl6_dport = usin->sin6_port; 270 + fl6->fl6_sport = inet->inet_sport; 271 + if (IS_ENABLED(CONFIG_IP_ROUTE_MULTIPATH) && !fl6->fl6_sport) 272 + fl6->flowi6_flags = FLOWI_FLAG_ANY_SPORT; 273 + fl6->flowi6_uid = sk_uid(sk); 275 274 276 275 opt = rcu_dereference_protected(np->opt, lockdep_sock_is_held(sk)); 277 - final_p = fl6_update_dst(&fl6, opt, &final); 276 + final_p = fl6_update_dst(fl6, opt, &np->final); 278 277 279 - security_sk_classify_flow(sk, flowi6_to_flowi_common(&fl6)); 278 + security_sk_classify_flow(sk, flowi6_to_flowi_common(fl6)); 280 279 281 - dst = ip6_dst_lookup_flow(net, sk, &fl6, final_p); 280 + dst = ip6_dst_lookup_flow(net, sk, fl6, final_p); 282 281 if (IS_ERR(dst)) { 283 282 err = PTR_ERR(dst); 284 283 goto failure; ··· 288 287 tcp_death_row = &sock_net(sk)->ipv4.tcp_death_row; 289 288 290 289 if (!saddr) { 291 - saddr = &fl6.saddr; 290 + saddr = &fl6->saddr; 292 291 293 292 err = inet_bhash2_update_saddr(sk, saddr, AF_INET6); 294 293 if (err) ··· 539 538 u8 tclass; 540 539 541 540 /* First, grab a route. */ 542 - if (!dst && (dst = inet6_csk_route_req(sk, fl6, req, 541 + if (!dst && (dst = inet6_csk_route_req(sk, NULL, fl6, req, 543 542 IPPROTO_TCP)) == NULL) 544 543 goto done; 545 544 ··· 789 788 if (security_inet_conn_request(sk, skb, req)) 790 789 return NULL; 791 790 792 - return inet6_csk_route_req(sk, &fl->u.ip6, req, IPPROTO_TCP); 791 + return inet6_csk_route_req(sk, NULL, &fl->u.ip6, req, IPPROTO_TCP); 793 792 } 794 793 795 794 struct request_sock_ops tcp6_request_sock_ops __read_mostly = { ··· 1318 1317 struct request_sock *req_unhash, 1319 1318 bool *own_req) 1320 1319 { 1321 - struct inet_request_sock *ireq; 1322 - struct ipv6_pinfo *newnp; 1323 1320 const struct ipv6_pinfo *np = tcp_inet6_sk(sk); 1321 + struct inet_request_sock *ireq; 1324 1322 struct ipv6_txoptions *opt; 1325 1323 struct inet_sock *newinet; 1326 1324 bool found_dup_sk = false; 1325 + struct ipv6_pinfo *newnp; 1327 1326 struct tcp_sock *newtp; 1328 1327 struct sock *newsk; 1329 1328 #ifdef CONFIG_TCP_MD5SIG ··· 1392 1391 if (sk_acceptq_is_full(sk)) 1393 1392 goto exit_overflow; 1394 1393 1395 - if (!dst) { 1396 - dst = inet6_csk_route_req(sk, &fl6, req, IPPROTO_TCP); 1397 - if (!dst) 1398 - goto exit; 1399 - } 1394 + dst = inet6_csk_route_req(sk, dst, &fl6, req, IPPROTO_TCP); 1395 + if (!dst) 1396 + goto exit; 1400 1397 1401 1398 newsk = tcp_create_openreq_child(sk, req, skb); 1402 1399 if (!newsk) ··· 1410 1411 inet6_sk_rx_dst_set(newsk, skb); 1411 1412 1412 1413 newinet = inet_sk(newsk); 1414 + newinet->cork.fl.u.ip6 = fl6; 1413 1415 newinet->pinet6 = tcp_inet6_sk(newsk); 1414 1416 newinet->ipv6_fl_list = NULL; 1415 1417 newinet->inet_opt = NULL;