diff options
Diffstat (limited to 'ldap/servers/slapd/back-ldbm/cache.c')
-rw-r--r-- | ldap/servers/slapd/back-ldbm/cache.c | 905 |
1 files changed, 765 insertions, 140 deletions
diff --git a/ldap/servers/slapd/back-ldbm/cache.c b/ldap/servers/slapd/back-ldbm/cache.c index 84e935c9..be45ddc9 100644 --- a/ldap/servers/slapd/back-ldbm/cache.c +++ b/ldap/servers/slapd/back-ldbm/cache.c @@ -46,7 +46,7 @@ #ifdef DEBUG #define LDAP_CACHE_DEBUG -/* #define LDAP_CACHE_DEBUG_LRU */ /* causes slowdown */ +/* #define LDAP_CACHE_DEBUG_LRU * causes slowdown */ #endif /* cache can't get any smaller than this (in bytes) */ @@ -82,6 +82,38 @@ #define LOG(_a, _x1, _x2, _x3) ; #endif +#define LRU_DETACH(cache, e) lru_detach((cache), (void *)(e)) + +#define CACHE_LRU_HEAD(cache, type) ((type)((cache)->c_lruhead)) +#define CACHE_LRU_TAIL(cache, type) ((type)((cache)->c_lrutail)) + +#define BACK_LRU_NEXT(entry, type) ((type)((entry)->ep_lrunext)) +#define BACK_LRU_PREV(entry, type) ((type)((entry)->ep_lruprev)) + +/* static functions */ +static void entrycache_clear_int(struct cache *cache); +static void entrycache_set_max_size(struct cache *cache, size_t bytes); +static int entrycache_remove_int(struct cache *cache, struct backentry *e); +static void entrycache_return(struct cache *cache, struct backentry **bep); +static int entrycache_replace(struct cache *cache, struct backentry *olde, struct backentry *newe); +static int entrycache_add_int(struct cache *cache, struct backentry *e, int state, struct backentry **alt); +static struct backentry *entrycache_flush(struct cache *cache); +#ifdef LDAP_CACHE_DEBUG_LRU +static void entry_lru_verify(struct cache *cache, struct backentry *e, int in); +#endif + +static int dn_same_id(const void *bdn, const void *k); +static void dncache_clear_int(struct cache *cache); +static void dncache_set_max_size(struct cache *cache, size_t bytes); +static int dncache_remove_int(struct cache *cache, struct backdn *dn); +static void dncache_return(struct cache *cache, struct backdn **bdn); +static int dncache_replace(struct cache *cache, struct backdn *olddn, struct backdn *newdn); +static int dncache_add_int(struct cache *cache, struct backdn *bdn, int state, struct backdn **alt); +static struct backdn *dncache_flush(struct cache *cache); +#ifdef LDAP_CACHE_DEBUG_LRU +static void dn_lru_verify(struct cache *cache, struct backdn *dn, int in); +#endif + /***** tiny hashtable implementation *****/ @@ -284,42 +316,67 @@ static void hash_stats(Hashtable *ht, u_long *slots, int *total_entries, /***** add/remove entries to/from the LRU list *****/ #ifdef LDAP_CACHE_DEBUG_LRU +static void +lru_verify(struct cache *cache, void *ptr, int in) +{ + struct backcommon *e; + if (NULL == ptr) + { + LOG("=> lru_verify\n<= lru_verify (null entry)\n", 0, 0, 0); + return; + } + e = (struct backcommon *)ptr; + if (CACHE_TYPE_ENTRY == e->ep_type) { + entry_lru_verify(cache, (struct backentry *)e, in); + } else { + dn_lru_verify(cache, (struct backdn *)e, in); + } +} + /* for debugging -- painstakingly verify the lru list is ok -- if 'in' is * true, then entry 'e' should be in the list right now; otherwise, it * should NOT be in the list. */ -static void lru_verify(struct cache *cache, struct backentry *e, int in) +static void +entry_lru_verify(struct cache *cache, struct backentry *e, int in) { int is_in = 0; int count = 0; struct backentry *ep; - ep = cache->c_lruhead; + ep = CACHE_LRU_HEAD(cache, struct backentry *); while (ep) { - count++; - if (ep == e) { + count++; + if (ep == e) { is_in = 1; } - if (ep->ep_lruprev) { - ASSERT(ep->ep_lruprev->ep_lrunext == ep); + if (ep->ep_lruprev) { + ASSERT(BACK_LRU_NEXT(BACK_LRU_PREV(ep, struct backentry *), struct backentry *)== ep); } else { - ASSERT(ep == cache->c_lruhead); + ASSERT(ep == CACHE_LRU_HEAD(cache, struct backentry *)); } - if (ep->ep_lrunext) { - ASSERT(ep->ep_lrunext->ep_lruprev == ep); + if (ep->ep_lrunext) { + ASSERT(BACK_LRU_PREV(BACK_LRU_NEXT(ep, struct backentry *), struct backentry *) == ep); } else { - ASSERT(ep == cache->c_lrutail); + ASSERT(ep == CACHE_LRU_TAIL(cache, struct backentry *)); } - ep = ep->ep_lrunext; + ep = BACK_LRU_NEXT(ep, struct backentry *); } ASSERT(is_in == in); } #endif /* assume lock is held */ -static void lru_detach(struct cache *cache, struct backentry *e) +static void lru_detach(struct cache *cache, void *ptr) { + struct backcommon *e; + if (NULL == ptr) + { + LOG("=> lru_detach\n<= lru_detach (null entry)\n", 0, 0, 0); + return; + } + e = (struct backcommon *)ptr; #ifdef LDAP_CACHE_DEBUG_LRU lru_verify(cache, e, 1); #endif @@ -339,8 +396,15 @@ static void lru_detach(struct cache *cache, struct backentry *e) } /* assume lock is held */ -static void lru_delete(struct cache *cache, struct backentry *e) +static void lru_delete(struct cache *cache, void *ptr) { + struct backcommon *e; + if (NULL == ptr) + { + LOG("=> lru_delete\n<= lru_delete (null entry)\n", 0, 0, 0); + return; + } + e = (struct backcommon *)ptr; #ifdef LDAP_CACHE_DEBUG_LRU lru_verify(cache, e, 1); #endif @@ -359,8 +423,15 @@ static void lru_delete(struct cache *cache, struct backentry *e) } /* assume lock is held */ -static void lru_add(struct cache *cache, struct backentry *e) +static void lru_add(struct cache *cache, void *ptr) { + struct backcommon *e; + if (NULL == ptr) + { + LOG("=> lru_add\n<= lru_add (null entry)\n", 0, 0, 0); + return; + } + e = (struct backcommon *)ptr; #ifdef LDAP_CACHE_DEBUG_LRU lru_verify(cache, e, 0); #endif @@ -379,28 +450,36 @@ static void lru_add(struct cache *cache, struct backentry *e) /***** cache overhead *****/ -static int cache_remove_int(struct cache *cache, struct backentry *e); - -static void cache_make_hashes(struct cache *cache) +static void cache_make_hashes(struct cache *cache, int type) { u_long hashsize = (cache->c_maxentries > 0) ? cache->c_maxentries : (cache->c_maxsize/512); - cache->c_dntable = new_hash(hashsize, - HASHLOC(struct backentry, ep_dn_link), - dn_hash, entry_same_dn); - cache->c_idtable = new_hash(hashsize, - HASHLOC(struct backentry, ep_id_link), - NULL, entry_same_id); + if (CACHE_TYPE_ENTRY == type) { + cache->c_dntable = new_hash(hashsize, + HASHLOC(struct backentry, ep_dn_link), + dn_hash, entry_same_dn); + cache->c_idtable = new_hash(hashsize, + HASHLOC(struct backentry, ep_id_link), + NULL, entry_same_id); +#ifdef UUIDCACHE_ON + cache->c_uuidtable = new_hash(hashsize, + HASHLOC(struct backentry, ep_uuid_link), + uuid_hash, entry_same_uuid); +#endif + } else if (CACHE_TYPE_DN == type) { + cache->c_dntable = NULL; + cache->c_idtable = new_hash(hashsize, + HASHLOC(struct backdn, dn_id_link), + NULL, dn_same_id); #ifdef UUIDCACHE_ON - cache->c_uuidtable = new_hash(hashsize, - HASHLOC(struct backentry, ep_uuid_link), - uuid_hash, entry_same_uuid); + cache->c_uuidtable = NULL; #endif + } } /* initialize the cache */ -int cache_init(struct cache *cache, size_t maxsize, long maxentries) +int cache_init(struct cache *cache, size_t maxsize, long maxentries, int type) { LDAPDebug(LDAP_DEBUG_TRACE, "=> cache_init\n", 0, 0, 0); cache->c_maxsize = maxsize; @@ -415,7 +494,7 @@ int cache_init(struct cache *cache, size_t maxsize, long maxentries) cache->c_tries = NULL; } cache->c_lruhead = cache->c_lrutail = NULL; - cache_make_hashes(cache); + cache_make_hashes(cache, type); if (((cache->c_mutex = PR_NewLock()) == NULL) || ((cache->c_emutexalloc_mutex = PR_NewLock()) == NULL)) { @@ -439,11 +518,12 @@ int cache_init(struct cache *cache, size_t maxsize, long maxentries) * of the cache. * These entries should be freed outside of the cache->c_mutex */ -static struct backentry * cache_flush(struct cache *cache) +static struct backentry * +entrycache_flush(struct cache *cache) { struct backentry *e = NULL; - LOG("=> cache_flush\n", 0, 0, 0); + LOG("=> entrycache_flush\n", 0, 0, 0); /* all entries on the LRU list are guaranteed to have a refcnt = 0 * (iow, nobody's using them), so just delete from the tail down @@ -453,62 +533,71 @@ static struct backentry * cache_flush(struct cache *cache) while ((cache->c_lrutail != NULL) && CACHE_FULL(cache)) { if (e == NULL) { - e = cache->c_lrutail; + e = CACHE_LRU_TAIL(cache, struct backentry *); } else { - e = e->ep_lruprev; + e = BACK_LRU_PREV(e, struct backentry *); } ASSERT(e->ep_refcnt == 0); e->ep_refcnt++; - if (cache_remove_int(cache, e) < 0) { - LDAPDebug(LDAP_DEBUG_ANY, "cache flush: unable to delete entry\n", - 0, 0, 0); + if (entrycache_remove_int(cache, e) < 0) { + LDAPDebug(LDAP_DEBUG_ANY, + "entry cache flush: unable to delete entry\n", 0, 0, 0); break; } - if(e == cache->c_lruhead) { + if(e == CACHE_LRU_HEAD(cache, struct backentry *)) { break; } } if (e) - lru_detach(cache, e); - LOG("<= cache_flush (down to %lu entries, %lu bytes)\n", cache->c_curentries, - slapi_counter_get_value(cache->c_cursize), 0); + LRU_DETACH(cache, e); + LOG("<= entrycache_flush (down to %lu entries, %lu bytes)\n", + cache->c_curentries, slapi_counter_get_value(cache->c_cursize), 0); return e; } /* remove everything from the cache */ -static void cache_clear_int(struct cache *cache) +static void entrycache_clear_int(struct cache *cache) { struct backentry *eflush = NULL; struct backentry *eflushtemp = NULL; size_t size = cache->c_maxsize; cache->c_maxsize = 0; - eflush = cache_flush(cache); + eflush = entrycache_flush(cache); while (eflush) { - eflushtemp = eflush->ep_lrunext; + eflushtemp = BACK_LRU_NEXT(eflush, struct backentry *); backentry_free(&eflush); eflush = eflushtemp; } cache->c_maxsize = size; if (cache->c_curentries > 0) { - LDAPDebug(LDAP_DEBUG_ANY, "somehow, there are still %ld entries " - "in the entry cache. :/\n", cache->c_curentries, 0, 0); + LDAPDebug1Arg(LDAP_DEBUG_ANY, + "entrycache_clear_int: there are still %ld entries " + "in the entry cache. :/\n", cache->c_curentries); } } -void cache_clear(struct cache *cache) +void cache_clear(struct cache *cache, int type) { PR_Lock(cache->c_mutex); - cache_clear_int(cache); + if (CACHE_TYPE_ENTRY == type) { + entrycache_clear_int(cache); + } else if (CACHE_TYPE_DN == type) { + dncache_clear_int(cache); + } PR_Unlock(cache->c_mutex); } -static void erase_cache(struct cache *cache) +static void erase_cache(struct cache *cache, int type) { - cache_clear_int(cache); + if (CACHE_TYPE_ENTRY == type) { + entrycache_clear_int(cache); + } else if (CACHE_TYPE_DN == type) { + dncache_clear_int(cache); + } slapi_ch_free((void **)&cache->c_dntable); slapi_ch_free((void **)&cache->c_idtable); #ifdef UUIDCACHE_ON @@ -517,14 +606,23 @@ static void erase_cache(struct cache *cache) } /* to be used on shutdown or when destroying a backend instance */ -void cache_destroy_please(struct cache *cache) +void cache_destroy_please(struct cache *cache, int type) { - erase_cache(cache); + erase_cache(cache, type); PR_DestroyLock(cache->c_mutex); PR_DestroyLock(cache->c_emutexalloc_mutex); } -void cache_set_max_size(struct cache *cache, size_t bytes) +void cache_set_max_size(struct cache *cache, size_t bytes, int type) +{ + if (CACHE_TYPE_ENTRY == type) { + entrycache_set_max_size(cache, bytes); + } else if (CACHE_TYPE_DN == type) { + dncache_set_max_size(cache, bytes); + } +} + +static void entrycache_set_max_size(struct cache *cache, size_t bytes) { struct backentry *eflush = NULL; struct backentry *eflushtemp = NULL; @@ -540,10 +638,10 @@ void cache_set_max_size(struct cache *cache, size_t bytes) LOG("entry cache size set to %lu\n", bytes, 0, 0); /* check for full cache, and clear out if necessary */ if (CACHE_FULL(cache)) - eflush = cache_flush(cache); + eflush = entrycache_flush(cache); while (eflush) { - eflushtemp = eflush->ep_lrunext; + eflushtemp = BACK_LRU_NEXT(eflush, struct backentry *); backentry_free(&eflush); eflush = eflushtemp; } @@ -551,8 +649,8 @@ void cache_set_max_size(struct cache *cache, size_t bytes) /* there's hardly anything left in the cache -- clear it out and * resize the hashtables for efficiency. */ - erase_cache(cache); - cache_make_hashes(cache); + erase_cache(cache, CACHE_TYPE_ENTRY); + cache_make_hashes(cache, CACHE_TYPE_ENTRY); } PR_Unlock(cache->c_mutex); if (! dblayer_is_cachesize_sane(&bytes)) { @@ -582,11 +680,11 @@ void cache_set_max_entries(struct cache *cache, long entries) /* check for full cache, and clear out if necessary */ if (CACHE_FULL(cache)) - eflush = cache_flush(cache); + eflush = entrycache_flush(cache); PR_Unlock(cache->c_mutex); while (eflush) { - eflushtemp = eflush->ep_lrunext; + eflushtemp = BACK_LRU_NEXT(eflush, struct backentry *); backentry_free(&eflush); eflush = eflushtemp; } @@ -594,7 +692,7 @@ void cache_set_max_entries(struct cache *cache, long entries) size_t cache_get_max_size(struct cache *cache) { - size_t n; + size_t n = 0; PR_Lock(cache->c_mutex); n = cache->c_maxsize; @@ -649,41 +747,44 @@ void cache_debug_hash(struct cache *cache, char **out) u_long slots; int total_entries, max_entries_per_slot, *slot_stats; int i, j; - Hashtable *ht; - char *name; + Hashtable *ht = NULL; + char *name = "unknown"; PR_Lock(cache->c_mutex); *out = (char *)slapi_ch_malloc(1024); **out = 0; for (i = 0; i < 3; i++) { - if (i > 0) - sprintf(*out + strlen(*out), "; "); - switch(i) { - case 0: - ht = cache->c_dntable; - name = "dn"; - break; - case 1: - ht = cache->c_idtable; - name = "id"; - break; + if (i > 0) + sprintf(*out + strlen(*out), "; "); + switch(i) { + case 0: + ht = cache->c_dntable; + name = "dn"; + break; + case 1: + ht = cache->c_idtable; + name = "id"; + break; #ifdef UUIDCACHE_ON - case 2: + case 2: default: - ht = cache->c_uuidtable; - name = "uuid"; - break; + ht = cache->c_uuidtable; + name = "uuid"; + break; #endif - } - hash_stats(ht, &slots, &total_entries, &max_entries_per_slot, - &slot_stats); - sprintf(*out + strlen(*out), "%s hash: %lu slots, %d entries (%d max " - "entries per slot) -- ", name, slots, total_entries, - max_entries_per_slot); - for (j = 0; j <= max_entries_per_slot; j++) - sprintf(*out + strlen(*out), "%d[%d] ", j, slot_stats[j]); - slapi_ch_free((void **)&slot_stats); + } + if (NULL == ht) { + continue; + } + hash_stats(ht, &slots, &total_entries, &max_entries_per_slot, + &slot_stats); + sprintf(*out + strlen(*out), "%s hash: %lu slots, %d items (%d max " + "items per slot) -- ", name, slots, total_entries, + max_entries_per_slot); + for (j = 0; j <= max_entries_per_slot; j++) + sprintf(*out + strlen(*out), "%d[%d] ", j, slot_stats[j]); + slapi_ch_free((void **)&slot_stats); } PR_Unlock(cache->c_mutex); } @@ -693,7 +794,8 @@ void cache_debug_hash(struct cache *cache, char **out) /* remove an entry from the cache */ /* you must be holding c_mutex !! */ -static int cache_remove_int(struct cache *cache, struct backentry *e) +static int +entrycache_remove_int(struct cache *cache, struct backentry *e) { int ret = 1; /* assume not in cache */ const char *ndn; @@ -701,7 +803,7 @@ static int cache_remove_int(struct cache *cache, struct backentry *e) const char *uuid; #endif - LOG("=> cache_remove (%s)\n", backentry_get_ndn(e), 0, 0); + LOG("=> entrycache_remove_int (%s)\n", backentry_get_ndn(e), 0, 0); if (e->ep_state & ENTRY_STATE_NOTINCACHE) { return ret; @@ -742,20 +844,20 @@ static int cache_remove_int(struct cache *cache, struct backentry *e) if (ret == 0) { /* won't be on the LRU list since it has a refcount on it */ /* adjust cache size */ - slapi_counter_subtract(cache->c_cursize, e->size); + slapi_counter_subtract(cache->c_cursize, e->ep_size); cache->c_curentries--; - LOG("<= cache_remove (size %lu): cache now %lu entries, %lu bytes\n", - e->size, cache->c_curentries, + LOG("<= entrycache_remove_int (size %lu): cache now %lu entries, " + "%lu bytes\n", e->ep_size, cache->c_curentries, slapi_counter_get_value(cache->c_cursize)); } /* mark for deletion (will be erased when refcount drops to zero) */ e->ep_state |= ENTRY_STATE_DELETED; - LOG("<= cache_remove: %d\n", ret, 0, 0); + LOG("<= entrycache_remove_int: %d\n", ret, 0, 0); return ret; } -/* remove an entry from the cache. +/* remove an entry/a dn from the cache. * you must have a refcount on e (iow, fetched via cache_find_*). the * entry is removed from the cache, but NOT freed! you are responsible * for freeing the entry yourself when done with it, preferrably via @@ -765,23 +867,54 @@ static int cache_remove_int(struct cache *cache, struct backentry *e) * returns: 0 on success * 1 if the entry wasn't in the cache at all (not even partially) */ -int cache_remove(struct cache *cache, struct backentry *e) +int cache_remove(struct cache *cache, void *ptr) { - int ret; + int ret = 0; + struct backcommon *e; + if (NULL == ptr) + { + LOG("=> lru_remove\n<= lru_remove (null entry)\n", 0, 0, 0); + return ret; + } + e = (struct backcommon *)ptr; PR_Lock(cache->c_mutex); - ASSERT(e->ep_refcnt > 0); - ret = cache_remove_int(cache, e); + if (CACHE_TYPE_ENTRY == e->ep_type) { + ASSERT(e->ep_refcnt > 0); + ret = entrycache_remove_int(cache, (struct backentry *)e); + } else if (CACHE_TYPE_DN == e->ep_type) { + ret = dncache_remove_int(cache, (struct backdn *)e); + } PR_Unlock(cache->c_mutex); return ret; } /* replace an entry in the cache. * returns: 0 on success - * 1 if the entry wasn't in the cache + * 1 if the entry wasn't in the cache */ -int cache_replace(struct cache *cache, struct backentry *olde, - struct backentry *newe) +int cache_replace(struct cache *cache, void *oldptr, void *newptr) +{ + struct backcommon *olde; + if (NULL == oldptr || NULL == newptr) + { + LOG("=> lru_replace\n<= lru_replace (null entry)\n", 0, 0, 0); + return 0; + } + olde = (struct backcommon *)oldptr; + + if (CACHE_TYPE_ENTRY == olde->ep_type) { + return entrycache_replace(cache, (struct backentry *)oldptr, + (struct backentry *)newptr); + } else if (CACHE_TYPE_DN == olde->ep_type) { + return dncache_replace(cache, (struct backdn *)oldptr, + (struct backdn *)newptr); + } + return 0; +} + +static int entrycache_replace(struct cache *cache, struct backentry *olde, + struct backentry *newe) { int found; const char *oldndn; @@ -791,7 +924,7 @@ int cache_replace(struct cache *cache, struct backentry *olde, const char *newuuid; #endif - LOG("=> cache_replace (%s) -> (%s)\n", backentry_get_ndn(olde), + LOG("=> entrycache_replace (%s) -> (%s)\n", backentry_get_ndn(olde), backentry_get_ndn(newe), 0); /* remove from all hashtables -- this function may be called from places @@ -819,7 +952,7 @@ int cache_replace(struct cache *cache, struct backentry *olde, found &= remove_hash(cache->c_uuidtable, (void *)olduuid, strlen(olduuid)); #endif if (!found) { - LOG("cache replace: cache index tables out of sync\n", 0, 0, 0); + LOG("entry cache replace: cache index tables out of sync\n", 0, 0, 0); PR_Unlock(cache->c_mutex); return 1; } @@ -831,9 +964,9 @@ int cache_replace(struct cache *cache, struct backentry *olde, */ if (remove_hash(cache->c_dntable, (void *)newndn, strlen(newndn))) { - slapi_counter_subtract(cache->c_cursize, newe->size); + slapi_counter_subtract(cache->c_cursize, newe->ep_size); cache->c_curentries--; - LOG("cache replace remove entry size %lu\n", newe->size, 0, 0); + LOG("entry cache replace remove entry size %lu\n", newe->ep_size, 0, 0); } } @@ -842,12 +975,12 @@ int cache_replace(struct cache *cache, struct backentry *olde, * tested enough that we believe it works.) */ if (!add_hash(cache->c_dntable, (void *)newndn, strlen(newndn), newe, NULL)) { - LOG("cache replace: can't add dn\n", 0, 0, 0); + LOG("entry cache replace: can't add dn\n", 0, 0, 0); PR_Unlock(cache->c_mutex); return 1; } if (!add_hash(cache->c_idtable, &(newe->ep_id), sizeof(ID), newe, NULL)) { - LOG("cache replace: can't add id\n", 0, 0, 0); + LOG("entry cache replace: can't add id\n", 0, 0, 0); remove_hash(cache->c_dntable, (void *)newndn, strlen(newndn)); PR_Unlock(cache->c_mutex); return 1; @@ -855,7 +988,7 @@ int cache_replace(struct cache *cache, struct backentry *olde, #ifdef UUIDCACHE_ON if (newuuid && !add_hash(cache->c_uuidtable, (void *)newuuid, strlen(newuuid), newe, NULL)) { - LOG("cache replace: can't add uuid\n", 0, 0, 0); + LOG("entry cache replace: can't add uuid\n", 0, 0, 0); remove_hash(cache->c_dntable, (void *)newndn, strlen(newndn)); remove_hash(cache->c_idtable, &(newe->ep_id), sizeof(ID)); PR_Unlock(cache->c_mutex); @@ -864,16 +997,16 @@ int cache_replace(struct cache *cache, struct backentry *olde, #endif /* adjust cache meta info */ newe->ep_refcnt = 1; - newe->size = cache_entry_size(newe); - if (newe->size > olde->size) { - slapi_counter_add(cache->c_cursize, newe->size - olde->size); - } else if (newe->size < olde->size) { - slapi_counter_subtract(cache->c_cursize, olde->size - newe->size); + newe->ep_size = cache_entry_size(newe); + if (newe->ep_size > olde->ep_size) { + slapi_counter_add(cache->c_cursize, newe->ep_size - olde->ep_size); + } else if (newe->ep_size < olde->ep_size) { + slapi_counter_subtract(cache->c_cursize, olde->ep_size - newe->ep_size); } olde->ep_state = ENTRY_STATE_DELETED; newe->ep_state = 0; PR_Unlock(cache->c_mutex); - LOG("<= cache_replace OK, cache size now %lu cache count now %ld\n", + LOG("<= entrycache_replace OK, cache size now %lu cache count now %ld\n", slapi_counter_get_value(cache->c_cursize), cache->c_curentries, 0); return 0; } @@ -881,18 +1014,33 @@ int cache_replace(struct cache *cache, struct backentry *olde, /* call this when you're done with an entry that was fetched via one of * the cache_find_* calls. */ -void cache_return(struct cache *cache, struct backentry **bep) +void cache_return(struct cache *cache, void **ptr) { - struct backentry *eflush = NULL; - struct backentry *eflushtemp = NULL; - struct backentry *e; - if (NULL == bep || NULL == *bep) + struct backcommon *bep; + + if (NULL == ptr || NULL == *ptr) { - LOG("=> cache_return (null entry)\n", 0, 0, 0); + LOG("=> cache_return\n<= cache_return (null entry)\n", 0, 0, 0); return; } + bep = *(struct backcommon **)ptr; + if (CACHE_TYPE_ENTRY == bep->ep_type) { + entrycache_return(cache, (struct backentry **)ptr); + } else if (CACHE_TYPE_DN == bep->ep_type) { + dncache_return(cache, (struct backdn **)ptr); + } +} + +static void +entrycache_return(struct cache *cache, struct backentry **bep) +{ + struct backentry *eflush = NULL; + struct backentry *eflushtemp = NULL; + struct backentry *e; + e = *bep; - LOG("=> cache_return (%s) entry count: %d, entry in cache:%ld\n", backentry_get_ndn(e), e->ep_refcnt, cache->c_curentries); + LOG("=> entrycache_return (%s) entry count: %d, entry in cache:%ld\n", + backentry_get_ndn(e), e->ep_refcnt, cache->c_curentries); PR_Lock(cache->c_mutex); if (e->ep_state & ENTRY_STATE_NOTINCACHE) @@ -909,17 +1057,18 @@ void cache_return(struct cache *cache, struct backentry **bep) lru_add(cache, e); /* the cache might be overfull... */ if (CACHE_FULL(cache)) - eflush = cache_flush(cache); + eflush = entrycache_flush(cache); } } } PR_Unlock(cache->c_mutex); while (eflush) { - eflushtemp = eflush->ep_lrunext; + eflushtemp = BACK_LRU_NEXT(eflush, struct backentry *); backentry_free(&eflush); eflush = eflushtemp; } + LOG("<= entrycache_return\n", 0, 0, 0); } @@ -941,7 +1090,7 @@ struct backentry *cache_find_dn(struct cache *cache, const char *dn, unsigned lo return NULL; } if (e->ep_refcnt == 0) - lru_delete(cache, e); + lru_delete(cache, (void *)e); e->ep_refcnt++; PR_Unlock(cache->c_mutex); slapi_counter_increment(cache->c_hits); @@ -972,7 +1121,7 @@ struct backentry *cache_find_id(struct cache *cache, ID id) return NULL; } if (e->ep_refcnt == 0) - lru_delete(cache, e); + lru_delete(cache, (void *)e); e->ep_refcnt++; PR_Unlock(cache->c_mutex); slapi_counter_increment(cache->c_hits); @@ -1003,7 +1152,7 @@ struct backentry *cache_find_uuid(struct cache *cache, const char *uuid) return NULL; } if (e->ep_refcnt == 0) - lru_delete(cache, e); + lru_delete(cache, (void *)e); e->ep_refcnt++; PR_Unlock(cache->c_mutex); slapi_counter_increment(cache->c_hits); @@ -1018,8 +1167,9 @@ struct backentry *cache_find_uuid(struct cache *cache, const char *uuid) #endif /* add an entry to the cache */ -static int cache_add_int(struct cache *cache, struct backentry *e, int state, - struct backentry **alt) +static int +entrycache_add_int(struct cache *cache, struct backentry *e, int state, + struct backentry **alt) { struct backentry *eflush = NULL; struct backentry *eflushtemp = NULL; @@ -1030,7 +1180,7 @@ static int cache_add_int(struct cache *cache, struct backentry *e, int state, struct backentry *my_alt; int already_in = 0; - LOG("=> cache_add_int( \"%s\", %ld )\n", backentry_get_ndn(e), + LOG("=> entrycache_add_int( \"%s\", %ld )\n", backentry_get_ndn(e), e->ep_id, 0); PR_Lock(cache->c_mutex); @@ -1061,7 +1211,7 @@ static int cache_add_int(struct cache *cache, struct backentry *e, int state, * ==> increase the refcnt */ if (e->ep_refcnt == 0) - lru_delete(cache, e); + lru_delete(cache, (void *)e); e->ep_refcnt++; e->ep_state = state; /* might be CREATING */ /* returning 1 (entry already existed), but don't set to alt @@ -1093,7 +1243,7 @@ static int cache_add_int(struct cache *cache, struct backentry *e, int state, if (alt) { *alt = my_alt; if ((*alt)->ep_refcnt == 0) - lru_delete(cache, *alt); + lru_delete(cache, (void *)*alt); (*alt)->ep_refcnt++; } PR_Unlock(cache->c_mutex); @@ -1126,7 +1276,7 @@ static int cache_add_int(struct cache *cache, struct backentry *e, int state, * remove the old entry and add the new one, and all will be * fine (i think). */ - LOG("<= cache_add_int (ignoring)\n", 0, 0, 0); + LOG("<= entrycache_add_int (ignoring)\n", 0, 0, 0); PR_Unlock(cache->c_mutex); return 0; } @@ -1156,30 +1306,30 @@ static int cache_add_int(struct cache *cache, struct backentry *e, int state, if (! already_in) { e->ep_refcnt = 1; - e->size = cache_entry_size(e); + e->ep_size = cache_entry_size(e); - slapi_counter_add(cache->c_cursize, e->size); + slapi_counter_add(cache->c_cursize, e->ep_size); cache->c_curentries++; /* don't add to lru since refcnt = 1 */ LOG("added entry of size %lu -> total now %lu out of max %lu\n", - e->size, slapi_counter_get_value(cache->c_cursize), cache->c_maxsize); + e->ep_size, slapi_counter_get_value(cache->c_cursize), cache->c_maxsize); if (cache->c_maxentries >= 0) { LOG(" total entries %ld out of %ld\n", cache->c_curentries, cache->c_maxentries, 0); } /* check for full cache, and clear out if necessary */ if (CACHE_FULL(cache)) - eflush = cache_flush(cache); + eflush = entrycache_flush(cache); } PR_Unlock(cache->c_mutex); while (eflush) { - eflushtemp = eflush->ep_lrunext; + eflushtemp = BACK_LRU_NEXT(eflush, struct backentry *); backentry_free(&eflush); eflush = eflushtemp; } - LOG("<= cache_add_int OK\n", 0, 0, 0); + LOG("<= entrycache_add_int OK\n", 0, 0, 0); return 0; } @@ -1194,10 +1344,23 @@ static int cache_add_int(struct cache *cache, struct backentry *e, int state, * (this means code which suffered from race conditions between multiple * entry modifiers can now work.) */ -int cache_add(struct cache *cache, struct backentry *e, - struct backentry **alt) +int cache_add(struct cache *cache, void *ptr, void **alt) { - return cache_add_int(cache, e, 0, alt); + struct backcommon *e; + if (NULL == ptr) + { + LOG("=> cache_add\n<= cache_add (null entry)\n", 0, 0, 0); + return 0; + } + e = (struct backcommon *)ptr; + if (CACHE_TYPE_ENTRY == e->ep_type) { + return entrycache_add_int(cache, (struct backentry *)e, + 0, (struct backentry **)alt); + } else if (CACHE_TYPE_DN == e->ep_type) { + return dncache_add_int(cache, (struct backdn *)e, + 0, (struct backdn **)alt); + } + return 0; } /* same as above, but add it tentatively: nobody else can use this entry @@ -1206,7 +1369,7 @@ int cache_add(struct cache *cache, struct backentry *e, int cache_add_tentative(struct cache *cache, struct backentry *e, struct backentry **alt) { - return cache_add_int(cache, e, ENTRY_STATE_CREATING, alt); + return entrycache_add_int(cache, e, ENTRY_STATE_CREATING, alt); } /* locks an entry so that it can be modified (you should have gotten the @@ -1248,3 +1411,465 @@ void cache_unlock_entry(struct cache *cache, struct backentry *e) LOG("=> cache_unlock_entry\n", 0, 0, 0); PR_Unlock(e->ep_mutexp); } + +/* DN cache */ +/* remove everything from the cache */ +static void +dncache_clear_int(struct cache *cache) +{ + struct backdn *dnflush = NULL; + struct backdn *dnflushtemp = NULL; + size_t size = cache->c_maxsize; + + if (!entryrdn_get_switch()) { + return; + } + + cache->c_maxsize = 0; + dnflush = dncache_flush(cache); + while (dnflush) + { + dnflushtemp = BACK_LRU_NEXT(dnflush, struct backdn *); + backdn_free(&dnflush); + dnflush = dnflushtemp; + } + cache->c_maxsize = size; + if (cache->c_curentries > 0) { + LDAPDebug1Arg(LDAP_DEBUG_ANY, + "dncache_clear_int: there are still %ld dn's " + "in the dn cache. :/\n", cache->c_curentries); + } +} + +static int +dn_same_id(const void *bdn, const void *k) +{ + return (((struct backdn *)bdn)->ep_id == *(ID *)k); +} + +static void +dncache_set_max_size(struct cache *cache, size_t bytes) +{ + struct backdn *dnflush = NULL; + struct backdn *dnflushtemp = NULL; + + if (!entryrdn_get_switch()) { + return; + } + + if (bytes < MINCACHESIZE) { + bytes = MINCACHESIZE; + LDAPDebug(LDAP_DEBUG_ANY, + "WARNING -- Minimum cache size is %lu -- rounding up\n", + MINCACHESIZE, 0, 0); + } + PR_Lock(cache->c_mutex); + cache->c_maxsize = bytes; + LOG("entry cache size set to %lu\n", bytes, 0, 0); + /* check for full cache, and clear out if necessary */ + if (CACHE_FULL(cache)) { + dnflush = dncache_flush(cache); + } + while (dnflush) + { + dnflushtemp = BACK_LRU_NEXT(dnflush, struct backdn *); + backdn_free(&dnflush); + dnflush = dnflushtemp; + } + if (cache->c_curentries < 50) { + /* there's hardly anything left in the cache -- clear it out and + * resize the hashtables for efficiency. + */ + erase_cache(cache, CACHE_TYPE_DN); + cache_make_hashes(cache, CACHE_TYPE_DN); + } + PR_Unlock(cache->c_mutex); + if (! dblayer_is_cachesize_sane(&bytes)) { + LDAPDebug1Arg(LDAP_DEBUG_ANY, + "WARNING -- Possible CONFIGURATION ERROR -- cachesize " + "(%lu) may be configured to use more than the available " + "physical memory.\n", bytes); + } +} + +/* remove a dn from the cache */ +/* you must be holding c_mutex !! */ +static int +dncache_remove_int(struct cache *cache, struct backdn *bdn) +{ + int ret = 1; /* assume not in cache */ + + if (!entryrdn_get_switch()) { + return 0; + } + + LOG("=> dncache_remove_int (%s)\n", slapi_sdn_get_dn(bdn->dn_sdn), 0, 0); + if (bdn->ep_state & ENTRY_STATE_NOTINCACHE) + { + return ret; + } + + /* remove from id hashtable */ + if (remove_hash(cache->c_idtable, &(bdn->ep_id), sizeof(ID))) + { + ret = 0; + } + else + { + LOG("remove %d from id hash failed\n", bdn->ep_id, 0, 0); + } + if (ret == 0) { + /* won't be on the LRU list since it has a refcount on it */ + /* adjust cache size */ + slapi_counter_subtract(cache->c_cursize, bdn->ep_size); + cache->c_curentries--; + LOG("<= dncache_remove_int (size %lu): cache now %lu dn's, %lu bytes\n", + bdn->ep_size, cache->c_curentries, + slapi_counter_get_value(cache->c_cursize)); + } + + /* mark for deletion (will be erased when refcount drops to zero) */ + bdn->ep_state |= ENTRY_STATE_DELETED; + LOG("<= dncache_remove_int: %d\n", ret, 0, 0); + return ret; +} + +static void +dncache_return(struct cache *cache, struct backdn **bdn) +{ + struct backdn *dnflush = NULL; + struct backdn *dnflushtemp = NULL; + + if (!entryrdn_get_switch()) { + return; + } + + LOG("=> dncache_return (%s) reference count: %d, dn in cache:%ld\n", + slapi_sdn_get_dn((*bdn)->dn_sdn), (*bdn)->ep_refcnt, cache->c_curentries); + + PR_Lock(cache->c_mutex); + if ((*bdn)->ep_state & ENTRY_STATE_NOTINCACHE) + { + backdn_free(bdn); + } + else + { + ASSERT((*bdn)->ep_refcnt > 0); + if (! --(*bdn)->ep_refcnt) { + if ((*bdn)->ep_state & ENTRY_STATE_DELETED) { + backdn_free(bdn); + } else { + lru_add(cache, (void *)*bdn); + /* the cache might be overfull... */ + if (CACHE_FULL(cache)) { + dnflush = dncache_flush(cache); + } + } + } + } + PR_Unlock(cache->c_mutex); + while (dnflush) + { + dnflushtemp = BACK_LRU_NEXT(dnflush, struct backdn *); + backdn_free(&dnflush); + dnflush = dnflushtemp; + } +} + +/* lookup a dn in the cache by its id# (you must return it later) */ +struct backdn * +dncache_find_id(struct cache *cache, ID id) +{ + struct backdn *bdn = NULL; + + if (!entryrdn_get_switch()) { + return bdn; + } + + LOG("=> dncache_find_id (%lu)\n", (u_long)id, 0, 0); + + PR_Lock(cache->c_mutex); + if (find_hash(cache->c_idtable, &id, sizeof(ID), (void **)&bdn)) { + /* need to check entry state */ + if (bdn->ep_state != 0) { + /* entry is deleted or not fully created yet */ + PR_Unlock(cache->c_mutex); + LOG("<= dncache_find_id (NOT FOUND)\n", 0, 0, 0); + return NULL; + } + if (bdn->ep_refcnt == 0) + lru_delete(cache, (void *)bdn); + bdn->ep_refcnt++; + PR_Unlock(cache->c_mutex); + slapi_counter_increment(cache->c_hits); + } else { + PR_Unlock(cache->c_mutex); + } + slapi_counter_increment(cache->c_tries); + + LOG("<= cache_find_id (%sFOUND)\n", bdn ? "" : "NOT ", 0, 0); + return bdn; +} + +/* add a dn to the cache */ +static int +dncache_add_int(struct cache *cache, struct backdn *bdn, int state, + struct backdn **alt) +{ + struct backdn *dnflush = NULL; + struct backdn *dnflushtemp = NULL; + struct backdn *my_alt; + int already_in = 0; + + if (!entryrdn_get_switch()) { + return 0; + } + + LOG("=> dncache_add_int( \"%s\", %ld )\n", slapi_sdn_get_dn(bdn->dn_sdn), + bdn->ep_id, 0); + + PR_Lock(cache->c_mutex); + + if (! add_hash(cache->c_idtable, &(bdn->ep_id), sizeof(ID), bdn, + (void **)&my_alt)) { + LOG("entry %s already in id cache!\n", slapi_sdn_get_dn(bdn->dn_sdn), 0, 0); + if (my_alt == bdn) + { + if ((bdn->ep_state & ENTRY_STATE_CREATING) && (state == 0)) + { + /* attempting to "add" a dn that's already in the cache, + * and the old entry was a placeholder and the new one isn't? + * sounds like a confirmation of a previous add! + */ + LOG("confirming a previous add\n", 0, 0, 0); + already_in = 1; + } + else + { + /* the entry already in the cache and either one of these: + * 1) ep_state: CREATING && state: CREATING + * ==> keep protecting the entry; increase the refcnt + * 2) ep_state: 0 && state: CREATING + * ==> change the state to CREATING (protect it); + * increase the refcnt + * 3) ep_state: 0 && state: 0 + * ==> increase the refcnt + */ + if (bdn->ep_refcnt == 0) + lru_delete(cache, (void *)bdn); + bdn->ep_refcnt++; + bdn->ep_state = state; /* might be CREATING */ + /* returning 1 (entry already existed), but don't set to alt + * to prevent that the caller accidentally thinks the existing + * entry is not the same one the caller has and releases it. + */ + PR_Unlock(cache->c_mutex); + return 1; + } + } + else + { + if (my_alt->ep_state & ENTRY_STATE_CREATING) + { + LOG("the entry is reserved\n", 0, 0, 0); + bdn->ep_state |= ENTRY_STATE_NOTINCACHE; + PR_Unlock(cache->c_mutex); + return -1; + } + else if (state != 0) + { + LOG("the entry already exists. cannot reserve it.\n", 0, 0, 0); + bdn->ep_state |= ENTRY_STATE_NOTINCACHE; + PR_Unlock(cache->c_mutex); + return -1; + } + else + { + if (alt) { + *alt = my_alt; + if ((*alt)->ep_refcnt == 0) + lru_delete(cache, (void *)*alt); + (*alt)->ep_refcnt++; + } + PR_Unlock(cache->c_mutex); + return 1; + } + } + } + + bdn->ep_state = state; + + if (! already_in) { + bdn->ep_refcnt = 1; + if (0 == bdn->ep_size) { + bdn->ep_size = slapi_sdn_get_size(bdn->dn_sdn); + } + + slapi_counter_add(cache->c_cursize, bdn->ep_size); + cache->c_curentries++; + /* don't add to lru since refcnt = 1 */ + LOG("added entry of size %lu -> total now %lu out of max %lu\n", + bdn->ep_size, slapi_counter_get_value(cache->c_cursize), + cache->c_maxsize); + if (cache->c_maxentries >= 0) { + LOG(" total entries %ld out of %ld\n", + cache->c_curentries, cache->c_maxentries, 0); + } + /* check for full cache, and clear out if necessary */ + if (CACHE_FULL(cache)) { + dnflush = dncache_flush(cache); + } + } + PR_Unlock(cache->c_mutex); + + while (dnflush) + { + dnflushtemp = BACK_LRU_NEXT(dnflush, struct backdn *); + backdn_free(&dnflush); + dnflush = dnflushtemp; + } + LOG("<= dncache_add_int OK\n", 0, 0, 0); + return 0; +} + +static int +dncache_replace(struct cache *cache, struct backdn *olddn, struct backdn *newdn) +{ + int found; + const char *oldndn; + const char *newndn; + + if (!entryrdn_get_switch()) { + return 0; + } + + LOG("=> dncache_replace (%s) -> (%s)\n", + slapi_sdn_get_dn(olddn->dn_sdn), slapi_sdn_get_dn(newdn->dn_sdn), 0); + + /* remove from all hashtable -- this function may be called from places + * where the entry isn't in all the table yet, so we don't care if any + * of these return errors. + */ + oldndn = slapi_sdn_get_ndn(olddn->dn_sdn); + newndn = slapi_sdn_get_ndn(newdn->dn_sdn); + PR_Lock(cache->c_mutex); + + /* + * First, remove the old entry from the hashtable. + * If the old entry is in cache but not in at least one of the + * cache tables, operation error + */ + if ( (olddn->ep_state & ENTRY_STATE_NOTINCACHE) == 0 ) { + + found = remove_hash(cache->c_idtable, &(olddn->ep_id), sizeof(ID)); + if (!found) { + LOG("dn cache replace: cache index tables out of sync\n", 0, 0, 0); + PR_Unlock(cache->c_mutex); + return 1; + } + } + + /* now, add the new entry to the hashtables */ + /* (probably don't need such extensive error handling, once this has been + * tested enough that we believe it works.) + */ + if (!add_hash(cache->c_idtable, &(newdn->ep_id), sizeof(ID), newdn, NULL)) { + LOG("dn cache replace: can't add id\n", 0, 0, 0); + PR_Unlock(cache->c_mutex); + return 1; + } + /* adjust cache meta info */ + newdn->ep_refcnt = 1; + if (0 == newdn->ep_size) { + newdn->ep_size = slapi_sdn_get_size(newdn->dn_sdn); + } + if (newdn->ep_size > olddn->ep_size) { + slapi_counter_add(cache->c_cursize, newdn->ep_size - olddn->ep_size); + } else if (newdn->ep_size < olddn->ep_size) { + slapi_counter_subtract(cache->c_cursize, olddn->ep_size - newdn->ep_size); + } + olddn->ep_state = ENTRY_STATE_DELETED; + newdn->ep_state = 0; + PR_Unlock(cache->c_mutex); + LOG("<= dncache_replace OK, cache size now %lu cache count now %ld\n", + slapi_counter_get_value(cache->c_cursize), cache->c_curentries, 0); + return 0; +} + +static struct backdn * +dncache_flush(struct cache *cache) +{ + struct backdn *dn = NULL; + + if (!entryrdn_get_switch()) { + return dn; + } + + LOG("=> dncache_flush\n", 0, 0, 0); + + /* all entries on the LRU list are guaranteed to have a refcnt = 0 + * (iow, nobody's using them), so just delete from the tail down + * until the cache is a managable size again. + * (cache->c_mutex is locked when we enter this) + */ + while ((cache->c_lrutail != NULL) && CACHE_FULL(cache)) { + if (dn == NULL) + { + dn = CACHE_LRU_TAIL(cache, struct backdn *); + } + else + { + dn = BACK_LRU_PREV(dn, struct backdn *); + } + ASSERT(dn->ep_refcnt == 0); + dn->ep_refcnt++; + if (dncache_remove_int(cache, dn) < 0) { + LDAPDebug(LDAP_DEBUG_ANY, "dn cache flush: unable to delete entry\n", + 0, 0, 0); + break; + } + if(dn == CACHE_LRU_HEAD(cache, struct backdn *)) { + break; + } + } + if (dn) + LRU_DETACH(cache, dn); + LOG("<= dncache_flush (down to %lu dns, %lu bytes)\n", cache->c_curentries, + slapi_counter_get_value(cache->c_cursize), 0); + return dn; +} + +#ifdef LDAP_CACHE_DEBUG_LRU +/* for debugging -- painstakingly verify the lru list is ok -- if 'in' is + * true, then dn 'dn' should be in the list right now; otherwise, it + * should NOT be in the list. + */ +static void +dn_lru_verify(struct cache *cache, struct backdn *dn, int in) +{ + int is_in = 0; + int count = 0; + struct backdn *dnp; + + dnp = CACHE_LRU_HEAD(cache, struct backdn *); + while (dnp) { + count++; + if (dnp == dn) { + is_in = 1; + } + if (dnp->ep_lruprev) { + ASSERT(BACK_LRU_NEXT(BACK_LRU_PREV(dnp, struct backdn *), struct backdn *)== dnp); + } else { + ASSERT(dnp == CACHE_LRU_HEAD(cache, struct backdn *)); + } + if (dnp->ep_lrunext) { + ASSERT(BACK_LRU_PREV(BACK_LRU_NEXT(dnp, struct backdn *), struct backdn *) == dnp); + } else { + ASSERT(dnp == CACHE_LRU_TAIL(cache, struct backdn *)); + } + + dnp = BACK_LRU_NEXT(dnp, struct backdn *); + } + ASSERT(is_in == in); +} +#endif |