From 377bccbc6c0c39bb64240562dbf18d7372122900 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Wed, 16 Apr 2003 16:23:36 +0000 Subject: [PATCH] Hierarchical cache management. --- servers/slapd/back-bdb/add.c | 141 +-- servers/slapd/back-bdb/back-bdb.h | 39 +- servers/slapd/back-bdb/bind.c | 21 +- servers/slapd/back-bdb/cache.c | 1356 ++++++++++------------------ servers/slapd/back-bdb/compare.c | 19 +- servers/slapd/back-bdb/delete.c | 115 ++- servers/slapd/back-bdb/dn2entry.c | 60 +- servers/slapd/back-bdb/dn2id.c | 144 +-- servers/slapd/back-bdb/id2entry.c | 55 +- servers/slapd/back-bdb/init.c | 2 - servers/slapd/back-bdb/modify.c | 65 +- servers/slapd/back-bdb/modrdn.c | 110 ++- servers/slapd/back-bdb/passwd.c | 40 +- servers/slapd/back-bdb/proto-bdb.h | 103 ++- servers/slapd/back-bdb/referral.c | 34 +- servers/slapd/back-bdb/search.c | 86 +- servers/slapd/back-bdb/tools.c | 13 +- 17 files changed, 994 insertions(+), 1409 deletions(-) diff --git a/servers/slapd/back-bdb/add.c b/servers/slapd/back-bdb/add.c index 0d919f1069..e52174153d 100644 --- a/servers/slapd/back-bdb/add.c +++ b/servers/slapd/back-bdb/add.c @@ -18,13 +18,13 @@ bdb_add(Operation *op, SlapReply *rs ) { struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private; struct berval pdn; - Entry *p = NULL; - const char *text; + Entry *p; + EntryInfo *ei; char textbuf[SLAP_TEXT_BUFLEN]; size_t textlen = sizeof textbuf; AttributeDescription *children = slap_schema.si_ad_children; AttributeDescription *entry = slap_schema.si_ad_entry; - DB_TXN *ltid = NULL; + DB_TXN *ltid = NULL, *lt2; struct bdb_op_info opinfo; #ifdef BDB_SUBENTRIES int subentry; @@ -137,42 +137,37 @@ retry: /* transaction retry */ dnParent( &op->oq_add.rs_e->e_nname, &pdn ); } - if( pdn.bv_len != 0 ) { - Entry *matched = NULL; - - /* get parent */ - rs->sr_err = bdb_dn2entry_r( op->o_bd, ltid, &pdn, &p, &matched, 0, locker, &lock ); - - switch( rs->sr_err ) { - case 0: - case DB_NOTFOUND: - break; - case DB_LOCK_DEADLOCK: - case DB_LOCK_NOTGRANTED: - goto retry; - case LDAP_BUSY: - rs->sr_text = "ldap server busy"; - goto return_results; - default: - rs->sr_err = LDAP_OTHER; - rs->sr_text = "internal error"; - goto return_results; - } - - if ( p == NULL ) { - if ( matched != NULL ) { - rs->sr_matched = ch_strdup( matched->e_dn ); - rs->sr_ref = is_entry_referral( matched ) - ? get_entry_referrals( op, matched ) - : NULL; - bdb_unlocked_cache_return_entry_r( &bdb->bi_cache, matched ); - matched = NULL; - - } else { - rs->sr_ref = referral_rewrite( default_referral, - NULL, &op->oq_add.rs_e->e_name, LDAP_SCOPE_DEFAULT ); - } + /* get entry or parent */ + rs->sr_err = bdb_dn2entry( op->o_bd, ltid, &op->ora_e->e_nname, &ei, + 1, locker, &lock, op->o_tmpmemctx ); + switch( rs->sr_err ) { + case 0: + rs->sr_err = LDAP_ALREADY_EXISTS; + goto return_results; + case DB_NOTFOUND: + break; + case DB_LOCK_DEADLOCK: + case DB_LOCK_NOTGRANTED: + goto retry; + case LDAP_BUSY: + rs->sr_text = "ldap server busy"; + goto return_results; + default: + rs->sr_err = LDAP_OTHER; + rs->sr_text = "internal error"; + goto return_results; + } + p = ei->bei_e; + if ( p ) { + if ( !bvmatch( &pdn, &p->e_nname ) ) { + rs->sr_matched = ber_strdup_x( p->e_name.bv_val, + op->o_tmpmemctx ); + rs->sr_ref = is_entry_referral( p ) + ? get_entry_referrals( op, p ) + : NULL; + bdb_unlocked_cache_return_entry_r( &bdb->bi_cache, p ); + p = NULL; #ifdef NEW_LOGGING LDAP_LOG ( OPERATION, DETAIL1, "bdb_add: parent does not exist\n", 0, 0, 0 ); @@ -185,7 +180,7 @@ retry: /* transaction retry */ send_ldap_result( op, rs ); ber_bvarray_free( rs->sr_ref ); - ch_free( (char *)rs->sr_matched ); + op->o_tmpfree( (char *)rs->sr_matched, op->o_tmpmemctx ); rs->sr_ref = NULL; rs->sr_matched = NULL; @@ -370,8 +365,26 @@ retry: /* transaction retry */ goto return_results;; } + /* nested transaction */ + rs->sr_err = TXN_BEGIN( bdb->bi_dbenv, ltid, <2, + bdb->bi_db_opflags ); + rs->sr_text = NULL; + if( rs->sr_err != 0 ) { +#ifdef NEW_LOGGING + LDAP_LOG ( OPERATION, ERR, + "bdb_add: txn_begin(2) failed: %s (%d)\n", db_strerror(rs->sr_err), rs->sr_err, 0 ); +#else + Debug( LDAP_DEBUG_TRACE, + "bdb_add: txn_begin(2) failed: %s (%d)\n", + db_strerror(rs->sr_err), rs->sr_err, 0 ); +#endif + rs->sr_err = LDAP_OTHER; + rs->sr_text = "internal error"; + goto return_results; + } + /* dn2id index */ - rs->sr_err = bdb_dn2id_add( op->o_bd, ltid, &pdn, op->oq_add.rs_e ); + rs->sr_err = bdb_dn2id_add( op->o_bd, lt2, &pdn, op->oq_add.rs_e ); if ( rs->sr_err != 0 ) { #ifdef NEW_LOGGING LDAP_LOG ( OPERATION, ERR, @@ -395,7 +408,7 @@ retry: /* transaction retry */ } /* id2entry index */ - rs->sr_err = bdb_id2entry_add( op->o_bd, ltid, op->oq_add.rs_e ); + rs->sr_err = bdb_id2entry_add( op->o_bd, lt2, op->oq_add.rs_e ); if ( rs->sr_err != 0 ) { #ifdef NEW_LOGGING LDAP_LOG ( OPERATION, ERR, "bdb_add: id2entry_add failed\n", 0, 0, 0 ); @@ -415,7 +428,7 @@ retry: /* transaction retry */ } /* attribute indexes */ - rs->sr_err = bdb_index_entry_add( op, ltid, op->oq_add.rs_e ); + rs->sr_err = bdb_index_entry_add( op, lt2, op->oq_add.rs_e ); if ( rs->sr_err != LDAP_SUCCESS ) { #ifdef NEW_LOGGING LDAP_LOG ( OPERATION, ERR, @@ -434,7 +447,11 @@ retry: /* transaction retry */ rs->sr_text = "index generation failed"; goto return_results; } - + if ( TXN_COMMIT( lt2, 0 ) != 0 ) { + rs->sr_err = LDAP_OTHER; + rs->sr_text = "txn_commit(2) failed"; + goto return_results; + } if( op->o_noop ) { if (( rs->sr_err=TXN_ABORT( ltid )) != 0 ) { @@ -454,32 +471,21 @@ retry: /* transaction retry */ rs->sr_text = "txn_prepare failed"; } else { - int ret = bdb_cache_add_entry_rw(bdb->bi_dbenv, - &bdb->bi_cache, op->oq_add.rs_e, CACHE_WRITE_LOCK, - locker, &lock); - switch ( ret ) { - case 0: - break; - case DB_LOCK_DEADLOCK: - case DB_LOCK_NOTGRANTED: - goto retry; - default: - ret = LDAP_OTHER; + struct berval nrdn; + + if (pdn.bv_len) { + nrdn.bv_val = op->ora_e->e_nname.bv_val; + nrdn.bv_len = pdn.bv_val - nrdn.bv_val - 1; + } else { + nrdn = op->ora_e->e_nname; } - if ( ret ) { - if(( rs->sr_err=TXN_ABORT( ltid )) != 0 ) { - rs->sr_text = "cache add & txn_abort failed"; - } else { - rs->sr_err = LDAP_OTHER; - rs->sr_text = "cache add failed"; - } + bdb_cache_add(bdb, ei, op->oq_add.rs_e, &nrdn, locker ); + + if(( rs->sr_err=TXN_COMMIT( ltid, 0 )) != 0 ) { + rs->sr_text = "txn_commit failed"; } else { - if(( rs->sr_err=TXN_COMMIT( ltid, 0 )) != 0 ) { - rs->sr_text = "txn_commit failed"; - } else { - rs->sr_err = LDAP_SUCCESS; - } + rs->sr_err = LDAP_SUCCESS; } } } @@ -497,9 +503,6 @@ retry: /* transaction retry */ op->o_noop ? " (no-op)" : "", op->oq_add.rs_e->e_id, op->oq_add.rs_e->e_dn ); #endif rs->sr_text = NULL; - if ( !noop ) { - bdb_cache_entry_commit( op->oq_add.rs_e ); - } } else { #ifdef NEW_LOGGING diff --git a/servers/slapd/back-bdb/back-bdb.h b/servers/slapd/back-bdb/back-bdb.h index 21d6184f61..3fcd17f8d6 100644 --- a/servers/slapd/back-bdb/back-bdb.h +++ b/servers/slapd/back-bdb/back-bdb.h @@ -82,16 +82,39 @@ typedef struct bdb_idl_cache_entry_s { } bdb_idl_cache_entry_t; #endif +/* BDB backend specific entry info */ +typedef struct bdb_entry_info { + struct bdb_entry_info *bei_parent; + ID bei_id; + + int bei_state; +#define CACHE_ENTRY_DELETED 1 + + /* + * remaining fields require backend cache lock to access + */ + struct berval bei_nrdn; + struct berval bei_rdn; + Entry *bei_e; + Avlnode *bei_kids; + ldap_pvt_thread_mutex_t bei_kids_mutex; + + struct bdb_entry_info *bei_lrunext; /* for cache lru list */ + struct bdb_entry_info *bei_lruprev; +} EntryInfo; +#undef BEI +#define BEI(e) ((EntryInfo *) ((e)->e_private)) + /* for the in-core cache of entries */ typedef struct bdb_cache { - int c_maxsize; - int c_cursize; - Avlnode *c_dntree; - Avlnode *c_idtree; - Entry *c_lruhead; /* lru - add accessed entries here */ - Entry *c_lrutail; /* lru - rem lru entries from here */ - ldap_pvt_thread_rdwr_t c_rwlock; - ldap_pvt_thread_mutex_t lru_mutex; + int c_maxsize; + int c_cursize; + EntryInfo c_dntree; + Avlnode *c_idtree; + EntryInfo *c_lruhead; /* lru - add accessed entries here */ + EntryInfo *c_lrutail; /* lru - rem lru entries from here */ + ldap_pvt_thread_rdwr_t c_rwlock; + ldap_pvt_thread_mutex_t lru_mutex; } Cache; #define CACHE_READ_LOCK 0 diff --git a/servers/slapd/back-bdb/bind.c b/servers/slapd/back-bdb/bind.c index 38aeaa2fb7..0158a9cff2 100644 --- a/servers/slapd/back-bdb/bind.c +++ b/servers/slapd/back-bdb/bind.c @@ -21,7 +21,7 @@ bdb_bind( Operation *op, SlapReply *rs ) struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private; Entry *e; Attribute *a; - Entry *matched; + EntryInfo *ei; #ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_KBIND char krbname[MAX_K_NAME_SZ + 1]; AttributeDescription *krbattr = slap_schema.si_ad_krbName; @@ -59,7 +59,8 @@ bdb_bind( Operation *op, SlapReply *rs ) dn2entry_retry: /* get entry with reader lock */ - rs->sr_err = bdb_dn2entry_r( op->o_bd, NULL, &op->o_req_ndn, &e, &matched, 0, locker, &lock ); + rs->sr_err = bdb_dn2entry( op->o_bd, NULL, &op->o_req_ndn, &ei, 1, + locker, &lock, op->o_tmpmemctx ); switch(rs->sr_err) { case DB_NOTFOUND: @@ -78,17 +79,17 @@ dn2entry_retry: return rs->sr_err; } - if ( e == NULL ) { - if( matched != NULL ) { - rs->sr_ref = is_entry_referral( matched ) - ? get_entry_referrals( op, matched ) + e = ei->bei_e; + if ( rs->sr_err == DB_NOTFOUND ) { + if( e != NULL ) { + rs->sr_ref = is_entry_referral( e ) + ? get_entry_referrals( op, e ) : NULL; if (rs->sr_ref) - rs->sr_matched = ch_strdup( matched->e_name.bv_val ); - - bdb_cache_return_entry_r( bdb->bi_dbenv, &bdb->bi_cache, matched, &lock ); - matched = NULL; + rs->sr_matched = ch_strdup( e->e_name.bv_val ); + bdb_cache_return_entry_r( bdb->bi_dbenv, &bdb->bi_cache, e, &lock ); + e = NULL; } else { rs->sr_ref = referral_rewrite( default_referral, NULL, &op->o_req_dn, LDAP_SCOPE_DEFAULT ); diff --git a/servers/slapd/back-bdb/cache.c b/servers/slapd/back-bdb/cache.c index badc54723a..457ab3aced 100644 --- a/servers/slapd/back-bdb/cache.c +++ b/servers/slapd/back-bdb/cache.c @@ -17,132 +17,70 @@ #include "back-bdb.h" -/* BDB backend specific entry info -- visible only to the cache */ -typedef struct bdb_entry_info { -#if 0 - ldap_pvt_thread_rdwr_t bei_rdwr; /* reader/writer lock */ -#endif - - /* - * remaining fields require backend cache lock to access - * These items are specific to the BDB backend and should - * be hidden. - */ - int bei_state; /* for the cache */ -#define CACHE_ENTRY_UNDEFINED 0 -#define CACHE_ENTRY_CREATING 1 -#define CACHE_ENTRY_READY 2 -#define CACHE_ENTRY_DELETED 3 -#define CACHE_ENTRY_COMMITTED 4 - - int bei_refcnt; /* # threads ref'ing this entry */ - Entry *bei_lrunext; /* for cache lru list */ - Entry *bei_lruprev; -} EntryInfo; -#undef BEI -#define BEI(e) ((EntryInfo *) ((e)->e_private)) - -static int bdb_cache_delete_entry_internal(Cache *cache, Entry *e); +static int bdb_cache_delete_entry_internal(Cache *cache, EntryInfo *e); #ifdef LDAP_DEBUG static void bdb_lru_print(Cache *cache); #endif -#if 0 /* unused */ -static int -bdb_cache_entry_rdwr_lock(Entry *e, int rw) +static EntryInfo * +bdb_cache_entryinfo_new( ) { -#ifdef NEW_LOGGING - LDAP_LOG( CACHE, ENTRY, - "bdb_cache_entry_rdwr_lock: %s lock on ID %ld\n", - rw ? "w" : "r", e->e_id, 0 ); -#else - Debug( LDAP_DEBUG_ARGS, "entry_rdwr_%slock: ID: %ld\n", - rw ? "w" : "r", e->e_id, 0); -#endif + EntryInfo *ei; - if (rw) - return ldap_pvt_thread_rdwr_wlock(&BEI(e)->bei_rdwr); - else - return ldap_pvt_thread_rdwr_rlock(&BEI(e)->bei_rdwr); -} + ei = ch_calloc(1, sizeof(struct bdb_entry_info)); + ldap_pvt_thread_mutex_init( &ei->bei_kids_mutex ); -static int -bdb_cache_entry_rdwr_trylock(Entry *e, int rw) -{ -#ifdef NEW_LOGGING - LDAP_LOG( CACHE, ENTRY, - "bdb_cache_entry_rdwr_trylock: try %s lock on ID: %ld.\n", - rw ? "w" : "r", e->e_id, 0 ); -#else - Debug( LDAP_DEBUG_ARGS, "entry_rdwr_%strylock: ID: %ld\n", - rw ? "w" : "r", e->e_id, 0); -#endif - - if (rw) - return ldap_pvt_thread_rdwr_wtrylock(&BEI(e)->bei_rdwr); - else - return ldap_pvt_thread_rdwr_rtrylock(&BEI(e)->bei_rdwr); + return ei; } -static int -bdb_cache_entry_rdwr_unlock(Entry *e, int rw) +/* Atomically release and reacquire a lock */ +int +bdb_cache_entry_db_relock( + DB_ENV *env, + u_int32_t locker, + EntryInfo *ei, + int rw, + int tryOnly, + DB_LOCK *lock ) { -#ifdef NEW_LOGGING - LDAP_LOG( CACHE, ENTRY, - "bdb_cache_entry_rdwr_unlock: remove %s lock on ID %ld.\n", - rw ? "w" : "r", e->e_id, 0 ); +#ifdef NO_THREADS + return 0; #else - Debug( LDAP_DEBUG_ARGS, "entry_rdwr_%sunlock: ID: %ld\n", - rw ? "w" : "r", e->e_id, 0); -#endif + int rc; + DBT lockobj; + DB_LOCKREQ list[2]; - if (rw) - return ldap_pvt_thread_rdwr_wunlock(&BEI(e)->bei_rdwr); - else - return ldap_pvt_thread_rdwr_runlock(&BEI(e)->bei_rdwr); -} -#endif /* unused */ + lockobj.data = ei; + lockobj.size = sizeof(ei->bei_parent) + sizeof(ei->bei_id); -#if 0 -static int -bdb_cache_entry_rdwr_init(Entry *e) -{ - return ldap_pvt_thread_rdwr_init( &BEI(e)->bei_rdwr ); -} + list[0].op = DB_LOCK_PUT; + list[0].lock = *lock; + list[1].op = DB_LOCK_GET; + list[1].lock = *lock; + list[1].mode = rw ? DB_LOCK_WRITE : DB_LOCK_READ; + list[1].obj = &lockobj; + rc = env->lock_vec(env, locker, tryOnly ? DB_LOCK_NOWAIT : 0, + list, 2, NULL ); -static int -bdb_cache_entry_rdwr_destroy(Entry *e) -{ - return ldap_pvt_thread_rdwr_destroy( &BEI(e)->bei_rdwr ); -} + if (rc) { +#ifdef NEW_LOGGING + LDAP_LOG( CACHE, DETAIL1, + "bdb_cache_entry_db_relock: entry %d, rw %d, rc %d\n", + ei->bei_id, rw, rc ); +#else + Debug( LDAP_DEBUG_TRACE, + "bdb_cache_entry_db_relock: entry %d, rw %d, rc %d\n", + ei->bei_id, rw, rc ); #endif - -static int -bdb_cache_entry_private_init( Entry *e ) -{ - assert( e->e_private == NULL ); - - if( e->e_private != NULL ) { - /* this should never happen */ - return 1; + } else { + *lock = list[1].lock; } - - e->e_private = ch_calloc(1, sizeof(struct bdb_entry_info)); - -#if 0 - if( bdb_cache_entry_rdwr_init( e ) != 0 ) { - free( BEI(e) ); - e->e_private = NULL; - return 1; - } + return rc; #endif - - return 0; } - int bdb_cache_entry_db_lock -( DB_ENV *env, u_int32_t locker, Entry *e, int rw, u_int32_t flags, DB_LOCK *lock ) +( DB_ENV *env, u_int32_t locker, EntryInfo *ei, int rw, int tryOnly, DB_LOCK *lock ) { #ifdef NO_THREADS return 0; @@ -156,24 +94,20 @@ bdb_cache_entry_db_lock else db_rw = DB_LOCK_READ; -#if 0 - lockobj.data = e->e_nname.bv_val; - lockobj.size = e->e_nname.bv_len; -#else - lockobj.data = &e->e_private; - lockobj.size = sizeof(e->e_private); -#endif - rc = LOCK_GET(env, locker, flags | DB_LOCK_NOWAIT, + lockobj.data = ei; + lockobj.size = sizeof(ei->bei_parent) + sizeof(ei->bei_id); + + rc = LOCK_GET(env, locker, tryOnly ? DB_LOCK_NOWAIT : 0, &lockobj, db_rw, lock); if (rc) { #ifdef NEW_LOGGING LDAP_LOG( CACHE, DETAIL1, - "bdb_cache_entry_db_lock: entry %s, rw %d, rc %d\n", - e->e_nname.bv_val, rw, rc ); + "bdb_cache_entry_db_lock: entry %d, rw %d, rc %d\n", + ei->bei_id, rw, rc ); #else Debug( LDAP_DEBUG_TRACE, - "bdb_cache_entry_db_lock: entry %s, rw %d, rc %d\n", - e->e_nname.bv_val, rw, rc ); + "bdb_cache_entry_db_lock: entry %d, rw %d, rc %d\n", + ei->bei_id, rw, rc ); #endif } return rc; @@ -194,808 +128,458 @@ bdb_cache_entry_db_unlock #endif } -/* - * marks an entry in CREATING state as committed, so it is really returned - * to the cache. Otherwise an entry in CREATING state is removed. - * Makes e_private be destroyed at the following cache_return_entry_w, - * but lets the entry untouched (owned by someone else) - */ -void -bdb_cache_entry_commit( Entry *e ) -{ - assert( e ); - assert( e->e_private ); - assert( BEI(e)->bei_state == CACHE_ENTRY_CREATING ); - /* assert( BEI(e)->bei_refcnt == 1 ); */ - - BEI(e)->bei_state = CACHE_ENTRY_COMMITTED; -} - static int -bdb_cache_entry_private_destroy( Entry *e ) +bdb_cache_entryinfo_destroy( EntryInfo *e ) { - assert( e->e_private ); - -#if 0 - bdb_cache_entry_rdwr_destroy( e ); -#endif - - free( e->e_private ); - e->e_private = NULL; + ldap_pvt_thread_mutex_destroy( &e->bei_kids_mutex ); + free( e->bei_nrdn.bv_val ); + free( e ); return 0; } -void -bdb_unlocked_cache_return_entry_rw( Cache *cache, Entry *e, int rw ) -{ - - ID id; - int refcnt, freeit = 1; - - /* set cache write lock */ - ldap_pvt_thread_rdwr_wlock( &cache->c_rwlock ); - - assert( e->e_private ); - -#if 0 - bdb_cache_entry_rdwr_unlock(e, rw); -#endif - - id = e->e_id; - refcnt = --BEI(e)->bei_refcnt; - - /* - * if the entry is returned when in CREATING state, it is deleted - * but not freed because it may belong to someone else (do_add, - * for instance) - */ - if ( BEI(e)->bei_state == CACHE_ENTRY_CREATING ) { - /* set lru mutex */ - ldap_pvt_thread_mutex_lock( &cache->lru_mutex ); - bdb_cache_delete_entry_internal( cache, e ); - /* free lru mutex */ - ldap_pvt_thread_mutex_unlock( &cache->lru_mutex ); - freeit = 0; - /* now the entry is in DELETED state */ - } - - if ( BEI(e)->bei_state == CACHE_ENTRY_COMMITTED ) { - BEI(e)->bei_state = CACHE_ENTRY_READY; - - /* free cache write lock */ - ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock ); - -#ifdef NEW_LOGGING - LDAP_LOG( CACHE, DETAIL1, - "bdb_unlocked_cache_return_entry_rw: return (%ld):%s, refcnt=%d\n", - id, rw ? "w" : "r", refcnt ); -#else - Debug( LDAP_DEBUG_TRACE, - "====> bdb_unlocked_cache_return_entry_%s( %ld ): created (%d)\n", - rw ? "w" : "r", id, refcnt ); -#endif - - - } else if ( BEI(e)->bei_state == CACHE_ENTRY_DELETED ) { - if( refcnt > 0 ) { - /* free cache write lock */ - ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock ); - -#ifdef NEW_LOGGING - LDAP_LOG( CACHE, DETAIL1, - "bdb_unlocked_cache_return_entry_rw: %ld, delete pending (%d).\n", - id, refcnt, 0 ); -#else - Debug( LDAP_DEBUG_TRACE, - "====> bdb_unlocked_cache_return_entry_%s( %ld ): delete pending (%d)\n", - rw ? "w" : "r", id, refcnt ); -#endif - - } else { - bdb_cache_entry_private_destroy( e ); - if ( freeit ) { - bdb_entry_return( e ); - } - - /* free cache write lock */ - ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock ); - -#ifdef NEW_LOGGING - LDAP_LOG( CACHE, DETAIL1, - "bdb_unlocked_cache_return_entry_rw: (%ld): deleted (%d)\n", - id, refcnt, 0 ); -#else - Debug( LDAP_DEBUG_TRACE, - "====> bdb_unlocked_cache_return_entry_%s( %ld ): deleted (%d)\n", - rw ? "w" : "r", id, refcnt ); -#endif - } - - } else { - /* free cache write lock */ - ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock ); - -#ifdef NEW_LOGGING - LDAP_LOG( CACHE, DETAIL1, - "bdb_unlocked_cache_return_entry_rw: ID %ld:%s returned (%d)\n", - id, rw ? "w": "r", refcnt ); -#else - Debug( LDAP_DEBUG_TRACE, - "====> bdb_unlocked_cache_return_entry_%s( %ld ): returned (%d)\n", - rw ? "w" : "r", id, refcnt); -#endif - } -} - -void -bdb_cache_return_entry_rw -( DB_ENV *env, Cache *cache, Entry *e, int rw, DB_LOCK *lock ) -{ - ID id; - int refcnt, freeit = 1; - - /* set cache write lock */ - ldap_pvt_thread_rdwr_wlock( &cache->c_rwlock ); - - assert( e->e_private ); - - bdb_cache_entry_db_unlock( env, lock ); -#if 0 - bdb_cache_entry_rdwr_unlock(e, rw); -#endif - - id = e->e_id; - refcnt = --BEI(e)->bei_refcnt; - - /* - * if the entry is returned when in CREATING state, it is deleted - * but not freed because it may belong to someone else (do_add, - * for instance) - */ - if ( BEI(e)->bei_state == CACHE_ENTRY_CREATING ) { - /* set lru mutex */ - ldap_pvt_thread_mutex_lock( &cache->lru_mutex ); - bdb_cache_delete_entry_internal( cache, e ); - /* free lru mutex */ - ldap_pvt_thread_mutex_unlock( &cache->lru_mutex ); - freeit = 0; - /* now the entry is in DELETED state */ - } - - if ( BEI(e)->bei_state == CACHE_ENTRY_COMMITTED ) { - BEI(e)->bei_state = CACHE_ENTRY_READY; - - /* free cache write lock */ - ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock ); - -#ifdef NEW_LOGGING - LDAP_LOG( CACHE, DETAIL1, - "bdb_cache_return_entry_rw: return (%ld):%s, refcnt=%d\n", - id, rw ? "w" : "r", refcnt ); -#else - Debug( LDAP_DEBUG_TRACE, - "====> bdb_cache_return_entry_%s( %ld ): created (%d)\n", - rw ? "w" : "r", id, refcnt ); -#endif - - - } else if ( BEI(e)->bei_state == CACHE_ENTRY_DELETED ) { - if( refcnt > 0 ) { - /* free cache write lock */ - ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock ); - -#ifdef NEW_LOGGING - LDAP_LOG( CACHE, DETAIL1, - "bdb_cache_return_entry_rw: %ld, delete pending (%d).\n", - id, refcnt, 0 ); -#else - Debug( LDAP_DEBUG_TRACE, - "====> bdb_cache_return_entry_%s( %ld ): delete pending (%d)\n", - rw ? "w" : "r", id, refcnt ); -#endif - - } else { - bdb_cache_entry_private_destroy( e ); - if ( freeit ) { - bdb_entry_return( e ); - } - - /* free cache write lock */ - ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock ); - -#ifdef NEW_LOGGING - LDAP_LOG( CACHE, DETAIL1, - "bdb_cache_return_entry_rw: (%ld): deleted (%d)\n", - id, refcnt, 0 ); -#else - Debug( LDAP_DEBUG_TRACE, - "====> bdb_cache_return_entry_%s( %ld ): deleted (%d)\n", - rw ? "w" : "r", id, refcnt ); -#endif - } - - } else { - /* free cache write lock */ - ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock ); - -#ifdef NEW_LOGGING - LDAP_LOG( CACHE, DETAIL1, - "bdb_cache_return_entry_rw: ID %ld:%s returned (%d)\n", - id, rw ? "w": "r", refcnt ); -#else - Debug( LDAP_DEBUG_TRACE, - "====> bdb_cache_return_entry_%s( %ld ): returned (%d)\n", - rw ? "w" : "r", id, refcnt); -#endif - } -} - -#define LRU_DELETE( cache, e ) do { \ - if ( BEI(e)->bei_lruprev != NULL ) { \ - BEI(BEI(e)->bei_lruprev)->bei_lrunext = BEI(e)->bei_lrunext; \ +#define LRU_DELETE( cache, ei ) do { \ + if ( (ei)->bei_lruprev != NULL ) { \ + (ei)->bei_lruprev->bei_lrunext = (ei)->bei_lrunext; \ } else { \ - (cache)->c_lruhead = BEI(e)->bei_lrunext; \ + (cache)->c_lruhead = (ei)->bei_lrunext; \ } \ - if ( BEI(e)->bei_lrunext != NULL ) { \ - BEI(BEI(e)->bei_lrunext)->bei_lruprev = BEI(e)->bei_lruprev; \ + if ( (ei)->bei_lrunext != NULL ) { \ + (ei)->bei_lrunext->bei_lruprev = (ei)->bei_lruprev; \ } else { \ - (cache)->c_lrutail = BEI(e)->bei_lruprev; \ + (cache)->c_lrutail = (ei)->bei_lruprev; \ } \ } while(0) -#define LRU_ADD( cache, e ) do { \ - BEI(e)->bei_lrunext = (cache)->c_lruhead; \ - if ( BEI(e)->bei_lrunext != NULL ) { \ - BEI(BEI(e)->bei_lrunext)->bei_lruprev = (e); \ +#define LRU_ADD( cache, ei ) do { \ + (ei)->bei_lrunext = (cache)->c_lruhead; \ + if ( (ei)->bei_lrunext != NULL ) { \ + (ei)->bei_lrunext->bei_lruprev = (ei); \ } \ - (cache)->c_lruhead = (e); \ - BEI(e)->bei_lruprev = NULL; \ + (cache)->c_lruhead = (ei); \ + (ei)->bei_lruprev = NULL; \ if ( (cache)->c_lrutail == NULL ) { \ - (cache)->c_lrutail = (e); \ + (cache)->c_lrutail = (ei); \ } \ } while(0) -/* - * cache_add_entry_rw - create and lock an entry in the cache - * returns: 0 entry has been created and locked - * 1 entry already existed - * -1 something bad happened - * other Berkeley DB locking error code - */ -int -bdb_cache_add_entry_rw( - DB_ENV *env, - Cache *cache, - Entry *e, - int rw, - u_int32_t locker, - DB_LOCK *lock -) +/* Do a lexical sort on normalized RDNs */ +static int +bdb_rdn_cmp( const void *v_e1, const void *v_e2 ) { - int i, rc; - Entry *ee; - -#ifdef NEW_LOGGING - LDAP_LOG( CACHE, ENTRY, - "bdb_cache_add_entry_rw: add (%s):%s to cache\n", - e->e_dn, rw ? "w" : "r", 0 ); -#endif - /* set cache write lock */ - ldap_pvt_thread_rdwr_wlock( &cache->c_rwlock ); - - assert( e->e_private == NULL ); - - if( bdb_cache_entry_private_init(e) != 0 ) { - /* free cache write lock */ - ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock ); - -#ifdef NEW_LOGGING - LDAP_LOG( CACHE, ERR, - "bdb_cache_add_entry_rw: add (%s):%ld private init failed!\n", - e->e_dn, e->e_id, 0 ); -#else - Debug( LDAP_DEBUG_ANY, - "====> bdb_cache_add_entry( %ld ): \"%s\": private init failed!\n", - e->e_id, e->e_dn, 0 ); -#endif - - - return( -1 ); - } - - if ( avl_insert( &cache->c_dntree, (caddr_t) e, - entry_dn_cmp, avl_dup_error ) != 0 ) - { - /* free cache write lock */ - ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock ); - -#ifdef NEW_LOGGING - LDAP_LOG( CACHE, DETAIL1, - "bdb_cache_add_entry: (%s):%ld already in cache.\n", - e->e_dn, e->e_id, 0 ); -#else - Debug( LDAP_DEBUG_TRACE, - "====> bdb_cache_add_entry( %ld ): \"%s\": already in dn cache\n", - e->e_id, e->e_dn, 0 ); -#endif - - bdb_cache_entry_private_destroy(e); - - return( 1 ); - } - - /* id tree */ - if ( avl_insert( &cache->c_idtree, (caddr_t) e, - entry_id_cmp, avl_dup_error ) != 0 ) - { -#ifdef NEW_LOGGING - LDAP_LOG( CACHE, DETAIL1, - "bdb_cache_add_entry: (%s):%ls already in cache.\n", - e->e_dn, e->e_id, 0 ); -#else - Debug( LDAP_DEBUG_ANY, - "====> bdb_cache_add_entry( %ld ): \"%s\": already in id cache\n", - e->e_id, e->e_dn, 0 ); -#endif - - /* delete from dn tree inserted above */ - if ( avl_delete( &cache->c_dntree, (caddr_t) e, - entry_dn_cmp ) == NULL ) - { -#ifdef NEW_LOGGING - LDAP_LOG( CACHE, INFO, - "bdb_cache_add_entry: can't delete (%s) from cache.\n", - e->e_dn, 0, 0 ); -#else - Debug( LDAP_DEBUG_ANY, "====> can't delete from dn cache\n", - 0, 0, 0 ); -#endif - } - - bdb_cache_entry_private_destroy(e); - - /* free cache write lock */ - ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock ); - return( -1 ); - } - - rc = bdb_cache_entry_db_lock( env, locker, e, rw, 0, lock ); - switch ( rc ) { - case 0 : - break; - case DB_LOCK_DEADLOCK : - case DB_LOCK_NOTGRANTED : - /* undo avl changes immediately */ - if ( avl_delete( &cache->c_idtree, (caddr_t) e, - entry_id_cmp ) == NULL ) { -#ifdef NEW_LOGGING - LDAP_LOG( CACHE, INFO, - "bdb_cache_add_entry: can't delete (%s) from cache.\n", - e->e_dn, 0, 0 ); -#else - Debug( LDAP_DEBUG_ANY, "====> can't delete from id cache\n", 0, 0, 0 ); -#endif - } - if ( avl_delete( &cache->c_dntree, (caddr_t) e, - entry_dn_cmp ) == NULL ) { -#ifdef NEW_LOGGING - LDAP_LOG( CACHE, INFO, - "bdb_cache_add_entry: can't delete (%s) from cache.\n", - e->e_dn, 0, 0 ); -#else - Debug( LDAP_DEBUG_ANY, "====> can't delete from dn cache\n", 0, 0, 0 ); -#endif - } - /* fall through */ - default : - bdb_cache_entry_private_destroy(e); - ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock ); - return rc; - } - - /* put the entry into 'CREATING' state */ - /* will be marked after when entry is returned */ - BEI(e)->bei_state = CACHE_ENTRY_CREATING; - BEI(e)->bei_refcnt = 1; - - /* set lru mutex */ - ldap_pvt_thread_mutex_lock( &cache->lru_mutex ); - /* lru */ - LRU_ADD( cache, e ); - if ( ++cache->c_cursize > cache->c_maxsize ) { - /* - * find the lru entry not currently in use and delete it. - * in case a lot of entries are in use, only look at the - * first 10 on the tail of the list. - */ - i = 0; - while ( cache->c_lrutail != NULL && - BEI(cache->c_lrutail)->bei_refcnt != 0 && - i < 10 ) - { - /* move this in-use entry to the front of the q */ - ee = cache->c_lrutail; - LRU_DELETE( cache, ee ); - LRU_ADD( cache, ee ); - i++; - } - - /* - * found at least one to delete - try to get back under - * the max cache size. - */ - while ( cache->c_lrutail != NULL && - BEI(cache->c_lrutail)->bei_refcnt == 0 && - cache->c_cursize > cache->c_maxsize ) - { - e = cache->c_lrutail; - - /* delete from cache and lru q */ - /* XXX do we need rc ? */ - rc = bdb_cache_delete_entry_internal( cache, e ); - bdb_cache_entry_private_destroy( e ); - bdb_entry_return( e ); - } - } + const EntryInfo *e1 = v_e1, *e2 = v_e2; + int rc = strncmp( e1->bei_nrdn.bv_val, e2->bei_nrdn.bv_val, e1->bei_nrdn.bv_len ); + if (rc == 0) rc = e1->bei_nrdn.bv_len - e2->bei_nrdn.bv_len; + return rc; +} - /* free lru mutex */ - ldap_pvt_thread_mutex_unlock( &cache->lru_mutex ); - /* free cache write lock */ - ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock ); - return( 0 ); +static int +bdb_id_cmp( const void *v_e1, const void *v_e2 ) +{ + const EntryInfo *e1 = v_e1, *e2 = v_e2; + return e1->bei_id - e2->bei_id; } -/* - * cache_update_entry - update a LOCKED entry which has been deleted. - * returns: 0 entry has been created and locked - * 1 entry already existed - * -1 something bad happened +/* Create an entryinfo in the cache. Caller must release the locks later. */ int -bdb_cache_update_entry( - Cache *cache, - Entry *e +bdb_entryinfo_add_internal( + struct bdb_info *bdb, + EntryInfo *eip, + ID id, + struct berval *nrdn, + EntryInfo **res, + u_int32_t locker ) { - int i, rc; - Entry *ee; - - /* set cache write lock */ - ldap_pvt_thread_rdwr_wlock( &cache->c_rwlock ); + Cache *cache = &bdb->bi_cache; + DB_ENV *env = bdb->bi_dbenv; + EntryInfo *ei2 = NULL; + int incr = 1; + int addkid = 1; + int rc; + DB_LOCK lock; - assert( e->e_private ); + *res = NULL; - if ( avl_insert( &cache->c_dntree, (caddr_t) e, - entry_dn_cmp, avl_dup_error ) != 0 ) - { -#ifdef NEW_LOGGING - LDAP_LOG( CACHE, DETAIL1, - "bdb_cache_update_entry: (%s):%ld already in dn cache\n", - e->e_dn, e->e_id, 0 ); -#else - Debug( LDAP_DEBUG_TRACE, - "====> bdb_cache_update_entry( %ld ): \"%s\": already in dn cache\n", - e->e_id, e->e_dn, 0 ); -#endif + ldap_pvt_thread_rdwr_wlock( &bdb->bi_cache.c_rwlock ); + bdb_cache_entryinfo_lock( eip ); - /* free cache write lock */ - ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock ); - return( 1 ); + /* if parent was previously considered a leaf node, + * it was on the LRU list. Now it's going to have + * kids, take it off the LRU list. + */ + ldap_pvt_thread_mutex_lock( &cache->lru_mutex ); + if ( eip->bei_id && !eip->bei_kids ) { + LRU_DELETE( cache, eip ); + incr = 0; } - /* id tree */ - if ( avl_insert( &cache->c_idtree, (caddr_t) e, - entry_id_cmp, avl_dup_error ) != 0 ) - { -#ifdef NEW_LOGGING - LDAP_LOG( CACHE, DETAIL1, - "bdb_cache_update_entry: (%s)%ld already in id cache\n", - e->e_dn, e->e_id, 0 ); -#else - Debug( LDAP_DEBUG_ANY, - "====> bdb_cache_update_entry( %ld ): \"%s\": already in id cache\n", - e->e_id, e->e_dn, 0 ); -#endif - - /* delete from dn tree inserted above */ - if ( avl_delete( &cache->c_dntree, (caddr_t) e, - entry_dn_cmp ) == NULL ) - { -#ifdef NEW_LOGGING - LDAP_LOG( CACHE, INFO, - "bdb_cache_update_entry: can't delete (%s)%ld from dn cache.\n", - e->e_dn, e->e_id, 0 ); -#else - Debug( LDAP_DEBUG_ANY, "====> can't delete from dn cache\n", - 0, 0, 0 ); -#endif - } + cache->c_cursize += incr; - /* free cache write lock */ - ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock ); - return( -1 ); - } + /* See if we're above the cache size limit */ + if ( cache->c_cursize > cache->c_maxsize ) { + EntryInfo *elru, *elprev; + int i = 0; + /* Look for an unused entry to remove */ + for (elru = cache->c_lrutail; elru; elru = elprev, i++ ) { + elprev = elru->bei_lruprev; - /* put the entry into 'CREATING' state */ - /* will be marked after when entry is returned */ - BEI(e)->bei_state = CACHE_ENTRY_CREATING; + /* Too many probes, not enough idle, give up */ + if (i > 10) break; - /* set lru mutex */ - ldap_pvt_thread_mutex_lock( &cache->lru_mutex ); - /* lru */ - LRU_ADD( cache, e ); - if ( ++cache->c_cursize > cache->c_maxsize ) { - /* - * find the lru entry not currently in use and delete it. - * in case a lot of entries are in use, only look at the - * first 10 on the tail of the list. - */ - i = 0; - while ( cache->c_lrutail != NULL && - BEI(cache->c_lrutail)->bei_refcnt != 0 && - i < 10 ) - { - /* move this in-use entry to the front of the q */ - ee = cache->c_lrutail; - LRU_DELETE( cache, ee ); - LRU_ADD( cache, ee ); - i++; + /* If we can successfully writelock it, then + * the object is idle. + */ + if ( bdb_cache_entry_db_lock( env, locker, elru, 1, 1, + &lock ) == 0 ) { + /* Need to lock parent to delete child */ + if ( ldap_pvt_thread_mutex_trylock( + &elru->bei_parent->bei_kids_mutex )) { + bdb_cache_entry_db_unlock( env, &lock ); + continue; + } + bdb_cache_delete_entry_internal( cache, elru ); + bdb_cache_entryinfo_unlock( elru->bei_parent ); + elru->bei_e->e_private = NULL; + bdb_entry_return( elru->bei_e ); + bdb_cache_entry_db_unlock( env, &lock ); + if (ei2) { + bdb_cache_entryinfo_destroy( elru ); + } else { + /* re-use this one */ + ch_free(elru->bei_nrdn.bv_val); + elru->bei_nrdn.bv_val = NULL; + elru->bei_e = NULL; + elru->bei_kids = NULL; + elru->bei_lrunext = NULL; + elru->bei_lruprev = NULL; + elru->bei_state = 0; + ei2 = elru; + } + if (cache->c_cursize < cache->c_maxsize) + break; + } } + } + if (!ei2) { + ei2 = bdb_cache_entryinfo_new(); + } + ei2->bei_id = id; + ei2->bei_parent = eip; + + /* Add to cache ID tree */ + if (avl_insert( &cache->c_idtree, ei2, bdb_id_cmp, avl_dup_error )) { + EntryInfo *ei; + ei = avl_find( cache->c_idtree, ei2, bdb_id_cmp ); + bdb_cache_entryinfo_destroy( ei2 ); + ei2 = ei; + addkid = 0; + cache->c_cursize -= incr; + } else { + LRU_ADD( cache, ei2 ); + ber_dupbv( &ei2->bei_nrdn, nrdn ); + } - /* - * found at least one to delete - try to get back under - * the max cache size. - */ - while ( cache->c_lrutail != NULL && - BEI(cache->c_lrutail)->bei_refcnt == 0 && - cache->c_cursize > cache->c_maxsize ) - { - e = cache->c_lrutail; - - /* delete from cache and lru q */ - /* XXX do we need rc ? */ - rc = bdb_cache_delete_entry_internal( cache, e ); - bdb_cache_entry_private_destroy( e ); - bdb_entry_return( e ); - } + if ( addkid ) { + avl_insert( &eip->bei_kids, ei2, bdb_rdn_cmp, avl_dup_error ); } - /* free lru mutex */ ldap_pvt_thread_mutex_unlock( &cache->lru_mutex ); - /* free cache write lock */ + +#if 0 /* caller must do these frees */ ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock ); - return( 0 ); + bdb_cache_entryinfo_unlock( eip ); +#endif + + *res = ei2; + return 0; } -ID +/* Find the EntryInfo for the requested DN. If the DN cannot be found, return + * the info for its closest ancestor. *res should be NULL to process a + * complete DN starting from the tree root. Otherwise *res must be the + * immediate parent of the requested DN, and only the RDN will be searched. + * The EntryInfo is locked upon return and must be unlocked by the caller. + */ +int bdb_cache_find_entry_ndn2id( Backend *be, - Cache *cache, - struct berval *ndn + DB_TXN *txn, + struct berval *ndn, + EntryInfo **res, + u_int32_t locker, + void *ctx ) { - Entry e, *ep; + struct bdb_info *bdb = (struct bdb_info *) be->be_private; + EntryInfo ei, *eip, *ei2; ID id; - int count = 0; + int rc = 0; + char *ptr; /* this function is always called with normalized DN */ - e.e_nname = *ndn; - -try_again: - /* set cache read lock */ - ldap_pvt_thread_rdwr_rlock( &cache->c_rwlock ); - - if ( (ep = (Entry *) avl_find( cache->c_dntree, (caddr_t) &e, - entry_dn_cmp )) != NULL ) - { - int state; - count++; - - /* - * ep now points to an unlocked entry - * we do not need to lock the entry if we only - * check the state, refcnt, LRU, and id. - */ - - assert( ep->e_private ); - - /* save id */ - id = ep->e_id; - state = BEI(ep)->bei_state; - - /* - * entry is deleted or not fully created yet - */ - if ( state != CACHE_ENTRY_READY && state != CACHE_ENTRY_COMMITTED ) { - assert(state != CACHE_ENTRY_UNDEFINED); - - /* free cache read lock */ - ldap_pvt_thread_rdwr_runlock( &cache->c_rwlock ); - -#ifdef NEW_LOGGING - LDAP_LOG( CACHE, INFO, - "bdb_cache_find_entry_dn2id: (%s) %ld not ready: %d\n", - ndn->bv_val, id, state ); -#else - Debug(LDAP_DEBUG_TRACE, - "====> bdb_cache_find_entry_dn2id(\"%s\"): %ld (not ready) %d\n", - ndn->bv_val, id, state); -#endif - + if ( *res ) { + /* we're doing a onelevel search for an RDN */ + ei.bei_nrdn.bv_val = ndn->bv_val; + ei.bei_nrdn.bv_len = dn_rdnlen( be, ndn ); + eip = *res; + } else { + /* we're searching a full DN from the root */ + ptr = ndn->bv_val + ndn->bv_len - be->be_nsuffix[0].bv_len; + ei.bei_nrdn.bv_val = ptr; + ei.bei_nrdn.bv_len = be->be_nsuffix[0].bv_len; + eip = &bdb->bi_cache.c_dntree; + } + + for ( bdb_cache_entryinfo_lock( eip ); eip; ) { + ei2 = (EntryInfo *)avl_find( eip->bei_kids, &ei, bdb_rdn_cmp ); + if ( !ei2 ) { + int len = ei.bei_nrdn.bv_len; + + ei.bei_nrdn.bv_len = ndn->bv_len - (ei.bei_nrdn.bv_val - ndn->bv_val); + bdb_cache_entryinfo_unlock( eip ); + + rc = bdb_dn2id( be, txn, &ei.bei_nrdn, &id, ctx ); + if (rc) { + bdb_cache_entryinfo_lock( eip ); + *res = eip; + return rc; + } + /* DN exists but needs to be added to cache */ + ei.bei_nrdn.bv_len = len; + rc = bdb_entryinfo_add_internal( bdb, + eip, id, &ei.bei_nrdn, &ei2, locker ); + /* add_internal left eip and c_rwlock locked */ + ldap_pvt_thread_rdwr_wunlock( &bdb->bi_cache.c_rwlock ); + if ( rc ) { + *res = eip; + return rc; + } + } else if ( ei2->bei_state == CACHE_ENTRY_DELETED ) { + /* In the midst of deleting? Give it a chance to + * complete. + */ + bdb_cache_entryinfo_unlock( eip ); ldap_pvt_thread_yield(); - goto try_again; + bdb_cache_entryinfo_lock( eip ); + *res = eip; + return DB_NOTFOUND; + } + bdb_cache_entryinfo_unlock( eip ); + bdb_cache_entryinfo_lock( ei2 ); + + eip = ei2; + + /* Advance to next lower RDN */ + for (ptr = ei.bei_nrdn.bv_val - 2; ptr > ndn->bv_val + && !DN_SEPARATOR(*ptr); ptr--); + if ( ptr >= ndn->bv_val ) { + if (DN_SEPARATOR(*ptr)) ptr++; + ei.bei_nrdn.bv_len = ei.bei_nrdn.bv_val - ptr - 1; + ei.bei_nrdn.bv_val = ptr; + } + if ( ptr < ndn->bv_val ) { + *res = eip; + break; } - - /* free cache read lock */ - ldap_pvt_thread_rdwr_runlock( &cache->c_rwlock ); - - /* set lru mutex */ - ldap_pvt_thread_mutex_lock( &cache->lru_mutex ); - - /* lru */ - LRU_DELETE( cache, ep ); - LRU_ADD( cache, ep ); - - /* free lru mutex */ - ldap_pvt_thread_mutex_unlock( &cache->lru_mutex ); - -#ifdef NEW_LOGGING - LDAP_LOG( CACHE, DETAIL1, - "bdb_cache_find_entry_dn2id: (%s): %ld %d tries\n", - ndn->bv_val, id, count ); -#else - Debug(LDAP_DEBUG_TRACE, - "====> bdb_cache_find_entry_dn2id(\"%s\"): %ld (%d tries)\n", - ndn->bv_val, id, count); -#endif - - } else { - /* free cache read lock */ - ldap_pvt_thread_rdwr_runlock( &cache->c_rwlock ); - - id = NOID; } - return( id ); + return rc; } /* - * cache_find_entry_id - find an entry in the cache, given id + * cache_find_entry_id - find an entry in the cache, given id. + * The entry is locked for Read upon return. Call with islocked TRUE if + * the supplied *eip was already locked. */ -Entry * +int bdb_cache_find_entry_id( - DB_ENV *env, - Cache *cache, + Backend *be, + DB_TXN *tid, ID id, - int rw, + EntryInfo **eip, + int islocked, u_int32_t locker, - DB_LOCK *lock + DB_LOCK *lock, + void *ctx ) { - Entry e; - Entry *ep; - int count = 0; - int rc; - - e.e_id = id; - -try_again: - /* set cache read lock */ - ldap_pvt_thread_rdwr_rlock( &cache->c_rwlock ); - - if ( (ep = (Entry *) avl_find( cache->c_idtree, (caddr_t) &e, - entry_id_cmp )) != NULL ) - { - int state; - ID ep_id; - - count++; - - assert( ep->e_private ); - - ep_id = ep->e_id; - state = BEI(ep)->bei_state; - - /* - * entry is deleted or not fully created yet - */ - if ( state != CACHE_ENTRY_READY && state != CACHE_ENTRY_COMMITTED ) { - - assert(state != CACHE_ENTRY_UNDEFINED); - - /* free cache read lock */ - ldap_pvt_thread_rdwr_runlock( &cache->c_rwlock ); + struct bdb_info *bdb = (struct bdb_info *) be->be_private; + Entry *ep = NULL; + int rc = 0; + EntryInfo ei; + + ei.bei_id = id; + + /* If we weren't given any info, see if we have it already cached */ + if ( !*eip ) { + ldap_pvt_thread_rdwr_rlock( &bdb->bi_cache.c_rwlock ); + *eip = (EntryInfo *) avl_find( bdb->bi_cache.c_idtree, + (caddr_t) &ei, bdb_id_cmp ); + if ( *eip ) { + bdb_cache_entryinfo_lock( *eip ); + islocked = 1; + } + ldap_pvt_thread_rdwr_runlock( &bdb->bi_cache.c_rwlock ); + } -#ifdef NEW_LOGGING - LDAP_LOG( CACHE, INFO, - "bdb_cache_find_entry_id: (%ld)->%ld not ready (%d).\n", - id, ep_id, state ); - -#else - Debug(LDAP_DEBUG_TRACE, - "====> bdb_cache_find_entry_id( %ld ): %ld (not ready) %d\n", - id, ep_id, state); -#endif + /* See if the ID exists in the database; add it to the cache if so */ + if ( !*eip ) { + rc = bdb_id2entry( be, tid, id, &ep ); + if ( rc == 0 ) { + rc = bdb_cache_find_entry_ndn2id( be, tid, + &ep->e_nname, eip, locker, ctx ); + if ( *eip ) + islocked = 1; + if ( rc ) { + bdb_entry_return( ep ); + ep = NULL; + } + } + } - ldap_pvt_thread_yield(); - goto try_again; + /* Ok, we found the info, do we have the entry? */ + if ( *eip && rc == 0 ) { + if ( (*eip)->bei_state == CACHE_ENTRY_DELETED ) { + rc = DB_NOTFOUND; + } else if (!(*eip)->bei_e ) { + if (!ep) { + rc = bdb_id2entry( be, tid, id, &ep ); + } + if ( rc == 0 ) { + bdb_cache_entry_db_lock( bdb->bi_dbenv, locker, + *eip, 1, 0, lock ); + (*eip)->bei_e = ep; + ep->e_private = *eip; + bdb_cache_entry_db_relock( bdb->bi_dbenv, locker, + *eip, 0, 0, lock ); + } + } else { + bdb_cache_entry_db_lock( bdb->bi_dbenv, locker, + *eip, 0, 0, lock ); } + } + if ( rc == 0 && (*eip)->bei_kids == NULL ) { + /* set lru mutex */ + ldap_pvt_thread_mutex_lock( &bdb->bi_cache.lru_mutex ); + LRU_DELETE( &bdb->bi_cache, *eip ); + LRU_ADD( &bdb->bi_cache, *eip ); + ldap_pvt_thread_mutex_unlock( &bdb->bi_cache.lru_mutex ); + } - /* acquire reader lock */ - rc = bdb_cache_entry_db_lock ( env, locker, ep, rw, 0, lock ); + if ( islocked ) { + bdb_cache_entryinfo_unlock( *eip ); + } + return rc; +} -#if 0 - if ( bdb_cache_entry_rdwr_trylock(ep, rw) == LDAP_PVT_THREAD_EBUSY ) { -#endif +/* Update the cache after a successful database Add. */ +int +bdb_cache_add( + struct bdb_info *bdb, + EntryInfo *ei, + Entry *e, + struct berval *nrdn, + u_int32_t locker +) +{ + EntryInfo *new; + int rc; - if ( rc ) { /* will be changed to retry beyond threshold */ - /* could not acquire entry lock... - * owner cannot free as we have the cache locked. - * so, unlock the cache, yield, and try again. - */ + rc = bdb_entryinfo_add_internal( bdb, ei, e->e_id, nrdn, &new, locker ); + new->bei_e = e; + e->e_private = new; + bdb_cache_entryinfo_unlock( ei ); + ldap_pvt_thread_rdwr_wunlock( &bdb->bi_cache.c_rwlock ); + return rc; +} - /* free cache read lock */ - ldap_pvt_thread_rdwr_runlock( &cache->c_rwlock ); +int +bdb_cache_modify( + Entry *e, + Attribute *newAttrs, + DB_ENV *env, + u_int32_t locker, + DB_LOCK *lock +) +{ + EntryInfo *ei = BEI(e); + + /* Get write lock on data */ + bdb_cache_entry_db_relock( env, locker, ei, 1, 0, lock ); -#ifdef NEW_LOGGING - LDAP_LOG( CACHE, INFO, - "bdb_cache_find_entry_id: %ld -> %ld (busy) %d.\n", - id, ep_id, state ); -#else - Debug(LDAP_DEBUG_TRACE, - "====> bdb_cache_find_entry_id( %ld ): %ld (busy) %d\n", - id, ep_id, state); - Debug(LDAP_DEBUG_TRACE, - "locker = %d\n", - locker, 0, 0); -#endif + /* If we've done repeated mods on a cached entry, then e_attrs + * is no longer contiguous with the entry, and must be freed. + */ + if ( (void *)e->e_attrs != (void *)(e+1) ) { + attrs_free( e->e_attrs ); + } + e->e_attrs = newAttrs; - ldap_pvt_thread_yield(); - goto try_again; - } + return 0; +} - /* free cache read lock */ - ldap_pvt_thread_rdwr_runlock( &cache->c_rwlock ); - /* set lru mutex */ - ldap_pvt_thread_mutex_lock( &cache->lru_mutex ); - /* lru */ - LRU_DELETE( cache, ep ); - LRU_ADD( cache, ep ); - - BEI(ep)->bei_refcnt++; +/* + * Change the rdn in the entryinfo. Also move to a new parent if needed. + */ +int +bdb_cache_modrdn( + Entry *e, + struct berval *nrdn, + Entry *new, + EntryInfo *ein, + DB_ENV *env, + u_int32_t locker, + DB_LOCK *lock +) +{ + EntryInfo *ei = BEI(e), *pei; + int rc = 0; - /* free lru mutex */ - ldap_pvt_thread_mutex_unlock( &cache->lru_mutex ); + /* Get write lock on data */ + bdb_cache_entry_db_relock( env, locker, ei, 1, 0, lock ); -#ifdef NEW_LOGGING - LDAP_LOG( CACHE, DETAIL1, - "bdb_cache_find_entry_id: %ld -> %s found %d tries.\n", - ep_id, ep->e_dn, count ); -#else - Debug(LDAP_DEBUG_TRACE, - "====> bdb_cache_find_entry_id( %ld ) \"%s\" (found) (%d tries)\n", - ep_id, ep->e_dn, count); + /* If we've done repeated mods on a cached entry, then e_attrs + * is no longer contiguous with the entry, and must be freed. + */ + if ( (void *)e->e_attrs != (void *)(e+1) ) { + attrs_free( e->e_attrs ); + } + e->e_attrs = new->e_attrs; +#ifdef BDB_HIER + ch_free(e->e_name.bv_val); +#else + if( e->e_nname.bv_val < e->e_bv.bv_val || e->e_nname.bv_val > + e->e_bv.bv_val + e->e_bv.bv_len ) { + ch_free(e->e_name.bv_val); + ch_free(e->e_nname.bv_val); + } #endif - - - return( ep ); + e->e_name = new->e_name; + e->e_nname = new->e_nname; + + /* Lock the parent's kids AVL tree */ + pei = ei->bei_parent; + bdb_cache_entryinfo_lock( pei ); + avl_delete( &pei->bei_kids, (caddr_t) ei, bdb_rdn_cmp ); + free( ei->bei_nrdn.bv_val ); + ber_dupbv( &ei->bei_nrdn, nrdn ); + if (!ein) { + ein = ei->bei_parent; + } else { + ei->bei_parent = ein; + bdb_cache_entryinfo_unlock( pei ); + bdb_cache_entryinfo_lock( ein ); } - - /* free cache read lock */ - ldap_pvt_thread_rdwr_runlock( &cache->c_rwlock ); - - return( NULL ); + avl_insert( &ein->bei_kids, ei, bdb_rdn_cmp, avl_dup_error ); + bdb_cache_entryinfo_unlock( ein ); + return rc; } - /* - * cache_delete_entry - delete the entry e from the cache. the caller - * should have obtained e (increasing its ref count) via a call to one - * of the cache_find_* routines. the caller should *not* call the - * cache_return_entry() routine prior to calling cache_delete_entry(). - * it performs this function. + * cache_delete_entry - delete the entry e from the cache. * * returns: 0 e was deleted ok * 1 e was not in the cache @@ -1004,15 +588,28 @@ try_again: int bdb_cache_delete_entry( Cache *cache, - Entry *e + Entry *e, + DB_ENV *env, + u_int32_t locker, + DB_LOCK *lock ) { + EntryInfo *ei = BEI(e); int rc; + assert( e->e_private ); + + /* Set this early, warn off any queriers */ + ei->bei_state = CACHE_ENTRY_DELETED; + + /* Get write lock on the data */ + bdb_cache_entry_db_relock( env, locker, ei, 1, 0, lock ); + /* set cache write lock */ ldap_pvt_thread_rdwr_wlock( &cache->c_rwlock ); - assert( e->e_private ); + /* Lock the parent's kids tree */ + bdb_cache_entryinfo_lock( ei->bei_parent ); #ifdef NEW_LOGGING LDAP_LOG( CACHE, ENTRY, @@ -1024,31 +621,40 @@ bdb_cache_delete_entry( /* set lru mutex */ ldap_pvt_thread_mutex_lock( &cache->lru_mutex ); - rc = bdb_cache_delete_entry_internal( cache, e ); + rc = bdb_cache_delete_entry_internal( cache, e->e_private ); /* free lru mutex */ ldap_pvt_thread_mutex_unlock( &cache->lru_mutex ); /* free cache write lock */ ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock ); + bdb_cache_entryinfo_unlock( ei->bei_parent ); + bdb_cache_entryinfo_destroy( ei ); + e->e_private = NULL; return( rc ); } static int bdb_cache_delete_entry_internal( Cache *cache, - Entry *e + EntryInfo *e ) { int rc = 0; /* return code */ /* dn tree */ - if ( avl_delete( &cache->c_dntree, (caddr_t) e, entry_dn_cmp ) == NULL ) + if ( avl_delete( &e->bei_parent->bei_kids, (caddr_t) e, bdb_rdn_cmp ) == NULL ) { rc = -1; } + /* If parent has no more kids, put in on LRU list */ + if ( e->bei_parent->bei_kids == NULL ) { + LRU_ADD( cache, e->bei_parent ); + cache->c_cursize++; + } + /* id tree */ - if ( avl_delete( &cache->c_idtree, (caddr_t) e, entry_id_cmp ) == NULL ) + if ( avl_delete( &cache->c_idtree, (caddr_t) e, bdb_id_cmp ) == NULL ) { rc = -1; } @@ -1064,17 +670,26 @@ bdb_cache_delete_entry_internal( /* * flag entry to be freed later by a call to cache_return_entry() */ - BEI(e)->bei_state = CACHE_ENTRY_DELETED; + e->bei_state = CACHE_ENTRY_DELETED; return( 0 ); } +static void +bdb_entryinfo_release( void *data ) +{ + EntryInfo *ei = (EntryInfo *)data; + avl_free( ei->bei_kids, NULL ); + if ( ei->bei_e ) { + ei->bei_e->e_private = NULL; + bdb_entry_return( ei->bei_e ); + } + bdb_cache_entryinfo_destroy( ei ); +} + void bdb_cache_release_all( Cache *cache ) { - Entry *e; - int rc; - /* set cache write lock */ ldap_pvt_thread_rdwr_wlock( &cache->c_rwlock ); /* set lru mutex */ @@ -1086,27 +701,10 @@ bdb_cache_release_all( Cache *cache ) Debug( LDAP_DEBUG_TRACE, "====> bdb_cache_release_all\n", 0, 0, 0 ); #endif - while ( (e = cache->c_lrutail) != NULL && BEI(e)->bei_refcnt == 0 ) { -#ifdef LDAP_RDWR_DEBUG - assert(!ldap_pvt_thread_rdwr_active(&BEI(e)->bei_rdwr)); -#endif - - /* delete from cache and lru q */ - /* XXX do we need rc ? */ - rc = bdb_cache_delete_entry_internal( cache, e ); - bdb_cache_entry_private_destroy( e ); - bdb_entry_return( e ); - } - - if ( cache->c_cursize ) { -#ifdef NEW_LOGGING - LDAP_LOG( CACHE, INFO, - "bdb_cache_release_all: Entry cache could not be emptied.\n", 0, 0, 0 ); -#else - Debug( LDAP_DEBUG_TRACE, "Entry-cache could not be emptied\n", 0, 0, 0 ); -#endif - - } + avl_free( cache->c_dntree.bei_kids, NULL ); + avl_free( cache->c_idtree, bdb_entryinfo_release ); + cache->c_lruhead = NULL; + cache->c_lrutail = NULL; /* free lru mutex */ ldap_pvt_thread_mutex_unlock( &cache->lru_mutex ); @@ -1118,17 +716,17 @@ bdb_cache_release_all( Cache *cache ) static void bdb_lru_print( Cache *cache ) { - Entry *e; + EntryInfo *e; fprintf( stderr, "LRU queue (head to tail):\n" ); - for ( e = cache->c_lruhead; e != NULL; e = BEI(e)->bei_lrunext ) { - fprintf( stderr, "\tdn \"%20s\" id %ld refcnt %d\n", - e->e_dn, e->e_id, BEI(e)->bei_refcnt ); + for ( e = cache->c_lruhead; e != NULL; e = e->bei_lrunext ) { + fprintf( stderr, "\trdn \"%20s\" id %ld\n", + e->bei_nrdn.bv_val, e->bei_id ); } fprintf( stderr, "LRU queue (tail to head):\n" ); - for ( e = cache->c_lrutail; e != NULL; e = BEI(e)->bei_lruprev ) { - fprintf( stderr, "\tdn \"%20s\" id %ld refcnt %d\n", - e->e_dn, e->e_id, BEI(e)->bei_refcnt ); + for ( e = cache->c_lrutail; e != NULL; e = e->bei_lruprev ) { + fprintf( stderr, "\trdn \"%20s\" id %ld\n", + e->bei_nrdn.bv_val, e->bei_id ); } } #endif diff --git a/servers/slapd/back-bdb/compare.c b/servers/slapd/back-bdb/compare.c index 65b9e6de9b..3c650cae32 100644 --- a/servers/slapd/back-bdb/compare.c +++ b/servers/slapd/back-bdb/compare.c @@ -17,8 +17,8 @@ int bdb_compare( Operation *op, SlapReply *rs ) { struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private; - Entry *matched; Entry *e; + EntryInfo *ei; Attribute *a; int manageDSAit = get_manageDSAit( op ); @@ -36,7 +36,7 @@ bdb_compare( Operation *op, SlapReply *rs ) dn2entry_retry: /* get entry */ - rs->sr_err = bdb_dn2entry_r( op->o_bd, NULL, &op->o_req_ndn, &e, &matched, 0, locker, &lock ); + rs->sr_err = bdb_dn2entry( op->o_bd, NULL, &op->o_req_ndn, &ei, 1, locker, &lock, op->o_tmpmemctx ); switch( rs->sr_err ) { case DB_NOTFOUND: @@ -54,14 +54,15 @@ dn2entry_retry: goto return_results; } - if ( e == NULL ) { - if ( matched != NULL ) { - rs->sr_matched = ch_strdup( matched->e_dn ); - rs->sr_ref = is_entry_referral( matched ) - ? get_entry_referrals( op, matched ) + e = ei->bei_e; + if ( rs->sr_err == DB_NOTFOUND ) { + if ( e != NULL ) { + rs->sr_matched = ch_strdup( e->e_dn ); + rs->sr_ref = is_entry_referral( e ) + ? get_entry_referrals( op, e ) : NULL; - bdb_cache_return_entry_r( bdb->bi_dbenv, &bdb->bi_cache, matched, &lock ); - matched = NULL; + bdb_cache_return_entry_r( bdb->bi_dbenv, &bdb->bi_cache, e, &lock ); + e = NULL; } else { rs->sr_ref = referral_rewrite( default_referral, diff --git a/servers/slapd/back-bdb/delete.c b/servers/slapd/back-bdb/delete.c index bb7c90def3..ac18552536 100644 --- a/servers/slapd/back-bdb/delete.c +++ b/servers/slapd/back-bdb/delete.c @@ -21,14 +21,15 @@ bdb_delete( Operation *op, SlapReply *rs ) struct berval pdn = {0, NULL}; Entry *e = NULL; Entry *p = NULL; + EntryInfo *ei = NULL, *eip = NULL; int manageDSAit = get_manageDSAit( op ); AttributeDescription *children = slap_schema.si_ad_children; AttributeDescription *entry = slap_schema.si_ad_entry; - DB_TXN *ltid = NULL; + DB_TXN *ltid = NULL, *lt2; struct bdb_op_info opinfo; u_int32_t locker = 0; - DB_LOCK lock; + DB_LOCK lock, plock; int noop = 0; @@ -47,6 +48,7 @@ bdb_delete( Operation *op, SlapReply *rs ) retry: /* transaction retry */ if( e != NULL ) { bdb_unlocked_cache_return_entry_w(&bdb->bi_cache, e); + e = NULL; } #ifdef NEW_LOGGING LDAP_LOG ( OPERATION, DETAIL1, @@ -99,27 +101,38 @@ retry: /* transaction retry */ dnParent( &op->o_req_ndn, &pdn ); } - if( pdn.bv_len != 0 ) { - /* get parent */ - rs->sr_err = bdb_dn2entry_r( op->o_bd, ltid, &pdn, &p, NULL, 0, locker, &lock ); + /* get entry */ + rs->sr_err = bdb_dn2entry( op->o_bd, ltid, &op->o_req_ndn, &ei, 1, + locker, &lock, op->o_tmpmemctx ); - switch( rs->sr_err ) { - case 0: - case DB_NOTFOUND: - break; - case DB_LOCK_DEADLOCK: - case DB_LOCK_NOTGRANTED: - goto retry; - case LDAP_BUSY: - rs->sr_text = "ldap server busy"; - goto return_results; - default: - rs->sr_err = LDAP_OTHER; - rs->sr_text = "internal error"; - goto return_results; - } + switch( rs->sr_err ) { + case 0: + case DB_NOTFOUND: + break; + case DB_LOCK_DEADLOCK: + case DB_LOCK_NOTGRANTED: + goto retry; + case LDAP_BUSY: + rs->sr_text = "ldap server busy"; + goto return_results; + default: + rs->sr_err = LDAP_OTHER; + rs->sr_text = "internal error"; + goto return_results; + } + + if ( rs->sr_err == 0 ) { + e = ei->bei_e; + eip = ei->bei_parent; + bdb_cache_find_entry_id( op->o_bd, ltid, eip->bei_id, &eip, + 0, locker, &plock, op->o_tmpmemctx ); + } + if ( eip ) { + p = eip->bei_e; + } - if( p == NULL) { + if ( pdn.bv_len != 0 ) { + if( p == NULL || !bvmatch( &pdn, &p->e_nname )) { #ifdef NEW_LOGGING LDAP_LOG ( OPERATION, DETAIL1, "<=- bdb_delete: parent does not exist\n", 0, 0, 0 ); @@ -208,25 +221,6 @@ retry: /* transaction retry */ } } - /* get entry for read/modify/write */ - rs->sr_err = bdb_dn2entry_w( op->o_bd, ltid, &op->o_req_ndn, &e, &matched, DB_RMW, locker, &lock ); - - switch( rs->sr_err ) { - case 0: - case DB_NOTFOUND: - break; - case DB_LOCK_DEADLOCK: - case DB_LOCK_NOTGRANTED: - goto retry; - case LDAP_BUSY: - rs->sr_text = "ldap server busy"; - goto return_results; - default: - rs->sr_err = LDAP_OTHER; - rs->sr_text = "internal error"; - goto return_results; - } - if ( e == NULL ) { #ifdef NEW_LOGGING LDAP_LOG ( OPERATION, ARGS, @@ -310,7 +304,27 @@ retry: /* transaction retry */ goto done; } - rs->sr_err = bdb_dn2id_children( op->o_bd, ltid, &e->e_nname, 0 ); + /* nested transaction */ + rs->sr_err = TXN_BEGIN( bdb->bi_dbenv, ltid, <2, + bdb->bi_db_opflags ); + rs->sr_text = NULL; + if( rs->sr_err != 0 ) { +#ifdef NEW_LOGGING + LDAP_LOG ( OPERATION, ERR, + "bdb_delete: txn_begin(2) failed: %s (%d)\n", db_strerror(rs->sr_err), rs->sr_err, 0 ); +#else + Debug( LDAP_DEBUG_TRACE, + "bdb_delete: txn_begin(2) failed: %s (%d)\n", + db_strerror(rs->sr_err), rs->sr_err, 0 ); +#endif + rs->sr_err = LDAP_OTHER; + rs->sr_text = "internal error"; + goto return_results; + } + + /* Can't do it if we have kids */ + rs->sr_err = ei->bei_kids ? 0 : bdb_dn2id_children( op->o_bd, lt2, + &e->e_nname, 0 ); if( rs->sr_err != DB_NOTFOUND ) { switch( rs->sr_err ) { case DB_LOCK_DEADLOCK: @@ -345,7 +359,7 @@ retry: /* transaction retry */ } /* delete from dn2id */ - rs->sr_err = bdb_dn2id_delete( op->o_bd, ltid, pdn.bv_val, e ); + rs->sr_err = bdb_dn2id_delete( op->o_bd, lt2, pdn.bv_val, e ); if ( rs->sr_err != 0 ) { switch( rs->sr_err ) { case DB_LOCK_DEADLOCK: @@ -366,7 +380,7 @@ retry: /* transaction retry */ } /* delete from id2entry */ - rs->sr_err = bdb_id2entry_delete( op->o_bd, ltid, e ); + rs->sr_err = bdb_id2entry_delete( op->o_bd, lt2, e ); if ( rs->sr_err != 0 ) { switch( rs->sr_err ) { case DB_LOCK_DEADLOCK: @@ -388,7 +402,7 @@ retry: /* transaction retry */ } /* delete indices for old attributes */ - rs->sr_err = bdb_index_entry_del( op, ltid, e ); + rs->sr_err = bdb_index_entry_del( op, lt2, e ); if ( rs->sr_err != LDAP_SUCCESS ) { switch( rs->sr_err ) { case DB_LOCK_DEADLOCK: @@ -406,6 +420,11 @@ retry: /* transaction retry */ rs->sr_err = LDAP_OTHER; goto return_results; } + if ( TXN_COMMIT( lt2, 0 ) != 0 ) { + rs->sr_err = LDAP_OTHER; + rs->sr_text = "txn_commit(2) failed"; + goto return_results; + } #if 0 /* Do we want to reclaim deleted IDs? */ ldap_pvt_thread_mutex_lock( &bdb->bi_lastid_mutex ); @@ -423,6 +442,8 @@ retry: /* transaction retry */ rs->sr_err = LDAP_SUCCESS; } } else { + bdb_cache_delete_entry( &bdb->bi_cache, e, bdb->bi_dbenv, + locker, &lock ); rs->sr_err = TXN_COMMIT( ltid, 0 ); } ltid = NULL; @@ -477,7 +498,11 @@ return_results: done: /* free entry */ if( e != NULL ) { - bdb_unlocked_cache_return_entry_w(&bdb->bi_cache, e); + if ( rs->sr_err == LDAP_SUCCESS ) { + bdb_entry_return( e ); + } else { + bdb_unlocked_cache_return_entry_w(&bdb->bi_cache, e); + } } if( ltid != NULL ) { diff --git a/servers/slapd/back-bdb/dn2entry.c b/servers/slapd/back-bdb/dn2entry.c index 32df2682fc..913c48ccb2 100644 --- a/servers/slapd/back-bdb/dn2entry.c +++ b/servers/slapd/back-bdb/dn2entry.c @@ -15,48 +15,60 @@ /* * dn2entry - look up dn in the cache/indexes and return the corresponding - * entry. + * entry. If the requested DN is not found and matched is TRUE, return info + * for the closest ancestor of the DN. Otherwise e is NULL. */ int -bdb_dn2entry_rw( +bdb_dn2entry( BackendDB *be, DB_TXN *tid, struct berval *dn, - Entry **e, - Entry **matched, - int flags, - int rw, + EntryInfo **e, + int matched, u_int32_t locker, - DB_LOCK *lock ) + DB_LOCK *lock, + void *ctx ) { + EntryInfo *ei = NULL; int rc; - ID id, id2 = 0; #ifdef NEW_LOGGING - LDAP_LOG ( CACHE, ARGS, "bdb_dn2entry_rw(\"%s\")\n", dn->bv_val, 0, 0 ); + LDAP_LOG ( CACHE, ARGS, "bdb_dn2entry(\"%s\")\n", dn->bv_val, 0, 0 ); #else - Debug(LDAP_DEBUG_TRACE, "bdb_dn2entry_rw(\"%s\")\n", + Debug(LDAP_DEBUG_TRACE, "bdb_dn2entry(\"%s\")\n", dn->bv_val, 0, 0 ); #endif *e = NULL; - if( matched != NULL ) { - *matched = NULL; - rc = bdb_dn2id_matched( be, tid, dn, &id, &id2, flags ); + rc = bdb_cache_find_entry_ndn2id( be, tid, dn, &ei, locker, ctx ); + if ( rc ) { + if ( matched && rc == DB_NOTFOUND ) { + /* Set the return value, whether we have its entry + * or not. + */ + *e = ei; + if ( ei && ei->bei_id ) + bdb_cache_find_entry_id( be, tid, ei->bei_id, + &ei, 1, locker, lock, ctx ); + else if ( ei ) + bdb_cache_entryinfo_unlock( ei ); + } else if ( ei ) { + bdb_cache_entryinfo_unlock( ei ); + } } else { - rc = bdb_dn2id( be, tid, dn, &id, flags ); - } - - if( rc != 0 ) { - return rc; - } - - if( id2 == 0 ) { - rc = bdb_id2entry_rw( be, tid, id, e, rw, locker, lock ); - } else { - rc = bdb_id2entry_r( be, tid, id2, matched, locker, lock ); + rc = bdb_cache_find_entry_id( be, tid, ei->bei_id, &ei, 1, + locker, lock, ctx ); + if ( rc == 0 ) { + *e = ei; + } else if ( matched && rc == DB_NOTFOUND ) { + /* always return EntryInfo */ + ei = ei->bei_parent; + bdb_cache_find_entry_id( be, tid, ei->bei_id, &ei, 1, + locker, lock, ctx ); + *e = ei; + } } return rc; diff --git a/servers/slapd/back-bdb/dn2id.c b/servers/slapd/back-bdb/dn2id.c index d3e0ede957..3352c420f8 100644 --- a/servers/slapd/back-bdb/dn2id.c +++ b/servers/slapd/back-bdb/dn2id.c @@ -299,7 +299,7 @@ bdb_dn2id( DB_TXN *txn, struct berval *dn, ID *id, - int flags ) + void *ctx ) { int rc; DBT key, data; @@ -314,14 +314,9 @@ bdb_dn2id( assert (id); - *id = bdb_cache_find_entry_ndn2id(be, &bdb->bi_cache, dn); - if (*id != NOID) { - return 0; - } - DBTzero( &key ); key.size = dn->bv_len + 2; - key.data = ch_malloc( key.size ); + key.data = sl_malloc( key.size, ctx ); ((char *)key.data)[0] = DN_BASE_PREFIX; AC_MEMCPY( &((char *)key.data)[1], dn->bv_val, key.size - 1 ); @@ -332,7 +327,7 @@ bdb_dn2id( data.flags = DB_DBT_USERMEM; /* fetch it */ - rc = db->get( db, txn, &key, &data, bdb->bi_db_opflags | flags); + rc = db->get( db, txn, &key, &data, bdb->bi_db_opflags ); if( rc != 0 ) { #ifdef NEW_LOGGING @@ -352,134 +347,7 @@ bdb_dn2id( #endif } - ch_free( key.data ); - return rc; -} - -int -bdb_dn2id_matched( - BackendDB *be, - DB_TXN *txn, - struct berval *in, - ID *id, - ID *id2, - int flags ) -{ - int rc; - DBT key, data; - struct bdb_info *bdb = (struct bdb_info *) be->be_private; - DB *db = bdb->bi_dn2id->bdi_db; - char *buf; - struct berval dn; - ID cached_id; - -#ifdef NEW_LOGGING - LDAP_LOG ( INDEX, ARGS, - "=> bdb_dn2id_matched( \"%s\" )\n", in->bv_val, 0, 0 ); -#else - Debug( LDAP_DEBUG_TRACE, "=> bdb_dn2id_matched( \"%s\" )\n", in->bv_val, 0, 0 ); -#endif - - DBTzero( &key ); - key.size = in->bv_len + 2; - buf = ch_malloc( key.size ); - key.data = buf; - dn.bv_val = buf+1; - dn.bv_len = key.size - 2; - AC_MEMCPY( dn.bv_val, in->bv_val, key.size - 1 ); - - /* store the ID */ - DBTzero( &data ); - data.data = id; - data.ulen = sizeof(ID); - data.flags = DB_DBT_USERMEM; - - while(1) { - dn.bv_val[-1] = DN_BASE_PREFIX; - - *id = NOID; - - /* lookup cache */ - cached_id = bdb_cache_find_entry_ndn2id(be, &bdb->bi_cache, &dn); - - if (cached_id != NOID) { - rc = 0; - *id = cached_id; - if ( dn.bv_val != buf+1 ) { - *id2 = *id; - } - break; - } else { - /* fetch it */ - rc = db->get(db, txn, &key, &data, bdb->bi_db_opflags | flags ); - } - - if( rc == DB_NOTFOUND ) { - struct berval pdn; - - if ( ! be_issuffix( be, &dn ) ) { - dnParent( &dn, &pdn ); - } else { -#ifdef NEW_LOGGING - LDAP_LOG ( INDEX, DETAIL1, - "<= bdb_dn2id_matched: no match\n", 0, 0, 0 ); -#else - Debug( LDAP_DEBUG_TRACE, - "<= bdb_dn2id_matched: no match\n", - 0, 0, 0 ); -#endif - break; - } - - key.size = pdn.bv_len + 2; - dn = pdn; - key.data = pdn.bv_val - 1; - - } else if ( rc == 0 ) { - if( data.size != sizeof( ID ) ) { -#ifdef NEW_LOGGING - LDAP_LOG ( INDEX, DETAIL1, - "<= bdb_dn2id_matched: get size mismatch:" - "expected %ld, got %ld\n", - (long) sizeof(ID), (long) data.size, 0 ); -#else - Debug( LDAP_DEBUG_ANY, - "<= bdb_dn2id_matched: get size mismatch: " - "expected %ld, got %ld\n", - (long) sizeof(ID), (long) data.size, 0 ); -#endif - } - - if( dn.bv_val != buf+1 ) { - *id2 = *id; - } - -#ifdef NEW_LOGGING - LDAP_LOG ( INDEX, DETAIL1, - "<= bdb_dn2id_matched: id=0x%08lx: %s %s\n", - (long) *id, *id2 == 0 ? "entry" : "matched", dn.bv_val ); -#else - Debug( LDAP_DEBUG_TRACE, - "<= bdb_dn2id_matched: id=0x%08lx: %s %s\n", - (long) *id, *id2 == 0 ? "entry" : "matched", dn.bv_val ); -#endif - break; - - } else { -#ifdef NEW_LOGGING - LDAP_LOG ( INDEX, ERR, - "<= bdb_dn2id_matched: get failed: %s (%d)\n", - db_strerror(rc), rc, 0 ); -#else - Debug( LDAP_DEBUG_ANY, - "<= bdb_dn2id_matched: get failed: %s (%d)\n", - db_strerror(rc), rc, 0 ); -#endif - break; - } - } - - ch_free( buf ); + sl_free( key.data, ctx ); return rc; } @@ -523,11 +391,11 @@ bdb_dn2id_children( #ifdef NEW_LOGGING LDAP_LOG ( INDEX, DETAIL1, - "<= bdb_dn2id_children( %s ): %schildren (%d)\n", + "<= bdb_dn2id_children( %s ): %s (%d)\n", dn->bv_val, rc == 0 ? "" : ( rc == DB_NOTFOUND ? "no " : db_strerror(rc)), rc ); #else - Debug( LDAP_DEBUG_TRACE, "<= bdb_dn2id_children( %s ): %schildren (%d)\n", + Debug( LDAP_DEBUG_TRACE, "<= bdb_dn2id_children( %s ): %s (%d)\n", dn->bv_val, rc == 0 ? "" : ( rc == DB_NOTFOUND ? "no " : db_strerror(rc) ), rc ); diff --git a/servers/slapd/back-bdb/id2entry.c b/servers/slapd/back-bdb/id2entry.c index 21478685de..e4bc3df9bc 100644 --- a/servers/slapd/back-bdb/id2entry.c +++ b/servers/slapd/back-bdb/id2entry.c @@ -76,14 +76,11 @@ int bdb_id2entry_update( return bdb_id2entry_put(be, tid, e, 0); } -int bdb_id2entry_rw( +int bdb_id2entry( BackendDB *be, DB_TXN *tid, ID id, - Entry **e, - int rw, - u_int32_t locker, - DB_LOCK *lock ) + Entry **e ) { struct bdb_info *bdb = (struct bdb_info *) be->be_private; DB *db = bdb->bi_id2entry->bdi_db; @@ -100,12 +97,8 @@ int bdb_id2entry_rw( DBTzero( &data ); data.flags = DB_DBT_MALLOC; - if ((*e = bdb_cache_find_entry_id(bdb->bi_dbenv, &bdb->bi_cache, id, rw, locker, lock)) != NULL) { - return 0; - } - /* fetch it */ - rc = db->get( db, tid, &key, &data, bdb->bi_db_opflags | ( rw ? DB_RMW : 0 )); + rc = db->get( db, tid, &key, &data, bdb->bi_db_opflags ); if( rc != 0 ) { return rc; @@ -128,38 +121,6 @@ int bdb_id2entry_rw( #ifdef BDB_HIER bdb_fix_dn(be, id, *e); #endif - ret = bdb_cache_add_entry_rw( bdb->bi_dbenv, - &bdb->bi_cache, *e, rw, locker, lock); - while ( ret == 1 || ret == -1 ) { - Entry *ee; - int add_loop_cnt = 0; - if ( (*e)->e_private != NULL ) { - free ((*e)->e_private); - } - (*e)->e_private = NULL; - if ( (ee = bdb_cache_find_entry_id - (bdb->bi_dbenv, &bdb->bi_cache, id, rw, locker, lock) ) != NULL) { - bdb_entry_return ( *e ); - *e = ee; - return 0; - } - if ( ++add_loop_cnt == BDB_MAX_ADD_LOOP ) { - bdb_entry_return ( *e ); - *e = NULL; - return LDAP_BUSY; - } - } - if ( ret != 0 ) { - if ( (*e)->e_private != NULL ) - free ( (*e)->e_private ); - bdb_entry_return( *e ); - *e = NULL; - } - rc = ret; - } - - if (rc == 0) { - bdb_cache_entry_commit(*e); } return rc; @@ -175,8 +136,6 @@ int bdb_id2entry_delete( DBT key; int rc; - bdb_cache_delete_entry(&bdb->bi_cache, e); - DBTzero( &key ); key.data = (char *) &e->e_id; key.size = sizeof(ID); @@ -252,7 +211,7 @@ int bdb_entry_release( bdb_unlocked_cache_return_entry_rw( &bdb->bi_cache, e, rw ); } else { bdb_cache_return_entry_rw( bdb->bi_dbenv, &bdb->bi_cache, e, rw, &boi->boi_lock ); - ch_free( boi ); + sl_free( boi, o->o_tmpmemctx ); o->o_private = NULL; } } else { @@ -279,6 +238,7 @@ int bdb_entry_get( struct bdb_op_info *boi = NULL; DB_TXN *txn = NULL; Entry *e; + EntryInfo *ei; int rc; const char *at_name = at->ad_cname.bv_val; @@ -321,7 +281,7 @@ int bdb_entry_get( dn2entry_retry: /* can we find entry */ - rc = bdb_dn2entry_rw( op->o_bd, txn, ndn, &e, NULL, 0, rw, locker, &lock ); + rc = bdb_dn2entry( op->o_bd, txn, ndn, &ei, 0, locker, &lock, op->o_tmpmemctx ); switch( rc ) { case DB_NOTFOUND: case 0: @@ -342,6 +302,7 @@ dn2entry_retry: } return (rc != LDAP_BUSY) ? LDAP_OTHER : LDAP_BUSY; } + if (ei) e = ei->bei_e; if (e == NULL) { #ifdef NEW_LOGGING LDAP_LOG( BACK_BDB, INFO, @@ -417,7 +378,7 @@ return_results: * release it later?? */ if ( op && !boi ) { - boi = ch_calloc(1,sizeof(struct bdb_op_info)); + boi = sl_calloc(1,sizeof(struct bdb_op_info),op->o_tmpmemctx); boi->boi_lock = lock; op->o_private = boi; } diff --git a/servers/slapd/back-bdb/init.c b/servers/slapd/back-bdb/init.c index 55e3361ac0..d23618ddb9 100644 --- a/servers/slapd/back-bdb/init.c +++ b/servers/slapd/back-bdb/init.c @@ -502,8 +502,6 @@ bdb_db_destroy( BackendDB *be ) #endif } - bdb_cache_release_all (&bdb->bi_cache); - rc = bdb->bi_dbenv->close( bdb->bi_dbenv, 0 ); bdb->bi_dbenv = NULL; if( rc != 0 ) { diff --git a/servers/slapd/back-bdb/modify.c b/servers/slapd/back-bdb/modify.c index 0ac22ed9d0..76380e9f5f 100644 --- a/servers/slapd/back-bdb/modify.c +++ b/servers/slapd/back-bdb/modify.c @@ -41,6 +41,7 @@ int bdb_modify_internal( return LDAP_INSUFFICIENT_ACCESS; } + /* save_attrs will be disposed of by bdb_cache_modify */ save_attrs = e->e_attrs; e->e_attrs = attrs_dup( e->e_attrs ); @@ -255,13 +256,6 @@ int bdb_modify_internal( } } - /* If we've done repeated mods on a cached entry, then e_attrs - * is no longer contiguous with the entry. - */ - if( (void *) save_attrs != (void *) (e+1)) { - attrs_free( save_attrs ); - } - return rc; } @@ -270,13 +264,14 @@ int bdb_modify( Operation *op, SlapReply *rs ) { struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private; - Entry *matched = NULL; Entry *e = NULL; + EntryInfo *ei = NULL; int manageDSAit = get_manageDSAit( op ); char textbuf[SLAP_TEXT_BUFLEN]; size_t textlen = sizeof textbuf; - DB_TXN *ltid = NULL; + DB_TXN *ltid = NULL, *lt2; struct bdb_op_info opinfo; + Entry dummy; u_int32_t locker = 0; DB_LOCK lock; @@ -297,8 +292,8 @@ bdb_modify( Operation *op, SlapReply *rs ) if( 0 ) { retry: /* transaction retry */ if( e != NULL ) { - bdb_cache_delete_entry(&bdb->bi_cache, e); bdb_unlocked_cache_return_entry_w(&bdb->bi_cache, e); + e = NULL; } #ifdef NEW_LOGGING LDAP_LOG ( OPERATION, DETAIL1, "bdb_modify: retrying...\n", 0, 0, 0 ); @@ -356,8 +351,9 @@ retry: /* transaction retry */ opinfo.boi_acl_cache = op->o_do_not_cache; op->o_private = &opinfo; - /* get entry */ - rs->sr_err = bdb_dn2entry_w( op->o_bd, ltid, &op->o_req_ndn, &e, &matched, 0, locker, &lock ); + /* get entry or ancestor */ + rs->sr_err = bdb_dn2entry( op->o_bd, ltid, &op->o_req_ndn, &ei, 1, + locker, &lock, op->o_tmpmemctx ); if ( rs->sr_err != 0 ) { #ifdef NEW_LOGGING @@ -384,15 +380,16 @@ retry: /* transaction retry */ } } + e = ei->bei_e; /* acquire and lock entry */ - if ( e == NULL ) { - if ( matched != NULL ) { - rs->sr_matched = ch_strdup( matched->e_dn ); - rs->sr_ref = is_entry_referral( matched ) - ? get_entry_referrals( op, matched ) + if ( rs->sr_err == DB_NOTFOUND ) { + if ( e != NULL ) { + rs->sr_matched = ch_strdup( e->e_dn ); + rs->sr_ref = is_entry_referral( e ) + ? get_entry_referrals( op, e ) : NULL; - bdb_unlocked_cache_return_entry_r (&bdb->bi_cache, matched); - matched = NULL; + bdb_unlocked_cache_return_entry_r (&bdb->bi_cache, e); + e = NULL; } else { rs->sr_ref = referral_rewrite( default_referral, @@ -440,9 +437,27 @@ retry: /* transaction retry */ } #endif + /* nested transaction */ + rs->sr_err = TXN_BEGIN( bdb->bi_dbenv, ltid, <2, + bdb->bi_db_opflags ); + rs->sr_text = NULL; + if( rs->sr_err != 0 ) { +#ifdef NEW_LOGGING + LDAP_LOG ( OPERATION, ERR, + "bdb_modify: txn_begin(2) failed: %s (%d)\n", db_strerror(rs->sr_err), rs->sr_err, 0 ); +#else + Debug( LDAP_DEBUG_TRACE, + "bdb_modify: txn_begin(2) failed: %s (%d)\n", + db_strerror(rs->sr_err), rs->sr_err, 0 ); +#endif + rs->sr_err = LDAP_OTHER; + rs->sr_text = "internal error"; + goto return_results; + } /* Modify the entry */ - rs->sr_err = bdb_modify_internal( op, ltid, op->oq_modify.rs_modlist, e, - &rs->sr_text, textbuf, textlen ); + dummy = *e; + rs->sr_err = bdb_modify_internal( op, lt2, op->oq_modify.rs_modlist, + &dummy, &rs->sr_text, textbuf, textlen ); if( rs->sr_err != LDAP_SUCCESS ) { #ifdef NEW_LOGGING @@ -465,7 +480,7 @@ retry: /* transaction retry */ } /* change the entry itself */ - rs->sr_err = bdb_id2entry_update( op->o_bd, ltid, e ); + rs->sr_err = bdb_id2entry_update( op->o_bd, lt2, &dummy ); if ( rs->sr_err != 0 ) { #ifdef NEW_LOGGING LDAP_LOG ( OPERATION, ERR, @@ -483,6 +498,11 @@ retry: /* transaction retry */ rs->sr_text = "entry update failed"; goto return_results; } + if ( TXN_COMMIT( lt2, 0 ) != 0 ) { + rs->sr_err = LDAP_OTHER; + rs->sr_text = "txn_commit(2) failed"; + goto return_results; + } if( op->o_noop ) { if ( ( rs->sr_err = TXN_ABORT( ltid ) ) != 0 ) { @@ -492,6 +512,7 @@ retry: /* transaction retry */ rs->sr_err = LDAP_SUCCESS; } } else { + bdb_cache_modify( e, dummy.e_attrs, bdb->bi_dbenv, locker, &lock ); rs->sr_err = TXN_COMMIT( ltid, 0 ); } ltid = NULL; diff --git a/servers/slapd/back-bdb/modrdn.c b/servers/slapd/back-bdb/modrdn.c index 6076f743a4..a606107070 100644 --- a/servers/slapd/back-bdb/modrdn.c +++ b/servers/slapd/back-bdb/modrdn.c @@ -24,14 +24,15 @@ bdb_modrdn( Operation *op, SlapReply *rs ) int isroot = -1; Entry *e = NULL; Entry *p = NULL; - Entry *matched; + EntryInfo *ei = NULL, *eip = NULL, *nei = NULL, *neip = NULL; /* LDAP v2 supporting correct attribute handling. */ LDAPRDN new_rdn = NULL; LDAPRDN old_rdn = NULL; char textbuf[SLAP_TEXT_BUFLEN]; size_t textlen = sizeof textbuf; - DB_TXN * ltid = NULL; + DB_TXN *ltid = NULL, *lt2; struct bdb_op_info opinfo; + Entry dummy, *save; ID id; @@ -46,7 +47,7 @@ bdb_modrdn( Operation *op, SlapReply *rs ) int manageDSAit = get_manageDSAit( op ); u_int32_t locker = 0; - DB_LOCK lock; + DB_LOCK lock, plock, nplock; int noop = 0; @@ -68,7 +69,6 @@ bdb_modrdn( Operation *op, SlapReply *rs ) if( 0 ) { retry: /* transaction retry */ if (e != NULL) { - bdb_cache_delete_entry(&bdb->bi_cache, e); bdb_unlocked_cache_return_entry_w(&bdb->bi_cache, e); e = NULL; } @@ -137,7 +137,8 @@ retry: /* transaction retry */ op->o_private = &opinfo; /* get entry */ - rs->sr_err = bdb_dn2entry_w( op->o_bd, ltid, &op->o_req_ndn, &e, &matched, DB_RMW, locker, &lock ); + rs->sr_err = bdb_dn2entry( op->o_bd, ltid, &op->o_req_ndn, &ei, 1, + locker, &lock, op->o_tmpmemctx ); switch( rs->sr_err ) { case 0: @@ -155,14 +156,15 @@ retry: /* transaction retry */ goto return_results; } - if ( e == NULL ) { - if( matched != NULL ) { - rs->sr_matched = ch_strdup( matched->e_dn ); - rs->sr_ref = is_entry_referral( matched ) - ? get_entry_referrals( op, matched ) + e = ei->bei_e; + if ( rs->sr_err == DB_NOTFOUND ) { + if( e != NULL ) { + rs->sr_matched = ch_strdup( e->e_dn ); + rs->sr_ref = is_entry_referral( e ) + ? get_entry_referrals( op, e ) : NULL; - bdb_unlocked_cache_return_entry_r( &bdb->bi_cache, matched); - matched = NULL; + bdb_unlocked_cache_return_entry_r( &bdb->bi_cache, e); + e = NULL; } else { rs->sr_ref = referral_rewrite( default_referral, @@ -203,7 +205,8 @@ retry: /* transaction retry */ } #ifndef BDB_HIER - rs->sr_err = bdb_dn2id_children( op->o_bd, ltid, &e->e_nname, 0 ); + rs->sr_err = ei->bei_kids ? 0 : bdb_dn2id_children( op->o_bd, ltid, + &e->e_nname, 0 ); if ( rs->sr_err != DB_NOTFOUND ) { switch( rs->sr_err ) { case DB_LOCK_DEADLOCK: @@ -269,7 +272,9 @@ retry: /* transaction retry */ /* Make sure parent entry exist and we can write its * children. */ - rs->sr_err = bdb_dn2entry_r( op->o_bd, ltid, &p_ndn, &p, NULL, 0, locker, &lock ); + eip = ei->bei_parent; + rs->sr_err = bdb_cache_find_entry_id( op->o_bd, ltid, + eip->bei_id, &eip, 0, locker, &plock, op->o_tmpmemctx ); switch( rs->sr_err ) { case 0: @@ -287,6 +292,7 @@ retry: /* transaction retry */ goto return_results; } + p = eip->bei_e; if( p == NULL) { #ifdef NEW_LOGGING LDAP_LOG ( OPERATION, ERR, @@ -459,11 +465,11 @@ retry: /* transaction retry */ /* newSuperior == entry being moved?, if so ==> ERROR */ /* Get Entry with dn=newSuperior. Does newSuperior exist? */ - rs->sr_err = bdb_dn2entry_r( op->o_bd, - ltid, np_ndn, &np, NULL, 0, locker, &lock ); + rs->sr_err = bdb_dn2entry( op->o_bd, ltid, np_ndn, + &neip, 0, locker, &nplock, op->o_tmpmemctx ); switch( rs->sr_err ) { - case 0: + case 0: np = neip->bei_e; case DB_NOTFOUND: break; case DB_LOCK_DEADLOCK: @@ -661,7 +667,11 @@ retry: /* transaction retry */ new_ndn.bv_val, 0, 0 ); #endif - rs->sr_err = bdb_dn2id ( op->o_bd, ltid, &new_ndn, &id, 0 ); + /* Shortcut the search */ + nei = neip ? neip : eip; + rs->sr_err = bdb_cache_find_entry_ndn2id ( op->o_bd, ltid, &new_ndn, + &nei, locker, op->o_tmpmemctx ); + if ( nei ) bdb_cache_entryinfo_unlock( nei ); switch( rs->sr_err ) { case DB_LOCK_DEADLOCK: case DB_LOCK_NOTGRANTED: @@ -741,9 +751,31 @@ retry: /* transaction retry */ goto return_results; } } - + + /* nested transaction */ + rs->sr_err = TXN_BEGIN( bdb->bi_dbenv, ltid, <2, + bdb->bi_db_opflags ); + rs->sr_text = NULL; + if( rs->sr_err != 0 ) { +#ifdef NEW_LOGGING + LDAP_LOG ( OPERATION, ERR, + "bdb_modrdn: txn_begin(2) failed: %s (%d)\n", db_strerror(rs->sr_err), rs->sr_err, 0 ); +#else + Debug( LDAP_DEBUG_TRACE, + "bdb_modrdn: txn_begin(2) failed: %s (%d)\n", + db_strerror(rs->sr_err), rs->sr_err, 0 ); +#endif + rs->sr_err = LDAP_OTHER; + rs->sr_text = "internal error"; + goto return_results; + } + + dummy = *e; + save = e; + e = &dummy; + /* delete old one */ - rs->sr_err = bdb_dn2id_delete( op->o_bd, ltid, p_ndn.bv_val, e ); + rs->sr_err = bdb_dn2id_delete( op->o_bd, lt2, p_ndn.bv_val, e ); if ( rs->sr_err != 0 ) { switch( rs->sr_err ) { case DB_LOCK_DEADLOCK: @@ -755,14 +787,12 @@ retry: /* transaction retry */ goto return_results; } - (void) bdb_cache_delete_entry(&bdb->bi_cache, e); - /* Binary format uses a single contiguous block, cannot * free individual fields. But if a previous modrdn has - * already happened, must free the names. + * already happened, must free the names. The frees are + * done in bdb_cache_modrdn(). */ #ifdef BDB_HIER - ch_free(e->e_name.bv_val); e->e_name.bv_val = ch_malloc(new_dn.bv_len + new_ndn.bv_len + 2); e->e_name.bv_len = new_dn.bv_len; e->e_nname.bv_val = e->e_name.bv_val + new_dn.bv_len + 1; @@ -772,8 +802,6 @@ retry: /* transaction retry */ #else if( e->e_nname.bv_val < e->e_bv.bv_val || e->e_nname.bv_val > e->e_bv.bv_val + e->e_bv.bv_len ) { - ch_free(e->e_name.bv_val); - ch_free(e->e_nname.bv_val); e->e_name.bv_val = NULL; e->e_nname.bv_val = NULL; } @@ -783,7 +811,7 @@ retry: /* transaction retry */ new_ndn.bv_val = NULL; #endif /* add new one */ - rs->sr_err = bdb_dn2id_add( op->o_bd, ltid, np_ndn, e ); + rs->sr_err = bdb_dn2id_add( op->o_bd, lt2, np_ndn, e ); if ( rs->sr_err != 0 ) { switch( rs->sr_err ) { case DB_LOCK_DEADLOCK: @@ -804,7 +832,7 @@ retry: /* transaction retry */ #endif /* modify entry */ - rs->sr_err = bdb_modify_internal( op, ltid, &mod[0], e, + rs->sr_err = bdb_modify_internal( op, lt2, &mod[0], e, &rs->sr_text, textbuf, textlen ); if( rs->sr_err != LDAP_SUCCESS ) { @@ -820,7 +848,7 @@ retry: /* transaction retry */ } /* id2entry index */ - rs->sr_err = bdb_id2entry_update( op->o_bd, ltid, e ); + rs->sr_err = bdb_id2entry_update( op->o_bd, lt2, e ); if ( rs->sr_err != 0 ) { switch( rs->sr_err ) { case DB_LOCK_DEADLOCK: @@ -831,6 +859,11 @@ retry: /* transaction retry */ rs->sr_text = "entry update failed"; goto return_results; } + if ( TXN_COMMIT( lt2, 0 ) != 0 ) { + rs->sr_err = LDAP_OTHER; + rs->sr_text = "txn_commit(2) failed"; + goto return_results; + } if( op->o_noop ) { if(( rs->sr_err=TXN_ABORT( ltid )) != 0 ) { @@ -849,21 +882,12 @@ retry: /* transaction retry */ if(( rs->sr_err=TXN_PREPARE( ltid, gid )) != 0 ) { rs->sr_text = "txn_prepare failed"; } else { - if( bdb_cache_update_entry(&bdb->bi_cache, e) == -1 ) { - if(( rs->sr_err=TXN_ABORT( ltid )) != 0 ) { - rs->sr_text ="cache update & txn_abort failed"; - } else { - rs->sr_err = LDAP_OTHER; - rs->sr_text = "cache update failed"; - } - + bdb_cache_modrdn( save, &op->orr_newrdn, e, neip, + bdb->bi_dbenv, locker, &lock ); + if(( rs->sr_err=TXN_COMMIT( ltid, 0 )) != 0 ) { + rs->sr_text = "txn_commit failed"; } else { - bdb_cache_entry_commit( e ); - if(( rs->sr_err=TXN_COMMIT( ltid, 0 )) != 0 ) { - rs->sr_text = "txn_commit failed"; - } else { - rs->sr_err = LDAP_SUCCESS; - } + rs->sr_err = LDAP_SUCCESS; } } } diff --git a/servers/slapd/back-bdb/passwd.c b/servers/slapd/back-bdb/passwd.c index 9631178d4b..d90c7c8f4d 100644 --- a/servers/slapd/back-bdb/passwd.c +++ b/servers/slapd/back-bdb/passwd.c @@ -20,8 +20,9 @@ bdb_exop_passwd( Operation *op, SlapReply *rs ) struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private; int rc; Entry *e = NULL; + EntryInfo *ei; struct berval hash = { 0, NULL }; - DB_TXN *ltid = NULL; + DB_TXN *ltid = NULL, *lt2; struct bdb_op_info opinfo; char textbuf[SLAP_TEXT_BUFLEN]; size_t textlen = sizeof textbuf; @@ -101,7 +102,6 @@ bdb_exop_passwd( Operation *op, SlapReply *rs ) if( 0 ) { retry: /* transaction retry */ if ( e != NULL ) { - bdb_cache_delete_entry(&bdb->bi_cache, e); bdb_unlocked_cache_return_entry_w(&bdb->bi_cache, e); } #ifdef NEW_LOGGING @@ -150,7 +150,7 @@ retry: /* transaction retry */ op->o_private = &opinfo; /* get entry */ - rc = bdb_dn2entry_w( op->o_bd, ltid, &ndn, &e, NULL, 0 , locker, &lock); + rc = bdb_dn2entry( op->o_bd, ltid, &ndn, &ei, 0 , locker, &lock, op->o_tmpmemctx ); switch(rc) { case DB_LOCK_DEADLOCK: @@ -168,6 +168,8 @@ retry: /* transaction retry */ goto done; } + if ( ei ) e = ei->bei_e; + if( e == NULL ) { rs->sr_text = "could not locate authorization entry"; rc = LDAP_NO_SUCH_OBJECT; @@ -198,9 +200,31 @@ retry: /* transaction retry */ goto done; } + /* nested transaction */ + rc = TXN_BEGIN( bdb->bi_dbenv, ltid, <2, + bdb->bi_db_opflags ); + rs->sr_text = NULL; + if( rc != 0 ) { +#ifdef NEW_LOGGING + LDAP_LOG ( OPERATION, ERR, + "bdb_exop_passwd: txn_begin(2) failed: %s (%d)\n", db_strerror(rs->sr_err), rs->sr_err, 0 ); +#else + Debug( LDAP_DEBUG_TRACE, + "bdb_exop_passwd: txn_begin(2) failed: %s (%d)\n", + db_strerror(rs->sr_err), rs->sr_err, 0 ); +#endif + rc = LDAP_OTHER; + rs->sr_text = "internal error"; + goto done; + } { Modifications ml; struct berval vals[2]; + Entry dummy, *save; + + save = e; + dummy = *e; + e = &dummy; vals[0] = hash; vals[1].bv_val = NULL; @@ -211,7 +235,7 @@ retry: /* transaction retry */ ml.sml_op = LDAP_MOD_REPLACE; ml.sml_next = NULL; - rc = bdb_modify_internal( op, ltid, + rc = bdb_modify_internal( op, lt2, &ml, e, &rs->sr_text, textbuf, textlen ); if ( (rc == LDAP_INSUFFICIENT_ACCESS) && opinfo.boi_err ) { @@ -232,7 +256,7 @@ retry: /* transaction retry */ } /* change the entry itself */ - rc = bdb_id2entry_update( op->o_bd, ltid, e ); + rc = bdb_id2entry_update( op->o_bd, lt2, e ); if( rc != 0 ) { switch(rc) { case DB_LOCK_DEADLOCK: @@ -242,11 +266,17 @@ retry: /* transaction retry */ rs->sr_text = "entry update failed"; rc = LDAP_OTHER; } + if ( TXN_COMMIT( lt2, 0 ) != 0 ) { + rc = LDAP_OTHER; + rs->sr_text = "txn_commit(2) failed"; + } if( rc == 0 ) { if( op->o_noop ) { rc = TXN_ABORT( ltid ); } else { + bdb_cache_modify( save, e->e_attrs, + bdb->bi_dbenv, locker, &lock ); rc = TXN_COMMIT( ltid, 0 ); } ltid = NULL; diff --git a/servers/slapd/back-bdb/proto-bdb.h b/servers/slapd/back-bdb/proto-bdb.h index 74e455bddb..2ac96d52a1 100644 --- a/servers/slapd/back-bdb/proto-bdb.h +++ b/servers/slapd/back-bdb/proto-bdb.h @@ -52,13 +52,9 @@ bdb_db_cache( /* * dn2entry.c */ -int bdb_dn2entry_rw LDAP_P(( BackendDB *be, DB_TXN *tid, - struct berval *dn, Entry **e, Entry **matched, int flags, int rw, - u_int32_t locker, DB_LOCK *lock)); -#define bdb_dn2entry_r(be, tid, dn, e, m, f, locker, lock) \ - bdb_dn2entry_rw((be), (tid), (dn), (e), (m), (f), 0, locker, lock) -#define bdb_dn2entry_w(be, tid, dn, e, m, f, locker, lock) \ - bdb_dn2entry_rw((be), (tid), (dn), (e), (m), (f), 1, locker, lock) +int bdb_dn2entry LDAP_P(( BackendDB *be, DB_TXN *tid, + struct berval *dn, EntryInfo **e, int matched, + u_int32_t locker, DB_LOCK *lock, void *ctx)); /* * dn2id.c @@ -68,15 +64,7 @@ int bdb_dn2id( DB_TXN *tid, struct berval *dn, ID *id, - int flags ); - -int bdb_dn2id_matched( - BackendDB *be, - DB_TXN *tid, - struct berval *dn, - ID *id, - ID *id2, - int flags ); + void *ctx ); int bdb_dn2id_add( BackendDB *be, @@ -143,18 +131,11 @@ int bdb_id2entry_delete( DB_TXN *tid, Entry *e); -int bdb_id2entry_rw( +int bdb_id2entry( BackendDB *be, DB_TXN *tid, ID id, - Entry **e, - int rw, - u_int32_t locker, - DB_LOCK *lock ); -#define bdb_id2entry_r(be, tid, id, e, locker, lock) \ - bdb_id2entry_rw((be), (tid), (id), (e), 0, locker, lock) -#define bdb_id2entry_w(be, tid, id, e, locker, lock) \ - bdb_id2entry_rw((be), (tid), (id), (e), 1, locker, lock) + Entry **e); void bdb_entry_free ( Entry *e ); @@ -303,46 +284,84 @@ BI_op_extended bdb_exop_passwd; * cache.c */ -void bdb_cache_entry_commit( Entry *e ); +#define bdb_cache_entryinfo_lock(e) \ + ldap_pvt_thread_mutex_lock( &(e)->bei_kids_mutex ) +#define bdb_cache_entryinfo_unlock(e) \ + ldap_pvt_thread_mutex_unlock( &(e)->bei_kids_mutex ) + +#if 0 void bdb_cache_return_entry_rw( DB_ENV *env, Cache *cache, Entry *e, int rw, DB_LOCK *lock ); +#else +#define bdb_cache_return_entry_rw( env, cache, e, rw, lock ) \ + bdb_cache_entry_db_unlock( env, lock ) +#define bdb_cache_return_entry( env, lock ) \ + bdb_cache_entry_db_unlock( env, lock ) +#endif #define bdb_cache_return_entry_r(env, c, e, l) \ bdb_cache_return_entry_rw((env), (c), (e), 0, (l)) #define bdb_cache_return_entry_w(env, c, e, l) \ bdb_cache_return_entry_rw((env), (c), (e), 1, (l)) +#if 0 void bdb_unlocked_cache_return_entry_rw( Cache *cache, Entry *e, int rw ); +#else +#define bdb_unlocked_cache_return_entry_rw( a, b, c ) +#endif #define bdb_unlocked_cache_return_entry_r( c, e ) \ bdb_unlocked_cache_return_entry_rw((c), (e), 0) #define bdb_unlocked_cache_return_entry_w( c, e ) \ bdb_unlocked_cache_return_entry_rw((c), (e), 1) -int bdb_cache_add_entry_rw( - DB_ENV *env, - Cache *cache, +int bdb_cache_add( + struct bdb_info *bdb, + EntryInfo *pei, Entry *e, - int rw, + struct berval *nrdn, + u_int32_t locker +); +int bdb_cache_modrdn( + Entry *e, + struct berval *nrdn, + Entry *new, + EntryInfo *ein, + DB_ENV *env, u_int32_t locker, - DB_LOCK *lock + DB_LOCK *lock +); +int bdb_cache_modify( + Entry *e, + Attribute *newAttrs, + DB_ENV *env, + u_int32_t locker, + DB_LOCK *lock ); int bdb_cache_update_entry( Cache *cache, Entry *e ); -ID bdb_cache_find_entry_ndn2id( - Backend *be, - Cache *cache, - struct berval *ndn +int bdb_cache_find_entry_ndn2id( + Backend *be, + DB_TXN *txn, + struct berval *ndn, + EntryInfo **res, + u_int32_t locker, + void *ctx ); -Entry* bdb_cache_find_entry_id( - DB_ENV *env, - Cache *cache, +int bdb_cache_find_entry_id( + Backend *be, + DB_TXN *tid, ID id, - int rw, + EntryInfo **eip, + int islocked, u_int32_t locker, - DB_LOCK *lock + DB_LOCK *lock, + void *ctx ); int bdb_cache_delete_entry( - Cache *cache, - Entry *e + Cache *cache, + Entry *e, + DB_ENV *env, + u_int32_t locker, + DB_LOCK *lock ); void bdb_cache_release_all( Cache *cache ); diff --git a/servers/slapd/back-bdb/referral.c b/servers/slapd/back-bdb/referral.c index 73b441d67c..6b6e741041 100644 --- a/servers/slapd/back-bdb/referral.c +++ b/servers/slapd/back-bdb/referral.c @@ -17,7 +17,7 @@ bdb_referrals( Operation *op, SlapReply *rs ) { struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private; Entry *e = NULL; - Entry *matched = NULL; + EntryInfo *ei; int rc = LDAP_SUCCESS; u_int32_t locker; @@ -43,20 +43,15 @@ bdb_referrals( Operation *op, SlapReply *rs ) dn2entry_retry: /* get entry */ - rc = bdb_dn2entry_r( op->o_bd, NULL, &op->o_req_ndn, &e, &matched, 0, locker, &lock ); + rc = bdb_dn2entry( op->o_bd, NULL, &op->o_req_ndn, &ei, 1, locker, + &lock, op->o_tmpmemctx ); + e = ei->bei_e; switch(rc) { case DB_NOTFOUND: - rc = 0; case 0: break; case LDAP_BUSY: - if (e != NULL) { - bdb_cache_return_entry_r(bdb->bi_dbenv, &bdb->bi_cache, e, &lock); - } - if (matched != NULL) { - bdb_cache_return_entry_r(bdb->bi_dbenv, &bdb->bi_cache, matched, &lock); - } send_ldap_error( op, rs, LDAP_BUSY, "ldap server busy" ); LOCK_ID_FREE ( bdb->bi_dbenv, locker ); return LDAP_BUSY; @@ -73,20 +68,15 @@ dn2entry_retry: "bdb_referrals: dn2entry failed: %s (%d)\n", db_strerror(rc), rc, 0 ); #endif - if (e != NULL) { - bdb_cache_return_entry_r(bdb->bi_dbenv, &bdb->bi_cache, e, &lock); - } - if (matched != NULL) { - bdb_cache_return_entry_r(bdb->bi_dbenv, &bdb->bi_cache, matched, &lock); - } send_ldap_error( op, rs, LDAP_OTHER, "internal error" ); LOCK_ID_FREE ( bdb->bi_dbenv, locker ); return rs->sr_err; } - if ( e == NULL ) { - if ( matched != NULL ) { - rs->sr_matched = ch_strdup( matched->e_name.bv_val ); + if ( rc == DB_NOTFOUND ) { + rc = 0; + if ( e != NULL ) { + rs->sr_matched = ch_strdup( e->e_name.bv_val ); #ifdef NEW_LOGGING LDAP_LOG ( OPERATION, DETAIL1, @@ -98,13 +88,13 @@ dn2entry_retry: (long) op->o_tag, op->o_req_dn.bv_val, rs->sr_matched ); #endif - if( is_entry_referral( matched ) ) { + if( is_entry_referral( e ) ) { rc = LDAP_OTHER; - rs->sr_ref = get_entry_referrals( op, matched ); + rs->sr_ref = get_entry_referrals( op, e ); } - bdb_cache_return_entry_r (bdb->bi_dbenv, &bdb->bi_cache, matched, &lock); - matched = NULL; + bdb_cache_return_entry_r (bdb->bi_dbenv, &bdb->bi_cache, e, &lock); + e = NULL; } else if ( default_referral != NULL ) { rc = LDAP_OTHER; rs->sr_ref = referral_rewrite( default_referral, diff --git a/servers/slapd/back-bdb/search.c b/servers/slapd/back-bdb/search.c index 595a65b0f6..ebb6d65afd 100644 --- a/servers/slapd/back-bdb/search.c +++ b/servers/slapd/back-bdb/search.c @@ -35,7 +35,7 @@ static void send_pagerequest_response( * dereferenced entry on success, NULL on any failure. */ static Entry * deref_base ( - BackendDB *be, + Operation *op, SlapReply *rs, Entry *e, Entry **matched, @@ -44,14 +44,15 @@ static Entry * deref_base ( ID *tmp, ID *visited ) { - struct bdb_info *bdb = (struct bdb_info *) be->be_private; + struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private; struct berval ndn; + EntryInfo *ei; DB_LOCK lockr; rs->sr_err = LDAP_ALIAS_DEREF_PROBLEM; rs->sr_text = "maximum deref depth exceeded"; - while (BDB_IDL_N(tmp) < be->be_max_deref_depth) { + while (BDB_IDL_N(tmp) < op->o_bd->be_max_deref_depth) { /* Remember the last entry we looked at, so we can * report broken links @@ -84,8 +85,9 @@ static Entry * deref_base ( break; } - rs->sr_err = bdb_dn2entry_r( be, NULL, &ndn, &e, - NULL, 0, locker, &lockr ); + rs->sr_err = bdb_dn2entry( op->o_bd, NULL, &ndn, &ei, + 0, locker, &lockr, op->o_tmpmemctx ); + if ( ei ) e = ei->bei_e; if (!e) { rs->sr_err = LDAP_ALIAS_PROBLEM; rs->sr_text = "aliasedObject not found"; @@ -129,6 +131,7 @@ static int search_aliases( ID *aliases, *curscop, *subscop, *visited, *newsubs, *oldsubs, *tmp; ID cursora, ida, cursoro, ido, *subscop2; Entry *matched, *a; + EntryInfo *ei; struct berval bv_alias = { sizeof("alias")-1, "alias" }; AttributeAssertion aa_alias; Filter af; @@ -194,11 +197,13 @@ static int search_aliases( for (ida = bdb_idl_first(curscop, &cursora); ida != NOID; ida = bdb_idl_next(curscop, &cursora)) { - rs->sr_err = bdb_id2entry_r(op->o_bd, NULL, ida, &a, - locker, &lockr); + ei = NULL; + rs->sr_err = bdb_cache_find_entry_id(op->o_bd, NULL, + ida, &ei, 0, locker, &lockr, op->o_tmpmemctx ); if (rs->sr_err != LDAP_SUCCESS) { continue; } + a = ei->bei_e; /* This should only happen if the curscop IDL has maxed out and * turned into a range that spans IDs indiscriminately @@ -211,7 +216,7 @@ static int search_aliases( /* Actually dereference the alias */ BDB_IDL_ZERO(tmp); - a = deref_base( op->o_bd, rs, a, &matched, locker, &lockr, + a = deref_base( op, rs, a, &matched, locker, &lockr, tmp, visited ); if (a) { /* If the target was not already in our current candidates, @@ -257,8 +262,11 @@ nextido: * we should never see the ID of an entry that doesn't exist. * Set the name so that the scope's IDL can be retrieved. */ - rs->sr_err = bdb_id2entry_r(op->o_bd, NULL, ido, &e, locker, &locka); + ei = NULL; + rs->sr_err = bdb_cache_find_entry_id(op->o_bd, NULL, ido, &ei, + 0, locker, &locka, op->o_tmpmemctx ); if (rs->sr_err != LDAP_SUCCESS) goto nextido; + e = ei->bei_e; sf->f_dn = &e->e_nname; } return rs->sr_err; @@ -338,8 +346,9 @@ int bdb_search( Operation *op, SlapReply *rs ) time_t stoptime; ID id, cursor; ID candidates[BDB_IDL_UM_SIZE]; - Entry *e = NULL; + Entry *e = NULL, dummy; Entry *matched = NULL; + EntryInfo *ei; struct berval realbase = { 0, NULL }; int manageDSAit; int tentries = 0; @@ -451,22 +460,16 @@ int bdb_search( Operation *op, SlapReply *rs ) } else { dn2entry_retry: /* get entry with reader lock */ - rs->sr_err = bdb_dn2entry_r( op->o_bd, NULL, &sop->o_req_ndn, &e, - &matched, 0, locker, &lock ); + rs->sr_err = bdb_dn2entry( op->o_bd, NULL, &sop->o_req_ndn, &ei, + 1, locker, &lock, op->o_tmpmemctx ); } switch(rs->sr_err) { case DB_NOTFOUND: + matched = ei->bei_e; break; case 0: - break; + e = ei->bei_e; break; case LDAP_BUSY: - if (e != NULL) { - bdb_cache_return_entry_r(bdb->bi_dbenv, &bdb->bi_cache, e, &lock); - } - if (matched != NULL) { - bdb_cache_return_entry_r(bdb->bi_dbenv, &bdb->bi_cache, - matched, &lock); - } send_ldap_error( sop, rs, LDAP_BUSY, "ldap server busy" ); LOCK_ID_FREE (bdb->bi_dbenv, locker ); return LDAP_BUSY; @@ -474,13 +477,6 @@ dn2entry_retry: case DB_LOCK_NOTGRANTED: goto dn2entry_retry; default: - if (e != NULL) { - bdb_cache_return_entry_r(bdb->bi_dbenv, &bdb->bi_cache, e, &lock); - } - if (matched != NULL) { - bdb_cache_return_entry_r(bdb->bi_dbenv, &bdb->bi_cache, - matched, &lock); - } send_ldap_error( sop, rs, LDAP_OTHER, "internal error" ); LOCK_ID_FREE (bdb->bi_dbenv, locker ); return rs->sr_err; @@ -488,7 +484,7 @@ dn2entry_retry: if ( e && (op->ors_deref & LDAP_DEREF_FINDING) && is_entry_alias(e) ) { BDB_IDL_ZERO(candidates); - e = deref_base( op->o_bd, rs, e, &matched, locker, &lock, + e = deref_base( op, rs, e, &matched, locker, &lock, candidates, NULL ); } @@ -648,13 +644,24 @@ dn2entry_retry: /* compute it anyway; root does not use it */ stoptime = op->o_time + sop->oq_search.rs_tlimit; + /* need normalized dn below */ + ber_dupbv( &realbase, &e->e_nname ); + + dummy.e_nname = realbase; + dummy.e_id = e->e_id; + + if ( e != &slap_entry_root ) { + bdb_cache_return_entry_r(bdb->bi_dbenv, &bdb->bi_cache, e, &lock); + } + e = NULL; + /* select candidates */ if ( sop->oq_search.rs_scope == LDAP_SCOPE_BASE ) { - rs->sr_err = base_candidate( op->o_bd, e, candidates ); + rs->sr_err = base_candidate( op->o_bd, &dummy, candidates ); } else { BDB_IDL_ALL( bdb, candidates ); - rs->sr_err = search_candidates( op, sop, rs, e, locker, candidates ); + rs->sr_err = search_candidates( op, sop, rs, &dummy, locker, candidates ); } /* start cursor at beginning of candidates. @@ -680,14 +687,6 @@ dn2entry_retry: } #endif - /* need normalized dn below */ - ber_dupbv( &realbase, &e->e_nname ); - - if ( e != &slap_entry_root ) { - bdb_cache_return_entry_r(bdb->bi_dbenv, &bdb->bi_cache, e, &lock); - } - e = NULL; - if ( candidates[0] == 0 ) { #ifdef NEW_LOGGING LDAP_LOG ( OPERATION, RESULTS, @@ -849,8 +848,9 @@ loop_begin: #endif id2entry_retry: /* get the entry with reader lock */ - rs->sr_err = bdb_id2entry_r( op->o_bd, NULL, id, - &e, locker, &lock ); + ei = NULL; + rs->sr_err = bdb_cache_find_entry_id( op->o_bd, NULL, + id, &ei, 0, locker, &lock, op->o_tmpmemctx ); if (rs->sr_err == LDAP_BUSY) { rs->sr_text = "ldap server busy"; @@ -863,6 +863,12 @@ id2entry_retry: goto id2entry_retry; } + if ( ei && rs->sr_err == 0 ) { + e = ei->bei_e; + } else { + e = NULL; + } + if ( e == NULL ) { if( !BDB_IDL_IS_RANGE(candidates) ) { /* only complain for non-range IDLs */ diff --git a/servers/slapd/back-bdb/tools.c b/servers/slapd/back-bdb/tools.c index f0da2f8662..ba8287aa6b 100644 --- a/servers/slapd/back-bdb/tools.c +++ b/servers/slapd/back-bdb/tools.c @@ -133,21 +133,24 @@ int bdb_tool_next_id( DB_TXN *tid, Entry *e, struct berval *text, - int hole ) + int hole, + u_int32_t locker ) { struct bdb_info *bdb = (struct bdb_info *) be->be_private; struct berval dn = e->e_nname; struct berval pdn; + EntryInfo *ei = NULL; int rc; - rc = bdb_dn2id( be, tid, &dn, &e->e_id, 0 ); + rc = bdb_cache_find_entry_ndn2id( be, tid, &dn, &ei, locker, NULL ); + if ( ei ) bdb_cache_entryinfo_unlock( ei ); if ( rc == DB_NOTFOUND ) { if ( be_issuffix( be, &dn ) ) { pdn = slap_empty_bv; } else { dnParent( &dn, &pdn ); e->e_nname = pdn; - rc = bdb_tool_next_id( be, tid, e, text, 1 ); + rc = bdb_tool_next_id( be, tid, e, text, 1, locker ); if ( rc ) { return rc; } @@ -221,6 +224,7 @@ ID bdb_tool_entry_put( DB_TXN *tid = NULL; struct berval pdn; Operation op = {0}; + u_int32_t locker; assert( be != NULL ); assert( slapMode & SLAP_TOOL_MODE ); @@ -253,8 +257,9 @@ ID bdb_tool_entry_put( return NOID; } + locker = TXN_ID( tid ); /* add dn2id indices */ - rc = bdb_tool_next_id( be, tid, e, text, 0 ); + rc = bdb_tool_next_id( be, tid, e, text, 0, locker ); if( rc != 0 ) { goto done; } -- 2.39.5