ITS#10218 Disabling and re-enabling an asyncmeta database via cn=config leaks memory

Make sure asyncmeta frees the pending operations structures, resets all connections, frees connection structures and stops the timeout-loop.
This commit is contained in:
Nadezhda Ivanova 2024-05-23 15:54:04 +03:00 committed by Quanah Gibson-Mount
parent f0ab743db4
commit 5740d1747d
3 changed files with 75 additions and 48 deletions

View file

@ -427,6 +427,7 @@ typedef struct a_metainfo_t {
a_metaconn_t *mi_conns; a_metaconn_t *mi_conns;
struct berval mi_suffix; struct berval mi_suffix;
volatile int mi_disabled;
} a_metainfo_t; } a_metainfo_t;
typedef enum meta_op_type { typedef enum meta_op_type {
@ -780,6 +781,9 @@ asyncmeta_db_has_mscs(a_metainfo_t *mi);
void void
asyncmeta_target_free(a_metatarget_t *mt); asyncmeta_target_free(a_metatarget_t *mt);
void
asyncmeta_back_clear_miconns(a_metainfo_t *mi);
/* The the maximum time in seconds after a result has been received on a connection, /* The the maximum time in seconds after a result has been received on a connection,
* after which it can be reset if a sender error occurs. Should this be configurable? */ * after which it can be reset if a sender error occurs. Should this be configurable? */
#define META_BACK_RESULT_INTERVAL (2) #define META_BACK_RESULT_INTERVAL (2)

View file

@ -240,13 +240,13 @@ asyncmeta_back_db_open(
char msg[SLAP_TEXT_BUFLEN]; char msg[SLAP_TEXT_BUFLEN];
int i; int i;
mi->mi_disabled = 0;
if ( mi->mi_ntargets == 0 ) { if ( mi->mi_ntargets == 0 ) {
Debug( LDAP_DEBUG_ANY, Debug( LDAP_DEBUG_ANY,
"asyncmeta_back_db_open: no targets defined\n" ); "asyncmeta_back_db_open: no targets defined\n" );
} }
mi->mi_num_conns = 0;
for ( i = 0; i < mi->mi_ntargets; i++ ) { for ( i = 0; i < mi->mi_ntargets; i++ ) {
a_metatarget_t *mt = mi->mi_targets[ i ]; a_metatarget_t *mt = mi->mi_targets[ i ];
if ( asyncmeta_target_finish( mi, mt, if ( asyncmeta_target_finish( mi, mt,
@ -255,31 +255,34 @@ asyncmeta_back_db_open(
} }
} }
mi->mi_num_conns = (mi->mi_max_target_conns == 0) ? META_BACK_CFG_MAX_TARGET_CONNS : mi->mi_max_target_conns; if ( mi->mi_conns == NULL ) {
assert(mi->mi_num_conns > 0); mi->mi_num_conns = (mi->mi_max_target_conns == 0) ? META_BACK_CFG_MAX_TARGET_CONNS : mi->mi_max_target_conns;
mi->mi_conns = ch_calloc( mi->mi_num_conns, sizeof( a_metaconn_t )); assert(mi->mi_num_conns > 0);
for (i = 0; i < mi->mi_num_conns; i++) { mi->mi_conns = ch_calloc( mi->mi_num_conns, sizeof( a_metaconn_t ));
a_metaconn_t *mc = &mi->mi_conns[i]; for (i = 0; i < mi->mi_num_conns; i++) {
ldap_pvt_thread_mutex_init( &mc->mc_om_mutex); a_metaconn_t *mc = &mi->mi_conns[i];
mc->mc_authz_target = META_BOUND_NONE; ldap_pvt_thread_mutex_init( &mc->mc_om_mutex);
mc->mc_authz_target = META_BOUND_NONE;
if ( mi->mi_ntargets > 0 ) { if ( mi->mi_ntargets > 0 ) {
mc->mc_conns = ch_calloc( mi->mi_ntargets, sizeof( a_metasingleconn_t )); mc->mc_conns = ch_calloc( mi->mi_ntargets, sizeof( a_metasingleconn_t ));
} else { } else {
mc->mc_conns = NULL; mc->mc_conns = NULL;
}
mc->mc_info = mi;
LDAP_STAILQ_INIT( &mc->mc_om_list );
} }
mc->mc_info = mi;
LDAP_STAILQ_INIT( &mc->mc_om_list );
}
ber_dupbv ( &mi->mi_suffix, &be->be_suffix[0] );
if ( ( slapMode & SLAP_SERVER_MODE ) && mi->mi_ntargets > 0 ) { ber_dupbv ( &mi->mi_suffix, &be->be_suffix[0] );
ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
mi->mi_task = ldap_pvt_runqueue_insert( &slapd_rq, 1, if ( ( slapMode & SLAP_SERVER_MODE ) && mi->mi_ntargets > 0 ) {
asyncmeta_timeout_loop, mi, "asyncmeta_timeout_loop", mi->mi_suffix.bv_val ); ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex ); mi->mi_task = ldap_pvt_runqueue_insert( &slapd_rq, 1,
asyncmeta_timeout_loop, mi, "asyncmeta_timeout_loop", mi->mi_suffix.bv_val );
ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
}
} }
return 0; return 0;
} }
@ -301,28 +304,26 @@ asyncmeta_back_conn_free(
free( mc ); free( mc );
} }
static void void
asyncmeta_back_stop_miconns( a_metainfo_t *mi )
{
/*Todo do any other mc cleanup here if necessary*/
}
static void
asyncmeta_back_clear_miconns( a_metainfo_t *mi ) asyncmeta_back_clear_miconns( a_metainfo_t *mi )
{ {
int i, j; int i, j;
a_metaconn_t *mc; a_metaconn_t *mc;
for (i = 0; i < mi->mi_num_conns; i++) { if ( mi->mi_conns != NULL ) {
mc = &mi->mi_conns[i]; for (i = 0; i < mi->mi_num_conns; i++) {
/* todo clear the message queue */ mc = &mi->mi_conns[i];
for (j = 0; j < mi->mi_ntargets; j ++) { for (j = 0; j < mi->mi_ntargets; j ++) {
asyncmeta_clear_one_msc(NULL, mc, j, 1, __FUNCTION__); asyncmeta_clear_one_msc(NULL, mc, j, 1, __FUNCTION__);
}
if ( mc->mc_conns )
free( mc->mc_conns );
ldap_pvt_thread_mutex_destroy( &mc->mc_om_mutex );
} }
free(mc->mc_conns); free(mi->mi_conns);
ldap_pvt_thread_mutex_destroy( &mc->mc_om_mutex );
} }
free(mi->mi_conns); mi->mi_conns = NULL;
mi->mi_num_conns = 0;
} }
void void
@ -392,17 +393,22 @@ asyncmeta_back_db_close(
if ( be->be_private ) { if ( be->be_private ) {
mi = ( a_metainfo_t * )be->be_private; mi = ( a_metainfo_t * )be->be_private;
if ( mi->mi_task != NULL ) { mi->mi_disabled = 1;
ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex ); /* there are no pending ops so we can free up the connections and stop the timeout loop
if ( ldap_pvt_runqueue_isrunning( &slapd_rq, mi->mi_task )) { * else timeout_loop will clear up the ops and connections and not reschedule */
ldap_pvt_runqueue_stoptask( &slapd_rq, mi->mi_task); if ( asyncmeta_db_has_pending_ops( mi ) == 0 ) {
} ldap_pvt_thread_mutex_lock( &mi->mi_mc_mutex );
ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex ); asyncmeta_back_clear_miconns(mi);
mi->mi_task = NULL; ldap_pvt_thread_mutex_unlock( &mi->mi_mc_mutex );
if ( mi->mi_task != NULL ) {
ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
if ( ldap_pvt_runqueue_isrunning( &slapd_rq, mi->mi_task )) {
ldap_pvt_runqueue_stoptask( &slapd_rq, mi->mi_task);
}
ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
mi->mi_task = NULL;
}
} }
ldap_pvt_thread_mutex_lock( &mi->mi_mc_mutex );
asyncmeta_back_stop_miconns( mi );
ldap_pvt_thread_mutex_unlock( &mi->mi_mc_mutex );
} }
return 0; return 0;
} }

View file

@ -1487,6 +1487,11 @@ asyncmeta_op_handle_result(void *ctx, void *arg)
bm_context_t *bc; bm_context_t *bc;
void *oldctx; void *oldctx;
/* exit if the database is disabled, this will let timeout_loop
* do it's job faster */
if ( mc->mc_info->mi_disabled > 0 )
return NULL;
ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex ); ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
rc = ++mc->mc_active; rc = ++mc->mc_active;
ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex ); ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
@ -1661,6 +1666,14 @@ void* asyncmeta_timeout_loop(void *ctx, void *arg)
LDAP_STAILQ_INIT( &timeout_list ); LDAP_STAILQ_INIT( &timeout_list );
Debug( asyncmeta_debug, "asyncmeta_timeout_loop[%p] start at [%ld] \n", rtask, current_time ); Debug( asyncmeta_debug, "asyncmeta_timeout_loop[%p] start at [%ld] \n", rtask, current_time );
if ( mi->mi_disabled > 0 && asyncmeta_db_has_pending_ops( mi ) == 0 ) {
Debug( asyncmeta_debug, "asyncmeta_timeout_loop[%p] database disabled, clearing connections [%ld] \n", rtask, current_time );
ldap_pvt_thread_mutex_lock( &mi->mi_mc_mutex );
asyncmeta_back_clear_miconns( mi );
mi->mi_task = NULL;
ldap_pvt_thread_mutex_unlock( &mi->mi_mc_mutex );
return NULL;
}
void *oldctx = slap_sl_mem_create(SLAP_SLAB_SIZE, SLAP_SLAB_STACK, ctx, 0); void *oldctx = slap_sl_mem_create(SLAP_SLAB_SIZE, SLAP_SLAB_STACK, ctx, 0);
for (i=0; i<mi->mi_num_conns; i++) { for (i=0; i<mi->mi_num_conns; i++) {
a_metaconn_t * mc= &mi->mi_conns[i]; a_metaconn_t * mc= &mi->mi_conns[i];
@ -1671,6 +1684,10 @@ void* asyncmeta_timeout_loop(void *ctx, void *arg)
continue; continue;
} }
if (mi->mi_disabled > 0) {
bc->bc_invalid = 1;
}
if (bc->op->o_abandon ) { if (bc->op->o_abandon ) {
Operation *op = bc->op; Operation *op = bc->op;