Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / external / bsd / bind / dist / lib / dns / cache.c
blobecebb7d8a48e74ffaf3cab2acbd308a1a11663ac
1 /* $NetBSD$ */
3 /*
4 * Copyright (C) 2004-2009 Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (C) 1999-2003 Internet Software Consortium.
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
20 /* Id: cache.c,v 1.87 2009/11/12 23:43:02 each Exp */
22 /*! \file */
24 #include <config.h>
26 #include <isc/mem.h>
27 #include <isc/string.h>
28 #include <isc/task.h>
29 #include <isc/time.h>
30 #include <isc/timer.h>
31 #include <isc/util.h>
33 #include <dns/cache.h>
34 #include <dns/db.h>
35 #include <dns/dbiterator.h>
36 #include <dns/events.h>
37 #include <dns/lib.h>
38 #include <dns/log.h>
39 #include <dns/masterdump.h>
40 #include <dns/rdata.h>
41 #include <dns/rdataset.h>
42 #include <dns/rdatasetiter.h>
43 #include <dns/result.h>
45 #define CACHE_MAGIC ISC_MAGIC('$', '$', '$', '$')
46 #define VALID_CACHE(cache) ISC_MAGIC_VALID(cache, CACHE_MAGIC)
48 /*!
49 * Control incremental cleaning.
50 * DNS_CACHE_MINSIZE is how many bytes is the floor for dns_cache_setcachesize().
51 * See also DNS_CACHE_CLEANERINCREMENT
53 #define DNS_CACHE_MINSIZE 2097152 /*%< Bytes. 2097152 = 2 MB */
54 /*!
55 * Control incremental cleaning.
56 * CLEANERINCREMENT is how many nodes are examined in one pass.
57 * See also DNS_CACHE_MINSIZE
59 #define DNS_CACHE_CLEANERINCREMENT 1000U /*%< Number of nodes. */
61 /***
62 *** Types
63 ***/
66 * A cache_cleaner_t encapsulates the state of the periodic
67 * cache cleaning.
70 typedef struct cache_cleaner cache_cleaner_t;
72 typedef enum {
73 cleaner_s_idle, /*%< Waiting for cleaning-interval to expire. */
74 cleaner_s_busy, /*%< Currently cleaning. */
75 cleaner_s_done /*%< Freed enough memory after being overmem. */
76 } cleaner_state_t;
79 * Convenience macros for comprehensive assertion checking.
81 #define CLEANER_IDLE(c) ((c)->state == cleaner_s_idle && \
82 (c)->resched_event != NULL)
83 #define CLEANER_BUSY(c) ((c)->state == cleaner_s_busy && \
84 (c)->iterator != NULL && \
85 (c)->resched_event == NULL)
87 /*%
88 * Accesses to a cache cleaner object are synchronized through
89 * task/event serialization, or locked from the cache object.
91 struct cache_cleaner {
92 isc_mutex_t lock;
93 /*%<
94 * Locks overmem_event, overmem. Note: never allocate memory
95 * while holding this lock - that could lead to deadlock since
96 * the lock is take by water() which is called from the memory
97 * allocator.
100 dns_cache_t *cache;
101 isc_task_t *task;
102 unsigned int cleaning_interval; /*% The cleaning-interval from
103 named.conf, in seconds. */
104 isc_timer_t *cleaning_timer;
105 isc_event_t *resched_event; /*% Sent by cleaner task to
106 itself to reschedule */
107 isc_event_t *overmem_event;
109 dns_dbiterator_t *iterator;
110 unsigned int increment; /*% Number of names to
111 clean in one increment */
112 cleaner_state_t state; /*% Idle/Busy. */
113 isc_boolean_t overmem; /*% The cache is in an overmem state. */
114 isc_boolean_t replaceiterator;
118 * The actual cache object.
121 struct dns_cache {
122 /* Unlocked. */
123 unsigned int magic;
124 isc_mutex_t lock;
125 isc_mutex_t filelock;
126 isc_mem_t *mctx;
127 char *name;
129 /* Locked by 'lock'. */
130 int references;
131 int live_tasks;
132 dns_rdataclass_t rdclass;
133 dns_db_t *db;
134 cache_cleaner_t cleaner;
135 char *db_type;
136 int db_argc;
137 char **db_argv;
138 isc_uint32_t size;
140 /* Locked by 'filelock'. */
141 char *filename;
142 /* Access to the on-disk cache file is also locked by 'filelock'. */
145 /***
146 *** Functions
147 ***/
149 static isc_result_t
150 cache_cleaner_init(dns_cache_t *cache, isc_taskmgr_t *taskmgr,
151 isc_timermgr_t *timermgr, cache_cleaner_t *cleaner);
153 static void
154 cleaning_timer_action(isc_task_t *task, isc_event_t *event);
156 static void
157 incremental_cleaning_action(isc_task_t *task, isc_event_t *event);
159 static void
160 cleaner_shutdown_action(isc_task_t *task, isc_event_t *event);
162 static void
163 overmem_cleaning_action(isc_task_t *task, isc_event_t *event);
165 static inline isc_result_t
166 cache_create_db(dns_cache_t *cache, dns_db_t **db) {
167 return (dns_db_create(cache->mctx, cache->db_type, dns_rootname,
168 dns_dbtype_cache, cache->rdclass,
169 cache->db_argc, cache->db_argv, db));
172 isc_result_t
173 dns_cache_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
174 isc_timermgr_t *timermgr, dns_rdataclass_t rdclass,
175 const char *db_type, unsigned int db_argc, char **db_argv,
176 dns_cache_t **cachep)
178 return (dns_cache_create2(mctx, taskmgr, timermgr, rdclass, "",
179 db_type, db_argc, db_argv, cachep));
182 isc_result_t
183 dns_cache_create2(isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
184 isc_timermgr_t *timermgr, dns_rdataclass_t rdclass,
185 const char *cachename, const char *db_type,
186 unsigned int db_argc, char **db_argv, dns_cache_t **cachep)
188 isc_result_t result;
189 dns_cache_t *cache;
190 int i;
191 isc_task_t *dbtask;
193 REQUIRE(cachep != NULL);
194 REQUIRE(*cachep == NULL);
195 REQUIRE(mctx != NULL);
196 REQUIRE(cachename != NULL);
198 cache = isc_mem_get(mctx, sizeof(*cache));
199 if (cache == NULL)
200 return (ISC_R_NOMEMORY);
202 cache->mctx = NULL;
203 isc_mem_attach(mctx, &cache->mctx);
205 cache->name = NULL;
206 if (cachename != NULL) {
207 cache->name = isc_mem_strdup(mctx, cachename);
208 if (cache->name == NULL) {
209 result = ISC_R_NOMEMORY;
210 goto cleanup_mem;
214 result = isc_mutex_init(&cache->lock);
215 if (result != ISC_R_SUCCESS)
216 goto cleanup_mem;
218 result = isc_mutex_init(&cache->filelock);
219 if (result != ISC_R_SUCCESS)
220 goto cleanup_lock;
222 cache->references = 1;
223 cache->live_tasks = 0;
224 cache->rdclass = rdclass;
226 cache->db_type = isc_mem_strdup(mctx, db_type);
227 if (cache->db_type == NULL) {
228 result = ISC_R_NOMEMORY;
229 goto cleanup_filelock;
232 cache->db_argc = db_argc;
233 if (cache->db_argc == 0)
234 cache->db_argv = NULL;
235 else {
236 cache->db_argv = isc_mem_get(mctx,
237 cache->db_argc * sizeof(char *));
238 if (cache->db_argv == NULL) {
239 result = ISC_R_NOMEMORY;
240 goto cleanup_dbtype;
242 for (i = 0; i < cache->db_argc; i++)
243 cache->db_argv[i] = NULL;
244 for (i = 0; i < cache->db_argc; i++) {
245 cache->db_argv[i] = isc_mem_strdup(mctx, db_argv[i]);
246 if (cache->db_argv[i] == NULL) {
247 result = ISC_R_NOMEMORY;
248 goto cleanup_dbargv;
253 cache->db = NULL;
254 result = cache_create_db(cache, &cache->db);
255 if (result != ISC_R_SUCCESS)
256 goto cleanup_dbargv;
257 if (taskmgr != NULL) {
258 dbtask = NULL;
259 result = isc_task_create(taskmgr, 1, &dbtask);
260 if (result != ISC_R_SUCCESS)
261 goto cleanup_db;
262 dns_db_settask(cache->db, dbtask);
263 isc_task_detach(&dbtask);
266 cache->filename = NULL;
268 cache->magic = CACHE_MAGIC;
271 * RBT-type cache DB has its own mechanism of cache cleaning and doesn't
272 * need the control of the generic cleaner.
274 if (strcmp(db_type, "rbt") == 0)
275 result = cache_cleaner_init(cache, NULL, NULL, &cache->cleaner);
276 else {
277 result = cache_cleaner_init(cache, taskmgr, timermgr,
278 &cache->cleaner);
280 if (result != ISC_R_SUCCESS)
281 goto cleanup_db;
283 *cachep = cache;
284 return (ISC_R_SUCCESS);
286 cleanup_db:
287 dns_db_detach(&cache->db);
288 cleanup_dbargv:
289 for (i = 0; i < cache->db_argc; i++)
290 if (cache->db_argv[i] != NULL)
291 isc_mem_free(mctx, cache->db_argv[i]);
292 if (cache->db_argv != NULL)
293 isc_mem_put(mctx, cache->db_argv,
294 cache->db_argc * sizeof(char *));
295 cleanup_dbtype:
296 isc_mem_free(mctx, cache->db_type);
297 cleanup_filelock:
298 DESTROYLOCK(&cache->filelock);
299 cleanup_lock:
300 DESTROYLOCK(&cache->lock);
301 cleanup_mem:
302 if (cache->name != NULL)
303 isc_mem_free(mctx, cache->name);
304 isc_mem_put(mctx, cache, sizeof(*cache));
305 isc_mem_detach(&mctx);
306 return (result);
309 static void
310 cache_free(dns_cache_t *cache) {
311 isc_mem_t *mctx;
312 int i;
314 REQUIRE(VALID_CACHE(cache));
315 REQUIRE(cache->references == 0);
317 isc_mem_setwater(cache->mctx, NULL, NULL, 0, 0);
319 if (cache->cleaner.task != NULL)
320 isc_task_detach(&cache->cleaner.task);
322 if (cache->cleaner.overmem_event != NULL)
323 isc_event_free(&cache->cleaner.overmem_event);
325 if (cache->cleaner.resched_event != NULL)
326 isc_event_free(&cache->cleaner.resched_event);
328 if (cache->cleaner.iterator != NULL)
329 dns_dbiterator_destroy(&cache->cleaner.iterator);
331 DESTROYLOCK(&cache->cleaner.lock);
333 if (cache->filename) {
334 isc_mem_free(cache->mctx, cache->filename);
335 cache->filename = NULL;
338 if (cache->db != NULL)
339 dns_db_detach(&cache->db);
341 if (cache->db_argv != NULL) {
342 for (i = 0; i < cache->db_argc; i++)
343 if (cache->db_argv[i] != NULL)
344 isc_mem_free(cache->mctx, cache->db_argv[i]);
345 isc_mem_put(cache->mctx, cache->db_argv,
346 cache->db_argc * sizeof(char *));
349 if (cache->db_type != NULL)
350 isc_mem_free(cache->mctx, cache->db_type);
352 if (cache->name != NULL)
353 isc_mem_free(cache->mctx, cache->name);
355 DESTROYLOCK(&cache->lock);
356 DESTROYLOCK(&cache->filelock);
357 cache->magic = 0;
358 mctx = cache->mctx;
359 isc_mem_put(cache->mctx, cache, sizeof(*cache));
360 isc_mem_detach(&mctx);
364 void
365 dns_cache_attach(dns_cache_t *cache, dns_cache_t **targetp) {
367 REQUIRE(VALID_CACHE(cache));
368 REQUIRE(targetp != NULL && *targetp == NULL);
370 LOCK(&cache->lock);
371 cache->references++;
372 UNLOCK(&cache->lock);
374 *targetp = cache;
377 void
378 dns_cache_detach(dns_cache_t **cachep) {
379 dns_cache_t *cache;
380 isc_boolean_t free_cache = ISC_FALSE;
382 REQUIRE(cachep != NULL);
383 cache = *cachep;
384 REQUIRE(VALID_CACHE(cache));
386 LOCK(&cache->lock);
387 REQUIRE(cache->references > 0);
388 cache->references--;
389 if (cache->references == 0) {
390 cache->cleaner.overmem = ISC_FALSE;
391 free_cache = ISC_TRUE;
394 *cachep = NULL;
396 if (free_cache) {
398 * When the cache is shut down, dump it to a file if one is
399 * specified.
401 isc_result_t result = dns_cache_dump(cache);
402 if (result != ISC_R_SUCCESS)
403 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
404 DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
405 "error dumping cache: %s ",
406 isc_result_totext(result));
409 * If the cleaner task exists, let it free the cache.
411 if (cache->live_tasks > 0) {
412 isc_task_shutdown(cache->cleaner.task);
413 free_cache = ISC_FALSE;
417 UNLOCK(&cache->lock);
419 if (free_cache)
420 cache_free(cache);
423 void
424 dns_cache_attachdb(dns_cache_t *cache, dns_db_t **dbp) {
425 REQUIRE(VALID_CACHE(cache));
426 REQUIRE(dbp != NULL && *dbp == NULL);
427 REQUIRE(cache->db != NULL);
429 LOCK(&cache->lock);
430 dns_db_attach(cache->db, dbp);
431 UNLOCK(&cache->lock);
435 isc_result_t
436 dns_cache_setfilename(dns_cache_t *cache, const char *filename) {
437 char *newname;
439 REQUIRE(VALID_CACHE(cache));
440 REQUIRE(filename != NULL);
442 newname = isc_mem_strdup(cache->mctx, filename);
443 if (newname == NULL)
444 return (ISC_R_NOMEMORY);
446 LOCK(&cache->filelock);
447 if (cache->filename)
448 isc_mem_free(cache->mctx, cache->filename);
449 cache->filename = newname;
450 UNLOCK(&cache->filelock);
452 return (ISC_R_SUCCESS);
455 #ifdef BIND9
456 isc_result_t
457 dns_cache_load(dns_cache_t *cache) {
458 isc_result_t result;
460 REQUIRE(VALID_CACHE(cache));
462 if (cache->filename == NULL)
463 return (ISC_R_SUCCESS);
465 LOCK(&cache->filelock);
466 result = dns_db_load(cache->db, cache->filename);
467 UNLOCK(&cache->filelock);
469 return (result);
471 #endif /* BIND9 */
473 isc_result_t
474 dns_cache_dump(dns_cache_t *cache) {
475 #ifdef BIND9
476 isc_result_t result;
477 #endif
479 REQUIRE(VALID_CACHE(cache));
481 if (cache->filename == NULL)
482 return (ISC_R_SUCCESS);
484 #ifdef BIND9
485 LOCK(&cache->filelock);
486 result = dns_master_dump(cache->mctx, cache->db, NULL,
487 &dns_master_style_cache, cache->filename);
488 UNLOCK(&cache->filelock);
489 return (result);
490 #else
491 return (ISC_R_NOTIMPLEMENTED);
492 #endif
496 void
497 dns_cache_setcleaninginterval(dns_cache_t *cache, unsigned int t) {
498 isc_interval_t interval;
499 isc_result_t result;
501 LOCK(&cache->lock);
504 * It may be the case that the cache has already shut down.
505 * If so, it has no timer.
507 if (cache->cleaner.cleaning_timer == NULL)
508 goto unlock;
510 cache->cleaner.cleaning_interval = t;
512 if (t == 0) {
513 result = isc_timer_reset(cache->cleaner.cleaning_timer,
514 isc_timertype_inactive,
515 NULL, NULL, ISC_TRUE);
516 } else {
517 isc_interval_set(&interval, cache->cleaner.cleaning_interval,
519 result = isc_timer_reset(cache->cleaner.cleaning_timer,
520 isc_timertype_ticker,
521 NULL, &interval, ISC_FALSE);
523 if (result != ISC_R_SUCCESS)
524 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
525 DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
526 "could not set cache cleaning interval: %s",
527 isc_result_totext(result));
529 unlock:
530 UNLOCK(&cache->lock);
533 unsigned int
534 dns_cache_getcleaninginterval(dns_cache_t *cache) {
535 unsigned int t;
537 REQUIRE(VALID_CACHE(cache));
539 LOCK(&cache->lock);
540 t = cache->cleaner.cleaning_interval;
541 UNLOCK(&cache->lock);
543 return (t);
546 const char *
547 dns_cache_getname(dns_cache_t *cache) {
548 REQUIRE(VALID_CACHE(cache));
550 return (cache->name);
554 * Initialize the cache cleaner object at *cleaner.
555 * Space for the object must be allocated by the caller.
558 static isc_result_t
559 cache_cleaner_init(dns_cache_t *cache, isc_taskmgr_t *taskmgr,
560 isc_timermgr_t *timermgr, cache_cleaner_t *cleaner)
562 isc_result_t result;
564 result = isc_mutex_init(&cleaner->lock);
565 if (result != ISC_R_SUCCESS)
566 goto fail;
568 cleaner->increment = DNS_CACHE_CLEANERINCREMENT;
569 cleaner->state = cleaner_s_idle;
570 cleaner->cache = cache;
571 cleaner->iterator = NULL;
572 cleaner->overmem = ISC_FALSE;
573 cleaner->replaceiterator = ISC_FALSE;
575 cleaner->task = NULL;
576 cleaner->cleaning_timer = NULL;
577 cleaner->resched_event = NULL;
578 cleaner->overmem_event = NULL;
579 cleaner->cleaning_interval = 0; /* Initially turned off. */
581 result = dns_db_createiterator(cleaner->cache->db, ISC_FALSE,
582 &cleaner->iterator);
583 if (result != ISC_R_SUCCESS)
584 goto cleanup;
586 if (taskmgr != NULL && timermgr != NULL) {
587 result = isc_task_create(taskmgr, 1, &cleaner->task);
588 if (result != ISC_R_SUCCESS) {
589 UNEXPECTED_ERROR(__FILE__, __LINE__,
590 "isc_task_create() failed: %s",
591 dns_result_totext(result));
592 result = ISC_R_UNEXPECTED;
593 goto cleanup;
595 cleaner->cache->live_tasks++;
596 isc_task_setname(cleaner->task, "cachecleaner", cleaner);
598 result = isc_task_onshutdown(cleaner->task,
599 cleaner_shutdown_action, cache);
600 if (result != ISC_R_SUCCESS) {
601 UNEXPECTED_ERROR(__FILE__, __LINE__,
602 "cache cleaner: "
603 "isc_task_onshutdown() failed: %s",
604 dns_result_totext(result));
605 goto cleanup;
608 result = isc_timer_create(timermgr, isc_timertype_inactive,
609 NULL, NULL, cleaner->task,
610 cleaning_timer_action, cleaner,
611 &cleaner->cleaning_timer);
612 if (result != ISC_R_SUCCESS) {
613 UNEXPECTED_ERROR(__FILE__, __LINE__,
614 "isc_timer_create() failed: %s",
615 dns_result_totext(result));
616 result = ISC_R_UNEXPECTED;
617 goto cleanup;
620 cleaner->resched_event =
621 isc_event_allocate(cache->mctx, cleaner,
622 DNS_EVENT_CACHECLEAN,
623 incremental_cleaning_action,
624 cleaner, sizeof(isc_event_t));
625 if (cleaner->resched_event == NULL) {
626 result = ISC_R_NOMEMORY;
627 goto cleanup;
630 cleaner->overmem_event =
631 isc_event_allocate(cache->mctx, cleaner,
632 DNS_EVENT_CACHEOVERMEM,
633 overmem_cleaning_action,
634 cleaner, sizeof(isc_event_t));
635 if (cleaner->overmem_event == NULL) {
636 result = ISC_R_NOMEMORY;
637 goto cleanup;
641 return (ISC_R_SUCCESS);
643 cleanup:
644 if (cleaner->overmem_event != NULL)
645 isc_event_free(&cleaner->overmem_event);
646 if (cleaner->resched_event != NULL)
647 isc_event_free(&cleaner->resched_event);
648 if (cleaner->cleaning_timer != NULL)
649 isc_timer_detach(&cleaner->cleaning_timer);
650 if (cleaner->task != NULL)
651 isc_task_detach(&cleaner->task);
652 if (cleaner->iterator != NULL)
653 dns_dbiterator_destroy(&cleaner->iterator);
654 DESTROYLOCK(&cleaner->lock);
655 fail:
656 return (result);
659 static void
660 begin_cleaning(cache_cleaner_t *cleaner) {
661 isc_result_t result = ISC_R_SUCCESS;
663 REQUIRE(CLEANER_IDLE(cleaner));
666 * Create an iterator, if it does not already exist, and
667 * position it at the beginning of the cache.
669 if (cleaner->iterator == NULL)
670 result = dns_db_createiterator(cleaner->cache->db, ISC_FALSE,
671 &cleaner->iterator);
672 if (result != ISC_R_SUCCESS)
673 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
674 DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
675 "cache cleaner could not create "
676 "iterator: %s", isc_result_totext(result));
677 else {
678 dns_dbiterator_setcleanmode(cleaner->iterator, ISC_TRUE);
679 result = dns_dbiterator_first(cleaner->iterator);
681 if (result != ISC_R_SUCCESS) {
683 * If the result is ISC_R_NOMORE, the database is empty,
684 * so there is nothing to be cleaned.
686 if (result != ISC_R_NOMORE && cleaner->iterator != NULL) {
687 UNEXPECTED_ERROR(__FILE__, __LINE__,
688 "cache cleaner: "
689 "dns_dbiterator_first() failed: %s",
690 dns_result_totext(result));
691 dns_dbiterator_destroy(&cleaner->iterator);
692 } else if (cleaner->iterator != NULL) {
693 result = dns_dbiterator_pause(cleaner->iterator);
694 RUNTIME_CHECK(result == ISC_R_SUCCESS);
696 } else {
698 * Pause the iterator to free its lock.
700 result = dns_dbiterator_pause(cleaner->iterator);
701 RUNTIME_CHECK(result == ISC_R_SUCCESS);
703 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
704 DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1),
705 "begin cache cleaning, mem inuse %lu",
706 (unsigned long)isc_mem_inuse(cleaner->cache->mctx));
707 cleaner->state = cleaner_s_busy;
708 isc_task_send(cleaner->task, &cleaner->resched_event);
711 return;
714 static void
715 end_cleaning(cache_cleaner_t *cleaner, isc_event_t *event) {
716 isc_result_t result;
718 REQUIRE(CLEANER_BUSY(cleaner));
719 REQUIRE(event != NULL);
721 result = dns_dbiterator_pause(cleaner->iterator);
722 if (result != ISC_R_SUCCESS)
723 dns_dbiterator_destroy(&cleaner->iterator);
725 dns_cache_setcleaninginterval(cleaner->cache,
726 cleaner->cleaning_interval);
728 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
729 ISC_LOG_DEBUG(1), "end cache cleaning, mem inuse %lu",
730 (unsigned long)isc_mem_inuse(cleaner->cache->mctx));
732 cleaner->state = cleaner_s_idle;
733 cleaner->resched_event = event;
737 * This is run once for every cache-cleaning-interval as defined in named.conf.
739 static void
740 cleaning_timer_action(isc_task_t *task, isc_event_t *event) {
741 cache_cleaner_t *cleaner = event->ev_arg;
743 UNUSED(task);
745 INSIST(task == cleaner->task);
746 INSIST(event->ev_type == ISC_TIMEREVENT_TICK);
748 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
749 ISC_LOG_DEBUG(1), "cache cleaning timer fired, "
750 "cleaner state = %d", cleaner->state);
752 if (cleaner->state == cleaner_s_idle)
753 begin_cleaning(cleaner);
755 isc_event_free(&event);
759 * This is called when the cache either surpasses its upper limit
760 * or shrinks beyond its lower limit.
762 static void
763 overmem_cleaning_action(isc_task_t *task, isc_event_t *event) {
764 cache_cleaner_t *cleaner = event->ev_arg;
765 isc_boolean_t want_cleaning = ISC_FALSE;
767 UNUSED(task);
769 INSIST(task == cleaner->task);
770 INSIST(event->ev_type == DNS_EVENT_CACHEOVERMEM);
771 INSIST(cleaner->overmem_event == NULL);
773 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
774 ISC_LOG_DEBUG(1), "overmem_cleaning_action called, "
775 "overmem = %d, state = %d", cleaner->overmem,
776 cleaner->state);
778 LOCK(&cleaner->lock);
780 if (cleaner->overmem) {
781 if (cleaner->state == cleaner_s_idle)
782 want_cleaning = ISC_TRUE;
783 } else {
784 if (cleaner->state == cleaner_s_busy)
786 * end_cleaning() can't be called here because
787 * then both cleaner->overmem_event and
788 * cleaner->resched_event will point to this
789 * event. Set the state to done, and then
790 * when the incremental_cleaning_action() event
791 * is posted, it will handle the end_cleaning.
793 cleaner->state = cleaner_s_done;
796 cleaner->overmem_event = event;
798 UNLOCK(&cleaner->lock);
800 if (want_cleaning)
801 begin_cleaning(cleaner);
805 * Do incremental cleaning.
807 static void
808 incremental_cleaning_action(isc_task_t *task, isc_event_t *event) {
809 cache_cleaner_t *cleaner = event->ev_arg;
810 isc_result_t result;
811 unsigned int n_names;
812 isc_time_t start;
814 UNUSED(task);
816 INSIST(task == cleaner->task);
817 INSIST(event->ev_type == DNS_EVENT_CACHECLEAN);
819 if (cleaner->state == cleaner_s_done) {
820 cleaner->state = cleaner_s_busy;
821 end_cleaning(cleaner, event);
822 LOCK(&cleaner->cache->lock);
823 LOCK(&cleaner->lock);
824 if (cleaner->replaceiterator) {
825 dns_dbiterator_destroy(&cleaner->iterator);
826 (void) dns_db_createiterator(cleaner->cache->db,
827 ISC_FALSE,
828 &cleaner->iterator);
829 cleaner->replaceiterator = ISC_FALSE;
831 UNLOCK(&cleaner->lock);
832 UNLOCK(&cleaner->cache->lock);
833 return;
836 INSIST(CLEANER_BUSY(cleaner));
838 n_names = cleaner->increment;
840 REQUIRE(DNS_DBITERATOR_VALID(cleaner->iterator));
842 isc_time_now(&start);
843 while (n_names-- > 0) {
844 dns_dbnode_t *node = NULL;
846 result = dns_dbiterator_current(cleaner->iterator, &node,
847 NULL);
848 if (result != ISC_R_SUCCESS) {
849 UNEXPECTED_ERROR(__FILE__, __LINE__,
850 "cache cleaner: dns_dbiterator_current() "
851 "failed: %s", dns_result_totext(result));
853 end_cleaning(cleaner, event);
854 return;
858 * The node was not needed, but was required by
859 * dns_dbiterator_current(). Give up its reference.
861 dns_db_detachnode(cleaner->cache->db, &node);
864 * Step to the next node.
866 result = dns_dbiterator_next(cleaner->iterator);
868 if (result != ISC_R_SUCCESS) {
870 * Either the end was reached (ISC_R_NOMORE) or
871 * some error was signaled. If the cache is still
872 * overmem and no error was encountered,
873 * keep trying to clean it, otherwise stop cleaning.
875 if (result != ISC_R_NOMORE)
876 UNEXPECTED_ERROR(__FILE__, __LINE__,
877 "cache cleaner: "
878 "dns_dbiterator_next() "
879 "failed: %s",
880 dns_result_totext(result));
881 else if (cleaner->overmem) {
882 result = dns_dbiterator_first(cleaner->
883 iterator);
884 if (result == ISC_R_SUCCESS) {
885 isc_log_write(dns_lctx,
886 DNS_LOGCATEGORY_DATABASE,
887 DNS_LOGMODULE_CACHE,
888 ISC_LOG_DEBUG(1),
889 "cache cleaner: "
890 "still overmem, "
891 "reset and try again");
892 continue;
896 end_cleaning(cleaner, event);
897 return;
902 * We have successfully performed a cleaning increment but have
903 * not gone through the entire cache. Free the iterator locks
904 * and reschedule another batch. If it fails, just try to continue
905 * anyway.
907 result = dns_dbiterator_pause(cleaner->iterator);
908 RUNTIME_CHECK(result == ISC_R_SUCCESS);
910 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
911 ISC_LOG_DEBUG(1), "cache cleaner: checked %u nodes, "
912 "mem inuse %lu, sleeping", cleaner->increment,
913 (unsigned long)isc_mem_inuse(cleaner->cache->mctx));
915 isc_task_send(task, &event);
916 INSIST(CLEANER_BUSY(cleaner));
917 return;
921 * Do immediate cleaning.
923 isc_result_t
924 dns_cache_clean(dns_cache_t *cache, isc_stdtime_t now) {
925 isc_result_t result;
926 dns_dbiterator_t *iterator = NULL;
928 REQUIRE(VALID_CACHE(cache));
930 result = dns_db_createiterator(cache->db, 0, &iterator);
931 if (result != ISC_R_SUCCESS)
932 return result;
934 result = dns_dbiterator_first(iterator);
936 while (result == ISC_R_SUCCESS) {
937 dns_dbnode_t *node = NULL;
938 result = dns_dbiterator_current(iterator, &node,
939 (dns_name_t *)NULL);
940 if (result != ISC_R_SUCCESS)
941 break;
944 * Check TTLs, mark expired rdatasets stale.
946 result = dns_db_expirenode(cache->db, node, now);
947 if (result != ISC_R_SUCCESS) {
948 UNEXPECTED_ERROR(__FILE__, __LINE__,
949 "cache cleaner: dns_db_expirenode() "
950 "failed: %s",
951 dns_result_totext(result));
953 * Continue anyway.
958 * This is where the actual freeing takes place.
960 dns_db_detachnode(cache->db, &node);
962 result = dns_dbiterator_next(iterator);
965 dns_dbiterator_destroy(&iterator);
967 if (result == ISC_R_NOMORE)
968 result = ISC_R_SUCCESS;
970 return (result);
973 static void
974 water(void *arg, int mark) {
975 dns_cache_t *cache = arg;
976 isc_boolean_t overmem = ISC_TF(mark == ISC_MEM_HIWATER);
978 REQUIRE(VALID_CACHE(cache));
980 LOCK(&cache->cleaner.lock);
982 if (overmem != cache->cleaner.overmem) {
983 dns_db_overmem(cache->db, overmem);
984 cache->cleaner.overmem = overmem;
985 isc_mem_waterack(cache->mctx, mark);
988 if (cache->cleaner.overmem_event != NULL)
989 isc_task_send(cache->cleaner.task,
990 &cache->cleaner.overmem_event);
992 UNLOCK(&cache->cleaner.lock);
995 void
996 dns_cache_setcachesize(dns_cache_t *cache, isc_uint32_t size) {
997 isc_uint32_t lowater;
998 isc_uint32_t hiwater;
1000 REQUIRE(VALID_CACHE(cache));
1003 * Impose a minimum cache size; pathological things happen if there
1004 * is too little room.
1006 if (size != 0 && size < DNS_CACHE_MINSIZE)
1007 size = DNS_CACHE_MINSIZE;
1009 LOCK(&cache->lock);
1010 cache->size = size;
1011 UNLOCK(&cache->lock);
1013 hiwater = size - (size >> 3); /* Approximately 7/8ths. */
1014 lowater = size - (size >> 2); /* Approximately 3/4ths. */
1017 * If the cache was overmem and cleaning, but now with the new limits
1018 * it is no longer in an overmem condition, then the next
1019 * isc_mem_put for cache memory will do the right thing and trigger
1020 * water().
1023 if (size == 0 || hiwater == 0 || lowater == 0)
1025 * Disable cache memory limiting.
1027 isc_mem_setwater(cache->mctx, water, cache, 0, 0);
1028 else
1030 * Establish new cache memory limits (either for the first
1031 * time, or replacing other limits).
1033 isc_mem_setwater(cache->mctx, water, cache, hiwater, lowater);
1036 isc_uint32_t
1037 dns_cache_getcachesize(dns_cache_t *cache) {
1038 isc_uint32_t size;
1040 REQUIRE(VALID_CACHE(cache));
1042 LOCK(&cache->lock);
1043 size = cache->size;
1044 UNLOCK(&cache->lock);
1046 return (size);
1050 * The cleaner task is shutting down; do the necessary cleanup.
1052 static void
1053 cleaner_shutdown_action(isc_task_t *task, isc_event_t *event) {
1054 dns_cache_t *cache = event->ev_arg;
1055 isc_boolean_t should_free = ISC_FALSE;
1057 UNUSED(task);
1059 INSIST(task == cache->cleaner.task);
1060 INSIST(event->ev_type == ISC_TASKEVENT_SHUTDOWN);
1062 if (CLEANER_BUSY(&cache->cleaner))
1063 end_cleaning(&cache->cleaner, event);
1064 else
1065 isc_event_free(&event);
1067 LOCK(&cache->lock);
1069 cache->live_tasks--;
1070 INSIST(cache->live_tasks == 0);
1072 if (cache->references == 0)
1073 should_free = ISC_TRUE;
1076 * By detaching the timer in the context of its task,
1077 * we are guaranteed that there will be no further timer
1078 * events.
1080 if (cache->cleaner.cleaning_timer != NULL)
1081 isc_timer_detach(&cache->cleaner.cleaning_timer);
1083 /* Make sure we don't reschedule anymore. */
1084 (void)isc_task_purge(task, NULL, DNS_EVENT_CACHECLEAN, NULL);
1086 UNLOCK(&cache->lock);
1088 if (should_free)
1089 cache_free(cache);
1092 isc_result_t
1093 dns_cache_flush(dns_cache_t *cache) {
1094 dns_db_t *db = NULL;
1095 isc_result_t result;
1097 result = cache_create_db(cache, &db);
1098 if (result != ISC_R_SUCCESS)
1099 return (result);
1101 LOCK(&cache->lock);
1102 LOCK(&cache->cleaner.lock);
1103 if (cache->cleaner.state == cleaner_s_idle) {
1104 if (cache->cleaner.iterator != NULL)
1105 dns_dbiterator_destroy(&cache->cleaner.iterator);
1106 (void) dns_db_createiterator(db, ISC_FALSE,
1107 &cache->cleaner.iterator);
1108 } else {
1109 if (cache->cleaner.state == cleaner_s_busy)
1110 cache->cleaner.state = cleaner_s_done;
1111 cache->cleaner.replaceiterator = ISC_TRUE;
1113 dns_db_detach(&cache->db);
1114 cache->db = db;
1115 UNLOCK(&cache->cleaner.lock);
1116 UNLOCK(&cache->lock);
1118 return (ISC_R_SUCCESS);
1121 isc_result_t
1122 dns_cache_flushname(dns_cache_t *cache, dns_name_t *name) {
1123 isc_result_t result;
1124 dns_rdatasetiter_t *iter = NULL;
1125 dns_dbnode_t *node = NULL;
1126 dns_db_t *db = NULL;
1128 LOCK(&cache->lock);
1129 if (cache->db != NULL)
1130 dns_db_attach(cache->db, &db);
1131 UNLOCK(&cache->lock);
1132 if (db == NULL)
1133 return (ISC_R_SUCCESS);
1134 result = dns_db_findnode(cache->db, name, ISC_FALSE, &node);
1135 if (result == ISC_R_NOTFOUND) {
1136 result = ISC_R_SUCCESS;
1137 goto cleanup_db;
1139 if (result != ISC_R_SUCCESS)
1140 goto cleanup_db;
1142 result = dns_db_allrdatasets(cache->db, node, NULL,
1143 (isc_stdtime_t)0, &iter);
1144 if (result != ISC_R_SUCCESS)
1145 goto cleanup_node;
1147 for (result = dns_rdatasetiter_first(iter);
1148 result == ISC_R_SUCCESS;
1149 result = dns_rdatasetiter_next(iter))
1151 dns_rdataset_t rdataset;
1152 dns_rdataset_init(&rdataset);
1154 dns_rdatasetiter_current(iter, &rdataset);
1155 result = dns_db_deleterdataset(cache->db, node, NULL,
1156 rdataset.type, rdataset.covers);
1157 dns_rdataset_disassociate(&rdataset);
1158 if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED)
1159 break;
1161 if (result == ISC_R_NOMORE)
1162 result = ISC_R_SUCCESS;
1164 dns_rdatasetiter_destroy(&iter);
1166 cleanup_node:
1167 dns_db_detachnode(cache->db, &node);
1169 cleanup_db:
1170 dns_db_detach(&db);
1171 return (result);