]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldbm/cache.c
Initial implementation of Kerberos password verification for
[openldap] / servers / slapd / back-ldbm / cache.c
1 /* cache.c - routines to maintain an in-core cache of entries */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 1998-1999 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-ldbm.h"
19
20 /* LDBM backend specific entry info -- visible only to the cache */
21 typedef struct ldbm_entry_info {
22         ldap_pvt_thread_rdwr_t  lei_rdwr;       /* reader/writer lock */
23
24         /*
25          * remaining fields require backend cache lock to access
26          * These items are specific to the LDBM backend and should
27          * be hidden.
28          */
29         int             lei_state;      /* for the cache */
30 #define CACHE_ENTRY_UNDEFINED   0
31 #define CACHE_ENTRY_CREATING    1
32 #define CACHE_ENTRY_READY               2
33 #define CACHE_ENTRY_DELETED             3
34
35         int             lei_refcnt;     /* # threads ref'ing this entry */
36         Entry   *lei_lrunext;   /* for cache lru list */
37         Entry   *lei_lruprev;
38 } EntryInfo;
39 #define LEI(e)  ((EntryInfo *) ((e)->e_private))
40
41 static int      cache_delete_entry_internal(Cache *cache, Entry *e);
42 #ifdef LDAP_DEBUG
43 static void     lru_print(Cache *cache);
44 #endif
45
46 static int
47 cache_entry_rdwr_lock(Entry *e, int rw)
48 {
49         Debug( LDAP_DEBUG_ARGS, "entry_rdwr_%slock: ID: %ld\n",
50                 rw ? "w" : "r", e->e_id, 0);
51
52         if (rw)
53                 return ldap_pvt_thread_rdwr_wlock(&LEI(e)->lei_rdwr);
54         else
55                 return ldap_pvt_thread_rdwr_rlock(&LEI(e)->lei_rdwr);
56 }
57
58 static int
59 cache_entry_rdwr_trylock(Entry *e, int rw)
60 {
61         Debug( LDAP_DEBUG_ARGS, "entry_rdwr_%strylock: ID: %ld\n",
62                 rw ? "w" : "r", e->e_id, 0);
63
64         if (rw)
65                 return ldap_pvt_thread_rdwr_wtrylock(&LEI(e)->lei_rdwr);
66         else
67                 return ldap_pvt_thread_rdwr_rtrylock(&LEI(e)->lei_rdwr);
68 }
69
70 static int
71 cache_entry_rdwr_unlock(Entry *e, int rw)
72 {
73         Debug( LDAP_DEBUG_ARGS, "entry_rdwr_%sunlock: ID: %ld\n",
74                 rw ? "w" : "r", e->e_id, 0);
75
76         if (rw)
77                 return ldap_pvt_thread_rdwr_wunlock(&LEI(e)->lei_rdwr);
78         else
79                 return ldap_pvt_thread_rdwr_runlock(&LEI(e)->lei_rdwr);
80 }
81
82 static int
83 cache_entry_rdwr_init(Entry *e)
84 {
85         return ldap_pvt_thread_rdwr_init( &LEI(e)->lei_rdwr );
86 }
87
88 static int
89 cache_entry_rdwr_destroy(Entry *e)
90 {
91         return ldap_pvt_thread_rdwr_destroy( &LEI(e)->lei_rdwr );
92 }
93
94 static int
95 cache_entry_private_init( Entry*e )
96 {
97         assert( e->e_private == NULL );
98
99         if( e->e_private != NULL ) {
100                 /* this should never happen */
101                 return 1;
102         }
103
104         e->e_private = ch_calloc(1, sizeof(struct ldbm_entry_info));
105
106         if( cache_entry_rdwr_init( e ) != 0 ) {
107                 free( LEI(e) );
108                 e->e_private = NULL;
109                 return 1;
110         } 
111
112         return 0;
113 }
114
115 static int
116 cache_entry_private_destroy( Entry*e )
117 {
118         assert( e->e_private );
119
120         cache_entry_rdwr_destroy( e );
121
122         free( e->e_private );
123         e->e_private = NULL;
124         return 0;
125 }
126
127 void
128 cache_return_entry_rw( Cache *cache, Entry *e, int rw )
129 {
130         ID id;
131         int refcnt;
132
133         /* set cache mutex */
134         ldap_pvt_thread_mutex_lock( &cache->c_mutex );
135
136         assert( e->e_private );
137
138         cache_entry_rdwr_unlock(e, rw);
139
140         id = e->e_id;
141         refcnt = --LEI(e)->lei_refcnt;
142
143         if ( LEI(e)->lei_state == CACHE_ENTRY_CREATING ) {
144                 LEI(e)->lei_state = CACHE_ENTRY_READY;
145
146                 /* free cache mutex */
147                 ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
148
149                 Debug( LDAP_DEBUG_TRACE,
150                         "====> cache_return_entry_%s( %ld ): created (%d)\n",
151                         rw ? "w" : "r", id, refcnt );
152
153         } else if ( LEI(e)->lei_state == CACHE_ENTRY_DELETED ) {
154                 if( refcnt > 0 ) {
155                         /* free cache mutex */
156                         ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
157
158                         Debug( LDAP_DEBUG_TRACE,
159                                 "====> cache_return_entry_%s( %ld ): delete pending (%d)\n",
160                                 rw ? "w" : "r", id, refcnt );
161
162                 } else {
163                         cache_entry_private_destroy( e );
164                         entry_free( e );
165
166                         /* free cache mutex */
167                         ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
168
169                         Debug( LDAP_DEBUG_TRACE,
170                                 "====> cache_return_entry_%s( %ld ): deleted (%d)\n",
171                                 rw ? "w" : "r", id, refcnt );
172                 }
173
174         } else {
175                 /* free cache mutex */
176                 ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
177
178                 Debug( LDAP_DEBUG_TRACE,
179                         "====> cache_return_entry_%s( %ld ): returned (%d)\n",
180                         rw ? "w" : "r", id, refcnt);
181         }
182 }
183
184 #define LRU_DELETE( cache, e ) do { \
185         if ( LEI(e)->lei_lruprev != NULL ) { \
186                 LEI(LEI(e)->lei_lruprev)->lei_lrunext = LEI(e)->lei_lrunext; \
187         } else { \
188                 (cache)->c_lruhead = LEI(e)->lei_lrunext; \
189         } \
190         if ( LEI(e)->lei_lrunext != NULL ) { \
191                 LEI(LEI(e)->lei_lrunext)->lei_lruprev = LEI(e)->lei_lruprev; \
192         } else { \
193                 (cache)->c_lrutail = LEI(e)->lei_lruprev; \
194         } \
195 } while(0)
196
197 #define LRU_ADD( cache, e ) do { \
198         LEI(e)->lei_lrunext = (cache)->c_lruhead; \
199         if ( LEI(e)->lei_lrunext != NULL ) { \
200                 LEI(LEI(e)->lei_lrunext)->lei_lruprev = (e); \
201         } \
202         (cache)->c_lruhead = (e); \
203         LEI(e)->lei_lruprev = NULL; \
204         if ( (cache)->c_lrutail == NULL ) { \
205                 (cache)->c_lrutail = (e); \
206         } \
207 } while(0)
208
209 /*
210  * cache_add_entry_rw - create and lock an entry in the cache
211  * returns:     0       entry has been created and locked
212  *              1       entry already existed
213  *              -1      something bad happened
214  */
215 int
216 cache_add_entry_rw(
217     Cache       *cache,
218     Entry               *e,
219         int             rw
220 )
221 {
222         int     i, rc;
223         Entry   *ee;
224
225         /* set cache mutex */
226         ldap_pvt_thread_mutex_lock( &cache->c_mutex );
227
228         assert( e->e_private == NULL );
229
230         if( cache_entry_private_init(e) != 0 ) {
231                 /* free cache mutex */
232                 ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
233
234                 Debug( LDAP_DEBUG_ANY,
235                         "====> cache_add_entry( %ld ): \"%s\": private init failed!\n",
236                     e->e_id, e->e_dn, 0 );
237
238                 return( -1 );
239         }
240
241         if ( avl_insert( &cache->c_dntree, (caddr_t) e,
242                 (AVL_CMP) entry_dn_cmp, avl_dup_error ) != 0 )
243         {
244                 /* free cache mutex */
245                 ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
246
247                 Debug( LDAP_DEBUG_TRACE,
248                         "====> cache_add_entry( %ld ): \"%s\": already in dn cache\n",
249                     e->e_id, e->e_dn, 0 );
250
251                 cache_entry_private_destroy(e);
252
253                 return( 1 );
254         }
255
256         /* id tree */
257         if ( avl_insert( &cache->c_idtree, (caddr_t) e,
258                 (AVL_CMP) entry_id_cmp, avl_dup_error ) != 0 )
259         {
260                 Debug( LDAP_DEBUG_ANY,
261                         "====> cache_add_entry( %ld ): \"%s\": already in id cache\n",
262                     e->e_id, e->e_dn, 0 );
263
264
265                 /* delete from dn tree inserted above */
266                 if ( avl_delete( &cache->c_dntree, (caddr_t) e,
267                         (AVL_CMP) entry_dn_cmp ) == NULL )
268                 {
269                         Debug( LDAP_DEBUG_ANY, "====> can't delete from dn cache\n",
270                             0, 0, 0 );
271                 }
272
273                 cache_entry_private_destroy(e);
274
275                 /* free cache mutex */
276                 ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
277                 return( -1 );
278         }
279
280         cache_entry_rdwr_lock( e, rw );
281
282         /* put the entry into 'CREATING' state */
283         /* will be marked after when entry is returned */
284         LEI(e)->lei_state = CACHE_ENTRY_CREATING;
285         LEI(e)->lei_refcnt = 1;
286
287         /* lru */
288         LRU_ADD( cache, e );
289         if ( ++cache->c_cursize > cache->c_maxsize ) {
290                 /*
291                  * find the lru entry not currently in use and delete it.
292                  * in case a lot of entries are in use, only look at the
293                  * first 10 on the tail of the list.
294                  */
295                 i = 0;
296                 while ( cache->c_lrutail != NULL &&
297                         LEI(cache->c_lrutail)->lei_refcnt != 0 &&
298                         i < 10 )
299                 {
300                         /* move this in-use entry to the front of the q */
301                         ee = cache->c_lrutail;
302                         LRU_DELETE( cache, ee );
303                         LRU_ADD( cache, ee );
304                         i++;
305                 }
306
307                 /*
308                  * found at least one to delete - try to get back under
309                  * the max cache size.
310                  */
311                 while ( cache->c_lrutail != NULL &&
312                         LEI(cache->c_lrutail)->lei_refcnt == 0 &&
313                         cache->c_cursize > cache->c_maxsize )
314                 {
315                         e = cache->c_lrutail;
316
317                         /* delete from cache and lru q */
318                         /* XXX do we need rc ? */
319                         rc = cache_delete_entry_internal( cache, e );
320                         cache_entry_private_destroy( e );
321                         entry_free( e );
322                 }
323         }
324
325         /* free cache mutex */
326         ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
327         return( 0 );
328 }
329
330 /*
331  * cache_update_entry - update a LOCKED entry which has been deleted.
332  * returns:     0       entry has been created and locked
333  *              1       entry already existed
334  *              -1      something bad happened
335  */
336 int
337 cache_update_entry(
338     Cache       *cache,
339     Entry               *e
340 )
341 {
342         int     i, rc;
343         Entry   *ee;
344
345         /* set cache mutex */
346         ldap_pvt_thread_mutex_lock( &cache->c_mutex );
347
348         assert( e->e_private );
349
350         if ( avl_insert( &cache->c_dntree, (caddr_t) e,
351                 (AVL_CMP) entry_dn_cmp, avl_dup_error ) != 0 )
352         {
353                 Debug( LDAP_DEBUG_TRACE,
354                         "====> cache_update_entry( %ld ): \"%s\": already in dn cache\n",
355                     e->e_id, e->e_dn, 0 );
356
357                 /* free cache mutex */
358                 ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
359                 return( 1 );
360         }
361
362         /* id tree */
363         if ( avl_insert( &cache->c_idtree, (caddr_t) e,
364                 (AVL_CMP) entry_id_cmp, avl_dup_error ) != 0 )
365         {
366                 Debug( LDAP_DEBUG_ANY,
367                         "====> cache_update_entry( %ld ): \"%s\": already in id cache\n",
368                     e->e_id, e->e_dn, 0 );
369
370                 /* delete from dn tree inserted above */
371                 if ( avl_delete( &cache->c_dntree, (caddr_t) e,
372                         (AVL_CMP) entry_dn_cmp ) == NULL )
373                 {
374                         Debug( LDAP_DEBUG_ANY, "====> can't delete from dn cache\n",
375                             0, 0, 0 );
376                 }
377
378                 /* free cache mutex */
379                 ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
380                 return( -1 );
381         }
382
383
384         /* put the entry into 'CREATING' state */
385         /* will be marked after when entry is returned */
386         LEI(e)->lei_state = CACHE_ENTRY_CREATING;
387
388         /* lru */
389         LRU_ADD( cache, e );
390         if ( ++cache->c_cursize > cache->c_maxsize ) {
391                 /*
392                  * find the lru entry not currently in use and delete it.
393                  * in case a lot of entries are in use, only look at the
394                  * first 10 on the tail of the list.
395                  */
396                 i = 0;
397                 while ( cache->c_lrutail != NULL &&
398                         LEI(cache->c_lrutail)->lei_refcnt != 0 &&
399                         i < 10 )
400                 {
401                         /* move this in-use entry to the front of the q */
402                         ee = cache->c_lrutail;
403                         LRU_DELETE( cache, ee );
404                         LRU_ADD( cache, ee );
405                         i++;
406                 }
407
408                 /*
409                  * found at least one to delete - try to get back under
410                  * the max cache size.
411                  */
412                 while ( cache->c_lrutail != NULL &&
413                         LEI(cache->c_lrutail)->lei_refcnt == 0 &&
414                         cache->c_cursize > cache->c_maxsize )
415                 {
416                         e = cache->c_lrutail;
417
418                         /* delete from cache and lru q */
419                         /* XXX do we need rc ? */
420                         rc = cache_delete_entry_internal( cache, e );
421                         cache_entry_private_destroy( e );
422                         entry_free( e );
423                 }
424         }
425
426         /* free cache mutex */
427         ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
428         return( 0 );
429 }
430
431 /*
432  * cache_find_entry_dn2id - find an entry in the cache, given dn
433  */
434
435 ID
436 cache_find_entry_dn2id(
437         Backend         *be,
438     Cache       *cache,
439     const char          *dn
440 )
441 {
442         Entry           e, *ep;
443         ID                      id;
444         int count = 0;
445
446         e.e_dn = (char *) dn;
447         e.e_ndn = ch_strdup( dn );
448         (void) dn_normalize( e.e_ndn );
449
450 try_again:
451         /* set cache mutex */
452         ldap_pvt_thread_mutex_lock( &cache->c_mutex );
453
454         if ( (ep = (Entry *) avl_find( cache->c_dntree, (caddr_t) &e,
455                 (AVL_CMP) entry_dn_cmp )) != NULL )
456         {
457                 int state;
458                 count++;
459
460                 /*
461                  * ep now points to an unlocked entry
462                  * we do not need to lock the entry if we only
463                  * check the state, refcnt, LRU, and id.
464                  */
465
466                 assert( ep->e_private );
467
468                 /* save id */
469                 id = ep->e_id;
470                 state = LEI(ep)->lei_state;
471
472                 /*
473                  * entry is deleted or not fully created yet
474                  */
475                 if ( state != CACHE_ENTRY_READY ) {
476                         assert(state != CACHE_ENTRY_UNDEFINED);
477
478                         /* free cache mutex */
479                         ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
480
481                         Debug(LDAP_DEBUG_TRACE,
482                                 "====> cache_find_entry_dn2id(\"%s\"): %ld (not ready) %d\n",
483                                 dn, id, state);
484
485                         ldap_pvt_thread_yield();
486                         goto try_again;
487                 }
488
489                 /* lru */
490                 LRU_DELETE( cache, ep );
491                 LRU_ADD( cache, ep );
492                 
493                 /* free cache mutex */
494                 ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
495
496                 Debug(LDAP_DEBUG_TRACE,
497                         "====> cache_find_entry_dn2id(\"%s\"): %ld (%d tries)\n",
498                         dn, id, count);
499
500         } else {
501                 /* free cache mutex */
502                 ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
503
504                 id = NOID;
505         }
506
507         free(e.e_ndn);
508
509         return( id );
510 }
511
512 /*
513  * cache_find_entry_id - find an entry in the cache, given id
514  */
515
516 Entry *
517 cache_find_entry_id(
518         Cache   *cache,
519         ID                              id,
520         int                             rw
521 )
522 {
523         Entry   e;
524         Entry   *ep;
525         int     count = 0;
526
527         e.e_id = id;
528
529 try_again:
530         /* set cache mutex */
531         ldap_pvt_thread_mutex_lock( &cache->c_mutex );
532
533         if ( (ep = (Entry *) avl_find( cache->c_idtree, (caddr_t) &e,
534                 (AVL_CMP) entry_id_cmp )) != NULL )
535         {
536                 int state;
537                 ID      ep_id;
538
539                 count++;
540
541                 assert( ep->e_private );
542
543                 ep_id = ep->e_id; 
544                 state = LEI(ep)->lei_state;
545
546                 /*
547                  * entry is deleted or not fully created yet
548                  */
549                 if ( state != CACHE_ENTRY_READY ) {
550
551                         assert(state != CACHE_ENTRY_UNDEFINED);
552
553                         /* free cache mutex */
554                         ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
555
556                         Debug(LDAP_DEBUG_TRACE,
557                                 "====> cache_find_entry_id( %ld ): %ld (not ready) %d\n",
558                                 id, ep_id, state);
559
560                         ldap_pvt_thread_yield();
561                         goto try_again;
562                 }
563
564                 /* acquire reader lock */
565                 if ( cache_entry_rdwr_trylock(ep, rw) == LDAP_PVT_THREAD_EBUSY ) {
566                         /* could not acquire entry lock...
567                          * owner cannot free as we have the cache locked.
568                          * so, unlock the cache, yield, and try again.
569                          */
570
571                         /* free cache mutex */
572                         ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
573
574                         Debug(LDAP_DEBUG_TRACE,
575                                 "====> cache_find_entry_id( %ld ): %ld (busy) %d\n",
576                                 id, ep_id, state);
577
578                         ldap_pvt_thread_yield();
579                         goto try_again;
580                 }
581
582                 /* lru */
583                 LRU_DELETE( cache, ep );
584                 LRU_ADD( cache, ep );
585                 
586                 LEI(ep)->lei_refcnt++;
587
588                 /* free cache mutex */
589                 ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
590
591                 Debug(LDAP_DEBUG_TRACE,
592                         "====> cache_find_entry_id( %ld ) \"%s\" (found) (%d tries)\n",
593                         ep_id, ep->e_dn, count);
594
595                 return( ep );
596         }
597
598         /* free cache mutex */
599         ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
600
601         return( NULL );
602 }
603
604 /*
605  * cache_delete_entry - delete the entry e from the cache.  the caller
606  * should have obtained e (increasing its ref count) via a call to one
607  * of the cache_find_* routines.  the caller should *not* call the
608  * cache_return_entry() routine prior to calling cache_delete_entry().
609  * it performs this function.
610  *
611  * returns:     0       e was deleted ok
612  *              1       e was not in the cache
613  *              -1      something bad happened
614  */
615 int
616 cache_delete_entry(
617     Cache       *cache,
618     Entry               *e
619 )
620 {
621         int     rc;
622
623         /* set cache mutex */
624         ldap_pvt_thread_mutex_lock( &cache->c_mutex );
625
626         assert( e->e_private );
627
628         Debug( LDAP_DEBUG_TRACE, "====> cache_delete_entry( %ld )\n",
629                 e->e_id, 0, 0 );
630
631         rc = cache_delete_entry_internal( cache, e );
632
633         /* free cache mutex */
634         ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
635         return( rc );
636 }
637
638 static int
639 cache_delete_entry_internal(
640     Cache       *cache,
641     Entry               *e
642 )
643 {
644         int rc = 0;     /* return code */
645
646         /* dn tree */
647         if ( avl_delete( &cache->c_dntree, (caddr_t) e, (AVL_CMP) entry_dn_cmp )
648                 == NULL )
649         {
650                 rc = -1;
651         }
652
653         /* id tree */
654         if ( avl_delete( &cache->c_idtree, (caddr_t) e, (AVL_CMP) entry_id_cmp )
655                 == NULL )
656         {
657                 rc = -1;
658         }
659
660         if (rc != 0) {
661                 return rc;
662         }
663
664         /* lru */
665         LRU_DELETE( cache, e );
666         cache->c_cursize--;
667
668         /*
669          * flag entry to be freed later by a call to cache_return_entry()
670          */
671         LEI(e)->lei_state = CACHE_ENTRY_DELETED;
672
673         return( 0 );
674 }
675
676 void
677 cache_release_all( Cache *cache )
678 {
679         Entry *e;
680         int rc;
681
682         /* set cache mutex */
683         ldap_pvt_thread_mutex_lock( &cache->c_mutex );
684
685         Debug( LDAP_DEBUG_TRACE, "====> cache_release_all\n", 0, 0, 0 );
686
687         while ( (e = cache->c_lrutail) != NULL && LEI(e)->lei_refcnt == 0 ) {
688                 assert(!ldap_pvt_thread_rdwr_active(&LEI(e)->lei_rdwr));
689
690                 /* delete from cache and lru q */
691                 /* XXX do we need rc ? */
692                 rc = cache_delete_entry_internal( cache, e );
693                 cache_entry_private_destroy( e );
694                 entry_free( e );
695         }
696
697         if ( cache->c_cursize ) {
698                 Debug( LDAP_DEBUG_TRACE, "Entry-cache could not be emptied\n", 0, 0, 0 );
699         }
700
701         /* free cache mutex */
702         ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
703 }
704
705 #ifdef LDAP_DEBUG
706
707 static void
708 lru_print( Cache *cache )
709 {
710         Entry   *e;
711
712         fprintf( stderr, "LRU queue (head to tail):\n" );
713         for ( e = cache->c_lruhead; e != NULL; e = LEI(e)->lei_lrunext ) {
714                 fprintf( stderr, "\tdn \"%20s\" id %ld refcnt %d\n",
715                         e->e_dn, e->e_id, LEI(e)->lei_refcnt );
716         }
717         fprintf( stderr, "LRU queue (tail to head):\n" );
718         for ( e = cache->c_lrutail; e != NULL; e = LEI(e)->lei_lruprev ) {
719                 fprintf( stderr, "\tdn \"%20s\" id %ld refcnt %d\n",
720                         e->e_dn, e->e_id, LEI(e)->lei_refcnt );
721         }
722 }
723
724 #endif
725