- 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@
diff --git a/channels.c b/channels.c
index e5783b1..cacd2fe 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 @@
 	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 @@
 		}
 		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 @@
 		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 @@
 		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 @@
 	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 @@
 }
 
 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 @@
 			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 @@
  */
 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 @@
 	memset(*writesetp, 0, sz);
 
 	if (!rekeying)
-		channel_handler(channel_pre, *readsetp, *writesetp);
+		channel_handler(channel_pre, *readsetp, *writesetp,
+		    minwait_secs);
 }
 
 /*
@@ -2132,7 +2168,7 @@
 void
 channel_after_select(fd_set *readset, fd_set *writeset)
 {
-	channel_handler(channel_post, readset, writeset);
+	channel_handler(channel_post, readset, writeset, NULL);
 }