On 07/30/16 00:17, Alexander Bluhm wrote: > Hi, > > Spliced TCP sockets become faster if we put the output part into > its own task thread. This is inspired by userland copy where we > also have to go through the scheduler. This gives the socket buffer > a chance to be filled up and tcp_output() is called less often and > with bigger chunks.
This is really interesting. Do you have a clear understanding why is it getting faster? I'm worried about introducing a hack just for socket splicing and would prefer a more generic solution if possible. > > ok? > > bluhm > > Index: kern/uipc_socket.c > =================================================================== > RCS file: /data/mirror/openbsd/cvs/src/sys/kern/uipc_socket.c,v > retrieving revision 1.152 > diff -u -p -r1.152 uipc_socket.c > --- kern/uipc_socket.c 13 Jun 2016 21:24:43 -0000 1.152 > +++ kern/uipc_socket.c 29 Jul 2016 15:44:33 -0000 > @@ -59,6 +59,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 *); > int somove(struct socket *, int); > > void filt_sordetach(struct knote *kn); > @@ -85,6 +86,7 @@ int sominconn = SOMINCONN; > struct pool socket_pool; > #ifdef SOCKET_SPLICE > struct pool sosplice_pool; > +struct taskq *sosplice_taskq; > #endif > > void > @@ -1041,6 +1043,7 @@ sorflush(struct socket *so) > #define so_splicemax so_sp->ssp_max > #define so_idletv so_sp->ssp_idletv > #define so_idleto so_sp->ssp_idleto > +#define so_splicetask so_sp->ssp_task > > int > sosplice(struct socket *so, int fd, off_t max, struct timeval *tv) > @@ -1049,6 +1052,10 @@ sosplice(struct socket *so, int fd, off_ > struct socket *sosp; > int s, error = 0; > > + if (sosplice_taskq == NULL) > + sosplice_taskq = taskq_create("sosplice", 1, IPL_SOFTNET, > + TASKQ_CANTSLEEP); > + > if ((so->so_proto->pr_flags & PR_SPLICE) == 0) > return (EPROTONOSUPPORT); > if (so->so_options & SO_ACCEPTCONN) > @@ -1126,6 +1133,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); > > /* > * To prevent softnet interrupt from calling somove() while > @@ -1149,6 +1157,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; > @@ -1171,6 +1180,27 @@ soidle(void *arg) > splx(s); > } > > +void > +sotask(void *arg) > +{ > + struct socket *so = arg; > + 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 > @@ -1444,19 +1474,26 @@ somove(struct socket *so, int wait) > return (1); > } > > -#undef so_splicelen > -#undef so_splicemax > -#undef so_idletv > -#undef so_idleto > - > #endif /* SOCKET_SPLICE */ > > 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 (isspliced(so)) > return; > #endif > @@ -1470,7 +1507,7 @@ sowwakeup(struct socket *so) > { > #ifdef SOCKET_SPLICE > if (so->so_snd.sb_flagsintr & SB_SPLICE) > - (void) somove(so->so_sp->ssp_soback, M_DONTWAIT); > + task_add(sosplice_taskq, &so->so_sp->ssp_soback->so_splicetask); > #endif > sowakeup(so, &so->so_snd); > } > Index: sys/socketvar.h > =================================================================== > RCS file: /data/mirror/openbsd/cvs/src/sys/sys/socketvar.h,v > retrieving revision 1.61 > diff -u -p -r1.61 socketvar.h > --- sys/socketvar.h 28 Jun 2016 14:47:00 -0000 1.61 > +++ sys/socketvar.h 29 Jul 2016 15:44:33 -0000 > @@ -34,6 +34,7 @@ > > #include <sys/selinfo.h> /* for struct selinfo */ > #include <sys/queue.h> > +#include <sys/task.h> > #include <sys/timeout.h> > #include <sys/rwlock.h> > > @@ -92,6 +93,7 @@ struct socket { > off_t ssp_max; /* maximum number of bytes */ > struct timeval ssp_idletv; /* idle timeout */ > struct timeout ssp_idleto; > + struct task ssp_task; /* task for somove */ > } *so_sp; > /* > * Variables for socket buffering. >