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.

tcp: Free TCP-AO/TCP-MD5 info/keys without RCU

Now that the destruction of info/keys is delayed until the socket
destructor, it's safe to use kfree() without an RCU callback.
The socket is in TCP_CLOSE state either because it never left it,
or it's already closed and the refcounter is zero. In any way,
no one can discover it anymore, it's safe to release memory
straight away.

Similar thing was possible for twsk already.

Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
Signed-off-by: Dmitry Safonov <dima@arista.com>
Link: https://patch.msgid.link/20250909-b4-tcp-ao-md5-rst-finwait2-v5-2-9ffaaaf8b236@arista.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Dmitry Safonov and committed by
Jakub Kicinski
51e547e8 9e472d9e

+12 -34
-1
include/net/tcp_ao.h
··· 130 130 u32 snd_sne; 131 131 u32 rcv_sne; 132 132 refcount_t refcnt; /* Protects twsk destruction */ 133 - struct rcu_head rcu; 134 133 }; 135 134 136 135 #ifdef CONFIG_TCP_MD5SIG
+3 -14
net/ipv4/tcp.c
··· 413 413 } 414 414 415 415 #ifdef CONFIG_TCP_MD5SIG 416 - static void tcp_md5sig_info_free_rcu(struct rcu_head *head) 417 - { 418 - struct tcp_md5sig_info *md5sig; 419 - 420 - md5sig = container_of(head, struct tcp_md5sig_info, rcu); 421 - kfree(md5sig); 422 - static_branch_slow_dec_deferred(&tcp_md5_needed); 423 - tcp_md5_release_sigpool(); 424 - } 425 - 426 416 void tcp_md5_destruct_sock(struct sock *sk) 427 417 { 428 418 struct tcp_sock *tp = tcp_sk(sk); 429 419 430 420 if (tp->md5sig_info) { 431 - struct tcp_md5sig_info *md5sig; 432 421 433 - md5sig = rcu_dereference_protected(tp->md5sig_info, 1); 434 422 tcp_clear_md5_list(sk); 435 - rcu_assign_pointer(tp->md5sig_info, NULL); 436 - call_rcu(&md5sig->rcu, tcp_md5sig_info_free_rcu); 423 + kfree(rcu_replace_pointer(tp->md5sig_info, NULL, 1)); 424 + static_branch_slow_dec_deferred(&tcp_md5_needed); 425 + tcp_md5_release_sigpool(); 437 426 } 438 427 } 439 428 EXPORT_IPV6_MOD_GPL(tcp_md5_destruct_sock);
+2 -3
net/ipv4/tcp_ao.c
··· 268 268 kfree_sensitive(key); 269 269 } 270 270 271 - static void tcp_ao_info_free_rcu(struct rcu_head *head) 271 + static void tcp_ao_info_free(struct tcp_ao_info *ao) 272 272 { 273 - struct tcp_ao_info *ao = container_of(head, struct tcp_ao_info, rcu); 274 273 struct tcp_ao_key *key; 275 274 struct hlist_node *n; 276 275 ··· 309 310 310 311 if (!twsk) 311 312 tcp_ao_sk_omem_free(sk, ao); 312 - call_rcu(&ao->rcu, tcp_ao_info_free_rcu); 313 + tcp_ao_info_free(ao); 313 314 } 314 315 315 316 void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp)
+2 -2
net/ipv4/tcp_ipv4.c
··· 1503 1503 md5sig = rcu_dereference_protected(tp->md5sig_info, 1); 1504 1504 1505 1505 hlist_for_each_entry_safe(key, n, &md5sig->head, node) { 1506 - hlist_del_rcu(&key->node); 1506 + hlist_del(&key->node); 1507 1507 atomic_sub(sizeof(*key), &sk->sk_omem_alloc); 1508 - kfree_rcu(key, rcu); 1508 + kfree(key); 1509 1509 } 1510 1510 } 1511 1511
+5 -14
net/ipv4/tcp_minisocks.c
··· 377 377 } 378 378 EXPORT_SYMBOL(tcp_time_wait); 379 379 380 - #ifdef CONFIG_TCP_MD5SIG 381 - static void tcp_md5_twsk_free_rcu(struct rcu_head *head) 382 - { 383 - struct tcp_md5sig_key *key; 384 - 385 - key = container_of(head, struct tcp_md5sig_key, rcu); 386 - kfree(key); 387 - static_branch_slow_dec_deferred(&tcp_md5_needed); 388 - tcp_md5_release_sigpool(); 389 - } 390 - #endif 391 - 392 380 void tcp_twsk_destructor(struct sock *sk) 393 381 { 394 382 #ifdef CONFIG_TCP_MD5SIG 395 383 if (static_branch_unlikely(&tcp_md5_needed.key)) { 396 384 struct tcp_timewait_sock *twsk = tcp_twsk(sk); 397 385 398 - if (twsk->tw_md5_key) 399 - call_rcu(&twsk->tw_md5_key->rcu, tcp_md5_twsk_free_rcu); 386 + if (twsk->tw_md5_key) { 387 + kfree(twsk->tw_md5_key); 388 + static_branch_slow_dec_deferred(&tcp_md5_needed); 389 + tcp_md5_release_sigpool(); 390 + } 400 391 } 401 392 #endif 402 393 tcp_ao_destroy_sock(sk, true);