Merge from vendor branch GCC:
[dragonfly.git] / contrib / bind-9.3 / lib / dns / cache.c
1 /*
2  * Copyright (C) 2004-2006  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 1999-2003  Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15  * PERFORMANCE OF THIS SOFTWARE.
16  */
17
18 /* $Id: cache.c,v 1.45.2.4.8.15 2006/08/01 01:07:05 marka Exp $ */
19
20 #include <config.h>
21
22 #include <isc/mem.h>
23 #include <isc/task.h>
24 #include <isc/time.h>
25 #include <isc/timer.h>
26 #include <isc/util.h>
27
28 #include <dns/cache.h>
29 #include <dns/db.h>
30 #include <dns/dbiterator.h>
31 #include <dns/events.h>
32 #include <dns/log.h>
33 #include <dns/masterdump.h>
34 #include <dns/rdata.h>
35 #include <dns/rdataset.h>
36 #include <dns/rdatasetiter.h>
37 #include <dns/result.h>
38
39 #define CACHE_MAGIC             ISC_MAGIC('$', '$', '$', '$')
40 #define VALID_CACHE(cache)      ISC_MAGIC_VALID(cache, CACHE_MAGIC)
41
42 /*
43  * The following two variables control incremental cleaning.
44  * MINSIZE is how many bytes is the floor for dns_cache_setcachesize().
45  * CLEANERINCREMENT is how many nodes are examined in one pass.
46  */
47 #define DNS_CACHE_MINSIZE               2097152 /* Bytes.  2097152 = 2 MB */
48 #define DNS_CACHE_CLEANERINCREMENT      1000    /* Number of nodes. */
49
50 /***
51  ***    Types
52  ***/
53
54 /*
55  * A cache_cleaner_t encapsulsates the state of the periodic
56  * cache cleaning.
57  */
58
59 typedef struct cache_cleaner cache_cleaner_t;
60
61 typedef enum {
62         cleaner_s_idle, /* Waiting for cleaning-interval to expire. */
63         cleaner_s_busy, /* Currently cleaning. */
64         cleaner_s_done  /* Freed enough memory after being overmem. */
65 } cleaner_state_t;
66
67 /*
68  * Convenience macros for comprehensive assertion checking.
69  */
70 #define CLEANER_IDLE(c) ((c)->state == cleaner_s_idle && \
71                          (c)->resched_event != NULL)
72 #define CLEANER_BUSY(c) ((c)->state == cleaner_s_busy && \
73                          (c)->iterator != NULL && \
74                          (c)->resched_event == NULL)
75
76 /*
77  * Accesses to a cache cleaner object are synchronized through
78  * task/event serialization, or locked from the cache object.
79  */
80 struct cache_cleaner {
81         isc_mutex_t     lock;
82         /*
83          * Locks overmem_event, overmem.  Note: never allocate memory
84          * while holding this lock - that could lead to deadlock since
85          * the lock is take by water() which is called from the memory
86          * allocator.
87          */
88
89         dns_cache_t     *cache;
90         isc_task_t      *task;
91         unsigned int    cleaning_interval; /* The cleaning-interval from
92                                               named.conf, in seconds. */
93         isc_timer_t     *cleaning_timer;
94         isc_event_t     *resched_event; /* Sent by cleaner task to
95                                            itself to reschedule */
96         isc_event_t     *overmem_event;
97
98         dns_dbiterator_t *iterator;
99         int              increment;     /* Number of names to
100                                            clean in one increment */
101         cleaner_state_t  state;         /* Idle/Busy. */
102         isc_boolean_t    overmem;       /* The cache is in an overmem state. */
103         isc_boolean_t    replaceiterator;
104 };
105
106 /*
107  * The actual cache object.
108  */
109
110 struct dns_cache {
111         /* Unlocked. */
112         unsigned int            magic;
113         isc_mutex_t             lock;
114         isc_mutex_t             filelock;
115         isc_mem_t               *mctx;
116
117         /* Locked by 'lock'. */
118         int                     references;
119         int                     live_tasks;
120         dns_rdataclass_t        rdclass;
121         dns_db_t                *db;
122         cache_cleaner_t         cleaner;
123         char                    *db_type;
124         int                     db_argc;
125         char                    **db_argv;
126
127         /* Locked by 'filelock'. */
128         char *                  filename;
129         /* Access to the on-disk cache file is also locked by 'filelock'. */
130 };
131
132 /***
133  ***    Functions
134  ***/
135
136 static isc_result_t
137 cache_cleaner_init(dns_cache_t *cache, isc_taskmgr_t *taskmgr,
138                    isc_timermgr_t *timermgr, cache_cleaner_t *cleaner);
139
140 static void
141 cleaning_timer_action(isc_task_t *task, isc_event_t *event);
142
143 static void
144 incremental_cleaning_action(isc_task_t *task, isc_event_t *event);
145
146 static void
147 cleaner_shutdown_action(isc_task_t *task, isc_event_t *event);
148
149 static void
150 overmem_cleaning_action(isc_task_t *task, isc_event_t *event);
151
152 static inline isc_result_t
153 cache_create_db(dns_cache_t *cache, dns_db_t **db) {
154         return (dns_db_create(cache->mctx, cache->db_type, dns_rootname,
155                               dns_dbtype_cache, cache->rdclass,
156                               cache->db_argc, cache->db_argv, db));
157 }
158
159 isc_result_t
160 dns_cache_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
161                  isc_timermgr_t *timermgr, dns_rdataclass_t rdclass,
162                  const char *db_type, unsigned int db_argc, char **db_argv,
163                  dns_cache_t **cachep)
164 {
165         isc_result_t result;
166         dns_cache_t *cache;
167         int i;
168
169         REQUIRE(cachep != NULL);
170         REQUIRE(*cachep == NULL);
171         REQUIRE(mctx != NULL);
172
173         cache = isc_mem_get(mctx, sizeof(*cache));
174         if (cache == NULL)
175                 return (ISC_R_NOMEMORY);
176
177         cache->mctx = NULL;
178         isc_mem_attach(mctx, &cache->mctx);
179
180         result = isc_mutex_init(&cache->lock);
181         if (result != ISC_R_SUCCESS) {
182                 UNEXPECTED_ERROR(__FILE__, __LINE__,
183                                  "isc_mutex_init() failed: %s",
184                                  dns_result_totext(result));
185                 result = ISC_R_UNEXPECTED;
186                 goto cleanup_mem;
187         }
188
189         result = isc_mutex_init(&cache->filelock);
190         if (result != ISC_R_SUCCESS) {
191                 UNEXPECTED_ERROR(__FILE__, __LINE__,
192                                  "isc_mutex_init() failed: %s",
193                                  dns_result_totext(result));
194                 result = ISC_R_UNEXPECTED;
195                 goto cleanup_lock;
196         }
197
198         cache->references = 1;
199         cache->live_tasks = 0;
200         cache->rdclass = rdclass;
201
202         cache->db_type = isc_mem_strdup(mctx, db_type);
203         if (cache->db_type == NULL) {
204                 result = ISC_R_NOMEMORY;
205                 goto cleanup_filelock;
206         }
207
208         cache->db_argc = db_argc;
209         if (cache->db_argc == 0)
210                 cache->db_argv = NULL;
211         else {
212                 cache->db_argv = isc_mem_get(mctx,
213                                              cache->db_argc * sizeof(char *));
214                 if (cache->db_argv == NULL) {
215                         result = ISC_R_NOMEMORY;
216                         goto cleanup_dbtype;
217                 }
218                 for (i = 0; i < cache->db_argc; i++)
219                         cache->db_argv[i] = NULL;
220                 for (i = 0; i < cache->db_argc; i++) {
221                         cache->db_argv[i] = isc_mem_strdup(mctx, db_argv[i]);
222                         if (cache->db_argv[i] == NULL) {
223                                 result = ISC_R_NOMEMORY;
224                                 goto cleanup_dbargv;
225                         }
226                 }
227         }
228
229         cache->db = NULL;
230         result = cache_create_db(cache, &cache->db);
231         if (result != ISC_R_SUCCESS)
232                 goto cleanup_dbargv;
233
234         cache->filename = NULL;
235
236         cache->magic = CACHE_MAGIC;
237
238         result = cache_cleaner_init(cache, taskmgr, timermgr, &cache->cleaner);
239         if (result != ISC_R_SUCCESS)
240                 goto cleanup_db;
241
242         *cachep = cache;
243         return (ISC_R_SUCCESS);
244
245  cleanup_db:
246         dns_db_detach(&cache->db);
247  cleanup_dbargv:
248         for (i = 0; i < cache->db_argc; i++)
249                 if (cache->db_argv[i] != NULL)
250                         isc_mem_free(mctx, cache->db_argv[i]);
251         if (cache->db_argv != NULL)
252                 isc_mem_put(mctx, cache->db_argv,
253                             cache->db_argc * sizeof(char *));
254  cleanup_dbtype:
255         isc_mem_free(mctx, cache->db_type);
256  cleanup_filelock:
257         DESTROYLOCK(&cache->filelock);
258  cleanup_lock:
259         DESTROYLOCK(&cache->lock);
260  cleanup_mem:
261         isc_mem_put(mctx, cache, sizeof(*cache));
262         isc_mem_detach(&mctx);
263         return (result);
264 }
265
266 static void
267 cache_free(dns_cache_t *cache) {
268         isc_mem_t *mctx;
269         int i;
270
271         REQUIRE(VALID_CACHE(cache));
272         REQUIRE(cache->references == 0);
273
274         isc_mem_setwater(cache->mctx, NULL, NULL, 0, 0);
275
276         if (cache->cleaner.task != NULL)
277                 isc_task_detach(&cache->cleaner.task);
278
279         if (cache->cleaner.overmem_event != NULL)
280                 isc_event_free(&cache->cleaner.overmem_event);
281
282         if (cache->cleaner.resched_event != NULL)
283                 isc_event_free(&cache->cleaner.resched_event);
284
285         if (cache->cleaner.iterator != NULL)
286                 dns_dbiterator_destroy(&cache->cleaner.iterator);
287
288         DESTROYLOCK(&cache->cleaner.lock);
289
290         if (cache->filename) {
291                 isc_mem_free(cache->mctx, cache->filename);
292                 cache->filename = NULL;
293         }
294
295         if (cache->db != NULL)
296                 dns_db_detach(&cache->db);
297
298         if (cache->db_argv != NULL) {
299                 for (i = 0; i < cache->db_argc; i++)
300                         if (cache->db_argv[i] != NULL)
301                                 isc_mem_free(cache->mctx, cache->db_argv[i]);
302                 isc_mem_put(cache->mctx, cache->db_argv,
303                             cache->db_argc * sizeof(char *));
304         }
305
306         if (cache->db_type != NULL)
307                 isc_mem_free(cache->mctx, cache->db_type);
308
309         DESTROYLOCK(&cache->lock);
310         DESTROYLOCK(&cache->filelock);
311         cache->magic = 0;
312         mctx = cache->mctx;
313         isc_mem_put(cache->mctx, cache, sizeof(*cache));
314         isc_mem_detach(&mctx);
315 }
316
317
318 void
319 dns_cache_attach(dns_cache_t *cache, dns_cache_t **targetp) {
320
321         REQUIRE(VALID_CACHE(cache));
322         REQUIRE(targetp != NULL && *targetp == NULL);
323
324         LOCK(&cache->lock);
325         cache->references++;
326         UNLOCK(&cache->lock);
327
328         *targetp = cache;
329 }
330
331 void
332 dns_cache_detach(dns_cache_t **cachep) {
333         dns_cache_t *cache;
334         isc_boolean_t free_cache = ISC_FALSE;
335
336         REQUIRE(cachep != NULL);
337         cache = *cachep;
338         REQUIRE(VALID_CACHE(cache));
339
340         LOCK(&cache->lock);
341         REQUIRE(cache->references > 0);
342         cache->references--;
343         if (cache->references == 0) {
344                 cache->cleaner.overmem = ISC_FALSE;
345                 free_cache = ISC_TRUE;
346         }
347
348         *cachep = NULL;
349
350         if (free_cache) {
351                 /*
352                  * When the cache is shut down, dump it to a file if one is
353                  * specified.
354                  */
355                 isc_result_t result = dns_cache_dump(cache);
356                 if (result != ISC_R_SUCCESS)
357                         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
358                                       DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
359                                       "error dumping cache: %s ",
360                                       isc_result_totext(result));
361
362                 /*
363                  * If the cleaner task exists, let it free the cache.
364                  */
365                 if (cache->live_tasks > 0) {
366                         isc_task_shutdown(cache->cleaner.task);
367                         free_cache = ISC_FALSE;
368                 }
369         }
370
371         UNLOCK(&cache->lock);
372
373         if (free_cache)
374                 cache_free(cache);
375 }
376
377 void
378 dns_cache_attachdb(dns_cache_t *cache, dns_db_t **dbp) {
379         REQUIRE(VALID_CACHE(cache));
380         REQUIRE(dbp != NULL && *dbp == NULL);
381         REQUIRE(cache->db != NULL);
382
383         LOCK(&cache->lock);
384         dns_db_attach(cache->db, dbp);
385         UNLOCK(&cache->lock);
386
387 }
388
389 isc_result_t
390 dns_cache_setfilename(dns_cache_t *cache, const char *filename) {
391         char *newname;
392
393         REQUIRE(VALID_CACHE(cache));
394         REQUIRE(filename != NULL);
395
396         newname = isc_mem_strdup(cache->mctx, filename);
397         if (newname == NULL)
398                 return (ISC_R_NOMEMORY);
399
400         LOCK(&cache->filelock);
401         if (cache->filename)
402                 isc_mem_free(cache->mctx, cache->filename);
403         cache->filename = newname;
404         UNLOCK(&cache->filelock);
405
406         return (ISC_R_SUCCESS);
407 }
408
409 isc_result_t
410 dns_cache_load(dns_cache_t *cache) {
411         isc_result_t result;
412
413         REQUIRE(VALID_CACHE(cache));
414
415         if (cache->filename == NULL)
416                 return (ISC_R_SUCCESS);
417
418         LOCK(&cache->filelock);
419         result = dns_db_load(cache->db, cache->filename);
420         UNLOCK(&cache->filelock);
421
422         return (result);
423 }
424
425 isc_result_t
426 dns_cache_dump(dns_cache_t *cache) {
427         isc_result_t result;
428
429         REQUIRE(VALID_CACHE(cache));
430
431         if (cache->filename == NULL)
432                 return (ISC_R_SUCCESS);
433
434         LOCK(&cache->filelock);
435         result = dns_master_dump(cache->mctx, cache->db, NULL,
436                                  &dns_master_style_cache, cache->filename);
437         UNLOCK(&cache->filelock);
438
439         return (result);
440 }
441
442 void
443 dns_cache_setcleaninginterval(dns_cache_t *cache, unsigned int t) {
444         isc_interval_t interval;
445         isc_result_t result;
446
447         LOCK(&cache->lock);
448
449         /*
450          * It may be the case that the cache has already shut down.
451          * If so, it has no timer.
452          */
453         if (cache->cleaner.cleaning_timer == NULL)
454                 goto unlock;
455
456         cache->cleaner.cleaning_interval = t;
457
458         if (t == 0) {
459                 result = isc_timer_reset(cache->cleaner.cleaning_timer,
460                                          isc_timertype_inactive,
461                                          NULL, NULL, ISC_TRUE);
462         } else {
463                 isc_interval_set(&interval, cache->cleaner.cleaning_interval,
464                                  0);
465                 result = isc_timer_reset(cache->cleaner.cleaning_timer,
466                                          isc_timertype_ticker,
467                                          NULL, &interval, ISC_FALSE);
468         }
469         if (result != ISC_R_SUCCESS)    
470                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
471                               DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
472                               "could not set cache cleaning interval: %s",
473                               isc_result_totext(result));
474
475  unlock:
476         UNLOCK(&cache->lock);
477 }
478
479 /*
480  * Initialize the cache cleaner object at *cleaner.
481  * Space for the object must be allocated by the caller.
482  */
483
484 static isc_result_t
485 cache_cleaner_init(dns_cache_t *cache, isc_taskmgr_t *taskmgr,
486                    isc_timermgr_t *timermgr, cache_cleaner_t *cleaner)
487 {
488         isc_result_t result;
489
490         result = isc_mutex_init(&cleaner->lock);
491         if (result != ISC_R_SUCCESS) {
492                 UNEXPECTED_ERROR(__FILE__, __LINE__,
493                                  "isc_mutex_init() failed: %s",
494                                  dns_result_totext(result));
495                 result = ISC_R_UNEXPECTED;
496                 goto fail;
497         }
498
499         cleaner->increment = DNS_CACHE_CLEANERINCREMENT;
500         cleaner->state = cleaner_s_idle;
501         cleaner->cache = cache;
502         cleaner->iterator = NULL;
503         cleaner->overmem = ISC_FALSE;
504         cleaner->replaceiterator = ISC_FALSE;
505
506         cleaner->task = NULL;
507         cleaner->cleaning_timer = NULL;
508         cleaner->resched_event = NULL;
509         cleaner->overmem_event = NULL;
510
511         result = dns_db_createiterator(cleaner->cache->db, ISC_FALSE,
512                                        &cleaner->iterator);
513         if (result != ISC_R_SUCCESS)
514                 goto cleanup;
515
516         if (taskmgr != NULL && timermgr != NULL) {
517                 result = isc_task_create(taskmgr, 1, &cleaner->task);
518                 if (result != ISC_R_SUCCESS) {
519                         UNEXPECTED_ERROR(__FILE__, __LINE__,
520                                          "isc_task_create() failed: %s",
521                                          dns_result_totext(result));
522                         result = ISC_R_UNEXPECTED;
523                         goto cleanup;
524                 }
525                 cleaner->cache->live_tasks++;
526                 isc_task_setname(cleaner->task, "cachecleaner", cleaner);
527
528                 result = isc_task_onshutdown(cleaner->task,
529                                              cleaner_shutdown_action, cache);
530                 if (result != ISC_R_SUCCESS) {
531                         UNEXPECTED_ERROR(__FILE__, __LINE__,
532                                          "cache cleaner: "
533                                          "isc_task_onshutdown() failed: %s",
534                                          dns_result_totext(result));
535                         goto cleanup;
536                 }
537
538                 cleaner->cleaning_interval = 0; /* Initially turned off. */
539                 result = isc_timer_create(timermgr, isc_timertype_inactive,
540                                            NULL, NULL,
541                                            cleaner->task,
542                                            cleaning_timer_action, cleaner,
543                                            &cleaner->cleaning_timer);
544                 if (result != ISC_R_SUCCESS) {
545                         UNEXPECTED_ERROR(__FILE__, __LINE__,
546                                          "isc_timer_create() failed: %s",
547                                          dns_result_totext(result));
548                         result = ISC_R_UNEXPECTED;
549                         goto cleanup;
550                 }
551
552                 cleaner->resched_event =
553                         isc_event_allocate(cache->mctx, cleaner,
554                                            DNS_EVENT_CACHECLEAN,
555                                            incremental_cleaning_action,
556                                            cleaner, sizeof(isc_event_t));
557                 if (cleaner->resched_event == NULL) {
558                         result = ISC_R_NOMEMORY;
559                         goto cleanup;
560                 }
561                 
562                 cleaner->overmem_event =
563                         isc_event_allocate(cache->mctx, cleaner,
564                                            DNS_EVENT_CACHEOVERMEM,
565                                            overmem_cleaning_action,
566                                            cleaner, sizeof(isc_event_t));
567                 if (cleaner->overmem_event == NULL) {
568                         result = ISC_R_NOMEMORY;
569                         goto cleanup;
570                 }
571         }
572
573         return (ISC_R_SUCCESS);
574
575  cleanup:
576         if (cleaner->overmem_event != NULL)
577                 isc_event_free(&cleaner->overmem_event);
578         if (cleaner->resched_event != NULL)
579                 isc_event_free(&cleaner->resched_event);
580         if (cleaner->cleaning_timer != NULL)
581                 isc_timer_detach(&cleaner->cleaning_timer);
582         if (cleaner->task != NULL)
583                 isc_task_detach(&cleaner->task);
584         if (cleaner->iterator != NULL)
585                 dns_dbiterator_destroy(&cleaner->iterator);
586         DESTROYLOCK(&cleaner->lock);
587  fail:
588         return (result);
589 }
590
591 static void
592 begin_cleaning(cache_cleaner_t *cleaner) {
593         isc_result_t result = ISC_R_SUCCESS;
594
595         REQUIRE(CLEANER_IDLE(cleaner));
596
597         /*
598          * Create an iterator, if it does not already exist, and
599          * position it at the beginning of the cache.
600          */
601         if (cleaner->iterator == NULL)
602                 result = dns_db_createiterator(cleaner->cache->db, ISC_FALSE,
603                                                &cleaner->iterator);
604         if (result != ISC_R_SUCCESS)
605                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
606                               DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
607                               "cache cleaner could not create "
608                               "iterator: %s", isc_result_totext(result));
609         else {
610                 dns_dbiterator_setcleanmode(cleaner->iterator, ISC_TRUE);
611                 result = dns_dbiterator_first(cleaner->iterator);
612         }
613         if (result != ISC_R_SUCCESS) {
614                 /*
615                  * If the result is ISC_R_NOMORE, the database is empty,
616                  * so there is nothing to be cleaned.
617                  */
618                 if (result != ISC_R_NOMORE && cleaner->iterator != NULL) {
619                         UNEXPECTED_ERROR(__FILE__, __LINE__,
620                                          "cache cleaner: "
621                                          "dns_dbiterator_first() failed: %s",
622                                          dns_result_totext(result));
623                         dns_dbiterator_destroy(&cleaner->iterator);
624                 } else if (cleaner->iterator != NULL) {
625                         result = dns_dbiterator_pause(cleaner->iterator);
626                         RUNTIME_CHECK(result == ISC_R_SUCCESS);
627                 }
628         } else {
629                 /*
630                  * Pause the iterator to free its lock.
631                  */
632                 result = dns_dbiterator_pause(cleaner->iterator);
633                 RUNTIME_CHECK(result == ISC_R_SUCCESS);
634
635                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
636                               DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1),
637                               "begin cache cleaning, mem inuse %lu",
638                             (unsigned long)isc_mem_inuse(cleaner->cache->mctx));
639                 cleaner->state = cleaner_s_busy;
640                 isc_task_send(cleaner->task, &cleaner->resched_event);
641         }
642
643         return;
644 }
645
646 static void
647 end_cleaning(cache_cleaner_t *cleaner, isc_event_t *event) {
648         isc_result_t result;
649
650         REQUIRE(CLEANER_BUSY(cleaner));
651         REQUIRE(event != NULL);
652
653         result = dns_dbiterator_pause(cleaner->iterator);
654         if (result != ISC_R_SUCCESS)
655                 dns_dbiterator_destroy(&cleaner->iterator);
656
657         dns_cache_setcleaninginterval(cleaner->cache,
658                                       cleaner->cleaning_interval);
659
660         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
661                       ISC_LOG_DEBUG(1), "end cache cleaning, mem inuse %lu",
662                       (unsigned long)isc_mem_inuse(cleaner->cache->mctx));
663
664         cleaner->state = cleaner_s_idle;
665         cleaner->resched_event = event;
666 }
667
668 /*
669  * This is run once for every cache-cleaning-interval as defined in named.conf.
670  */
671 static void
672 cleaning_timer_action(isc_task_t *task, isc_event_t *event) {
673         cache_cleaner_t *cleaner = event->ev_arg;
674
675         UNUSED(task);
676
677         INSIST(task == cleaner->task);
678         INSIST(event->ev_type == ISC_TIMEREVENT_TICK);
679
680         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
681                       ISC_LOG_DEBUG(1), "cache cleaning timer fired, "
682                       "cleaner state = %d", cleaner->state);
683
684         if (cleaner->state == cleaner_s_idle)
685                 begin_cleaning(cleaner);
686
687         isc_event_free(&event);
688 }
689
690 /*
691  * This is called when the cache either surpasses its upper limit
692  * or shrinks beyond its lower limit.
693  */
694 static void
695 overmem_cleaning_action(isc_task_t *task, isc_event_t *event) {
696         cache_cleaner_t *cleaner = event->ev_arg;
697         isc_boolean_t want_cleaning = ISC_FALSE;
698         
699         UNUSED(task);
700
701         INSIST(task == cleaner->task);
702         INSIST(event->ev_type == DNS_EVENT_CACHEOVERMEM);
703         INSIST(cleaner->overmem_event == NULL);
704
705         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
706                       ISC_LOG_DEBUG(1), "overmem_cleaning_action called, "
707                       "overmem = %d, state = %d", cleaner->overmem,
708                       cleaner->state);
709
710         LOCK(&cleaner->lock);
711
712         if (cleaner->overmem) {
713                 if (cleaner->state == cleaner_s_idle)
714                         want_cleaning = ISC_TRUE;
715         } else {
716                 if (cleaner->state == cleaner_s_busy)
717                         /*
718                          * end_cleaning() can't be called here because
719                          * then both cleaner->overmem_event and
720                          * cleaner->resched_event will point to this
721                          * event.  Set the state to done, and then
722                          * when the incremental_cleaning_action() event
723                          * is posted, it will handle the end_cleaning.
724                          */
725                         cleaner->state = cleaner_s_done;
726         }
727
728         cleaner->overmem_event = event;
729
730         UNLOCK(&cleaner->lock);
731
732         if (want_cleaning)
733                 begin_cleaning(cleaner);
734 }
735
736 /*
737  * Do incremental cleaning.
738  */
739 static void
740 incremental_cleaning_action(isc_task_t *task, isc_event_t *event) {
741         cache_cleaner_t *cleaner = event->ev_arg;
742         isc_result_t result;
743         int n_names;
744
745         UNUSED(task);
746
747         INSIST(task == cleaner->task);
748         INSIST(event->ev_type == DNS_EVENT_CACHECLEAN);
749
750         if (cleaner->state == cleaner_s_done) {
751                 cleaner->state = cleaner_s_busy;
752                 end_cleaning(cleaner, event);
753                 LOCK(&cleaner->cache->lock);
754                 LOCK(&cleaner->lock);
755                 if (cleaner->replaceiterator) {
756                         dns_dbiterator_destroy(&cleaner->iterator);
757                         (void) dns_db_createiterator(cleaner->cache->db,
758                                                      ISC_FALSE,
759                                                      &cleaner->iterator);
760                         cleaner->replaceiterator = ISC_FALSE;
761                 }
762                 UNLOCK(&cleaner->lock);
763                 UNLOCK(&cleaner->cache->lock);
764                 return;
765         }
766
767         INSIST(CLEANER_BUSY(cleaner));
768
769         n_names = cleaner->increment;
770
771         REQUIRE(DNS_DBITERATOR_VALID(cleaner->iterator));
772
773         while (n_names-- > 0) {
774                 dns_dbnode_t *node = NULL;
775
776                 result = dns_dbiterator_current(cleaner->iterator, &node,
777                                                 NULL);
778                 if (result != ISC_R_SUCCESS) {
779                         UNEXPECTED_ERROR(__FILE__, __LINE__,
780                                  "cache cleaner: dns_dbiterator_current() "
781                                  "failed: %s", dns_result_totext(result));
782
783                         end_cleaning(cleaner, event);
784                         return;
785                 }
786
787                 /*
788                  * The node was not needed, but was required by
789                  * dns_dbiterator_current().  Give up its reference.
790                  */
791                 dns_db_detachnode(cleaner->cache->db, &node);
792
793                 /*
794                  * Step to the next node.
795                  */
796                 result = dns_dbiterator_next(cleaner->iterator);
797
798                 if (result != ISC_R_SUCCESS) {
799                         /*
800                          * Either the end was reached (ISC_R_NOMORE) or
801                          * some error was signaled.  If the cache is still
802                          * overmem and no error was encountered,
803                          * keep trying to clean it, otherwise stop cleaning.
804                          */
805                         if (result != ISC_R_NOMORE)
806                                 UNEXPECTED_ERROR(__FILE__, __LINE__,
807                                                  "cache cleaner: "
808                                                  "dns_dbiterator_next() "
809                                                  "failed: %s",
810                                                  dns_result_totext(result));
811                         else if (cleaner->overmem) {
812                                 result = dns_dbiterator_first(cleaner->
813                                                               iterator);
814                                 if (result == ISC_R_SUCCESS) {
815                                         isc_log_write(dns_lctx,
816                                                       DNS_LOGCATEGORY_DATABASE,
817                                                       DNS_LOGMODULE_CACHE,
818                                                       ISC_LOG_DEBUG(1),
819                                                       "cache cleaner: "
820                                                       "still overmem, "
821                                                       "reset and try again");
822                                         continue;
823                                 }
824                         }
825
826                         end_cleaning(cleaner, event);
827                         return;
828                 }
829         }
830
831         /*
832          * We have successfully performed a cleaning increment but have
833          * not gone through the entire cache.  Free the iterator locks
834          * and reschedule another batch.  If it fails, just try to continue
835          * anyway.
836          */
837         result = dns_dbiterator_pause(cleaner->iterator);
838         RUNTIME_CHECK(result == ISC_R_SUCCESS);
839
840         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
841                       ISC_LOG_DEBUG(1), "cache cleaner: checked %d nodes, "
842                       "mem inuse %lu, sleeping", cleaner->increment,
843                       (unsigned long)isc_mem_inuse(cleaner->cache->mctx));
844
845         isc_task_send(task, &event);
846         INSIST(CLEANER_BUSY(cleaner));
847         return;
848 }
849
850 /*
851  * Do immediate cleaning.
852  */
853 isc_result_t
854 dns_cache_clean(dns_cache_t *cache, isc_stdtime_t now) {
855         isc_result_t result;
856         dns_dbiterator_t *iterator = NULL;
857
858         REQUIRE(VALID_CACHE(cache));
859
860         result = dns_db_createiterator(cache->db, ISC_FALSE, &iterator);
861         if (result != ISC_R_SUCCESS)
862                 return result;
863
864         result = dns_dbiterator_first(iterator);
865
866         while (result == ISC_R_SUCCESS) {
867                 dns_dbnode_t *node = NULL;
868                 result = dns_dbiterator_current(iterator, &node,
869                                                 (dns_name_t *)NULL);
870                 if (result != ISC_R_SUCCESS)
871                         break;
872
873                 /*
874                  * Check TTLs, mark expired rdatasets stale.
875                  */
876                 result = dns_db_expirenode(cache->db, node, now);
877                 if (result != ISC_R_SUCCESS) {
878                         UNEXPECTED_ERROR(__FILE__, __LINE__,
879                                          "cache cleaner: dns_db_expirenode() "
880                                          "failed: %s",
881                                          dns_result_totext(result));
882                         /*
883                          * Continue anyway.
884                          */
885                 }
886
887                 /*
888                  * This is where the actual freeing takes place.
889                  */
890                 dns_db_detachnode(cache->db, &node);
891
892                 result = dns_dbiterator_next(iterator);
893         }
894
895         dns_dbiterator_destroy(&iterator);
896
897         if (result == ISC_R_NOMORE)
898                 result = ISC_R_SUCCESS;
899
900         return (result);
901 }
902
903 static void
904 water(void *arg, int mark) {
905         dns_cache_t *cache = arg;
906         isc_boolean_t overmem = ISC_TF(mark == ISC_MEM_HIWATER);
907
908         REQUIRE(VALID_CACHE(cache));
909
910         LOCK(&cache->cleaner.lock);
911         
912         dns_db_overmem(cache->db, overmem);
913         cache->cleaner.overmem = overmem;
914
915         if (cache->cleaner.overmem_event != NULL)
916                 isc_task_send(cache->cleaner.task,
917                               &cache->cleaner.overmem_event);
918
919         UNLOCK(&cache->cleaner.lock);
920 }
921
922 void
923 dns_cache_setcachesize(dns_cache_t *cache, isc_uint32_t size) {
924         isc_uint32_t lowater;
925         isc_uint32_t hiwater;
926
927         REQUIRE(VALID_CACHE(cache));
928
929         /*
930          * Impose a minumum cache size; pathological things happen if there
931          * is too little room.
932          */
933         if (size != 0 && size < DNS_CACHE_MINSIZE)
934                 size = DNS_CACHE_MINSIZE;
935
936         hiwater = size - (size >> 3);   /* Approximately 7/8ths. */
937         lowater = size - (size >> 2);   /* Approximately 3/4ths. */
938
939         /*
940          * If the cache was overmem and cleaning, but now with the new limits
941          * it is no longer in an overmem condition, then the next
942          * isc_mem_put for cache memory will do the right thing and trigger
943          * water().
944          */
945
946         if (size == 0 || hiwater == 0 || lowater == 0)
947                 /*
948                  * Disable cache memory limiting.
949                  */
950                 isc_mem_setwater(cache->mctx, water, cache, 0, 0);
951         else
952                 /*
953                  * Establish new cache memory limits (either for the first
954                  * time, or replacing other limits).
955                  */
956                 isc_mem_setwater(cache->mctx, water, cache, hiwater, lowater);
957 }
958
959 /*
960  * The cleaner task is shutting down; do the necessary cleanup.
961  */
962 static void
963 cleaner_shutdown_action(isc_task_t *task, isc_event_t *event) {
964         dns_cache_t *cache = event->ev_arg;
965         isc_boolean_t should_free = ISC_FALSE;
966
967         UNUSED(task);
968
969         INSIST(task == cache->cleaner.task);
970         INSIST(event->ev_type == ISC_TASKEVENT_SHUTDOWN);
971
972         if (CLEANER_BUSY(&cache->cleaner))
973                 end_cleaning(&cache->cleaner, event);
974         else
975                 isc_event_free(&event);
976
977         LOCK(&cache->lock);
978
979         cache->live_tasks--;
980         INSIST(cache->live_tasks == 0);
981
982         if (cache->references == 0)
983                 should_free = ISC_TRUE;
984
985         /*
986          * By detaching the timer in the context of its task,
987          * we are guaranteed that there will be no further timer
988          * events.
989          */
990         if (cache->cleaner.cleaning_timer != NULL)
991                 isc_timer_detach(&cache->cleaner.cleaning_timer);
992
993         /* Make sure we don't reschedule anymore. */
994         (void)isc_task_purge(task, NULL, DNS_EVENT_CACHECLEAN, NULL);
995
996         UNLOCK(&cache->lock);
997
998         if (should_free)
999                 cache_free(cache);
1000 }
1001
1002 isc_result_t
1003 dns_cache_flush(dns_cache_t *cache) {
1004         dns_db_t *db = NULL;
1005         isc_result_t result;
1006
1007         result = cache_create_db(cache, &db);
1008         if (result != ISC_R_SUCCESS)
1009                 return (result);
1010
1011         LOCK(&cache->lock);
1012         LOCK(&cache->cleaner.lock);
1013         if (cache->cleaner.state == cleaner_s_idle) {
1014                 if (cache->cleaner.iterator != NULL)
1015                         dns_dbiterator_destroy(&cache->cleaner.iterator);
1016                 (void) dns_db_createiterator(db, ISC_FALSE,
1017                                              &cache->cleaner.iterator);
1018         } else {
1019                 if (cache->cleaner.state == cleaner_s_busy)
1020                         cache->cleaner.state = cleaner_s_done;
1021                 cache->cleaner.replaceiterator = ISC_TRUE;
1022         }
1023         dns_db_detach(&cache->db);
1024         cache->db = db;
1025         UNLOCK(&cache->cleaner.lock);
1026         UNLOCK(&cache->lock);
1027
1028         return (ISC_R_SUCCESS);
1029 }
1030
1031 isc_result_t
1032 dns_cache_flushname(dns_cache_t *cache, dns_name_t *name) {
1033         isc_result_t result;
1034         dns_rdatasetiter_t *iter = NULL;
1035         dns_dbnode_t *node = NULL;
1036         dns_db_t *db = NULL;
1037         
1038         LOCK(&cache->lock);
1039         if (cache->db != NULL)
1040                 dns_db_attach(cache->db, &db);
1041         UNLOCK(&cache->lock);
1042         if (db == NULL)
1043                 return (ISC_R_SUCCESS);
1044         result = dns_db_findnode(cache->db, name, ISC_FALSE, &node);
1045         if (result == ISC_R_NOTFOUND) {
1046                 result = ISC_R_SUCCESS;
1047                 goto cleanup_db;
1048         }
1049         if (result != ISC_R_SUCCESS)
1050                 goto cleanup_db;
1051
1052         result = dns_db_allrdatasets(cache->db, node, NULL,
1053                                      (isc_stdtime_t)0, &iter);
1054         if (result != ISC_R_SUCCESS)
1055                 goto cleanup_node;
1056
1057         for (result = dns_rdatasetiter_first(iter);
1058              result == ISC_R_SUCCESS;
1059              result = dns_rdatasetiter_next(iter))
1060         {
1061                 dns_rdataset_t rdataset;
1062                 dns_rdataset_init(&rdataset);
1063
1064                 dns_rdatasetiter_current(iter, &rdataset);
1065                 result = dns_db_deleterdataset(cache->db, node, NULL,
1066                                                rdataset.type, rdataset.covers);
1067                 dns_rdataset_disassociate(&rdataset);
1068                 if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED)
1069                         break;
1070         }
1071         if (result == ISC_R_NOMORE)
1072                 result = ISC_R_SUCCESS;
1073
1074         dns_rdatasetiter_destroy(&iter);
1075
1076  cleanup_node:
1077         dns_db_detachnode(cache->db, &node);
1078
1079  cleanup_db:
1080         dns_db_detach(&db);
1081         return (result);
1082 }