From bc05508d28d6520002fcdc173bfa8e85b95e8a9f Mon Sep 17 00:00:00 2001 From: Quanah Gibson-Mount <quanah@openldap.org> Date: Thu, 7 May 2009 22:20:32 +0000 Subject: [PATCH] ITS#6095 --- CHANGES | 5 +- servers/slapd/back-bdb/back-bdb.h | 1 + servers/slapd/back-bdb/cache.c | 171 +++++++++++++++++------------ servers/slapd/back-bdb/dn2id.c | 12 +- servers/slapd/back-bdb/proto-bdb.h | 6 +- servers/slapd/back-bdb/search.c | 9 +- 6 files changed, 123 insertions(+), 81 deletions(-) diff --git a/CHANGES b/CHANGES index 5f827b7d44..386df8fced 100644 --- a/CHANGES +++ b/CHANGES @@ -12,8 +12,9 @@ OpenLDAP 2.4.17 Engineering Fixed slapd normalization of updated schema attributes (ITS#5540) Fixed slapd pagedresults stacked control with overlays (ITS#6056) Fixed slapd sockets usage on windows (ITS#6039) - Fixed slapd-bdb freeing of already freed entries (ITS#6074) - Fixed slapd-bdb entryinfo cleanup (ITS#6088) + Fixed slapd-hdb freeing of already freed entries (ITS#6074) + Fixed slapd-hdb entryinfo cleanup (ITS#6088) + Fixed slapd-hdb dncache lockups (ITS#6095) Added slapo-rwm rwm-drop-unrequested-attrs config option (ITS#6057) Fixed slapo-rwm dn passing (ITS#6070) Fixed slapo-rwm entry free (ITS#6058) diff --git a/servers/slapd/back-bdb/back-bdb.h b/servers/slapd/back-bdb/back-bdb.h index 0219e3ff14..16bc3dc353 100644 --- a/servers/slapd/back-bdb/back-bdb.h +++ b/servers/slapd/back-bdb/back-bdb.h @@ -92,6 +92,7 @@ typedef struct bdb_entry_info { #define CACHE_ENTRY_ONELEVEL 0x40 #define CACHE_ENTRY_REFERENCED 0x80 #define CACHE_ENTRY_NOT_CACHED 0x100 +#define CACHE_ENTRY_PURGED 0x200 int bei_finders; /* diff --git a/servers/slapd/back-bdb/cache.c b/servers/slapd/back-bdb/cache.c index a20d772026..216816c6ca 100644 --- a/servers/slapd/back-bdb/cache.c +++ b/servers/slapd/back-bdb/cache.c @@ -94,10 +94,14 @@ bdb_cache_entryinfo_free( Cache *cache, EntryInfo *ei ) ei->bei_kids = NULL; ei->bei_lruprev = NULL; +#if 0 ldap_pvt_thread_mutex_lock( &cache->c_eifree_mutex ); ei->bei_lrunext = cache->c_eifree; cache->c_eifree = ei; ldap_pvt_thread_mutex_unlock( &cache->c_eifree_mutex ); +#else + ch_free( ei ); +#endif } #define LRU_DEL( c, e ) do { \ @@ -334,8 +338,8 @@ bdb_entryinfo_add_internal( ei2 = bdb_cache_entryinfo_new( &bdb->bi_cache ); - ldap_pvt_thread_rdwr_wlock( &bdb->bi_cache.c_rwlock ); bdb_cache_entryinfo_lock( ei->bei_parent ); + ldap_pvt_thread_rdwr_wlock( &bdb->bi_cache.c_rwlock ); ei2->bei_id = ei->bei_id; ei2->bei_parent = ei->bei_parent; @@ -372,13 +376,11 @@ bdb_entryinfo_add_internal( bdb->bi_cache.c_leaves++; rc = avl_insert( &ei->bei_parent->bei_kids, ei2, bdb_rdn_cmp, avl_dup_error ); - if ( rc ) { - /* This should never happen; entry cache is corrupt */ - bdb->bi_dbenv->log_flush( bdb->bi_dbenv, NULL ); - assert( !rc ); - } #ifdef BDB_HIER - ei->bei_parent->bei_ckids++; + /* it's possible for hdb_cache_find_parent to beat us to it */ + if ( !rc ) { + ei->bei_parent->bei_ckids++; + } #endif } @@ -443,6 +445,7 @@ bdb_cache_find_ndn( ei.bei_nrdn.bv_len = ndn->bv_len - (ei.bei_nrdn.bv_val - ndn->bv_val); + eip->bei_finders++; bdb_cache_entryinfo_unlock( eip ); BDB_LOG_PRINTF( bdb->bi_dbenv, NULL, "slapd Reading %s", @@ -452,6 +455,7 @@ bdb_cache_find_ndn( rc = bdb_dn2id( op, &ei.bei_nrdn, &ei, txn, &lock ); if (rc) { bdb_cache_entryinfo_lock( eip ); + eip->bei_finders--; bdb_cache_entry_db_unlock( bdb, &lock ); *res = eip; return rc; @@ -464,6 +468,7 @@ bdb_cache_find_ndn( ei.bei_nrdn.bv_len = len; rc = bdb_entryinfo_add_internal( bdb, &ei, &ei2 ); /* add_internal left eip and c_rwlock locked */ + eip->bei_finders--; ldap_pvt_thread_rdwr_wunlock( &bdb->bi_cache.c_rwlock ); bdb_cache_entry_db_unlock( bdb, &lock ); if ( rc ) { @@ -480,8 +485,8 @@ bdb_cache_find_ndn( *res = eip; return DB_NOTFOUND; } - bdb_cache_entryinfo_unlock( eip ); bdb_cache_entryinfo_lock( ei2 ); + bdb_cache_entryinfo_unlock( eip ); eip = ei2; @@ -515,7 +520,7 @@ hdb_cache_find_parent( { struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private; EntryInfo ei, eip, *ei2 = NULL, *ein = NULL, *eir = NULL; - int rc; + int rc, add; ei.bei_id = id; ei.bei_kids = NULL; @@ -539,36 +544,53 @@ hdb_cache_find_parent( ein->bei_bdb = bdb; #endif ei.bei_ckids = 0; + add = 1; /* This node is not fully connected yet */ ein->bei_state |= CACHE_ENTRY_NOT_LINKED; + /* If this is the first time, save this node + * to be returned later. + */ + if ( eir == NULL ) { + eir = ein; + ein->bei_finders++; + } + +again: /* Insert this node into the ID tree */ ldap_pvt_thread_rdwr_wlock( &bdb->bi_cache.c_rwlock ); if ( avl_insert( &bdb->bi_cache.c_idtree, (caddr_t)ein, bdb_id_cmp, bdb_id_dup_err ) ) { EntryInfo *eix = ein->bei_lrunext; + if ( eix->bei_state & (CACHE_ENTRY_PURGED|CACHE_ENTRY_DELETED) ) { + ldap_pvt_thread_rdwr_wunlock( &bdb->bi_cache.c_rwlock ); + ldap_pvt_thread_yield(); + goto again; + } + ldap_pvt_thread_rdwr_wunlock( &bdb->bi_cache.c_rwlock ); + /* Someone else created this node just before us. * Free our new copy and use the existing one. */ bdb_cache_entryinfo_free( &bdb->bi_cache, ein ); - ein = eix; - - /* Link in any kids we've already processed */ - if ( ei2 ) { - bdb_cache_entryinfo_lock( ein ); - avl_insert( &ein->bei_kids, (caddr_t)ei2, - bdb_rdn_cmp, avl_dup_error ); - ein->bei_ckids++; - bdb_cache_entryinfo_unlock( ein ); + + /* if it was the node we were looking for, just return it */ + if ( eir == ein ) { + *res = eix; + rc = 0; + bdb_cache_entryinfo_lock( eix ); + break; } - } - /* If this is the first time, save this node - * to be returned later. - */ - if ( eir == NULL ) eir = ein; + ein = ei2; + ei2 = eix; + add = 0; + + /* otherwise, link up what we have and return */ + goto gotparent; + } /* If there was a previous node, link it to this one */ if ( ei2 ) ei2->bei_parent = ein; @@ -577,23 +599,29 @@ hdb_cache_find_parent( if ( eip.bei_id ) { ei2 = (EntryInfo *) avl_find( bdb->bi_cache.c_idtree, (caddr_t) &eip, bdb_id_cmp ); + if ( ei2 && ( ei2->bei_state & ( CACHE_ENTRY_PURGED|CACHE_ENTRY_DELETED ))) { + ei2 = NULL; + } } else { ei2 = &bdb->bi_cache.c_dntree; } - bdb->bi_cache.c_eiused++; - if ( ei2 && ( ei2->bei_kids || !ei2->bei_id )) + if ( add ) { + bdb->bi_cache.c_eiused++; + if ( ei2 && ( ei2->bei_kids || !ei2->bei_id )) bdb->bi_cache.c_leaves++; + } ldap_pvt_thread_rdwr_wunlock( &bdb->bi_cache.c_rwlock ); +gotparent: /* Got the parent, link in and we're done. */ if ( ei2 ) { - bdb_cache_entryinfo_lock( eir ); bdb_cache_entryinfo_lock( ei2 ); + bdb_cache_entryinfo_lock( eir ); ein->bei_parent = ei2; - avl_insert( &ei2->bei_kids, (caddr_t)ein, bdb_rdn_cmp, - avl_dup_error); - ei2->bei_ckids++; + if ( avl_insert( &ei2->bei_kids, (caddr_t)ein, bdb_rdn_cmp, + avl_dup_error) == 0 ) + ei2->bei_ckids++; /* Reset all the state info */ for (ein = eir; ein != ei2; ein=ein->bei_parent) @@ -724,12 +752,18 @@ bdb_cache_lru_purge( struct bdb_info *bdb ) * or this node is being deleted, skip it. */ if (( elru->bei_state & ( CACHE_ENTRY_NOT_LINKED | - CACHE_ENTRY_DELETED | CACHE_ENTRY_LOADING )) || + CACHE_ENTRY_DELETED | CACHE_ENTRY_LOADING | + CACHE_ENTRY_ONELEVEL )) || elru->bei_finders > 0 ) { bdb_cache_entryinfo_unlock( elru ); goto bottom; } + if ( bdb_cache_entryinfo_trylock( elru->bei_parent )) { + bdb_cache_entryinfo_unlock( elru ); + goto bottom; + } + /* entryinfo is locked */ islocked = 1; @@ -782,20 +816,13 @@ bdb_cache_lru_purge( struct bdb_info *bdb ) } next: - if ( islocked ) + if ( islocked ) { bdb_cache_entryinfo_unlock( elru ); + bdb_cache_entryinfo_unlock( elru->bei_parent ); + } - if ( count >= efree && eicount >= eifree ) { - if ( count || ecount > bdb->bi_cache.c_cursize ) { - ldap_pvt_thread_mutex_lock( &bdb->bi_cache.c_count_mutex ); - /* HACK: we seem to be losing track, fix up now */ - if ( ecount > bdb->bi_cache.c_cursize ) - bdb->bi_cache.c_cursize = ecount; - bdb->bi_cache.c_cursize -= count; - ldap_pvt_thread_mutex_unlock( &bdb->bi_cache.c_count_mutex ); - } + if ( count >= efree && eicount >= eifree ) break; - } bottom: if ( elnext == bdb->bi_cache.c_lruhead ) break; @@ -804,28 +831,19 @@ bottom: #endif } + if ( count || ecount > bdb->bi_cache.c_cursize ) { + ldap_pvt_thread_mutex_lock( &bdb->bi_cache.c_count_mutex ); + /* HACK: we seem to be losing track, fix up now */ + if ( ecount > bdb->bi_cache.c_cursize ) + bdb->bi_cache.c_cursize = ecount; + bdb->bi_cache.c_cursize -= count; + ldap_pvt_thread_mutex_unlock( &bdb->bi_cache.c_count_mutex ); + } bdb->bi_cache.c_lruhead = elnext; ldap_pvt_thread_mutex_unlock( &bdb->bi_cache.c_lru_mutex ); bdb->bi_cache.c_purging = 0; } -EntryInfo * -bdb_cache_find_info( - struct bdb_info *bdb, - ID id ) -{ - EntryInfo ei = { 0 }, - *ei2; - - ei.bei_id = id; - - ldap_pvt_thread_rdwr_rlock( &bdb->bi_cache.c_rwlock ); - ei2 = (EntryInfo *) avl_find( bdb->bi_cache.c_idtree, - (caddr_t) &ei, bdb_id_cmp ); - ldap_pvt_thread_rdwr_runlock( &bdb->bi_cache.c_rwlock ); - return ei2; -} - /* * cache_find_id - find an entry in the cache, given id. * The entry is locked for Read upon return. Call with flag ID_LOCKED if @@ -859,11 +877,12 @@ again: ldap_pvt_thread_rdwr_rlock( &bdb->bi_cache.c_rwlock ); if ( *eip ) { /* If the lock attempt fails, the info is in use */ if ( bdb_cache_entryinfo_trylock( *eip )) { + int del = (*eip)->bei_state & CACHE_ENTRY_DELETED; ldap_pvt_thread_rdwr_runlock( &bdb->bi_cache.c_rwlock ); /* If this node is being deleted, treat * as if the delete has already finished */ - if ( (*eip)->bei_state & CACHE_ENTRY_DELETED ) { + if ( del ) { return DB_NOTFOUND; } /* otherwise, wait for the info to free up */ @@ -920,6 +939,10 @@ again: ldap_pvt_thread_rdwr_rlock( &bdb->bi_cache.c_rwlock ); } else { (*eip)->bei_finders++; (*eip)->bei_state |= CACHE_ENTRY_REFERENCED; + if ( flag & ID_NOENTRY ) { + bdb_cache_entryinfo_unlock( *eip ); + return 0; + } /* Make sure only one thread tries to load the entry */ load1: #ifdef SLAP_ZONE_ALLOC @@ -961,6 +984,8 @@ load1: if ( rc == 0 ) { ep->e_private = *eip; #ifdef BDB_HIER + while ( (*eip)->bei_state & CACHE_ENTRY_NOT_LINKED ) + ldap_pvt_thread_yield(); bdb_fix_dn( ep, 0 ); #endif (*eip)->bei_e = ep; @@ -1037,7 +1062,7 @@ load1: bdb->bi_cache.c_leaves > bdb->bi_cache.c_eimax ) { ldap_pvt_thread_mutex_lock( &bdb->bi_cache.c_count_mutex ); if ( !bdb->bi_cache.c_purging ) { - if ( !( flag & ID_NOCACHE )) { + if ( load && !( flag & ID_NOCACHE )) { bdb->bi_cache.c_cursize++; if ( bdb->bi_cache.c_cursize > bdb->bi_cache.c_maxsize ) { purge = 1; @@ -1124,6 +1149,12 @@ bdb_cache_add( if ( eip->bei_dkids ) eip->bei_dkids++; #endif + if (eip->bei_parent) { + bdb_cache_entryinfo_lock( eip->bei_parent ); + eip->bei_parent->bei_state &= ~CACHE_ENTRY_NO_GRANDKIDS; + bdb_cache_entryinfo_unlock( eip->bei_parent ); + } + rc = bdb_entryinfo_add_internal( bdb, &ei, &new ); /* bdb_csn_commit can cause this when adding the database root entry */ if ( new->bei_e ) { @@ -1138,9 +1169,6 @@ bdb_cache_add( e->e_private = new; new->bei_state |= CACHE_ENTRY_NO_KIDS | CACHE_ENTRY_NO_GRANDKIDS; eip->bei_state &= ~CACHE_ENTRY_NO_KIDS; - if (eip->bei_parent) { - eip->bei_parent->bei_state &= ~CACHE_ENTRY_NO_GRANDKIDS; - } bdb_cache_entryinfo_unlock( eip ); ldap_pvt_thread_rdwr_wunlock( &bdb->bi_cache.c_rwlock ); @@ -1320,8 +1348,10 @@ bdb_cache_delete( /* Get write lock on the data */ rc = bdb_cache_entry_db_relock( bdb, txn, ei, 1, 0, lock ); if ( rc ) { + bdb_cache_entryinfo_lock( ei ); /* couldn't lock, undo and give up */ ei->bei_state ^= CACHE_ENTRY_DELETED; + bdb_cache_entryinfo_unlock( ei ); return rc; } @@ -1331,7 +1361,10 @@ bdb_cache_delete( /* set lru mutex */ ldap_pvt_thread_mutex_lock( &bdb->bi_cache.c_lru_mutex ); + bdb_cache_entryinfo_lock( ei->bei_parent ); + bdb_cache_entryinfo_lock( ei ); rc = bdb_cache_delete_internal( &bdb->bi_cache, e->e_private, 1 ); + bdb_cache_entryinfo_unlock( ei ); /* free lru mutex */ ldap_pvt_thread_mutex_unlock( &bdb->bi_cache.c_lru_mutex ); @@ -1373,11 +1406,12 @@ bdb_cache_delete_internal( int decr_leaf = 0; /* already freed? */ - if ( !e->bei_parent ) + if ( !e->bei_parent ) { + assert(0); return -1; + } - /* Lock the parent's kids tree */ - bdb_cache_entryinfo_lock( e->bei_parent ); + e->bei_state |= CACHE_ENTRY_PURGED; #ifdef BDB_HIER e->bei_parent->bei_ckids--; @@ -1388,12 +1422,11 @@ bdb_cache_delete_internal( == NULL ) { rc = -1; + assert(0); } if ( e->bei_parent->bei_kids ) decr_leaf = 1; - bdb_cache_entryinfo_unlock( e->bei_parent ); - ldap_pvt_thread_rdwr_wlock( &cache->c_rwlock ); /* id tree */ if ( avl_delete( &cache->c_idtree, (caddr_t) e, bdb_id_cmp )) { @@ -1402,8 +1435,10 @@ bdb_cache_delete_internal( cache->c_leaves--; } else { rc = -1; + assert(0); } ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock ); + bdb_cache_entryinfo_unlock( e->bei_parent ); if ( rc == 0 ){ /* lru */ diff --git a/servers/slapd/back-bdb/dn2id.c b/servers/slapd/back-bdb/dn2id.c index f4c528fa48..24c8266676 100644 --- a/servers/slapd/back-bdb/dn2id.c +++ b/servers/slapd/back-bdb/dn2id.c @@ -1102,7 +1102,7 @@ hdb_dn2idl_internal( cx->rc = cx->dbc->c_close( cx->dbc ); done_one: bdb_cache_entryinfo_lock( cx->ei ); - cx->ei->bei_state ^= CACHE_ENTRY_ONELEVEL; + cx->ei->bei_state &= ~CACHE_ENTRY_ONELEVEL; bdb_cache_entryinfo_unlock( cx->ei ); if ( cx->rc ) return cx->rc; @@ -1151,15 +1151,23 @@ gotit: for ( cx->id = bdb_idl_first( save, &idcurs ); cx->id != NOID; cx->id = bdb_idl_next( save, &idcurs )) { - cx->ei = bdb_cache_find_info( cx->bdb, cx->id ); + EntryInfo *ei2; + cx->ei = NULL; + if ( bdb_cache_find_id( cx->op, cx->txn, cx->id, &cx->ei, + ID_NOENTRY, NULL )) + continue; if ( !cx->ei || ( cx->ei->bei_state & CACHE_ENTRY_NO_KIDS )) continue; + ei2 = cx->ei; BDB_ID2DISK( cx->id, &cx->nid ); hdb_dn2idl_internal( cx ); if ( !BDB_IDL_IS_ZERO( cx->tmp )) nokids = 0; + bdb_cache_entryinfo_lock( ei2 ); + ei2->bei_finders--; + bdb_cache_entryinfo_unlock( ei2 ); } cx->depth--; cx->op->o_tmpfree( save, cx->op->o_tmpmemctx ); diff --git a/servers/slapd/back-bdb/proto-bdb.h b/servers/slapd/back-bdb/proto-bdb.h index d26184d20c..caf9633e92 100644 --- a/servers/slapd/back-bdb/proto-bdb.h +++ b/servers/slapd/back-bdb/proto-bdb.h @@ -501,7 +501,6 @@ void bdb_unlocked_cache_return_entry_rw( struct bdb_info *bdb, Entry *e, int rw #define bdb_cache_delete BDB_SYMBOL(cache_delete) #define bdb_cache_delete_cleanup BDB_SYMBOL(cache_delete_cleanup) #define bdb_cache_find_id BDB_SYMBOL(cache_find_id) -#define bdb_cache_find_info BDB_SYMBOL(cache_find_info) #define bdb_cache_find_ndn BDB_SYMBOL(cache_find_ndn) #define bdb_cache_find_parent BDB_SYMBOL(cache_find_parent) #define bdb_cache_modify BDB_SYMBOL(cache_modify) @@ -544,13 +543,10 @@ int bdb_cache_find_ndn( struct berval *ndn, EntryInfo **res ); -EntryInfo * bdb_cache_find_info( - struct bdb_info *bdb, - ID id -); #define ID_LOCKED 1 #define ID_NOCACHE 2 +#define ID_NOENTRY 4 int bdb_cache_find_id( Operation *op, DB_TXN *tid, diff --git a/servers/slapd/back-bdb/search.c b/servers/slapd/back-bdb/search.c index def485ec9e..09faf8ebe9 100644 --- a/servers/slapd/back-bdb/search.c +++ b/servers/slapd/back-bdb/search.c @@ -569,10 +569,6 @@ dn2entry_retry: #ifdef SLAP_ZONE_ALLOC slap_zn_runlock(bdb->bi_cache.c_zctx, e); #endif - if ( e != e_root ) { - bdb_cache_return_entry_r(bdb, e, &lock); - } - e = NULL; /* select candidates */ if ( op->oq_search.rs_scope == LDAP_SCOPE_BASE ) { @@ -595,6 +591,11 @@ cand_retry: } } + if ( e != e_root ) { + bdb_cache_return_entry_r(bdb, e, &lock); + } + e = NULL; + /* start cursor at beginning of candidates. */ cursor = 0; -- GitLab