]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb/cache.c
d18b7e57964374ae6f019a1bf7d0b32ad2a5ed94
[openldap] / servers / slapd / back-bdb / cache.c
1 /* cache.c - routines to maintain an in-core cache of entries */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 1998-2003 The OpenLDAP Foundation, All Rights Reserved.
5  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
6  */
7
8 #include "portable.h"
9
10 #include <stdio.h>
11
12 #include <ac/errno.h>
13 #include <ac/string.h>
14 #include <ac/socket.h>
15
16 #include "slap.h"
17
18 #include "back-bdb.h"
19
20 static int      bdb_cache_delete_entry_internal(Cache *cache, EntryInfo *e);
21 #ifdef LDAP_DEBUG
22 static void     bdb_lru_print(Cache *cache);
23 #endif
24
25 static EntryInfo *
26 bdb_cache_entryinfo_new( )
27 {
28         EntryInfo *ei;
29
30         ei = ch_calloc(1, sizeof(struct bdb_entry_info));
31         ldap_pvt_thread_mutex_init( &ei->bei_kids_mutex );
32
33         return ei;
34 }
35
36 /* Atomically release and reacquire a lock */
37 int
38 bdb_cache_entry_db_relock(
39         DB_ENV *env,
40         u_int32_t locker,
41         EntryInfo *ei,
42         int rw,
43         int tryOnly,
44         DB_LOCK *lock )
45 {
46 #ifdef NO_THREADS
47         return 0;
48 #else
49         int     rc;
50         DBT     lockobj;
51         DB_LOCKREQ list[2];
52
53         if ( !lock ) return 0;
54
55         lockobj.data = ei;
56         lockobj.size = sizeof(ei->bei_parent) + sizeof(ei->bei_id);
57
58         list[0].op = DB_LOCK_PUT;
59         list[0].lock = *lock;
60         list[1].op = DB_LOCK_GET;
61         list[1].lock = *lock;
62         list[1].mode = rw ? DB_LOCK_WRITE : DB_LOCK_READ;
63         list[1].obj = &lockobj;
64         rc = env->lock_vec(env, locker, tryOnly ? DB_LOCK_NOWAIT : 0,
65                 list, 2, NULL );
66
67         if (rc) {
68 #ifdef NEW_LOGGING
69                 LDAP_LOG( CACHE, DETAIL1, 
70                         "bdb_cache_entry_db_relock: entry %d, rw %d, rc %d\n",
71                         ei->bei_id, rw, rc );
72 #else
73                 Debug( LDAP_DEBUG_TRACE,
74                         "bdb_cache_entry_db_relock: entry %d, rw %d, rc %d\n",
75                         ei->bei_id, rw, rc );
76 #endif
77         } else {
78                 *lock = list[1].lock;
79         }
80         return rc;
81 #endif
82 }
83 int
84 bdb_cache_entry_db_lock
85 ( DB_ENV *env, u_int32_t locker, EntryInfo *ei, int rw, int tryOnly, DB_LOCK *lock )
86 {
87 #ifdef NO_THREADS
88         return 0;
89 #else
90         int       rc;
91         DBT       lockobj;
92         int       db_rw;
93
94         if ( !lock ) return 0;
95
96         if (rw)
97                 db_rw = DB_LOCK_WRITE;
98         else
99                 db_rw = DB_LOCK_READ;
100
101         lockobj.data = ei;
102         lockobj.size = sizeof(ei->bei_parent) + sizeof(ei->bei_id);
103
104         rc = LOCK_GET(env, locker, tryOnly ? DB_LOCK_NOWAIT : 0,
105                                         &lockobj, db_rw, lock);
106         if (rc) {
107 #ifdef NEW_LOGGING
108                 LDAP_LOG( CACHE, DETAIL1, 
109                         "bdb_cache_entry_db_lock: entry %d, rw %d, rc %d\n",
110                         ei->bei_id, rw, rc );
111 #else
112                 Debug( LDAP_DEBUG_TRACE,
113                         "bdb_cache_entry_db_lock: entry %d, rw %d, rc %d\n",
114                         ei->bei_id, rw, rc );
115 #endif
116         }
117         return rc;
118 #endif /* NO_THREADS */
119 }
120
121 int
122 bdb_cache_entry_db_unlock
123 ( DB_ENV *env, DB_LOCK *lock )
124 {
125 #ifdef NO_THREADS
126         return 0;
127 #else
128         int rc;
129
130         rc = LOCK_PUT ( env, lock );
131         return rc;
132 #endif
133 }
134
135 static int
136 bdb_cache_entryinfo_destroy( EntryInfo *e )
137 {
138         ldap_pvt_thread_mutex_destroy( &e->bei_kids_mutex );
139         free( e->bei_nrdn.bv_val );
140 #ifdef BDB_HIER
141         free( e->bei_rdn.bv_val );
142 #endif
143         free( e );
144         return 0;
145 }
146
147 #define LRU_DELETE( cache, ei ) do { \
148         if ( (ei)->bei_lruprev != NULL ) { \
149                 (ei)->bei_lruprev->bei_lrunext = (ei)->bei_lrunext; \
150         } else { \
151                 (cache)->c_lruhead = (ei)->bei_lrunext; \
152         } \
153         if ( (ei)->bei_lrunext != NULL ) { \
154                 (ei)->bei_lrunext->bei_lruprev = (ei)->bei_lruprev; \
155         } else { \
156                 (cache)->c_lrutail = (ei)->bei_lruprev; \
157         } \
158 } while(0)
159
160 #define LRU_ADD( cache, ei ) do { \
161         (ei)->bei_lrunext = (cache)->c_lruhead; \
162         if ( (ei)->bei_lrunext != NULL ) { \
163                 (ei)->bei_lrunext->bei_lruprev = (ei); \
164         } \
165         (cache)->c_lruhead = (ei); \
166         (ei)->bei_lruprev = NULL; \
167         if ( (cache)->c_lrutail == NULL ) { \
168                 (cache)->c_lrutail = (ei); \
169         } \
170 } while(0)
171
172 /* Do a lexical sort on normalized RDNs */
173 static int
174 bdb_rdn_cmp( const void *v_e1, const void *v_e2 )
175 {
176         const EntryInfo *e1 = v_e1, *e2 = v_e2;
177         int rc = strncmp( e1->bei_nrdn.bv_val, e2->bei_nrdn.bv_val, e1->bei_nrdn.bv_len );
178         if (rc == 0) rc = e1->bei_nrdn.bv_len - e2->bei_nrdn.bv_len;
179         return rc;
180 }
181
182 static int
183 bdb_id_cmp( const void *v_e1, const void *v_e2 )
184 {
185         const EntryInfo *e1 = v_e1, *e2 = v_e2;
186         return e1->bei_id - e2->bei_id;
187 }
188
189 /* Create an entryinfo in the cache. Caller must release the locks later.
190  */
191 int
192 bdb_entryinfo_add_internal(
193         struct bdb_info *bdb,
194         EntryInfo *ei,
195         EntryInfo **res,
196         u_int32_t locker
197 )
198 {
199         Cache *cache = &bdb->bi_cache;
200         DB_ENV *env = bdb->bi_dbenv;
201         EntryInfo *ei2 = NULL;
202         int incr = 1;
203         int addkid = 1;
204         int rc;
205         DB_LOCK lock;
206
207         *res = NULL;
208
209         ldap_pvt_thread_rdwr_wlock( &bdb->bi_cache.c_rwlock );
210         bdb_cache_entryinfo_lock( ei->bei_parent );
211
212         /* if parent was previously considered a leaf node,
213          * it was on the LRU list. Now it's going to have
214          * kids, take it off the LRU list.
215          */
216         ldap_pvt_thread_mutex_lock( &cache->lru_mutex );
217         if ( ei->bei_parent->bei_id && !ei->bei_parent->bei_kids ) {
218                 LRU_DELETE( cache, ei->bei_parent );
219                 incr = 0;
220         }
221
222         cache->c_cursize += incr;
223
224         /* See if we're above the cache size limit */
225         if ( cache->c_cursize > cache->c_maxsize ) {
226                 EntryInfo *elru, *elprev;
227                 int i = 0;
228
229                 /* Look for an unused entry to remove */
230                 for (elru = cache->c_lrutail; elru; elru = elprev, i++ ) {
231                         elprev = elru->bei_lruprev;
232
233                         /* Too many probes, not enough idle, give up */
234                         if (i > 10) break;
235
236                         /* If we can successfully writelock it, then
237                          * the object is idle.
238                          */
239                         if ( bdb_cache_entry_db_lock( env, locker, elru, 1, 1,
240                                 &lock ) == 0 ) {
241                                 /* Need to lock parent to delete child */
242                                 if ( ldap_pvt_thread_mutex_trylock(
243                                         &elru->bei_parent->bei_kids_mutex )) {
244                                         bdb_cache_entry_db_unlock( env, &lock );
245                                         continue;
246                                 }
247                                 bdb_cache_delete_entry_internal( cache, elru );
248                                 bdb_cache_entryinfo_unlock( elru->bei_parent );
249                                 elru->bei_e->e_private = NULL;
250                                 bdb_entry_return( elru->bei_e );
251                                 bdb_cache_entry_db_unlock( env, &lock );
252                                 if (ei2) {
253                                         bdb_cache_entryinfo_destroy( elru );
254                                 } else {
255                                         /* re-use this one */
256                                         ch_free(elru->bei_nrdn.bv_val);
257                                         elru->bei_nrdn.bv_val = NULL;
258                                         elru->bei_e = NULL;
259                                         elru->bei_kids = NULL;
260                                         elru->bei_lrunext = NULL;
261                                         elru->bei_lruprev = NULL;
262                                         elru->bei_state = 0;
263                                         ei2 = elru;
264                                 }
265                                 if (cache->c_cursize < cache->c_maxsize)
266                                         break;
267                         }
268                 }
269         }
270         if (!ei2) {
271                 ei2 = bdb_cache_entryinfo_new();
272         }
273         ei2->bei_id = ei->bei_id;
274         ei2->bei_parent = ei->bei_parent;
275 #ifdef BDB_HIER
276         ei2->bei_rdn = ei->bei_rdn;
277 #endif
278
279         /* Add to cache ID tree */
280         if (avl_insert( &cache->c_idtree, ei2, bdb_id_cmp, avl_dup_error )) {
281                 EntryInfo *eix;
282                 eix = avl_find( cache->c_idtree, ei2, bdb_id_cmp );
283                 bdb_cache_entryinfo_destroy( ei2 );
284                 ei2 = eix;
285                 addkid = 0;
286                 cache->c_cursize -= incr;
287 #ifdef BDB_HIER
288                 if ( ei->bei_rdn.bv_val )
289                         ber_memfree_x( ei->bei_rdn.bv_val, NULL );
290 #endif
291         } else {
292                 LRU_ADD( cache, ei2 );
293                 ber_dupbv( &ei2->bei_nrdn, &ei->bei_nrdn );
294         }
295
296         if ( addkid ) {
297                 avl_insert( &ei->bei_parent->bei_kids, ei2, bdb_rdn_cmp,
298                         avl_dup_error );
299         }
300
301         ldap_pvt_thread_mutex_unlock( &cache->lru_mutex );
302
303         *res = ei2;
304         return 0;
305 }
306
307 /* Find the EntryInfo for the requested DN. If the DN cannot be found, return
308  * the info for its closest ancestor. *res should be NULL to process a
309  * complete DN starting from the tree root. Otherwise *res must be the
310  * immediate parent of the requested DN, and only the RDN will be searched.
311  * The EntryInfo is locked upon return and must be unlocked by the caller.
312  */
313 int
314 bdb_cache_find_entry_ndn2id(
315         Backend         *be,
316         DB_TXN          *txn,
317         struct berval   *ndn,
318         EntryInfo       **res,
319         u_int32_t       locker,
320         void            *ctx
321 )
322 {
323         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
324         EntryInfo       ei, *eip, *ei2;
325         int rc = 0;
326         char *ptr;
327
328         /* this function is always called with normalized DN */
329         if ( *res ) {
330                 /* we're doing a onelevel search for an RDN */
331                 ei.bei_nrdn.bv_val = ndn->bv_val;
332                 ei.bei_nrdn.bv_len = dn_rdnlen( be, ndn );
333                 eip = *res;
334         } else {
335                 /* we're searching a full DN from the root */
336                 ptr = ndn->bv_val + ndn->bv_len - be->be_nsuffix[0].bv_len;
337                 ei.bei_nrdn.bv_val = ptr;
338                 ei.bei_nrdn.bv_len = be->be_nsuffix[0].bv_len;
339                 eip = &bdb->bi_cache.c_dntree;
340         }
341         
342         for ( bdb_cache_entryinfo_lock( eip ); eip; ) {
343                 ei.bei_parent = eip;
344                 ei2 = (EntryInfo *)avl_find( eip->bei_kids, &ei, bdb_rdn_cmp );
345                 if ( !ei2 ) {
346                         int len = ei.bei_nrdn.bv_len;
347                                 
348                         ei.bei_nrdn.bv_len = ndn->bv_len - (ei.bei_nrdn.bv_val - ndn->bv_val);
349                         bdb_cache_entryinfo_unlock( eip );
350
351                         rc = bdb_dn2id( be, txn, &ei.bei_nrdn, &ei, ctx );
352                         if (rc) {
353                                 bdb_cache_entryinfo_lock( eip );
354                                 *res = eip;
355                                 return rc;
356                         }
357
358                         /* DN exists but needs to be added to cache */
359                         ei.bei_nrdn.bv_len = len;
360                         rc = bdb_entryinfo_add_internal( bdb, &ei, &ei2,
361                                 locker );
362                         /* add_internal left eip and c_rwlock locked */
363                         ldap_pvt_thread_rdwr_wunlock( &bdb->bi_cache.c_rwlock );
364                         if ( rc ) {
365                                 *res = eip;
366                                 return rc;
367                         }
368                 } else if ( ei2->bei_state & CACHE_ENTRY_DELETED ) {
369                         /* In the midst of deleting? Give it a chance to
370                          * complete.
371                          */
372                         bdb_cache_entryinfo_unlock( eip );
373                         ldap_pvt_thread_yield();
374                         bdb_cache_entryinfo_lock( eip );
375                         *res = eip;
376                         return DB_NOTFOUND;
377                 }
378                 bdb_cache_entryinfo_unlock( eip );
379                 bdb_cache_entryinfo_lock( ei2 );
380
381                 eip = ei2;
382
383                 /* Advance to next lower RDN */
384                 for (ptr = ei.bei_nrdn.bv_val - 2; ptr > ndn->bv_val
385                         && !DN_SEPARATOR(*ptr); ptr--);
386                 if ( ptr >= ndn->bv_val ) {
387                         if (DN_SEPARATOR(*ptr)) ptr++;
388                         ei.bei_nrdn.bv_len = ei.bei_nrdn.bv_val - ptr - 1;
389                         ei.bei_nrdn.bv_val = ptr;
390                 }
391                 if ( ptr < ndn->bv_val ) {
392                         *res = eip;
393                         break;
394                 }
395         }
396
397         return rc;
398 }
399
400 #ifdef BDB_HIER
401 /* Walk up the tree from a child node, looking for an ID that's already
402  * been linked into the cache.
403  */
404 int
405 bdb_cache_find_parent(
406         Backend *be,
407         DB_TXN *txn,
408         ID id,
409         EntryInfo **res,
410         void *ctx
411 )
412 {
413         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
414         EntryInfo ei, eip, *ei2 = NULL, *ein = NULL, *eir = NULL;
415         ID parent;
416         int rc;
417
418         ei.bei_id = id;
419         ei.bei_kids = NULL;
420
421         for (;;) {
422                 rc = bdb_dn2id_parent( be, txn, &ei, &eip.bei_id, ctx );
423                 if ( rc ) break;
424
425                 /* Save the previous node, if any */
426                 ei2 = ein;
427
428                 /* Create a new node for the current ID */
429                 ein = bdb_cache_entryinfo_new();
430                 ein->bei_id = ei.bei_id;
431                 ein->bei_kids = ei.bei_kids;
432                 ein->bei_nrdn = ei.bei_nrdn;
433 #ifdef BDB_HIER
434                 ein->bei_rdn = ei.bei_rdn;
435 #endif
436                 
437                 /* This node is not fully connected yet */
438                 ein->bei_state = CACHE_ENTRY_NOT_LINKED;
439
440                 /* If this is the first time, save this node
441                  * to be returned later.
442                  */
443                 if ( eir == NULL ) eir = ein;
444
445                 /* Insert this node into the ID tree */
446                 ldap_pvt_thread_rdwr_rlock( &bdb->bi_cache.c_rwlock );
447                 if ( avl_insert( &bdb->bi_cache.c_idtree, (caddr_t)ein,
448                         bdb_id_cmp, avl_dup_error ) ) {
449
450                         /* Hm, can this really happen? */
451                         bdb_cache_entryinfo_destroy( ein );
452                         ein = (EntryInfo *)avl_find( bdb->bi_cache.c_idtree,
453                                 (caddr_t) &ei, bdb_id_cmp );
454                         bdb_cache_entryinfo_lock( ein );
455                         avl_insert( &ein->bei_kids, (caddr_t)ei2, bdb_rdn_cmp,
456                                 avl_dup_error );
457                         bdb_cache_entryinfo_unlock( ein );
458                 }
459
460                 /* If there was a previous node, link it to this one */
461                 if ( ei2 ) ei2->bei_parent = ein;
462
463                 if ( eip.bei_id ) {
464                         ei2 = (EntryInfo *) avl_find( bdb->bi_cache.c_idtree,
465                                         (caddr_t) &eip, bdb_id_cmp );
466                 } else {
467                         ei2 = &bdb->bi_cache.c_dntree;
468                 }
469
470                 if ( ei2 ) {
471                         ein->bei_parent = ei2;
472                         bdb_cache_entryinfo_lock( ei2 );
473                         avl_insert( &ei2->bei_kids, (caddr_t)ein, bdb_rdn_cmp,
474                                 avl_dup_error);
475                         bdb_cache_entryinfo_unlock( ei2 );
476                         *res = eir;
477                         bdb_cache_entryinfo_lock( eir );
478                 }
479                 ldap_pvt_thread_rdwr_runlock( &bdb->bi_cache.c_rwlock );
480                 if ( ei2 ) {
481                         /* Found a link. Reset all the state info */
482                         for (ein = eir; ein != ei2; ein=ein->bei_parent)
483                                 ein->bei_state &= ~CACHE_ENTRY_NOT_LINKED;
484                         break;
485                 }
486                 ei.bei_kids = NULL;
487                 ei.bei_id = eip.bei_id;
488                 avl_insert( &ei.bei_kids, (caddr_t)ein, bdb_rdn_cmp,
489                         avl_dup_error );
490         }
491         return rc;
492 }
493 #endif
494
495 /*
496  * cache_find_entry_id - find an entry in the cache, given id.
497  * The entry is locked for Read upon return. Call with islocked TRUE if
498  * the supplied *eip was already locked.
499  */
500
501 int
502 bdb_cache_find_entry_id(
503         Backend *be,
504         DB_TXN  *tid,
505         ID                              id,
506         EntryInfo       **eip,
507         int             islocked,
508         u_int32_t       locker,
509         DB_LOCK         *lock,
510         void            *ctx
511 )
512 {
513         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
514         Entry   *ep = NULL;
515         int     rc = 0;
516         EntryInfo ei;
517
518         ei.bei_id = id;
519
520         /* If we weren't given any info, see if we have it already cached */
521         if ( !*eip ) {
522                 ldap_pvt_thread_rdwr_rlock( &bdb->bi_cache.c_rwlock );
523                 *eip = (EntryInfo *) avl_find( bdb->bi_cache.c_idtree,
524                                         (caddr_t) &ei, bdb_id_cmp );
525                 if ( *eip ) {
526                         bdb_cache_entryinfo_lock( *eip );
527                         islocked = 1;
528                 }
529                 ldap_pvt_thread_rdwr_runlock( &bdb->bi_cache.c_rwlock );
530         }
531
532         /* See if the ID exists in the database; add it to the cache if so */
533         if ( !*eip ) {
534 #ifndef BDB_HIER
535                 rc = bdb_id2entry( be, tid, id, &ep );
536                 if ( rc == 0 ) {
537                         rc = bdb_cache_find_entry_ndn2id( be, tid,
538                                 &ep->e_nname, eip, locker, ctx );
539                         if ( *eip )
540                                 islocked = 1;
541                         if ( rc ) {
542                                 bdb_entry_return( ep );
543                                 ep = NULL;
544                         }
545                 }
546 #else
547                 rc = bdb_cache_find_parent(be, tid, id, eip, ctx );
548                 if ( rc == 0 && *eip )
549                         islocked = 1;
550 #endif
551         }
552
553         /* Ok, we found the info, do we have the entry? */
554         if ( *eip && rc == 0 ) {
555                 if ( (*eip)->bei_state & CACHE_ENTRY_DELETED ) {
556                         rc = DB_NOTFOUND;
557                 } else if (!(*eip)->bei_e ) {
558                         if (!ep) {
559                                 rc = bdb_id2entry( be, tid, id, &ep );
560                         }
561                         if ( rc == 0 ) {
562                                 bdb_cache_entry_db_lock( bdb->bi_dbenv, locker,
563                                         *eip, 1, 0, lock );
564                                 ep->e_private = *eip;
565 #ifdef BDB_HIER
566                                 hdb_fix_dn( ep );
567 #endif
568                                 (*eip)->bei_e = ep;
569                                 bdb_cache_entry_db_relock( bdb->bi_dbenv, locker,
570                                         *eip, 0, 0, lock );
571                         }
572                 } else {
573                         bdb_cache_entry_db_lock( bdb->bi_dbenv, locker,
574                                         *eip, 0, 0, lock );
575                 }
576         }
577         if ( rc == 0 && (*eip)->bei_kids == NULL ) {
578                 /* set lru mutex */
579                 ldap_pvt_thread_mutex_lock( &bdb->bi_cache.lru_mutex );
580                 LRU_DELETE( &bdb->bi_cache, *eip );
581                 LRU_ADD( &bdb->bi_cache, *eip );
582                 ldap_pvt_thread_mutex_unlock( &bdb->bi_cache.lru_mutex );
583         }
584
585         if ( islocked ) {
586                 bdb_cache_entryinfo_unlock( *eip );
587         }
588         return rc;
589 }
590
591 int
592 bdb_cache_children(
593         Operation *op,
594         DB_TXN *txn,
595         Entry *e
596 )
597 {
598         int rc;
599
600         if ( BEI(e)->bei_kids ) {
601                 return 0;
602         }
603         if ( BEI(e)->bei_state & CACHE_ENTRY_NO_KIDS ) {
604                 return DB_NOTFOUND;
605         }
606         rc = bdb_dn2id_children( op, txn, e );
607         if ( rc == DB_NOTFOUND ) {
608                 BEI(e)->bei_state |= CACHE_ENTRY_NO_KIDS;
609         }
610         return rc;
611 }
612
613 /* Update the cache after a successful database Add. */
614 int
615 bdb_cache_add(
616         struct bdb_info *bdb,
617         EntryInfo *eip,
618         Entry *e,
619         struct berval *nrdn,
620         u_int32_t locker
621 )
622 {
623         EntryInfo *new, ei;
624         struct berval rdn = e->e_name;
625         int rc;
626
627         ei.bei_id = e->e_id;
628         ei.bei_parent = eip;
629         ei.bei_nrdn = *nrdn;
630 #ifdef BDB_HIER
631         if ( nrdn->bv_len != e->e_nname.bv_len ) {
632                 char *ptr = strchr( rdn.bv_val, ',' );
633                 rdn.bv_len = ptr - rdn.bv_val;
634         }
635         ber_dupbv( &ei.bei_rdn, &rdn );
636 #endif
637         rc = bdb_entryinfo_add_internal( bdb, &ei, &new, locker );
638         new->bei_e = e;
639         e->e_private = new;
640         new->bei_state = CACHE_ENTRY_NO_KIDS;
641         eip->bei_state &= ~CACHE_ENTRY_NO_KIDS;
642         bdb_cache_entryinfo_unlock( eip );
643         ldap_pvt_thread_rdwr_wunlock( &bdb->bi_cache.c_rwlock );
644         return rc;
645 }
646
647 int
648 bdb_cache_modify(
649         Entry *e,
650         Attribute *newAttrs,
651         DB_ENV *env,
652         u_int32_t locker,
653         DB_LOCK *lock
654 )
655 {
656         EntryInfo *ei = BEI(e);
657         
658         /* Get write lock on data */
659         bdb_cache_entry_db_relock( env, locker, ei, 1, 0, lock );
660
661         /* If we've done repeated mods on a cached entry, then e_attrs
662          * is no longer contiguous with the entry, and must be freed.
663          */
664         if ( (void *)e->e_attrs != (void *)(e+1) ) {
665                 attrs_free( e->e_attrs );
666         }
667         e->e_attrs = newAttrs;
668
669         return 0;
670 }
671
672 /*
673  * Change the rdn in the entryinfo. Also move to a new parent if needed.
674  */
675 int
676 bdb_cache_modrdn(
677         Entry *e,
678         struct berval *nrdn,
679         Entry *new,
680         EntryInfo *ein,
681         DB_ENV *env,
682         u_int32_t locker,
683         DB_LOCK *lock
684 )
685 {
686         EntryInfo *ei = BEI(e), *pei;
687         struct berval rdn;
688         int rc = 0;
689
690         /* Get write lock on data */
691         bdb_cache_entry_db_relock( env, locker, ei, 1, 0, lock );
692
693         /* If we've done repeated mods on a cached entry, then e_attrs
694          * is no longer contiguous with the entry, and must be freed.
695          */
696         if ( (void *)e->e_attrs != (void *)(e+1) ) {
697                 attrs_free( e->e_attrs );
698         }
699         e->e_attrs = new->e_attrs;
700 #ifdef BDB_HIER
701         ch_free(e->e_name.bv_val);
702 #else
703         if( e->e_nname.bv_val < e->e_bv.bv_val || e->e_nname.bv_val >
704                 e->e_bv.bv_val + e->e_bv.bv_len ) {
705                 ch_free(e->e_name.bv_val);
706                 ch_free(e->e_nname.bv_val);
707         }
708 #endif
709         e->e_name = new->e_name;
710         e->e_nname = new->e_nname;
711
712         /* Lock the parent's kids AVL tree */
713         pei = ei->bei_parent;
714         bdb_cache_entryinfo_lock( pei );
715         avl_delete( &pei->bei_kids, (caddr_t) ei, bdb_rdn_cmp );
716         free( ei->bei_nrdn.bv_val );
717         ber_dupbv( &ei->bei_nrdn, nrdn );
718 #ifdef BDB_HIER
719         free( ei->bei_rdn.bv_val );
720
721         rdn = e->e_name;
722         if ( nrdn->bv_len != e->e_nname.bv_len ) {
723                 char *ptr = strchr(rdn.bv_val, ',');
724                 rdn.bv_len = ptr - rdn.bv_val;
725         }
726         ber_dupbv( &ei->bei_rdn, &rdn );
727 #endif
728
729         if (!ein) {
730                 ein = ei->bei_parent;
731         } else {
732                 ei->bei_parent = ein;
733                 bdb_cache_entryinfo_unlock( pei );
734                 bdb_cache_entryinfo_lock( ein );
735         }
736         avl_insert( &ein->bei_kids, ei, bdb_rdn_cmp, avl_dup_error );
737         bdb_cache_entryinfo_unlock( ein );
738         return rc;
739 }
740 /*
741  * cache_delete_entry - delete the entry e from the cache. 
742  *
743  * returns:     0       e was deleted ok
744  *              1       e was not in the cache
745  *              -1      something bad happened
746  */
747 int
748 bdb_cache_delete_entry(
749     Cache       *cache,
750     Entry               *e,
751     DB_ENV      *env,
752     u_int32_t   locker,
753     DB_LOCK     *lock
754 )
755 {
756         EntryInfo *ei = BEI(e);
757         int     rc;
758
759         assert( e->e_private );
760
761         /* Set this early, warn off any queriers */
762         ei->bei_state |= CACHE_ENTRY_DELETED;
763
764         /* Get write lock on the data */
765         bdb_cache_entry_db_relock( env, locker, ei, 1, 0, lock );
766
767         /* set cache write lock */
768         ldap_pvt_thread_rdwr_wlock( &cache->c_rwlock );
769
770         /* Lock the parent's kids tree */
771         bdb_cache_entryinfo_lock( ei->bei_parent );
772
773 #ifdef NEW_LOGGING
774         LDAP_LOG( CACHE, ENTRY, 
775                 "bdb_cache_delete_entry: delete %ld.\n", e->e_id, 0, 0 );
776 #else
777         Debug( LDAP_DEBUG_TRACE, "====> bdb_cache_delete_entry( %ld )\n",
778                 e->e_id, 0, 0 );
779 #endif
780
781         /* set lru mutex */
782         ldap_pvt_thread_mutex_lock( &cache->lru_mutex );
783         rc = bdb_cache_delete_entry_internal( cache, e->e_private );
784         /* free lru mutex */
785         ldap_pvt_thread_mutex_unlock( &cache->lru_mutex );
786
787         /* free cache write lock */
788         ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock );
789         bdb_cache_entryinfo_unlock( ei->bei_parent );
790         bdb_cache_entryinfo_destroy( ei );
791         e->e_private = NULL;
792         return( rc );
793 }
794
795 static int
796 bdb_cache_delete_entry_internal(
797     Cache       *cache,
798     EntryInfo           *e
799 )
800 {
801         int rc = 0;     /* return code */
802
803         /* dn tree */
804         if ( avl_delete( &e->bei_parent->bei_kids, (caddr_t) e, bdb_rdn_cmp ) == NULL )
805         {
806                 rc = -1;
807         }
808
809         /* If parent has no more kids, put in on LRU list */
810         if ( e->bei_parent->bei_kids == NULL ) {
811                 LRU_ADD( cache, e->bei_parent );
812                 cache->c_cursize++;
813         }
814
815         /* id tree */
816         if ( avl_delete( &cache->c_idtree, (caddr_t) e, bdb_id_cmp ) == NULL )
817         {
818                 rc = -1;
819         }
820
821         if (rc != 0) {
822                 return rc;
823         }
824
825         /* lru */
826         LRU_DELETE( cache, e );
827         cache->c_cursize--;
828
829         /*
830          * flag entry to be freed later by a call to cache_return_entry()
831          */
832         e->bei_state |= CACHE_ENTRY_DELETED;
833
834         return( 0 );
835 }
836
837 static void
838 bdb_entryinfo_release( void *data )
839 {
840         EntryInfo *ei = (EntryInfo *)data;
841         if ( ei->bei_kids ) {
842                 avl_free( ei->bei_kids, NULL );
843         }
844         if ( ei->bei_e ) {
845                 ei->bei_e->e_private = NULL;
846                 bdb_entry_return( ei->bei_e );
847         }
848         bdb_cache_entryinfo_destroy( ei );
849 }
850
851 void
852 bdb_cache_release_all( Cache *cache )
853 {
854         /* set cache write lock */
855         ldap_pvt_thread_rdwr_wlock( &cache->c_rwlock );
856         /* set lru mutex */
857         ldap_pvt_thread_mutex_lock( &cache->lru_mutex );
858
859 #ifdef NEW_LOGGING
860         LDAP_LOG( CACHE, ENTRY, "bdb_cache_release_all: enter\n", 0, 0, 0 );
861 #else
862         Debug( LDAP_DEBUG_TRACE, "====> bdb_cache_release_all\n", 0, 0, 0 );
863 #endif
864
865         avl_free( cache->c_dntree.bei_kids, NULL );
866         avl_free( cache->c_idtree, bdb_entryinfo_release );
867         cache->c_lruhead = NULL;
868         cache->c_lrutail = NULL;
869
870         /* free lru mutex */
871         ldap_pvt_thread_mutex_unlock( &cache->lru_mutex );
872         /* free cache write lock */
873         ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock );
874 }
875
876 #ifdef LDAP_DEBUG
877 static void
878 bdb_lru_print( Cache *cache )
879 {
880         EntryInfo       *e;
881
882         fprintf( stderr, "LRU queue (head to tail):\n" );
883         for ( e = cache->c_lruhead; e != NULL; e = e->bei_lrunext ) {
884                 fprintf( stderr, "\trdn \"%20s\" id %ld\n",
885                         e->bei_nrdn.bv_val, e->bei_id );
886         }
887         fprintf( stderr, "LRU queue (tail to head):\n" );
888         for ( e = cache->c_lrutail; e != NULL; e = e->bei_lruprev ) {
889                 fprintf( stderr, "\trdn \"%20s\" id %ld\n",
890                         e->bei_nrdn.bv_val, e->bei_id );
891         }
892 }
893 #endif
894
895 #ifdef BDB_REUSE_LOCKERS
896 void
897 bdb_locker_id_free( void *key, void *data )
898 {
899         DB_ENV *env = key;
900         int lockid = (int) data;
901
902         XLOCK_ID_FREE( env, lockid );
903 }
904
905 int
906 bdb_locker_id( Operation *op, DB_ENV *env, int *locker )
907 {
908         int i, rc, lockid;
909         void *data;
910         void *ctx;
911
912         if ( !env || !locker ) return -1;
913
914         /* If no op was provided, try to find the ctx anyway... */
915         if ( op ) {
916                 ctx = op->o_threadctx;
917         } else {
918                 ctx = ldap_pvt_thread_pool_context();
919         }
920
921         /* Shouldn't happen unless we're single-threaded */
922         if ( !ctx ) {
923                 *locker = 0;
924                 return 0;
925         }
926
927         if ( ldap_pvt_thread_pool_getkey( ctx, env, &data, NULL ) ) {
928                 for ( i=0, rc=1; rc != 0 && i<4; i++ ) {
929                         rc = XLOCK_ID( env, &lockid );
930                         if (rc) ldap_pvt_thread_yield();
931                 }
932                 if ( rc != 0) {
933                         return rc;
934                 }
935                 data = (void *)lockid;
936                 if ( ( rc = ldap_pvt_thread_pool_setkey( ctx, env,
937                         data, bdb_locker_id_free ) ) ) {
938                         XLOCK_ID_FREE( env, lockid );
939 #ifdef NEW_LOGGING
940                         LDAP_LOG( BACK_BDB, ERR, "bdb_locker_id: err %s(%d)\n",
941                                 db_strerror(rc), rc, 0 );
942 #else
943                         Debug( LDAP_DEBUG_ANY, "bdb_locker_id: err %s(%d)\n",
944                                 db_strerror(rc), rc, 0 );
945 #endif
946
947                         return rc;
948                 }
949         } else {
950                 lockid = (int)data;
951         }
952         *locker = lockid;
953         return 0;
954 }
955 #endif