]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
af_unix: Try to run GC async.
authorKuniyuki Iwashima <kuniyu@amazon.com>
Tue, 23 Jan 2024 17:08:56 +0000 (09:08 -0800)
committerJakub Kicinski <kuba@kernel.org>
Sat, 27 Jan 2024 04:34:25 +0000 (20:34 -0800)
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>
include/net/af_unix.h
include/net/scm.h
net/core/scm.c
net/unix/af_unix.c
net/unix/garbage.c

index 2c98ef95017b2a8396c35d215c0e1006cee0ecb2..f045bbd9017d137cfe8ef27027079f8bf93cd62e 100644 (file)
@@ -8,13 +8,21 @@
 #include <linux/refcount.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_notinflight(struct user_struct *user, struct file *fp);
 void unix_destruct_scm(struct sk_buff *skb);
 void io_uring_destruct_scm(struct sk_buff *skb);
 void unix_gc(void);
-void wait_for_unix_gc(void);
-struct unix_sock *unix_get_socket(struct file *filp);
+void wait_for_unix_gc(struct scm_fp_list *fpl);
 struct sock *unix_peer_get(struct sock *sk);
 
 #define UNIX_HASH_MOD  (256 - 1)
index cf68acec4d70a5f8c0a021a0635e6d3436e93a1e..92276a2c554368f78b0b21f98294500b911b3a27 100644 (file)
@@ -25,6 +25,7 @@ struct scm_creds {
 
 struct scm_fp_list {
        short                   count;
+       short                   count_unix;
        short                   max;
        struct user_struct      *user;
        struct file             *fp[SCM_MAX_FD];
index d0e0852a24d5f05d41c2ad0724a5178a4b8489e1..9cd4b0a01cd603914daf894b29d5a3dd1f925757 100644 (file)
@@ -36,6 +36,7 @@
 #include <net/compat.h>
 #include <net/scm.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;
                *fplp = fpl;
                fpl->count = 0;
+               fpl->count_unix = 0;
                fpl->max = SCM_MAX_FD;
                fpl->user = NULL;
        }
@@ -109,6 +111,9 @@ static int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp)
                        fput(file);
                        return -EINVAL;
                }
+               if (unix_get_socket(file))
+                       fpl->count_unix++;
+
                *fpp++ = file;
                fpl->count++;
        }
index 1e9378036dcc3614dafee8aa47cad4d402926983..1720419d93d68482b6fbfb148df211e6ad3c5706 100644 (file)
@@ -1923,11 +1923,12 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
        long timeo;
        int err;
 
-       wait_for_unix_gc();
        err = scm_send(sock, msg, &scm, false);
        if (err < 0)
                return err;
 
+       wait_for_unix_gc(scm.fp);
+
        err = -EOPNOTSUPP;
        if (msg->msg_flags&MSG_OOB)
                goto out;
@@ -2199,11 +2200,12 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg,
        bool fds_sent = false;
        int data_len;
 
-       wait_for_unix_gc();
        err = scm_send(sock, msg, &scm, false);
        if (err < 0)
                return err;
 
+       wait_for_unix_gc(scm.fp);
+
        err = -EOPNOTSUPP;
        if (msg->msg_flags & MSG_OOB) {
 #if IS_ENABLED(CONFIG_AF_UNIX_OOB)
index 3f599db59f9dc327a57b738624e7db034302961f..4046c606f0e6ecef3a5d6b42bec249f2693c2a43 100644 (file)
@@ -311,8 +311,9 @@ void unix_gc(void)
 }
 
 #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,
         * force a garbage collect right now.
@@ -324,6 +325,13 @@ void wait_for_unix_gc(void)
            !READ_ONCE(gc_in_progress))
                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))
                flush_work(&unix_gc_work);
 }