]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldbm/dbcache.c
Experiment with busy loop protection...
[openldap] / servers / slapd / back-ldbm / dbcache.c
1 /* ldbmcache.c - maintain a cache of open ldbm files */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 1998-2000 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/socket.h>
14 #include <ac/string.h>
15 #include <ac/time.h>
16 #include <sys/stat.h>
17
18 #include "slap.h"
19 #include "back-ldbm.h"
20
21 DBCache *
22 ldbm_cache_open(
23     Backend     *be,
24     const char  *name,
25     const char  *suffix,
26     int         flags
27 )
28 {
29         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
30         int             i, lru, empty;
31         time_t          oldtime, curtime;
32         char            buf[MAXPATHLEN];
33 #ifdef HAVE_ST_BLKSIZE
34         struct stat     st;
35 #endif
36
37         if (li->li_envdirok)
38                 sprintf( buf, "%s%s", name, suffix );
39         else
40                 sprintf( buf, "%s" LDAP_DIRSEP "%s%s",
41                         li->li_directory, name, suffix );
42
43         if( li->li_dblocking ) {
44                 flags |= LDBM_LOCKING;
45         } else {
46                 flags |= LDBM_NOLOCKING;
47         }
48         
49         if( li->li_dbwritesync ) {
50                 flags |= LDBM_SYNC;
51         } else {
52                 flags |= LDBM_NOSYNC;
53         }
54         
55 #ifdef NEW_LOGGING
56         LDAP_LOG(( "cache", LDAP_LEVEL_ENTRY,
57                    "ldbm_cache_open: \"%s\", %d, %o\n", buf, flags, li->li_mode ));
58 #else
59         Debug( LDAP_DEBUG_TRACE, "=> ldbm_cache_open( \"%s\", %d, %o )\n", buf,
60             flags, li->li_mode );
61 #endif
62
63
64         curtime = slap_get_time();
65         empty = MAXDBCACHE;
66
67         ldap_pvt_thread_mutex_lock( &li->li_dbcache_mutex );
68         do {
69                 lru = 0;
70                 oldtime = curtime;
71                 for ( i = 0; i < MAXDBCACHE; i++ ) {
72                         /* see if this slot is free */
73                         if ( li->li_dbcache[i].dbc_name == NULL) {
74                                 if (empty == MAXDBCACHE)
75                                         empty = i;
76                                 continue;
77                         }
78
79                         if ( strcmp( li->li_dbcache[i].dbc_name, buf ) == 0 ) {
80                                 /* already open - return it */
81                                 if (li->li_dbcache[i].dbc_flags != flags
82                                         && li->li_dbcache[i].dbc_refcnt == 0)
83                                 {
84                                         /* we don't want to use an open cache with different
85                                          * permissions (esp. if we need write but the open
86                                          * cache is read-only).  So close this one if
87                                          * possible, and re-open below.
88                                          *
89                                          * FIXME:  what about the case where the refcount
90                                          * is > 0?  right now, we're using it anyway and
91                                          * just praying.  Can there be more than one open
92                                          * cache to the same db?
93                                          *
94                                          * Also, it's really only necessary to compare the
95                                          * read-only flag, instead of all of the flags,
96                                          * but for now I'm checking all of them.
97                                          */
98                                         lru = i;
99                                         empty = MAXDBCACHE;
100                                         break;
101                                 }
102                                 li->li_dbcache[i].dbc_refcnt++;
103 #ifdef NEW_LOGGING
104                                 LDAP_LOG(( "cache", LDAP_LEVEL_DETAIL1,
105                                            "ldbm_cache_open: cache %d\n", i ));
106 #else
107                                 Debug( LDAP_DEBUG_TRACE,
108                                     "<= ldbm_cache_open (cache %d)\n", i, 0, 0 );
109 #endif
110
111                                 ldap_pvt_thread_mutex_unlock( &li->li_dbcache_mutex );
112                                 return( &li->li_dbcache[i] );
113                         }
114
115                         /* keep track of lru db */
116                         if ( li->li_dbcache[i].dbc_lastref < oldtime
117                                 && li->li_dbcache[i].dbc_refcnt == 0 )
118                         {
119                                 lru = i;
120                                 oldtime = li->li_dbcache[i].dbc_lastref;
121                         }
122                 }
123
124                 i = empty;
125                 if ( i == MAXDBCACHE ) {
126                         /* no empty slots, not already open - close lru and use that slot */
127                         if ( li->li_dbcache[lru].dbc_refcnt == 0 ) {
128                                 i = lru;
129                                 ldbm_close( li->li_dbcache[i].dbc_db );
130                                 free( li->li_dbcache[i].dbc_name );
131                                 li->li_dbcache[i].dbc_name = NULL;
132                         } else {
133 #ifdef NEW_LOGGING
134                                 LDAP_LOG(( "cache", LDAP_LEVEL_INFO,
135                                            "ldbm_cache_open: no unused db to close - waiting\n" ));
136 #else
137                                 Debug( LDAP_DEBUG_ANY,
138                                     "ldbm_cache_open no unused db to close - waiting\n",
139                                     0, 0, 0 );
140 #endif
141
142                                 ldap_pvt_thread_cond_wait( &li->li_dbcache_cv,
143                                             &li->li_dbcache_mutex );
144                                 /* after waiting for a free slot, go back to square
145                                  * one: look for an open cache for this db, or an
146                                  * empty slot, or an unref'ed cache, or wait again.
147                                  */
148                         }
149                 }
150         } while (i == MAXDBCACHE);
151
152         if ( (li->li_dbcache[i].dbc_db = ldbm_open( li->li_dbenv, buf, flags, li->li_mode,
153             li->li_dbcachesize )) == NULL )
154         {
155                 int err = errno;
156 #ifdef NEW_LOGGING
157                 LDAP_LOG(( "cache", LDAP_LEVEL_ERR,
158                            "ldbm_cache_open: \"%s\" failed, errono=%d, reason=%s\n",
159                            buf, err, err > -1 && err < sys_nerr ? sys_errlist[err] :
160                            "unknown" ));
161 #else
162                 Debug( LDAP_DEBUG_TRACE,
163                     "<= ldbm_cache_open NULL \"%s\" errno=%d reason=\"%s\")\n",
164                     buf, err, err > -1 && err < sys_nerr ?
165                     sys_errlist[err] : "unknown" );
166 #endif
167
168                 ldap_pvt_thread_mutex_unlock( &li->li_dbcache_mutex );
169                 return( NULL );
170         }
171         li->li_dbcache[i].dbc_name = ch_strdup( buf );
172         li->li_dbcache[i].dbc_refcnt = 1;
173         li->li_dbcache[i].dbc_lastref = curtime;
174         li->li_dbcache[i].dbc_flags = flags;
175         li->li_dbcache[i].dbc_dirty = 0;
176 #ifdef HAVE_ST_BLKSIZE
177         if ( stat( buf, &st ) == 0 ) {
178                 li->li_dbcache[i].dbc_blksize = st.st_blksize;
179         } else
180 #endif
181         {
182                 li->li_dbcache[i].dbc_blksize = DEFAULT_BLOCKSIZE;
183         }
184         li->li_dbcache[i].dbc_maxids = (li->li_dbcache[i].dbc_blksize /
185             sizeof(ID)) - ID_BLOCK_IDS_OFFSET;
186         li->li_dbcache[i].dbc_maxindirect = ( SLAPD_LDBM_MIN_MAXIDS /
187             li->li_dbcache[i].dbc_maxids ) + 1;
188
189         assert( li->li_dbcache[i].dbc_maxindirect < 256 );
190
191 #ifdef NEW_LOGGING
192         LDAP_LOG(( "cache", LDAP_LEVEL_ARGS,
193                    "ldbm_cache_open: blksize:%ld  maxids:%d  maxindirect:%d\n",
194                    li->li_dbcache[i].dbc_blksize, li->li_dbcache[i].dbc_maxids,
195                    li->li_dbcache[i].dbc_maxindirect ));
196 #else
197         Debug( LDAP_DEBUG_ARGS,
198             "ldbm_cache_open (blksize %ld) (maxids %d) (maxindirect %d)\n",
199             li->li_dbcache[i].dbc_blksize, li->li_dbcache[i].dbc_maxids,
200             li->li_dbcache[i].dbc_maxindirect );
201 #endif
202
203 #ifdef NEW_LOGGING
204         LDAP_LOG(( "cache", LDAP_LEVEL_DETAIL1,
205                    "ldbm_cache_open: opened %d\n", i ));
206 #else
207         Debug( LDAP_DEBUG_TRACE, "<= ldbm_cache_open (opened %d)\n", i, 0, 0 );
208 #endif
209
210         ldap_pvt_thread_mutex_init( &li->li_dbcache[i].dbc_write_mutex );
211
212         ldap_pvt_thread_mutex_unlock( &li->li_dbcache_mutex );
213         return( &li->li_dbcache[i] );
214 }
215
216 void
217 ldbm_cache_close( Backend *be, DBCache *db )
218 {
219         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
220
221         if( li->li_dbwritesync && db->dbc_dirty ) {
222                 ldbm_sync( db->dbc_db );
223                 db->dbc_dirty = 0;
224         }
225
226         ldap_pvt_thread_mutex_lock( &li->li_dbcache_mutex );
227         if ( --db->dbc_refcnt <= 0 ) {
228                 db->dbc_refcnt = 0;
229                 ldap_pvt_thread_cond_signal( &li->li_dbcache_cv );
230         }
231         ldap_pvt_thread_mutex_unlock( &li->li_dbcache_mutex );
232 }
233
234 void
235 ldbm_cache_really_close( Backend *be, DBCache *db )
236 {
237         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
238
239         ldap_pvt_thread_mutex_lock( &li->li_dbcache_mutex );
240         if ( --db->dbc_refcnt <= 0 ) {
241                 db->dbc_refcnt = 0;
242                 ldap_pvt_thread_cond_signal( &li->li_dbcache_cv );
243                 ldbm_close( db->dbc_db );
244                 free( db->dbc_name );
245                 db->dbc_name = NULL;
246                 ldap_pvt_thread_mutex_destroy( &db->dbc_write_mutex );
247         }
248         ldap_pvt_thread_mutex_unlock( &li->li_dbcache_mutex );
249 }
250
251 void
252 ldbm_cache_flush_all( Backend *be )
253 {
254         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
255         int             i;
256
257         ldap_pvt_thread_mutex_lock( &li->li_dbcache_mutex );
258         for ( i = 0; i < MAXDBCACHE; i++ ) {
259                 if ( li->li_dbcache[i].dbc_name != NULL ) {
260 #ifdef NEW_LOGGING
261                         LDAP_LOG(( "cache", LDAP_LEVEL_DETAIL1,
262                                    "ldbm_cache_flush_all: flushing db (%s)\n",
263                                    li->li_dbcache[i].dbc_name ));
264 #else
265                         Debug( LDAP_DEBUG_TRACE, "ldbm flushing db (%s)\n",
266                             li->li_dbcache[i].dbc_name, 0, 0 );
267 #endif
268
269                         ldbm_sync( li->li_dbcache[i].dbc_db );
270                         li->li_dbcache[i].dbc_dirty = 0;
271                         if ( li->li_dbcache[i].dbc_refcnt != 0 ) {
272 #ifdef NEW_LOGGING
273                                 LDAP_LOG(( "cache", LDAP_LEVEL_INFO,
274                                            "ldbm_cache_flush_all: couldn't close db (%s), refcnt=%d\n",
275                                            li->li_dbcache[i].dbc_name, li->li_dbcache[i].dbc_refcnt ));
276 #else
277                                 Debug( LDAP_DEBUG_TRACE,
278                                        "refcnt = %d, couldn't close db (%s)\n",
279                                        li->li_dbcache[i].dbc_refcnt,
280                                        li->li_dbcache[i].dbc_name, 0 );
281 #endif
282
283                         } else {
284 #ifdef NEW_LOGGING
285                                 LDAP_LOG(( "cache", LDAP_LEVEL_DETAIL1,
286                                            "ldbm_cache_flush_all: ldbm closing db (%s)\n",
287                                            li->li_dbcache[i].dbc_name ));
288 #else
289                                 Debug( LDAP_DEBUG_TRACE,
290                                        "ldbm closing db (%s)\n",
291                                        li->li_dbcache[i].dbc_name, 0, 0 );
292 #endif
293
294                                 ldap_pvt_thread_cond_signal( &li->li_dbcache_cv );
295                                 ldbm_close( li->li_dbcache[i].dbc_db );
296                                 free( li->li_dbcache[i].dbc_name );
297                                 li->li_dbcache[i].dbc_name = NULL;
298                         }
299                 }
300         }
301         ldap_pvt_thread_mutex_unlock( &li->li_dbcache_mutex );
302 }
303
304 void
305 ldbm_cache_sync( Backend *be )
306 {
307         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
308         int             i;
309
310         ldap_pvt_thread_mutex_lock( &li->li_dbcache_mutex );
311         for ( i = 0; i < MAXDBCACHE; i++ ) {
312                 if ( li->li_dbcache[i].dbc_name != NULL && li->li_dbcache[i].dbc_dirty ) {
313                         Debug(  LDAP_DEBUG_TRACE, "ldbm syncing db (%s)\n",
314                                 li->li_dbcache[i].dbc_name, 0, 0 );
315                         ldbm_sync( li->li_dbcache[i].dbc_db );
316                         li->li_dbcache[i].dbc_dirty = 0;
317                 }
318         }
319         ldap_pvt_thread_mutex_unlock( &li->li_dbcache_mutex );
320 }
321
322 Datum
323 ldbm_cache_fetch(
324     DBCache     *db,
325     Datum               key
326 )
327 {
328         return ldbm_fetch( db->dbc_db, key );
329 }
330
331 int
332 ldbm_cache_store(
333     DBCache     *db,
334     Datum               key,
335     Datum               data,
336     int                 flags
337 )
338 {
339         int     rc;
340
341 #ifdef LDBM_DEBUG
342         Statslog( LDAP_DEBUG_STATS,
343                 "=> ldbm_cache_store(): key.dptr=%s, key.dsize=%d\n",
344                 key.dptr, key.dsize, 0, 0, 0 );
345
346         Statslog( LDAP_DEBUG_STATS,
347                 "=> ldbm_cache_store(): key.dptr=0x%08x, data.dptr=0x%0 8x\n",
348                 key.dptr, data.dptr, 0, 0, 0 );
349
350         Statslog( LDAP_DEBUG_STATS,
351                 "=> ldbm_cache_store(): data.dptr=%s, data.dsize=%d\n",
352                 data.dptr, data.dsize, 0, 0, 0 );
353
354         Statslog( LDAP_DEBUG_STATS,
355                 "=> ldbm_cache_store(): flags=0x%08x\n",
356                 flags, 0, 0, 0, 0 );
357 #endif /* LDBM_DEBUG */
358
359         db->dbc_dirty = 1;
360         rc = ldbm_store( db->dbc_db, key, data, flags );
361
362         return( rc );
363 }
364
365 int
366 ldbm_cache_delete(
367     DBCache     *db,
368     Datum               key
369 )
370 {
371         int     rc;
372
373         db->dbc_dirty = 1;
374         rc = ldbm_delete( db->dbc_db, key );
375
376         return( rc );
377 }
378
379 void *
380 ldbm_cache_sync_daemon(
381         void *be_ptr
382 )
383 {
384         Backend *be = (Backend *)be_ptr;
385         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
386
387         Debug( LDAP_DEBUG_ANY, "synchronizer starting for %s\n", li->li_directory, 0, 0 );
388   
389         while (!li->li_dbshutdown) {
390                 int i = li->li_dbsyncwaitn;
391
392                 sleep( li->li_dbsyncfreq );
393
394                 while (i && ldap_pvt_thread_pool_backload(&connection_pool) != 0) {
395                         Debug( LDAP_DEBUG_TRACE, "delay syncing %s\n", li->li_directory, 0, 0 );
396                         sleep(li->li_dbsyncwaitinterval);
397                         i--;
398                 }
399
400                 if (!li->li_dbshutdown) {
401                         Debug( LDAP_DEBUG_TRACE, "syncing %s\n", li->li_directory, 0, 0 );
402                         ldbm_cache_sync( be );
403                 }
404         }
405
406         Debug( LDAP_DEBUG_ANY, "synchronizer stopping\n", 0, 0, 0 );
407   
408         return NULL;
409 }