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