> On 31 Oct 2014, at 07:10, Alexander Bluhm <alexander.bl...@gmx.net> wrote:
> 
> 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 without splicing, the payloads from multiple tcp packets (at least all of 
the ones in a single softnet run?) get bundled up into a buffer that userland 
reads and then writes out again in a single go. right?

you're suggesting the taskq as a way to defer output till after the current 
softnet call has processed all its packets and queued all the tcp packet 
payloads onto the socket?

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

its worth remembering there are other memory costs too. i think a kthread (the 
thing taskqs run on) is 5 pages amd64, so 20KB.

> 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