int bdb_cache_find_id( Operation *op, DB_TXN *tid, ID id, EntryInfo **eip, int flag, DB_LOCK *lock ) { struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private; Entry *ep = NULL; int rc = 0, load = 0; EntryInfo ei = { 0 }; ei.bei_id = id; #ifdef SLAP_ZONE_ALLOC slap_zh_rlock(bdb->bi_cache.c_zctx); #endif /* If we weren't given any info, see if we have it already cached */ if ( !*eip ) { again: 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 ) { /* If the lock attempt fails, the info is in use */ if ( bdb_cache_entryinfo_trylock( *eip )) { int del = (*eip)->bei_state & CACHE_ENTRY_DELETED; ldap_pvt_thread_rdwr_runlock( &bdb->bi_cache.c_rwlock ); /* If this node is being deleted, treat * as if the delete has already finished */ if ( del ) { return DB_NOTFOUND; } /* otherwise, wait for the info to free up */ ldap_pvt_thread_yield(); goto again; } /* If this info isn't hooked up to its parent yet, * unlock and wait for it to be fully initialized */ if ( (*eip)->bei_state & CACHE_ENTRY_NOT_LINKED ) { bdb_cache_entryinfo_unlock( *eip ); ldap_pvt_thread_rdwr_runlock( &bdb->bi_cache.c_rwlock ); ldap_pvt_thread_yield(); goto again; } flag |= ID_LOCKED; } ldap_pvt_thread_rdwr_runlock( &bdb->bi_cache.c_rwlock ); } /* See if the ID exists in the database; add it to the cache if so */ if ( !*eip ) { #ifndef BDB_HIER rc = bdb_id2entry( op->o_bd, tid, id, &ep ); if ( rc == 0 ) { rc = bdb_cache_find_ndn( op, tid, &ep->e_nname, eip ); if ( *eip ) flag |= ID_LOCKED; if ( rc ) { ep->e_private = NULL; #ifdef SLAP_ZONE_ALLOC bdb_entry_return( bdb, ep, (*eip)->bei_zseq ); #else bdb_entry_return( ep ); #endif ep = NULL; } } #else rc = hdb_cache_find_parent(op, tid, id, eip ); if ( rc == 0 ) flag |= ID_LOCKED; #endif } /* Ok, we found the info, do we have the entry? */ if ( rc == 0 ) { if ( !( flag & ID_LOCKED )) { bdb_cache_entryinfo_lock( *eip ); flag |= ID_LOCKED; } if ( (*eip)->bei_state & CACHE_ENTRY_DELETED ) { rc = DB_NOTFOUND; } else { (*eip)->bei_finders++; (*eip)->bei_state |= CACHE_ENTRY_REFERENCED; if ( flag & ID_NOENTRY ) { bdb_cache_entryinfo_unlock( *eip ); return 0; } /* Make sure only one thread tries to load the entry */ load1: #ifdef SLAP_ZONE_ALLOC if ((*eip)->bei_e && !slap_zn_validate( bdb->bi_cache.c_zctx, (*eip)->bei_e, (*eip)->bei_zseq)) { (*eip)->bei_e = NULL; (*eip)->bei_zseq = 0; } #endif if ( !(*eip)->bei_e && !((*eip)->bei_state & CACHE_ENTRY_LOADING)) { load = 1; (*eip)->bei_state |= CACHE_ENTRY_LOADING; flag |= ID_CHKPURGE; } if ( !load ) { /* Clear the uncached state if we are not * loading it, i.e it is already cached or * another thread is currently loading it. */ if ( (*eip)->bei_state & CACHE_ENTRY_NOT_CACHED ) { (*eip)->bei_state ^= CACHE_ENTRY_NOT_CACHED; flag |= ID_CHKPURGE; } } if ( flag & ID_LOCKED ) { bdb_cache_entryinfo_unlock( *eip ); flag ^= ID_LOCKED; } rc = bdb_cache_entry_db_lock( bdb, tid, *eip, load, 0, lock ); if ( (*eip)->bei_state & CACHE_ENTRY_DELETED ) { rc = DB_NOTFOUND; bdb_cache_entry_db_unlock( bdb, lock ); bdb_cache_entryinfo_lock( *eip ); (*eip)->bei_finders--; bdb_cache_entryinfo_unlock( *eip ); } else if ( rc == 0 ) { if ( load ) { if ( !ep) { rc = bdb_id2entry( op->o_bd, tid, id, &ep ); } if ( rc == 0 ) { ep->e_private = *eip; #ifdef BDB_HIER while ( (*eip)->bei_state & CACHE_ENTRY_NOT_LINKED ) ldap_pvt_thread_yield(); bdb_fix_dn( ep, 0 ); #endif bdb_cache_entryinfo_lock( *eip ); (*eip)->bei_e = ep; #ifdef SLAP_ZONE_ALLOC (*eip)->bei_zseq = *((ber_len_t *)ep - 2); #endif ep = NULL; if ( flag & ID_NOCACHE ) { /* Set the cached state only if no other thread * found the info while we were loading the entry. */ if ( (*eip)->bei_finders == 1 ) { (*eip)->bei_state |= CACHE_ENTRY_NOT_CACHED; flag ^= ID_CHKPURGE; } } bdb_cache_entryinfo_unlock( *eip ); bdb_cache_lru_link( bdb, *eip ); } if ( rc == 0 ) { /* If we succeeded, downgrade back to a readlock. */ rc = bdb_cache_entry_db_relock( bdb, tid, *eip, 0, 0, lock ); } else { /* Otherwise, release the lock. */ bdb_cache_entry_db_unlock( bdb, lock ); } } else if ( !(*eip)->bei_e ) { /* Some other thread is trying to load the entry, * wait for it to finish. */ bdb_cache_entry_db_unlock( bdb, lock ); bdb_cache_entryinfo_lock( *eip ); flag |= ID_LOCKED; goto load1; #ifdef BDB_HIER } else { /* Check for subtree renames */ rc = bdb_fix_dn( (*eip)->bei_e, 1 ); if ( rc ) { bdb_cache_entry_db_relock( bdb, tid, *eip, 1, 0, lock ); /* check again in case other modifier did it already */ if ( bdb_fix_dn( (*eip)->bei_e, 1 ) ) rc = bdb_fix_dn( (*eip)->bei_e, 2 ); bdb_cache_entry_db_relock( bdb, tid, *eip, 0, 0, lock ); } #endif } bdb_cache_entryinfo_lock( *eip ); (*eip)->bei_finders--; if ( load ) (*eip)->bei_state ^= CACHE_ENTRY_LOADING; bdb_cache_entryinfo_unlock( *eip ); } } } if ( flag & ID_LOCKED ) { bdb_cache_entryinfo_unlock( *eip ); } if ( ep ) { ep->e_private = NULL; #ifdef SLAP_ZONE_ALLOC bdb_entry_return( bdb, ep, (*eip)->bei_zseq ); #else bdb_entry_return( ep ); #endif } if ( rc == 0 ) { int purge = 0; if (( flag & ID_CHKPURGE ) || bdb->bi_cache.c_eimax ) { ldap_pvt_thread_mutex_lock( &bdb->bi_cache.c_count_mutex ); if ( flag & ID_CHKPURGE ) { bdb->bi_cache.c_cursize++; if ( !bdb->bi_cache.c_purging && bdb->bi_cache.c_cursize > bdb->bi_cache.c_maxsize ) { purge = 1; bdb->bi_cache.c_purging = 1; } } else if ( !bdb->bi_cache.c_purging && bdb->bi_cache.c_eimax && bdb->bi_cache.c_leaves > bdb->bi_cache.c_eimax ) { purge = 1; bdb->bi_cache.c_purging = 1; } ldap_pvt_thread_mutex_unlock( &bdb->bi_cache.c_count_mutex ); } if ( purge ) bdb_cache_lru_purge( bdb ); } #ifdef SLAP_ZONE_ALLOC if (rc == 0 && (*eip)->bei_e) { slap_zn_rlock(bdb->bi_cache.c_zctx, (*eip)->bei_e); } slap_zh_runlock(bdb->bi_cache.c_zctx); #endif return rc; }
Entry* bdb_tool_entry_get( BackendDB *be, ID id ) { Entry *e = NULL; char *dptr; int rc, eoff; assert( be != NULL ); assert( slapMode & SLAP_TOOL_MODE ); if ( id != previd ) { data.ulen = data.dlen = sizeof( ehbuf ); data.data = ehbuf; data.flags |= DB_DBT_PARTIAL; BDB_ID2DISK( id, &nid ); rc = cursor->c_get( cursor, &key, &data, DB_SET ); if ( rc ) goto done; } /* Get the header */ dptr = eh.bv.bv_val; eh.bv.bv_val = ehbuf; eh.bv.bv_len = data.size; rc = entry_header( &eh ); eoff = eh.data - eh.bv.bv_val; eh.bv.bv_val = dptr; if ( rc ) goto done; /* Get the size */ data.flags &= ~DB_DBT_PARTIAL; data.ulen = 0; rc = cursor->c_get( cursor, &key, &data, DB_CURRENT ); if ( rc != DB_BUFFER_SMALL ) goto done; /* Allocate a block and retrieve the data */ eh.bv.bv_len = eh.nvals * sizeof( struct berval ) + data.size; eh.bv.bv_val = ch_realloc( eh.bv.bv_val, eh.bv.bv_len ); eh.data = eh.bv.bv_val + eh.nvals * sizeof( struct berval ); data.data = eh.data; data.ulen = data.size; /* Skip past already parsed nattr/nvals */ eh.data += eoff; rc = cursor->c_get( cursor, &key, &data, DB_CURRENT ); if ( rc ) goto done; #ifdef SLAP_ZONE_ALLOC /* FIXME: will add ctx later */ rc = entry_decode( &eh, &e, NULL ); #else rc = entry_decode( &eh, &e ); #endif if( rc == LDAP_SUCCESS ) { e->e_id = id; #ifdef BDB_HIER if ( slapMode & SLAP_TOOL_READONLY ) { EntryInfo *ei = NULL; Operation op = {0}; Opheader ohdr = {0}; op.o_hdr = &ohdr; op.o_bd = be; op.o_tmpmemctx = NULL; op.o_tmpmfuncs = &ch_mfuncs; rc = bdb_cache_find_parent( &op, CURSOR_GETLOCKER(cursor), id, &ei ); if ( rc == LDAP_SUCCESS ) { bdb_cache_entryinfo_unlock( ei ); e->e_private = ei; ei->bei_e = e; bdb_fix_dn( e, 0 ); ei->bei_e = NULL; e->e_private = NULL; } } #endif } done: return e; }
static int bdb_tool_entry_get_int( BackendDB *be, ID id, Entry **ep ) { Entry *e = NULL; char *dptr; int rc, eoff; assert( be != NULL ); assert( slapMode & SLAP_TOOL_MODE ); if ( ( tool_filter || tool_base ) && id == previd && tool_next_entry != NULL ) { *ep = tool_next_entry; tool_next_entry = NULL; return LDAP_SUCCESS; } if ( id != previd ) { data.ulen = data.dlen = sizeof( ehbuf ); data.data = ehbuf; data.flags |= DB_DBT_PARTIAL; BDB_ID2DISK( id, &nid ); rc = cursor->c_get( cursor, &key, &data, DB_SET ); if ( rc ) { rc = LDAP_OTHER; goto done; } } /* Get the header */ dptr = eh.bv.bv_val; eh.bv.bv_val = ehbuf; eh.bv.bv_len = data.size; rc = entry_header( &eh ); eoff = eh.data - eh.bv.bv_val; eh.bv.bv_val = dptr; if ( rc ) { rc = LDAP_OTHER; goto done; } /* Get the size */ data.flags &= ~DB_DBT_PARTIAL; data.ulen = 0; rc = cursor->c_get( cursor, &key, &data, DB_CURRENT ); if ( rc != DB_BUFFER_SMALL ) { rc = LDAP_OTHER; goto done; } /* Allocate a block and retrieve the data */ eh.bv.bv_len = eh.nvals * sizeof( struct berval ) + data.size; eh.bv.bv_val = ch_realloc( eh.bv.bv_val, eh.bv.bv_len ); eh.data = eh.bv.bv_val + eh.nvals * sizeof( struct berval ); data.data = eh.data; data.ulen = data.size; /* Skip past already parsed nattr/nvals */ eh.data += eoff; rc = cursor->c_get( cursor, &key, &data, DB_CURRENT ); if ( rc ) { rc = LDAP_OTHER; goto done; } #ifndef BDB_HIER /* TODO: handle BDB_HIER accordingly */ if ( tool_base != NULL ) { struct berval ndn; entry_decode_dn( &eh, NULL, &ndn ); if ( !dnIsSuffixScope( &ndn, tool_base, tool_scope ) ) { return LDAP_NO_SUCH_OBJECT; } } #endif #ifdef SLAP_ZONE_ALLOC /* FIXME: will add ctx later */ rc = entry_decode( &eh, &e, NULL ); #else rc = entry_decode( &eh, &e ); #endif if( rc == LDAP_SUCCESS ) { e->e_id = id; #ifdef BDB_HIER if ( slapMode & SLAP_TOOL_READONLY ) { struct bdb_info *bdb = (struct bdb_info *) be->be_private; EntryInfo *ei = NULL; Operation op = {0}; Opheader ohdr = {0}; op.o_hdr = &ohdr; op.o_bd = be; op.o_tmpmemctx = NULL; op.o_tmpmfuncs = &ch_mfuncs; rc = bdb_cache_find_parent( &op, bdb->bi_cache.c_txn, id, &ei ); if ( rc == LDAP_SUCCESS ) { bdb_cache_entryinfo_unlock( ei ); e->e_private = ei; ei->bei_e = e; bdb_fix_dn( e, 0 ); ei->bei_e = NULL; e->e_private = NULL; } } #endif } done: if ( e != NULL ) { *ep = e; } return rc; }
int bdb_id2entry_rw( BackendDB *be, DB_TXN *tid, ID id, Entry **e, int rw, u_int32_t locker, DB_LOCK *lock ) { struct bdb_info *bdb = (struct bdb_info *) be->be_private; DB *db = bdb->bi_id2entry->bdi_db; DBT key, data; struct berval bv; int rc = 0, ret = 0; *e = NULL; DBTzero( &key ); key.data = (char *) &id; key.size = sizeof(ID); 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 )); if( rc != 0 ) { return rc; } DBT2bv( &data, &bv ); rc = entry_decode( &bv, e ); if( rc == 0 ) { (*e)->e_id = id; } else { /* only free on error. On success, the entry was * decoded in place. */ ch_free( data.data ); } if ( rc == 0 ) { #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; }