On Fri, Jun 05, 2015 at 05:15:09PM -0500, Matthew Martin wrote:
> >Synopsis: Serving large files with httpd eats fscktons of memory.
> >Category: system
> >Environment:
> System : OpenBSD 5.7
> Details : OpenBSD 5.7 (GENERIC) #0: Thu Apr 30 22:01:01 CEST 2015
>
> [email protected]:/binpatchng/work-binpatch57-amd64/src/sys/arch/amd64/compile/GENERIC
>
> Architecture: OpenBSD.amd64
> Machine : amd64
> >Description:
> A couple of people concurrently downloading iso sized files can drag
> the server into using all of swap (there's only a gig) in a
> frighteningly short time.
>
> Revision 1.30 of
> http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.sbin/httpd/server_file.c
> had message "Adjust the read/write watermarks according to the TCP
> send buffer. This fixes sending of large files. Previously, httpd was
> reading the input file too quickly and could run out of memory when
> filling the input buffer."
>
> That pretty well describes the situation I'm seeing now with 5.7
> (-RELEASE + m:tier binpatches). It also happens with -CURRENT (as of
> Jun 5) httpd compiled and slapped in its place.
>
> >How-To-Repeat:
> Something along the lines of
> hostA# dd if=/dev/zero of=/var/www/htdocs/test_512.dat bs=1M count=512
> hostB$ ftp http://hostA/test_512.dat
>
> >Fix:
> I've attempted various settings of 'tcp socket buffer' and shotgunning
> around the lines modified in 1.30 to no avail. Trying to limit its
> consumption via login.conf just makes it die when it hits the limit.
the problem is that we have to bufferevents. clt->clt_bev which is
handleing the accept(2)ed socket and clt->clt_srvbev which is
handleing the fd of the served file.
it goes like this:
clt_bev: Here I am, brain the size of a planet, and they ask me to serve
a file.
clt_srvbev: Whee, I can read clt_sndbufsiz bytes from the file. Hello
clt_bev, here, have some data.
clt_bev: Ok, I can't send it right now, but I have a buffer, I'll put
the data at the end.
[last message repeated n times]
clt_bev: Phew, I can send some data to the client, hopfully clt_bev
stops to send me data soon.
and so on... In the end the whole iso ends up in clt_bev.
I'm not sure yet about the 32 and 256 magic numbers, they seem to it
some kind of sweet spot on *my* system. Make them to small and
everything slows down, make them to large and you waste memory.
Please try this:
diff --git server.c server.c
index ca67a47..22b47b8 100644
--- server.c
+++ server.c
@@ -704,7 +704,7 @@ server_input(struct client *clt)
/* Adjust write watermark to the socket buffer output size */
bufferevent_setwatermark(clt->clt_bev, EV_WRITE,
- clt->clt_sndbufsiz, 0);
+ 32 * clt->clt_sndbufsiz, 0);
/* Read at most amount of data that fits in one fcgi record. */
bufferevent_setwatermark(clt->clt_bev, EV_READ, 0, FCGI_CONTENT_SIZE);
@@ -729,6 +729,10 @@ server_write(struct bufferevent *bev, void *arg)
goto done;
bufferevent_enable(bev, EV_READ);
+
+ if (clt->clt_srvbev && !(clt->clt_srvbev->enabled & EV_READ))
+ bufferevent_enable(clt->clt_srvbev, EV_READ);
+
return;
done:
(*bev->errorcb)(bev, EVBUFFER_WRITE|EVBUFFER_EOF, bev->cbarg);
@@ -769,6 +773,11 @@ server_read(struct bufferevent *bev, void *arg)
goto fail;
if (clt->clt_done)
goto done;
+
+ if (EVBUFFER_LENGTH(EVBUFFER_OUTPUT(clt->clt_bev)) > 256 *
+ clt->clt_sndbufsiz)
+ bufferevent_disable(bev, EV_READ);
+
return;
done:
(*bev->errorcb)(bev, EVBUFFER_READ|EVBUFFER_EOF, bev->cbarg);
--
I'm not entirely sure you are real.