Remove building with NOCRYPTO option
[minix.git] / external / bsd / bind / dist / lib / dns / cache.c
blobd09f4df396fe0f0555bb21be71575fc4b86bde23
1 /* $NetBSD: cache.c,v 1.8 2014/12/10 04:37:58 christos Exp $ */
3 /*
4 * Copyright (C) 2004-2009, 2011-2013 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.91 2011/08/26 05:12:56 marka Exp */
22 /*! \file */
24 #include <config.h>
26 #include <isc/json.h>
27 #include <isc/mem.h>
28 #include <isc/print.h>
29 #include <isc/string.h>
30 #include <isc/stats.h>
31 #include <isc/task.h>
32 #include <isc/time.h>
33 #include <isc/timer.h>
34 #include <isc/util.h>
35 #include <isc/xml.h>
37 #include <dns/cache.h>
38 #include <dns/db.h>
39 #include <dns/dbiterator.h>
40 #include <dns/events.h>
41 #include <dns/lib.h>
42 #include <dns/log.h>
43 #include <dns/masterdump.h>
44 #include <dns/rdata.h>
45 #include <dns/rdataset.h>
46 #include <dns/rdatasetiter.h>
47 #include <dns/result.h>
48 #include <dns/stats.h>
50 #include "rbtdb.h"
52 #define CACHE_MAGIC ISC_MAGIC('$', '$', '$', '$')
53 #define VALID_CACHE(cache) ISC_MAGIC_VALID(cache, CACHE_MAGIC)
55 /*!
56 * Control incremental cleaning.
57 * DNS_CACHE_MINSIZE is how many bytes is the floor for dns_cache_setcachesize().
58 * See also DNS_CACHE_CLEANERINCREMENT
60 #define DNS_CACHE_MINSIZE 2097152U /*%< Bytes. 2097152 = 2 MB */
61 /*!
62 * Control incremental cleaning.
63 * CLEANERINCREMENT is how many nodes are examined in one pass.
64 * See also DNS_CACHE_MINSIZE
66 #define DNS_CACHE_CLEANERINCREMENT 1000U /*%< Number of nodes. */
68 /***
69 *** Types
70 ***/
73 * A cache_cleaner_t encapsulates the state of the periodic
74 * cache cleaning.
77 typedef struct cache_cleaner cache_cleaner_t;
79 typedef enum {
80 cleaner_s_idle, /*%< Waiting for cleaning-interval to expire. */
81 cleaner_s_busy, /*%< Currently cleaning. */
82 cleaner_s_done /*%< Freed enough memory after being overmem. */
83 } cleaner_state_t;
86 * Convenience macros for comprehensive assertion checking.
88 #define CLEANER_IDLE(c) ((c)->state == cleaner_s_idle && \
89 (c)->resched_event != NULL)
90 #define CLEANER_BUSY(c) ((c)->state == cleaner_s_busy && \
91 (c)->iterator != NULL && \
92 (c)->resched_event == NULL)
94 /*%
95 * Accesses to a cache cleaner object are synchronized through
96 * task/event serialization, or locked from the cache object.
98 struct cache_cleaner {
99 isc_mutex_t lock;
100 /*%<
101 * Locks overmem_event, overmem. Note: never allocate memory
102 * while holding this lock - that could lead to deadlock since
103 * the lock is take by water() which is called from the memory
104 * allocator.
107 dns_cache_t *cache;
108 isc_task_t *task;
109 unsigned int cleaning_interval; /*% The cleaning-interval from
110 named.conf, in seconds. */
111 isc_timer_t *cleaning_timer;
112 isc_event_t *resched_event; /*% Sent by cleaner task to
113 itself to reschedule */
114 isc_event_t *overmem_event;
116 dns_dbiterator_t *iterator;
117 unsigned int increment; /*% Number of names to
118 clean in one increment */
119 cleaner_state_t state; /*% Idle/Busy. */
120 isc_boolean_t overmem; /*% The cache is in an overmem state. */
121 isc_boolean_t replaceiterator;
125 * The actual cache object.
128 struct dns_cache {
129 /* Unlocked. */
130 unsigned int magic;
131 isc_mutex_t lock;
132 isc_mutex_t filelock;
133 isc_mem_t *mctx; /* Main cache memory */
134 isc_mem_t *hmctx; /* Heap memory */
135 char *name;
137 /* Locked by 'lock'. */
138 int references;
139 int live_tasks;
140 dns_rdataclass_t rdclass;
141 dns_db_t *db;
142 cache_cleaner_t cleaner;
143 char *db_type;
144 int db_argc;
145 char **db_argv;
146 size_t size;
147 isc_stats_t *stats;
149 /* Locked by 'filelock'. */
150 char *filename;
151 /* Access to the on-disk cache file is also locked by 'filelock'. */
154 /***
155 *** Functions
156 ***/
158 static isc_result_t
159 cache_cleaner_init(dns_cache_t *cache, isc_taskmgr_t *taskmgr,
160 isc_timermgr_t *timermgr, cache_cleaner_t *cleaner);
162 static void
163 cleaning_timer_action(isc_task_t *task, isc_event_t *event);
165 static void
166 incremental_cleaning_action(isc_task_t *task, isc_event_t *event);
168 static void
169 cleaner_shutdown_action(isc_task_t *task, isc_event_t *event);
171 static void
172 overmem_cleaning_action(isc_task_t *task, isc_event_t *event);
174 static inline isc_result_t
175 cache_create_db(dns_cache_t *cache, dns_db_t **db) {
176 return (dns_db_create(cache->mctx, cache->db_type, dns_rootname,
177 dns_dbtype_cache, cache->rdclass,
178 cache->db_argc, cache->db_argv, db));
181 isc_result_t
182 dns_cache_create(isc_mem_t *cmctx, isc_taskmgr_t *taskmgr,
183 isc_timermgr_t *timermgr, dns_rdataclass_t rdclass,
184 const char *db_type, unsigned int db_argc, char **db_argv,
185 dns_cache_t **cachep)
187 return (dns_cache_create3(cmctx, cmctx, taskmgr, timermgr, rdclass, "",
188 db_type, db_argc, db_argv, cachep));
191 isc_result_t
192 dns_cache_create2(isc_mem_t *cmctx, isc_taskmgr_t *taskmgr,
193 isc_timermgr_t *timermgr, dns_rdataclass_t rdclass,
194 const char *cachename, const char *db_type,
195 unsigned int db_argc, char **db_argv, dns_cache_t **cachep)
197 return (dns_cache_create3(cmctx, cmctx, taskmgr, timermgr, rdclass,
198 cachename, db_type, db_argc, db_argv,
199 cachep));
202 isc_result_t
203 dns_cache_create3(isc_mem_t *cmctx, isc_mem_t *hmctx, isc_taskmgr_t *taskmgr,
204 isc_timermgr_t *timermgr, dns_rdataclass_t rdclass,
205 const char *cachename, const char *db_type,
206 unsigned int db_argc, char **db_argv, dns_cache_t **cachep)
208 isc_result_t result;
209 dns_cache_t *cache;
210 int i, extra = 0;
211 isc_task_t *dbtask;
213 REQUIRE(cachep != NULL);
214 REQUIRE(*cachep == NULL);
215 REQUIRE(cmctx != NULL);
216 REQUIRE(hmctx != NULL);
217 REQUIRE(cachename != NULL);
219 cache = isc_mem_get(cmctx, sizeof(*cache));
220 if (cache == NULL)
221 return (ISC_R_NOMEMORY);
223 cache->mctx = cache->hmctx = NULL;
224 isc_mem_attach(cmctx, &cache->mctx);
225 isc_mem_attach(hmctx, &cache->hmctx);
227 cache->name = NULL;
228 if (cachename != NULL) {
229 cache->name = isc_mem_strdup(cmctx, cachename);
230 if (cache->name == NULL) {
231 result = ISC_R_NOMEMORY;
232 goto cleanup_mem;
236 result = isc_mutex_init(&cache->lock);
237 if (result != ISC_R_SUCCESS)
238 goto cleanup_mem;
240 result = isc_mutex_init(&cache->filelock);
241 if (result != ISC_R_SUCCESS)
242 goto cleanup_lock;
244 cache->references = 1;
245 cache->live_tasks = 0;
246 cache->rdclass = rdclass;
248 cache->stats = NULL;
249 result = isc_stats_create(cmctx, &cache->stats,
250 dns_cachestatscounter_max);
251 if (result != ISC_R_SUCCESS)
252 goto cleanup_filelock;
254 cache->db_type = isc_mem_strdup(cmctx, db_type);
255 if (cache->db_type == NULL) {
256 result = ISC_R_NOMEMORY;
257 goto cleanup_stats;
261 * For databases of type "rbt" we pass hmctx to dns_db_create()
262 * via cache->db_argv, followed by the rest of the arguments in
263 * db_argv (of which there really shouldn't be any).
265 if (strcmp(cache->db_type, "rbt") == 0)
266 extra = 1;
268 cache->db_argc = db_argc + extra;
269 cache->db_argv = NULL;
271 if (cache->db_argc != 0) {
272 cache->db_argv = isc_mem_get(cmctx,
273 cache->db_argc * sizeof(char *));
274 if (cache->db_argv == NULL) {
275 result = ISC_R_NOMEMORY;
276 goto cleanup_dbtype;
279 for (i = 0; i < cache->db_argc; i++)
280 cache->db_argv[i] = NULL;
282 cache->db_argv[0] = (char *) hmctx;
283 for (i = extra; i < cache->db_argc; i++) {
284 cache->db_argv[i] = isc_mem_strdup(cmctx,
285 db_argv[i - extra]);
286 if (cache->db_argv[i] == NULL) {
287 result = ISC_R_NOMEMORY;
288 goto cleanup_dbargv;
294 * Create the database
296 cache->db = NULL;
297 result = cache_create_db(cache, &cache->db);
298 if (result != ISC_R_SUCCESS)
299 goto cleanup_dbargv;
300 if (taskmgr != NULL) {
301 dbtask = NULL;
302 result = isc_task_create(taskmgr, 1, &dbtask);
303 if (result != ISC_R_SUCCESS)
304 goto cleanup_db;
305 dns_db_settask(cache->db, dbtask);
306 isc_task_detach(&dbtask);
309 cache->filename = NULL;
311 cache->magic = CACHE_MAGIC;
314 * RBT-type cache DB has its own mechanism of cache cleaning and doesn't
315 * need the control of the generic cleaner.
317 if (strcmp(db_type, "rbt") == 0)
318 result = cache_cleaner_init(cache, NULL, NULL, &cache->cleaner);
319 else {
320 result = cache_cleaner_init(cache, taskmgr, timermgr,
321 &cache->cleaner);
323 if (result != ISC_R_SUCCESS)
324 goto cleanup_db;
326 result = dns_db_setcachestats(cache->db, cache->stats);
327 if (result != ISC_R_SUCCESS)
328 goto cleanup_db;
331 *cachep = cache;
332 return (ISC_R_SUCCESS);
334 cleanup_db:
335 dns_db_detach(&cache->db);
336 cleanup_dbargv:
337 for (i = extra; i < cache->db_argc; i++)
338 if (cache->db_argv[i] != NULL)
339 isc_mem_free(cmctx, cache->db_argv[i]);
340 if (cache->db_argv != NULL)
341 isc_mem_put(cmctx, cache->db_argv,
342 cache->db_argc * sizeof(char *));
343 cleanup_dbtype:
344 isc_mem_free(cmctx, cache->db_type);
345 cleanup_filelock:
346 DESTROYLOCK(&cache->filelock);
347 cleanup_stats:
348 isc_stats_detach(&cache->stats);
349 cleanup_lock:
350 DESTROYLOCK(&cache->lock);
351 cleanup_mem:
352 if (cache->name != NULL)
353 isc_mem_free(cmctx, cache->name);
354 isc_mem_detach(&cache->hmctx);
355 isc_mem_putanddetach(&cache->mctx, cache, sizeof(*cache));
356 return (result);
359 static void
360 cache_free(dns_cache_t *cache) {
361 int i;
363 REQUIRE(VALID_CACHE(cache));
364 REQUIRE(cache->references == 0);
366 isc_mem_setwater(cache->mctx, NULL, NULL, 0, 0);
368 if (cache->cleaner.task != NULL)
369 isc_task_detach(&cache->cleaner.task);
371 if (cache->cleaner.overmem_event != NULL)
372 isc_event_free(&cache->cleaner.overmem_event);
374 if (cache->cleaner.resched_event != NULL)
375 isc_event_free(&cache->cleaner.resched_event);
377 if (cache->cleaner.iterator != NULL)
378 dns_dbiterator_destroy(&cache->cleaner.iterator);
380 DESTROYLOCK(&cache->cleaner.lock);
382 if (cache->filename) {
383 isc_mem_free(cache->mctx, cache->filename);
384 cache->filename = NULL;
387 if (cache->db != NULL)
388 dns_db_detach(&cache->db);
390 if (cache->db_argv != NULL) {
392 * We don't free db_argv[0] in "rbt" cache databases
393 * as it's a pointer to hmctx
395 int extra = 0;
396 if (strcmp(cache->db_type, "rbt") == 0)
397 extra = 1;
398 for (i = extra; i < cache->db_argc; i++)
399 if (cache->db_argv[i] != NULL)
400 isc_mem_free(cache->mctx, cache->db_argv[i]);
401 isc_mem_put(cache->mctx, cache->db_argv,
402 cache->db_argc * sizeof(char *));
405 if (cache->db_type != NULL)
406 isc_mem_free(cache->mctx, cache->db_type);
408 if (cache->name != NULL)
409 isc_mem_free(cache->mctx, cache->name);
411 if (cache->stats != NULL)
412 isc_stats_detach(&cache->stats);
414 DESTROYLOCK(&cache->lock);
415 DESTROYLOCK(&cache->filelock);
417 cache->magic = 0;
418 isc_mem_detach(&cache->hmctx);
419 isc_mem_putanddetach(&cache->mctx, cache, sizeof(*cache));
423 void
424 dns_cache_attach(dns_cache_t *cache, dns_cache_t **targetp) {
426 REQUIRE(VALID_CACHE(cache));
427 REQUIRE(targetp != NULL && *targetp == NULL);
429 LOCK(&cache->lock);
430 cache->references++;
431 UNLOCK(&cache->lock);
433 *targetp = cache;
436 void
437 dns_cache_detach(dns_cache_t **cachep) {
438 dns_cache_t *cache;
439 isc_boolean_t free_cache = ISC_FALSE;
441 REQUIRE(cachep != NULL);
442 cache = *cachep;
443 REQUIRE(VALID_CACHE(cache));
445 LOCK(&cache->lock);
446 REQUIRE(cache->references > 0);
447 cache->references--;
448 if (cache->references == 0) {
449 cache->cleaner.overmem = ISC_FALSE;
450 free_cache = ISC_TRUE;
453 *cachep = NULL;
455 if (free_cache) {
457 * When the cache is shut down, dump it to a file if one is
458 * specified.
460 isc_result_t result = dns_cache_dump(cache);
461 if (result != ISC_R_SUCCESS)
462 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
463 DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
464 "error dumping cache: %s ",
465 isc_result_totext(result));
468 * If the cleaner task exists, let it free the cache.
470 if (cache->live_tasks > 0) {
471 isc_task_shutdown(cache->cleaner.task);
472 free_cache = ISC_FALSE;
476 UNLOCK(&cache->lock);
478 if (free_cache)
479 cache_free(cache);
482 void
483 dns_cache_attachdb(dns_cache_t *cache, dns_db_t **dbp) {
484 REQUIRE(VALID_CACHE(cache));
485 REQUIRE(dbp != NULL && *dbp == NULL);
486 REQUIRE(cache->db != NULL);
488 LOCK(&cache->lock);
489 dns_db_attach(cache->db, dbp);
490 UNLOCK(&cache->lock);
494 isc_result_t
495 dns_cache_setfilename(dns_cache_t *cache, const char *filename) {
496 char *newname;
498 REQUIRE(VALID_CACHE(cache));
499 REQUIRE(filename != NULL);
501 newname = isc_mem_strdup(cache->mctx, filename);
502 if (newname == NULL)
503 return (ISC_R_NOMEMORY);
505 LOCK(&cache->filelock);
506 if (cache->filename)
507 isc_mem_free(cache->mctx, cache->filename);
508 cache->filename = newname;
509 UNLOCK(&cache->filelock);
511 return (ISC_R_SUCCESS);
514 isc_result_t
515 dns_cache_load(dns_cache_t *cache) {
516 isc_result_t result;
518 REQUIRE(VALID_CACHE(cache));
520 if (cache->filename == NULL)
521 return (ISC_R_SUCCESS);
523 LOCK(&cache->filelock);
524 result = dns_db_load(cache->db, cache->filename);
525 UNLOCK(&cache->filelock);
527 return (result);
530 isc_result_t
531 dns_cache_dump(dns_cache_t *cache) {
532 isc_result_t result;
534 REQUIRE(VALID_CACHE(cache));
536 if (cache->filename == NULL)
537 return (ISC_R_SUCCESS);
539 LOCK(&cache->filelock);
540 result = dns_master_dump(cache->mctx, cache->db, NULL,
541 &dns_master_style_cache, cache->filename);
542 UNLOCK(&cache->filelock);
543 return (result);
547 void
548 dns_cache_setcleaninginterval(dns_cache_t *cache, unsigned int t) {
549 isc_interval_t interval;
550 isc_result_t result;
552 LOCK(&cache->lock);
555 * It may be the case that the cache has already shut down.
556 * If so, it has no timer.
558 if (cache->cleaner.cleaning_timer == NULL)
559 goto unlock;
561 cache->cleaner.cleaning_interval = t;
563 if (t == 0) {
564 result = isc_timer_reset(cache->cleaner.cleaning_timer,
565 isc_timertype_inactive,
566 NULL, NULL, ISC_TRUE);
567 } else {
568 isc_interval_set(&interval, cache->cleaner.cleaning_interval,
570 result = isc_timer_reset(cache->cleaner.cleaning_timer,
571 isc_timertype_ticker,
572 NULL, &interval, ISC_FALSE);
574 if (result != ISC_R_SUCCESS)
575 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
576 DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
577 "could not set cache cleaning interval: %s",
578 isc_result_totext(result));
580 unlock:
581 UNLOCK(&cache->lock);
584 unsigned int
585 dns_cache_getcleaninginterval(dns_cache_t *cache) {
586 unsigned int t;
588 REQUIRE(VALID_CACHE(cache));
590 LOCK(&cache->lock);
591 t = cache->cleaner.cleaning_interval;
592 UNLOCK(&cache->lock);
594 return (t);
597 const char *
598 dns_cache_getname(dns_cache_t *cache) {
599 REQUIRE(VALID_CACHE(cache));
601 return (cache->name);
605 * Initialize the cache cleaner object at *cleaner.
606 * Space for the object must be allocated by the caller.
609 static isc_result_t
610 cache_cleaner_init(dns_cache_t *cache, isc_taskmgr_t *taskmgr,
611 isc_timermgr_t *timermgr, cache_cleaner_t *cleaner)
613 isc_result_t result;
615 result = isc_mutex_init(&cleaner->lock);
616 if (result != ISC_R_SUCCESS)
617 goto fail;
619 cleaner->increment = DNS_CACHE_CLEANERINCREMENT;
620 cleaner->state = cleaner_s_idle;
621 cleaner->cache = cache;
622 cleaner->iterator = NULL;
623 cleaner->overmem = ISC_FALSE;
624 cleaner->replaceiterator = ISC_FALSE;
626 cleaner->task = NULL;
627 cleaner->cleaning_timer = NULL;
628 cleaner->resched_event = NULL;
629 cleaner->overmem_event = NULL;
630 cleaner->cleaning_interval = 0; /* Initially turned off. */
632 result = dns_db_createiterator(cleaner->cache->db, ISC_FALSE,
633 &cleaner->iterator);
634 if (result != ISC_R_SUCCESS)
635 goto cleanup;
637 if (taskmgr != NULL && timermgr != NULL) {
638 result = isc_task_create(taskmgr, 1, &cleaner->task);
639 if (result != ISC_R_SUCCESS) {
640 UNEXPECTED_ERROR(__FILE__, __LINE__,
641 "isc_task_create() failed: %s",
642 dns_result_totext(result));
643 result = ISC_R_UNEXPECTED;
644 goto cleanup;
646 cleaner->cache->live_tasks++;
647 isc_task_setname(cleaner->task, "cachecleaner", cleaner);
649 result = isc_task_onshutdown(cleaner->task,
650 cleaner_shutdown_action, cache);
651 if (result != ISC_R_SUCCESS) {
652 UNEXPECTED_ERROR(__FILE__, __LINE__,
653 "cache cleaner: "
654 "isc_task_onshutdown() failed: %s",
655 dns_result_totext(result));
656 goto cleanup;
659 result = isc_timer_create(timermgr, isc_timertype_inactive,
660 NULL, NULL, cleaner->task,
661 cleaning_timer_action, cleaner,
662 &cleaner->cleaning_timer);
663 if (result != ISC_R_SUCCESS) {
664 UNEXPECTED_ERROR(__FILE__, __LINE__,
665 "isc_timer_create() failed: %s",
666 dns_result_totext(result));
667 result = ISC_R_UNEXPECTED;
668 goto cleanup;
671 cleaner->resched_event =
672 isc_event_allocate(cache->mctx, cleaner,
673 DNS_EVENT_CACHECLEAN,
674 incremental_cleaning_action,
675 cleaner, sizeof(isc_event_t));
676 if (cleaner->resched_event == NULL) {
677 result = ISC_R_NOMEMORY;
678 goto cleanup;
681 cleaner->overmem_event =
682 isc_event_allocate(cache->mctx, cleaner,
683 DNS_EVENT_CACHEOVERMEM,
684 overmem_cleaning_action,
685 cleaner, sizeof(isc_event_t));
686 if (cleaner->overmem_event == NULL) {
687 result = ISC_R_NOMEMORY;
688 goto cleanup;
692 return (ISC_R_SUCCESS);
694 cleanup:
695 if (cleaner->overmem_event != NULL)
696 isc_event_free(&cleaner->overmem_event);
697 if (cleaner->resched_event != NULL)
698 isc_event_free(&cleaner->resched_event);
699 if (cleaner->cleaning_timer != NULL)
700 isc_timer_detach(&cleaner->cleaning_timer);
701 if (cleaner->task != NULL)
702 isc_task_detach(&cleaner->task);
703 if (cleaner->iterator != NULL)
704 dns_dbiterator_destroy(&cleaner->iterator);
705 DESTROYLOCK(&cleaner->lock);
706 fail:
707 return (result);
710 static void
711 begin_cleaning(cache_cleaner_t *cleaner) {
712 isc_result_t result = ISC_R_SUCCESS;
714 REQUIRE(CLEANER_IDLE(cleaner));
717 * Create an iterator, if it does not already exist, and
718 * position it at the beginning of the cache.
720 if (cleaner->iterator == NULL)
721 result = dns_db_createiterator(cleaner->cache->db, ISC_FALSE,
722 &cleaner->iterator);
723 if (result != ISC_R_SUCCESS)
724 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
725 DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
726 "cache cleaner could not create "
727 "iterator: %s", isc_result_totext(result));
728 else {
729 dns_dbiterator_setcleanmode(cleaner->iterator, ISC_TRUE);
730 result = dns_dbiterator_first(cleaner->iterator);
732 if (result != ISC_R_SUCCESS) {
734 * If the result is ISC_R_NOMORE, the database is empty,
735 * so there is nothing to be cleaned.
737 if (result != ISC_R_NOMORE && cleaner->iterator != NULL) {
738 UNEXPECTED_ERROR(__FILE__, __LINE__,
739 "cache cleaner: "
740 "dns_dbiterator_first() failed: %s",
741 dns_result_totext(result));
742 dns_dbiterator_destroy(&cleaner->iterator);
743 } else if (cleaner->iterator != NULL) {
744 result = dns_dbiterator_pause(cleaner->iterator);
745 RUNTIME_CHECK(result == ISC_R_SUCCESS);
747 } else {
749 * Pause the iterator to free its lock.
751 result = dns_dbiterator_pause(cleaner->iterator);
752 RUNTIME_CHECK(result == ISC_R_SUCCESS);
754 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
755 DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1),
756 "begin cache cleaning, mem inuse %lu",
757 (unsigned long)isc_mem_inuse(cleaner->cache->mctx));
758 cleaner->state = cleaner_s_busy;
759 isc_task_send(cleaner->task, &cleaner->resched_event);
762 return;
765 static void
766 end_cleaning(cache_cleaner_t *cleaner, isc_event_t *event) {
767 isc_result_t result;
769 REQUIRE(CLEANER_BUSY(cleaner));
770 REQUIRE(event != NULL);
772 result = dns_dbiterator_pause(cleaner->iterator);
773 if (result != ISC_R_SUCCESS)
774 dns_dbiterator_destroy(&cleaner->iterator);
776 dns_cache_setcleaninginterval(cleaner->cache,
777 cleaner->cleaning_interval);
779 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
780 ISC_LOG_DEBUG(1), "end cache cleaning, mem inuse %lu",
781 (unsigned long)isc_mem_inuse(cleaner->cache->mctx));
783 cleaner->state = cleaner_s_idle;
784 cleaner->resched_event = event;
788 * This is run once for every cache-cleaning-interval as defined in named.conf.
790 static void
791 cleaning_timer_action(isc_task_t *task, isc_event_t *event) {
792 cache_cleaner_t *cleaner = event->ev_arg;
794 UNUSED(task);
796 INSIST(task == cleaner->task);
797 INSIST(event->ev_type == ISC_TIMEREVENT_TICK);
799 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
800 ISC_LOG_DEBUG(1), "cache cleaning timer fired, "
801 "cleaner state = %d", cleaner->state);
803 if (cleaner->state == cleaner_s_idle)
804 begin_cleaning(cleaner);
806 isc_event_free(&event);
810 * This is called when the cache either surpasses its upper limit
811 * or shrinks beyond its lower limit.
813 static void
814 overmem_cleaning_action(isc_task_t *task, isc_event_t *event) {
815 cache_cleaner_t *cleaner = event->ev_arg;
816 isc_boolean_t want_cleaning = ISC_FALSE;
818 UNUSED(task);
820 INSIST(task == cleaner->task);
821 INSIST(event->ev_type == DNS_EVENT_CACHEOVERMEM);
822 INSIST(cleaner->overmem_event == NULL);
824 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
825 ISC_LOG_DEBUG(1), "overmem_cleaning_action called, "
826 "overmem = %d, state = %d", cleaner->overmem,
827 cleaner->state);
829 LOCK(&cleaner->lock);
831 if (cleaner->overmem) {
832 if (cleaner->state == cleaner_s_idle)
833 want_cleaning = ISC_TRUE;
834 } else {
835 if (cleaner->state == cleaner_s_busy)
837 * end_cleaning() can't be called here because
838 * then both cleaner->overmem_event and
839 * cleaner->resched_event will point to this
840 * event. Set the state to done, and then
841 * when the incremental_cleaning_action() event
842 * is posted, it will handle the end_cleaning.
844 cleaner->state = cleaner_s_done;
847 cleaner->overmem_event = event;
849 UNLOCK(&cleaner->lock);
851 if (want_cleaning)
852 begin_cleaning(cleaner);
856 * Do incremental cleaning.
858 static void
859 incremental_cleaning_action(isc_task_t *task, isc_event_t *event) {
860 cache_cleaner_t *cleaner = event->ev_arg;
861 isc_result_t result;
862 unsigned int n_names;
863 isc_time_t start;
865 UNUSED(task);
867 INSIST(task == cleaner->task);
868 INSIST(event->ev_type == DNS_EVENT_CACHECLEAN);
870 if (cleaner->state == cleaner_s_done) {
871 cleaner->state = cleaner_s_busy;
872 end_cleaning(cleaner, event);
873 LOCK(&cleaner->cache->lock);
874 LOCK(&cleaner->lock);
875 if (cleaner->replaceiterator) {
876 dns_dbiterator_destroy(&cleaner->iterator);
877 (void) dns_db_createiterator(cleaner->cache->db,
878 ISC_FALSE,
879 &cleaner->iterator);
880 cleaner->replaceiterator = ISC_FALSE;
882 UNLOCK(&cleaner->lock);
883 UNLOCK(&cleaner->cache->lock);
884 return;
887 INSIST(CLEANER_BUSY(cleaner));
889 n_names = cleaner->increment;
891 REQUIRE(DNS_DBITERATOR_VALID(cleaner->iterator));
893 isc_time_now(&start);
894 while (n_names-- > 0) {
895 dns_dbnode_t *node = NULL;
897 result = dns_dbiterator_current(cleaner->iterator, &node,
898 NULL);
899 if (result != ISC_R_SUCCESS) {
900 UNEXPECTED_ERROR(__FILE__, __LINE__,
901 "cache cleaner: dns_dbiterator_current() "
902 "failed: %s", dns_result_totext(result));
904 end_cleaning(cleaner, event);
905 return;
909 * The node was not needed, but was required by
910 * dns_dbiterator_current(). Give up its reference.
912 dns_db_detachnode(cleaner->cache->db, &node);
915 * Step to the next node.
917 result = dns_dbiterator_next(cleaner->iterator);
919 if (result != ISC_R_SUCCESS) {
921 * Either the end was reached (ISC_R_NOMORE) or
922 * some error was signaled. If the cache is still
923 * overmem and no error was encountered,
924 * keep trying to clean it, otherwise stop cleaning.
926 if (result != ISC_R_NOMORE)
927 UNEXPECTED_ERROR(__FILE__, __LINE__,
928 "cache cleaner: "
929 "dns_dbiterator_next() "
930 "failed: %s",
931 dns_result_totext(result));
932 else if (cleaner->overmem) {
933 result = dns_dbiterator_first(cleaner->
934 iterator);
935 if (result == ISC_R_SUCCESS) {
936 isc_log_write(dns_lctx,
937 DNS_LOGCATEGORY_DATABASE,
938 DNS_LOGMODULE_CACHE,
939 ISC_LOG_DEBUG(1),
940 "cache cleaner: "
941 "still overmem, "
942 "reset and try again");
943 continue;
947 end_cleaning(cleaner, event);
948 return;
953 * We have successfully performed a cleaning increment but have
954 * not gone through the entire cache. Free the iterator locks
955 * and reschedule another batch. If it fails, just try to continue
956 * anyway.
958 result = dns_dbiterator_pause(cleaner->iterator);
959 RUNTIME_CHECK(result == ISC_R_SUCCESS);
961 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
962 ISC_LOG_DEBUG(1), "cache cleaner: checked %u nodes, "
963 "mem inuse %lu, sleeping", cleaner->increment,
964 (unsigned long)isc_mem_inuse(cleaner->cache->mctx));
966 isc_task_send(task, &event);
967 INSIST(CLEANER_BUSY(cleaner));
968 return;
972 * Do immediate cleaning.
974 isc_result_t
975 dns_cache_clean(dns_cache_t *cache, isc_stdtime_t now) {
976 isc_result_t result;
977 dns_dbiterator_t *iterator = NULL;
979 REQUIRE(VALID_CACHE(cache));
981 result = dns_db_createiterator(cache->db, 0, &iterator);
982 if (result != ISC_R_SUCCESS)
983 return result;
985 result = dns_dbiterator_first(iterator);
987 while (result == ISC_R_SUCCESS) {
988 dns_dbnode_t *node = NULL;
989 result = dns_dbiterator_current(iterator, &node,
990 (dns_name_t *)NULL);
991 if (result != ISC_R_SUCCESS)
992 break;
995 * Check TTLs, mark expired rdatasets stale.
997 result = dns_db_expirenode(cache->db, node, now);
998 if (result != ISC_R_SUCCESS) {
999 UNEXPECTED_ERROR(__FILE__, __LINE__,
1000 "cache cleaner: dns_db_expirenode() "
1001 "failed: %s",
1002 dns_result_totext(result));
1004 * Continue anyway.
1009 * This is where the actual freeing takes place.
1011 dns_db_detachnode(cache->db, &node);
1013 result = dns_dbiterator_next(iterator);
1016 dns_dbiterator_destroy(&iterator);
1018 if (result == ISC_R_NOMORE)
1019 result = ISC_R_SUCCESS;
1021 return (result);
1024 static void
1025 water(void *arg, int mark) {
1026 dns_cache_t *cache = arg;
1027 isc_boolean_t overmem = ISC_TF(mark == ISC_MEM_HIWATER);
1029 REQUIRE(VALID_CACHE(cache));
1031 LOCK(&cache->cleaner.lock);
1033 if (overmem != cache->cleaner.overmem) {
1034 dns_db_overmem(cache->db, overmem);
1035 cache->cleaner.overmem = overmem;
1036 isc_mem_waterack(cache->mctx, mark);
1039 if (cache->cleaner.overmem_event != NULL)
1040 isc_task_send(cache->cleaner.task,
1041 &cache->cleaner.overmem_event);
1043 UNLOCK(&cache->cleaner.lock);
1046 void
1047 dns_cache_setcachesize(dns_cache_t *cache, size_t size) {
1048 size_t hiwater, lowater;
1050 REQUIRE(VALID_CACHE(cache));
1053 * Impose a minimum cache size; pathological things happen if there
1054 * is too little room.
1056 if (size != 0U && size < DNS_CACHE_MINSIZE)
1057 size = DNS_CACHE_MINSIZE;
1059 LOCK(&cache->lock);
1060 cache->size = size;
1061 UNLOCK(&cache->lock);
1063 hiwater = size - (size >> 3); /* Approximately 7/8ths. */
1064 lowater = size - (size >> 2); /* Approximately 3/4ths. */
1067 * If the cache was overmem and cleaning, but now with the new limits
1068 * it is no longer in an overmem condition, then the next
1069 * isc_mem_put for cache memory will do the right thing and trigger
1070 * water().
1073 if (size == 0U || hiwater == 0U || lowater == 0U)
1075 * Disable cache memory limiting.
1077 isc_mem_setwater(cache->mctx, water, cache, 0, 0);
1078 else
1080 * Establish new cache memory limits (either for the first
1081 * time, or replacing other limits).
1083 isc_mem_setwater(cache->mctx, water, cache, hiwater, lowater);
1086 size_t
1087 dns_cache_getcachesize(dns_cache_t *cache) {
1088 size_t size;
1090 REQUIRE(VALID_CACHE(cache));
1092 LOCK(&cache->lock);
1093 size = cache->size;
1094 UNLOCK(&cache->lock);
1096 return (size);
1100 * The cleaner task is shutting down; do the necessary cleanup.
1102 static void
1103 cleaner_shutdown_action(isc_task_t *task, isc_event_t *event) {
1104 dns_cache_t *cache = event->ev_arg;
1105 isc_boolean_t should_free = ISC_FALSE;
1107 UNUSED(task);
1109 INSIST(task == cache->cleaner.task);
1110 INSIST(event->ev_type == ISC_TASKEVENT_SHUTDOWN);
1112 if (CLEANER_BUSY(&cache->cleaner))
1113 end_cleaning(&cache->cleaner, event);
1114 else
1115 isc_event_free(&event);
1117 LOCK(&cache->lock);
1119 cache->live_tasks--;
1120 INSIST(cache->live_tasks == 0);
1122 if (cache->references == 0)
1123 should_free = ISC_TRUE;
1126 * By detaching the timer in the context of its task,
1127 * we are guaranteed that there will be no further timer
1128 * events.
1130 if (cache->cleaner.cleaning_timer != NULL)
1131 isc_timer_detach(&cache->cleaner.cleaning_timer);
1133 /* Make sure we don't reschedule anymore. */
1134 (void)isc_task_purge(task, NULL, DNS_EVENT_CACHECLEAN, NULL);
1136 UNLOCK(&cache->lock);
1138 if (should_free)
1139 cache_free(cache);
1142 isc_result_t
1143 dns_cache_flush(dns_cache_t *cache) {
1144 dns_db_t *db = NULL;
1145 isc_result_t result;
1147 result = cache_create_db(cache, &db);
1148 if (result != ISC_R_SUCCESS)
1149 return (result);
1151 LOCK(&cache->lock);
1152 LOCK(&cache->cleaner.lock);
1153 if (cache->cleaner.state == cleaner_s_idle) {
1154 if (cache->cleaner.iterator != NULL)
1155 dns_dbiterator_destroy(&cache->cleaner.iterator);
1156 (void) dns_db_createiterator(db, ISC_FALSE,
1157 &cache->cleaner.iterator);
1158 } else {
1159 if (cache->cleaner.state == cleaner_s_busy)
1160 cache->cleaner.state = cleaner_s_done;
1161 cache->cleaner.replaceiterator = ISC_TRUE;
1163 dns_db_detach(&cache->db);
1164 cache->db = db;
1165 dns_db_setcachestats(cache->db, cache->stats);
1166 UNLOCK(&cache->cleaner.lock);
1167 UNLOCK(&cache->lock);
1169 return (ISC_R_SUCCESS);
1172 static isc_result_t
1173 clearnode(dns_db_t *db, dns_dbnode_t *node) {
1174 isc_result_t result;
1175 dns_rdatasetiter_t *iter = NULL;
1177 result = dns_db_allrdatasets(db, node, NULL, (isc_stdtime_t)0, &iter);
1178 if (result != ISC_R_SUCCESS)
1179 return (result);
1181 for (result = dns_rdatasetiter_first(iter);
1182 result == ISC_R_SUCCESS;
1183 result = dns_rdatasetiter_next(iter))
1185 dns_rdataset_t rdataset;
1186 dns_rdataset_init(&rdataset);
1188 dns_rdatasetiter_current(iter, &rdataset);
1189 result = dns_db_deleterdataset(db, node, NULL,
1190 rdataset.type, rdataset.covers);
1191 dns_rdataset_disassociate(&rdataset);
1192 if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED)
1193 break;
1196 if (result == ISC_R_NOMORE)
1197 result = ISC_R_SUCCESS;
1199 dns_rdatasetiter_destroy(&iter);
1200 return (result);
1203 static isc_result_t
1204 cleartree(dns_db_t *db, dns_name_t *name) {
1205 isc_result_t result, answer = ISC_R_SUCCESS;
1206 dns_dbiterator_t *iter = NULL;
1207 dns_dbnode_t *node = NULL;
1208 dns_fixedname_t fnodename;
1209 dns_name_t *nodename;
1211 dns_fixedname_init(&fnodename);
1212 nodename = dns_fixedname_name(&fnodename);
1214 result = dns_db_createiterator(db, 0, &iter);
1215 if (result != ISC_R_SUCCESS)
1216 goto cleanup;
1218 result = dns_dbiterator_seek(iter, name);
1219 if (result != ISC_R_SUCCESS)
1220 goto cleanup;
1222 while (result == ISC_R_SUCCESS) {
1223 result = dns_dbiterator_current(iter, &node, nodename);
1224 if (result == DNS_R_NEWORIGIN)
1225 result = ISC_R_SUCCESS;
1226 if (result != ISC_R_SUCCESS)
1227 goto cleanup;
1229 * Are we done?
1231 if (! dns_name_issubdomain(nodename, name))
1232 goto cleanup;
1235 * If clearnode fails record and move onto the next node.
1237 result = clearnode(db, node);
1238 if (result != ISC_R_SUCCESS && answer == ISC_R_SUCCESS)
1239 answer = result;
1240 dns_db_detachnode(db, &node);
1241 result = dns_dbiterator_next(iter);
1244 cleanup:
1245 if (result == ISC_R_NOMORE || result == ISC_R_NOTFOUND)
1246 result = ISC_R_SUCCESS;
1247 if (result != ISC_R_SUCCESS && answer == ISC_R_SUCCESS)
1248 answer = result;
1249 if (node != NULL)
1250 dns_db_detachnode(db, &node);
1251 if (iter != NULL)
1252 dns_dbiterator_destroy(&iter);
1254 return (answer);
1257 isc_result_t
1258 dns_cache_flushname(dns_cache_t *cache, dns_name_t *name) {
1259 return (dns_cache_flushnode(cache, name, ISC_FALSE));
1262 isc_result_t
1263 dns_cache_flushnode(dns_cache_t *cache, dns_name_t *name,
1264 isc_boolean_t tree)
1266 isc_result_t result;
1267 dns_dbnode_t *node = NULL;
1268 dns_db_t *db = NULL;
1270 if (dns_name_equal(name, dns_rootname))
1271 return (dns_cache_flush(cache));
1273 LOCK(&cache->lock);
1274 if (cache->db != NULL)
1275 dns_db_attach(cache->db, &db);
1276 UNLOCK(&cache->lock);
1277 if (db == NULL)
1278 return (ISC_R_SUCCESS);
1280 if (tree) {
1281 result = cleartree(cache->db, name);
1282 } else {
1283 result = dns_db_findnode(cache->db, name, ISC_FALSE, &node);
1284 if (result == ISC_R_NOTFOUND) {
1285 result = ISC_R_SUCCESS;
1286 goto cleanup_db;
1288 if (result != ISC_R_SUCCESS)
1289 goto cleanup_db;
1290 result = clearnode(cache->db, node);
1291 dns_db_detachnode(cache->db, &node);
1294 cleanup_db:
1295 dns_db_detach(&db);
1296 return (result);
1299 isc_stats_t *
1300 dns_cache_getstats(dns_cache_t *cache) {
1301 REQUIRE(VALID_CACHE(cache));
1302 return (cache->stats);
1305 void
1306 dns_cache_updatestats(dns_cache_t *cache, isc_result_t result) {
1307 REQUIRE(VALID_CACHE(cache));
1308 if (cache->stats == NULL)
1309 return;
1311 switch (result) {
1312 case ISC_R_SUCCESS:
1313 case DNS_R_NCACHENXDOMAIN:
1314 case DNS_R_NCACHENXRRSET:
1315 case DNS_R_CNAME:
1316 case DNS_R_DNAME:
1317 case DNS_R_GLUE:
1318 case DNS_R_ZONECUT:
1319 isc_stats_increment(cache->stats,
1320 dns_cachestatscounter_queryhits);
1321 break;
1322 default:
1323 isc_stats_increment(cache->stats,
1324 dns_cachestatscounter_querymisses);
1329 * XXX: Much of the following code has been copied in from statschannel.c.
1330 * We should refactor this into a generic function in stats.c that can be
1331 * called from both places.
1333 typedef struct
1334 cache_dumparg {
1335 isc_statsformat_t type;
1336 void *arg; /* type dependent argument */
1337 int ncounters; /* for general statistics */
1338 int *counterindices; /* for general statistics */
1339 isc_uint64_t *countervalues; /* for general statistics */
1340 isc_result_t result;
1341 } cache_dumparg_t;
1343 static void
1344 getcounter(isc_statscounter_t counter, isc_uint64_t val, void *arg) {
1345 cache_dumparg_t *dumparg = arg;
1347 REQUIRE(counter < dumparg->ncounters);
1348 dumparg->countervalues[counter] = val;
1351 static void
1352 getcounters(isc_stats_t *stats, isc_statsformat_t type, int ncounters,
1353 int *indices, isc_uint64_t *values)
1355 cache_dumparg_t dumparg;
1357 memset(values, 0, sizeof(values[0]) * ncounters);
1359 dumparg.type = type;
1360 dumparg.ncounters = ncounters;
1361 dumparg.counterindices = indices;
1362 dumparg.countervalues = values;
1364 isc_stats_dump(stats, getcounter, &dumparg, ISC_STATSDUMP_VERBOSE);
1367 void
1368 dns_cache_dumpstats(dns_cache_t *cache, FILE *fp) {
1369 int indices[dns_cachestatscounter_max];
1370 isc_uint64_t values[dns_cachestatscounter_max];
1372 REQUIRE(VALID_CACHE(cache));
1374 getcounters(cache->stats, isc_statsformat_file,
1375 dns_cachestatscounter_max, indices, values);
1377 fprintf(fp, "%20" ISC_PRINT_QUADFORMAT "u %s\n",
1378 values[dns_cachestatscounter_hits],
1379 "cache hits");
1380 fprintf(fp, "%20" ISC_PRINT_QUADFORMAT "u %s\n",
1381 values[dns_cachestatscounter_misses],
1382 "cache misses");
1383 fprintf(fp, "%20" ISC_PRINT_QUADFORMAT "u %s\n",
1384 values[dns_cachestatscounter_queryhits],
1385 "cache hits (from query)");
1386 fprintf(fp, "%20" ISC_PRINT_QUADFORMAT "u %s\n",
1387 values[dns_cachestatscounter_querymisses],
1388 "cache misses (from query)");
1389 fprintf(fp, "%20" ISC_PRINT_QUADFORMAT "u %s\n",
1390 values[dns_cachestatscounter_deletelru],
1391 "cache records deleted due to memory exhaustion");
1392 fprintf(fp, "%20" ISC_PRINT_QUADFORMAT "u %s\n",
1393 values[dns_cachestatscounter_deletettl],
1394 "cache records deleted due to TTL expiration");
1395 fprintf(fp, "%20u %s\n", dns_db_nodecount(cache->db),
1396 "cache database nodes");
1397 fprintf(fp, "%20u %s\n", dns_db_hashsize(cache->db),
1398 "cache database hash buckets");
1400 fprintf(fp, "%20u %s\n", (unsigned int) isc_mem_total(cache->mctx),
1401 "cache tree memory total");
1402 fprintf(fp, "%20u %s\n", (unsigned int) isc_mem_inuse(cache->mctx),
1403 "cache tree memory in use");
1404 fprintf(fp, "%20u %s\n", (unsigned int) isc_mem_maxinuse(cache->mctx),
1405 "cache tree highest memory in use");
1407 fprintf(fp, "%20u %s\n", (unsigned int) isc_mem_total(cache->hmctx),
1408 "cache heap memory total");
1409 fprintf(fp, "%20u %s\n", (unsigned int) isc_mem_inuse(cache->hmctx),
1410 "cache heap memory in use");
1411 fprintf(fp, "%20u %s\n", (unsigned int) isc_mem_maxinuse(cache->hmctx),
1412 "cache heap highest memory in use");
1415 #ifdef HAVE_LIBXML2
1416 #define TRY0(a) do { xmlrc = (a); if (xmlrc < 0) goto error; } while(/*CONSTCOND*/0)
1417 static int
1418 renderstat(const char *name, isc_uint64_t value, xmlTextWriterPtr writer) {
1419 int xmlrc;
1421 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter"));
1422 TRY0(xmlTextWriterWriteAttribute(writer,
1423 ISC_XMLCHAR "name", ISC_XMLCHAR name));
1424 TRY0(xmlTextWriterWriteFormatString(writer,
1425 "%" ISC_PRINT_QUADFORMAT "u",
1426 value));
1427 TRY0(xmlTextWriterEndElement(writer)); /* counter */
1429 error:
1430 return (xmlrc);
1434 dns_cache_renderxml(dns_cache_t *cache, xmlTextWriterPtr writer) {
1435 int indices[dns_cachestatscounter_max];
1436 isc_uint64_t values[dns_cachestatscounter_max];
1437 int xmlrc;
1439 REQUIRE(VALID_CACHE(cache));
1441 getcounters(cache->stats, isc_statsformat_file,
1442 dns_cachestatscounter_max, indices, values);
1443 TRY0(renderstat("CacheHits",
1444 values[dns_cachestatscounter_hits], writer));
1445 TRY0(renderstat("CacheMisses",
1446 values[dns_cachestatscounter_misses], writer));
1447 TRY0(renderstat("QueryHits",
1448 values[dns_cachestatscounter_queryhits], writer));
1449 TRY0(renderstat("QueryMisses",
1450 values[dns_cachestatscounter_querymisses], writer));
1451 TRY0(renderstat("DeleteLRU",
1452 values[dns_cachestatscounter_deletelru], writer));
1453 TRY0(renderstat("DeleteTTL",
1454 values[dns_cachestatscounter_deletettl], writer));
1456 TRY0(renderstat("CacheNodes", dns_db_nodecount(cache->db), writer));
1457 TRY0(renderstat("CacheBuckets", dns_db_hashsize(cache->db), writer));
1459 TRY0(renderstat("TreeMemTotal", isc_mem_total(cache->mctx), writer));
1460 TRY0(renderstat("TreeMemInUse", isc_mem_inuse(cache->mctx), writer));
1461 TRY0(renderstat("TreeMemMax", isc_mem_maxinuse(cache->mctx), writer));
1463 TRY0(renderstat("HeapMemTotal", isc_mem_total(cache->hmctx), writer));
1464 TRY0(renderstat("HeapMemInUse", isc_mem_inuse(cache->hmctx), writer));
1465 TRY0(renderstat("HeapMemMax", isc_mem_maxinuse(cache->hmctx), writer));
1466 error:
1467 return (xmlrc);
1469 #endif
1471 #ifdef HAVE_JSON
1472 #define CHECKMEM(m) do { \
1473 if (m == NULL) { \
1474 result = ISC_R_NOMEMORY;\
1475 goto error;\
1477 } while(/*CONSTCOND*/0)
1479 isc_result_t
1480 dns_cache_renderjson(dns_cache_t *cache, json_object *cstats) {
1481 isc_result_t result = ISC_R_SUCCESS;
1482 int indices[dns_cachestatscounter_max];
1483 isc_uint64_t values[dns_cachestatscounter_max];
1484 json_object *obj;
1486 REQUIRE(VALID_CACHE(cache));
1488 getcounters(cache->stats, isc_statsformat_file,
1489 dns_cachestatscounter_max, indices, values);
1491 obj = json_object_new_int64(values[dns_cachestatscounter_hits]);
1492 CHECKMEM(obj);
1493 json_object_object_add(cstats, "CacheHits", obj);
1495 obj = json_object_new_int64(values[dns_cachestatscounter_misses]);
1496 CHECKMEM(obj);
1497 json_object_object_add(cstats, "CacheMisses", obj);
1499 obj = json_object_new_int64(values[dns_cachestatscounter_queryhits]);
1500 CHECKMEM(obj);
1501 json_object_object_add(cstats, "QueryHits", obj);
1503 obj = json_object_new_int64(values[dns_cachestatscounter_querymisses]);
1504 CHECKMEM(obj);
1505 json_object_object_add(cstats, "QueryMisses", obj);
1507 obj = json_object_new_int64(values[dns_cachestatscounter_deletelru]);
1508 CHECKMEM(obj);
1509 json_object_object_add(cstats, "DeleteLRU", obj);
1511 obj = json_object_new_int64(values[dns_cachestatscounter_deletettl]);
1512 CHECKMEM(obj);
1513 json_object_object_add(cstats, "DeleteTTL", obj);
1515 obj = json_object_new_int64(dns_db_nodecount(cache->db));
1516 CHECKMEM(obj);
1517 json_object_object_add(cstats, "CacheNodes", obj);
1519 obj = json_object_new_int64(dns_db_hashsize(cache->db));
1520 CHECKMEM(obj);
1521 json_object_object_add(cstats, "CacheBuckets", obj);
1523 obj = json_object_new_int64(isc_mem_total(cache->mctx));
1524 CHECKMEM(obj);
1525 json_object_object_add(cstats, "TreeMemTotal", obj);
1527 obj = json_object_new_int64(isc_mem_inuse(cache->mctx));
1528 CHECKMEM(obj);
1529 json_object_object_add(cstats, "TreeMemInUse", obj);
1531 obj = json_object_new_int64(isc_mem_maxinuse(cache->mctx));
1532 CHECKMEM(obj);
1533 json_object_object_add(cstats, "HeapMemMax", obj);
1535 obj = json_object_new_int64(isc_mem_total(cache->hmctx));
1536 CHECKMEM(obj);
1537 json_object_object_add(cstats, "HeapMemTotal", obj);
1539 obj = json_object_new_int64(isc_mem_inuse(cache->hmctx));
1540 CHECKMEM(obj);
1541 json_object_object_add(cstats, "HeapMemInUse", obj);
1543 obj = json_object_new_int64(isc_mem_maxinuse(cache->hmctx));
1544 CHECKMEM(obj);
1545 json_object_object_add(cstats, "HeapMemMax", obj);
1547 result = ISC_R_SUCCESS;
1548 error:
1549 return (result);
1551 #endif