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