xref: /netbsd-src/external/mpl/bind/dist/lib/dns/cache.c (revision 154bfe8e089c1a0a4e9ed8414f08d3da90949162)
1 /*	$NetBSD: cache.c,v 1.4 2020/05/24 19:46:22 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * This Source Code Form is subject to the terms of the Mozilla Public
7  * License, v. 2.0. If a copy of the MPL was not distributed with this
8  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9  *
10  * See the COPYRIGHT file distributed with this work for additional
11  * information regarding copyright ownership.
12  */
13 
14 /*! \file */
15 
16 #include <inttypes.h>
17 #include <stdbool.h>
18 
19 #include <isc/mem.h>
20 #include <isc/print.h>
21 #include <isc/refcount.h>
22 #include <isc/stats.h>
23 #include <isc/string.h>
24 #include <isc/task.h>
25 #include <isc/time.h>
26 #include <isc/timer.h>
27 #include <isc/util.h>
28 
29 #include <dns/cache.h>
30 #include <dns/db.h>
31 #include <dns/dbiterator.h>
32 #include <dns/events.h>
33 #include <dns/lib.h>
34 #include <dns/log.h>
35 #include <dns/masterdump.h>
36 #include <dns/rdata.h>
37 #include <dns/rdataset.h>
38 #include <dns/rdatasetiter.h>
39 #include <dns/result.h>
40 #include <dns/stats.h>
41 
42 #ifdef HAVE_JSON_C
43 #include <json_object.h>
44 #endif /* HAVE_JSON_C */
45 
46 #ifdef HAVE_LIBXML2
47 #include <libxml/xmlwriter.h>
48 #define ISC_XMLCHAR (const xmlChar *)
49 #endif /* HAVE_LIBXML2 */
50 
51 #include "rbtdb.h"
52 
53 #define CACHE_MAGIC	   ISC_MAGIC('$', '$', '$', '$')
54 #define VALID_CACHE(cache) ISC_MAGIC_VALID(cache, CACHE_MAGIC)
55 
56 /*!
57  * Control incremental cleaning.
58  * DNS_CACHE_MINSIZE is how many bytes is the floor for
59  * dns_cache_setcachesize(). See also DNS_CACHE_CLEANERINCREMENT
60  */
61 #define DNS_CACHE_MINSIZE 2097152U /*%< Bytes.  2097152 = 2 MB */
62 /*!
63  * Control incremental cleaning.
64  * CLEANERINCREMENT is how many nodes are examined in one pass.
65  * See also DNS_CACHE_MINSIZE
66  */
67 #define DNS_CACHE_CLEANERINCREMENT 1000U /*%< Number of nodes. */
68 
69 /***
70  ***	Types
71  ***/
72 
73 /*
74  * A cache_cleaner_t encapsulates the state of the periodic
75  * cache cleaning.
76  */
77 
78 typedef struct cache_cleaner cache_cleaner_t;
79 
80 typedef enum {
81 	cleaner_s_idle, /*%< Waiting for cleaning-interval to expire. */
82 	cleaner_s_busy, /*%< Currently cleaning. */
83 	cleaner_s_done	/*%< Freed enough memory after being overmem. */
84 } cleaner_state_t;
85 
86 /*
87  * Convenience macros for comprehensive assertion checking.
88  */
89 #define CLEANER_IDLE(c) \
90 	((c)->state == cleaner_s_idle && (c)->resched_event != NULL)
91 #define CLEANER_BUSY(c)                                           \
92 	((c)->state == cleaner_s_busy && (c)->iterator != NULL && \
93 	 (c)->resched_event == NULL)
94 
95 /*%
96  * Accesses to a cache cleaner object are synchronized through
97  * task/event serialization, or locked from the cache object.
98  */
99 struct cache_cleaner {
100 	isc_mutex_t lock;
101 	/*%<
102 	 * Locks overmem_event, overmem.  Note: never allocate memory
103 	 * while holding this lock - that could lead to deadlock since
104 	 * the lock is take by water() which is called from the memory
105 	 * allocator.
106 	 */
107 
108 	dns_cache_t *cache;
109 	isc_task_t *task;
110 	isc_event_t *resched_event; /*% Sent by cleaner task to
111 				     * itself to reschedule */
112 	isc_event_t *overmem_event;
113 
114 	dns_dbiterator_t *iterator;
115 	unsigned int increment; /*% Number of names to
116 				 * clean in one increment */
117 	cleaner_state_t state;	/*% Idle/Busy. */
118 	bool overmem;		/*% The cache is in an overmem state.
119 				 * */
120 	bool replaceiterator;
121 };
122 
123 /*%
124  * The actual cache object.
125  */
126 
127 struct dns_cache {
128 	/* Unlocked. */
129 	unsigned int magic;
130 	isc_mutex_t lock;
131 	isc_mutex_t filelock;
132 	isc_mem_t *mctx;  /* Main cache memory */
133 	isc_mem_t *hmctx; /* Heap memory */
134 	char *name;
135 	isc_refcount_t references;
136 	isc_refcount_t live_tasks;
137 
138 	/* Locked by 'lock'. */
139 	dns_rdataclass_t rdclass;
140 	dns_db_t *db;
141 	cache_cleaner_t cleaner;
142 	char *db_type;
143 	int db_argc;
144 	char **db_argv;
145 	size_t size;
146 	dns_ttl_t serve_stale_ttl;
147 	isc_stats_t *stats;
148 
149 	/* Locked by 'filelock'. */
150 	char *filename;
151 	/* Access to the on-disk cache file is also locked by 'filelock'. */
152 };
153 
154 /***
155  ***	Functions
156  ***/
157 
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);
161 
162 static void
163 incremental_cleaning_action(isc_task_t *task, isc_event_t *event);
164 
165 static void
166 cleaner_shutdown_action(isc_task_t *task, isc_event_t *event);
167 
168 static void
169 overmem_cleaning_action(isc_task_t *task, isc_event_t *event);
170 
171 static inline isc_result_t
172 cache_create_db(dns_cache_t *cache, dns_db_t **db) {
173 	isc_result_t result;
174 	result = dns_db_create(cache->mctx, cache->db_type, dns_rootname,
175 			       dns_dbtype_cache, cache->rdclass, cache->db_argc,
176 			       cache->db_argv, db);
177 	if (result == ISC_R_SUCCESS) {
178 		dns_db_setservestalettl(*db, cache->serve_stale_ttl);
179 	}
180 	return (result);
181 }
182 
183 isc_result_t
184 dns_cache_create(isc_mem_t *cmctx, isc_mem_t *hmctx, isc_taskmgr_t *taskmgr,
185 		 isc_timermgr_t *timermgr, dns_rdataclass_t rdclass,
186 		 const char *cachename, const char *db_type,
187 		 unsigned int db_argc, char **db_argv, dns_cache_t **cachep) {
188 	isc_result_t result;
189 	dns_cache_t *cache;
190 	int i, extra = 0;
191 	isc_task_t *dbtask;
192 
193 	REQUIRE(cachep != NULL);
194 	REQUIRE(*cachep == NULL);
195 	REQUIRE(cmctx != NULL);
196 	REQUIRE(hmctx != NULL);
197 	REQUIRE(cachename != NULL);
198 
199 	cache = isc_mem_get(cmctx, sizeof(*cache));
200 
201 	cache->mctx = cache->hmctx = NULL;
202 	isc_mem_attach(cmctx, &cache->mctx);
203 	isc_mem_attach(hmctx, &cache->hmctx);
204 
205 	cache->name = NULL;
206 	if (cachename != NULL) {
207 		cache->name = isc_mem_strdup(cmctx, cachename);
208 	}
209 
210 	isc_mutex_init(&cache->lock);
211 	isc_mutex_init(&cache->filelock);
212 
213 	isc_refcount_init(&cache->references, 1);
214 	isc_refcount_init(&cache->live_tasks, 1);
215 	cache->rdclass = rdclass;
216 	cache->serve_stale_ttl = 0;
217 
218 	cache->stats = NULL;
219 	result = isc_stats_create(cmctx, &cache->stats,
220 				  dns_cachestatscounter_max);
221 	if (result != ISC_R_SUCCESS) {
222 		goto cleanup_filelock;
223 	}
224 
225 	cache->db_type = isc_mem_strdup(cmctx, db_type);
226 
227 	/*
228 	 * For databases of type "rbt" we pass hmctx to dns_db_create()
229 	 * via cache->db_argv, followed by the rest of the arguments in
230 	 * db_argv (of which there really shouldn't be any).
231 	 */
232 	if (strcmp(cache->db_type, "rbt") == 0) {
233 		extra = 1;
234 	}
235 
236 	cache->db_argc = db_argc + extra;
237 	cache->db_argv = NULL;
238 
239 	if (cache->db_argc != 0) {
240 		cache->db_argv = isc_mem_get(cmctx,
241 					     cache->db_argc * sizeof(char *));
242 
243 		for (i = 0; i < cache->db_argc; i++) {
244 			cache->db_argv[i] = NULL;
245 		}
246 
247 		cache->db_argv[0] = (char *)hmctx;
248 		for (i = extra; i < cache->db_argc; i++) {
249 			cache->db_argv[i] = isc_mem_strdup(cmctx,
250 							   db_argv[i - extra]);
251 		}
252 	}
253 
254 	/*
255 	 * Create the database
256 	 */
257 	cache->db = NULL;
258 	result = cache_create_db(cache, &cache->db);
259 	if (result != ISC_R_SUCCESS) {
260 		goto cleanup_dbargv;
261 	}
262 	if (taskmgr != NULL) {
263 		dbtask = NULL;
264 		result = isc_task_create(taskmgr, 1, &dbtask);
265 		if (result != ISC_R_SUCCESS) {
266 			goto cleanup_db;
267 		}
268 
269 		isc_task_setname(dbtask, "cache_dbtask", NULL);
270 		dns_db_settask(cache->db, dbtask);
271 		isc_task_detach(&dbtask);
272 	}
273 
274 	cache->filename = NULL;
275 
276 	cache->magic = CACHE_MAGIC;
277 
278 	/*
279 	 * RBT-type cache DB has its own mechanism of cache cleaning and doesn't
280 	 * need the control of the generic cleaner.
281 	 */
282 	if (strcmp(db_type, "rbt") == 0) {
283 		result = cache_cleaner_init(cache, NULL, NULL, &cache->cleaner);
284 	} else {
285 		result = cache_cleaner_init(cache, taskmgr, timermgr,
286 					    &cache->cleaner);
287 	}
288 	if (result != ISC_R_SUCCESS) {
289 		goto cleanup_db;
290 	}
291 
292 	result = dns_db_setcachestats(cache->db, cache->stats);
293 	if (result != ISC_R_SUCCESS) {
294 		goto cleanup_db;
295 	}
296 
297 	*cachep = cache;
298 	return (ISC_R_SUCCESS);
299 
300 cleanup_db:
301 	dns_db_detach(&cache->db);
302 cleanup_dbargv:
303 	for (i = extra; i < cache->db_argc; i++) {
304 		if (cache->db_argv[i] != NULL) {
305 			isc_mem_free(cmctx, cache->db_argv[i]);
306 		}
307 	}
308 	if (cache->db_argv != NULL) {
309 		isc_mem_put(cmctx, cache->db_argv,
310 			    cache->db_argc * sizeof(char *));
311 	}
312 	isc_mem_free(cmctx, cache->db_type);
313 cleanup_filelock:
314 	isc_mutex_destroy(&cache->filelock);
315 	isc_stats_detach(&cache->stats);
316 	isc_mutex_destroy(&cache->lock);
317 	if (cache->name != NULL) {
318 		isc_mem_free(cmctx, cache->name);
319 	}
320 	isc_mem_detach(&cache->hmctx);
321 	isc_mem_putanddetach(&cache->mctx, cache, sizeof(*cache));
322 	return (result);
323 }
324 
325 static void
326 cache_free(dns_cache_t *cache) {
327 	REQUIRE(VALID_CACHE(cache));
328 
329 	isc_refcount_destroy(&cache->references);
330 	isc_refcount_destroy(&cache->live_tasks);
331 
332 	isc_mem_setwater(cache->mctx, NULL, NULL, 0, 0);
333 
334 	if (cache->cleaner.task != NULL) {
335 		isc_task_detach(&cache->cleaner.task);
336 	}
337 
338 	if (cache->cleaner.overmem_event != NULL) {
339 		isc_event_free(&cache->cleaner.overmem_event);
340 	}
341 
342 	if (cache->cleaner.resched_event != NULL) {
343 		isc_event_free(&cache->cleaner.resched_event);
344 	}
345 
346 	if (cache->cleaner.iterator != NULL) {
347 		dns_dbiterator_destroy(&cache->cleaner.iterator);
348 	}
349 
350 	isc_mutex_destroy(&cache->cleaner.lock);
351 
352 	if (cache->filename) {
353 		isc_mem_free(cache->mctx, cache->filename);
354 		cache->filename = NULL;
355 	}
356 
357 	if (cache->db != NULL) {
358 		dns_db_detach(&cache->db);
359 	}
360 
361 	if (cache->db_argv != NULL) {
362 		/*
363 		 * We don't free db_argv[0] in "rbt" cache databases
364 		 * as it's a pointer to hmctx
365 		 */
366 		int extra = 0;
367 		if (strcmp(cache->db_type, "rbt") == 0) {
368 			extra = 1;
369 		}
370 		for (int i = extra; i < cache->db_argc; i++) {
371 			if (cache->db_argv[i] != NULL) {
372 				isc_mem_free(cache->mctx, cache->db_argv[i]);
373 			}
374 		}
375 		isc_mem_put(cache->mctx, cache->db_argv,
376 			    cache->db_argc * sizeof(char *));
377 	}
378 
379 	if (cache->db_type != NULL) {
380 		isc_mem_free(cache->mctx, cache->db_type);
381 	}
382 
383 	if (cache->name != NULL) {
384 		isc_mem_free(cache->mctx, cache->name);
385 	}
386 
387 	if (cache->stats != NULL) {
388 		isc_stats_detach(&cache->stats);
389 	}
390 
391 	isc_mutex_destroy(&cache->lock);
392 	isc_mutex_destroy(&cache->filelock);
393 
394 	cache->magic = 0;
395 	isc_mem_detach(&cache->hmctx);
396 	isc_mem_putanddetach(&cache->mctx, cache, sizeof(*cache));
397 }
398 
399 void
400 dns_cache_attach(dns_cache_t *cache, dns_cache_t **targetp) {
401 	REQUIRE(VALID_CACHE(cache));
402 	REQUIRE(targetp != NULL && *targetp == NULL);
403 
404 	isc_refcount_increment(&cache->references);
405 
406 	*targetp = cache;
407 }
408 
409 void
410 dns_cache_detach(dns_cache_t **cachep) {
411 	dns_cache_t *cache;
412 
413 	REQUIRE(cachep != NULL);
414 	cache = *cachep;
415 	*cachep = NULL;
416 	REQUIRE(VALID_CACHE(cache));
417 
418 	if (isc_refcount_decrement(&cache->references) == 1) {
419 		cache->cleaner.overmem = false;
420 		/*
421 		 * When the cache is shut down, dump it to a file if one is
422 		 * specified.
423 		 */
424 		isc_result_t result = dns_cache_dump(cache);
425 		if (result != ISC_R_SUCCESS) {
426 			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
427 				      DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
428 				      "error dumping cache: %s ",
429 				      isc_result_totext(result));
430 		}
431 
432 		/*
433 		 * If the cleaner task exists, let it free the cache.
434 		 */
435 		if (isc_refcount_decrement(&cache->live_tasks) > 1) {
436 			isc_task_shutdown(cache->cleaner.task);
437 		} else {
438 			cache_free(cache);
439 		}
440 	}
441 }
442 
443 void
444 dns_cache_attachdb(dns_cache_t *cache, dns_db_t **dbp) {
445 	REQUIRE(VALID_CACHE(cache));
446 	REQUIRE(dbp != NULL && *dbp == NULL);
447 	REQUIRE(cache->db != NULL);
448 
449 	LOCK(&cache->lock);
450 	dns_db_attach(cache->db, dbp);
451 	UNLOCK(&cache->lock);
452 }
453 
454 isc_result_t
455 dns_cache_setfilename(dns_cache_t *cache, const char *filename) {
456 	char *newname;
457 
458 	REQUIRE(VALID_CACHE(cache));
459 	REQUIRE(filename != NULL);
460 
461 	newname = isc_mem_strdup(cache->mctx, filename);
462 
463 	LOCK(&cache->filelock);
464 	if (cache->filename) {
465 		isc_mem_free(cache->mctx, cache->filename);
466 	}
467 	cache->filename = newname;
468 	UNLOCK(&cache->filelock);
469 
470 	return (ISC_R_SUCCESS);
471 }
472 
473 isc_result_t
474 dns_cache_load(dns_cache_t *cache) {
475 	isc_result_t result;
476 
477 	REQUIRE(VALID_CACHE(cache));
478 
479 	if (cache->filename == NULL) {
480 		return (ISC_R_SUCCESS);
481 	}
482 
483 	LOCK(&cache->filelock);
484 	result = dns_db_load(cache->db, cache->filename, dns_masterformat_text,
485 			     0);
486 	UNLOCK(&cache->filelock);
487 
488 	return (result);
489 }
490 
491 isc_result_t
492 dns_cache_dump(dns_cache_t *cache) {
493 	isc_result_t result;
494 
495 	REQUIRE(VALID_CACHE(cache));
496 
497 	if (cache->filename == NULL) {
498 		return (ISC_R_SUCCESS);
499 	}
500 
501 	LOCK(&cache->filelock);
502 	result = dns_master_dump(cache->mctx, cache->db, NULL,
503 				 &dns_master_style_cache, cache->filename,
504 				 dns_masterformat_text, NULL);
505 	UNLOCK(&cache->filelock);
506 	return (result);
507 }
508 
509 const char *
510 dns_cache_getname(dns_cache_t *cache) {
511 	REQUIRE(VALID_CACHE(cache));
512 
513 	return (cache->name);
514 }
515 
516 /*
517  * Initialize the cache cleaner object at *cleaner.
518  * Space for the object must be allocated by the caller.
519  */
520 
521 static isc_result_t
522 cache_cleaner_init(dns_cache_t *cache, isc_taskmgr_t *taskmgr,
523 		   isc_timermgr_t *timermgr, cache_cleaner_t *cleaner) {
524 	isc_result_t result;
525 
526 	isc_mutex_init(&cleaner->lock);
527 
528 	cleaner->increment = DNS_CACHE_CLEANERINCREMENT;
529 	cleaner->state = cleaner_s_idle;
530 	cleaner->cache = cache;
531 	cleaner->iterator = NULL;
532 	cleaner->overmem = false;
533 	cleaner->replaceiterator = false;
534 
535 	cleaner->task = NULL;
536 	cleaner->resched_event = NULL;
537 	cleaner->overmem_event = NULL;
538 
539 	result = dns_db_createiterator(cleaner->cache->db, false,
540 				       &cleaner->iterator);
541 	if (result != ISC_R_SUCCESS) {
542 		goto cleanup;
543 	}
544 
545 	if (taskmgr != NULL && timermgr != NULL) {
546 		result = isc_task_create(taskmgr, 1, &cleaner->task);
547 		if (result != ISC_R_SUCCESS) {
548 			UNEXPECTED_ERROR(__FILE__, __LINE__,
549 					 "isc_task_create() failed: %s",
550 					 dns_result_totext(result));
551 			result = ISC_R_UNEXPECTED;
552 			goto cleanup;
553 		}
554 		isc_refcount_increment(&cleaner->cache->live_tasks);
555 		isc_task_setname(cleaner->task, "cachecleaner", cleaner);
556 
557 		result = isc_task_onshutdown(cleaner->task,
558 					     cleaner_shutdown_action, cache);
559 		if (result != ISC_R_SUCCESS) {
560 			isc_refcount_decrement(&cleaner->cache->live_tasks);
561 			UNEXPECTED_ERROR(__FILE__, __LINE__,
562 					 "cache cleaner: "
563 					 "isc_task_onshutdown() failed: %s",
564 					 dns_result_totext(result));
565 			goto cleanup;
566 		}
567 
568 		cleaner->resched_event = isc_event_allocate(
569 			cache->mctx, cleaner, DNS_EVENT_CACHECLEAN,
570 			incremental_cleaning_action, cleaner,
571 			sizeof(isc_event_t));
572 
573 		cleaner->overmem_event = isc_event_allocate(
574 			cache->mctx, cleaner, DNS_EVENT_CACHEOVERMEM,
575 			overmem_cleaning_action, cleaner, sizeof(isc_event_t));
576 	}
577 
578 	return (ISC_R_SUCCESS);
579 
580 cleanup:
581 	if (cleaner->overmem_event != NULL) {
582 		isc_event_free(&cleaner->overmem_event);
583 	}
584 	if (cleaner->resched_event != NULL) {
585 		isc_event_free(&cleaner->resched_event);
586 	}
587 	if (cleaner->task != NULL) {
588 		isc_task_detach(&cleaner->task);
589 	}
590 	if (cleaner->iterator != NULL) {
591 		dns_dbiterator_destroy(&cleaner->iterator);
592 	}
593 	isc_mutex_destroy(&cleaner->lock);
594 
595 	return (result);
596 }
597 
598 static void
599 begin_cleaning(cache_cleaner_t *cleaner) {
600 	isc_result_t result = ISC_R_SUCCESS;
601 
602 	REQUIRE(CLEANER_IDLE(cleaner));
603 
604 	/*
605 	 * Create an iterator, if it does not already exist, and
606 	 * position it at the beginning of the cache.
607 	 */
608 	if (cleaner->iterator == NULL) {
609 		result = dns_db_createiterator(cleaner->cache->db, false,
610 					       &cleaner->iterator);
611 	}
612 	if (result != ISC_R_SUCCESS) {
613 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
614 			      DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
615 			      "cache cleaner could not create "
616 			      "iterator: %s",
617 			      isc_result_totext(result));
618 	} else {
619 		dns_dbiterator_setcleanmode(cleaner->iterator, true);
620 		result = dns_dbiterator_first(cleaner->iterator);
621 	}
622 	if (result != ISC_R_SUCCESS) {
623 		/*
624 		 * If the result is ISC_R_NOMORE, the database is empty,
625 		 * so there is nothing to be cleaned.
626 		 */
627 		if (result != ISC_R_NOMORE && cleaner->iterator != NULL) {
628 			UNEXPECTED_ERROR(__FILE__, __LINE__,
629 					 "cache cleaner: "
630 					 "dns_dbiterator_first() failed: %s",
631 					 dns_result_totext(result));
632 			dns_dbiterator_destroy(&cleaner->iterator);
633 		} else if (cleaner->iterator != NULL) {
634 			result = dns_dbiterator_pause(cleaner->iterator);
635 			RUNTIME_CHECK(result == ISC_R_SUCCESS);
636 		}
637 	} else {
638 		/*
639 		 * Pause the iterator to free its lock.
640 		 */
641 		result = dns_dbiterator_pause(cleaner->iterator);
642 		RUNTIME_CHECK(result == ISC_R_SUCCESS);
643 
644 		isc_log_write(
645 			dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
646 			ISC_LOG_DEBUG(1), "begin cache cleaning, mem inuse %lu",
647 			(unsigned long)isc_mem_inuse(cleaner->cache->mctx));
648 		cleaner->state = cleaner_s_busy;
649 		isc_task_send(cleaner->task, &cleaner->resched_event);
650 	}
651 
652 	return;
653 }
654 
655 static void
656 end_cleaning(cache_cleaner_t *cleaner, isc_event_t *event) {
657 	isc_result_t result;
658 
659 	REQUIRE(CLEANER_BUSY(cleaner));
660 	REQUIRE(event != NULL);
661 
662 	result = dns_dbiterator_pause(cleaner->iterator);
663 	if (result != ISC_R_SUCCESS) {
664 		dns_dbiterator_destroy(&cleaner->iterator);
665 	}
666 
667 	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
668 		      ISC_LOG_DEBUG(1), "end cache cleaning, mem inuse %lu",
669 		      (unsigned long)isc_mem_inuse(cleaner->cache->mctx));
670 
671 	cleaner->state = cleaner_s_idle;
672 	cleaner->resched_event = event;
673 }
674 
675 /*
676  * This is called when the cache either surpasses its upper limit
677  * or shrinks beyond its lower limit.
678  */
679 static void
680 overmem_cleaning_action(isc_task_t *task, isc_event_t *event) {
681 	cache_cleaner_t *cleaner = event->ev_arg;
682 	bool want_cleaning = false;
683 
684 	UNUSED(task);
685 
686 	INSIST(task == cleaner->task);
687 	INSIST(event->ev_type == DNS_EVENT_CACHEOVERMEM);
688 	INSIST(cleaner->overmem_event == NULL);
689 
690 	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
691 		      ISC_LOG_DEBUG(1),
692 		      "overmem_cleaning_action called, "
693 		      "overmem = %d, state = %d",
694 		      cleaner->overmem, cleaner->state);
695 
696 	LOCK(&cleaner->lock);
697 
698 	if (cleaner->overmem) {
699 		if (cleaner->state == cleaner_s_idle) {
700 			want_cleaning = true;
701 		}
702 	} else {
703 		if (cleaner->state == cleaner_s_busy) {
704 			/*
705 			 * end_cleaning() can't be called here because
706 			 * then both cleaner->overmem_event and
707 			 * cleaner->resched_event will point to this
708 			 * event.  Set the state to done, and then
709 			 * when the incremental_cleaning_action() event
710 			 * is posted, it will handle the end_cleaning.
711 			 */
712 			cleaner->state = cleaner_s_done;
713 		}
714 	}
715 
716 	cleaner->overmem_event = event;
717 
718 	UNLOCK(&cleaner->lock);
719 
720 	if (want_cleaning) {
721 		begin_cleaning(cleaner);
722 	}
723 }
724 
725 /*
726  * Do incremental cleaning.
727  */
728 static void
729 incremental_cleaning_action(isc_task_t *task, isc_event_t *event) {
730 	cache_cleaner_t *cleaner = event->ev_arg;
731 	isc_result_t result;
732 	unsigned int n_names;
733 	isc_time_t start;
734 
735 	UNUSED(task);
736 
737 	INSIST(task == cleaner->task);
738 	INSIST(event->ev_type == DNS_EVENT_CACHECLEAN);
739 
740 	if (cleaner->state == cleaner_s_done) {
741 		cleaner->state = cleaner_s_busy;
742 		end_cleaning(cleaner, event);
743 		LOCK(&cleaner->cache->lock);
744 		LOCK(&cleaner->lock);
745 		if (cleaner->replaceiterator) {
746 			dns_dbiterator_destroy(&cleaner->iterator);
747 			(void)dns_db_createiterator(cleaner->cache->db, false,
748 						    &cleaner->iterator);
749 			cleaner->replaceiterator = false;
750 		}
751 		UNLOCK(&cleaner->lock);
752 		UNLOCK(&cleaner->cache->lock);
753 		return;
754 	}
755 
756 	INSIST(CLEANER_BUSY(cleaner));
757 
758 	n_names = cleaner->increment;
759 
760 	REQUIRE(DNS_DBITERATOR_VALID(cleaner->iterator));
761 
762 	isc_time_now(&start);
763 	while (n_names-- > 0) {
764 		dns_dbnode_t *node = NULL;
765 
766 		result = dns_dbiterator_current(cleaner->iterator, &node, NULL);
767 		if (result != ISC_R_SUCCESS) {
768 			UNEXPECTED_ERROR(__FILE__, __LINE__,
769 					 "cache cleaner: "
770 					 "dns_dbiterator_current() "
771 					 "failed: %s",
772 					 dns_result_totext(result));
773 
774 			end_cleaning(cleaner, event);
775 			return;
776 		}
777 
778 		/*
779 		 * The node was not needed, but was required by
780 		 * dns_dbiterator_current().  Give up its reference.
781 		 */
782 		dns_db_detachnode(cleaner->cache->db, &node);
783 
784 		/*
785 		 * Step to the next node.
786 		 */
787 		result = dns_dbiterator_next(cleaner->iterator);
788 
789 		if (result != ISC_R_SUCCESS) {
790 			/*
791 			 * Either the end was reached (ISC_R_NOMORE) or
792 			 * some error was signaled.  If the cache is still
793 			 * overmem and no error was encountered,
794 			 * keep trying to clean it, otherwise stop cleaning.
795 			 */
796 			if (result != ISC_R_NOMORE) {
797 				UNEXPECTED_ERROR(__FILE__, __LINE__,
798 						 "cache cleaner: "
799 						 "dns_dbiterator_next() "
800 						 "failed: %s",
801 						 dns_result_totext(result));
802 			} else if (cleaner->overmem) {
803 				result =
804 					dns_dbiterator_first(cleaner->iterator);
805 				if (result == ISC_R_SUCCESS) {
806 					isc_log_write(dns_lctx,
807 						      DNS_LOGCATEGORY_DATABASE,
808 						      DNS_LOGMODULE_CACHE,
809 						      ISC_LOG_DEBUG(1),
810 						      "cache cleaner: "
811 						      "still overmem, "
812 						      "reset and try again");
813 					continue;
814 				}
815 			}
816 
817 			end_cleaning(cleaner, event);
818 			return;
819 		}
820 	}
821 
822 	/*
823 	 * We have successfully performed a cleaning increment but have
824 	 * not gone through the entire cache.  Free the iterator locks
825 	 * and reschedule another batch.  If it fails, just try to continue
826 	 * anyway.
827 	 */
828 	result = dns_dbiterator_pause(cleaner->iterator);
829 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
830 
831 	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
832 		      ISC_LOG_DEBUG(1),
833 		      "cache cleaner: checked %u nodes, "
834 		      "mem inuse %lu, sleeping",
835 		      cleaner->increment,
836 		      (unsigned long)isc_mem_inuse(cleaner->cache->mctx));
837 
838 	isc_task_send(task, &event);
839 	INSIST(CLEANER_BUSY(cleaner));
840 	return;
841 }
842 
843 /*
844  * Do immediate cleaning.
845  */
846 isc_result_t
847 dns_cache_clean(dns_cache_t *cache, isc_stdtime_t now) {
848 	isc_result_t result;
849 	dns_dbiterator_t *iterator = NULL;
850 
851 	REQUIRE(VALID_CACHE(cache));
852 
853 	result = dns_db_createiterator(cache->db, 0, &iterator);
854 	if (result != ISC_R_SUCCESS) {
855 		return (result);
856 	}
857 
858 	result = dns_dbiterator_first(iterator);
859 
860 	while (result == ISC_R_SUCCESS) {
861 		dns_dbnode_t *node = NULL;
862 		result = dns_dbiterator_current(iterator, &node,
863 						(dns_name_t *)NULL);
864 		if (result != ISC_R_SUCCESS) {
865 			break;
866 		}
867 
868 		/*
869 		 * Check TTLs, mark expired rdatasets stale.
870 		 */
871 		result = dns_db_expirenode(cache->db, node, now);
872 		if (result != ISC_R_SUCCESS) {
873 			UNEXPECTED_ERROR(__FILE__, __LINE__,
874 					 "cache cleaner: dns_db_expirenode() "
875 					 "failed: %s",
876 					 dns_result_totext(result));
877 			/*
878 			 * Continue anyway.
879 			 */
880 		}
881 
882 		/*
883 		 * This is where the actual freeing takes place.
884 		 */
885 		dns_db_detachnode(cache->db, &node);
886 
887 		result = dns_dbiterator_next(iterator);
888 	}
889 
890 	dns_dbiterator_destroy(&iterator);
891 
892 	if (result == ISC_R_NOMORE) {
893 		result = ISC_R_SUCCESS;
894 	}
895 
896 	return (result);
897 }
898 
899 static void
900 water(void *arg, int mark) {
901 	dns_cache_t *cache = arg;
902 	bool overmem = (mark == ISC_MEM_HIWATER);
903 
904 	REQUIRE(VALID_CACHE(cache));
905 
906 	LOCK(&cache->cleaner.lock);
907 
908 	if (overmem != cache->cleaner.overmem) {
909 		dns_db_overmem(cache->db, overmem);
910 		cache->cleaner.overmem = overmem;
911 		isc_mem_waterack(cache->mctx, mark);
912 	}
913 
914 	if (cache->cleaner.overmem_event != NULL) {
915 		isc_task_send(cache->cleaner.task,
916 			      &cache->cleaner.overmem_event);
917 	}
918 
919 	UNLOCK(&cache->cleaner.lock);
920 }
921 
922 void
923 dns_cache_setcachesize(dns_cache_t *cache, size_t size) {
924 	size_t hiwater, lowater;
925 
926 	REQUIRE(VALID_CACHE(cache));
927 
928 	/*
929 	 * Impose a minimum cache size; pathological things happen if there
930 	 * is too little room.
931 	 */
932 	if (size != 0U && size < DNS_CACHE_MINSIZE) {
933 		size = DNS_CACHE_MINSIZE;
934 	}
935 
936 	LOCK(&cache->lock);
937 	cache->size = size;
938 	UNLOCK(&cache->lock);
939 
940 	hiwater = size - (size >> 3); /* Approximately 7/8ths. */
941 	lowater = size - (size >> 2); /* Approximately 3/4ths. */
942 
943 	/*
944 	 * If the cache was overmem and cleaning, but now with the new limits
945 	 * it is no longer in an overmem condition, then the next
946 	 * isc_mem_put for cache memory will do the right thing and trigger
947 	 * water().
948 	 */
949 
950 	if (size == 0U || hiwater == 0U || lowater == 0U) {
951 		/*
952 		 * Disable cache memory limiting.
953 		 */
954 		isc_mem_setwater(cache->mctx, water, cache, 0, 0);
955 	} else {
956 		/*
957 		 * Establish new cache memory limits (either for the first
958 		 * time, or replacing other limits).
959 		 */
960 		isc_mem_setwater(cache->mctx, water, cache, hiwater, lowater);
961 	}
962 }
963 
964 size_t
965 dns_cache_getcachesize(dns_cache_t *cache) {
966 	size_t size;
967 
968 	REQUIRE(VALID_CACHE(cache));
969 
970 	LOCK(&cache->lock);
971 	size = cache->size;
972 	UNLOCK(&cache->lock);
973 
974 	return (size);
975 }
976 
977 void
978 dns_cache_setservestalettl(dns_cache_t *cache, dns_ttl_t ttl) {
979 	REQUIRE(VALID_CACHE(cache));
980 
981 	LOCK(&cache->lock);
982 	cache->serve_stale_ttl = ttl;
983 	UNLOCK(&cache->lock);
984 
985 	(void)dns_db_setservestalettl(cache->db, ttl);
986 }
987 
988 dns_ttl_t
989 dns_cache_getservestalettl(dns_cache_t *cache) {
990 	dns_ttl_t ttl;
991 	isc_result_t result;
992 
993 	REQUIRE(VALID_CACHE(cache));
994 
995 	/*
996 	 * Could get it straight from the dns_cache_t, but use db
997 	 * to confirm the value that the db is really using.
998 	 */
999 	result = dns_db_getservestalettl(cache->db, &ttl);
1000 	return (result == ISC_R_SUCCESS ? ttl : 0);
1001 }
1002 
1003 /*
1004  * The cleaner task is shutting down; do the necessary cleanup.
1005  */
1006 static void
1007 cleaner_shutdown_action(isc_task_t *task, isc_event_t *event) {
1008 	dns_cache_t *cache = event->ev_arg;
1009 
1010 	UNUSED(task);
1011 
1012 	INSIST(task == cache->cleaner.task);
1013 	INSIST(event->ev_type == ISC_TASKEVENT_SHUTDOWN);
1014 
1015 	if (CLEANER_BUSY(&cache->cleaner)) {
1016 		end_cleaning(&cache->cleaner, event);
1017 	} else {
1018 		isc_event_free(&event);
1019 	}
1020 
1021 	/* Make sure we don't reschedule anymore. */
1022 	(void)isc_task_purge(task, NULL, DNS_EVENT_CACHECLEAN, NULL);
1023 
1024 	INSIST(isc_refcount_decrement(&cache->live_tasks) == 1);
1025 
1026 	cache_free(cache);
1027 }
1028 
1029 isc_result_t
1030 dns_cache_flush(dns_cache_t *cache) {
1031 	dns_db_t *db = NULL, *olddb;
1032 	dns_dbiterator_t *dbiterator = NULL, *olddbiterator = NULL;
1033 	isc_result_t result;
1034 
1035 	result = cache_create_db(cache, &db);
1036 	if (result != ISC_R_SUCCESS) {
1037 		return (result);
1038 	}
1039 
1040 	result = dns_db_createiterator(db, false, &dbiterator);
1041 	if (result != ISC_R_SUCCESS) {
1042 		dns_db_detach(&db);
1043 		return (result);
1044 	}
1045 
1046 	LOCK(&cache->lock);
1047 	LOCK(&cache->cleaner.lock);
1048 	if (cache->cleaner.state == cleaner_s_idle) {
1049 		olddbiterator = cache->cleaner.iterator;
1050 		cache->cleaner.iterator = dbiterator;
1051 		dbiterator = NULL;
1052 	} else {
1053 		if (cache->cleaner.state == cleaner_s_busy) {
1054 			cache->cleaner.state = cleaner_s_done;
1055 		}
1056 		cache->cleaner.replaceiterator = true;
1057 	}
1058 	olddb = cache->db;
1059 	cache->db = db;
1060 	dns_db_setcachestats(cache->db, cache->stats);
1061 	UNLOCK(&cache->cleaner.lock);
1062 	UNLOCK(&cache->lock);
1063 
1064 	if (dbiterator != NULL) {
1065 		dns_dbiterator_destroy(&dbiterator);
1066 	}
1067 	if (olddbiterator != NULL) {
1068 		dns_dbiterator_destroy(&olddbiterator);
1069 	}
1070 	dns_db_detach(&olddb);
1071 
1072 	return (ISC_R_SUCCESS);
1073 }
1074 
1075 static isc_result_t
1076 clearnode(dns_db_t *db, dns_dbnode_t *node) {
1077 	isc_result_t result;
1078 	dns_rdatasetiter_t *iter = NULL;
1079 
1080 	result = dns_db_allrdatasets(db, node, NULL, (isc_stdtime_t)0, &iter);
1081 	if (result != ISC_R_SUCCESS) {
1082 		return (result);
1083 	}
1084 
1085 	for (result = dns_rdatasetiter_first(iter); result == ISC_R_SUCCESS;
1086 	     result = dns_rdatasetiter_next(iter))
1087 	{
1088 		dns_rdataset_t rdataset;
1089 		dns_rdataset_init(&rdataset);
1090 
1091 		dns_rdatasetiter_current(iter, &rdataset);
1092 		result = dns_db_deleterdataset(db, node, NULL, rdataset.type,
1093 					       rdataset.covers);
1094 		dns_rdataset_disassociate(&rdataset);
1095 		if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED) {
1096 			break;
1097 		}
1098 	}
1099 
1100 	if (result == ISC_R_NOMORE) {
1101 		result = ISC_R_SUCCESS;
1102 	}
1103 
1104 	dns_rdatasetiter_destroy(&iter);
1105 	return (result);
1106 }
1107 
1108 static isc_result_t
1109 cleartree(dns_db_t *db, const dns_name_t *name) {
1110 	isc_result_t result, answer = ISC_R_SUCCESS;
1111 	dns_dbiterator_t *iter = NULL;
1112 	dns_dbnode_t *node = NULL, *top = NULL;
1113 	dns_fixedname_t fnodename;
1114 	dns_name_t *nodename;
1115 
1116 	/*
1117 	 * Create the node if it doesn't exist so dns_dbiterator_seek()
1118 	 * can find it.  We will continue even if this fails.
1119 	 */
1120 	(void)dns_db_findnode(db, name, true, &top);
1121 
1122 	nodename = dns_fixedname_initname(&fnodename);
1123 
1124 	result = dns_db_createiterator(db, 0, &iter);
1125 	if (result != ISC_R_SUCCESS) {
1126 		goto cleanup;
1127 	}
1128 
1129 	result = dns_dbiterator_seek(iter, name);
1130 	if (result == DNS_R_PARTIALMATCH) {
1131 		result = dns_dbiterator_next(iter);
1132 	}
1133 	if (result != ISC_R_SUCCESS) {
1134 		goto cleanup;
1135 	}
1136 
1137 	while (result == ISC_R_SUCCESS) {
1138 		result = dns_dbiterator_current(iter, &node, nodename);
1139 		if (result == DNS_R_NEWORIGIN) {
1140 			result = ISC_R_SUCCESS;
1141 		}
1142 		if (result != ISC_R_SUCCESS) {
1143 			goto cleanup;
1144 		}
1145 		/*
1146 		 * Are we done?
1147 		 */
1148 		if (!dns_name_issubdomain(nodename, name)) {
1149 			goto cleanup;
1150 		}
1151 
1152 		/*
1153 		 * If clearnode fails record and move onto the next node.
1154 		 */
1155 		result = clearnode(db, node);
1156 		if (result != ISC_R_SUCCESS && answer == ISC_R_SUCCESS) {
1157 			answer = result;
1158 		}
1159 		dns_db_detachnode(db, &node);
1160 		result = dns_dbiterator_next(iter);
1161 	}
1162 
1163 cleanup:
1164 	if (result == ISC_R_NOMORE || result == ISC_R_NOTFOUND) {
1165 		result = ISC_R_SUCCESS;
1166 	}
1167 	if (result != ISC_R_SUCCESS && answer == ISC_R_SUCCESS) {
1168 		answer = result;
1169 	}
1170 	if (node != NULL) {
1171 		dns_db_detachnode(db, &node);
1172 	}
1173 	if (iter != NULL) {
1174 		dns_dbiterator_destroy(&iter);
1175 	}
1176 	if (top != NULL) {
1177 		dns_db_detachnode(db, &top);
1178 	}
1179 
1180 	return (answer);
1181 }
1182 
1183 isc_result_t
1184 dns_cache_flushname(dns_cache_t *cache, const dns_name_t *name) {
1185 	return (dns_cache_flushnode(cache, name, false));
1186 }
1187 
1188 isc_result_t
1189 dns_cache_flushnode(dns_cache_t *cache, const dns_name_t *name, bool tree) {
1190 	isc_result_t result;
1191 	dns_dbnode_t *node = NULL;
1192 	dns_db_t *db = NULL;
1193 
1194 	if (tree && dns_name_equal(name, dns_rootname)) {
1195 		return (dns_cache_flush(cache));
1196 	}
1197 
1198 	LOCK(&cache->lock);
1199 	if (cache->db != NULL) {
1200 		dns_db_attach(cache->db, &db);
1201 	}
1202 	UNLOCK(&cache->lock);
1203 	if (db == NULL) {
1204 		return (ISC_R_SUCCESS);
1205 	}
1206 
1207 	if (tree) {
1208 		result = cleartree(cache->db, name);
1209 	} else {
1210 		result = dns_db_findnode(cache->db, name, false, &node);
1211 		if (result == ISC_R_NOTFOUND) {
1212 			result = ISC_R_SUCCESS;
1213 			goto cleanup_db;
1214 		}
1215 		if (result != ISC_R_SUCCESS) {
1216 			goto cleanup_db;
1217 		}
1218 		result = clearnode(cache->db, node);
1219 		dns_db_detachnode(cache->db, &node);
1220 	}
1221 
1222 cleanup_db:
1223 	dns_db_detach(&db);
1224 	return (result);
1225 }
1226 
1227 isc_stats_t *
1228 dns_cache_getstats(dns_cache_t *cache) {
1229 	REQUIRE(VALID_CACHE(cache));
1230 	return (cache->stats);
1231 }
1232 
1233 void
1234 dns_cache_updatestats(dns_cache_t *cache, isc_result_t result) {
1235 	REQUIRE(VALID_CACHE(cache));
1236 	if (cache->stats == NULL) {
1237 		return;
1238 	}
1239 
1240 	switch (result) {
1241 	case ISC_R_SUCCESS:
1242 	case DNS_R_NCACHENXDOMAIN:
1243 	case DNS_R_NCACHENXRRSET:
1244 	case DNS_R_CNAME:
1245 	case DNS_R_DNAME:
1246 	case DNS_R_GLUE:
1247 	case DNS_R_ZONECUT:
1248 		isc_stats_increment(cache->stats,
1249 				    dns_cachestatscounter_queryhits);
1250 		break;
1251 	default:
1252 		isc_stats_increment(cache->stats,
1253 				    dns_cachestatscounter_querymisses);
1254 	}
1255 }
1256 
1257 /*
1258  * XXX: Much of the following code has been copied in from statschannel.c.
1259  * We should refactor this into a generic function in stats.c that can be
1260  * called from both places.
1261  */
1262 typedef struct cache_dumparg {
1263 	isc_statsformat_t type;
1264 	void *arg;		 /* type dependent argument */
1265 	int ncounters;		 /* for general statistics */
1266 	int *counterindices;	 /* for general statistics */
1267 	uint64_t *countervalues; /* for general statistics */
1268 	isc_result_t result;
1269 } cache_dumparg_t;
1270 
1271 static void
1272 getcounter(isc_statscounter_t counter, uint64_t val, void *arg) {
1273 	cache_dumparg_t *dumparg = arg;
1274 
1275 	REQUIRE(counter < dumparg->ncounters);
1276 	dumparg->countervalues[counter] = val;
1277 }
1278 
1279 static void
1280 getcounters(isc_stats_t *stats, isc_statsformat_t type, int ncounters,
1281 	    int *indices, uint64_t *values) {
1282 	cache_dumparg_t dumparg;
1283 
1284 	memset(values, 0, sizeof(values[0]) * ncounters);
1285 
1286 	dumparg.type = type;
1287 	dumparg.ncounters = ncounters;
1288 	dumparg.counterindices = indices;
1289 	dumparg.countervalues = values;
1290 
1291 	isc_stats_dump(stats, getcounter, &dumparg, ISC_STATSDUMP_VERBOSE);
1292 }
1293 
1294 void
1295 dns_cache_dumpstats(dns_cache_t *cache, FILE *fp) {
1296 	int indices[dns_cachestatscounter_max];
1297 	uint64_t values[dns_cachestatscounter_max];
1298 
1299 	REQUIRE(VALID_CACHE(cache));
1300 
1301 	getcounters(cache->stats, isc_statsformat_file,
1302 		    dns_cachestatscounter_max, indices, values);
1303 
1304 	fprintf(fp, "%20" PRIu64 " %s\n", values[dns_cachestatscounter_hits],
1305 		"cache hits");
1306 	fprintf(fp, "%20" PRIu64 " %s\n", values[dns_cachestatscounter_misses],
1307 		"cache misses");
1308 	fprintf(fp, "%20" PRIu64 " %s\n",
1309 		values[dns_cachestatscounter_queryhits],
1310 		"cache hits (from query)");
1311 	fprintf(fp, "%20" PRIu64 " %s\n",
1312 		values[dns_cachestatscounter_querymisses],
1313 		"cache misses (from query)");
1314 	fprintf(fp, "%20" PRIu64 " %s\n",
1315 		values[dns_cachestatscounter_deletelru],
1316 		"cache records deleted due to memory exhaustion");
1317 	fprintf(fp, "%20" PRIu64 " %s\n",
1318 		values[dns_cachestatscounter_deletettl],
1319 		"cache records deleted due to TTL expiration");
1320 	fprintf(fp, "%20u %s\n", dns_db_nodecount(cache->db),
1321 		"cache database nodes");
1322 	fprintf(fp, "%20" PRIu64 " %s\n", (uint64_t)dns_db_hashsize(cache->db),
1323 		"cache database hash buckets");
1324 
1325 	fprintf(fp, "%20" PRIu64 " %s\n", (uint64_t)isc_mem_total(cache->mctx),
1326 		"cache tree memory total");
1327 	fprintf(fp, "%20" PRIu64 " %s\n", (uint64_t)isc_mem_inuse(cache->mctx),
1328 		"cache tree memory in use");
1329 	fprintf(fp, "%20" PRIu64 " %s\n",
1330 		(uint64_t)isc_mem_maxinuse(cache->mctx),
1331 		"cache tree highest memory in use");
1332 
1333 	fprintf(fp, "%20" PRIu64 " %s\n", (uint64_t)isc_mem_total(cache->hmctx),
1334 		"cache heap memory total");
1335 	fprintf(fp, "%20" PRIu64 " %s\n", (uint64_t)isc_mem_inuse(cache->hmctx),
1336 		"cache heap memory in use");
1337 	fprintf(fp, "%20" PRIu64 " %s\n",
1338 		(uint64_t)isc_mem_maxinuse(cache->hmctx),
1339 		"cache heap highest memory in use");
1340 }
1341 
1342 #ifdef HAVE_LIBXML2
1343 #define TRY0(a)                     \
1344 	do {                        \
1345 		xmlrc = (a);        \
1346 		if (xmlrc < 0)      \
1347 			goto error; \
1348 	} while (/*CONSTCOND*/0)
1349 static int
1350 renderstat(const char *name, uint64_t value, xmlTextWriterPtr writer) {
1351 	int xmlrc;
1352 
1353 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter"));
1354 	TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "name",
1355 					 ISC_XMLCHAR name));
1356 	TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "", value));
1357 	TRY0(xmlTextWriterEndElement(writer)); /* counter */
1358 
1359 error:
1360 	return (xmlrc);
1361 }
1362 
1363 int
1364 dns_cache_renderxml(dns_cache_t *cache, void *writer0) {
1365 	int indices[dns_cachestatscounter_max];
1366 	uint64_t values[dns_cachestatscounter_max];
1367 	int xmlrc;
1368 	xmlTextWriterPtr writer = (xmlTextWriterPtr)writer0;
1369 
1370 	REQUIRE(VALID_CACHE(cache));
1371 
1372 	getcounters(cache->stats, isc_statsformat_file,
1373 		    dns_cachestatscounter_max, indices, values);
1374 	TRY0(renderstat("CacheHits", values[dns_cachestatscounter_hits],
1375 			writer));
1376 	TRY0(renderstat("CacheMisses", values[dns_cachestatscounter_misses],
1377 			writer));
1378 	TRY0(renderstat("QueryHits", values[dns_cachestatscounter_queryhits],
1379 			writer));
1380 	TRY0(renderstat("QueryMisses",
1381 			values[dns_cachestatscounter_querymisses], writer));
1382 	TRY0(renderstat("DeleteLRU", values[dns_cachestatscounter_deletelru],
1383 			writer));
1384 	TRY0(renderstat("DeleteTTL", values[dns_cachestatscounter_deletettl],
1385 			writer));
1386 
1387 	TRY0(renderstat("CacheNodes", dns_db_nodecount(cache->db), writer));
1388 	TRY0(renderstat("CacheBuckets", dns_db_hashsize(cache->db), writer));
1389 
1390 	TRY0(renderstat("TreeMemTotal", isc_mem_total(cache->mctx), writer));
1391 	TRY0(renderstat("TreeMemInUse", isc_mem_inuse(cache->mctx), writer));
1392 	TRY0(renderstat("TreeMemMax", isc_mem_maxinuse(cache->mctx), writer));
1393 
1394 	TRY0(renderstat("HeapMemTotal", isc_mem_total(cache->hmctx), writer));
1395 	TRY0(renderstat("HeapMemInUse", isc_mem_inuse(cache->hmctx), writer));
1396 	TRY0(renderstat("HeapMemMax", isc_mem_maxinuse(cache->hmctx), writer));
1397 error:
1398 	return (xmlrc);
1399 }
1400 #endif /* ifdef HAVE_LIBXML2 */
1401 
1402 #ifdef HAVE_JSON_C
1403 #define CHECKMEM(m)                              \
1404 	do {                                     \
1405 		if (m == NULL) {                 \
1406 			result = ISC_R_NOMEMORY; \
1407 			goto error;              \
1408 		}                                \
1409 	} while(/*CONSTCOND*/0)
1410 
1411 isc_result_t
1412 dns_cache_renderjson(dns_cache_t *cache, void *cstats0) {
1413 	isc_result_t result = ISC_R_SUCCESS;
1414 	int indices[dns_cachestatscounter_max];
1415 	uint64_t values[dns_cachestatscounter_max];
1416 	json_object *obj;
1417 	json_object *cstats = (json_object *)cstats0;
1418 
1419 	REQUIRE(VALID_CACHE(cache));
1420 
1421 	getcounters(cache->stats, isc_statsformat_file,
1422 		    dns_cachestatscounter_max, indices, values);
1423 
1424 	obj = json_object_new_int64(values[dns_cachestatscounter_hits]);
1425 	CHECKMEM(obj);
1426 	json_object_object_add(cstats, "CacheHits", obj);
1427 
1428 	obj = json_object_new_int64(values[dns_cachestatscounter_misses]);
1429 	CHECKMEM(obj);
1430 	json_object_object_add(cstats, "CacheMisses", obj);
1431 
1432 	obj = json_object_new_int64(values[dns_cachestatscounter_queryhits]);
1433 	CHECKMEM(obj);
1434 	json_object_object_add(cstats, "QueryHits", obj);
1435 
1436 	obj = json_object_new_int64(values[dns_cachestatscounter_querymisses]);
1437 	CHECKMEM(obj);
1438 	json_object_object_add(cstats, "QueryMisses", obj);
1439 
1440 	obj = json_object_new_int64(values[dns_cachestatscounter_deletelru]);
1441 	CHECKMEM(obj);
1442 	json_object_object_add(cstats, "DeleteLRU", obj);
1443 
1444 	obj = json_object_new_int64(values[dns_cachestatscounter_deletettl]);
1445 	CHECKMEM(obj);
1446 	json_object_object_add(cstats, "DeleteTTL", obj);
1447 
1448 	obj = json_object_new_int64(dns_db_nodecount(cache->db));
1449 	CHECKMEM(obj);
1450 	json_object_object_add(cstats, "CacheNodes", obj);
1451 
1452 	obj = json_object_new_int64(dns_db_hashsize(cache->db));
1453 	CHECKMEM(obj);
1454 	json_object_object_add(cstats, "CacheBuckets", obj);
1455 
1456 	obj = json_object_new_int64(isc_mem_total(cache->mctx));
1457 	CHECKMEM(obj);
1458 	json_object_object_add(cstats, "TreeMemTotal", obj);
1459 
1460 	obj = json_object_new_int64(isc_mem_inuse(cache->mctx));
1461 	CHECKMEM(obj);
1462 	json_object_object_add(cstats, "TreeMemInUse", obj);
1463 
1464 	obj = json_object_new_int64(isc_mem_maxinuse(cache->mctx));
1465 	CHECKMEM(obj);
1466 	json_object_object_add(cstats, "TreeMemMax", obj);
1467 
1468 	obj = json_object_new_int64(isc_mem_total(cache->hmctx));
1469 	CHECKMEM(obj);
1470 	json_object_object_add(cstats, "HeapMemTotal", obj);
1471 
1472 	obj = json_object_new_int64(isc_mem_inuse(cache->hmctx));
1473 	CHECKMEM(obj);
1474 	json_object_object_add(cstats, "HeapMemInUse", obj);
1475 
1476 	obj = json_object_new_int64(isc_mem_maxinuse(cache->hmctx));
1477 	CHECKMEM(obj);
1478 	json_object_object_add(cstats, "HeapMemMax", obj);
1479 
1480 	result = ISC_R_SUCCESS;
1481 error:
1482 	return (result);
1483 }
1484 #endif /* ifdef HAVE_JSON_C */
1485