2 * Copyright (C) 2004-2009 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 1999-2003 Internet Software Consortium.
5 * Permission to use, copy, modify, and/or 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.
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.
18 /* $Id: cache.c,v 1.80.50.3 2009/05/06 23:34:30 jinmei Exp $ */
25 #include <isc/string.h>
28 #include <isc/timer.h>
31 #include <dns/cache.h>
33 #include <dns/dbiterator.h>
34 #include <dns/events.h>
37 #include <dns/masterdump.h>
38 #include <dns/rdata.h>
39 #include <dns/rdataset.h>
40 #include <dns/rdatasetiter.h>
41 #include <dns/result.h>
43 #define CACHE_MAGIC ISC_MAGIC('$', '$', '$', '$')
44 #define VALID_CACHE(cache) ISC_MAGIC_VALID(cache, CACHE_MAGIC)
47 * Control incremental cleaning.
48 * DNS_CACHE_MINSIZE is how many bytes is the floor for dns_cache_setcachesize().
49 * See also DNS_CACHE_CLEANERINCREMENT
51 #define DNS_CACHE_MINSIZE 2097152 /*%< Bytes. 2097152 = 2 MB */
53 * Control incremental cleaning.
54 * CLEANERINCREMENT is how many nodes are examined in one pass.
55 * See also DNS_CACHE_MINSIZE
57 #define DNS_CACHE_CLEANERINCREMENT 1000U /*%< Number of nodes. */
64 * A cache_cleaner_t encapsulates the state of the periodic
68 typedef struct cache_cleaner cache_cleaner_t;
71 cleaner_s_idle, /*%< Waiting for cleaning-interval to expire. */
72 cleaner_s_busy, /*%< Currently cleaning. */
73 cleaner_s_done /*%< Freed enough memory after being overmem. */
77 * Convenience macros for comprehensive assertion checking.
79 #define CLEANER_IDLE(c) ((c)->state == cleaner_s_idle && \
80 (c)->resched_event != NULL)
81 #define CLEANER_BUSY(c) ((c)->state == cleaner_s_busy && \
82 (c)->iterator != NULL && \
83 (c)->resched_event == NULL)
86 * Accesses to a cache cleaner object are synchronized through
87 * task/event serialization, or locked from the cache object.
89 struct cache_cleaner {
92 * Locks overmem_event, overmem. Note: never allocate memory
93 * while holding this lock - that could lead to deadlock since
94 * the lock is take by water() which is called from the memory
100 unsigned int cleaning_interval; /*% The cleaning-interval from
101 named.conf, in seconds. */
102 isc_timer_t *cleaning_timer;
103 isc_event_t *resched_event; /*% Sent by cleaner task to
104 itself to reschedule */
105 isc_event_t *overmem_event;
107 dns_dbiterator_t *iterator;
108 unsigned int increment; /*% Number of names to
109 clean in one increment */
110 cleaner_state_t state; /*% Idle/Busy. */
111 isc_boolean_t overmem; /*% The cache is in an overmem state. */
112 isc_boolean_t replaceiterator;
116 * The actual cache object.
123 isc_mutex_t filelock;
126 /* Locked by 'lock'. */
129 dns_rdataclass_t rdclass;
131 cache_cleaner_t cleaner;
136 /* Locked by 'filelock'. */
138 /* Access to the on-disk cache file is also locked by 'filelock'. */
146 cache_cleaner_init(dns_cache_t *cache, isc_taskmgr_t *taskmgr,
147 isc_timermgr_t *timermgr, cache_cleaner_t *cleaner);
150 cleaning_timer_action(isc_task_t *task, isc_event_t *event);
153 incremental_cleaning_action(isc_task_t *task, isc_event_t *event);
156 cleaner_shutdown_action(isc_task_t *task, isc_event_t *event);
159 overmem_cleaning_action(isc_task_t *task, isc_event_t *event);
161 static inline isc_result_t
162 cache_create_db(dns_cache_t *cache, dns_db_t **db) {
163 return (dns_db_create(cache->mctx, cache->db_type, dns_rootname,
164 dns_dbtype_cache, cache->rdclass,
165 cache->db_argc, cache->db_argv, db));
169 dns_cache_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
170 isc_timermgr_t *timermgr, dns_rdataclass_t rdclass,
171 const char *db_type, unsigned int db_argc, char **db_argv,
172 dns_cache_t **cachep)
179 REQUIRE(cachep != NULL);
180 REQUIRE(*cachep == NULL);
181 REQUIRE(mctx != NULL);
183 cache = isc_mem_get(mctx, sizeof(*cache));
185 return (ISC_R_NOMEMORY);
188 isc_mem_attach(mctx, &cache->mctx);
190 result = isc_mutex_init(&cache->lock);
191 if (result != ISC_R_SUCCESS)
194 result = isc_mutex_init(&cache->filelock);
195 if (result != ISC_R_SUCCESS)
198 cache->references = 1;
199 cache->live_tasks = 0;
200 cache->rdclass = rdclass;
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;
208 cache->db_argc = db_argc;
209 if (cache->db_argc == 0)
210 cache->db_argv = NULL;
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;
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;
230 result = cache_create_db(cache, &cache->db);
231 if (result != ISC_R_SUCCESS)
233 if (taskmgr != NULL) {
235 result = isc_task_create(taskmgr, 1, &dbtask);
236 if (result != ISC_R_SUCCESS)
238 dns_db_settask(cache->db, dbtask);
239 isc_task_detach(&dbtask);
242 cache->filename = NULL;
244 cache->magic = CACHE_MAGIC;
247 * RBT-type cache DB has its own mechanism of cache cleaning and doesn't
248 * need the control of the generic cleaner.
250 if (strcmp(db_type, "rbt") == 0)
251 result = cache_cleaner_init(cache, NULL, NULL, &cache->cleaner);
253 result = cache_cleaner_init(cache, taskmgr, timermgr,
256 if (result != ISC_R_SUCCESS)
260 return (ISC_R_SUCCESS);
263 dns_db_detach(&cache->db);
265 for (i = 0; i < cache->db_argc; i++)
266 if (cache->db_argv[i] != NULL)
267 isc_mem_free(mctx, cache->db_argv[i]);
268 if (cache->db_argv != NULL)
269 isc_mem_put(mctx, cache->db_argv,
270 cache->db_argc * sizeof(char *));
272 isc_mem_free(mctx, cache->db_type);
274 DESTROYLOCK(&cache->filelock);
276 DESTROYLOCK(&cache->lock);
278 isc_mem_put(mctx, cache, sizeof(*cache));
279 isc_mem_detach(&mctx);
284 cache_free(dns_cache_t *cache) {
288 REQUIRE(VALID_CACHE(cache));
289 REQUIRE(cache->references == 0);
291 isc_mem_setwater(cache->mctx, NULL, NULL, 0, 0);
293 if (cache->cleaner.task != NULL)
294 isc_task_detach(&cache->cleaner.task);
296 if (cache->cleaner.overmem_event != NULL)
297 isc_event_free(&cache->cleaner.overmem_event);
299 if (cache->cleaner.resched_event != NULL)
300 isc_event_free(&cache->cleaner.resched_event);
302 if (cache->cleaner.iterator != NULL)
303 dns_dbiterator_destroy(&cache->cleaner.iterator);
305 DESTROYLOCK(&cache->cleaner.lock);
307 if (cache->filename) {
308 isc_mem_free(cache->mctx, cache->filename);
309 cache->filename = NULL;
312 if (cache->db != NULL)
313 dns_db_detach(&cache->db);
315 if (cache->db_argv != NULL) {
316 for (i = 0; i < cache->db_argc; i++)
317 if (cache->db_argv[i] != NULL)
318 isc_mem_free(cache->mctx, cache->db_argv[i]);
319 isc_mem_put(cache->mctx, cache->db_argv,
320 cache->db_argc * sizeof(char *));
323 if (cache->db_type != NULL)
324 isc_mem_free(cache->mctx, cache->db_type);
326 DESTROYLOCK(&cache->lock);
327 DESTROYLOCK(&cache->filelock);
330 isc_mem_put(cache->mctx, cache, sizeof(*cache));
331 isc_mem_detach(&mctx);
336 dns_cache_attach(dns_cache_t *cache, dns_cache_t **targetp) {
338 REQUIRE(VALID_CACHE(cache));
339 REQUIRE(targetp != NULL && *targetp == NULL);
343 UNLOCK(&cache->lock);
349 dns_cache_detach(dns_cache_t **cachep) {
351 isc_boolean_t free_cache = ISC_FALSE;
353 REQUIRE(cachep != NULL);
355 REQUIRE(VALID_CACHE(cache));
358 REQUIRE(cache->references > 0);
360 if (cache->references == 0) {
361 cache->cleaner.overmem = ISC_FALSE;
362 free_cache = ISC_TRUE;
369 * When the cache is shut down, dump it to a file if one is
372 isc_result_t result = dns_cache_dump(cache);
373 if (result != ISC_R_SUCCESS)
374 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
375 DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
376 "error dumping cache: %s ",
377 isc_result_totext(result));
380 * If the cleaner task exists, let it free the cache.
382 if (cache->live_tasks > 0) {
383 isc_task_shutdown(cache->cleaner.task);
384 free_cache = ISC_FALSE;
388 UNLOCK(&cache->lock);
395 dns_cache_attachdb(dns_cache_t *cache, dns_db_t **dbp) {
396 REQUIRE(VALID_CACHE(cache));
397 REQUIRE(dbp != NULL && *dbp == NULL);
398 REQUIRE(cache->db != NULL);
401 dns_db_attach(cache->db, dbp);
402 UNLOCK(&cache->lock);
407 dns_cache_setfilename(dns_cache_t *cache, const char *filename) {
410 REQUIRE(VALID_CACHE(cache));
411 REQUIRE(filename != NULL);
413 newname = isc_mem_strdup(cache->mctx, filename);
415 return (ISC_R_NOMEMORY);
417 LOCK(&cache->filelock);
419 isc_mem_free(cache->mctx, cache->filename);
420 cache->filename = newname;
421 UNLOCK(&cache->filelock);
423 return (ISC_R_SUCCESS);
427 dns_cache_load(dns_cache_t *cache) {
430 REQUIRE(VALID_CACHE(cache));
432 if (cache->filename == NULL)
433 return (ISC_R_SUCCESS);
435 LOCK(&cache->filelock);
436 result = dns_db_load(cache->db, cache->filename);
437 UNLOCK(&cache->filelock);
443 dns_cache_dump(dns_cache_t *cache) {
446 REQUIRE(VALID_CACHE(cache));
448 if (cache->filename == NULL)
449 return (ISC_R_SUCCESS);
451 LOCK(&cache->filelock);
452 result = dns_master_dump(cache->mctx, cache->db, NULL,
453 &dns_master_style_cache, cache->filename);
454 UNLOCK(&cache->filelock);
460 dns_cache_setcleaninginterval(dns_cache_t *cache, unsigned int t) {
461 isc_interval_t interval;
467 * It may be the case that the cache has already shut down.
468 * If so, it has no timer.
470 if (cache->cleaner.cleaning_timer == NULL)
473 cache->cleaner.cleaning_interval = t;
476 result = isc_timer_reset(cache->cleaner.cleaning_timer,
477 isc_timertype_inactive,
478 NULL, NULL, ISC_TRUE);
480 isc_interval_set(&interval, cache->cleaner.cleaning_interval,
482 result = isc_timer_reset(cache->cleaner.cleaning_timer,
483 isc_timertype_ticker,
484 NULL, &interval, ISC_FALSE);
486 if (result != ISC_R_SUCCESS)
487 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
488 DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
489 "could not set cache cleaning interval: %s",
490 isc_result_totext(result));
493 UNLOCK(&cache->lock);
497 * Initialize the cache cleaner object at *cleaner.
498 * Space for the object must be allocated by the caller.
502 cache_cleaner_init(dns_cache_t *cache, isc_taskmgr_t *taskmgr,
503 isc_timermgr_t *timermgr, cache_cleaner_t *cleaner)
507 result = isc_mutex_init(&cleaner->lock);
508 if (result != ISC_R_SUCCESS)
511 cleaner->increment = DNS_CACHE_CLEANERINCREMENT;
512 cleaner->state = cleaner_s_idle;
513 cleaner->cache = cache;
514 cleaner->iterator = NULL;
515 cleaner->overmem = ISC_FALSE;
516 cleaner->replaceiterator = ISC_FALSE;
518 cleaner->task = NULL;
519 cleaner->cleaning_timer = NULL;
520 cleaner->resched_event = NULL;
521 cleaner->overmem_event = NULL;
523 result = dns_db_createiterator(cleaner->cache->db, ISC_FALSE,
525 if (result != ISC_R_SUCCESS)
528 if (taskmgr != NULL && timermgr != NULL) {
529 result = isc_task_create(taskmgr, 1, &cleaner->task);
530 if (result != ISC_R_SUCCESS) {
531 UNEXPECTED_ERROR(__FILE__, __LINE__,
532 "isc_task_create() failed: %s",
533 dns_result_totext(result));
534 result = ISC_R_UNEXPECTED;
537 cleaner->cache->live_tasks++;
538 isc_task_setname(cleaner->task, "cachecleaner", cleaner);
540 result = isc_task_onshutdown(cleaner->task,
541 cleaner_shutdown_action, cache);
542 if (result != ISC_R_SUCCESS) {
543 UNEXPECTED_ERROR(__FILE__, __LINE__,
545 "isc_task_onshutdown() failed: %s",
546 dns_result_totext(result));
550 cleaner->cleaning_interval = 0; /* Initially turned off. */
551 result = isc_timer_create(timermgr, isc_timertype_inactive,
552 NULL, NULL, cleaner->task,
553 cleaning_timer_action, cleaner,
554 &cleaner->cleaning_timer);
555 if (result != ISC_R_SUCCESS) {
556 UNEXPECTED_ERROR(__FILE__, __LINE__,
557 "isc_timer_create() failed: %s",
558 dns_result_totext(result));
559 result = ISC_R_UNEXPECTED;
563 cleaner->resched_event =
564 isc_event_allocate(cache->mctx, cleaner,
565 DNS_EVENT_CACHECLEAN,
566 incremental_cleaning_action,
567 cleaner, sizeof(isc_event_t));
568 if (cleaner->resched_event == NULL) {
569 result = ISC_R_NOMEMORY;
573 cleaner->overmem_event =
574 isc_event_allocate(cache->mctx, cleaner,
575 DNS_EVENT_CACHEOVERMEM,
576 overmem_cleaning_action,
577 cleaner, sizeof(isc_event_t));
578 if (cleaner->overmem_event == NULL) {
579 result = ISC_R_NOMEMORY;
584 return (ISC_R_SUCCESS);
587 if (cleaner->overmem_event != NULL)
588 isc_event_free(&cleaner->overmem_event);
589 if (cleaner->resched_event != NULL)
590 isc_event_free(&cleaner->resched_event);
591 if (cleaner->cleaning_timer != NULL)
592 isc_timer_detach(&cleaner->cleaning_timer);
593 if (cleaner->task != NULL)
594 isc_task_detach(&cleaner->task);
595 if (cleaner->iterator != NULL)
596 dns_dbiterator_destroy(&cleaner->iterator);
597 DESTROYLOCK(&cleaner->lock);
603 begin_cleaning(cache_cleaner_t *cleaner) {
604 isc_result_t result = ISC_R_SUCCESS;
606 REQUIRE(CLEANER_IDLE(cleaner));
609 * Create an iterator, if it does not already exist, and
610 * position it at the beginning of the cache.
612 if (cleaner->iterator == NULL)
613 result = dns_db_createiterator(cleaner->cache->db, ISC_FALSE,
615 if (result != ISC_R_SUCCESS)
616 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
617 DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
618 "cache cleaner could not create "
619 "iterator: %s", isc_result_totext(result));
621 dns_dbiterator_setcleanmode(cleaner->iterator, ISC_TRUE);
622 result = dns_dbiterator_first(cleaner->iterator);
624 if (result != ISC_R_SUCCESS) {
626 * If the result is ISC_R_NOMORE, the database is empty,
627 * so there is nothing to be cleaned.
629 if (result != ISC_R_NOMORE && cleaner->iterator != NULL) {
630 UNEXPECTED_ERROR(__FILE__, __LINE__,
632 "dns_dbiterator_first() failed: %s",
633 dns_result_totext(result));
634 dns_dbiterator_destroy(&cleaner->iterator);
635 } else if (cleaner->iterator != NULL) {
636 result = dns_dbiterator_pause(cleaner->iterator);
637 RUNTIME_CHECK(result == ISC_R_SUCCESS);
641 * Pause the iterator to free its lock.
643 result = dns_dbiterator_pause(cleaner->iterator);
644 RUNTIME_CHECK(result == ISC_R_SUCCESS);
646 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
647 DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1),
648 "begin cache cleaning, mem inuse %lu",
649 (unsigned long)isc_mem_inuse(cleaner->cache->mctx));
650 cleaner->state = cleaner_s_busy;
651 isc_task_send(cleaner->task, &cleaner->resched_event);
658 end_cleaning(cache_cleaner_t *cleaner, isc_event_t *event) {
661 REQUIRE(CLEANER_BUSY(cleaner));
662 REQUIRE(event != NULL);
664 result = dns_dbiterator_pause(cleaner->iterator);
665 if (result != ISC_R_SUCCESS)
666 dns_dbiterator_destroy(&cleaner->iterator);
668 dns_cache_setcleaninginterval(cleaner->cache,
669 cleaner->cleaning_interval);
671 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
672 ISC_LOG_DEBUG(1), "end cache cleaning, mem inuse %lu",
673 (unsigned long)isc_mem_inuse(cleaner->cache->mctx));
675 cleaner->state = cleaner_s_idle;
676 cleaner->resched_event = event;
680 * This is run once for every cache-cleaning-interval as defined in named.conf.
683 cleaning_timer_action(isc_task_t *task, isc_event_t *event) {
684 cache_cleaner_t *cleaner = event->ev_arg;
688 INSIST(task == cleaner->task);
689 INSIST(event->ev_type == ISC_TIMEREVENT_TICK);
691 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
692 ISC_LOG_DEBUG(1), "cache cleaning timer fired, "
693 "cleaner state = %d", cleaner->state);
695 if (cleaner->state == cleaner_s_idle)
696 begin_cleaning(cleaner);
698 isc_event_free(&event);
702 * This is called when the cache either surpasses its upper limit
703 * or shrinks beyond its lower limit.
706 overmem_cleaning_action(isc_task_t *task, isc_event_t *event) {
707 cache_cleaner_t *cleaner = event->ev_arg;
708 isc_boolean_t want_cleaning = ISC_FALSE;
712 INSIST(task == cleaner->task);
713 INSIST(event->ev_type == DNS_EVENT_CACHEOVERMEM);
714 INSIST(cleaner->overmem_event == NULL);
716 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
717 ISC_LOG_DEBUG(1), "overmem_cleaning_action called, "
718 "overmem = %d, state = %d", cleaner->overmem,
721 LOCK(&cleaner->lock);
723 if (cleaner->overmem) {
724 if (cleaner->state == cleaner_s_idle)
725 want_cleaning = ISC_TRUE;
727 if (cleaner->state == cleaner_s_busy)
729 * end_cleaning() can't be called here because
730 * then both cleaner->overmem_event and
731 * cleaner->resched_event will point to this
732 * event. Set the state to done, and then
733 * when the incremental_cleaning_action() event
734 * is posted, it will handle the end_cleaning.
736 cleaner->state = cleaner_s_done;
739 cleaner->overmem_event = event;
741 UNLOCK(&cleaner->lock);
744 begin_cleaning(cleaner);
748 * Do incremental cleaning.
751 incremental_cleaning_action(isc_task_t *task, isc_event_t *event) {
752 cache_cleaner_t *cleaner = event->ev_arg;
754 unsigned int n_names;
759 INSIST(task == cleaner->task);
760 INSIST(event->ev_type == DNS_EVENT_CACHECLEAN);
762 if (cleaner->state == cleaner_s_done) {
763 cleaner->state = cleaner_s_busy;
764 end_cleaning(cleaner, event);
765 LOCK(&cleaner->cache->lock);
766 LOCK(&cleaner->lock);
767 if (cleaner->replaceiterator) {
768 dns_dbiterator_destroy(&cleaner->iterator);
769 (void) dns_db_createiterator(cleaner->cache->db,
772 cleaner->replaceiterator = ISC_FALSE;
774 UNLOCK(&cleaner->lock);
775 UNLOCK(&cleaner->cache->lock);
779 INSIST(CLEANER_BUSY(cleaner));
781 n_names = cleaner->increment;
783 REQUIRE(DNS_DBITERATOR_VALID(cleaner->iterator));
785 isc_time_now(&start);
786 while (n_names-- > 0) {
787 dns_dbnode_t *node = NULL;
789 result = dns_dbiterator_current(cleaner->iterator, &node,
791 if (result != ISC_R_SUCCESS) {
792 UNEXPECTED_ERROR(__FILE__, __LINE__,
793 "cache cleaner: dns_dbiterator_current() "
794 "failed: %s", dns_result_totext(result));
796 end_cleaning(cleaner, event);
801 * The node was not needed, but was required by
802 * dns_dbiterator_current(). Give up its reference.
804 dns_db_detachnode(cleaner->cache->db, &node);
807 * Step to the next node.
809 result = dns_dbiterator_next(cleaner->iterator);
811 if (result != ISC_R_SUCCESS) {
813 * Either the end was reached (ISC_R_NOMORE) or
814 * some error was signaled. If the cache is still
815 * overmem and no error was encountered,
816 * keep trying to clean it, otherwise stop cleaning.
818 if (result != ISC_R_NOMORE)
819 UNEXPECTED_ERROR(__FILE__, __LINE__,
821 "dns_dbiterator_next() "
823 dns_result_totext(result));
824 else if (cleaner->overmem) {
825 result = dns_dbiterator_first(cleaner->
827 if (result == ISC_R_SUCCESS) {
828 isc_log_write(dns_lctx,
829 DNS_LOGCATEGORY_DATABASE,
834 "reset and try again");
839 end_cleaning(cleaner, event);
845 * We have successfully performed a cleaning increment but have
846 * not gone through the entire cache. Free the iterator locks
847 * and reschedule another batch. If it fails, just try to continue
850 result = dns_dbiterator_pause(cleaner->iterator);
851 RUNTIME_CHECK(result == ISC_R_SUCCESS);
853 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
854 ISC_LOG_DEBUG(1), "cache cleaner: checked %u nodes, "
855 "mem inuse %lu, sleeping", cleaner->increment,
856 (unsigned long)isc_mem_inuse(cleaner->cache->mctx));
858 isc_task_send(task, &event);
859 INSIST(CLEANER_BUSY(cleaner));
864 * Do immediate cleaning.
867 dns_cache_clean(dns_cache_t *cache, isc_stdtime_t now) {
869 dns_dbiterator_t *iterator = NULL;
871 REQUIRE(VALID_CACHE(cache));
873 result = dns_db_createiterator(cache->db, 0, &iterator);
874 if (result != ISC_R_SUCCESS)
877 result = dns_dbiterator_first(iterator);
879 while (result == ISC_R_SUCCESS) {
880 dns_dbnode_t *node = NULL;
881 result = dns_dbiterator_current(iterator, &node,
883 if (result != ISC_R_SUCCESS)
887 * Check TTLs, mark expired rdatasets stale.
889 result = dns_db_expirenode(cache->db, node, now);
890 if (result != ISC_R_SUCCESS) {
891 UNEXPECTED_ERROR(__FILE__, __LINE__,
892 "cache cleaner: dns_db_expirenode() "
894 dns_result_totext(result));
901 * This is where the actual freeing takes place.
903 dns_db_detachnode(cache->db, &node);
905 result = dns_dbiterator_next(iterator);
908 dns_dbiterator_destroy(&iterator);
910 if (result == ISC_R_NOMORE)
911 result = ISC_R_SUCCESS;
917 water(void *arg, int mark) {
918 dns_cache_t *cache = arg;
919 isc_boolean_t overmem = ISC_TF(mark == ISC_MEM_HIWATER);
921 REQUIRE(VALID_CACHE(cache));
923 LOCK(&cache->cleaner.lock);
925 if (overmem != cache->cleaner.overmem) {
926 dns_db_overmem(cache->db, overmem);
927 cache->cleaner.overmem = overmem;
928 isc_mem_waterack(cache->mctx, mark);
931 if (cache->cleaner.overmem_event != NULL)
932 isc_task_send(cache->cleaner.task,
933 &cache->cleaner.overmem_event);
935 UNLOCK(&cache->cleaner.lock);
939 dns_cache_setcachesize(dns_cache_t *cache, isc_uint32_t size) {
940 isc_uint32_t lowater;
941 isc_uint32_t hiwater;
943 REQUIRE(VALID_CACHE(cache));
946 * Impose a minimum cache size; pathological things happen if there
947 * is too little room.
949 if (size != 0 && size < DNS_CACHE_MINSIZE)
950 size = DNS_CACHE_MINSIZE;
952 hiwater = size - (size >> 3); /* Approximately 7/8ths. */
953 lowater = size - (size >> 2); /* Approximately 3/4ths. */
956 * If the cache was overmem and cleaning, but now with the new limits
957 * it is no longer in an overmem condition, then the next
958 * isc_mem_put for cache memory will do the right thing and trigger
962 if (size == 0 || hiwater == 0 || lowater == 0)
964 * Disable cache memory limiting.
966 isc_mem_setwater(cache->mctx, water, cache, 0, 0);
969 * Establish new cache memory limits (either for the first
970 * time, or replacing other limits).
972 isc_mem_setwater(cache->mctx, water, cache, hiwater, lowater);
976 * The cleaner task is shutting down; do the necessary cleanup.
979 cleaner_shutdown_action(isc_task_t *task, isc_event_t *event) {
980 dns_cache_t *cache = event->ev_arg;
981 isc_boolean_t should_free = ISC_FALSE;
985 INSIST(task == cache->cleaner.task);
986 INSIST(event->ev_type == ISC_TASKEVENT_SHUTDOWN);
988 if (CLEANER_BUSY(&cache->cleaner))
989 end_cleaning(&cache->cleaner, event);
991 isc_event_free(&event);
996 INSIST(cache->live_tasks == 0);
998 if (cache->references == 0)
999 should_free = ISC_TRUE;
1002 * By detaching the timer in the context of its task,
1003 * we are guaranteed that there will be no further timer
1006 if (cache->cleaner.cleaning_timer != NULL)
1007 isc_timer_detach(&cache->cleaner.cleaning_timer);
1009 /* Make sure we don't reschedule anymore. */
1010 (void)isc_task_purge(task, NULL, DNS_EVENT_CACHECLEAN, NULL);
1012 UNLOCK(&cache->lock);
1019 dns_cache_flush(dns_cache_t *cache) {
1020 dns_db_t *db = NULL;
1021 isc_result_t result;
1023 result = cache_create_db(cache, &db);
1024 if (result != ISC_R_SUCCESS)
1028 LOCK(&cache->cleaner.lock);
1029 if (cache->cleaner.state == cleaner_s_idle) {
1030 if (cache->cleaner.iterator != NULL)
1031 dns_dbiterator_destroy(&cache->cleaner.iterator);
1032 (void) dns_db_createiterator(db, ISC_FALSE,
1033 &cache->cleaner.iterator);
1035 if (cache->cleaner.state == cleaner_s_busy)
1036 cache->cleaner.state = cleaner_s_done;
1037 cache->cleaner.replaceiterator = ISC_TRUE;
1039 dns_db_detach(&cache->db);
1041 UNLOCK(&cache->cleaner.lock);
1042 UNLOCK(&cache->lock);
1044 return (ISC_R_SUCCESS);
1048 dns_cache_flushname(dns_cache_t *cache, dns_name_t *name) {
1049 isc_result_t result;
1050 dns_rdatasetiter_t *iter = NULL;
1051 dns_dbnode_t *node = NULL;
1052 dns_db_t *db = NULL;
1055 if (cache->db != NULL)
1056 dns_db_attach(cache->db, &db);
1057 UNLOCK(&cache->lock);
1059 return (ISC_R_SUCCESS);
1060 result = dns_db_findnode(cache->db, name, ISC_FALSE, &node);
1061 if (result == ISC_R_NOTFOUND) {
1062 result = ISC_R_SUCCESS;
1065 if (result != ISC_R_SUCCESS)
1068 result = dns_db_allrdatasets(cache->db, node, NULL,
1069 (isc_stdtime_t)0, &iter);
1070 if (result != ISC_R_SUCCESS)
1073 for (result = dns_rdatasetiter_first(iter);
1074 result == ISC_R_SUCCESS;
1075 result = dns_rdatasetiter_next(iter))
1077 dns_rdataset_t rdataset;
1078 dns_rdataset_init(&rdataset);
1080 dns_rdatasetiter_current(iter, &rdataset);
1081 result = dns_db_deleterdataset(cache->db, node, NULL,
1082 rdataset.type, rdataset.covers);
1083 dns_rdataset_disassociate(&rdataset);
1084 if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED)
1087 if (result == ISC_R_NOMORE)
1088 result = ISC_R_SUCCESS;
1090 dns_rdatasetiter_destroy(&iter);
1093 dns_db_detachnode(cache->db, &node);