📄 cache.c
字号:
/* cache.c - routines to maintain an in-core cache of entries *//* $OpenLDAP: pkg/ldap/servers/slapd/back-bdb/cache.c,v 1.88.2.21 2007/01/25 12:42:38 hyc Exp $ *//* This work is part of OpenLDAP Software <http://www.openldap.org/>. * * Copyright 2000-2007 The OpenLDAP Foundation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted only as authorized by the OpenLDAP * Public License. * * A copy of this license is available in the file LICENSE in the * top-level directory of the distribution or, alternatively, at * <http://www.OpenLDAP.org/license.html>. */#include "portable.h"#include <stdio.h>#include <ac/errno.h>#include <ac/string.h>#include <ac/socket.h>#include "slap.h"#include "back-bdb.h"#include "ldap_rq.h"#ifdef BDB_HIER#define bdb_cache_lru_add hdb_cache_lru_add#endifstatic void bdb_cache_lru_add( struct bdb_info *bdb, EntryInfo *ei );static int bdb_cache_delete_internal(Cache *cache, EntryInfo *e, int decr);#ifdef LDAP_DEBUG#ifdef SLAPD_UNUSEDstatic void bdb_lru_print(Cache *cache);#endif#endifstatic EntryInfo *bdb_cache_entryinfo_new( Cache *cache ){ EntryInfo *ei = NULL; if ( cache->c_eifree ) { ldap_pvt_thread_rdwr_wlock( &cache->c_rwlock ); if ( cache->c_eifree ) { ei = cache->c_eifree; cache->c_eifree = ei->bei_lrunext; } ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock ); } if ( ei ) { ei->bei_lrunext = NULL; ei->bei_state = 0; } else { ei = ch_calloc(1, sizeof(struct bdb_entry_info)); ldap_pvt_thread_mutex_init( &ei->bei_kids_mutex ); } return ei;}/* Atomically release and reacquire a lock */intbdb_cache_entry_db_relock( DB_ENV *env, u_int32_t locker, EntryInfo *ei, int rw, int tryOnly, DB_LOCK *lock ){#ifdef NO_THREADS return 0;#else int rc; DBT lockobj; DB_LOCKREQ list[2]; if ( !lock ) return 0; lockobj.data = &ei->bei_id; lockobj.size = sizeof(ei->bei_id) + 1; 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 ); if (rc && !tryOnly) { Debug( LDAP_DEBUG_TRACE, "bdb_cache_entry_db_relock: entry %ld, rw %d, rc %d\n", ei->bei_id, rw, rc ); } else { *lock = list[1].lock; } return rc;#endif}static intbdb_cache_entry_db_lock( DB_ENV *env, u_int32_t locker, EntryInfo *ei, int rw, int tryOnly, DB_LOCK *lock ){#ifdef NO_THREADS return 0;#else int rc; DBT lockobj; int db_rw; if ( !lock ) return 0; if (rw) db_rw = DB_LOCK_WRITE; else db_rw = DB_LOCK_READ; lockobj.data = &ei->bei_id; lockobj.size = sizeof(ei->bei_id) + 1; rc = LOCK_GET(env, locker, tryOnly ? DB_LOCK_NOWAIT : 0, &lockobj, db_rw, lock); if (rc && !tryOnly) { Debug( LDAP_DEBUG_TRACE, "bdb_cache_entry_db_lock: entry %ld, rw %d, rc %d\n", ei->bei_id, rw, rc ); } return rc;#endif /* NO_THREADS */}intbdb_cache_entry_db_unlock ( DB_ENV *env, DB_LOCK *lock ){#ifdef NO_THREADS return 0;#else int rc; if ( !lock || lock->mode == DB_LOCK_NG ) return 0; rc = LOCK_PUT ( env, lock ); return rc;#endif}static intbdb_cache_entryinfo_destroy( EntryInfo *e ){ ldap_pvt_thread_mutex_destroy( &e->bei_kids_mutex ); free( e->bei_nrdn.bv_val );#ifdef BDB_HIER free( e->bei_rdn.bv_val );#endif free( e ); return 0;}#define LRU_DELETE( cache, ei ) do { \ if ( (ei)->bei_lruprev != NULL ) { \ (ei)->bei_lruprev->bei_lrunext = (ei)->bei_lrunext; \ } else { \ (cache)->c_lruhead = (ei)->bei_lrunext; \ } \ if ( (ei)->bei_lrunext != NULL ) { \ (ei)->bei_lrunext->bei_lruprev = (ei)->bei_lruprev; \ } else { \ (cache)->c_lrutail = (ei)->bei_lruprev; \ } \ (ei)->bei_lrunext = (ei)->bei_lruprev = NULL; \} while(0)#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 = (ei); \ (ei)->bei_lruprev = NULL; \ if ( !ldap_pvt_thread_mutex_trylock( &(cache)->lru_tail_mutex )) { \ if ( (cache)->c_lrutail == NULL ) \ (cache)->c_lrutail = (ei); \ ldap_pvt_thread_mutex_unlock( &(cache)->lru_tail_mutex ); \ } \} while(0)/* Do a length-ordered sort on normalized RDNs */static intbdb_rdn_cmp( const void *v_e1, const void *v_e2 ){ const EntryInfo *e1 = v_e1, *e2 = v_e2; int rc = e1->bei_nrdn.bv_len - e2->bei_nrdn.bv_len; if (rc == 0) { rc = strncmp( e1->bei_nrdn.bv_val, e2->bei_nrdn.bv_val, e1->bei_nrdn.bv_len ); } return rc;}static intbdb_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;}/* Create an entryinfo in the cache. Caller must release the locks later. */static intbdb_entryinfo_add_internal( struct bdb_info *bdb, EntryInfo *ei, EntryInfo **res ){ EntryInfo *ei2 = NULL; *res = NULL; ei2 = bdb_cache_entryinfo_new( &bdb->bi_cache ); ldap_pvt_thread_rdwr_wlock( &bdb->bi_cache.c_rwlock ); bdb_cache_entryinfo_lock( ei->bei_parent ); ei2->bei_id = ei->bei_id; ei2->bei_parent = ei->bei_parent;#ifdef BDB_HIER ei2->bei_rdn = ei->bei_rdn;#endif#ifdef SLAP_ZONE_ALLOC ei2->bei_bdb = bdb;#endif /* Add to cache ID tree */ if (avl_insert( &bdb->bi_cache.c_idtree, ei2, bdb_id_cmp, avl_dup_error )) { EntryInfo *eix; eix = avl_find( bdb->bi_cache.c_idtree, ei2, bdb_id_cmp ); bdb_cache_entryinfo_destroy( ei2 ); ei2 = eix;#ifdef BDB_HIER /* It got freed above because its value was * assigned to ei2. */ ei->bei_rdn.bv_val = NULL;#endif } else { bdb->bi_cache.c_eiused++; ber_dupbv( &ei2->bei_nrdn, &ei->bei_nrdn ); /* This is a new leaf node. But if parent had no kids, then it was * a leaf and we would be decrementing that. So, only increment if * the parent already has kids. */ if ( ei->bei_parent->bei_kids || !ei->bei_parent->bei_id ) bdb->bi_cache.c_leaves++; avl_insert( &ei->bei_parent->bei_kids, ei2, bdb_rdn_cmp, avl_dup_error );#ifdef BDB_HIER ei->bei_parent->bei_ckids++;#endif } *res = ei2; return 0;}/* 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. */intbdb_cache_find_ndn( Operation *op, DB_TXN *txn, struct berval *ndn, EntryInfo **res ){ struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private; EntryInfo ei, *eip, *ei2; int rc = 0; char *ptr; /* this function is always called with normalized DN */ 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( op->o_bd, ndn ); eip = *res; } else { /* we're searching a full DN from the root */ ptr = ndn->bv_val + ndn->bv_len - op->o_bd->be_nsuffix[0].bv_len; ei.bei_nrdn.bv_val = ptr; ei.bei_nrdn.bv_len = op->o_bd->be_nsuffix[0].bv_len; /* Skip to next rdn if suffix is empty */ if ( ei.bei_nrdn.bv_len == 0 ) { for (ptr = ei.bei_nrdn.bv_val - 2; ptr > ndn->bv_val && !DN_SEPARATOR(*ptr); ptr--) /* empty */; if ( ptr >= ndn->bv_val ) { if (DN_SEPARATOR(*ptr)) ptr++; ei.bei_nrdn.bv_len = ei.bei_nrdn.bv_val - ptr; ei.bei_nrdn.bv_val = ptr; } } eip = &bdb->bi_cache.c_dntree; } for ( bdb_cache_entryinfo_lock( eip ); eip; ) { ei.bei_parent = eip; ei2 = (EntryInfo *)avl_find( eip->bei_kids, &ei, bdb_rdn_cmp ); if ( !ei2 ) { int len = ei.bei_nrdn.bv_len; if ( BER_BVISEMPTY( ndn )) { *res = eip; return LDAP_SUCCESS; } ei.bei_nrdn.bv_len = ndn->bv_len - (ei.bei_nrdn.bv_val - ndn->bv_val); bdb_cache_entryinfo_unlock( eip ); rc = bdb_dn2id( op, txn, &ei.bei_nrdn, &ei ); 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, &ei, &ei2 ); /* 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(); 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--) /* empty */; 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; } } return rc;}#ifdef BDB_HIER/* Walk up the tree from a child node, looking for an ID that's already * been linked into the cache. */inthdb_cache_find_parent( Operation *op, DB_TXN *txn, u_int32_t locker, ID id, EntryInfo **res ){ struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private; EntryInfo ei, eip, *ei2 = NULL, *ein = NULL, *eir = NULL; int rc; int addlru = 0; ei.bei_id = id; ei.bei_kids = NULL; ei.bei_ckids = 0; for (;;) { rc = hdb_dn2id_parent( op, txn, locker, &ei, &eip.bei_id ); if ( rc ) break; /* Save the previous node, if any */ ei2 = ein; /* Create a new node for the current ID */ ein = bdb_cache_entryinfo_new( &bdb->bi_cache ); ein->bei_id = ei.bei_id; ein->bei_kids = ei.bei_kids; ein->bei_nrdn = ei.bei_nrdn; ein->bei_rdn = ei.bei_rdn; ein->bei_ckids = ei.bei_ckids;#ifdef SLAP_ZONE_ALLOC ein->bei_bdb = bdb;#endif ei.bei_ckids = 0; /* This node is not fully connected yet */ ein->bei_state = CACHE_ENTRY_NOT_LINKED; /* Insert this node into the ID tree */ ldap_pvt_thread_rdwr_wlock( &bdb->bi_cache.c_rwlock ); if ( avl_insert( &bdb->bi_cache.c_idtree, (caddr_t)ein, bdb_id_cmp, avl_dup_error ) ) { /* Someone else created this node just before us. * Free our new copy and use the existing one. */ bdb_cache_entryinfo_destroy( ein ); ein = (EntryInfo *)avl_find( bdb->bi_cache.c_idtree, (caddr_t) &ei, bdb_id_cmp ); /* Link in any kids we've already processed */ if ( ei2 ) { bdb_cache_entryinfo_lock( ein ); avl_insert( &ein->bei_kids, (caddr_t)ei2, bdb_rdn_cmp, avl_dup_error ); ein->bei_ckids++; bdb_cache_entryinfo_unlock( ein ); } addlru = 0; } /* If this is the first time, save this node * to be returned later. */ if ( eir == NULL ) eir = ein; /* If there was a previous node, link it to this one */ if ( ei2 ) ei2->bei_parent = ein; /* Look for this node's parent */ if ( eip.bei_id ) { ei2 = (EntryInfo *) avl_find( bdb->bi_cache.c_idtree, (caddr_t) &eip, bdb_id_cmp ); } else { ei2 = &bdb->bi_cache.c_dntree; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -