diff --git a/CHANGES b/CHANGES index 2bb9ca2e25d358b0394a601ac05ed74fff1229d1..22caa824763e3ff9ede86a51aa8f6faa74bb9ae7 100644 --- a/CHANGES +++ b/CHANGES @@ -11,6 +11,8 @@ OpenLDAP 2.4.14 Engineering Added slapo-rwm newRDN rewriting (ITS#5834) Fixed slapd bconfig to return error codes (ITS#5867) Fixed slapd bconfig encoding incorrectly (ITS#5897) + Fixed slapd connection assert (ITS#5835) + Fixed slapd epoll handling (ITS#5886) Fixed slapd syncrepl rename handling (ITS#5809) Fixed slapd syncrepl MMR when adding new server (ITS#5850) Fixed slapd syncrepl replication with glued DB (ITS#5866) diff --git a/servers/slapd/connection.c b/servers/slapd/connection.c index 4c3178e338c74a58b00cc00beabccb38e8f3259d..7c40df58584347e6ccef8207743785f25db82a35 100644 --- a/servers/slapd/connection.c +++ b/servers/slapd/connection.c @@ -163,8 +163,10 @@ int connections_destroy(void) if( connections[i].c_struct_state != SLAP_C_UNINITIALIZED ) { ber_sockbuf_free( connections[i].c_sb ); ldap_pvt_thread_mutex_destroy( &connections[i].c_mutex ); - ldap_pvt_thread_mutex_destroy( &connections[i].c_write_mutex ); - ldap_pvt_thread_cond_destroy( &connections[i].c_write_cv ); + ldap_pvt_thread_mutex_destroy( &connections[i].c_write1_mutex ); + ldap_pvt_thread_mutex_destroy( &connections[i].c_write2_mutex ); + ldap_pvt_thread_cond_destroy( &connections[i].c_write1_cv ); + ldap_pvt_thread_cond_destroy( &connections[i].c_write2_cv ); #ifdef LDAP_SLAPI if ( slapi_plugins_used ) { slapi_int_free_object_extensions( SLAPI_X_EXT_CONNECTION, @@ -384,8 +386,10 @@ Connection * connection_init( /* should check status of thread calls */ ldap_pvt_thread_mutex_init( &c->c_mutex ); - ldap_pvt_thread_mutex_init( &c->c_write_mutex ); - ldap_pvt_thread_cond_init( &c->c_write_cv ); + ldap_pvt_thread_mutex_init( &c->c_write1_mutex ); + ldap_pvt_thread_mutex_init( &c->c_write2_mutex ); + ldap_pvt_thread_cond_init( &c->c_write1_cv ); + ldap_pvt_thread_cond_init( &c->c_write2_cv ); #ifdef LDAP_SLAPI if ( slapi_plugins_used ) { @@ -417,6 +421,7 @@ Connection * connection_init( assert( c->c_sasl_bindop == NULL ); assert( c->c_currentber == NULL ); assert( c->c_writewaiter == 0); + assert( c->c_writers == 0); c->c_listener = listener; c->c_sd = s; @@ -593,6 +598,7 @@ connection_destroy( Connection *c ) assert( LDAP_STAILQ_EMPTY(&c->c_txn_ops) ); #endif assert( c->c_writewaiter == 0); + assert( c->c_writers == 0); /* only for stats (print -1 as "%lu" may give unexpected results ;) */ connid = c->c_connid; @@ -752,18 +758,24 @@ void connection_closing( Connection *c, const char *why ) connection_abandon( c ); /* wake write blocked operations */ - if ( c->c_writewaiter ) { - ldap_pvt_thread_cond_signal( &c->c_write_cv ); - /* ITS#4667 this may allow another thread to drop into - * connection_resched / connection_close before we - * finish, but that's OK. - */ - slapd_clr_write( c->c_sd, 1 ); - ldap_pvt_thread_mutex_unlock( &c->c_mutex ); - ldap_pvt_thread_mutex_lock( &c->c_write_mutex ); - ldap_pvt_thread_mutex_lock( &c->c_mutex ); - ldap_pvt_thread_mutex_unlock( &c->c_write_mutex ); + ldap_pvt_thread_mutex_lock( &c->c_write1_mutex ); + if ( c->c_writers > 0 ) { + c->c_writers = -c->c_writers; + ldap_pvt_thread_cond_broadcast( &c->c_write1_cv ); + ldap_pvt_thread_mutex_unlock( &c->c_write1_mutex ); + if ( c->c_writewaiter ) { + ldap_pvt_thread_mutex_lock( &c->c_write2_mutex ); + ldap_pvt_thread_cond_signal( &c->c_write2_cv ); + slapd_clr_write( c->c_sd, 1 ); + ldap_pvt_thread_mutex_unlock( &c->c_write2_mutex ); + } + ldap_pvt_thread_mutex_lock( &c->c_write1_mutex ); + while ( c->c_writers ) { + ldap_pvt_thread_cond_wait( &c->c_write1_cv, &c->c_write1_mutex ); + } + ldap_pvt_thread_mutex_unlock( &c->c_write1_mutex ); } else { + ldap_pvt_thread_mutex_unlock( &c->c_write1_mutex ); slapd_clr_write( c->c_sd, 1 ); } @@ -778,11 +790,6 @@ connection_close( Connection *c ) { assert( connections != NULL ); assert( c != NULL ); - - /* ITS#4667 we may have gotten here twice */ - if ( c->c_conn_state == SLAP_C_INVALID ) - return; - assert( c->c_struct_state == SLAP_C_USED ); assert( c->c_conn_state == SLAP_C_CLOSING ); @@ -1174,7 +1181,7 @@ void connection_client_stop( /* get (locked) connection */ c = connection_get( s ); - + assert( c->c_conn_state == SLAP_C_CLIENT ); c->c_listener = NULL; @@ -1246,6 +1253,24 @@ int connection_read_activate( ber_socket_t s ) return rc; } +void +connection_hangup( ber_socket_t s ) +{ + Connection *c; + + c = connection_get( s ); + if ( c ) { + if ( c->c_conn_state == SLAP_C_CLIENT ) { + connection_return( c ); + connection_read_activate( s ); + } else { + connection_closing( c, "connection lost" ); + connection_close( c ); + connection_return( c ); + } + } +} + static int connection_read( ber_socket_t s, conn_readinfo *cri ) { @@ -1831,7 +1856,7 @@ int connection_write(ber_socket_t s) Debug( LDAP_DEBUG_TRACE, "connection_write(%d): waking output for id=%lu\n", s, c->c_connid, 0 ); - ldap_pvt_thread_cond_signal( &c->c_write_cv ); + ldap_pvt_thread_cond_signal( &c->c_write2_cv ); if ( ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_NEEDS_READ, NULL ) ) { slapd_set_read( s, 1 ); diff --git a/servers/slapd/daemon.c b/servers/slapd/daemon.c index fea9d35e2324ee5f9c57fda37ecb9922e10d709a..7f953e4d329abe40b4d3f6ed0e04da5a9d590839 100644 --- a/servers/slapd/daemon.c +++ b/servers/slapd/daemon.c @@ -2495,7 +2495,7 @@ slapd_daemon_task( #endif /* LDAP_DEBUG */ for ( i = 0; i < ns; i++ ) { - int rc = 1, fd; + int rc = 1, fd, w = 0; if ( SLAP_EVENT_IS_LISTENER( i ) ) { rc = slap_listener_activate( SLAP_EVENT_LISTENER( i ) ); @@ -2522,6 +2522,7 @@ slapd_daemon_task( fd, 0, 0 ); SLAP_EVENT_CLR_WRITE( i ); + w = 1; /* * NOTE: it is possible that the connection was closed @@ -2541,9 +2542,10 @@ slapd_daemon_task( SLAP_EVENT_CLR_READ( i ); connection_read_activate( fd ); - } else { + } else if ( !w ) { Debug( LDAP_DEBUG_CONNS, "daemon: hangup on %d\n", fd, 0, 0 ); + connection_hangup( fd ); } } } diff --git a/servers/slapd/proto-slap.h b/servers/slapd/proto-slap.h index 5d07c4c259e5ef59a158b501552d38ba7128fab7..4b17b54a59f942ac9a4ee271118178339f09a4a9 100644 --- a/servers/slapd/proto-slap.h +++ b/servers/slapd/proto-slap.h @@ -745,6 +745,7 @@ LDAP_SLAPD_F (Connection *) connection_init LDAP_P(( LDAP_SLAPD_F (void) connection_closing LDAP_P(( Connection *c, const char *why )); +LDAP_SLAPD_F (void) connection_hangup LDAP_P(( ber_socket_t fd )); LDAP_SLAPD_F (int) connection_state_closing LDAP_P(( Connection *c )); LDAP_SLAPD_F (const char *) connection_state2str LDAP_P(( int state )) LDAP_GCCATTR((const)); diff --git a/servers/slapd/result.c b/servers/slapd/result.c index a2e7343eb6ceca4568d990848114e4a317525405..c9351fb47776d023eb0c7d1c2dc8bfe2e0dc9b58 100644 --- a/servers/slapd/result.c +++ b/servers/slapd/result.c @@ -137,27 +137,44 @@ static long send_ldap_ber( BerElement *ber ) { ber_len_t bytes; + long ret = 0; + int closing = 0; ber_get_option( ber, LBER_OPT_BER_BYTES_TO_WRITE, &bytes ); /* write only one pdu at a time - wait til it's our turn */ - ldap_pvt_thread_mutex_lock( &conn->c_write_mutex ); + ldap_pvt_thread_mutex_lock( &conn->c_write1_mutex ); + if ( connection_state_closing( conn )) { + ldap_pvt_thread_mutex_unlock( &conn->c_write1_mutex ); + return 0; + } + while ( conn->c_writers > 0 ) { + ldap_pvt_thread_cond_wait( &conn->c_write1_cv, &conn->c_write1_mutex ); + } + /* connection was closed under us */ + if ( conn->c_writers < 0 ) { + closing = 1; + /* we're the last waiter, let the closer continue */ + if ( conn->c_writers == -1 ) + ldap_pvt_thread_cond_signal( &conn->c_write1_cv ); + } - /* lock the connection */ - ldap_pvt_thread_mutex_lock( &conn->c_mutex ); + conn->c_writers++; + ldap_pvt_thread_mutex_unlock( &conn->c_write1_mutex ); + + if ( closing ) + return 0; /* write the pdu */ while( 1 ) { int err; - if ( connection_state_closing( conn ) ) { - ldap_pvt_thread_mutex_unlock( &conn->c_mutex ); - ldap_pvt_thread_mutex_unlock( &conn->c_write_mutex ); - - return 0; - } + /* lock the connection */ + ldap_pvt_thread_mutex_lock( &conn->c_mutex ); if ( ber_flush2( conn->c_sb, ber, LBER_FLUSH_FREE_NEVER ) == 0 ) { + ldap_pvt_thread_mutex_unlock( &conn->c_mutex ); + ret = bytes; break; } @@ -176,23 +193,41 @@ static long send_ldap_ber( connection_closing( conn, "connection lost on write" ); ldap_pvt_thread_mutex_unlock( &conn->c_mutex ); - ldap_pvt_thread_mutex_unlock( &conn->c_write_mutex ); - return( -1 ); + ret = -1; + break; } /* wait for socket to be write-ready */ + ldap_pvt_thread_mutex_lock( &conn->c_write2_mutex ); conn->c_writewaiter = 1; slapd_set_write( conn->c_sd, 1 ); - ldap_pvt_thread_cond_wait( &conn->c_write_cv, &conn->c_mutex ); + ldap_pvt_thread_mutex_unlock( &conn->c_mutex ); + ldap_pvt_thread_cond_wait( &conn->c_write2_cv, &conn->c_write2_mutex ); conn->c_writewaiter = 0; + ldap_pvt_thread_mutex_unlock( &conn->c_write2_mutex ); + ldap_pvt_thread_mutex_lock( &conn->c_write1_mutex ); + closing = ( conn->c_writers < 0 ); + ldap_pvt_thread_mutex_unlock( &conn->c_write1_mutex ); + if ( closing ) { + ret = 0; + break; + } } - ldap_pvt_thread_mutex_unlock( &conn->c_mutex ); - ldap_pvt_thread_mutex_unlock( &conn->c_write_mutex ); + ldap_pvt_thread_mutex_lock( &conn->c_write1_mutex ); + if ( conn->c_writers < 0 ) { + conn->c_writers++; + if ( !conn->c_writers ) + ldap_pvt_thread_cond_signal( &conn->c_write1_cv ); + } else { + conn->c_writers--; + ldap_pvt_thread_cond_signal( &conn->c_write1_cv ); + } + ldap_pvt_thread_mutex_unlock( &conn->c_write1_mutex ); - return bytes; + return ret; } static int diff --git a/servers/slapd/slap.h b/servers/slapd/slap.h index dadd88d5c92db7cc444587eeb1c056e5d29f7468..b4e50e39123094c08e7a4f01222d2da3f55aa77c 100644 --- a/servers/slapd/slap.h +++ b/servers/slapd/slap.h @@ -2801,14 +2801,17 @@ struct Connection { LDAP_STAILQ_HEAD(c_o, Operation) c_ops; /* list of operations being processed */ LDAP_STAILQ_HEAD(c_po, Operation) c_pending_ops; /* list of pending operations */ - ldap_pvt_thread_mutex_t c_write_mutex; /* only one pdu written at a time */ - ldap_pvt_thread_cond_t c_write_cv; /* used to wait for sd write-ready*/ + ldap_pvt_thread_mutex_t c_write1_mutex; /* only one pdu written at a time */ + ldap_pvt_thread_cond_t c_write1_cv; /* only one pdu written at a time */ + ldap_pvt_thread_mutex_t c_write2_mutex; /* used to wait for sd write-ready */ + ldap_pvt_thread_cond_t c_write2_cv; /* used to wait for sd write-ready*/ BerElement *c_currentber; /* ber we're attempting to read */ + int c_writers; /* number of writers waiting */ char c_sasl_bind_in_progress; /* multi-op bind in progress */ + char c_writewaiter; /* true if blocked on write */ - char c_writewaiter; /* true if writer is waiting */ #define CONN_IS_TLS 1 #define CONN_IS_UDP 2 diff --git a/servers/slapd/slapi/slapi_ops.c b/servers/slapd/slapi/slapi_ops.c index e7aab5d8d370532d92b2ed06f8a443c49777ef85..eaf4060b01739d929894a98c9ac0191b9a3bf269 100644 --- a/servers/slapd/slapi/slapi_ops.c +++ b/servers/slapd/slapi/slapi_ops.c @@ -224,8 +224,10 @@ slapi_int_connection_init_pb( Slapi_PBlock *pb, ber_tag_t tag ) /* should check status of thread calls */ ldap_pvt_thread_mutex_init( &conn->c_mutex ); - ldap_pvt_thread_mutex_init( &conn->c_write_mutex ); - ldap_pvt_thread_cond_init( &conn->c_write_cv ); + ldap_pvt_thread_mutex_init( &conn->c_write1_mutex ); + ldap_pvt_thread_mutex_init( &conn->c_write2_mutex ); + ldap_pvt_thread_cond_init( &conn->c_write1_cv ); + ldap_pvt_thread_cond_init( &conn->c_write2_cv ); ldap_pvt_thread_mutex_lock( &conn->c_mutex );