Hi,

Some performance measurements showed that socket splicing for TCP
can be made faster.  The main slowdown was that tcp_output() got
called for every incomming packet.  When copying through user-land
this cannot happen as the scheduler gets involved.

So my idea is to do the socket splicing for TCP in a special kernel
thread.  One drawback might be that the struct socket gets larger.
On amd64 that is from 472 to 520 bytes.  I could try to put the
splicing fields into a seperate struct that gets only allocated
when needed.

Does someone want to do some performance measurements with relayd?

ok?

bluhm

Index: sys/kern/uipc_socket.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/kern/uipc_socket.c,v
retrieving revision 1.133
diff -u -p -u -p -r1.133 uipc_socket.c
--- sys/kern/uipc_socket.c      9 Sep 2014 02:07:17 -0000       1.133
+++ sys/kern/uipc_socket.c      30 Oct 2014 18:56:28 -0000
@@ -56,6 +56,7 @@ void  sbsync(struct sockbuf *, struct mbu
 int    sosplice(struct socket *, int, off_t, struct timeval *);
 void   sounsplice(struct socket *, struct socket *, int);
 void   soidle(void *);
+void   sotask(void *, void *);
 int    somove(struct socket *, int);
 
 void   filt_sordetach(struct knote *kn);
@@ -80,12 +81,18 @@ int somaxconn = SOMAXCONN;
 int    sominconn = SOMINCONN;
 
 struct pool socket_pool;
+#ifdef SOCKET_SPLICE
+struct taskq *sosplice_taskq;
+#endif
 
 void
 soinit(void)
 {
 
        pool_init(&socket_pool, sizeof(struct socket), 0, 0, 0, "sockpl", NULL);
+#ifdef SOCKET_SPLICE
+       sosplice_taskq = taskq_create("sosplice", 1, IPL_SOFTNET);
+#endif
 }
 
 /*
@@ -1101,6 +1108,7 @@ sosplice(struct socket *so, int fd, off_
        else
                timerclear(&so->so_idletv);
        timeout_set(&so->so_idleto, soidle, so);
+       task_set(&so->so_splicetask, sotask, so, NULL);
 
        /*
         * To prevent softnet interrupt from calling somove() while
@@ -1124,6 +1132,7 @@ sounsplice(struct socket *so, struct soc
 {
        splsoftassert(IPL_SOFTNET);
 
+       task_del(sosplice_taskq, &so->so_splicetask);
        timeout_del(&so->so_idleto);
        sosp->so_snd.sb_flagsintr &= ~SB_SPLICE;
        so->so_rcv.sb_flagsintr &= ~SB_SPLICE;
@@ -1139,13 +1148,34 @@ soidle(void *arg)
        int s;
 
        s = splsoftnet();
-       if (so->so_splice) {
+       if (so->so_rcv.sb_flagsintr & SB_SPLICE) {
                so->so_error = ETIMEDOUT;
                sounsplice(so, so->so_splice, 1);
        }
        splx(s);
 }
 
+void
+sotask(void *arg1, void *arg2)
+{
+       struct socket *so = arg1;
+       int s;
+
+       s = splsoftnet();
+       if (so->so_rcv.sb_flagsintr & SB_SPLICE) {
+               /*
+                * We may not sleep here as sofree() and unsplice() may be
+                * called from softnet interrupt context.  This would remove
+                * the socket during somove().
+                */
+               somove(so, M_DONTWAIT);
+       }
+       splx(s);
+
+       /* Avoid user land starvation. */
+       yield();
+}
+
 /*
  * Move data from receive buffer of spliced source socket to send
  * buffer of drain socket.  Try to move as much as possible in one
@@ -1414,8 +1444,20 @@ void
 sorwakeup(struct socket *so)
 {
 #ifdef SOCKET_SPLICE
-       if (so->so_rcv.sb_flagsintr & SB_SPLICE)
-               (void) somove(so, M_DONTWAIT);
+       if (so->so_rcv.sb_flagsintr & SB_SPLICE) {
+               /*
+                * TCP has a sendbuffer that can handle multiple packets
+                * at once.  So queue the stream a bit to accumulate data.
+                * The sosplice thread will call somove() later and send
+                * the packets calling tcp_output() only once.
+                * In the UDP case, send out the packets immediately.
+                * Using a thread would make things slower.
+                */
+               if (so->so_proto->pr_flags & PR_WANTRCVD)
+                       task_add(sosplice_taskq, &so->so_splicetask);
+               else
+                       somove(so, M_DONTWAIT);
+       }
        if (so->so_splice)
                return;
 #endif
@@ -1429,7 +1471,7 @@ sowwakeup(struct socket *so)
 {
 #ifdef SOCKET_SPLICE
        if (so->so_snd.sb_flagsintr & SB_SPLICE)
-               (void) somove(so->so_spliceback, M_DONTWAIT);
+               task_add(sosplice_taskq, &so->so_spliceback->so_splicetask);
 #endif
        sowakeup(so, &so->so_snd);
 }
Index: sys/sys/socketvar.h
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/sys/socketvar.h,v
retrieving revision 1.56
diff -u -p -u -p -r1.56 socketvar.h
--- sys/sys/socketvar.h 9 Sep 2014 02:07:17 -0000       1.56
+++ sys/sys/socketvar.h 30 Oct 2014 18:32:16 -0000
@@ -34,6 +34,7 @@
 
 #include <sys/selinfo.h>                       /* for struct selinfo */
 #include <sys/queue.h>
+#include <sys/task.h>
 #include <sys/timeout.h>
 
 #ifndef        _SOCKLEN_T_DEFINED_
@@ -88,6 +89,7 @@ struct socket {
        off_t   so_splicemax;           /* maximum number of bytes to splice */
        struct  timeval so_idletv;      /* idle timeout */
        struct  timeout so_idleto;
+       struct  task so_splicetask;     /* task for somove */
 /*
  * Variables for socket buffering.
  */

Reply via email to