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