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. */