summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStuart Henderson <sthen@cvs.openbsd.org>2012-05-16 08:57:22 +0000
committerStuart Henderson <sthen@cvs.openbsd.org>2012-05-16 08:57:22 +0000
commit096815990943e7237e8fd06a1d29b5f185bb417c (patch)
tree9d036599d3a64c5afc8ea1e1db77c3e4253e19c5
parent2d0265882a16694c42b396ac0b2598691875b4dc (diff)
Don't spin accept() when hitting ENFILE/EMFILE. Upstream r2663.
-rw-r--r--usr.sbin/unbound/daemon/remote.c16
-rw-r--r--usr.sbin/unbound/daemon/remote.h12
-rw-r--r--usr.sbin/unbound/daemon/worker.c18
-rw-r--r--usr.sbin/unbound/daemon/worker.h6
-rw-r--r--usr.sbin/unbound/doc/Changelog3
-rw-r--r--usr.sbin/unbound/libunbound/libworker.c10
-rw-r--r--usr.sbin/unbound/services/listen_dnsport.c27
-rw-r--r--usr.sbin/unbound/services/listen_dnsport.h12
-rw-r--r--usr.sbin/unbound/smallapp/worker_cb.c10
-rw-r--r--usr.sbin/unbound/util/fptr_wlist.c13
-rw-r--r--usr.sbin/unbound/util/fptr_wlist.h16
-rw-r--r--usr.sbin/unbound/util/netevent.c61
-rw-r--r--usr.sbin/unbound/util/netevent.h32
13 files changed, 236 insertions, 0 deletions
diff --git a/usr.sbin/unbound/daemon/remote.c b/usr.sbin/unbound/daemon/remote.c
index a2b2204c240..f0b5a01a581 100644
--- a/usr.sbin/unbound/daemon/remote.c
+++ b/usr.sbin/unbound/daemon/remote.c
@@ -361,6 +361,22 @@ int daemon_remote_open_accept(struct daemon_remote* rc,
return 1;
}
+void daemon_remote_stop_accept(struct daemon_remote* rc)
+{
+ struct listen_list* p;
+ for(p=rc->accept_list; p; p=p->next) {
+ comm_point_stop_listening(p->com);
+ }
+}
+
+void daemon_remote_start_accept(struct daemon_remote* rc)
+{
+ struct listen_list* p;
+ for(p=rc->accept_list; p; p=p->next) {
+ comm_point_start_listening(p->com, -1, -1);
+ }
+}
+
int remote_accept_callback(struct comm_point* c, void* arg, int err,
struct comm_reply* ATTR_UNUSED(rep))
{
diff --git a/usr.sbin/unbound/daemon/remote.h b/usr.sbin/unbound/daemon/remote.h
index 1edc1b887bd..5919be4f2a3 100644
--- a/usr.sbin/unbound/daemon/remote.h
+++ b/usr.sbin/unbound/daemon/remote.h
@@ -136,6 +136,18 @@ int daemon_remote_open_accept(struct daemon_remote* rc,
struct listen_port* ports, struct worker* worker);
/**
+ * Stop accept handlers for TCP (until enabled again)
+ * @param rc: state
+ */
+void daemon_remote_stop_accept(struct daemon_remote* rc);
+
+/**
+ * Stop accept handlers for TCP (until enabled again)
+ * @param rc: state
+ */
+void daemon_remote_start_accept(struct daemon_remote* rc);
+
+/**
* Handle nonthreaded remote cmd execution.
* @param worker: this worker (the remote worker).
*/
diff --git a/usr.sbin/unbound/daemon/worker.c b/usr.sbin/unbound/daemon/worker.c
index 11d7c810820..07af5b64380 100644
--- a/usr.sbin/unbound/daemon/worker.c
+++ b/usr.sbin/unbound/daemon/worker.c
@@ -1053,6 +1053,8 @@ worker_init(struct worker* worker, struct config_file *cfg,
worker_delete(worker);
return 0;
}
+ comm_base_set_slow_accept_handlers(worker->base, &worker_stop_accept,
+ &worker_start_accept, worker);
if(do_sigs) {
#ifdef SIGHUP
ub_thread_sig_unblock(SIGHUP);
@@ -1279,6 +1281,22 @@ void worker_stats_clear(struct worker* worker)
worker->back->unwanted_replies = 0;
}
+void worker_start_accept(void* arg)
+{
+ struct worker* worker = (struct worker*)arg;
+ listen_start_accept(worker->front);
+ if(worker->thread_num == 0)
+ daemon_remote_start_accept(worker->daemon->rc);
+}
+
+void worker_stop_accept(void* arg)
+{
+ struct worker* worker = (struct worker*)arg;
+ listen_stop_accept(worker->front);
+ if(worker->thread_num == 0)
+ daemon_remote_stop_accept(worker->daemon->rc);
+}
+
/* --- fake callbacks for fptr_wlist to work --- */
struct outbound_entry* libworker_send_query(uint8_t* ATTR_UNUSED(qname),
size_t ATTR_UNUSED(qnamelen), uint16_t ATTR_UNUSED(qtype),
diff --git a/usr.sbin/unbound/daemon/worker.h b/usr.sbin/unbound/daemon/worker.h
index 96c04676a11..c510ebfd735 100644
--- a/usr.sbin/unbound/daemon/worker.h
+++ b/usr.sbin/unbound/daemon/worker.h
@@ -225,4 +225,10 @@ void worker_stat_timer_cb(void* arg);
/** probe timer callback handler */
void worker_probe_timer_cb(void* arg);
+/** start accept callback handler */
+void worker_start_accept(void* arg);
+
+/** stop accept callback handler */
+void worker_stop_accept(void* arg);
+
#endif /* DAEMON_WORKER_H */
diff --git a/usr.sbin/unbound/doc/Changelog b/usr.sbin/unbound/doc/Changelog
index d5300330bb6..dbbe0abf17e 100644
--- a/usr.sbin/unbound/doc/Changelog
+++ b/usr.sbin/unbound/doc/Changelog
@@ -1,3 +1,6 @@
+8 May 2012: Wouter
+ - Fix for accept spinning reported by OpenBSD.
+
2 February 2012: Wouter
- 1.4.16 release tag.
diff --git a/usr.sbin/unbound/libunbound/libworker.c b/usr.sbin/unbound/libunbound/libworker.c
index 0a734a294c6..c98008cc0c6 100644
--- a/usr.sbin/unbound/libunbound/libworker.c
+++ b/usr.sbin/unbound/libunbound/libworker.c
@@ -862,6 +862,16 @@ void worker_probe_timer_cb(void* ATTR_UNUSED(arg))
log_assert(0);
}
+void worker_start_accept(void* ATTR_UNUSED(arg))
+{
+ log_assert(0);
+}
+
+void worker_stop_accept(void* ATTR_UNUSED(arg))
+{
+ log_assert(0);
+}
+
int order_lock_cmp(const void* ATTR_UNUSED(e1), const void* ATTR_UNUSED(e2))
{
log_assert(0);
diff --git a/usr.sbin/unbound/services/listen_dnsport.c b/usr.sbin/unbound/services/listen_dnsport.c
index ea7ec3a6603..59ca1991eb1 100644
--- a/usr.sbin/unbound/services/listen_dnsport.c
+++ b/usr.sbin/unbound/services/listen_dnsport.c
@@ -915,3 +915,30 @@ size_t listen_get_mem(struct listen_dnsport* listen)
}
return s;
}
+
+void listen_stop_accept(struct listen_dnsport* listen)
+{
+ /* do not stop the ones that have no tcp_free list
+ * (they have already stopped listening) */
+ struct listen_list* p;
+ for(p=listen->cps; p; p=p->next) {
+ if(p->com->type == comm_tcp_accept &&
+ p->com->tcp_free != NULL) {
+ comm_point_stop_listening(p->com);
+ }
+ }
+}
+
+void listen_start_accept(struct listen_dnsport* listen)
+{
+ /* do not start the ones that have no tcp_free list, it is no
+ * use to listen to them because they have no free tcp handlers */
+ struct listen_list* p;
+ for(p=listen->cps; p; p=p->next) {
+ if(p->com->type == comm_tcp_accept &&
+ p->com->tcp_free != NULL) {
+ comm_point_start_listening(p->com, -1, -1);
+ }
+ }
+}
+
diff --git a/usr.sbin/unbound/services/listen_dnsport.h b/usr.sbin/unbound/services/listen_dnsport.h
index 22fa8280453..4d37aca5eee 100644
--- a/usr.sbin/unbound/services/listen_dnsport.h
+++ b/usr.sbin/unbound/services/listen_dnsport.h
@@ -154,6 +154,18 @@ void listen_list_delete(struct listen_list* list);
size_t listen_get_mem(struct listen_dnsport* listen);
/**
+ * stop accept handlers for TCP (until enabled again)
+ * @param listen: listening structure.
+ */
+void listen_stop_accept(struct listen_dnsport* listen);
+
+/**
+ * start accept handlers for TCP (was stopped before)
+ * @param listen: listening structure.
+ */
+void listen_start_accept(struct listen_dnsport* listen);
+
+/**
* Create and bind nonblocking UDP socket
* @param family: for socket call.
* @param socktype: for socket call.
diff --git a/usr.sbin/unbound/smallapp/worker_cb.c b/usr.sbin/unbound/smallapp/worker_cb.c
index 8e86caaeb57..bc37e3307ae 100644
--- a/usr.sbin/unbound/smallapp/worker_cb.c
+++ b/usr.sbin/unbound/smallapp/worker_cb.c
@@ -195,6 +195,16 @@ void worker_probe_timer_cb(void* ATTR_UNUSED(arg))
log_assert(0);
}
+void worker_start_accept(void* ATTR_UNUSED(arg))
+{
+ log_assert(0);
+}
+
+void worker_stop_accept(void* ATTR_UNUSED(arg))
+{
+ log_assert(0);
+}
+
/** keep track of lock id in lock-verify application */
struct order_id {
/** the thread id that created it */
diff --git a/usr.sbin/unbound/util/fptr_wlist.c b/usr.sbin/unbound/util/fptr_wlist.c
index cf3ebf6fcf9..6bb95a5318b 100644
--- a/usr.sbin/unbound/util/fptr_wlist.c
+++ b/usr.sbin/unbound/util/fptr_wlist.c
@@ -119,6 +119,18 @@ fptr_whitelist_comm_signal(void (*fptr)(int, void*))
return 0;
}
+int fptr_whitelist_start_accept(void (*fptr)(void*))
+{
+ if(fptr == &worker_start_accept) return 1;
+ return 0;
+}
+
+int fptr_whitelist_stop_accept(void (*fptr)(void*))
+{
+ if(fptr == &worker_stop_accept) return 1;
+ return 0;
+}
+
int
fptr_whitelist_event(void (*fptr)(int, short, void *))
{
@@ -131,6 +143,7 @@ fptr_whitelist_event(void (*fptr)(int, short, void *))
else if(fptr == &comm_point_local_handle_callback) return 1;
else if(fptr == &comm_point_raw_handle_callback) return 1;
else if(fptr == &tube_handle_signal) return 1;
+ else if(fptr == &comm_base_handle_slow_accept) return 1;
#ifdef UB_ON_WINDOWS
else if(fptr == &worker_win_stop_cb) return 1;
#endif
diff --git a/usr.sbin/unbound/util/fptr_wlist.h b/usr.sbin/unbound/util/fptr_wlist.h
index 8ec823dd6f1..d204e923884 100644
--- a/usr.sbin/unbound/util/fptr_wlist.h
+++ b/usr.sbin/unbound/util/fptr_wlist.h
@@ -107,6 +107,22 @@ int fptr_whitelist_comm_timer(void (*fptr)(void*));
int fptr_whitelist_comm_signal(void (*fptr)(int, void*));
/**
+ * Check function pointer whitelist for start_accept callback values.
+ *
+ * @param fptr: function pointer to check.
+ * @return false if not in whitelist.
+ */
+int fptr_whitelist_start_accept(void (*fptr)(void*));
+
+/**
+ * Check function pointer whitelist for stop_accept callback values.
+ *
+ * @param fptr: function pointer to check.
+ * @return false if not in whitelist.
+ */
+int fptr_whitelist_stop_accept(void (*fptr)(void*));
+
+/**
* Check function pointer whitelist for event structure callback values.
* This is not called by libevent itself, but checked by netevent.
*
diff --git a/usr.sbin/unbound/util/netevent.c b/usr.sbin/unbound/util/netevent.c
index c6f5279f6a7..cb47352afa9 100644
--- a/usr.sbin/unbound/util/netevent.c
+++ b/usr.sbin/unbound/util/netevent.c
@@ -115,6 +115,10 @@ struct internal_base {
uint32_t secs;
/** timeval with current time */
struct timeval now;
+ /** the event used for slow_accept timeouts */
+ struct event slow_accept;
+ /** true if slow_accept is enabled */
+ int slow_accept_enabled;
};
/**
@@ -225,6 +229,11 @@ comm_base_delete(struct comm_base* b)
{
if(!b)
return;
+ if(b->eb->slow_accept_enabled) {
+ if(event_del(&b->eb->slow_accept) != 0) {
+ log_err("could not event_del slow_accept");
+ }
+ }
#ifdef USE_MINI_EVENT
event_base_free(b->eb->base);
#elif defined(HAVE_EVENT_BASE_FREE) && defined(HAVE_EVENT_BASE_ONCE)
@@ -263,6 +272,14 @@ void comm_base_exit(struct comm_base* b)
}
}
+void comm_base_set_slow_accept_handlers(struct comm_base* b,
+ void (*stop_acc)(void*), void (*start_acc)(void*), void* arg)
+{
+ b->stop_accept = stop_acc;
+ b->start_accept = start_acc;
+ b->cb_arg = arg;
+}
+
struct event_base* comm_base_internal(struct comm_base* b)
{
return b->eb->base;
@@ -646,6 +663,18 @@ setup_tcp_handler(struct comm_point* c, int fd)
comm_point_start_listening(c, fd, TCP_QUERY_TIMEOUT);
}
+void comm_base_handle_slow_accept(int fd, short event, void* arg)
+{
+ struct comm_base* b = (struct comm_base*)arg;
+ /* timeout for the slow accept, re-enable accepts again */
+ if(b->start_accept) {
+ verbose(VERB_ALGO, "wait is over, slow accept disabled");
+ fptr_ok(fptr_whitelist_start_accept(b->start_accept));
+ (*b->start_accept)(b->cb_arg);
+ b->eb->slow_accept_enabled = 0;
+ }
+}
+
int comm_point_perform_accept(struct comm_point* c,
struct sockaddr_storage* addr, socklen_t* addrlen)
{
@@ -667,6 +696,38 @@ int comm_point_perform_accept(struct comm_point* c,
#endif /* EPROTO */
)
return -1;
+#if defined(ENFILE) && defined(EMFILE)
+ if(errno == ENFILE || errno == EMFILE) {
+ /* out of file descriptors, likely outside of our
+ * control. stop accept() calls for some time */
+ if(c->ev->base->stop_accept) {
+ struct comm_base* b = c->ev->base;
+ struct timeval tv;
+ verbose(VERB_ALGO, "out of file descriptors: "
+ "slow accept");
+ b->eb->slow_accept_enabled = 1;
+ fptr_ok(fptr_whitelist_stop_accept(
+ b->stop_accept));
+ (*b->stop_accept)(b->cb_arg);
+ /* set timeout, no mallocs */
+ tv.tv_sec = NETEVENT_SLOW_ACCEPT_TIME/1000;
+ tv.tv_usec = NETEVENT_SLOW_ACCEPT_TIME%1000;
+ event_set(&b->eb->slow_accept, -1, EV_TIMEOUT,
+ comm_base_handle_slow_accept, b);
+ if(event_base_set(b->eb->base,
+ &b->eb->slow_accept) != 0) {
+ /* we do not want to log here, because
+ * that would spam the logfiles.
+ * error: "event_base_set failed." */
+ }
+ if(event_add(&b->eb->slow_accept, &tv) != 0) {
+ /* we do not want to log here,
+ * error: "event_add failed." */
+ }
+ }
+ return -1;
+ }
+#endif
log_err("accept failed: %s", strerror(errno));
#else /* USE_WINSOCK */
if(WSAGetLastError() == WSAEINPROGRESS ||
diff --git a/usr.sbin/unbound/util/netevent.h b/usr.sbin/unbound/util/netevent.h
index 8ce62e7fa75..feec4e82310 100644
--- a/usr.sbin/unbound/util/netevent.h
+++ b/usr.sbin/unbound/util/netevent.h
@@ -83,12 +83,23 @@ typedef int comm_point_callback_t(struct comm_point*, void*, int,
/** to pass fallback from capsforID to callback function; 0x20 failed */
#define NETEVENT_CAPSFAIL -3
+/** timeout to slow accept calls when not possible, in msec. */
+#define NETEVENT_SLOW_ACCEPT_TIME 2000
+
/**
* A communication point dispatcher. Thread specific.
*/
struct comm_base {
/** behind the scenes structure. with say libevent info. alloced */
struct internal_base* eb;
+ /** callback to stop listening on accept sockets,
+ * performed when accept() will not function properly */
+ void (*stop_accept)(void*);
+ /** callback to start listening on accept sockets, performed
+ * after stop_accept() then a timeout has passed. */
+ void (*start_accept)(void*);
+ /** user argument for stop_accept and start_accept functions */
+ void* cb_arg;
};
/**
@@ -312,6 +323,17 @@ void comm_base_dispatch(struct comm_base* b);
void comm_base_exit(struct comm_base* b);
/**
+ * Set the slow_accept mode handlers. You can not provide these if you do
+ * not perform accept() calls.
+ * @param b: comm base
+ * @param stop_accept: function that stops listening to accept fds.
+ * @param start_accept: function that resumes listening to accept fds.
+ * @param arg: callback arg to pass to the functions.
+ */
+void comm_base_set_slow_accept_handlers(struct comm_base* b,
+ void (*stop_accept)(void*), void (*start_accept)(void*), void* arg);
+
+/**
* Access internal data structure (for util/tube.c on windows)
* @param b: comm base
* @return event_base. Could be libevent, or internal event handler.
@@ -636,6 +658,16 @@ void comm_point_local_handle_callback(int fd, short event, void* arg);
*/
void comm_point_raw_handle_callback(int fd, short event, void* arg);
+/**
+ * This routine is published for checks and tests, and is only used internally.
+ * libevent callback for timeout on slow accept.
+ * @param fd: file descriptor.
+ * @param event: event bits from libevent:
+ * EV_READ, EV_WRITE, EV_SIGNAL, EV_TIMEOUT.
+ * @param arg: the comm_point structure.
+ */
+void comm_base_handle_slow_accept(int fd, short event, void* arg);
+
#ifdef USE_WINSOCK
/**
* Callback for openssl BIO to on windows detect WSAEWOULDBLOCK and notify