af_unix: Try to run GC async.
commit d9f21b3613337b55cc9d4a6ead484dca68475143 upstream. If more than 16000 inflight AF_UNIX sockets exist and the garbage collector is not running, unix_(dgram|stream)_sendmsg() call unix_gc(). Also, they wait for unix_gc() to complete. In unix_gc(), all inflight AF_UNIX sockets are traversed at least once, and more if they are the GC candidate. Thus, sendmsg() significantly slows down with too many inflight AF_UNIX sockets. However, if a process sends data with no AF_UNIX FD, the sendmsg() call does not need to wait for GC. After this change, only the process that meets the condition below will be blocked under such a situation. 1) cmsg contains AF_UNIX socket 2) more than 32 AF_UNIX sent by the same user are still inflight Note that even a sendmsg() call that does not meet the condition but has AF_UNIX FD will be blocked later in unix_scm_to_skb() by the spinlock, but we allow that as a bonus for sane users. The results below are the time spent in unix_dgram_sendmsg() sending 1 byte of data with no FD 4096 times on a host where 32K inflight AF_UNIX sockets exist. Without series: the sane sendmsg() needs to wait gc unreasonably. $ sudo /usr/share/bcc/tools/funclatency -p 11165 unix_dgram_sendmsg Tracing 1 functions for "unix_dgram_sendmsg"... Hit Ctrl-C to end. ^C nsecs : count distribution [...] 524288 -> 1048575 : 0 | | 1048576 -> 2097151 : 3881 |****************************************| 2097152 -> 4194303 : 214 |** | 4194304 -> 8388607 : 1 | | avg = 1825567 nsecs, total: 7477526027 nsecs, count: 4096 With series: the sane sendmsg() can finish much faster. $ sudo /usr/share/bcc/tools/funclatency -p 8702 unix_dgram_sendmsg Tracing 1 functions for "unix_dgram_sendmsg"... Hit Ctrl-C to end. ^C nsecs : count distribution [...] 128 -> 255 : 0 | | 256 -> 511 : 4092 |****************************************| 512 -> 1023 : 2 | | 1024 -> 2047 : 0 | | 2048 -> 4095 : 0 | | 4096 -> 8191 : 1 | | 8192 -> 16383 : 1 | | avg = 410 nsecs, total: 1680510 nsecs, count: 4096 Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com> Link: https://lore.kernel.org/r/20240123170856.41348-6-kuniyu@amazon.com Signed-off-by: Jakub Kicinski <kuba@kernel.org> Signed-off-by: Lee Jones <lee@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
328840c93b
commit
acc97866c1
@@ -8,13 +8,21 @@
|
|||||||
#include <linux/refcount.h>
|
#include <linux/refcount.h>
|
||||||
#include <net/sock.h>
|
#include <net/sock.h>
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_UNIX)
|
||||||
|
struct unix_sock *unix_get_socket(struct file *filp);
|
||||||
|
#else
|
||||||
|
static inline struct unix_sock *unix_get_socket(struct file *filp)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void unix_inflight(struct user_struct *user, struct file *fp);
|
void unix_inflight(struct user_struct *user, struct file *fp);
|
||||||
void unix_notinflight(struct user_struct *user, struct file *fp);
|
void unix_notinflight(struct user_struct *user, struct file *fp);
|
||||||
void unix_destruct_scm(struct sk_buff *skb);
|
void unix_destruct_scm(struct sk_buff *skb);
|
||||||
void io_uring_destruct_scm(struct sk_buff *skb);
|
void io_uring_destruct_scm(struct sk_buff *skb);
|
||||||
void unix_gc(void);
|
void unix_gc(void);
|
||||||
void wait_for_unix_gc(void);
|
void wait_for_unix_gc(struct scm_fp_list *fpl);
|
||||||
struct unix_sock *unix_get_socket(struct file *filp);
|
|
||||||
struct sock *unix_peer_get(struct sock *sk);
|
struct sock *unix_peer_get(struct sock *sk);
|
||||||
|
|
||||||
#define UNIX_HASH_MOD (256 - 1)
|
#define UNIX_HASH_MOD (256 - 1)
|
||||||
|
@@ -24,6 +24,7 @@ struct scm_creds {
|
|||||||
|
|
||||||
struct scm_fp_list {
|
struct scm_fp_list {
|
||||||
short count;
|
short count;
|
||||||
|
short count_unix;
|
||||||
short max;
|
short max;
|
||||||
struct user_struct *user;
|
struct user_struct *user;
|
||||||
struct file *fp[SCM_MAX_FD];
|
struct file *fp[SCM_MAX_FD];
|
||||||
|
@@ -36,6 +36,7 @@
|
|||||||
#include <net/compat.h>
|
#include <net/compat.h>
|
||||||
#include <net/scm.h>
|
#include <net/scm.h>
|
||||||
#include <net/cls_cgroup.h>
|
#include <net/cls_cgroup.h>
|
||||||
|
#include <net/af_unix.h>
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -85,6 +86,7 @@ static int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp)
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
*fplp = fpl;
|
*fplp = fpl;
|
||||||
fpl->count = 0;
|
fpl->count = 0;
|
||||||
|
fpl->count_unix = 0;
|
||||||
fpl->max = SCM_MAX_FD;
|
fpl->max = SCM_MAX_FD;
|
||||||
fpl->user = NULL;
|
fpl->user = NULL;
|
||||||
}
|
}
|
||||||
@@ -109,6 +111,9 @@ static int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp)
|
|||||||
fput(file);
|
fput(file);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
if (unix_get_socket(file))
|
||||||
|
fpl->count_unix++;
|
||||||
|
|
||||||
*fpp++ = file;
|
*fpp++ = file;
|
||||||
fpl->count++;
|
fpl->count++;
|
||||||
}
|
}
|
||||||
|
@@ -1885,11 +1885,12 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
|
|||||||
long timeo;
|
long timeo;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
wait_for_unix_gc();
|
|
||||||
err = scm_send(sock, msg, &scm, false);
|
err = scm_send(sock, msg, &scm, false);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
wait_for_unix_gc(scm.fp);
|
||||||
|
|
||||||
err = -EOPNOTSUPP;
|
err = -EOPNOTSUPP;
|
||||||
if (msg->msg_flags&MSG_OOB)
|
if (msg->msg_flags&MSG_OOB)
|
||||||
goto out;
|
goto out;
|
||||||
@@ -2157,11 +2158,12 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg,
|
|||||||
bool fds_sent = false;
|
bool fds_sent = false;
|
||||||
int data_len;
|
int data_len;
|
||||||
|
|
||||||
wait_for_unix_gc();
|
|
||||||
err = scm_send(sock, msg, &scm, false);
|
err = scm_send(sock, msg, &scm, false);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
wait_for_unix_gc(scm.fp);
|
||||||
|
|
||||||
err = -EOPNOTSUPP;
|
err = -EOPNOTSUPP;
|
||||||
if (msg->msg_flags & MSG_OOB) {
|
if (msg->msg_flags & MSG_OOB) {
|
||||||
#if IS_ENABLED(CONFIG_AF_UNIX_OOB)
|
#if IS_ENABLED(CONFIG_AF_UNIX_OOB)
|
||||||
|
@@ -335,8 +335,9 @@ void unix_gc(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#define UNIX_INFLIGHT_TRIGGER_GC 16000
|
#define UNIX_INFLIGHT_TRIGGER_GC 16000
|
||||||
|
#define UNIX_INFLIGHT_SANE_USER (SCM_MAX_FD * 8)
|
||||||
|
|
||||||
void wait_for_unix_gc(void)
|
void wait_for_unix_gc(struct scm_fp_list *fpl)
|
||||||
{
|
{
|
||||||
/* If number of inflight sockets is insane,
|
/* If number of inflight sockets is insane,
|
||||||
* force a garbage collect right now.
|
* force a garbage collect right now.
|
||||||
@@ -348,6 +349,13 @@ void wait_for_unix_gc(void)
|
|||||||
!READ_ONCE(gc_in_progress))
|
!READ_ONCE(gc_in_progress))
|
||||||
unix_gc();
|
unix_gc();
|
||||||
|
|
||||||
|
/* Penalise users who want to send AF_UNIX sockets
|
||||||
|
* but whose sockets have not been received yet.
|
||||||
|
*/
|
||||||
|
if (!fpl || !fpl->count_unix ||
|
||||||
|
READ_ONCE(fpl->user->unix_inflight) < UNIX_INFLIGHT_SANE_USER)
|
||||||
|
return;
|
||||||
|
|
||||||
if (READ_ONCE(gc_in_progress))
|
if (READ_ONCE(gc_in_progress))
|
||||||
flush_work(&unix_gc_work);
|
flush_work(&unix_gc_work);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user