diff --git a/man/3proxy.cfg.5 b/man/3proxy.cfg.5 index 383a77f..f4b446b 100644 --- a/man/3proxy.cfg.5 +++ b/man/3proxy.cfg.5 @@ -229,6 +229,17 @@ the filters see all of the data, nor does one ending or beginning TLS, unless the kernel does TLS for it both ways (\fBssl_ktls\fR). Available in builds with WITHSPLICE, the default on Linux. .br +.B -b +\fISIZE\fR[\fB,\fIPIPESIZE\fR] (for TCP services) read at most \fISIZE\fR bytes at +once into a connection's buffer: less than the 16K it has, never more. +\fIPIPESIZE\fR (Linux, with \fB-s\fR) makes the pipes of splice() hold that many +bytes instead of 64K, from 4096 to 67108864, e.g. \fB-b,262144\fR: the +system rounds it up to a power of 2 pages, and gives an unprivileged process +no more than /proc/sys/fs/pipe-max-size (1M by default). A pipe that cannot +be made so large is not used, and the data is copied. Larger pipes move more +at once and take more kernel memory for each connection; 64K was as fast as +larger ones in our tests. +.br .B -C (for TCP services) a connection closed by any of the sides terminates the session, buffered data is delivered before the sockets are closed. By default diff --git a/src/mainconf.c b/src/mainconf.c index 390b95a..cb1407f 100644 --- a/src/mainconf.c +++ b/src/mainconf.c @@ -400,7 +400,16 @@ static int srvprepare(struct srvstart *s, int argc, char **argv){ srv->family = atoi(argv[i]+1); break; case 'b': + /* -b[SIZE][,PIPESIZE] */ srv->bufsize = atoi(argv[i]+2); +#ifdef WITHSPLICE + if(strchr(argv[i]+2, ',')){ + long n = atol(strchr(argv[i]+2, ',') + 1); + + if(n < 4096 || n > MAXPIPESIZE) error = 1; + else srv->pipesize = (unsigned)n; + } +#endif break; #ifdef STDMAIN #ifndef _WIN32 diff --git a/src/mainloop.c b/src/mainloop.c index 62ee40e..1f297f6 100644 --- a/src/mainloop.c +++ b/src/mainloop.c @@ -620,13 +620,13 @@ static int cbway(struct cbmap *m, struct cbway *w){ /* what was buffered goes first: the pipe is read once it is through */ - if(!inb && !w->wantbuf && w->left > w->inpipe && !m->term_r[w->src] && !end && w->inpipe < PIPESIZE && + if(!inb && !w->wantbuf && w->left > w->inpipe && !m->term_r[w->src] && !end && w->inpipe < SRVPIPESIZE(param->srv) && (rs->ready & (POLLIN | POLLHUP | POLLERR))){ - if(pipe[0] < 0 && pipenew(pipe) < 0){ + if(pipe[0] < 0 && pipenew(pipe, SRVPIPESIZE(param->srv)) < 0){ w->splice = 0; return 1; } - n = splice(from, NULL, pipe[1], NULL, (size_t)CBMIN((uint64_t)PIPESIZE - w->inpipe, + n = splice(from, NULL, pipe[1], NULL, (size_t)CBMIN((uint64_t)SRVPIPESIZE(param->srv) - w->inpipe, w->left - w->inpipe), SPLICE_F_NONBLOCK | SPLICE_F_MOVE); if(n > 0){ w->inpipe += n; diff --git a/src/proxy.h b/src/proxy.h index ed09130..04e2bee 100644 --- a/src/proxy.h +++ b/src/proxy.h @@ -614,11 +614,14 @@ extern struct commands commandhandlers[]; #define SPLICE_TLSPENDING 2 extern int (*splicetls)(struct clientparam *param, SOCKET s); /* what a pipe of splice() is given at most: 64K, what a pipe holds; - built with -DPIPESIZE=, pipes are made that large */ + built with -DPIPESIZE=, or for a service given -b,, pipes + are made that large, up to MAXPIPESIZE */ #ifndef PIPESIZE #define PIPESIZE 65536 #endif -int pipenew(int p[2]); +#define MAXPIPESIZE (64 * 1024 * 1024) +#define SRVPIPESIZE(srv) ((srv)->pipesize? (srv)->pipesize : (unsigned)PIPESIZE) +int pipenew(int p[2], unsigned size); #define mapsocket(a,b) ((a->srv->usesplice && !a->nosplice && !a->ndatfilterssrv && !a->ndatfilterscli)?sockmap(a,b,a->srv->usesplice):sockmap(a,b,0)) #else #define mapsocket(a,b) sockmap(a,b, 0) diff --git a/src/sockmap.c b/src/sockmap.c index 3c8e82e..88e3532 100644 --- a/src/sockmap.c +++ b/src/sockmap.c @@ -35,18 +35,16 @@ ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t le and EBADMSG on some kernels */ #define SPLICEREFUSED(e) ((e) == EINVAL || (e) == EIO || (e) == EBADMSG) -/* A pipe for splice(), nonblocking, of PIPESIZE: -1 if there is none to be - had, or not of that size. */ -int pipenew(int p[2]){ +/* A pipe for splice(), nonblocking, holding size: -1 if there is none to + be had, or not so large. A pipe holds 64K as it is made. */ +int pipenew(int p[2], unsigned size){ if(pipe2(p, O_NONBLOCK) < 0) return -1; -#if PIPESIZE != 65536 - if(fcntl(p[0], F_SETPIPE_SZ, PIPESIZE) < 0){ + if(size != 65536 && fcntl(p[0], F_SETPIPE_SZ, (int)size) < 0){ close(p[0]); close(p[1]); p[0] = p[1] = -1; return -1; } -#endif return 0; } @@ -140,6 +138,7 @@ int sockmap(struct clientparam * param, int timeo, int usesplice){ #ifdef WITHSPLICE uint64_t inclientpipe = 0, inserverpipe = 0; + unsigned pipesize = SRVPIPESIZE(param->srv); int TOCLIENTPIPE = 0, FROMCLIENTPIPE = 0, TOSERVERPIPE = 0, FROMSERVERPIPE = 0; /* The client's data goes through pipecli, the server's through pipesrv. They belong to the connection: made when first needed, kept for the @@ -361,7 +360,7 @@ int sockmap(struct clientparam * param, int timeo, int usesplice){ /* each direction goes through its pipe if spliced, through its buffer otherwise; what was in the buffer before is sent first */ if(splicecli && inclientpipe && !inclientbuf && FROMCLIENTPIPE && TOSERVER){ - res = splice(pipecli[0], NULL, param->remsock, NULL, MIN(PIPESIZE, inclientpipe), SPLICE_F_NONBLOCK|SPLICE_F_MOVE); + res = splice(pipecli[0], NULL, param->remsock, NULL, MIN(pipesize, inclientpipe), SPLICE_F_NONBLOCK|SPLICE_F_MOVE); /* from a pipe holding data: the socket is full */ if(res < 0 && errno == EAGAIN && fiberblockfunc) fiberblockfunc(param->remsock, POLLOUT); if(res >0) { @@ -388,7 +387,7 @@ int sockmap(struct clientparam * param, int timeo, int usesplice){ } } if(splicesrv && inserverpipe && !inserverbuf && FROMSERVERPIPE && TOCLIENT){ - res = splice(pipesrv[0], NULL, param->clisock, NULL, MIN(PIPESIZE, inserverpipe), SPLICE_F_NONBLOCK|SPLICE_F_MOVE); + res = splice(pipesrv[0], NULL, param->clisock, NULL, MIN(pipesize, inserverpipe), SPLICE_F_NONBLOCK|SPLICE_F_MOVE); /* from a pipe holding data: the socket is full */ if(res < 0 && errno == EAGAIN && fiberblockfunc) fiberblockfunc(param->clisock, POLLOUT); if(res > 0) { @@ -408,7 +407,7 @@ int sockmap(struct clientparam * param, int timeo, int usesplice){ } } if(splicecli && !wantbufcli && fromclient>inclientpipe && FROMCLIENT && TOCLIENTPIPE && !SESSIONEND){ - if(pipecli[0] < 0 && pipenew(pipecli) < 0){ + if(pipecli[0] < 0 && pipenew(pipecli, pipesize) < 0){ /* no pipe to be had: copy instead */ allowcli = splicecli = TOCLIENTPIPE = FROMCLIENTPIPE = 0; if(param->cliinbuf < CLIROOM) TOCLIENTBUF = 1; @@ -425,7 +424,7 @@ int sockmap(struct clientparam * param, int timeo, int usesplice){ continue; } errno = 0; - res = splice(param->clisock, NULL, pipecli[1], NULL, (int)MIN((uint64_t)PIPESIZE - inclientpipe, (uint64_t)fromclient-inclientpipe), SPLICE_F_NONBLOCK|SPLICE_F_MOVE); + res = splice(param->clisock, NULL, pipecli[1], NULL, (int)MIN((uint64_t)pipesize - inclientpipe, (uint64_t)fromclient-inclientpipe), SPLICE_F_NONBLOCK|SPLICE_F_MOVE); if(res <= 0) { FROMCLIENT = TOCLIENTPIPE = 0; if(res == 0 && !errno) { @@ -455,13 +454,13 @@ int sockmap(struct clientparam * param, int timeo, int usesplice){ else if(++streakcli > splicestreak) wantbufcli = 1; } inclientpipe += res; - if(inclientpipe >= PIPESIZE) TOCLIENTPIPE = 0; + if(inclientpipe >= pipesize) TOCLIENTPIPE = 0; needaction = 0; continue; } } if(splicesrv && !wantbufsrv && fromserver > inserverpipe && FROMSERVER && TOSERVERPIPE && !SESSIONEND){ - if(pipesrv[0] < 0 && pipenew(pipesrv) < 0){ + if(pipesrv[0] < 0 && pipenew(pipesrv, pipesize) < 0){ allowsrv = splicesrv = TOSERVERPIPE = FROMSERVERPIPE = 0; if(param->srvinbuf < SRVROOM) TOSERVERBUF = 1; continue; @@ -475,7 +474,7 @@ int sockmap(struct clientparam * param, int timeo, int usesplice){ continue; } errno = 0; - res = splice(param->remsock, NULL, pipesrv[1], NULL, MIN(PIPESIZE - inserverpipe, fromserver - inserverpipe), SPLICE_F_NONBLOCK|SPLICE_F_MOVE); + res = splice(param->remsock, NULL, pipesrv[1], NULL, MIN(pipesize - inserverpipe, fromserver - inserverpipe), SPLICE_F_NONBLOCK|SPLICE_F_MOVE); if(res <= 0) { FROMSERVER = TOSERVERPIPE = 0; if(res == 0 && !errno) { @@ -505,7 +504,7 @@ int sockmap(struct clientparam * param, int timeo, int usesplice){ param->nreads++; param->statssrv64 += res; inserverpipe += res; - if(inserverpipe >= PIPESIZE) TOSERVERPIPE = 0; + if(inserverpipe >= pipesize) TOSERVERPIPE = 0; if(param->bandlimfunc) { int sl1; sl1 = (*param->bandlimfunc)(param, res, 0); @@ -753,7 +752,7 @@ int sockmap(struct clientparam * param, int timeo, int usesplice){ // } #ifdef WITHSPLICE { - if(splicecli && fromclient>inclientpipe && !TOCLIENTPIPE && inclientpipe < PIPESIZE){ + if(splicecli && fromclient>inclientpipe && !TOCLIENTPIPE && inclientpipe < pipesize){ if(!after){ fds[fdsc].fd = pipecli[1]; fds[fdsc].events |= POLLOUT; @@ -781,7 +780,7 @@ int sockmap(struct clientparam * param, int timeo, int usesplice){ } fdsc++; } - if(splicesrv && fromserver>inserverpipe && !TOSERVERPIPE && inserverpipe < PIPESIZE){ + if(splicesrv && fromserver>inserverpipe && !TOSERVERPIPE && inserverpipe < pipesize){ if(!after){ fds[fdsc].fd = pipesrv[1]; fds[fdsc].events |= POLLOUT; diff --git a/src/structures.h b/src/structures.h index e771dda..5b9ecbe 100644 --- a/src/structures.h +++ b/src/structures.h @@ -723,6 +723,7 @@ struct srvparam { int usesplice; /* the automatic mode's heuristics, -s[N]:MIN,STREAK,SMALL */ int splicemin, splicestreak, splicesmall; + unsigned pipesize; /* -b,SIZE: what a pipe is made to hold, 0 PIPESIZE */ #endif int halfclose; int udpauth;