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