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 */
27 #include <isc/string.h>
30 #include <isc/timer.h>
33 #include <dns/cache.h>
35 #include <dns/dbiterator.h>
36 #include <dns/events.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)
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 */
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. */
66 * A cache_cleaner_t encapsulates the state of the periodic
70 typedef struct cache_cleaner cache_cleaner_t
;
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. */
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)
88 * Accesses to a cache cleaner object are synchronized through
89 * task/event serialization, or locked from the cache object.
91 struct cache_cleaner
{
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
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.
125 isc_mutex_t filelock
;
129 /* Locked by 'lock'. */
132 dns_rdataclass_t rdclass
;
134 cache_cleaner_t cleaner
;
140 /* Locked by 'filelock'. */
142 /* Access to the on-disk cache file is also locked by 'filelock'. */
150 cache_cleaner_init(dns_cache_t
*cache
, isc_taskmgr_t
*taskmgr
,
151 isc_timermgr_t
*timermgr
, cache_cleaner_t
*cleaner
);
154 cleaning_timer_action(isc_task_t
*task
, isc_event_t
*event
);
157 incremental_cleaning_action(isc_task_t
*task
, isc_event_t
*event
);
160 cleaner_shutdown_action(isc_task_t
*task
, isc_event_t
*event
);
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
));
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
));
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
)
193 REQUIRE(cachep
!= NULL
);
194 REQUIRE(*cachep
== NULL
);
195 REQUIRE(mctx
!= NULL
);
196 REQUIRE(cachename
!= NULL
);
198 cache
= isc_mem_get(mctx
, sizeof(*cache
));
200 return (ISC_R_NOMEMORY
);
203 isc_mem_attach(mctx
, &cache
->mctx
);
206 if (cachename
!= NULL
) {
207 cache
->name
= isc_mem_strdup(mctx
, cachename
);
208 if (cache
->name
== NULL
) {
209 result
= ISC_R_NOMEMORY
;
214 result
= isc_mutex_init(&cache
->lock
);
215 if (result
!= ISC_R_SUCCESS
)
218 result
= isc_mutex_init(&cache
->filelock
);
219 if (result
!= ISC_R_SUCCESS
)
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
;
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
;
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
;
254 result
= cache_create_db(cache
, &cache
->db
);
255 if (result
!= ISC_R_SUCCESS
)
257 if (taskmgr
!= NULL
) {
259 result
= isc_task_create(taskmgr
, 1, &dbtask
);
260 if (result
!= ISC_R_SUCCESS
)
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
);
277 result
= cache_cleaner_init(cache
, taskmgr
, timermgr
,
280 if (result
!= ISC_R_SUCCESS
)
284 return (ISC_R_SUCCESS
);
287 dns_db_detach(&cache
->db
);
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 *));
296 isc_mem_free(mctx
, cache
->db_type
);
298 DESTROYLOCK(&cache
->filelock
);
300 DESTROYLOCK(&cache
->lock
);
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
);
310 cache_free(dns_cache_t
*cache
) {
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
);
359 isc_mem_put(cache
->mctx
, cache
, sizeof(*cache
));
360 isc_mem_detach(&mctx
);
365 dns_cache_attach(dns_cache_t
*cache
, dns_cache_t
**targetp
) {
367 REQUIRE(VALID_CACHE(cache
));
368 REQUIRE(targetp
!= NULL
&& *targetp
== NULL
);
372 UNLOCK(&cache
->lock
);
378 dns_cache_detach(dns_cache_t
**cachep
) {
380 isc_boolean_t free_cache
= ISC_FALSE
;
382 REQUIRE(cachep
!= NULL
);
384 REQUIRE(VALID_CACHE(cache
));
387 REQUIRE(cache
->references
> 0);
389 if (cache
->references
== 0) {
390 cache
->cleaner
.overmem
= ISC_FALSE
;
391 free_cache
= ISC_TRUE
;
398 * When the cache is shut down, dump it to a file if one is
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
);
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
);
430 dns_db_attach(cache
->db
, dbp
);
431 UNLOCK(&cache
->lock
);
436 dns_cache_setfilename(dns_cache_t
*cache
, const char *filename
) {
439 REQUIRE(VALID_CACHE(cache
));
440 REQUIRE(filename
!= NULL
);
442 newname
= isc_mem_strdup(cache
->mctx
, filename
);
444 return (ISC_R_NOMEMORY
);
446 LOCK(&cache
->filelock
);
448 isc_mem_free(cache
->mctx
, cache
->filename
);
449 cache
->filename
= newname
;
450 UNLOCK(&cache
->filelock
);
452 return (ISC_R_SUCCESS
);
457 dns_cache_load(dns_cache_t
*cache
) {
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
);
474 dns_cache_dump(dns_cache_t
*cache
) {
479 REQUIRE(VALID_CACHE(cache
));
481 if (cache
->filename
== NULL
)
482 return (ISC_R_SUCCESS
);
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
);
491 return (ISC_R_NOTIMPLEMENTED
);
497 dns_cache_setcleaninginterval(dns_cache_t
*cache
, unsigned int t
) {
498 isc_interval_t interval
;
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
)
510 cache
->cleaner
.cleaning_interval
= t
;
513 result
= isc_timer_reset(cache
->cleaner
.cleaning_timer
,
514 isc_timertype_inactive
,
515 NULL
, NULL
, ISC_TRUE
);
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
));
530 UNLOCK(&cache
->lock
);
534 dns_cache_getcleaninginterval(dns_cache_t
*cache
) {
537 REQUIRE(VALID_CACHE(cache
));
540 t
= cache
->cleaner
.cleaning_interval
;
541 UNLOCK(&cache
->lock
);
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.
559 cache_cleaner_init(dns_cache_t
*cache
, isc_taskmgr_t
*taskmgr
,
560 isc_timermgr_t
*timermgr
, cache_cleaner_t
*cleaner
)
564 result
= isc_mutex_init(&cleaner
->lock
);
565 if (result
!= ISC_R_SUCCESS
)
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
,
583 if (result
!= ISC_R_SUCCESS
)
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
;
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__
,
603 "isc_task_onshutdown() failed: %s",
604 dns_result_totext(result
));
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
;
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
;
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
;
641 return (ISC_R_SUCCESS
);
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
);
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
,
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
));
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__
,
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
);
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
);
715 end_cleaning(cache_cleaner_t
*cleaner
, isc_event_t
*event
) {
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.
740 cleaning_timer_action(isc_task_t
*task
, isc_event_t
*event
) {
741 cache_cleaner_t
*cleaner
= event
->ev_arg
;
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.
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
;
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
,
778 LOCK(&cleaner
->lock
);
780 if (cleaner
->overmem
) {
781 if (cleaner
->state
== cleaner_s_idle
)
782 want_cleaning
= ISC_TRUE
;
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
);
801 begin_cleaning(cleaner
);
805 * Do incremental cleaning.
808 incremental_cleaning_action(isc_task_t
*task
, isc_event_t
*event
) {
809 cache_cleaner_t
*cleaner
= event
->ev_arg
;
811 unsigned int n_names
;
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
,
829 cleaner
->replaceiterator
= ISC_FALSE
;
831 UNLOCK(&cleaner
->lock
);
832 UNLOCK(&cleaner
->cache
->lock
);
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
,
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
);
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__
,
878 "dns_dbiterator_next() "
880 dns_result_totext(result
));
881 else if (cleaner
->overmem
) {
882 result
= dns_dbiterator_first(cleaner
->
884 if (result
== ISC_R_SUCCESS
) {
885 isc_log_write(dns_lctx
,
886 DNS_LOGCATEGORY_DATABASE
,
891 "reset and try again");
896 end_cleaning(cleaner
, event
);
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
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
));
921 * Do immediate cleaning.
924 dns_cache_clean(dns_cache_t
*cache
, isc_stdtime_t now
) {
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
)
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
,
940 if (result
!= ISC_R_SUCCESS
)
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() "
951 dns_result_totext(result
));
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
;
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
);
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
;
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
1023 if (size
== 0 || hiwater
== 0 || lowater
== 0)
1025 * Disable cache memory limiting.
1027 isc_mem_setwater(cache
->mctx
, water
, cache
, 0, 0);
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
);
1037 dns_cache_getcachesize(dns_cache_t
*cache
) {
1040 REQUIRE(VALID_CACHE(cache
));
1044 UNLOCK(&cache
->lock
);
1050 * The cleaner task is shutting down; do the necessary cleanup.
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
;
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
);
1065 isc_event_free(&event
);
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
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
);
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
)
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
);
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
);
1115 UNLOCK(&cache
->cleaner
.lock
);
1116 UNLOCK(&cache
->lock
);
1118 return (ISC_R_SUCCESS
);
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
;
1129 if (cache
->db
!= NULL
)
1130 dns_db_attach(cache
->db
, &db
);
1131 UNLOCK(&cache
->lock
);
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
;
1139 if (result
!= ISC_R_SUCCESS
)
1142 result
= dns_db_allrdatasets(cache
->db
, node
, NULL
,
1143 (isc_stdtime_t
)0, &iter
);
1144 if (result
!= ISC_R_SUCCESS
)
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
)
1161 if (result
== ISC_R_NOMORE
)
1162 result
= ISC_R_SUCCESS
;
1164 dns_rdatasetiter_destroy(&iter
);
1167 dns_db_detachnode(cache
->db
, &node
);