On Thu, Oct 30, 2014 at 09:55:35PM -0400, Ted Unangst wrote: > On Thu, Oct 30, 2014 at 22:10, Alexander Bluhm wrote: > > + > > + /* Avoid user land starvation. */ > > + yield(); > > I think this is the responsibility of the taskq thread, not the > individual task.
I am not sure about this. Without an explicit yield() I see user land starvation, but I only used a virtual test machine. Note that the net_livelocks detection does not work as softtimer may interrupt the kernel thread. Originally I have put the yield() there as copy through user land always calls the scheduler. I have removed it for now from this diff. It has no impact on throughput. I have merged the diff with sosplice pool. Now the struct socket size remains at 392 bytes, but struct sosplice grows from 88 to 136 bytes. To save memory the thread is only created on demand when the machine uses splicing for the first time. ok? bluhm Index: kern/uipc_socket.c =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/kern/uipc_socket.c,v retrieving revision 1.134 diff -u -p -r1.134 uipc_socket.c --- kern/uipc_socket.c 3 Nov 2014 17:20:46 -0000 1.134 +++ kern/uipc_socket.c 3 Nov 2014 23:03:32 -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); @@ -82,6 +83,7 @@ int sominconn = SOMINCONN; struct pool socket_pool; #ifdef SOCKET_SPLICE struct pool sosplice_pool; +struct taskq *sosplice_taskq; #endif void @@ -1038,6 +1040,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) @@ -1046,6 +1049,9 @@ 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); + if ((so->so_proto->pr_flags & PR_SPLICE) == 0) return (EPROTONOSUPPORT); if (so->so_options & SO_ACCEPTCONN) @@ -1123,6 +1129,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 @@ -1146,6 +1153,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; @@ -1168,6 +1176,24 @@ soidle(void *arg) 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); +} + /* * Move data from receive buffer of spliced source socket to send * buffer of drain socket. Try to move as much as possible in one @@ -1435,6 +1461,7 @@ somove(struct socket *so, int wait) #undef so_splicemax #undef so_idletv #undef so_idleto +#undef so_splicetask #endif /* SOCKET_SPLICE */ @@ -1442,8 +1469,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_sp->ssp_task); + else + somove(so, M_DONTWAIT); + } if (isspliced(so)) return; #endif @@ -1457,7 +1496,8 @@ 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_sp->ssp_task); #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.57 diff -u -p -r1.57 socketvar.h --- sys/socketvar.h 3 Nov 2014 17:20:46 -0000 1.57 +++ sys/socketvar.h 3 Nov 2014 21:39:39 -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_ @@ -91,6 +92,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.