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