summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDamien Miller <djm@mindrot.org>2012-04-22 11:21:10 +1000
committerDamien Miller <djm@mindrot.org>2012-04-22 11:21:10 +1000
commita6508753db3c49910068d8fc324bd284d72ff153 (patch)
treede5d49ef31b5ba61e9086316baf0bd2afb78fd1b
parentc6081482b2b72caccce938c100c1b55f7b5e30fe (diff)
- 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@
-rw-r--r--ChangeLog4
-rw-r--r--channels.c52
-rw-r--r--channels.h6
-rw-r--r--clientloop.c8
-rw-r--r--serverloop.c14
5 files changed, 68 insertions, 16 deletions
diff --git a/ChangeLog b/ChangeLog
index 1b5e78a4..d19da34a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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]
diff --git a/channels.c b/channels.c
index e5783b19..cacd2fe5 100644
--- a/channels.c
+++ b/channels.c
@@ -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);
}
diff --git a/channels.h b/channels.h
index 6ed1ce00..d75b800f 100644
--- a/channels.h
+++ b/channels.h
@@ -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);
diff --git a/clientloop.c b/clientloop.c
index f69a9b02..58357cf3 100644
--- a/clientloop.c
+++ b/clientloop.c
@@ -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 {
diff --git a/serverloop.c b/serverloop.c
index 19b84ff2..50be16b7 100644
--- a/serverloop.c
+++ b/serverloop.c
@@ -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 */