syzbot has reported 100+ possible deadlock splats involving NBD, typically following this pattern: lock_sock(sk) -> GFP_KERNEL memory allocation -> fs reclaim -> lock_sock(sk) at NBD Before calling sock_sendmsg() or sock_recvmsg(), NBD sets sk->sk_allocation to GFP_NOIO to prevent fs reclaim from being triggered during memory allocation for the backend socket. However, even after a socket is passed to NBD, it remains exposed to userspace and thus can exercise various slow paths under lock_sock(), where GFP_KERNEL is used directly instead of sk->sk_allocation, leading to the deadlock. Some of those paths do not currently have a reference to struct sock, and plumbing the sk pointer through the call chain just to fix the allocation flags would be extremely cumbersome. Even with that, lockdep would not be happy because such a path could be exercised before passing the socket to NBD, and then lockdep would learn that the path could trigger fs reclaim. Additionally, since the socket is exposed to userspace, we cannot change the lockdep key (even for sk->sk_lock.dep_map, due to lock_sock_fast()). We could spread memalloc_noio_{save,restore} over the networking code, but we want to avoid that and solve it in the NBD layer, which requires the trylock variant of lock_sock(). Let's introduce lock_sock_try() for that purpose. Signed-off-by: Kuniyuki Iwashima --- include/net/sock.h | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/include/net/sock.h b/include/net/sock.h index 6c9a83016e95..69e4b8d17afb 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1710,6 +1710,37 @@ static inline void lock_sock(struct sock *sk) } void __lock_sock(struct sock *sk); + +/** + * lock_sock_try - trylock version of lock_sock + * @sk: socket + * + * Use of this function is strongly discouraged. + * + * It is primarily intended for NBD, where the driver must avoid + * deadlock during fs reclaim caused by the backend socket remaining + * exposed to userspace even after being handed over to NBD, + * which _is_ bad but too late to change. + * + * Return: true if the lock was acquired, false otherwise. + */ +static inline bool lock_sock_try(struct sock *sk) +{ + if (!spin_trylock_bh(&sk->sk_lock.slock)) + return false; + + if (sk->sk_lock.owned) { + spin_unlock_bh(&sk->sk_lock.slock); + return false; + } + + sk->sk_lock.owned = 1; + spin_unlock_bh(&sk->sk_lock.slock); + + mutex_acquire(&sk->sk_lock.dep_map, 0, 1, _RET_IP_); + return true; +} + void __release_sock(struct sock *sk); void release_sock(struct sock *sk); -- 2.53.0.1018.g2bb0e51243-goog