Import of bind-9.3.2-P1
[dragonfly.git] / contrib / bind-9.3 / lib / dns / cache.c
1 /*
2  * Copyright (C) 2004, 2005  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.9 2005/03/17 03:58:30 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)->iterator == NULL && \
72                          (c)->resched_event != NULL)
73 #define CLEANER_BUSY(c) ((c)->state == cleaner_s_busy && \
74                          (c)->iterator != NULL && \
75                          (c)->resched_event == NULL)
76
77 /*
78  * Accesses to a cache cleaner object are synchronized through
79  * task/event serialization, or locked from the cache object.
80  */
81 struct cache_cleaner {
82         isc_mutex_t     lock;
83         /*
84          * Locks overmem_event, overmem.  Note: never allocate memory
85          * while holding this lock - that could lead to deadlock since
86          * the lock is take by water() which is called from the memory
87          * allocator.
88          */
89
90         dns_cache_t     *cache;
91         isc_task_t      *task;
92         unsigned int    cleaning_interval; /* The cleaning-interval from
93                                               named.conf, in seconds. */
94         isc_timer_t     *cleaning_timer;
95         isc_event_t     *resched_event; /* Sent by cleaner task to
96                                            itself to reschedule */
97         isc_event_t     *overmem_event;
98
99         dns_dbiterator_t *iterator;
100         int              increment;     /* Number of names to
101                                            clean in one increment */
102         cleaner_state_t  state;         /* Idle/Busy. */
103         isc_boolean_t    overmem;       /* The cache is in an overmem state. */
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, 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
505         cleaner->task = NULL;
506         cleaner->cleaning_timer = NULL;
507         cleaner->resched_event = NULL;
508         cleaner->overmem_event = NULL;
509
510         if (taskmgr != NULL && timermgr != NULL) {
511                 result = isc_task_create(taskmgr, 1, &cleaner->task);
512                 if (result != ISC_R_SUCCESS) {
513                         UNEXPECTED_ERROR(__FILE__, __LINE__,
514                                          "isc_task_create() failed: %s",
515                                          dns_result_totext(result));
516                         result = ISC_R_UNEXPECTED;
517                         goto cleanup;
518                 }
519                 cleaner->cache->live_tasks++;
520                 isc_task_setname(cleaner->task, "cachecleaner", cleaner);
521
522                 result = isc_task_onshutdown(cleaner->task,
523                                              cleaner_shutdown_action, cache);
524                 if (result != ISC_R_SUCCESS) {
525                         UNEXPECTED_ERROR(__FILE__, __LINE__,
526                                          "cache cleaner: "
527                                          "isc_task_onshutdown() failed: %s",
528                                          dns_result_totext(result));
529                         goto cleanup;
530                 }
531
532                 cleaner->cleaning_interval = 0; /* Initially turned off. */
533                 result = isc_timer_create(timermgr, isc_timertype_inactive,
534                                            NULL, NULL,
535                                            cleaner->task,
536                                            cleaning_timer_action, cleaner,
537                                            &cleaner->cleaning_timer);
538                 if (result != ISC_R_SUCCESS) {
539                         UNEXPECTED_ERROR(__FILE__, __LINE__,
540                                          "isc_timer_create() failed: %s",
541                                          dns_result_totext(result));
542                         result = ISC_R_UNEXPECTED;
543                         goto cleanup;
544                 }
545
546                 cleaner->resched_event =
547                         isc_event_allocate(cache->mctx, cleaner,
548                                            DNS_EVENT_CACHECLEAN,
549                                            incremental_cleaning_action,
550                                            cleaner, sizeof(isc_event_t));
551                 if (cleaner->resched_event == NULL) {
552                         result = ISC_R_NOMEMORY;
553                         goto cleanup;
554                 }
555                 
556                 cleaner->overmem_event =
557                         isc_event_allocate(cache->mctx, cleaner,
558                                            DNS_EVENT_CACHEOVERMEM,
559                                            overmem_cleaning_action,
560                                            cleaner, sizeof(isc_event_t));
561                 if (cleaner->overmem_event == NULL) {
562                         result = ISC_R_NOMEMORY;
563                         goto cleanup;
564                 }
565         }
566
567         return (ISC_R_SUCCESS);
568
569  cleanup:
570         if (cleaner->overmem_event != NULL)
571                 isc_event_free(&cleaner->overmem_event);
572         if (cleaner->resched_event != NULL)
573                 isc_event_free(&cleaner->resched_event);
574         if (cleaner->cleaning_timer != NULL)
575                 isc_timer_detach(&cleaner->cleaning_timer);
576         if (cleaner->task != NULL)
577                 isc_task_detach(&cleaner->task);
578         DESTROYLOCK(&cleaner->lock);
579  fail:
580         return (result);
581 }
582
583 static void
584 begin_cleaning(cache_cleaner_t *cleaner) {
585         isc_result_t result;
586
587         REQUIRE(CLEANER_IDLE(cleaner));
588
589         /*
590          * Create an iterator and position it at the beginning of the cache.
591          */
592         result = dns_db_createiterator(cleaner->cache->db, ISC_FALSE,
593                                        &cleaner->iterator);
594         if (result != ISC_R_SUCCESS)
595                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
596                               DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
597                               "cache cleaner could not create "
598                               "iterator: %s", isc_result_totext(result));
599         else {
600                 dns_dbiterator_setcleanmode(cleaner->iterator, ISC_TRUE);
601                 result = dns_dbiterator_first(cleaner->iterator);
602         }
603
604         if (result != ISC_R_SUCCESS) {
605                 /*
606                  * If the result is ISC_R_NOMORE, the database is empty,
607                  * so there is nothing to be cleaned.
608                  */
609                 if (result != ISC_R_NOMORE)
610                         UNEXPECTED_ERROR(__FILE__, __LINE__,
611                                          "cache cleaner: "
612                                          "dns_dbiterator_first() failed: %s",
613                                          dns_result_totext(result));
614
615                 if (cleaner->iterator != NULL)
616                         dns_dbiterator_destroy(&cleaner->iterator);
617         } else {
618                 /*
619                  * Pause the iterator to free its lock.
620                  */
621                 result = dns_dbiterator_pause(cleaner->iterator);
622                 RUNTIME_CHECK(result == ISC_R_SUCCESS);
623
624                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
625                               DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1),
626                               "begin cache cleaning, mem inuse %lu",
627                             (unsigned long)isc_mem_inuse(cleaner->cache->mctx));
628                 cleaner->state = cleaner_s_busy;
629                 isc_task_send(cleaner->task, &cleaner->resched_event);
630         }
631
632         return;
633 }
634
635 static void
636 end_cleaning(cache_cleaner_t *cleaner, isc_event_t *event) {
637         REQUIRE(CLEANER_BUSY(cleaner));
638         REQUIRE(event != NULL);
639
640         dns_dbiterator_destroy(&cleaner->iterator);
641
642         dns_cache_setcleaninginterval(cleaner->cache,
643                                       cleaner->cleaning_interval);
644
645         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
646                       ISC_LOG_DEBUG(1), "end cache cleaning, mem inuse %lu",
647                       (unsigned long)isc_mem_inuse(cleaner->cache->mctx));
648
649         cleaner->state = cleaner_s_idle;
650         cleaner->resched_event = event;
651 }
652
653 /*
654  * This is run once for every cache-cleaning-interval as defined in named.conf.
655  */
656 static void
657 cleaning_timer_action(isc_task_t *task, isc_event_t *event) {
658         cache_cleaner_t *cleaner = event->ev_arg;
659
660         UNUSED(task);
661
662         INSIST(task == cleaner->task);
663         INSIST(event->ev_type == ISC_TIMEREVENT_TICK);
664
665         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
666                       ISC_LOG_DEBUG(1), "cache cleaning timer fired, "
667                       "cleaner state = %d", cleaner->state);
668
669         if (cleaner->state == cleaner_s_idle)
670                 begin_cleaning(cleaner);
671
672         isc_event_free(&event);
673 }
674
675 /*
676  * This is called when the cache either surpasses its upper limit
677  * or shrinks beyond its lower limit.
678  */
679 static void
680 overmem_cleaning_action(isc_task_t *task, isc_event_t *event) {
681         cache_cleaner_t *cleaner = event->ev_arg;
682         isc_boolean_t want_cleaning = ISC_FALSE;
683         
684         UNUSED(task);
685
686         INSIST(task == cleaner->task);
687         INSIST(event->ev_type == DNS_EVENT_CACHEOVERMEM);
688         INSIST(cleaner->overmem_event == NULL);
689
690         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
691                       ISC_LOG_DEBUG(1), "overmem_cleaning_action called, "
692                       "overmem = %d, state = %d", cleaner->overmem,
693                       cleaner->state);
694
695         LOCK(&cleaner->lock);
696
697         if (cleaner->overmem) {
698                 if (cleaner->state == cleaner_s_idle)
699                         want_cleaning = ISC_TRUE;
700         } else {
701                 if (cleaner->state == cleaner_s_busy)
702                         /*
703                          * end_cleaning() can't be called here because
704                          * then both cleaner->overmem_event and
705                          * cleaner->resched_event will point to this
706                          * event.  Set the state to done, and then
707                          * when the incremental_cleaning_action() event
708                          * is posted, it will handle the end_cleaning.
709                          */
710                         cleaner->state = cleaner_s_done;
711         }
712
713         cleaner->overmem_event = event;
714
715         UNLOCK(&cleaner->lock);
716
717         if (want_cleaning)
718                 begin_cleaning(cleaner);
719 }
720
721 /*
722  * Do incremental cleaning.
723  */
724 static void
725 incremental_cleaning_action(isc_task_t *task, isc_event_t *event) {
726         cache_cleaner_t *cleaner = event->ev_arg;
727         isc_result_t result;
728         int n_names;
729
730         UNUSED(task);
731
732         INSIST(task == cleaner->task);
733         INSIST(event->ev_type == DNS_EVENT_CACHECLEAN);
734
735         if (cleaner->state == cleaner_s_done) {
736                 cleaner->state = cleaner_s_busy;
737                 end_cleaning(cleaner, event);
738                 return;
739         }
740
741         INSIST(CLEANER_BUSY(cleaner));
742
743         n_names = cleaner->increment;
744
745         REQUIRE(DNS_DBITERATOR_VALID(cleaner->iterator));
746
747         while (n_names-- > 0) {
748                 dns_dbnode_t *node = NULL;
749
750                 result = dns_dbiterator_current(cleaner->iterator, &node,
751                                                 NULL);
752                 if (result != ISC_R_SUCCESS) {
753                         UNEXPECTED_ERROR(__FILE__, __LINE__,
754                                  "cache cleaner: dns_dbiterator_current() "
755                                  "failed: %s", dns_result_totext(result));
756
757                         end_cleaning(cleaner, event);
758                         return;
759                 }
760
761                 /*
762                  * The node was not needed, but was required by
763                  * dns_dbiterator_current().  Give up its reference.
764                  */
765                 dns_db_detachnode(cleaner->cache->db, &node);
766
767                 /*
768                  * Step to the next node.
769                  */
770                 result = dns_dbiterator_next(cleaner->iterator);
771
772                 if (result != ISC_R_SUCCESS) {
773                         /*
774                          * Either the end was reached (ISC_R_NOMORE) or
775                          * some error was signaled.  If the cache is still
776                          * overmem and no error was encountered,
777                          * keep trying to clean it, otherwise stop cleanng.
778                          */
779                         if (result != ISC_R_NOMORE)
780                                 UNEXPECTED_ERROR(__FILE__, __LINE__,
781                                                  "cache cleaner: "
782                                                  "dns_dbiterator_next() "
783                                                  "failed: %s",
784                                                  dns_result_totext(result));
785                         else if (cleaner->overmem) {
786                                 result = dns_dbiterator_first(cleaner->
787                                                               iterator);
788                                 if (result == ISC_R_SUCCESS) {
789                                         isc_log_write(dns_lctx,
790                                                       DNS_LOGCATEGORY_DATABASE,
791                                                       DNS_LOGMODULE_CACHE,
792                                                       ISC_LOG_DEBUG(1),
793                                                       "cache cleaner: "
794                                                       "still overmem, "
795                                                       "reset and try again");
796                                         continue;
797                                 }
798                         }
799
800                         end_cleaning(cleaner, event);
801                         return;
802                 }
803         }
804
805         /*
806          * We have successfully performed a cleaning increment but have
807          * not gone through the entire cache.  Free the iterator locks
808          * and reschedule another batch.  If it fails, just try to continue
809          * anyway.
810          */
811         result = dns_dbiterator_pause(cleaner->iterator);
812         RUNTIME_CHECK(result == ISC_R_SUCCESS);
813
814         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
815                       ISC_LOG_DEBUG(1), "cache cleaner: checked %d nodes, "
816                       "mem inuse %lu, sleeping", cleaner->increment,
817                       (unsigned long)isc_mem_inuse(cleaner->cache->mctx));
818
819         isc_task_send(task, &event);
820         INSIST(CLEANER_BUSY(cleaner));
821         return;
822 }
823
824 /*
825  * Do immediate cleaning.
826  */
827 isc_result_t
828 dns_cache_clean(dns_cache_t *cache, isc_stdtime_t now) {
829         isc_result_t result;
830         dns_dbiterator_t *iterator = NULL;
831
832         REQUIRE(VALID_CACHE(cache));
833
834         result = dns_db_createiterator(cache->db, ISC_FALSE, &iterator);
835         if (result != ISC_R_SUCCESS)
836                 return result;
837
838         result = dns_dbiterator_first(iterator);
839
840         while (result == ISC_R_SUCCESS) {
841                 dns_dbnode_t *node = NULL;
842                 result = dns_dbiterator_current(iterator, &node,
843                                                 (dns_name_t *)NULL);
844                 if (result != ISC_R_SUCCESS)
845                         break;
846
847                 /*
848                  * Check TTLs, mark expired rdatasets stale.
849                  */
850                 result = dns_db_expirenode(cache->db, node, now);
851                 if (result != ISC_R_SUCCESS) {
852                         UNEXPECTED_ERROR(__FILE__, __LINE__,
853                                          "cache cleaner: dns_db_expirenode() "
854                                          "failed: %s",
855                                          dns_result_totext(result));
856                         /*
857                          * Continue anyway.
858                          */
859                 }
860
861                 /*
862                  * This is where the actual freeing takes place.
863                  */
864                 dns_db_detachnode(cache->db, &node);
865
866                 result = dns_dbiterator_next(iterator);
867         }
868
869         dns_dbiterator_destroy(&iterator);
870
871         if (result == ISC_R_NOMORE)
872                 result = ISC_R_SUCCESS;
873
874         return (result);
875 }
876
877 static void
878 water(void *arg, int mark) {
879         dns_cache_t *cache = arg;
880         isc_boolean_t overmem = ISC_TF(mark == ISC_MEM_HIWATER);
881
882         REQUIRE(VALID_CACHE(cache));
883
884         LOCK(&cache->cleaner.lock);
885         
886         dns_db_overmem(cache->db, overmem);
887         cache->cleaner.overmem = overmem;
888
889         if (cache->cleaner.overmem_event != NULL)
890                 isc_task_send(cache->cleaner.task,
891                               &cache->cleaner.overmem_event);
892
893         UNLOCK(&cache->cleaner.lock);
894 }
895
896 void
897 dns_cache_setcachesize(dns_cache_t *cache, isc_uint32_t size) {
898         isc_uint32_t lowater;
899         isc_uint32_t hiwater;
900
901         REQUIRE(VALID_CACHE(cache));
902
903         /*
904          * Impose a minumum cache size; pathological things happen if there
905          * is too little room.
906          */
907         if (size != 0 && size < DNS_CACHE_MINSIZE)
908                 size = DNS_CACHE_MINSIZE;
909
910         hiwater = size - (size >> 3);   /* Approximately 7/8ths. */
911         lowater = size - (size >> 2);   /* Approximately 3/4ths. */
912
913         /*
914          * If the cache was overmem and cleaning, but now with the new limits
915          * it is no longer in an overmem condition, then the next
916          * isc_mem_put for cache memory will do the right thing and trigger
917          * water().
918          */
919
920         if (size == 0 || hiwater == 0 || lowater == 0)
921                 /*
922                  * Disable cache memory limiting.
923                  */
924                 isc_mem_setwater(cache->mctx, water, cache, 0, 0);
925         else
926                 /*
927                  * Establish new cache memory limits (either for the first
928                  * time, or replacing other limits).
929                  */
930                 isc_mem_setwater(cache->mctx, water, cache, hiwater, lowater);
931 }
932
933 /*
934  * The cleaner task is shutting down; do the necessary cleanup.
935  */
936 static void
937 cleaner_shutdown_action(isc_task_t *task, isc_event_t *event) {
938         dns_cache_t *cache = event->ev_arg;
939         isc_boolean_t should_free = ISC_FALSE;
940
941         UNUSED(task);
942
943         INSIST(task == cache->cleaner.task);
944         INSIST(event->ev_type == ISC_TASKEVENT_SHUTDOWN);
945
946         if (CLEANER_BUSY(&cache->cleaner))
947                 end_cleaning(&cache->cleaner, event);
948         else
949                 isc_event_free(&event);
950
951         LOCK(&cache->lock);
952
953         cache->live_tasks--;
954         INSIST(cache->live_tasks == 0);
955
956         if (cache->references == 0)
957                 should_free = ISC_TRUE;
958
959         /*
960          * By detaching the timer in the context of its task,
961          * we are guaranteed that there will be no further timer
962          * events.
963          */
964         if (cache->cleaner.cleaning_timer != NULL)
965                 isc_timer_detach(&cache->cleaner.cleaning_timer);
966
967         /* Make sure we don't reschedule anymore. */
968         (void)isc_task_purge(task, NULL, DNS_EVENT_CACHECLEAN, NULL);
969
970         UNLOCK(&cache->lock);
971
972         if (should_free)
973                 cache_free(cache);
974 }
975
976 isc_result_t
977 dns_cache_flush(dns_cache_t *cache) {
978         dns_db_t *db = NULL;
979         isc_result_t result;
980
981         result = cache_create_db(cache, &db);
982         if (result != ISC_R_SUCCESS)
983                 return (result);
984
985         dns_db_detach(&cache->db);
986         cache->db = db;
987         return (ISC_R_SUCCESS);
988 }
989
990 isc_result_t
991 dns_cache_flushname(dns_cache_t *cache, dns_name_t *name) {
992         isc_result_t result;
993         dns_rdatasetiter_t *iter = NULL;
994         dns_dbnode_t *node = NULL;
995         dns_db_t *db = NULL;
996         
997         LOCK(&cache->lock);
998         if (cache->db != NULL)
999                 dns_db_attach(cache->db, &db);
1000         UNLOCK(&cache->lock);
1001         if (db == NULL)
1002                 return (ISC_R_SUCCESS);
1003         result = dns_db_findnode(cache->db, name, ISC_FALSE, &node);
1004         if (result == ISC_R_NOTFOUND) {
1005                 result = ISC_R_SUCCESS;
1006                 goto cleanup_db;
1007         }
1008         if (result != ISC_R_SUCCESS)
1009                 goto cleanup_db;
1010
1011         result = dns_db_allrdatasets(cache->db, node, NULL,
1012                                      (isc_stdtime_t)0, &iter);
1013         if (result != ISC_R_SUCCESS)
1014                 goto cleanup_node;
1015
1016         for (result = dns_rdatasetiter_first(iter);
1017              result == ISC_R_SUCCESS;
1018              result = dns_rdatasetiter_next(iter))
1019         {
1020                 dns_rdataset_t rdataset;
1021                 dns_rdataset_init(&rdataset);
1022
1023                 dns_rdatasetiter_current(iter, &rdataset);
1024                 result = dns_db_deleterdataset(cache->db, node, NULL,
1025                                                rdataset.type, rdataset.covers);
1026                 dns_rdataset_disassociate(&rdataset);
1027                 if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED)
1028                         break;
1029         }
1030         if (result == ISC_R_NOMORE)
1031                 result = ISC_R_SUCCESS;
1032
1033         dns_rdatasetiter_destroy(&iter);
1034
1035  cleanup_node:
1036         dns_db_detachnode(cache->db, &node);
1037
1038  cleanup_db:
1039         dns_db_detach(&db);
1040         return (result);
1041 }