- djm@cvs.openbsd.org 2012/04/11 13:16:19

[channels.c channels.h clientloop.c serverloop.c]
     don't spin in accept() when out of fds (ENFILE/ENFILE) - back off for a
     while; ok deraadt@ markus@
This commit is contained in:
Damien Miller 2012-04-22 11:21:10 +10:00
parent c6081482b2
commit a6508753db
5 changed files with 68 additions and 16 deletions

View File

@ -17,6 +17,10 @@
[channels.c channels.h servconf.c]
Add PermitOpen none option based on patch from Loganaden Velvindron
(bz #1949). ok djm@
- djm@cvs.openbsd.org 2012/04/11 13:16:19
[channels.c channels.h clientloop.c serverloop.c]
don't spin in accept() when out of fds (ENFILE/ENFILE) - back off for a
while; ok deraadt@ markus@
20120420
- (djm) [contrib/caldera/openssh.spec contrib/redhat/openssh.spec]

View File

@ -1,4 +1,4 @@
/* $OpenBSD: channels.c,v 1.316 2012/03/29 23:54:36 dtucker Exp $ */
/* $OpenBSD: channels.c,v 1.317 2012/04/11 13:16:19 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -311,6 +311,7 @@ channel_new(char *ctype, int type, int rfd, int wfd, int efd,
c->istate = CHAN_INPUT_OPEN;
c->flags = 0;
channel_register_fds(c, rfd, wfd, efd, extusage, nonblock, 0);
c->notbefore = 0;
c->self = found;
c->type = type;
c->ctype = ctype;
@ -1339,6 +1340,8 @@ channel_post_x11_listener(Channel *c, fd_set *readset, fd_set *writeset)
}
if (newsock < 0) {
error("accept: %.100s", strerror(errno));
if (errno == EMFILE || errno == ENFILE)
c->notbefore = time(NULL) + 1;
return;
}
set_nodelay(newsock);
@ -1482,6 +1485,8 @@ channel_post_port_listener(Channel *c, fd_set *readset, fd_set *writeset)
newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
if (newsock < 0) {
error("accept: %.100s", strerror(errno));
if (errno == EMFILE || errno == ENFILE)
c->notbefore = time(NULL) + 1;
return;
}
set_nodelay(newsock);
@ -1514,7 +1519,10 @@ channel_post_auth_listener(Channel *c, fd_set *readset, fd_set *writeset)
addrlen = sizeof(addr);
newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
if (newsock < 0) {
error("accept from auth socket: %.100s", strerror(errno));
error("accept from auth socket: %.100s",
strerror(errno));
if (errno == EMFILE || errno == ENFILE)
c->notbefore = time(NULL) + 1;
return;
}
nc = channel_new("accepted auth socket",
@ -1917,6 +1925,8 @@ channel_post_mux_listener(Channel *c, fd_set *readset, fd_set *writeset)
if ((newsock = accept(c->sock, (struct sockaddr*)&addr,
&addrlen)) == -1) {
error("%s accept: %s", __func__, strerror(errno));
if (errno == EMFILE || errno == ENFILE)
c->notbefore = time(NULL) + 1;
return;
}
@ -2067,16 +2077,21 @@ channel_garbage_collect(Channel *c)
}
static void
channel_handler(chan_fn *ftab[], fd_set *readset, fd_set *writeset)
channel_handler(chan_fn *ftab[], fd_set *readset, fd_set *writeset,
time_t *unpause_secs)
{
static int did_init = 0;
u_int i, oalloc;
Channel *c;
time_t now;
if (!did_init) {
channel_handler_init();
did_init = 1;
}
now = time(NULL);
if (unpause_secs != NULL)
*unpause_secs = 0;
for (i = 0, oalloc = channels_alloc; i < oalloc; i++) {
c = channels[i];
if (c == NULL)
@ -2087,10 +2102,30 @@ channel_handler(chan_fn *ftab[], fd_set *readset, fd_set *writeset)
else
continue;
}
if (ftab[c->type] != NULL)
(*ftab[c->type])(c, readset, writeset);
if (ftab[c->type] != NULL) {
/*
* Run handlers that are not paused.
*/
if (c->notbefore <= now)
(*ftab[c->type])(c, readset, writeset);
else if (unpause_secs != NULL) {
/*
* Collect the time that the earliest
* channel comes off pause.
*/
debug3("%s: chan %d: skip for %d more seconds",
__func__, c->self,
(int)(c->notbefore - now));
if (*unpause_secs == 0 ||
(c->notbefore - now) < *unpause_secs)
*unpause_secs = c->notbefore - now;
}
}
channel_garbage_collect(c);
}
if (unpause_secs != NULL && *unpause_secs != 0)
debug3("%s: first channel unpauses in %d seconds",
__func__, (int)*unpause_secs);
}
/*
@ -2099,7 +2134,7 @@ channel_handler(chan_fn *ftab[], fd_set *readset, fd_set *writeset)
*/
void
channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp,
u_int *nallocp, int rekeying)
u_int *nallocp, int *minwait_secs, int rekeying)
{
u_int n, sz, nfdset;
@ -2122,7 +2157,8 @@ channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp,
memset(*writesetp, 0, sz);
if (!rekeying)
channel_handler(channel_pre, *readsetp, *writesetp);
channel_handler(channel_pre, *readsetp, *writesetp,
minwait_secs);
}
/*
@ -2132,7 +2168,7 @@ channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp,
void
channel_after_select(fd_set *readset, fd_set *writeset)
{
channel_handler(channel_post, readset, writeset);
channel_handler(channel_post, readset, writeset, NULL);
}

View File

@ -1,4 +1,4 @@
/* $OpenBSD: channels.h,v 1.110 2012/03/29 23:54:36 dtucker Exp $ */
/* $OpenBSD: channels.h,v 1.111 2012/04/11 13:16:19 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -105,6 +105,7 @@ struct Channel {
int wfd_isatty; /* wfd is a tty */
int client_tty; /* (client) TTY has been requested */
int force_drain; /* force close on iEOF */
time_t notbefore; /* Pause IO until deadline (time_t) */
int delayed; /* post-select handlers for newly created
* channels are delayed until the first call
* to a matching pre-select handler.
@ -238,7 +239,8 @@ void channel_input_status_confirm(int, u_int32_t, void *);
/* file descriptor handling (read/write) */
void channel_prepare_select(fd_set **, fd_set **, int *, u_int*, int);
void channel_prepare_select(fd_set **, fd_set **, int *, u_int*,
time_t*, int);
void channel_after_select(fd_set *, fd_set *);
void channel_output_poll(void);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: clientloop.c,v 1.238 2012/01/18 21:46:43 dtucker Exp $ */
/* $OpenBSD: clientloop.c,v 1.239 2012/04/11 13:16:19 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -583,10 +583,12 @@ client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp,
{
struct timeval tv, *tvp;
int timeout_secs;
time_t minwait_secs;
int ret;
/* Add any selections by the channel mechanism. */
channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, rekeying);
channel_prepare_select(readsetp, writesetp, maxfdp, nallocp,
&minwait_secs, rekeying);
if (!compat20) {
/* Read from the connection, unless our buffers are full. */
@ -639,6 +641,8 @@ client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp,
if (timeout_secs < 0)
timeout_secs = 0;
}
if (minwait_secs != 0)
timeout_secs = MIN(timeout_secs, (int)minwait_secs);
if (timeout_secs == INT_MAX)
tvp = NULL;
else {

View File

@ -1,4 +1,4 @@
/* $OpenBSD: serverloop.c,v 1.160 2011/05/15 08:09:01 djm Exp $ */
/* $OpenBSD: serverloop.c,v 1.161 2012/04/11 13:16:19 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -281,9 +281,18 @@ wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, int *maxfdp,
{
struct timeval tv, *tvp;
int ret;
time_t minwait_secs;
int client_alive_scheduled = 0;
int program_alive_scheduled = 0;
/* Allocate and update select() masks for channel descriptors. */
channel_prepare_select(readsetp, writesetp, maxfdp, nallocp,
&minwait_secs, 0);
if (minwait_secs != 0)
max_time_milliseconds = MIN(max_time_milliseconds,
(u_int)minwait_secs * 1000);
/*
* if using client_alive, set the max timeout accordingly,
* and indicate that this particular timeout was for client
@ -298,9 +307,6 @@ wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, int *maxfdp,
max_time_milliseconds = options.client_alive_interval * 1000;
}
/* Allocate and update select() masks for channel descriptors. */
channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, 0);
if (compat20) {
#if 0
/* wrong: bad condition XXX */