dovecot-2.0: *-login: Fixed dropping oldest connection when reac...

dovecot at dovecot.org dovecot at dovecot.org
Fri Sep 11 01:57:01 EEST 2009


details:   http://hg.dovecot.org/dovecot-2.0/rev/77228b5431e1
changeset: 9923:77228b5431e1
user:      Timo Sirainen <tss at iki.fi>
date:      Thu Sep 10 18:56:49 2009 -0400
description:
*-login: Fixed dropping oldest connection when reaching all limits.

diffstat:

15 files changed, 159 insertions(+), 72 deletions(-)
src/lib-master/master-auth.c            |   18 ++++++++
src/lib-master/master-interface.h       |    1 
src/lib-master/master-service-private.h |    5 ++
src/lib-master/master-service.c         |   39 ++++++++++++------
src/lib-master/master-service.h         |    5 ++
src/login-common/client-common.c        |   66 ++++++++++---------------------
src/login-common/login-settings.c       |    6 --
src/login-common/login-settings.h       |    1 
src/login-common/main.c                 |   14 +++++-
src/master/service-auth-source.c        |   29 +++++++++++++
src/master/service-auth-source.h        |    3 +
src/master/service-monitor.c            |   32 ++++++++++++---
src/master/service-process.c            |    7 ++-
src/master/service-process.h            |    2 
src/master/service.h                    |    3 +

diffs (truncated from 526 to 300 lines):

diff -r 2e94a44c34ff -r 77228b5431e1 src/lib-master/master-auth.c
--- a/src/lib-master/master-auth.c	Thu Sep 10 18:54:14 2009 -0400
+++ b/src/lib-master/master-auth.c	Thu Sep 10 18:56:49 2009 -0400
@@ -103,10 +103,28 @@ void master_auth_request_abort(struct ma
 	auth->free_nodes = node;
 }
 
+static void
+master_notify_have_more_avail_processes(struct master_service *service,
+					bool have_more)
+{
+	if (!have_more) {
+		/* make sure we're listening for more connections */
+		master_service_io_listeners_add(service);
+	}
+	service->call_avail_overflow = !have_more;
+}
+
 static void request_handle(struct master_auth *auth,
 			   struct master_auth_reply *reply)
 {
         struct master_auth_request_node *node;
+
+	if (reply->tag == 0) {
+		/* notification from master */
+		master_notify_have_more_avail_processes(auth->service,
+							reply->status == 0);
+		return;
+	}
 
 	node = hash_table_lookup(auth->requests, POINTER_CAST(reply->tag));
 	if (node == NULL)
diff -r 2e94a44c34ff -r 77228b5431e1 src/lib-master/master-interface.h
--- a/src/lib-master/master-interface.h	Thu Sep 10 18:54:14 2009 -0400
+++ b/src/lib-master/master-interface.h	Thu Sep 10 18:56:49 2009 -0400
@@ -64,6 +64,7 @@ enum master_auth_status {
 };
 
 struct master_auth_reply {
+	/* tag=0 are notifications from master */
 	unsigned int tag;
 	enum master_auth_status status;
 	/* PID of the post-login mail process handling this connection */
diff -r 2e94a44c34ff -r 77228b5431e1 src/lib-master/master-service-private.h
--- a/src/lib-master/master-service-private.h	Thu Sep 10 18:54:14 2009 -0400
+++ b/src/lib-master/master-service-private.h	Thu Sep 10 18:56:49 2009 -0400
@@ -34,6 +34,8 @@ struct master_service {
 	unsigned int total_available_count;
 	struct master_status master_status;
 
+	void (*avail_overflow_callback)(void);
+
         struct master_auth *auth;
 	master_service_connection_callback_t *callback;
 
@@ -46,6 +48,9 @@ struct master_service {
 	unsigned int initial_status_sent:1;
 	unsigned int default_settings:1;
 	unsigned int die_with_master:1;
+	unsigned int call_avail_overflow:1;
 };
 
+void master_service_io_listeners_add(struct master_service *service);
+
 #endif
diff -r 2e94a44c34ff -r 77228b5431e1 src/lib-master/master-service.c
--- a/src/lib-master/master-service.c	Thu Sep 10 18:54:14 2009 -0400
+++ b/src/lib-master/master-service.c	Thu Sep 10 18:56:49 2009 -0400
@@ -28,7 +28,6 @@
 
 struct master_service *master_service;
 
-static void io_listeners_add(struct master_service *service);
 static void io_listeners_remove(struct master_service *service);
 static void master_status_update(struct master_service *service);
 
@@ -294,7 +293,7 @@ void master_service_init_finish(struct m
 		master_service_set_service_count(service, 1);
 	}
 
-	io_listeners_add(service);
+	master_service_io_listeners_add(service);
 
 	if ((service->flags & MASTER_SERVICE_FLAG_STD_CLIENT) != 0) {
 		/* we already have a connection to be served */
@@ -380,6 +379,12 @@ unsigned int master_service_get_socket_c
 	return service->socket_count;
 }
 
+void master_service_set_avail_overflow_callback(struct master_service *service,
+						void (*callback)(void))
+{
+	service->avail_overflow_callback = callback;
+}
+
 const char *master_service_get_config_path(struct master_service *service)
 {
 	return service->config_path;
@@ -433,7 +438,7 @@ void master_service_client_connection_de
 void master_service_client_connection_destroyed(struct master_service *service)
 {
 	/* we can listen again */
-	io_listeners_add(service);
+	master_service_io_listeners_add(service);
 
 	i_assert(service->total_available_count > 0);
 
@@ -497,12 +502,20 @@ void master_service_deinit(struct master
 
 static void master_service_listen(struct master_service_listener *l)
 {
+	struct master_service *service = l->service;
 	struct master_service_connection conn;
 
-	if (l->service->master_status.available_count == 0) {
-		/* we are full. stop listening for now. */
-		io_listeners_remove(l->service);
-		return;
+	if (service->master_status.available_count == 0) {
+		/* we are full. stop listening for now, unless overflow
+		   callback destroys one of the existing connections */
+		if (service->call_avail_overflow &&
+		    service->avail_overflow_callback != NULL)
+			service->avail_overflow_callback();
+
+		if (service->master_status.available_count == 0) {
+			io_listeners_remove(service);
+			return;
+		}
 	}
 
 	memset(&conn, 0, sizeof(conn));
@@ -514,7 +527,7 @@ static void master_service_listen(struct
 
 		if (errno != ENOTSOCK) {
 			i_error("net_accept() failed: %m");
-			master_service_error(l->service);
+			master_service_error(service);
 			return;
 		}
 		/* it's not a socket. probably a fifo. use the "listener"
@@ -529,10 +542,10 @@ static void master_service_listen(struct
 	conn.ssl = l->ssl;
 	net_set_nonblock(conn.fd, TRUE);
 
-	l->service->master_status.available_count--;
-        master_status_update(l->service);
-
-        l->service->callback(&conn);
+	service->master_status.available_count--;
+        master_status_update(service);
+
+        service->callback(&conn);
 }
 
 static void io_listeners_init(struct master_service *service)
@@ -556,7 +569,7 @@ static void io_listeners_init(struct mas
 	}
 }
 
-static void io_listeners_add(struct master_service *service)
+void master_service_io_listeners_add(struct master_service *service)
 {
 	unsigned int i;
 
diff -r 2e94a44c34ff -r 77228b5431e1 src/lib-master/master-service.h
--- a/src/lib-master/master-service.h	Thu Sep 10 18:54:14 2009 -0400
+++ b/src/lib-master/master-service.h	Thu Sep 10 18:56:49 2009 -0400
@@ -56,6 +56,11 @@ void master_service_init_log(struct mast
    Normally all existing clients are handled first. */
 void master_service_set_die_with_master(struct master_service *service,
 					bool set);
+/* Call the given callback when there are no available connections and master
+   has indicated that it can't create any more processes to handle requests.
+   The callback could decide to kill one of the existing connections. */
+void master_service_set_avail_overflow_callback(struct master_service *service,
+						void (*callback)(void));
 
 /* Set maximum number of clients we can handle. Default is given by master. */
 void master_service_set_client_limit(struct master_service *service,
diff -r 2e94a44c34ff -r 77228b5431e1 src/login-common/client-common.c
--- a/src/login-common/client-common.c	Thu Sep 10 18:54:14 2009 -0400
+++ b/src/login-common/client-common.c	Thu Sep 10 18:56:49 2009 -0400
@@ -19,12 +19,7 @@
 
 #include <stdlib.h>
 
-/* When max. number of simultaneous connections is reached, few of the
-   oldest connections are disconnected. Since we have to go through all of the
-   clients, it's faster if we disconnect multiple clients. */
-#define CLIENT_DESTROY_OLDEST_COUNT 16
-
-struct client *clients = NULL;
+struct client *clients = NULL, *last_client = NULL;
 static unsigned int clients_count = 0;
 
 static void client_idle_disconnect_timeout(struct client *client)
@@ -50,12 +45,6 @@ struct client *client_create(int fd, boo
 	struct client *client;
 
 	i_assert(fd != -1);
-
-	if (clients_get_count() >= set->login_max_connections) {
-		/* reached max. users count, kill few of the
-		   oldest connections */
-		client_destroy_oldest();
-	}
 
 	/* always use nonblocking I/O */
 	net_set_nonblock(fd, TRUE);
@@ -80,6 +69,8 @@ struct client *client_create(int fd, boo
 	client->secured = ssl || client->trusted ||
 		net_ip_compare(remote_ip, local_ip);
 
+	if (last_client == NULL)
+		last_client = client;
 	DLLIST_PREPEND(&clients, client);
 	clients_count++;
 
@@ -114,6 +105,10 @@ void client_destroy(struct client *clien
 
 	i_assert(clients_count > 0);
 	clients_count--;
+	if (last_client == client) {
+		i_assert(client->prev != NULL || clients_count == 0);
+		last_client = client->prev;
+	}
 	DLLIST_REMOVE(&clients, client);
 
 	if (client->input != NULL)
@@ -213,39 +208,24 @@ bool client_unref(struct client *client)
 
 void client_destroy_oldest(void)
 {
-	unsigned int max_connections =
-		global_login_settings->login_max_connections;
 	struct client *client;
-	struct client *destroy_buf[CLIENT_DESTROY_OLDEST_COUNT];
-	unsigned int i, destroy_count;
-
-	/* find the oldest clients and put them to destroy-buffer */
-	memset(destroy_buf, 0, sizeof(destroy_buf));
-
-	destroy_count = max_connections > CLIENT_DESTROY_OLDEST_COUNT*2 ?
-		CLIENT_DESTROY_OLDEST_COUNT : I_MIN(max_connections/2, 1);
-	for (client = clients; client != NULL; client = client->next) {
-		for (i = 0; i < destroy_count; i++) {
-			if (destroy_buf[i] == NULL ||
-			    destroy_buf[i]->created > client->created) {
-				/* @UNSAFE */
-				memmove(destroy_buf+i+1, destroy_buf+i,
-					sizeof(destroy_buf) -
-					(i+1) * sizeof(destroy_buf[0]));
-				destroy_buf[i] = client;
-				break;
-			}
-		}
-	}
-
-	/* then kill them */
-	for (i = 0; i < destroy_count; i++) {
-		if (destroy_buf[i] == NULL)
+
+	if (last_client == NULL) {
+		/* we have no clients */
+		return;
+	}
+
+	/* destroy the last client that hasn't successfully authenticated yet.
+	   this is usually the last client, but don't kill it if it's just
+	   waiting for master to finish its job. */
+	for (client = last_client; client != NULL; client = client->prev) {
+		if (client->master_tag == 0)
 			break;
-
-		client_destroy(destroy_buf[i],
-			       "Disconnected: Connection queue full");
-	}
+	}
+	if (client == NULL)
+		client = last_client;
+
+	client_destroy(client, "Disconnected: Connection queue full");
 }
 
 void clients_destroy_all(void)
diff -r 2e94a44c34ff -r 77228b5431e1 src/login-common/login-settings.c
--- a/src/login-common/login-settings.c	Thu Sep 10 18:54:14 2009 -0400
+++ b/src/login-common/login-settings.c	Thu Sep 10 18:56:49 2009 -0400
@@ -39,7 +39,6 @@ static struct setting_define login_setti
 	DEF(SET_BOOL, auth_debug),
 	DEF(SET_BOOL, verbose_proctitle),
 
-	DEF(SET_UINT, login_max_connections),
 	DEF(SET_UINT, mail_max_userip_connections),
 
 	SETTING_DEFINE_LIST_END
@@ -70,7 +69,6 @@ static struct login_settings login_defau
 	MEMBER(auth_debug) FALSE,
 	MEMBER(verbose_proctitle) FALSE,


More information about the dovecot-cvs mailing list