Skip to content

Commit 951e454

Browse files
q2vengregkh
authored andcommitted
af_unix: Try not to hold unix_gc_lock during accept().
commit fd86344823b521149bb31d91eba900ba3525efa6 upstream. Commit dcf70df2048d ("af_unix: Fix up unix_edge.successor for embryo socket.") added spin_lock(&unix_gc_lock) in accept() path, and it caused regression in a stress test as reported by kernel test robot. If the embryo socket is not part of the inflight graph, we need not hold the lock. To decide that in O(1) time and avoid the regression in the normal use case, 1. add a new stat unix_sk(sk)->scm_stat.nr_unix_fds 2. count the number of inflight AF_UNIX sockets in the receive queue under unix_state_lock() 3. move unix_update_edges() call under unix_state_lock() 4. avoid locking if nr_unix_fds is 0 in unix_update_edges() Reported-by: kernel test robot <[email protected]> Closes: https://lore.kernel.org/oe-lkp/[email protected] Signed-off-by: Kuniyuki Iwashima <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Paolo Abeni <[email protected]> Signed-off-by: Lee Jones <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 61a7536 commit 951e454

File tree

3 files changed

+18
-5
lines changed

3 files changed

+18
-5
lines changed

include/net/af_unix.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ struct unix_skb_parms {
6767

6868
struct scm_stat {
6969
atomic_t nr_fds;
70+
unsigned long nr_unix_fds;
7071
};
7172

7273
#define UNIXCB(skb) (*(struct unix_skb_parms *)&((skb)->cb))

net/unix/af_unix.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1677,12 +1677,12 @@ static int unix_accept(struct socket *sock, struct socket *newsock, int flags,
16771677
}
16781678

16791679
tsk = skb->sk;
1680-
unix_update_edges(unix_sk(tsk));
16811680
skb_free_datagram(sk, skb);
16821681
wake_up_interruptible(&unix_sk(sk)->peer_wait);
16831682

16841683
/* attach accepted sock to socket */
16851684
unix_state_lock(tsk);
1685+
unix_update_edges(unix_sk(tsk));
16861686
newsock->state = SS_CONNECTED;
16871687
unix_sock_inherit_flags(sock, newsock);
16881688
sock_graft(tsk, newsock);

net/unix/garbage.c

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ void unix_add_edges(struct scm_fp_list *fpl, struct unix_sock *receiver)
209209
unix_add_edge(fpl, edge);
210210
} while (i < fpl->count_unix);
211211

212+
receiver->scm_stat.nr_unix_fds += fpl->count_unix;
212213
WRITE_ONCE(unix_tot_inflight, unix_tot_inflight + fpl->count_unix);
213214
out:
214215
WRITE_ONCE(fpl->user->unix_inflight, fpl->user->unix_inflight + fpl->count);
@@ -222,6 +223,7 @@ void unix_add_edges(struct scm_fp_list *fpl, struct unix_sock *receiver)
222223

223224
void unix_del_edges(struct scm_fp_list *fpl)
224225
{
226+
struct unix_sock *receiver;
225227
int i = 0;
226228

227229
spin_lock(&unix_gc_lock);
@@ -235,6 +237,8 @@ void unix_del_edges(struct scm_fp_list *fpl)
235237
unix_del_edge(fpl, edge);
236238
} while (i < fpl->count_unix);
237239

240+
receiver = fpl->edges[0].successor;
241+
receiver->scm_stat.nr_unix_fds -= fpl->count_unix;
238242
WRITE_ONCE(unix_tot_inflight, unix_tot_inflight - fpl->count_unix);
239243
out:
240244
WRITE_ONCE(fpl->user->unix_inflight, fpl->user->unix_inflight - fpl->count);
@@ -246,10 +250,18 @@ void unix_del_edges(struct scm_fp_list *fpl)
246250

247251
void unix_update_edges(struct unix_sock *receiver)
248252
{
249-
spin_lock(&unix_gc_lock);
250-
unix_update_graph(unix_sk(receiver->listener)->vertex);
251-
receiver->listener = NULL;
252-
spin_unlock(&unix_gc_lock);
253+
/* nr_unix_fds is only updated under unix_state_lock().
254+
* If it's 0 here, the embryo socket is not part of the
255+
* inflight graph, and GC will not see it, so no lock needed.
256+
*/
257+
if (!receiver->scm_stat.nr_unix_fds) {
258+
receiver->listener = NULL;
259+
} else {
260+
spin_lock(&unix_gc_lock);
261+
unix_update_graph(unix_sk(receiver->listener)->vertex);
262+
receiver->listener = NULL;
263+
spin_unlock(&unix_gc_lock);
264+
}
253265
}
254266

255267
int unix_prepare_fpl(struct scm_fp_list *fpl)

0 commit comments

Comments
 (0)