627 lines
25 KiB
Diff
627 lines
25 KiB
Diff
From 26716f0edefe4690d9fd9143d07156e01321020c Mon Sep 17 00:00:00 2001
|
|
From: progier389 <progier@redhat.com>
|
|
Date: Thu, 28 Sep 2023 12:15:25 +0200
|
|
Subject: [PATCH] issue 5924 - ASAN server build crash when looping
|
|
opening/closing connections (#5926)
|
|
|
|
* issue 5924 - ASAN server build crash when looping opening/closing connections
|
|
Issue: Got a crash due to:
|
|
1. Failure to get a connection slot because connection freelist is misshandled.
|
|
2. A confusion between listening and acceptedfd descriptor leaded to
|
|
close the listening descriptor while handing the error.
|
|
|
|
Solution:
|
|
Rename clearly the file descriptor variables
|
|
Close the accepted file descriptor in error handler
|
|
Rewrite the freelist management so that first connection chosen is the last released one.
|
|
(Code is simpler, this fix the end of list issue, and it avoid to spread the open connection over too much memory)
|
|
|
|
Issue: #5924
|
|
|
|
Reviewed by: @Firstyear, @vashirov, @droideck (Thanks !)
|
|
|
|
(cherry picked from commit 02d333251419ff3c4d0384595e9fe7ded5bcd8fc)
|
|
---
|
|
dirsrvtests/tests/suites/basic/basic_test.py | 287 +++++++++++++++++++
|
|
ldap/servers/slapd/conntable.c | 100 +++----
|
|
ldap/servers/slapd/daemon.c | 35 ++-
|
|
ldap/servers/slapd/fe.h | 1 -
|
|
4 files changed, 351 insertions(+), 72 deletions(-)
|
|
|
|
diff --git a/dirsrvtests/tests/suites/basic/basic_test.py b/dirsrvtests/tests/suites/basic/basic_test.py
|
|
index fac0f7371e..f4525e1848 100644
|
|
--- a/dirsrvtests/tests/suites/basic/basic_test.py
|
|
+++ b/dirsrvtests/tests/suites/basic/basic_test.py
|
|
@@ -28,7 +28,15 @@
|
|
from lib389.backend import Backends
|
|
from lib389.idm.domain import Domain
|
|
from lib389.nss_ssl import NssSsl
|
|
+from lib389._constants import *
|
|
+from lib389 import DirSrv
|
|
+from lib389.instance.setup import SetupDs
|
|
+from lib389.instance.options import General2Base, Slapd2Base
|
|
import os
|
|
+import random
|
|
+import ldap
|
|
+import time
|
|
+import subprocess
|
|
|
|
|
|
pytestmark = pytest.mark.tier0
|
|
@@ -36,6 +44,7 @@
|
|
default_paths = Paths()
|
|
|
|
log = logging.getLogger(__name__)
|
|
+DEBUGGING = os.getenv("DEBUGGING", default=False)
|
|
|
|
# Globals
|
|
USER1_DN = 'uid=user1,' + DEFAULT_SUFFIX
|
|
@@ -51,6 +60,190 @@
|
|
'vendorName',
|
|
'vendorVersion')
|
|
|
|
+# This MAX_FDS value left about 22 connections available with bdb
|
|
+# (should have more connections with lmdb)
|
|
+MAX_FDS = 150
|
|
+
|
|
+
|
|
+
|
|
+default_paths = Paths()
|
|
+
|
|
+
|
|
+
|
|
+log = logging.getLogger(__name__)
|
|
+DEBUGGING = os.getenv("DEBUGGING", default=False)
|
|
+
|
|
+
|
|
+class CustomSetup():
|
|
+ DEFAULT_GENERAL = { 'config_version': 2,
|
|
+ 'full_machine_name': 'localhost.localdomain',
|
|
+ 'strict_host_checking': False,
|
|
+ # Not setting 'systemd' because it is not used.
|
|
+ # (that is the global default.inf setting that matters)
|
|
+ }
|
|
+ DEFAULT_SLAPD = { 'root_password': PW_DM,
|
|
+ 'defaults': INSTALL_LATEST_CONFIG,
|
|
+ }
|
|
+ DEFAULT_BACKENDS = [ {
|
|
+ 'cn': 'userroot',
|
|
+ 'nsslapd-suffix': DEFAULT_SUFFIX,
|
|
+ 'sample_entries': 'yes',
|
|
+ BACKEND_SAMPLE_ENTRIES: INSTALL_LATEST_CONFIG,
|
|
+ }, ]
|
|
+
|
|
+ WRAPPER_FORMAT = '''#!/bin/sh
|
|
+{wrapper_options}
|
|
+exec {nsslapd} -D {cfgdir} -i {pidfile}
|
|
+'''
|
|
+
|
|
+
|
|
+ class CustomDirSrv(DirSrv):
|
|
+ def __init__(self, verbose=False, external_log=log):
|
|
+ super().__init__(verbose=verbose, external_log=external_log)
|
|
+ self.wrapper = None # placeholder for the wrapper file name
|
|
+
|
|
+ def _reset_systemd(self):
|
|
+ self.systemd_override = False
|
|
+
|
|
+ def status(self):
|
|
+ self._reset_systemd()
|
|
+ return super().status()
|
|
+
|
|
+ def start(self, timeout=120, *args):
|
|
+ if self.status():
|
|
+ return
|
|
+ tmp_env = os.environ
|
|
+ # Unset PYTHONPATH to avoid mixing old CLI tools and new lib389
|
|
+ if "PYTHONPATH" in tmp_env:
|
|
+ del tmp_env["PYTHONPATH"]
|
|
+ try:
|
|
+ subprocess.check_call([
|
|
+ '/usr/bin/sh',
|
|
+ self.wrapper
|
|
+ ], env=tmp_env, stderr=subprocess.STDOUT)
|
|
+ except subprocess.CalledProcessError as e:
|
|
+ log.fatal("%s failed! Error (%s) %s" % (self.wrapper, e.returncode, e.output))
|
|
+ raise e from None
|
|
+ for count in range(timeout):
|
|
+ if self.status():
|
|
+ return
|
|
+ time.sleep(1)
|
|
+ raise TimeoutException('Failed to start ns-slpad')
|
|
+
|
|
+ def stop(self, timeout=120):
|
|
+ self._reset_systemd()
|
|
+ super().stop(timeout=timeout)
|
|
+
|
|
+
|
|
+ def _search_be(belist, beinfo):
|
|
+ for be in belist:
|
|
+ if be['cn'] == beinfo['cn']:
|
|
+ return be
|
|
+ return None
|
|
+
|
|
+ def __init__(self, serverid, general=None, slapd=None, backends=None, log=log):
|
|
+ verbose = log.level > logging.DEBUG
|
|
+ self.log = log
|
|
+ self.serverid = serverid
|
|
+ self.verbose = verbose
|
|
+ self.wrapper = f'/tmp/ds_{serverid}_wrapper.sh'
|
|
+ if serverid.startswith('slapd-'):
|
|
+ self.instname = server.id
|
|
+ else:
|
|
+ self.instname = 'slapd-'+serverid
|
|
+ self.ldapi = None
|
|
+ self.pid_file = None
|
|
+ self.inst = None
|
|
+
|
|
+ # Lets prepare the options
|
|
+ general_options = General2Base(log)
|
|
+ for d in (CustomSetup.DEFAULT_GENERAL, general):
|
|
+ if d:
|
|
+ for key,val in d.items():
|
|
+ general_options.set(key,val)
|
|
+ log.debug('[general]: %s' % general_options._options)
|
|
+ self.general = general_options
|
|
+
|
|
+ slapd_options = Slapd2Base(self.log)
|
|
+ slapd_options.set('instance_name', serverid)
|
|
+ for d in (CustomSetup.DEFAULT_SLAPD, slapd):
|
|
+ if d:
|
|
+ for key,val in d.items():
|
|
+ slapd_options.set(key,val)
|
|
+ log.debug('[slapd]: %s' % slapd_options._options)
|
|
+ self.slapd = slapd_options
|
|
+
|
|
+ backend_options = []
|
|
+ for backend_list in (CustomSetup.DEFAULT_BACKENDS, backends):
|
|
+ if not backend_list:
|
|
+ continue
|
|
+ for backend in backend_list:
|
|
+ target_be = CustomSetup._search_be(backend_options, backend)
|
|
+ if not target_be:
|
|
+ target_be = {}
|
|
+ backend_options.append(target_be)
|
|
+ for key,val in backend.items():
|
|
+ target_be[key] = val
|
|
+ log.debug('[backends]: %s' % backend_options)
|
|
+ self.backends = backend_options
|
|
+
|
|
+ def _to_dirsrv_args(self):
|
|
+ args = {}
|
|
+ slapd = self.slapd.collect()
|
|
+ general = self.general.collect()
|
|
+ args["SER_HOST"] = general['full_machine_name']
|
|
+ args["SER_PORT"] = slapd['port']
|
|
+ args["SER_SECURE_PORT"] = slapd['secure_port']
|
|
+ args["SER_SERVERID_PROP"] = self.serverid
|
|
+ return args
|
|
+
|
|
+ def create_instance(self):
|
|
+ sds = SetupDs(verbose=self.verbose, dryrun=False, log=self.log)
|
|
+ self.general.verify()
|
|
+ general = self.general.collect()
|
|
+ self.slapd.verify()
|
|
+ slapd = self.slapd.collect()
|
|
+ sds.create_from_args(general, slapd, self.backends, None)
|
|
+ self.ldapi = get_ldapurl_from_serverid(self.serverid)[0]
|
|
+ args = self._to_dirsrv_args()
|
|
+ log.debug('DirSrv.allocate args = %s' % str(args))
|
|
+ log.debug('ldapi = %s' % str(self.ldapi))
|
|
+ root_dn = slapd['root_dn']
|
|
+ root_password = slapd['root_password']
|
|
+ inst = DirSrv(verbose=self.verbose, external_log=self.log)
|
|
+ inst.local_simple_allocate(self.serverid, ldapuri=self.ldapi, binddn=root_dn, password=root_password)
|
|
+ self.pid_file = inst.pid_file()
|
|
+ # inst.setup_ldapi()
|
|
+ log.debug('DirSrv = %s' % str(inst.__dict__))
|
|
+ inst.open()
|
|
+ inst.stop()
|
|
+ inst = CustomSetup.CustomDirSrv(verbose=self.verbose, external_log=self.log)
|
|
+ inst.local_simple_allocate(self.serverid, ldapuri=self.ldapi, binddn=root_dn, password=root_password)
|
|
+ self.inst = inst
|
|
+ return inst
|
|
+
|
|
+ def create_wrapper(self, maxfds=None):
|
|
+ self.inst.wrapper = self.wrapper
|
|
+ slapd = self.slapd.collect()
|
|
+ sbin_dir = slapd['sbin_dir']
|
|
+ config_dir = slapd['config_dir']
|
|
+ fmtvalues = {
|
|
+ 'nsslapd': f'{sbin_dir}/ns-slapd',
|
|
+ 'cfgdir': config_dir.format(instance_name=self.instname),
|
|
+ 'pidfile': self.pid_file,
|
|
+ 'wrapper_options': ''
|
|
+ }
|
|
+ if maxfds:
|
|
+ fmtvalues['wrapper_options']=f'ulimit -n {maxfds}\nulimit -H -n {maxfds}'
|
|
+ with open(self.wrapper, 'w') as f:
|
|
+ f.write(CustomSetup.WRAPPER_FORMAT.format(**fmtvalues))
|
|
+
|
|
+ def cleanup(self):
|
|
+ self.inst.stop()
|
|
+ self.inst.delete()
|
|
+ if os.path.exists(self.wrapper):
|
|
+ os.remove(self.wrapper)
|
|
+
|
|
|
|
@pytest.fixture(scope="function")
|
|
def _reset_attr(request, topology_st):
|
|
@@ -2222,6 +2415,100 @@ def test_dscreate_with_different_rdn(dscreate_test_rdn_value):
|
|
assert True
|
|
|
|
|
|
+@pytest.fixture(scope="module")
|
|
+def dscreate_custom_instance(request):
|
|
+ topo = CustomSetup('custom')
|
|
+
|
|
+ def fin():
|
|
+ topo.cleanup()
|
|
+
|
|
+ request.addfinalizer(fin)
|
|
+ topo.create_instance()
|
|
+ # Return CustomSetup object associated with
|
|
+ # a stopped instance named "custom"
|
|
+ return topo
|
|
+
|
|
+ obj.create_wrapper(maxfds=150)
|
|
+ log.info("Starting wrapper")
|
|
+ inst.start()
|
|
+ log.info("Server is started.")
|
|
+ log.info("Open connection")
|
|
+ inst.open()
|
|
+
|
|
+
|
|
+@pytest.fixture(scope="module", params=set(range(1,5)))
|
|
+def dscreate_with_numlistener(request, dscreate_custom_instance):
|
|
+ numlisteners = request.param
|
|
+ dscreate_custom_instance.create_wrapper(maxfds=MAX_FDS)
|
|
+ inst = dscreate_custom_instance.inst
|
|
+ inst.stop()
|
|
+ dse_ldif = DSEldif(inst)
|
|
+ dse_ldif.replace('cn=config', 'nsslapd-numlisteners', str(numlisteners))
|
|
+ inst.start()
|
|
+ inst.open()
|
|
+ return inst
|
|
+
|
|
+
|
|
+@pytest.mark.skipif(ds_is_older('2.2.0.0'),
|
|
+ reason="This test is only required with multiple listener support.")
|
|
+def test_conn_limits(dscreate_with_numlistener):
|
|
+ """Check the connections limits for various number of listeners.
|
|
+
|
|
+ :id: 7be2eb5c-4d8f-11ee-ae3d-482ae39447e5
|
|
+ :parametrized: yes
|
|
+ :setup: Setup an instance then set nsslapd-numlisteners and maximum file descriptors
|
|
+ :steps:
|
|
+ 1. Loops on:
|
|
+ Open new connection and perform search until timeout expires
|
|
+ 2. Close one of the previously open connections
|
|
+ 3. Loops MAX_FDS times on:
|
|
+ - opening a new connection
|
|
+ - perform a search
|
|
+ - close the connection
|
|
+ 4. Close all open connections
|
|
+ 5. Remove the instance
|
|
+ :expectedresults:
|
|
+ 1. Should get a timeout (because the server has no more any connections)
|
|
+ 2. Should success
|
|
+ 3. Should success (otherwise issue #5924 has probably been hit)
|
|
+ 4. Should success
|
|
+ 5. Should success
|
|
+ """
|
|
+ inst = dscreate_with_numlistener
|
|
+
|
|
+ conns = []
|
|
+ timeout_occured = False
|
|
+ for i in range(MAX_FDS):
|
|
+ try:
|
|
+ ldc = ldap.initialize(f'ldap://localhost:{inst.port}')
|
|
+ ldc.set_option(ldap.OPT_TIMEOUT, 5)
|
|
+ ldc.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, "(uid=demo)")
|
|
+ conns.append(ldc)
|
|
+ except ldap.TIMEOUT:
|
|
+ timeout_occured = True
|
|
+ break
|
|
+ # Should not be able to open MAX_FDS connections (some file descriptor are
|
|
+ # reserved (for example for the listening socket )
|
|
+ assert timeout_occured
|
|
+
|
|
+ conn = random.choice(conns)
|
|
+ conn.unbind()
|
|
+ conns.remove(conn)
|
|
+
|
|
+ # Should loop enough time so trigger issue #5924 if it is not fixed.
|
|
+ for i in range(MAX_FDS):
|
|
+ ldc = ldap.initialize(f'ldap://localhost:{inst.port}')
|
|
+ # Set a timeout long enough so that the test fails if server is unresponsive
|
|
+ ldc.set_option(ldap.OPT_TIMEOUT, 60)
|
|
+ ldc.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, "(uid=demo)")
|
|
+ ldc.unbind()
|
|
+
|
|
+ # Close all open connections
|
|
+ for c in conns:
|
|
+ c.unbind()
|
|
+
|
|
+ # Step 6 is done in teardown phase by dscreate_instance finalizer
|
|
+
|
|
if __name__ == '__main__':
|
|
# Run isolated
|
|
# -s for DEBUG mode
|
|
diff --git a/ldap/servers/slapd/conntable.c b/ldap/servers/slapd/conntable.c
|
|
index 11e997432b..1ca60e0e5f 100644
|
|
--- a/ldap/servers/slapd/conntable.c
|
|
+++ b/ldap/servers/slapd/conntable.c
|
|
@@ -48,69 +48,59 @@
|
|
* under the connection table lock. This should move the allocation algorithm from O(n) worst case
|
|
* to O(1) worst case as we always recieve and empty slot *or* ct full. It also reduces lock/atomic
|
|
* contention on the CPU to improve things.
|
|
+ * Lastly a change was done: Instead of having a sliding windows that tend to get a never used
|
|
+ * slot for each new connection, it nows reuse last freed one. That has several benefits:
|
|
+ * - Fix a bug because the last free list slots may not be alloced.
|
|
+ * - Avoid to grow the memory footprint when there is no much load
|
|
+ * - Simplify the code (as only a single is needed. )
|
|
*
|
|
- * The freelist is a ringbuffer of pointers to the connection table. On a small scale it looks like:
|
|
+ * The freelist is a stack of pointers to the connection table.
|
|
+ * It is NULL terminated. On a small scale it looks like:
|
|
*
|
|
* |--------------------------------------------|
|
|
- * | slot 1 | slot 2 | slot 3 | slot 4 | slot 5 |
|
|
- * | _ ptr | _ ptr | _ ptr | _ ptr | _ ptr |
|
|
+ * | slot 0 | slot 1 | slot 2 | slot 3 | slot 4 |
|
|
+ * | _ ptr | _ ptr | _ ptr | _ ptr | NULL |
|
|
* |--------------------------------------------|
|
|
- * ^ ^- conn_next
|
|
- * |
|
|
- * \-- conn_free
|
|
+ * ^- conn_next
|
|
*
|
|
- * As we allocate, we shift conn_next through the list, yielding the ptr that was stored (and
|
|
- * setting it to NULL as we proceed)
|
|
+ * To allocate, we pop one of the stored connection ptr out of the stack (yield the ptr, set
|
|
+ * its slot to NULL then increase conn_next)
|
|
*
|
|
* |--------------------------------------------|
|
|
- * | slot 1 | slot 2 | slot 3 | slot 4 | slot 5 |
|
|
- * | _NULL | _NULL | _ ptr | _ ptr | _ ptr |
|
|
+ * | slot 0 | slot 1 | slot 2 | slot 3 | slot 4 |
|
|
+ * | _NULL | _NULL | _ ptr | _ ptr | NULL |
|
|
* |--------------------------------------------|
|
|
- * ^ ^- conn_next
|
|
- * |
|
|
- * \-- conn_free
|
|
+ * ^- conn_next
|
|
*
|
|
- * When a connection is "freed" we return it to conn_free, which is then also slid up.
|
|
+ * When a connection is "freed" we push it back in the stack after decreasing conn_next
|
|
*
|
|
* |--------------------------------------------|
|
|
- * | slot 1 | slot 2 | slot 3 | slot 4 | slot 5 |
|
|
- * | _ ptr | _NULL | _ ptr | _ ptr | _ ptr |
|
|
+ * | slot 0 | slot 1 | slot 2 | slot 3 | slot 4 |
|
|
+ * | _NULL | _ ptr | _ ptr | _ ptr | NULL |
|
|
* |--------------------------------------------|
|
|
- * ^ ^- conn_next
|
|
- * |
|
|
- * \-- conn_free
|
|
+ * ^- conn_next
|
|
*
|
|
- * If all connections are exhausted, conn_next will == conn_next, as conn_next must have proceeded
|
|
- * to the end of the ring, and then wrapped back allocating all previous until we meet with conn_free.
|
|
+ * If all connections are exhausted, freelist[conn_next] is NULL
|
|
*
|
|
* |--------------------------------------------|
|
|
- * | slot 1 | slot 2 | slot 3 | slot 4 | slot 5 |
|
|
+ * | slot 0 | slot 1 | slot 2 | slot 3 | slot 4 |
|
|
* | _NULL | _NULL | _NULL | _NULL | _NULL |
|
|
* |--------------------------------------------|
|
|
- * ^ ^- conn_next
|
|
- * |
|
|
- * \-- conn_free
|
|
+ * ^- conn_next
|
|
*
|
|
* This means allocations of conns will keep failing until a connection is returned.
|
|
*
|
|
* |--------------------------------------------|
|
|
- * | slot 1 | slot 2 | slot 3 | slot 4 | slot 5 |
|
|
- * | _NULL | _ ptr | _NULL | _NULL | _NULL |
|
|
+ * | slot 0 | slot 1 | slot 2 | slot 3 | slot 4 |
|
|
+ * | _NULL | _NULL | _NULL | _ ptr | NULL |
|
|
* |--------------------------------------------|
|
|
- * ^- conn_next ^
|
|
- * |
|
|
- * \-- conn_free
|
|
+ * ^- conn_next
|
|
*
|
|
* And now conn_next can begin to allocate again.
|
|
*
|
|
*
|
|
* -- invariants
|
|
- * * when conn_free is slid back to meet conn_next, there can be no situation where another
|
|
- * connection is returned, as none must allocated -if they were allocated, conn_free would have
|
|
- * moved_along.
|
|
- * * the ring buffer must be as large as conntable.
|
|
- * * We do not check conn_next == conn_free (that's the starting state), but we check if the
|
|
- * slot at conn_next is NULL, which must imply that conn_free has nothing to return.
|
|
+ * * the stack must be as large as conntable.
|
|
* * connection_table_move_connection_out_of_active_list is the only function able to return a
|
|
* connection to the freelist, as it is the function that is called when the event system has
|
|
* determined all IO's are complete, or unable to complete. This function is what prepares the
|
|
@@ -136,11 +126,9 @@ connection_table_new(int table_size)
|
|
ct->c = (Connection **)slapi_ch_calloc(1, ct->size * sizeof(Connection *));
|
|
ct->fd = (struct POLL_STRUCT **)slapi_ch_calloc(1, ct->list_num * sizeof(struct POLL_STRUCT*));
|
|
ct->table_mutex = PR_NewLock();
|
|
- /* Allocate the freelist */
|
|
- ct->c_freelist = (Connection **)slapi_ch_calloc(1, ct->size * sizeof(Connection *));
|
|
- /* NEVER use slot 0, this is a list pointer */
|
|
- ct->conn_next_offset = 1;
|
|
- ct->conn_free_offset = 1;
|
|
+ /* Allocate the freelist (a slot for each connection plus another slot for the final NULL pointer) */
|
|
+ ct->c_freelist = (Connection **)slapi_ch_calloc(1, (ct->size+1) * sizeof(Connection *));
|
|
+ ct->conn_next_offset = 0;
|
|
|
|
slapi_log_err(SLAPI_LOG_INFO, "connection_table_new", "Number of connection sub-tables %d, each containing %d slots.\n",
|
|
ct->list_num, ct->list_size);
|
|
@@ -273,22 +261,22 @@ connection_table_get_connection(Connection_Table *ct, int sd)
|
|
{
|
|
PR_Lock(ct->table_mutex);
|
|
|
|
- PR_ASSERT(ct->conn_next_offset != 0);
|
|
Connection *c = ct->c_freelist[ct->conn_next_offset];
|
|
if (c != NULL) {
|
|
/* We allocated it, so now NULL the slot and move forward. */
|
|
- ct->c_freelist[ct->conn_next_offset] = NULL;
|
|
- /* Handle overflow. */
|
|
- ct->conn_next_offset = (ct->conn_next_offset + 1) % ct->size;
|
|
- if (ct->conn_next_offset == 0) {
|
|
- /* Never use slot 0 */
|
|
- ct->conn_next_offset += 1;
|
|
- }
|
|
+ PR_ASSERT(ct->conn_next_offset>=0 && ct->conn_next_offset<ct->size);
|
|
+ ct->c_freelist[ct->conn_next_offset++] = NULL;
|
|
PR_Unlock(ct->table_mutex);
|
|
} else {
|
|
/* couldn't find a Connection, table must be full */
|
|
- slapi_log_err(SLAPI_LOG_CONNS, "connection_table_get_connection", "Max open connections reached\n");
|
|
PR_Unlock(ct->table_mutex);
|
|
+ static time_t last_err_msg_time = 0;
|
|
+ time_t curtime = slapi_current_utc_time();
|
|
+ /* Logs the message only once per seconds */
|
|
+ if (curtime != last_err_msg_time) {
|
|
+ slapi_log_err(SLAPI_LOG_ERR, "connection_table_get_connection", "Max open connections reached\n");
|
|
+ last_err_msg_time = curtime;
|
|
+ }
|
|
return NULL;
|
|
}
|
|
|
|
@@ -461,14 +449,10 @@ connection_table_move_connection_out_of_active_list(Connection_Table *ct, Connec
|
|
|
|
/* Finally, place the connection back into the freelist for use */
|
|
PR_ASSERT(c->c_refcnt == 0);
|
|
- PR_ASSERT(ct->conn_free_offset != 0);
|
|
- PR_ASSERT(ct->c_freelist[ct->conn_free_offset] == NULL);
|
|
- ct->c_freelist[ct->conn_free_offset] = c;
|
|
- ct->conn_free_offset = (ct->conn_free_offset + 1) % ct->size;
|
|
- if (ct->conn_free_offset == 0) {
|
|
- /* Never use slot 0 */
|
|
- ct->conn_free_offset += 1;
|
|
- }
|
|
+ PR_ASSERT(ct->conn_next_offset != 0);
|
|
+ ct->conn_next_offset--;
|
|
+ PR_ASSERT(ct->c_freelist[ct->conn_next_offset] == NULL);
|
|
+ ct->c_freelist[ct->conn_next_offset] = c;
|
|
|
|
PR_Unlock(ct->table_mutex);
|
|
|
|
diff --git a/ldap/servers/slapd/daemon.c b/ldap/servers/slapd/daemon.c
|
|
index e8b979acaf..10aabed6d3 100644
|
|
--- a/ldap/servers/slapd/daemon.c
|
|
+++ b/ldap/servers/slapd/daemon.c
|
|
@@ -135,19 +135,25 @@ get_pid_file(void)
|
|
}
|
|
|
|
static int
|
|
-accept_and_configure(int s __attribute__((unused)), PRFileDesc *pr_acceptfd, PRNetAddr *pr_netaddr, int addrlen __attribute__((unused)), int secure, int local, PRFileDesc **pr_clonefd)
|
|
+accept_and_configure(int s __attribute__((unused)), PRFileDesc *listenfd, PRNetAddr *pr_netaddr, int addrlen __attribute__((unused)), int secure, int local, PRFileDesc **pr_accepted_fd)
|
|
{
|
|
int ns = 0;
|
|
PRIntervalTime pr_timeout = PR_MillisecondsToInterval(slapd_accept_wakeup_timer);
|
|
|
|
- (*pr_clonefd) = PR_Accept(pr_acceptfd, pr_netaddr, pr_timeout);
|
|
- if (!(*pr_clonefd)) {
|
|
+ (*pr_accepted_fd) = PR_Accept(listenfd, pr_netaddr, pr_timeout);
|
|
+ if (!(*pr_accepted_fd)) {
|
|
PRErrorCode prerr = PR_GetError();
|
|
- slapi_log_err(SLAPI_LOG_ERR, "accept_and_configure", "PR_Accept() failed, " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
|
|
- prerr, slapd_pr_strerror(prerr));
|
|
+ static time_t last_err_msg_time = 0;
|
|
+ time_t curtime = slapi_current_utc_time();
|
|
+ /* Logs the message only once per seconds */
|
|
+ if (curtime != last_err_msg_time) {
|
|
+ slapi_log_err(SLAPI_LOG_ERR, "accept_and_configure", "PR_Accept() failed, " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
|
|
+ prerr, slapd_pr_strerror(prerr));
|
|
+ last_err_msg_time = curtime;
|
|
+ }
|
|
return (SLAPD_INVALID_SOCKET);
|
|
}
|
|
- ns = configure_pr_socket(pr_clonefd, secure, local);
|
|
+ ns = configure_pr_socket(pr_accepted_fd, secure, local);
|
|
|
|
return ns;
|
|
}
|
|
@@ -155,7 +161,7 @@ accept_and_configure(int s __attribute__((unused)), PRFileDesc *pr_acceptfd, PRN
|
|
/*
|
|
* This is the shiny new re-born daemon function, without all the hair
|
|
*/
|
|
-static int handle_new_connection(Connection_Table *ct, int tcps, PRFileDesc *pr_acceptfd, int secure, int local, Connection **newconn);
|
|
+static int handle_new_connection(Connection_Table *ct, int tcps, PRFileDesc *listenfd, int secure, int local, Connection **newconn);
|
|
static void handle_pr_read_ready(Connection_Table *ct, int list_id, PRIntn num_poll);
|
|
static int clear_signal(struct POLL_STRUCT *fds, int list_id);
|
|
static void unfurl_banners(Connection_Table *ct, daemon_ports_t *ports, PRFileDesc **n_tcps, PRFileDesc **s_tcps, PRFileDesc **i_unix);
|
|
@@ -831,6 +837,7 @@ accept_thread(void *vports)
|
|
}
|
|
/* Need a sleep delay here. */
|
|
PR_Sleep(pr_timeout);
|
|
+ last_accept_new_connections = accept_new_connections;
|
|
continue;
|
|
} else {
|
|
/* Log that we are now listening again */
|
|
@@ -1846,28 +1853,30 @@ handle_closed_connection(Connection *conn)
|
|
* this function returns the connection table list the new connection is in
|
|
*/
|
|
static int
|
|
-handle_new_connection(Connection_Table *ct, int tcps, PRFileDesc *pr_acceptfd, int secure, int local, Connection **newconn)
|
|
+handle_new_connection(Connection_Table *ct, int tcps, PRFileDesc *listenfd, int secure, int local, Connection **newconn)
|
|
{
|
|
int ns = 0;
|
|
Connection *conn = NULL;
|
|
/* struct sockaddr_in from;*/
|
|
PRNetAddr from = {{0}};
|
|
- PRFileDesc *pr_clonefd = NULL;
|
|
+ PRFileDesc *pr_accepted_fd = NULL;
|
|
slapdFrontendConfig_t *fecfg = getFrontendConfig();
|
|
ber_len_t maxbersize;
|
|
|
|
if (newconn) {
|
|
*newconn = NULL;
|
|
}
|
|
- if ((ns = accept_and_configure(tcps, pr_acceptfd, &from,
|
|
- sizeof(from), secure, local, &pr_clonefd)) == SLAPD_INVALID_SOCKET) {
|
|
+ if ((ns = accept_and_configure(tcps, listenfd, &from,
|
|
+ sizeof(from), secure, local, &pr_accepted_fd)) == SLAPD_INVALID_SOCKET) {
|
|
return -1;
|
|
}
|
|
|
|
/* get a new Connection from the Connection Table */
|
|
conn = connection_table_get_connection(ct, ns);
|
|
if (conn == NULL) {
|
|
- PR_Close(pr_acceptfd);
|
|
+ if (pr_accepted_fd) {
|
|
+ PR_Close(pr_accepted_fd);
|
|
+ }
|
|
return -1;
|
|
}
|
|
pthread_mutex_lock(&(conn->c_mutex));
|
|
@@ -1879,7 +1888,7 @@ handle_new_connection(Connection_Table *ct, int tcps, PRFileDesc *pr_acceptfd, i
|
|
conn->c_idletimeout = fecfg->idletimeout;
|
|
conn->c_idletimeout_handle = idletimeout_reslimit_handle;
|
|
conn->c_sd = ns;
|
|
- conn->c_prfd = pr_clonefd;
|
|
+ conn->c_prfd = pr_accepted_fd;
|
|
conn->c_flags &= ~CONN_FLAG_CLOSING;
|
|
|
|
/* Set per connection static config */
|
|
diff --git a/ldap/servers/slapd/fe.h b/ldap/servers/slapd/fe.h
|
|
index 64c6645e7b..95dfeeb89b 100644
|
|
--- a/ldap/servers/slapd/fe.h
|
|
+++ b/ldap/servers/slapd/fe.h
|
|
@@ -88,7 +88,6 @@ struct connection_table
|
|
/* An array of free connections awaiting allocation. */;
|
|
Connection **c_freelist;
|
|
size_t conn_next_offset;
|
|
- size_t conn_free_offset;
|
|
struct POLL_STRUCT **fd;
|
|
PRLock *table_mutex;
|
|
};
|