dovecot-2.2: master: Added reuse_port setting to inet_listeners,...

dovecot at dovecot.org dovecot at dovecot.org
Mon Sep 23 09:45:23 EEST 2013


details:   http://hg.dovecot.org/dovecot-2.2/rev/a991a0547daa
changeset: 16823:a991a0547daa
user:      Timo Sirainen <tss at iki.fi>
date:      Mon Sep 23 04:25:16 2013 +0300
description:
master: Added reuse_port setting to inet_listeners, which enables SO_REUSEPORT if available.
After forking a new service process, a new listener socket is created for
each such inet_listener. Linux v3.9+ added SO_REUSEPORT feature, which
should distribute clients more uniformly to the processes. I'm not sure if
this makes any difference in BSDs.

At least in Linux v3.9 there was still a bug that if the number of listening
processes changed, some TCP handshakes might not finish. I don't see if this
has already been fixed, so this is probably safe to use only for services
whose process count doesn't change (e.g. process_min_avail is set high
enough).

diffstat:

 src/imap-login/imap-login-settings.c |   4 ++--
 src/lib-master/service-settings.h    |   1 +
 src/master/master-settings.c         |   4 +++-
 src/master/service-listen.c          |  35 +++++++++++++++++++++--------------
 src/master/service-listen.h          |   2 ++
 src/master/service-process.c         |  20 ++++++++++++++++++++
 src/master/service.h                 |   2 ++
 src/pop3-login/pop3-login-settings.c |   4 ++--
 8 files changed, 53 insertions(+), 19 deletions(-)

diffs (200 lines):

diff -r 6a814345f16c -r a991a0547daa src/imap-login/imap-login-settings.c
--- a/src/imap-login/imap-login-settings.c	Mon Sep 23 04:06:08 2013 +0300
+++ b/src/imap-login/imap-login-settings.c	Mon Sep 23 04:25:16 2013 +0300
@@ -11,8 +11,8 @@
 
 /* <settings checks> */
 static struct inet_listener_settings imap_login_inet_listeners_array[] = {
-	{ "imap", "", 143, FALSE },
-	{ "imaps", "", 993, TRUE }
+	{ .name = "imap", .address = "", .port = 143 },
+	{ .name = "imaps", .address = "", .port = 993, .ssl = TRUE }
 };
 static struct inet_listener_settings *imap_login_inet_listeners[] = {
 	&imap_login_inet_listeners_array[0],
diff -r 6a814345f16c -r a991a0547daa src/lib-master/service-settings.h
--- a/src/lib-master/service-settings.h	Mon Sep 23 04:06:08 2013 +0300
+++ b/src/lib-master/service-settings.h	Mon Sep 23 04:25:16 2013 +0300
@@ -31,6 +31,7 @@
 	const char *address;
 	unsigned int port;
 	bool ssl;
+	bool reuse_port;
 };
 ARRAY_DEFINE_TYPE(inet_listener_settings, struct inet_listener_settings *);
 
diff -r 6a814345f16c -r a991a0547daa src/master/master-settings.c
--- a/src/master/master-settings.c	Mon Sep 23 04:06:08 2013 +0300
+++ b/src/master/master-settings.c	Mon Sep 23 04:25:16 2013 +0300
@@ -64,6 +64,7 @@
 	DEF(SET_STR, address),
 	DEF(SET_UINT, port),
 	DEF(SET_BOOL, ssl),
+	DEF(SET_BOOL, reuse_port),
 
 	SETTING_DEFINE_LIST_END
 };
@@ -72,7 +73,8 @@
 	.name = "",
 	.address = "",
 	.port = 0,
-	.ssl = FALSE
+	.ssl = FALSE,
+	.reuse_port = FALSE
 };
 
 static const struct setting_parser_info inet_listener_setting_parser_info = {
diff -r 6a814345f16c -r a991a0547daa src/master/service-listen.c
--- a/src/master/service-listen.c	Mon Sep 23 04:06:08 2013 +0300
+++ b/src/master/service-listen.c	Mon Sep 23 04:25:16 2013 +0300
@@ -189,9 +189,11 @@
 static int service_inet_listener_listen(struct service_listener *l)
 {
         struct service *service = l->service;
+	enum net_listen_flags flags = 0;
 	const struct inet_listener_settings *set = l->set.inetset.set;
 	unsigned int port = set->port;
 	int fd;
+
 #ifdef HAVE_SYSTEMD
 	if (systemd_listen_fd(&l->set.inetset.ip, port, &fd) < 0)
 		return -1;
@@ -199,13 +201,16 @@
 	if (fd == -1)
 #endif
 	{
-		fd = net_listen(&l->set.inetset.ip, &port,
-				service_get_backlog(service));
+		if (set->reuse_port)
+			flags |= NET_LISTEN_FLAG_REUSEPORT;
+		fd = net_listen_full(&l->set.inetset.ip, &port, &flags,
+				     service_get_backlog(service));
 		if (fd < 0) {
 			service_error(service, "listen(%s, %u) failed: %m",
 				      l->inet_address, set->port);
 			return errno == EADDRINUSE ? 0 : -1;
 		}
+		l->reuse_port = (flags & NET_LISTEN_FLAG_REUSEPORT) != 0;
 	}
 	net_set_nonblock(fd, TRUE);
 	fd_close_on_exec(fd, TRUE);
@@ -214,6 +219,19 @@
 	return 1;
 }
 
+int service_listener_listen(struct service_listener *l)
+{
+	switch (l->type) {
+	case SERVICE_LISTENER_UNIX:
+		return service_unix_listener_listen(l);
+	case SERVICE_LISTENER_FIFO:
+		return service_fifo_listener_listen(l);
+	case SERVICE_LISTENER_INET:
+		return service_inet_listener_listen(l);
+	}
+	i_unreached();
+}
+
 static int service_listen(struct service *service)
 {
 	struct service_listener *const *listeners;
@@ -225,18 +243,7 @@
 		if (l->fd != -1)
 			continue;
 
-		switch (l->type) {
-		case SERVICE_LISTENER_UNIX:
-			ret2 = service_unix_listener_listen(l);
-			break;
-		case SERVICE_LISTENER_FIFO:
-			ret2 = service_fifo_listener_listen(l);
-			break;
-		case SERVICE_LISTENER_INET:
-			ret2 = service_inet_listener_listen(l);
-			break;
-		}
-
+		ret2 = service_listener_listen(l);
 		if (ret2 < ret)
 			ret = ret2;
 	}
diff -r 6a814345f16c -r a991a0547daa src/master/service-listen.h
--- a/src/master/service-listen.h	Mon Sep 23 04:06:08 2013 +0300
+++ b/src/master/service-listen.h	Mon Sep 23 04:25:16 2013 +0300
@@ -13,4 +13,6 @@
 int services_listen_using(struct service_list *new_service_list,
 			  struct service_list *old_service_list);
 
+int service_listener_listen(struct service_listener *l);
+
 #endif
diff -r 6a814345f16c -r a991a0547daa src/master/service-process.c
--- a/src/master/service-process.c	Mon Sep 23 04:06:08 2013 +0300
+++ b/src/master/service-process.c	Mon Sep 23 04:25:16 2013 +0300
@@ -23,6 +23,7 @@
 #include "dup2-array.h"
 #include "service.h"
 #include "service-anvil.h"
+#include "service-listen.h"
 #include "service-log.h"
 #include "service-process-notify.h"
 #include "service-process.h"
@@ -34,6 +35,24 @@
 #include <signal.h>
 #include <sys/wait.h>
 
+static void service_reopen_inet_listeners(struct service *service)
+{
+	struct service_listener *const *listeners;
+	unsigned int i, count;
+	int old_fd;
+
+	listeners = array_get(&service->listeners, &count);
+	for (i = 0; i < count; i++) {
+		if (!listeners[i]->reuse_port || listeners[i]->fd == -1)
+			continue;
+
+		old_fd = listeners[i]->fd;
+		listeners[i]->fd = -1;
+		if (service_listener_listen(listeners[i]) < 0)
+			listeners[i]->fd = old_fd;
+	}
+}
+
 static void
 service_dup_fds(struct service *service)
 {
@@ -305,6 +324,7 @@
 	if (pid == 0) {
 		/* child */
 		service_process_setup_environment(service, uid);
+		service_reopen_inet_listeners(service);
 		service_dup_fds(service);
 		drop_privileges(service);
 		process_exec(service->executable, NULL);
diff -r 6a814345f16c -r a991a0547daa src/master/service.h
--- a/src/master/service.h	Mon Sep 23 04:06:08 2013 +0300
+++ b/src/master/service.h	Mon Sep 23 04:25:16 2013 +0300
@@ -38,6 +38,8 @@
 			struct ip_addr ip;
 		} inetset;
 	} set;
+
+	bool reuse_port;
 };
 
 struct service {
diff -r 6a814345f16c -r a991a0547daa src/pop3-login/pop3-login-settings.c
--- a/src/pop3-login/pop3-login-settings.c	Mon Sep 23 04:06:08 2013 +0300
+++ b/src/pop3-login/pop3-login-settings.c	Mon Sep 23 04:25:16 2013 +0300
@@ -11,8 +11,8 @@
 
 /* <settings checks> */
 static struct inet_listener_settings pop3_login_inet_listeners_array[] = {
-	{ "pop3", "", 110, FALSE },
-	{ "pop3s", "", 995, TRUE }
+	{ .name = "pop3", .address = "", .port = 110 },
+	{ .name = "pop3s", .address = "", .port = 995, .ssl = TRUE }
 };
 static struct inet_listener_settings *pop3_login_inet_listeners[] = {
 	&pop3_login_inet_listeners_array[0],


More information about the dovecot-cvs mailing list