net: lockless sock_i_ino()
Subsystems (4)
PATCH 1/1 net: lockless sock_i_ino()
Followup of commit c51da3f7a161 ("net: remove sock_i_uid()")
A recent syzbot report was the trigger for this change.
Over the years, we had many problems caused by the read_lock[_bh](&sk->sk_callback_lock) in sock_i_uid().
We could fix smc_diag_dump_proto() or make a more radical move:
Instead of waiting for new syzbot reports, cache the socket inode number in sk->sk_ino, so that we no longer need to acquire sk->sk_callback_lock in sock_i_ino().
This makes socket dumps faster (one less cache line miss, and two atomic ops avoided).
Prior art:
commit 25a9c8a4431c ("netlink: Add __sock_i_ino() for __netlink_diag_dump().") commit 4f9bf2a2f5aa ("tcp: Don't acquire inet_listen_hashbucket::lock with disabled BH.") commit efc3dbc37412 ("rds: Make rds_sock_lock BH rather than IRQ safe.")
--- include/net/sock.h | 17 +++++++++++++---- net/core/sock.c | 22 ---------------------- net/mptcp/protocol.c | 1 - net/netlink/diag.c | 2 +- 4 files changed, 14 insertions(+), 28 deletions(-)
include/net/sock.h +13 -4
@@ -285,6 +285,7 @@ struct sk_filter;* @sk_ack_backlog: current listen backlog * @sk_max_ack_backlog: listen backlog set in listen() * @sk_uid: user id of owner +* @sk_ino: inode number (zero if orphaned) * @sk_prefer_busy_poll: prefer busypolling over softirq processing * @sk_busy_poll_budget: napi processing budget when busypolling * @sk_priority: %SO_PRIORITY setting
@@ -518,6 +519,7 @@ struct sock {u32 sk_ack_backlog;u32 sk_max_ack_backlog;kuid_t sk_uid; +unsigned long sk_ino;spinlock_t sk_peer_lock;int sk_bind_phc;struct pid *sk_peer_pid;
@@ -2056,6 +2058,10 @@ static inline int sk_rx_queue_get(const struct sock *sk)static inline void sk_set_socket (struct sock *sk,struct socket *sock) { sk->sk_socket = sock; +if (sock) { +WRITE_ONCE (sk->sk_uid,SOCK_INODE (sock)->i_uid); +WRITE_ONCE (sk->sk_ino,SOCK_INODE (sock)->i_ino); + } }static inline wait_queue_head_t *sk_sleep (struct sock *sk)
@@ -2077,6 +2083,7 @@ static inline void sock_orphan(struct sock *sk)sk_set_socket (sk,NULL ); sk->sk_wq =NULL ;/* Note: sk_uid is unchanged. */ +WRITE_ONCE (sk->sk_ino,0 );write_unlock_bh (&sk->sk_callback_lock); }
@@ -2087,20 +2094,22 @@ static inline void sock_graft(struct sock *sk, struct socket *parent)rcu_assign_pointer (sk->sk_wq, &parent->wq); parent->sk = sk;sk_set_socket (sk, parent); -WRITE_ONCE (sk->sk_uid,SOCK_INODE (parent)->i_uid);security_sock_graft (sk, parent);write_unlock_bh (&sk->sk_callback_lock); } +static inline unsigned long sock_i_ino (const struct sock *sk) +{ +/* Paired with WRITE_ONCE() in sock_graft() and sock_orphan() */ +return READ_ONCE (sk->sk_ino); +} +static inline kuid_t sk_uid (const struct sock *sk) {/* Paired with WRITE_ONCE() in sockfs_setattr() */ return READ_ONCE (sk->sk_uid); } -unsigned long __sock_i_ino (struct sock *sk); -unsigned long sock_i_ino (struct sock *sk); -static inline kuid_t sock_net_uid (const struct net *net,const struct sock *sk) {return sk ?sk_uid (sk) :make_kuid (net->user_ns,0 );
net/core/sock.c +0 -22
@@ -2780,28 +2780,6 @@ void sock_pfree(struct sk_buff *skb)EXPORT_SYMBOL (sock_pfree);#endif /* CONFIG_INET */ -unsigned long __sock_i_ino (struct sock *sk) -{ -unsigned long ino; - -read_lock (&sk->sk_callback_lock); - ino = sk->sk_socket ?SOCK_INODE (sk->sk_socket)->i_ino :0 ; -read_unlock (&sk->sk_callback_lock); -return ino; -} -EXPORT_SYMBOL (__sock_i_ino); - -unsigned long sock_i_ino (struct sock *sk) -{ -unsigned long ino; - -local_bh_disable (); - ino =__sock_i_ino (sk); -local_bh_enable (); -return ino; -} -EXPORT_SYMBOL (sock_i_ino); -/* * Allocate a skb from the socket's send buffer. */
net/mptcp/protocol.c +0 -1
@@ -3554,7 +3554,6 @@ void mptcp_sock_graft(struct sock *sk, struct socket *parent)write_lock_bh (&sk->sk_callback_lock);rcu_assign_pointer (sk->sk_wq, &parent->wq);sk_set_socket (sk, parent); -WRITE_ONCE (sk->sk_uid,SOCK_INODE (parent)->i_uid);write_unlock_bh (&sk->sk_callback_lock); }
net/netlink/diag.c +1 -1
@@ -168,7 +168,7 @@ static int __netlink_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, - __sock_i_ino(sk)) < 0) { + sock_i_ino(sk)) < 0) { ret =1 ;break ; }
Signature · 1 line
2.51.0.338.gd7d06c2dae-goog
Quoted 19 lines · Eric Dumazet
Followup of commit
c51da3f7a161("net: removesock_i_uid()")A recent syzbot report was the trigger for this change.
Over the years, we had many problems caused by the read_lock[_bh](&sk->sk_callback_lock) in
sock_i_uid().We could fix
smc_diag_dump_proto()or make a more radical move:Instead of waiting for new syzbot reports, cache the socket inode number in
sk->sk_ino, so that we no longer need to acquiresk->sk_callback_lockinsock_i_ino().This makes socket dumps faster (one less cache line miss, and two atomic ops avoided).
Prior art:
Quoted 8 lines
commit
25a9c8a4431c("netlink: Add__sock_i_ino()for__netlink_diag_dump().") commit4f9bf2a2f5aa("tcp: Don't acquireinet_listen_hashbucket::lockwith disabled BH.") commitefc3dbc37412("rds: Makerds_sock_lockBH rather than IRQ safe.")Fixes d2d6422f8bd1 ("x86: Allow to enable PREEMPT_RT.")Reported-by [email protected]Signed-off-by Eric Dumazet <[email protected]>
Thanks!
Quoted 22 lines · Eric Dumazet
Followup of commit
c51da3f7a161("net: removesock_i_uid()")A recent syzbot report was the trigger for this change.
Over the years, we had many problems caused by the read_lock[_bh](&sk->sk_callback_lock) in
sock_i_uid().We could fix
smc_diag_dump_proto()or make a more radical move:Instead of waiting for new syzbot reports, cache the socket inode number in
sk->sk_ino, so that we no longer need to acquiresk->sk_callback_lockinsock_i_ino().This makes socket dumps faster (one less cache line miss, and two atomic ops avoided).
Prior art:
commit
25a9c8a4431c("netlink: Add__sock_i_ino()for__netlink_diag_dump().") commit4f9bf2a2f5aa("tcp: Don't acquireinet_listen_hashbucket::lockwith disabled BH.") commitefc3dbc37412("rds: Makerds_sock_lockBH rather than IRQ safe.")
Quoted 6 lines
Fixes d2d6422f8bd1 ("x86: Allow to enable PREEMPT_RT.")Reported-by [email protected]Signed-off-by Eric Dumazet <[email protected]>Cc Sebastian Andrzej Siewior <[email protected]>Cc Kuniyuki Iwashima <[email protected]>
Sebastian
This patch was applied to netdev/net.git (main) by Jakub Kicinski <[email protected]>:
Hello:
This patch was applied to netdev/net.git (main)
by Jakub Kicinski <[email protected]>:
Quoted 8 lines · On Tue, 2 Sep 2025 18:36:03 +0000 you wrote:
Followup of commit
c51da3f7a161("net: removesock_i_uid()")A recent syzbot report was the trigger for this change.
Over the years, we had many problems caused by the read_lock[_bh](&sk->sk_callback_lock) in
sock_i_uid().[...]
Here is the summary with links:
- [net] net: lockless sock_i_ino()
https://git.kernel.org/netdev/net/c/5d6b58c932ec
You are awesome, thank you!
Signature · 2 lines
Deet-doot-dot, I am a bot. https://korg.docs.kernel.org/patchwork/pwbot.html
include/net/sock.h static inline int sk_rx_queue_get(const struct sock *sk) · line 2064
Quoted 8 lines · Eric Dumazet
include/net/sock.h +4
-0@@ -2056,6 +2058,10 @@ static inline int sk_rx_queue_get(const struct sock *sk)static inline void sk_set_socket (struct sock *sk,struct socket *sock) { sk->sk_socket = sock; +if (sock) { +WRITE_ONCE (sk->sk_uid,SOCK_INODE (sock)->i_uid); +WRITE_ONCE (sk->sk_ino,SOCK_INODE (sock)->i_ino); + }
Hi Eric.
This change breaks CRIU [1]. The issue is that socket_diag reports two sockets with the same inode number. It seems inet_csk_clone_lock copies sk->sk_ino to child sockets, but sk_set_socket doesn’t reset it to zero when sock is NULL.
[1] https://github.com/checkpoint-restore/criu/issues/2744
Thanks,
Andrei
Quoted 13 lines · Andrei Vagin
Review static inline int sk_rx_queue_get(const struct sock *sk) · line 2064
Quoted 8 lines · Eric Dumazet
(quoted diff) +4
-0@@ -2056,6 +2058,10 @@ static inline int sk_rx_queue_get(const struct sock *sk) static inline void sk_set_socket(struct sock *sk, struct socket *sock) { sk->sk_socket = sock; + if (sock) { + WRITE_ONCE(sk->sk_uid, SOCK_INODE(sock)->i_uid); + WRITE_ONCE(sk->sk_ino, SOCK_INODE(sock)->i_ino); + }Hi Eric.
Quoted 4 lines
This change breaks CRIU [1]. The issue is that
socket_diagreports two sockets with the same inode number. It seemsinet_csk_clone_lockcopiessk->sk_inoto child sockets, butsk_set_socketdoesn’t reset it to zero when sock is NULL.
Hi Andrei, thanks for the report.
Could you test this patch ?
include/net/sock.h +0 -0
100644
include/net/sock.h +0 -0
@@ -2063,6 +2063,9 @@ static inline void sk_set_socket(struct sock
*sk, struct socket *sock)
if (sock) {
WRITE_ONCE(sk->sk_uid, SOCK_INODE(sock)->i_uid);
WRITE_ONCE(sk->sk_ino, SOCK_INODE(sock)->i_ino);
+ } else {
+ /* Note: sk_uid is unchanged. */
+ WRITE_ONCE(sk->sk_ino, 0);
}}
@@ -2084,8 +2087,6 @@ static inline void sock_orphan(struct sock *sk)
sock_set_flag(sk, SOCK_DEAD);
sk_set_socket(sk, NULL);
sk->sk_wq = NULL;
- /* Note: sk_uid is unchanged. */
- WRITE_ONCE(sk->sk_ino, 0);
write_unlock_bh(&sk->sk_callback_lock);}
Quoted 30 lines · Eric Dumazet
Quoted 13 lines · Andrei Vagin
Review static inline int sk_rx_queue_get(const struct sock *sk) · line 2064
Quoted 8 lines · Eric Dumazet
(quoted diff) +4
-0@@ -2056,6 +2058,10 @@ static inline int sk_rx_queue_get(const struct sock *sk) static inline void sk_set_socket(struct sock *sk, struct socket *sock) { sk->sk_socket = sock; + if (sock) { + WRITE_ONCE(sk->sk_uid, SOCK_INODE(sock)->i_uid); + WRITE_ONCE(sk->sk_ino, SOCK_INODE(sock)->i_ino); + }Hi Eric.
Quoted 4 lines
This change breaks CRIU [1]. The issue is that
socket_diagreports two sockets with the same inode number. It seemsinet_csk_clone_lockcopiessk->sk_inoto child sockets, butsk_set_socketdoesn’t reset it to zero when sock is NULL.Hi Andrei, thanks for the report.
Could you test this patch ?
include/net/sock.h +0
-0100644
include/net/sock.h static inline void sk_set_socket(struct sock · line 2069
Quoted 7 lines
include/net/sock.h +3
-0@@ -2063,6 +2063,9 @@ static inline void sk_set_socket(struct sock *sk, struct socket *sock)if (sock) {WRITE_ONCE (sk->sk_uid,SOCK_INODE (sock)->i_uid);WRITE_ONCE (sk->sk_ino,SOCK_INODE (sock)->i_ino); + }else { +/* Note: sk_uid is unchanged. */ +WRITE_ONCE (sk->sk_ino,0 );
It fixes the issue. Thanks!