]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb/cache.c
10e591bffe5bc92394f3b193d5c0037c8ff55283
[openldap] / servers / slapd / back-bdb / cache.c
1 /* cache.c - routines to maintain an in-core cache of entries */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2000-2007 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16
17 #include "portable.h"
18
19 #include <stdio.h>
20
21 #include <ac/errno.h>
22 #include <ac/string.h>
23 #include <ac/socket.h>
24
25 #include "slap.h"
26
27 #include "back-bdb.h"
28
29 #include "ldap_rq.h"
30
31 #ifdef BDB_HIER
32 #define bdb_cache_lru_add       hdb_cache_lru_add
33 #endif
34 static void bdb_cache_lru_add( struct bdb_info *bdb, EntryInfo *ei );
35
36 static int      bdb_cache_delete_internal(Cache *cache, EntryInfo *e, int decr);
37 #ifdef LDAP_DEBUG
38 #ifdef SLAPD_UNUSED
39 static void     bdb_lru_print(Cache *cache);
40 #endif
41 #endif
42
43 static EntryInfo *
44 bdb_cache_entryinfo_new( Cache *cache )
45 {
46         EntryInfo *ei = NULL;
47
48         if ( cache->c_eifree ) {
49                 ldap_pvt_thread_rdwr_wlock( &cache->c_rwlock );
50                 if ( cache->c_eifree ) {
51                         ei = cache->c_eifree;
52                         cache->c_eifree = ei->bei_lrunext;
53                 }
54                 ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock );
55         }
56         if ( ei ) {
57                 ei->bei_lrunext = NULL;
58                 ei->bei_state = 0;
59         } else {
60                 ei = ch_calloc(1, sizeof(struct bdb_entry_info));
61                 ldap_pvt_thread_mutex_init( &ei->bei_kids_mutex );
62         }
63
64         return ei;
65 }
66
67 /* Atomically release and reacquire a lock */
68 int
69 bdb_cache_entry_db_relock(
70         DB_ENV *env,
71         u_int32_t locker,
72         EntryInfo *ei,
73         int rw,
74         int tryOnly,
75         DB_LOCK *lock )
76 {
77 #ifdef NO_THREADS
78         return 0;
79 #else
80         int     rc;
81         DBT     lockobj;
82         DB_LOCKREQ list[2];
83
84         if ( !lock ) return 0;
85
86         lockobj.data = &ei->bei_id;
87         lockobj.size = sizeof(ei->bei_id) + 1;
88
89         list[0].op = DB_LOCK_PUT;
90         list[0].lock = *lock;
91         list[1].op = DB_LOCK_GET;
92         list[1].lock = *lock;
93         list[1].mode = rw ? DB_LOCK_WRITE : DB_LOCK_READ;
94         list[1].obj = &lockobj;
95         rc = env->lock_vec(env, locker, tryOnly ? DB_LOCK_NOWAIT : 0,
96                 list, 2, NULL );
97
98         if (rc && !tryOnly) {
99                 Debug( LDAP_DEBUG_TRACE,
100                         "bdb_cache_entry_db_relock: entry %ld, rw %d, rc %d\n",
101                         ei->bei_id, rw, rc );
102         } else {
103                 *lock = list[1].lock;
104         }
105         return rc;
106 #endif
107 }
108
109 static int
110 bdb_cache_entry_db_lock( DB_ENV *env, u_int32_t locker, EntryInfo *ei,
111         int rw, int tryOnly, DB_LOCK *lock )
112 {
113 #ifdef NO_THREADS
114         return 0;
115 #else
116         int       rc;
117         DBT       lockobj;
118         int       db_rw;
119
120         if ( !lock ) return 0;
121
122         if (rw)
123                 db_rw = DB_LOCK_WRITE;
124         else
125                 db_rw = DB_LOCK_READ;
126
127         lockobj.data = &ei->bei_id;
128         lockobj.size = sizeof(ei->bei_id) + 1;
129
130         rc = LOCK_GET(env, locker, tryOnly ? DB_LOCK_NOWAIT : 0,
131                                         &lockobj, db_rw, lock);
132         if (rc && !tryOnly) {
133                 Debug( LDAP_DEBUG_TRACE,
134                         "bdb_cache_entry_db_lock: entry %ld, rw %d, rc %d\n",
135                         ei->bei_id, rw, rc );
136         }
137         return rc;
138 #endif /* NO_THREADS */
139 }
140
141 int
142 bdb_cache_entry_db_unlock ( DB_ENV *env, DB_LOCK *lock )
143 {
144 #ifdef NO_THREADS
145         return 0;
146 #else
147         int rc;
148
149         if ( !lock || lock->mode == DB_LOCK_NG ) return 0;
150
151         rc = LOCK_PUT ( env, lock );
152         return rc;
153 #endif
154 }
155
156 static int
157 bdb_cache_entryinfo_destroy( EntryInfo *e )
158 {
159         ldap_pvt_thread_mutex_destroy( &e->bei_kids_mutex );
160         free( e->bei_nrdn.bv_val );
161 #ifdef BDB_HIER
162         free( e->bei_rdn.bv_val );
163 #endif
164         free( e );
165         return 0;
166 }
167
168 #define LRU_DELETE( cache, ei ) do { \
169         if ( (ei)->bei_lruprev != NULL ) { \
170                 (ei)->bei_lruprev->bei_lrunext = (ei)->bei_lrunext; \
171         } else { \
172                 (cache)->c_lruhead = (ei)->bei_lrunext; \
173         } \
174         if ( (ei)->bei_lrunext != NULL ) { \
175                 (ei)->bei_lrunext->bei_lruprev = (ei)->bei_lruprev; \
176         } else { \
177                 (cache)->c_lrutail = (ei)->bei_lruprev; \
178         } \
179         (ei)->bei_lrunext = (ei)->bei_lruprev = NULL; \
180 } while(0)
181
182 #define LRU_ADD( cache, ei ) do { \
183         (ei)->bei_lrunext = (cache)->c_lruhead; \
184         if ( (ei)->bei_lrunext != NULL ) { \
185                 (ei)->bei_lrunext->bei_lruprev = (ei); \
186         } \
187         (cache)->c_lruhead = (ei); \
188         (ei)->bei_lruprev = NULL; \
189         if ( !ldap_pvt_thread_mutex_trylock( &(cache)->lru_tail_mutex )) { \
190                 if ( (cache)->c_lrutail == NULL ) \
191                         (cache)->c_lrutail = (ei); \
192                 ldap_pvt_thread_mutex_unlock( &(cache)->lru_tail_mutex ); \
193         } \
194 } while(0)
195
196 /* Do a length-ordered sort on normalized RDNs */
197 static int
198 bdb_rdn_cmp( const void *v_e1, const void *v_e2 )
199 {
200         const EntryInfo *e1 = v_e1, *e2 = v_e2;
201         int rc = e1->bei_nrdn.bv_len - e2->bei_nrdn.bv_len;
202         if (rc == 0) {
203                 rc = strncmp( e1->bei_nrdn.bv_val, e2->bei_nrdn.bv_val,
204                         e1->bei_nrdn.bv_len );
205         }
206         return rc;
207 }
208
209 static int
210 bdb_id_cmp( const void *v_e1, const void *v_e2 )
211 {
212         const EntryInfo *e1 = v_e1, *e2 = v_e2;
213         return e1->bei_id - e2->bei_id;
214 }
215
216 /* Create an entryinfo in the cache. Caller must release the locks later.
217  */
218 static int
219 bdb_entryinfo_add_internal(
220         struct bdb_info *bdb,
221         EntryInfo *ei,
222         EntryInfo **res )
223 {
224         EntryInfo *ei2 = NULL;
225
226         *res = NULL;
227
228         ei2 = bdb_cache_entryinfo_new( &bdb->bi_cache );
229
230         ldap_pvt_thread_rdwr_wlock( &bdb->bi_cache.c_rwlock );
231         bdb_cache_entryinfo_lock( ei->bei_parent );
232
233         ei2->bei_id = ei->bei_id;
234         ei2->bei_parent = ei->bei_parent;
235 #ifdef BDB_HIER
236         ei2->bei_rdn = ei->bei_rdn;
237 #endif
238 #ifdef SLAP_ZONE_ALLOC
239         ei2->bei_bdb = bdb;
240 #endif
241
242         /* Add to cache ID tree */
243         if (avl_insert( &bdb->bi_cache.c_idtree, ei2, bdb_id_cmp, avl_dup_error )) {
244                 EntryInfo *eix;
245                 eix = avl_find( bdb->bi_cache.c_idtree, ei2, bdb_id_cmp );
246                 bdb_cache_entryinfo_destroy( ei2 );
247                 ei2 = eix;
248 #ifdef BDB_HIER
249                 /* It got freed above because its value was
250                  * assigned to ei2.
251                  */
252                 ei->bei_rdn.bv_val = NULL;
253 #endif
254         } else {
255                 int rc;
256
257                 bdb->bi_cache.c_eiused++;
258                 ber_dupbv( &ei2->bei_nrdn, &ei->bei_nrdn );
259
260                 /* This is a new leaf node. But if parent had no kids, then it was
261                  * a leaf and we would be decrementing that. So, only increment if
262                  * the parent already has kids.
263                  */
264                 if ( ei->bei_parent->bei_kids || !ei->bei_parent->bei_id )
265                         bdb->bi_cache.c_leaves++;
266                 rc = avl_insert( &ei->bei_parent->bei_kids, ei2, bdb_rdn_cmp,
267                         avl_dup_error );
268                 if ( rc ) {
269                         /* This should never happen; entry cache is corrupt */
270                         bdb->bi_dbenv->log_flush( bdb->bi_dbenv, NULL );
271                         assert( !rc );
272                 }
273 #ifdef BDB_HIER
274                 ei->bei_parent->bei_ckids++;
275 #endif
276         }
277
278         *res = ei2;
279         return 0;
280 }
281
282 /* Find the EntryInfo for the requested DN. If the DN cannot be found, return
283  * the info for its closest ancestor. *res should be NULL to process a
284  * complete DN starting from the tree root. Otherwise *res must be the
285  * immediate parent of the requested DN, and only the RDN will be searched.
286  * The EntryInfo is locked upon return and must be unlocked by the caller.
287  */
288 int
289 bdb_cache_find_ndn(
290         Operation       *op,
291         u_int32_t               locker,
292         struct berval   *ndn,
293         EntryInfo       **res )
294 {
295         struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
296         EntryInfo       ei, *eip, *ei2;
297         int rc = 0;
298         char *ptr;
299
300         /* this function is always called with normalized DN */
301         if ( *res ) {
302                 /* we're doing a onelevel search for an RDN */
303                 ei.bei_nrdn.bv_val = ndn->bv_val;
304                 ei.bei_nrdn.bv_len = dn_rdnlen( op->o_bd, ndn );
305                 eip = *res;
306         } else {
307                 /* we're searching a full DN from the root */
308                 ptr = ndn->bv_val + ndn->bv_len - op->o_bd->be_nsuffix[0].bv_len;
309                 ei.bei_nrdn.bv_val = ptr;
310                 ei.bei_nrdn.bv_len = op->o_bd->be_nsuffix[0].bv_len;
311                 /* Skip to next rdn if suffix is empty */
312                 if ( ei.bei_nrdn.bv_len == 0 ) {
313                         for (ptr = ei.bei_nrdn.bv_val - 2; ptr > ndn->bv_val
314                                 && !DN_SEPARATOR(*ptr); ptr--) /* empty */;
315                         if ( ptr >= ndn->bv_val ) {
316                                 if (DN_SEPARATOR(*ptr)) ptr++;
317                                 ei.bei_nrdn.bv_len = ei.bei_nrdn.bv_val - ptr;
318                                 ei.bei_nrdn.bv_val = ptr;
319                         }
320                 }
321                 eip = &bdb->bi_cache.c_dntree;
322         }
323         
324         for ( bdb_cache_entryinfo_lock( eip ); eip; ) {
325                 ei.bei_parent = eip;
326                 ei2 = (EntryInfo *)avl_find( eip->bei_kids, &ei, bdb_rdn_cmp );
327                 if ( !ei2 ) {
328                         DB_LOCK lock;
329                         int len = ei.bei_nrdn.bv_len;
330                                 
331                         if ( BER_BVISEMPTY( ndn )) {
332                                 *res = eip;
333                                 return LDAP_SUCCESS;
334                         }
335
336                         ei.bei_nrdn.bv_len = ndn->bv_len -
337                                 (ei.bei_nrdn.bv_val - ndn->bv_val);
338                         bdb_cache_entryinfo_unlock( eip );
339
340                         lock.mode = DB_LOCK_NG;
341                         rc = bdb_dn2id( op, &ei.bei_nrdn, &ei, locker, &lock );
342                         if (rc) {
343                                 bdb_cache_entryinfo_lock( eip );
344                                 bdb_cache_entry_db_unlock( bdb->bi_dbenv, &lock );
345                                 *res = eip;
346                                 return rc;
347                         }
348
349                         /* DN exists but needs to be added to cache */
350                         ei.bei_nrdn.bv_len = len;
351                         rc = bdb_entryinfo_add_internal( bdb, &ei, &ei2 );
352                         /* add_internal left eip and c_rwlock locked */
353                         ldap_pvt_thread_rdwr_wunlock( &bdb->bi_cache.c_rwlock );
354                         bdb_cache_entry_db_unlock( bdb->bi_dbenv, &lock );
355                         if ( rc ) {
356                                 *res = eip;
357                                 return rc;
358                         }
359                 } else if ( ei2->bei_state & CACHE_ENTRY_DELETED ) {
360                         /* In the midst of deleting? Give it a chance to
361                          * complete.
362                          */
363                         bdb_cache_entryinfo_unlock( eip );
364                         ldap_pvt_thread_yield();
365                         bdb_cache_entryinfo_lock( eip );
366                         *res = eip;
367                         return DB_NOTFOUND;
368                 }
369                 bdb_cache_entryinfo_unlock( eip );
370                 bdb_cache_entryinfo_lock( ei2 );
371
372                 eip = ei2;
373
374                 /* Advance to next lower RDN */
375                 for (ptr = ei.bei_nrdn.bv_val - 2; ptr > ndn->bv_val
376                         && !DN_SEPARATOR(*ptr); ptr--) /* empty */;
377                 if ( ptr >= ndn->bv_val ) {
378                         if (DN_SEPARATOR(*ptr)) ptr++;
379                         ei.bei_nrdn.bv_len = ei.bei_nrdn.bv_val - ptr - 1;
380                         ei.bei_nrdn.bv_val = ptr;
381                 }
382                 if ( ptr < ndn->bv_val ) {
383                         *res = eip;
384                         break;
385                 }
386         }
387
388         return rc;
389 }
390
391 #ifdef BDB_HIER
392 /* Walk up the tree from a child node, looking for an ID that's already
393  * been linked into the cache.
394  */
395 int
396 hdb_cache_find_parent(
397         Operation *op,
398         u_int32_t       locker,
399         ID id,
400         EntryInfo **res )
401 {
402         struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
403         EntryInfo ei, eip, *ei2 = NULL, *ein = NULL, *eir = NULL;
404         int rc;
405         int addlru = 0;
406
407         ei.bei_id = id;
408         ei.bei_kids = NULL;
409         ei.bei_ckids = 0;
410
411         for (;;) {
412                 rc = hdb_dn2id_parent( op, locker, &ei, &eip.bei_id );
413                 if ( rc ) break;
414
415                 /* Save the previous node, if any */
416                 ei2 = ein;
417
418                 /* Create a new node for the current ID */
419                 ein = bdb_cache_entryinfo_new( &bdb->bi_cache );
420                 ein->bei_id = ei.bei_id;
421                 ein->bei_kids = ei.bei_kids;
422                 ein->bei_nrdn = ei.bei_nrdn;
423                 ein->bei_rdn = ei.bei_rdn;
424                 ein->bei_ckids = ei.bei_ckids;
425 #ifdef SLAP_ZONE_ALLOC
426                 ein->bei_bdb = bdb;
427 #endif
428                 ei.bei_ckids = 0;
429                 
430                 /* This node is not fully connected yet */
431                 ein->bei_state = CACHE_ENTRY_NOT_LINKED;
432
433                 /* Insert this node into the ID tree */
434                 ldap_pvt_thread_rdwr_wlock( &bdb->bi_cache.c_rwlock );
435                 if ( avl_insert( &bdb->bi_cache.c_idtree, (caddr_t)ein,
436                         bdb_id_cmp, avl_dup_error ) ) {
437
438                         /* Someone else created this node just before us.
439                          * Free our new copy and use the existing one.
440                          */
441                         bdb_cache_entryinfo_destroy( ein );
442                         ein = (EntryInfo *)avl_find( bdb->bi_cache.c_idtree,
443                                 (caddr_t) &ei, bdb_id_cmp );
444                         
445                         /* Link in any kids we've already processed */
446                         if ( ei2 ) {
447                                 bdb_cache_entryinfo_lock( ein );
448                                 avl_insert( &ein->bei_kids, (caddr_t)ei2,
449                                         bdb_rdn_cmp, avl_dup_error );
450                                 ein->bei_ckids++;
451                                 bdb_cache_entryinfo_unlock( ein );
452                         }
453                         addlru = 0;
454
455                 }
456
457                 /* If this is the first time, save this node
458                  * to be returned later.
459                  */
460                 if ( eir == NULL ) eir = ein;
461
462                 /* If there was a previous node, link it to this one */
463                 if ( ei2 ) ei2->bei_parent = ein;
464
465                 /* Look for this node's parent */
466                 if ( eip.bei_id ) {
467                         ei2 = (EntryInfo *) avl_find( bdb->bi_cache.c_idtree,
468                                         (caddr_t) &eip, bdb_id_cmp );
469                 } else {
470                         ei2 = &bdb->bi_cache.c_dntree;
471                 }
472                 bdb->bi_cache.c_eiused++;
473                 if ( ei2 && ( ei2->bei_kids || !ei2->bei_id ))
474                                 bdb->bi_cache.c_leaves++;
475                 ldap_pvt_thread_rdwr_wunlock( &bdb->bi_cache.c_rwlock );
476
477                 if ( addlru ) {
478                         ldap_pvt_thread_mutex_lock( &bdb->bi_cache.lru_head_mutex );
479                         bdb_cache_lru_add( bdb, ein );
480                 }
481                 addlru = 1;
482
483                 /* Got the parent, link in and we're done. */
484                 if ( ei2 ) {
485                         bdb_cache_entryinfo_lock( ei2 );
486                         ein->bei_parent = ei2;
487                         avl_insert( &ei2->bei_kids, (caddr_t)ein, bdb_rdn_cmp,
488                                 avl_dup_error);
489                         ei2->bei_ckids++;
490                         bdb_cache_entryinfo_unlock( ei2 );
491                         bdb_cache_entryinfo_lock( eir );
492
493                         /* Reset all the state info */
494                         for (ein = eir; ein != ei2; ein=ein->bei_parent)
495                                 ein->bei_state &= ~CACHE_ENTRY_NOT_LINKED;
496                         *res = eir;
497                         break;
498                 }
499                 ei.bei_kids = NULL;
500                 ei.bei_id = eip.bei_id;
501                 ei.bei_ckids = 1;
502                 avl_insert( &ei.bei_kids, (caddr_t)ein, bdb_rdn_cmp,
503                         avl_dup_error );
504         }
505         return rc;
506 }
507
508 /* Used by hdb_dn2idl when loading the EntryInfo for all the children
509  * of a given node
510  */
511 int hdb_cache_load(
512         struct bdb_info *bdb,
513         EntryInfo *ei,
514         EntryInfo **res )
515 {
516         EntryInfo *ei2;
517         int rc;
518
519         /* See if we already have this one */
520         bdb_cache_entryinfo_lock( ei->bei_parent );
521         ei2 = (EntryInfo *)avl_find( ei->bei_parent->bei_kids, ei, bdb_rdn_cmp );
522         bdb_cache_entryinfo_unlock( ei->bei_parent );
523
524         if ( !ei2 ) {
525                 /* Not found, add it */
526                 struct berval bv;
527
528                 /* bei_rdn was not malloc'd before, do it now */
529                 ber_dupbv( &bv, &ei->bei_rdn );
530                 ei->bei_rdn = bv;
531
532                 rc = bdb_entryinfo_add_internal( bdb, ei, res );
533                 bdb_cache_entryinfo_unlock( ei->bei_parent );
534                 ldap_pvt_thread_rdwr_wunlock( &bdb->bi_cache.c_rwlock );
535         } else {
536                 /* Found, return it */
537                 *res = ei2;
538                 return 0;
539         }
540         return rc;
541 }
542 #endif
543
544 /* caller must have lru_head_mutex locked. mutex
545  * will be unlocked on return.
546  */
547 static void
548 bdb_cache_lru_add(
549         struct bdb_info *bdb,
550         EntryInfo *ei )
551 {
552         DB_LOCK         lock, *lockp;
553         EntryInfo *elru, *elprev;
554         int count = 0;
555
556         LRU_ADD( &bdb->bi_cache, ei );
557         ldap_pvt_thread_mutex_unlock( &bdb->bi_cache.lru_head_mutex );
558
559         /* See if we're above the cache size limit */
560         if ( bdb->bi_cache.c_cursize <= bdb->bi_cache.c_maxsize )
561                 return;
562
563         if ( bdb->bi_cache.c_locker ) {
564                 lockp = &lock;
565         } else {
566                 lockp = NULL;
567         }
568
569         /* Don't bother if we can't get the lock */
570         if ( ldap_pvt_thread_mutex_trylock( &bdb->bi_cache.lru_tail_mutex ) )
571                 return;
572
573         /* Look for an unused entry to remove */
574         for (elru = bdb->bi_cache.c_lrutail; elru; elru = elprev ) {
575                 elprev = elru->bei_lruprev;
576
577                 /* If we can successfully writelock it, then
578                  * the object is idle.
579                  */
580                 if ( bdb_cache_entry_db_lock( bdb->bi_dbenv,
581                                 bdb->bi_cache.c_locker, elru, 1, 1, lockp ) == 0 ) {
582
583
584                         /* If this node is in the process of linking into the cache,
585                          * or this node is being deleted, skip it.
586                          */
587                         if ( elru->bei_state &
588                                 ( CACHE_ENTRY_NOT_LINKED | CACHE_ENTRY_DELETED )) {
589                                 bdb_cache_entry_db_unlock( bdb->bi_dbenv, lockp );
590                                 continue;
591                         }
592                         /* Free entry for this node if it's present */
593                         if ( elru->bei_e ) {
594                                 elru->bei_e->e_private = NULL;
595 #ifdef SLAP_ZONE_ALLOC
596                                 bdb_entry_return( bdb, elru->bei_e, elru->bei_zseq );
597 #else
598                                 bdb_entry_return( elru->bei_e );
599 #endif
600                                 elru->bei_e = NULL;
601                                 count++;
602                         }
603                         /* ITS#4010 if we're in slapcat, and this node is a leaf
604                          * node, free it.
605                          *
606                          * FIXME: we need to do this for slapd as well, (which is
607                          * why we compute bi_cache.c_leaves now) but at the moment
608                          * we can't because it causes unresolvable deadlocks. 
609                          */
610                         if ( slapMode & SLAP_TOOL_READONLY ) {
611                                 if ( !elru->bei_kids ) {
612                                         /* This does LRU_DELETE for us */
613                                         bdb_cache_delete_internal( &bdb->bi_cache, elru, 0 );
614                                         bdb_cache_delete_cleanup( &bdb->bi_cache, elru );
615                                 }
616                                 /* Leave node on LRU list for a future pass */
617                         } else {
618                                 LRU_DELETE( &bdb->bi_cache, elru );
619                         }
620                         bdb_cache_entry_db_unlock( bdb->bi_dbenv, lockp );
621
622                         if ( count >= bdb->bi_cache.c_minfree ) {
623                                 ldap_pvt_thread_rdwr_wlock( &bdb->bi_cache.c_rwlock );
624                                 bdb->bi_cache.c_cursize -= count;
625                                 ldap_pvt_thread_rdwr_wunlock( &bdb->bi_cache.c_rwlock );
626                                 break;
627                         }
628                 }
629         }
630
631         ldap_pvt_thread_mutex_unlock( &bdb->bi_cache.lru_tail_mutex );
632 }
633
634 EntryInfo *
635 bdb_cache_find_info(
636         struct bdb_info *bdb,
637         ID id )
638 {
639         EntryInfo       ei = { 0 },
640                         *ei2;
641
642         ei.bei_id = id;
643
644         ldap_pvt_thread_rdwr_rlock( &bdb->bi_cache.c_rwlock );
645         ei2 = (EntryInfo *) avl_find( bdb->bi_cache.c_idtree,
646                                         (caddr_t) &ei, bdb_id_cmp );
647         ldap_pvt_thread_rdwr_runlock( &bdb->bi_cache.c_rwlock );
648         return ei2;
649 }
650
651 /*
652  * cache_find_id - find an entry in the cache, given id.
653  * The entry is locked for Read upon return. Call with islocked TRUE if
654  * the supplied *eip was already locked.
655  */
656
657 int
658 bdb_cache_find_id(
659         Operation *op,
660         DB_TXN  *tid,
661         ID                              id,
662         EntryInfo       **eip,
663         int             islocked,
664         u_int32_t       locker,
665         DB_LOCK         *lock )
666 {
667         struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
668         Entry   *ep = NULL;
669         int     rc = 0, load = 0;
670         EntryInfo ei = { 0 };
671
672         ei.bei_id = id;
673
674 #ifdef SLAP_ZONE_ALLOC
675         slap_zh_rlock(bdb->bi_cache.c_zctx);
676 #endif
677         /* If we weren't given any info, see if we have it already cached */
678         if ( !*eip ) {
679 again:  ldap_pvt_thread_rdwr_rlock( &bdb->bi_cache.c_rwlock );
680                 *eip = (EntryInfo *) avl_find( bdb->bi_cache.c_idtree,
681                         (caddr_t) &ei, bdb_id_cmp );
682                 if ( *eip ) {
683                         /* If the lock attempt fails, the info is in use */
684                         if ( ldap_pvt_thread_mutex_trylock(
685                                         &(*eip)->bei_kids_mutex )) {
686                                 ldap_pvt_thread_rdwr_runlock( &bdb->bi_cache.c_rwlock );
687                                 /* If this node is being deleted, treat
688                                  * as if the delete has already finished
689                                  */
690                                 if ( (*eip)->bei_state & CACHE_ENTRY_DELETED ) {
691                                         return DB_NOTFOUND;
692                                 }
693                                 /* otherwise, wait for the info to free up */
694                                 ldap_pvt_thread_yield();
695                                 goto again;
696                         }
697                         /* If this info isn't hooked up to its parent yet,
698                          * unlock and wait for it to be fully initialized
699                          */
700                         if ( (*eip)->bei_state & CACHE_ENTRY_NOT_LINKED ) {
701                                 bdb_cache_entryinfo_unlock( *eip );
702                                 ldap_pvt_thread_rdwr_runlock( &bdb->bi_cache.c_rwlock );
703                                 ldap_pvt_thread_yield();
704                                 goto again;
705                         }
706                         islocked = 1;
707                 }
708                 ldap_pvt_thread_rdwr_runlock( &bdb->bi_cache.c_rwlock );
709         }
710
711         /* See if the ID exists in the database; add it to the cache if so */
712         if ( !*eip ) {
713 #ifndef BDB_HIER
714                 rc = bdb_id2entry( op->o_bd, tid, locker, id, &ep );
715                 if ( rc == 0 ) {
716                         rc = bdb_cache_find_ndn( op, locker,
717                                 &ep->e_nname, eip );
718                         if ( *eip ) islocked = 1;
719                         if ( rc ) {
720                                 ep->e_private = NULL;
721 #ifdef SLAP_ZONE_ALLOC
722                                 bdb_entry_return( bdb, ep, (*eip)->bei_zseq );
723 #else
724                                 bdb_entry_return( ep );
725 #endif
726                                 ep = NULL;
727                         }
728                 }
729 #else
730                 rc = hdb_cache_find_parent(op, locker, id, eip );
731                 if ( rc == 0 ) islocked = 1;
732 #endif
733         }
734
735         /* Ok, we found the info, do we have the entry? */
736         if ( rc == 0 ) {
737                 if ( (*eip)->bei_state & CACHE_ENTRY_DELETED ) {
738                         rc = DB_NOTFOUND;
739                 } else {
740                         /* Make sure only one thread tries to load the entry */
741 load1:
742 #ifdef SLAP_ZONE_ALLOC
743                         if ((*eip)->bei_e && !slap_zn_validate(
744                                         bdb->bi_cache.c_zctx, (*eip)->bei_e, (*eip)->bei_zseq)) {
745                                 (*eip)->bei_e = NULL;
746                                 (*eip)->bei_zseq = 0;
747                         }
748 #endif
749                         if ( !(*eip)->bei_e && !((*eip)->bei_state & CACHE_ENTRY_LOADING)) {
750                                 load = 1;
751                                 (*eip)->bei_state |= CACHE_ENTRY_LOADING;
752                         }
753                         if ( islocked ) {
754                                 bdb_cache_entryinfo_unlock( *eip );
755                                 islocked = 0;
756                         }
757                         rc = bdb_cache_entry_db_lock( bdb->bi_dbenv, locker, *eip, 0, 0, lock );
758                         if ( (*eip)->bei_state & CACHE_ENTRY_DELETED ) {
759                                 rc = DB_NOTFOUND;
760                                 bdb_cache_entry_db_unlock( bdb->bi_dbenv, lock );
761                         } else if ( rc == 0 ) {
762                                 if ( load ) {
763                                         /* Give up original read lock, obtain write lock
764                                          */
765                                     if ( rc == 0 ) {
766                                                 rc = bdb_cache_entry_db_relock( bdb->bi_dbenv, locker,
767                                                         *eip, 1, 0, lock );
768                                         }
769                                         if ( rc == 0 && !ep) {
770                                                 rc = bdb_id2entry( op->o_bd, tid, locker, id, &ep );
771                                         }
772                                         if ( rc == 0 ) {
773                                                 ep->e_private = *eip;
774 #ifdef BDB_HIER
775                                                 bdb_fix_dn( ep, 0 );
776 #endif
777                                                 (*eip)->bei_e = ep;
778 #ifdef SLAP_ZONE_ALLOC
779                                                 (*eip)->bei_zseq = *((ber_len_t *)ep - 2);
780 #endif
781                                                 ep = NULL;
782                                         }
783                                         bdb_cache_entryinfo_lock( *eip );
784                                         (*eip)->bei_state ^= CACHE_ENTRY_LOADING;
785                                         bdb_cache_entryinfo_unlock( *eip );
786                                         if ( rc == 0 ) {
787                                                 /* If we succeeded, downgrade back to a readlock. */
788                                                 rc = bdb_cache_entry_db_relock( bdb->bi_dbenv, locker,
789                                                         *eip, 0, 0, lock );
790                                         } else {
791                                                 /* Otherwise, release the lock. */
792                                                 bdb_cache_entry_db_unlock( bdb->bi_dbenv, lock );
793                                         }
794                                 } else if ( !(*eip)->bei_e ) {
795                                         /* Some other thread is trying to load the entry,
796                                          * give it a chance to finish.
797                                          */
798                                         bdb_cache_entry_db_unlock( bdb->bi_dbenv, lock );
799                                         ldap_pvt_thread_yield();
800                                         bdb_cache_entryinfo_lock( *eip );
801                                         islocked = 1;
802                                         goto load1;
803 #ifdef BDB_HIER
804                                 } else {
805                                         /* Check for subtree renames
806                                          */
807                                         rc = bdb_fix_dn( (*eip)->bei_e, 1 );
808                                         if ( rc ) {
809                                                 bdb_cache_entry_db_relock( bdb->bi_dbenv,
810                                                         locker, *eip, 1, 0, lock );
811                                                 /* check again in case other modifier did it already */
812                                                 if ( bdb_fix_dn( (*eip)->bei_e, 1 ) )
813                                                         rc = bdb_fix_dn( (*eip)->bei_e, 2 );
814                                                 bdb_cache_entry_db_relock( bdb->bi_dbenv,
815                                                         locker, *eip, 0, 0, lock );
816                                         }
817 #endif
818                                 }
819
820                         }
821                 }
822         }
823         if ( islocked ) {
824                 bdb_cache_entryinfo_unlock( *eip );
825         }
826         if ( ep ) {
827                 ep->e_private = NULL;
828 #ifdef SLAP_ZONE_ALLOC
829                 bdb_entry_return( bdb, ep, (*eip)->bei_zseq );
830 #else
831                 bdb_entry_return( ep );
832 #endif
833         }
834         if ( rc == 0 ) {
835
836                 if ( load ) {
837                         ldap_pvt_thread_rdwr_wlock( &bdb->bi_cache.c_rwlock );
838                         bdb->bi_cache.c_cursize++;
839                         ldap_pvt_thread_rdwr_wunlock( &bdb->bi_cache.c_rwlock );
840                 }
841
842                 ldap_pvt_thread_mutex_lock( &bdb->bi_cache.lru_head_mutex );
843
844                 /* If the LRU list has only one entry and this is it, it
845                  * doesn't need to be added again.
846                  */
847                 if ( bdb->bi_cache.c_lruhead == bdb->bi_cache.c_lrutail &&
848                         bdb->bi_cache.c_lruhead == *eip ) {
849                         ldap_pvt_thread_mutex_unlock( &bdb->bi_cache.lru_head_mutex );
850                 } else {
851                         /* if entry is on LRU list, remove from old spot */
852                         if ( (*eip)->bei_lrunext || (*eip)->bei_lruprev ) {
853                                 ldap_pvt_thread_mutex_lock( &bdb->bi_cache.lru_tail_mutex );
854                                 LRU_DELETE( &bdb->bi_cache, *eip );
855                                 ldap_pvt_thread_mutex_unlock( &bdb->bi_cache.lru_tail_mutex );
856                         }
857                         /* lru_head_mutex is unlocked for us */
858                         bdb_cache_lru_add( bdb, *eip );
859                 }
860         }
861
862 #ifdef SLAP_ZONE_ALLOC
863         if (rc == 0 && (*eip)->bei_e) {
864                 slap_zn_rlock(bdb->bi_cache.c_zctx, (*eip)->bei_e);
865         }
866         slap_zh_runlock(bdb->bi_cache.c_zctx);
867 #endif
868         return rc;
869 }
870
871 int
872 bdb_cache_children(
873         Operation *op,
874         DB_TXN *txn,
875         Entry *e )
876 {
877         int rc;
878
879         if ( BEI(e)->bei_kids ) {
880                 return 0;
881         }
882         if ( BEI(e)->bei_state & CACHE_ENTRY_NO_KIDS ) {
883                 return DB_NOTFOUND;
884         }
885         rc = bdb_dn2id_children( op, txn, e );
886         if ( rc == DB_NOTFOUND ) {
887                 BEI(e)->bei_state |= CACHE_ENTRY_NO_KIDS | CACHE_ENTRY_NO_GRANDKIDS;
888         }
889         return rc;
890 }
891
892 /* Update the cache after a successful database Add. */
893 int
894 bdb_cache_add(
895         struct bdb_info *bdb,
896         EntryInfo *eip,
897         Entry *e,
898         struct berval *nrdn,
899         u_int32_t locker )
900 {
901         EntryInfo *new, ei;
902         DB_LOCK lock;
903         int rc;
904 #ifdef BDB_HIER
905         struct berval rdn = e->e_name;
906 #endif
907
908         ei.bei_id = e->e_id;
909         ei.bei_parent = eip;
910         ei.bei_nrdn = *nrdn;
911         ei.bei_lockpad = 0;
912
913         /* Lock this entry so that bdb_add can run to completion.
914          * It can only fail if BDB has run out of lock resources.
915          */
916         rc = bdb_cache_entry_db_lock( bdb->bi_dbenv, locker, &ei, 1, 0, &lock );
917         if ( rc ) {
918                 bdb_cache_entryinfo_unlock( eip );
919                 return rc;
920         }
921
922 #ifdef BDB_HIER
923         if ( nrdn->bv_len != e->e_nname.bv_len ) {
924                 char *ptr = ber_bvchr( &rdn, ',' );
925                 assert( ptr != NULL );
926                 rdn.bv_len = ptr - rdn.bv_val;
927         }
928         ber_dupbv( &ei.bei_rdn, &rdn );
929         if ( eip->bei_dkids ) eip->bei_dkids++;
930 #endif
931
932         rc = bdb_entryinfo_add_internal( bdb, &ei, &new );
933         /* bdb_csn_commit can cause this when adding the database root entry */
934         if ( new->bei_e ) {
935                 new->bei_e->e_private = NULL;
936 #ifdef SLAP_ZONE_ALLOC
937                 bdb_entry_return( bdb, new->bei_e, new->bei_zseq );
938 #else
939                 bdb_entry_return( new->bei_e );
940 #endif
941         }
942         new->bei_e = e;
943         e->e_private = new;
944         new->bei_state = CACHE_ENTRY_NO_KIDS | CACHE_ENTRY_NO_GRANDKIDS;
945         eip->bei_state &= ~CACHE_ENTRY_NO_KIDS;
946         if (eip->bei_parent) {
947                 eip->bei_parent->bei_state &= ~CACHE_ENTRY_NO_GRANDKIDS;
948         }
949         bdb_cache_entryinfo_unlock( eip );
950
951         ++bdb->bi_cache.c_cursize;
952         ldap_pvt_thread_rdwr_wunlock( &bdb->bi_cache.c_rwlock );
953
954         /* set lru mutex */
955         ldap_pvt_thread_mutex_lock( &bdb->bi_cache.lru_head_mutex );
956
957         /* lru_head_mutex is unlocked for us */
958         bdb_cache_lru_add( bdb, new );
959
960         return rc;
961 }
962
963 int
964 bdb_cache_modify(
965         Entry *e,
966         Attribute *newAttrs,
967         DB_ENV *env,
968         u_int32_t locker,
969         DB_LOCK *lock )
970 {
971         EntryInfo *ei = BEI(e);
972         int rc;
973         /* Get write lock on data */
974         rc = bdb_cache_entry_db_relock( env, locker, ei, 1, 0, lock );
975
976         /* If we've done repeated mods on a cached entry, then e_attrs
977          * is no longer contiguous with the entry, and must be freed.
978          */
979         if ( ! rc ) {
980                 if ( (void *)e->e_attrs != (void *)(e+1) ) {
981                         attrs_free( e->e_attrs ); 
982                 }
983                 e->e_attrs = newAttrs;
984         }
985         return rc;
986 }
987
988 /*
989  * Change the rdn in the entryinfo. Also move to a new parent if needed.
990  */
991 int
992 bdb_cache_modrdn(
993         struct bdb_info *bdb,
994         Entry *e,
995         struct berval *nrdn,
996         Entry *new,
997         EntryInfo *ein,
998         u_int32_t locker,
999         DB_LOCK *lock )
1000 {
1001         EntryInfo *ei = BEI(e), *pei;
1002         int rc;
1003 #ifdef BDB_HIER
1004         struct berval rdn;
1005 #endif
1006
1007         /* Get write lock on data */
1008         rc =  bdb_cache_entry_db_relock( bdb->bi_dbenv, locker, ei, 1, 0, lock );
1009         if ( rc ) return rc;
1010
1011         /* If we've done repeated mods on a cached entry, then e_attrs
1012          * is no longer contiguous with the entry, and must be freed.
1013          */
1014         if ( (void *)e->e_attrs != (void *)(e+1) ) {
1015                 attrs_free( e->e_attrs );
1016         }
1017         e->e_attrs = new->e_attrs;
1018         if( e->e_nname.bv_val < e->e_bv.bv_val ||
1019                 e->e_nname.bv_val > e->e_bv.bv_val + e->e_bv.bv_len )
1020         {
1021                 ch_free(e->e_name.bv_val);
1022                 ch_free(e->e_nname.bv_val);
1023         }
1024         e->e_name = new->e_name;
1025         e->e_nname = new->e_nname;
1026
1027         /* Lock the parent's kids AVL tree */
1028         pei = ei->bei_parent;
1029         bdb_cache_entryinfo_lock( pei );
1030         avl_delete( &pei->bei_kids, (caddr_t) ei, bdb_rdn_cmp );
1031         free( ei->bei_nrdn.bv_val );
1032         ber_dupbv( &ei->bei_nrdn, nrdn );
1033
1034         if ( !pei->bei_kids )
1035                 pei->bei_state |= CACHE_ENTRY_NO_KIDS | CACHE_ENTRY_NO_GRANDKIDS;
1036
1037 #ifdef BDB_HIER
1038         free( ei->bei_rdn.bv_val );
1039
1040         rdn = e->e_name;
1041         if ( nrdn->bv_len != e->e_nname.bv_len ) {
1042                 char *ptr = ber_bvchr(&rdn, ',');
1043                 assert( ptr != NULL );
1044                 rdn.bv_len = ptr - rdn.bv_val;
1045         }
1046         ber_dupbv( &ei->bei_rdn, &rdn );
1047         pei->bei_ckids--;
1048         if ( pei->bei_dkids ) pei->bei_dkids--;
1049 #endif
1050
1051         if (!ein) {
1052                 ein = ei->bei_parent;
1053         } else {
1054                 ei->bei_parent = ein;
1055                 bdb_cache_entryinfo_unlock( pei );
1056                 bdb_cache_entryinfo_lock( ein );
1057         }
1058         /* parent now has kids */
1059         if ( ein->bei_state & CACHE_ENTRY_NO_KIDS )
1060                 ein->bei_state ^= CACHE_ENTRY_NO_KIDS;
1061 #ifdef BDB_HIER
1062         /* parent might now have grandkids */
1063         if ( ein->bei_state & CACHE_ENTRY_NO_GRANDKIDS &&
1064                 !(ei->bei_state & (CACHE_ENTRY_NO_KIDS)))
1065                 ein->bei_state ^= CACHE_ENTRY_NO_GRANDKIDS;
1066
1067         {
1068                 /* Record the generation number of this change */
1069                 ldap_pvt_thread_mutex_lock( &bdb->bi_modrdns_mutex );
1070                 bdb->bi_modrdns++;
1071                 ei->bei_modrdns = bdb->bi_modrdns;
1072                 ldap_pvt_thread_mutex_unlock( &bdb->bi_modrdns_mutex );
1073         }
1074         ein->bei_ckids++;
1075         if ( ein->bei_dkids ) ein->bei_dkids++;
1076 #endif
1077         avl_insert( &ein->bei_kids, ei, bdb_rdn_cmp, avl_dup_error );
1078         bdb_cache_entryinfo_unlock( ein );
1079         return rc;
1080 }
1081 /*
1082  * cache_delete - delete the entry e from the cache. 
1083  *
1084  * returns:     0       e was deleted ok
1085  *              1       e was not in the cache
1086  *              -1      something bad happened
1087  */
1088 int
1089 bdb_cache_delete(
1090     Cache       *cache,
1091     Entry               *e,
1092     DB_ENV      *env,
1093     u_int32_t   locker,
1094     DB_LOCK     *lock )
1095 {
1096         EntryInfo *ei = BEI(e);
1097         int     rc;
1098
1099         assert( e->e_private != NULL );
1100
1101         /* Set this early, warn off any queriers */
1102         ei->bei_state |= CACHE_ENTRY_DELETED;
1103
1104         /* Lock the entry's info */
1105         bdb_cache_entryinfo_lock( ei );
1106
1107         /* Get write lock on the data */
1108         rc = bdb_cache_entry_db_relock( env, locker, ei, 1, 0, lock );
1109         if ( rc ) {
1110                 /* couldn't lock, undo and give up */
1111                 ei->bei_state ^= CACHE_ENTRY_DELETED;
1112                 bdb_cache_entryinfo_unlock( ei );
1113                 return rc;
1114         }
1115
1116         Debug( LDAP_DEBUG_TRACE, "====> bdb_cache_delete( %ld )\n",
1117                 e->e_id, 0, 0 );
1118
1119         /* set lru mutex */
1120         ldap_pvt_thread_mutex_lock( &cache->lru_tail_mutex );
1121
1122         /* set cache write lock */
1123         ldap_pvt_thread_rdwr_wlock( &cache->c_rwlock );
1124
1125         rc = bdb_cache_delete_internal( cache, e->e_private, 1 );
1126
1127         /* free cache write lock */
1128         ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock );
1129
1130         /* free lru mutex */
1131         ldap_pvt_thread_mutex_unlock( &cache->lru_tail_mutex );
1132
1133         /* Leave entry info locked */
1134
1135         return( rc );
1136 }
1137
1138 void
1139 bdb_cache_delete_cleanup(
1140         Cache *cache,
1141         EntryInfo *ei )
1142 {
1143         if ( ei->bei_e ) {
1144                 ei->bei_e->e_private = NULL;
1145 #ifdef SLAP_ZONE_ALLOC
1146                 bdb_entry_return( ei->bei_bdb, ei->bei_e, ei->bei_zseq );
1147 #else
1148                 bdb_entry_return( ei->bei_e );
1149 #endif
1150                 ei->bei_e = NULL;
1151         }
1152
1153         free( ei->bei_nrdn.bv_val );
1154         ei->bei_nrdn.bv_val = NULL;
1155 #ifdef BDB_HIER
1156         free( ei->bei_rdn.bv_val );
1157         ei->bei_rdn.bv_val = NULL;
1158         ei->bei_modrdns = 0;
1159         ei->bei_ckids = 0;
1160         ei->bei_dkids = 0;
1161 #endif
1162         ei->bei_parent = NULL;
1163         ei->bei_kids = NULL;
1164         ei->bei_lruprev = NULL;
1165
1166         ldap_pvt_thread_rdwr_wlock( &cache->c_rwlock );
1167         ei->bei_lrunext = cache->c_eifree;
1168         cache->c_eifree = ei;
1169         ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock );
1170         bdb_cache_entryinfo_unlock( ei );
1171 }
1172
1173 static int
1174 bdb_cache_delete_internal(
1175     Cache       *cache,
1176     EntryInfo           *e,
1177     int         decr )
1178 {
1179         int rc = 0;     /* return code */
1180
1181         /* Lock the parent's kids tree */
1182         bdb_cache_entryinfo_lock( e->bei_parent );
1183
1184 #ifdef BDB_HIER
1185         e->bei_parent->bei_ckids--;
1186         if ( decr && e->bei_parent->bei_dkids ) e->bei_parent->bei_dkids--;
1187 #endif
1188         /* dn tree */
1189         if ( avl_delete( &e->bei_parent->bei_kids, (caddr_t) e, bdb_rdn_cmp )
1190                 == NULL )
1191         {
1192                 rc = -1;
1193         }
1194         if ( e->bei_parent->bei_kids )
1195                 cache->c_leaves--;
1196
1197         /* id tree */
1198         if ( avl_delete( &cache->c_idtree, (caddr_t) e, bdb_id_cmp ) == NULL ) {
1199                 rc = -1;
1200         }
1201
1202         if ( rc == 0 ){
1203                 cache->c_eiused--;
1204
1205                 /* lru */
1206                 LRU_DELETE( cache, e );
1207                 if ( e->bei_e ) cache->c_cursize--;
1208         }
1209
1210         bdb_cache_entryinfo_unlock( e->bei_parent );
1211
1212         return( rc );
1213 }
1214
1215 static void
1216 bdb_entryinfo_release( void *data )
1217 {
1218         EntryInfo *ei = (EntryInfo *)data;
1219         if ( ei->bei_kids ) {
1220                 avl_free( ei->bei_kids, NULL );
1221         }
1222         if ( ei->bei_e ) {
1223                 ei->bei_e->e_private = NULL;
1224 #ifdef SLAP_ZONE_ALLOC
1225                 bdb_entry_return( ei->bei_bdb, ei->bei_e, ei->bei_zseq );
1226 #else
1227                 bdb_entry_return( ei->bei_e );
1228 #endif
1229         }
1230         bdb_cache_entryinfo_destroy( ei );
1231 }
1232
1233 void
1234 bdb_cache_release_all( Cache *cache )
1235 {
1236         /* set cache write lock */
1237         ldap_pvt_thread_rdwr_wlock( &cache->c_rwlock );
1238         /* set lru mutex */
1239         ldap_pvt_thread_mutex_lock( &cache->lru_tail_mutex );
1240
1241         Debug( LDAP_DEBUG_TRACE, "====> bdb_cache_release_all\n", 0, 0, 0 );
1242
1243         avl_free( cache->c_dntree.bei_kids, NULL );
1244         avl_free( cache->c_idtree, bdb_entryinfo_release );
1245         for (;cache->c_eifree;cache->c_eifree = cache->c_lruhead) {
1246                 cache->c_lruhead = cache->c_eifree->bei_lrunext;
1247                 bdb_cache_entryinfo_destroy(cache->c_eifree);
1248         }
1249         cache->c_cursize = 0;
1250         cache->c_eiused = 0;
1251         cache->c_leaves = 0;
1252         cache->c_idtree = NULL;
1253         cache->c_lruhead = NULL;
1254         cache->c_lrutail = NULL;
1255         cache->c_dntree.bei_kids = NULL;
1256
1257         /* free lru mutex */
1258         ldap_pvt_thread_mutex_unlock( &cache->lru_tail_mutex );
1259         /* free cache write lock */
1260         ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock );
1261 }
1262
1263 #ifdef LDAP_DEBUG
1264 #ifdef SLAPD_UNUSED
1265 static void
1266 bdb_lru_print( Cache *cache )
1267 {
1268         EntryInfo       *e;
1269
1270         fprintf( stderr, "LRU queue (head to tail):\n" );
1271         for ( e = cache->c_lruhead; e != NULL; e = e->bei_lrunext ) {
1272                 fprintf( stderr, "\trdn \"%20s\" id %ld\n",
1273                         e->bei_nrdn.bv_val, e->bei_id );
1274         }
1275         fprintf( stderr, "LRU queue (tail to head):\n" );
1276         for ( e = cache->c_lrutail; e != NULL; e = e->bei_lruprev ) {
1277                 fprintf( stderr, "\trdn \"%20s\" id %ld\n",
1278                         e->bei_nrdn.bv_val, e->bei_id );
1279         }
1280 }
1281 #endif
1282 #endif
1283
1284 #ifdef BDB_REUSE_LOCKERS
1285 static void
1286 bdb_locker_id_free( void *key, void *data )
1287 {
1288         DB_ENV *env = key;
1289         u_int32_t lockid = (long)data;
1290         int rc;
1291
1292         rc = XLOCK_ID_FREE( env, lockid );
1293         if ( rc == EINVAL ) {
1294                 DB_LOCKREQ lr;
1295                 Debug( LDAP_DEBUG_ANY,
1296                         "bdb_locker_id_free: %lu err %s(%d)\n",
1297                         (unsigned long) lockid, db_strerror(rc), rc );
1298                 /* release all locks held by this locker. */
1299                 lr.op = DB_LOCK_PUT_ALL;
1300                 lr.obj = NULL;
1301                 env->lock_vec( env, lockid, 0, &lr, 1, NULL );
1302                 XLOCK_ID_FREE( env, lockid );
1303         }
1304 }
1305
1306 /* free up any keys used by the main thread */
1307 void
1308 bdb_locker_flush( DB_ENV *env )
1309 {
1310         void *data;
1311         void *ctx = ldap_pvt_thread_pool_context();
1312
1313         if ( !ldap_pvt_thread_pool_getkey( ctx, env, &data, NULL ) ) {
1314                 ldap_pvt_thread_pool_setkey( ctx, env, NULL, NULL );
1315                 bdb_locker_id_free( env, data );
1316         }
1317 }
1318
1319 int
1320 bdb_locker_id( Operation *op, DB_ENV *env, u_int32_t *locker )
1321 {
1322         int i, rc;
1323         u_int32_t lockid;
1324         void *data;
1325         void *ctx;
1326
1327         if ( !env || !locker ) return -1;
1328
1329         /* If no op was provided, try to find the ctx anyway... */
1330         if ( op ) {
1331                 ctx = op->o_threadctx;
1332         } else {
1333                 ctx = ldap_pvt_thread_pool_context();
1334         }
1335
1336         /* Shouldn't happen unless we're single-threaded */
1337         if ( !ctx ) {
1338                 *locker = 0;
1339                 return 0;
1340         }
1341
1342         if ( ldap_pvt_thread_pool_getkey( ctx, env, &data, NULL ) ) {
1343                 for ( i=0, rc=1; rc != 0 && i<4; i++ ) {
1344                         rc = XLOCK_ID( env, &lockid );
1345                         if (rc) ldap_pvt_thread_yield();
1346                 }
1347                 if ( rc != 0) {
1348                         return rc;
1349                 }
1350                 data = (void *)((long)lockid);
1351                 if ( ( rc = ldap_pvt_thread_pool_setkey( ctx, env,
1352                         data, bdb_locker_id_free ) ) ) {
1353                         XLOCK_ID_FREE( env, lockid );
1354                         Debug( LDAP_DEBUG_ANY, "bdb_locker_id: err %s(%d)\n",
1355                                 db_strerror(rc), rc, 0 );
1356
1357                         return rc;
1358                 }
1359         } else {
1360                 lockid = (long)data;
1361         }
1362         *locker = lockid;
1363         return 0;
1364 }
1365 #endif /* BDB_REUSE_LOCKERS */
1366
1367 void
1368 bdb_cache_delete_entry(
1369         struct bdb_info *bdb,
1370         EntryInfo *ei,
1371         u_int32_t locker,
1372         DB_LOCK *lock )
1373 {
1374         ldap_pvt_thread_rdwr_wlock( &bdb->bi_cache.c_rwlock );
1375         if ( bdb_cache_entry_db_lock( bdb->bi_dbenv, bdb->bi_cache.c_locker, ei, 1, 1, lock ) == 0 )
1376         {
1377                 if ( ei->bei_e && !(ei->bei_state & CACHE_ENTRY_NOT_LINKED )) {
1378                         LRU_DELETE( &bdb->bi_cache, ei );
1379                         ei->bei_e->e_private = NULL;
1380 #ifdef SLAP_ZONE_ALLOC
1381                         bdb_entry_return( bdb, ei->bei_e, ei->bei_zseq );
1382 #else
1383                         bdb_entry_return( ei->bei_e );
1384 #endif
1385                         ei->bei_e = NULL;
1386                         --bdb->bi_cache.c_cursize;
1387                 }
1388                 bdb_cache_entry_db_unlock( bdb->bi_dbenv, lock );
1389         }
1390         ldap_pvt_thread_rdwr_wunlock( &bdb->bi_cache.c_rwlock );
1391 }