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