1 /* $NetBSD: cache.c,v 1.8 2014/12/10 04:37:58 christos Exp $ */
2
3 /*
4 * Copyright (C) 2004-2009, 2011-2013 Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (C) 1999-2003 Internet Software Consortium.
6 *
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.
10 *
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.
18 */
19
20 /* Id: cache.c,v 1.91 2011/08/26 05:12:56 marka Exp */
21
22 /*! \file */
23
24 #include <config.h>
25
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>
36
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>
49
50 #include "rbtdb.h"
51
52 #define CACHE_MAGIC ISC_MAGIC('$', '$', '$', '$')
53 #define VALID_CACHE(cache) ISC_MAGIC_VALID(cache, CACHE_MAGIC)
54
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
59 */
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
65 */
66 #define DNS_CACHE_CLEANERINCREMENT 1000U /*%< Number of nodes. */
67
68 /***
69 *** Types
70 ***/
71
72 /*
73 * A cache_cleaner_t encapsulates the state of the periodic
74 * cache cleaning.
75 */
76
77 typedef struct cache_cleaner cache_cleaner_t;
78
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;
84
85 /*
86 * Convenience macros for comprehensive assertion checking.
87 */
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)
93
94 /*%
95 * Accesses to a cache cleaner object are synchronized through
96 * task/event serialization, or locked from the cache object.
97 */
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.
105 */
106
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;
115
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;
122 };
123
124 /*%
125 * The actual cache object.
126 */
127
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;
136
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;
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 cleaning_timer_action(isc_task_t *task, isc_event_t *event);
164
165 static void
166 incremental_cleaning_action(isc_task_t *task, isc_event_t *event);
167
168 static void
169 cleaner_shutdown_action(isc_task_t *task, isc_event_t *event);
170
171 static void
172 overmem_cleaning_action(isc_task_t *task, isc_event_t *event);
173
174 static inline isc_result_t
cache_create_db(dns_cache_t * cache,dns_db_t ** db)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));
179 }
180
181 isc_result_t
dns_cache_create(isc_mem_t * cmctx,isc_taskmgr_t * taskmgr,isc_timermgr_t * timermgr,dns_rdataclass_t rdclass,const char * db_type,unsigned int db_argc,char ** db_argv,dns_cache_t ** cachep)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)
186 {
187 return (dns_cache_create3(cmctx, cmctx, taskmgr, timermgr, rdclass, "",
188 db_type, db_argc, db_argv, cachep));
189 }
190
191 isc_result_t
dns_cache_create2(isc_mem_t * cmctx,isc_taskmgr_t * taskmgr,isc_timermgr_t * timermgr,dns_rdataclass_t rdclass,const char * cachename,const char * db_type,unsigned int db_argc,char ** db_argv,dns_cache_t ** cachep)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)
196 {
197 return (dns_cache_create3(cmctx, cmctx, taskmgr, timermgr, rdclass,
198 cachename, db_type, db_argc, db_argv,
199 cachep));
200 }
201
202 isc_result_t
dns_cache_create3(isc_mem_t * cmctx,isc_mem_t * hmctx,isc_taskmgr_t * taskmgr,isc_timermgr_t * timermgr,dns_rdataclass_t rdclass,const char * cachename,const char * db_type,unsigned int db_argc,char ** db_argv,dns_cache_t ** cachep)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)
207 {
208 isc_result_t result;
209 dns_cache_t *cache;
210 int i, extra = 0;
211 isc_task_t *dbtask;
212
213 REQUIRE(cachep != NULL);
214 REQUIRE(*cachep == NULL);
215 REQUIRE(cmctx != NULL);
216 REQUIRE(hmctx != NULL);
217 REQUIRE(cachename != NULL);
218
219 cache = isc_mem_get(cmctx, sizeof(*cache));
220 if (cache == NULL)
221 return (ISC_R_NOMEMORY);
222
223 cache->mctx = cache->hmctx = NULL;
224 isc_mem_attach(cmctx, &cache->mctx);
225 isc_mem_attach(hmctx, &cache->hmctx);
226
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;
233 }
234 }
235
236 result = isc_mutex_init(&cache->lock);
237 if (result != ISC_R_SUCCESS)
238 goto cleanup_mem;
239
240 result = isc_mutex_init(&cache->filelock);
241 if (result != ISC_R_SUCCESS)
242 goto cleanup_lock;
243
244 cache->references = 1;
245 cache->live_tasks = 0;
246 cache->rdclass = rdclass;
247
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;
253
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;
258 }
259
260 /*
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).
264 */
265 if (strcmp(cache->db_type, "rbt") == 0)
266 extra = 1;
267
268 cache->db_argc = db_argc + extra;
269 cache->db_argv = NULL;
270
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;
277 }
278
279 for (i = 0; i < cache->db_argc; i++)
280 cache->db_argv[i] = NULL;
281
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;
289 }
290 }
291 }
292
293 /*
294 * Create the database
295 */
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);
307 }
308
309 cache->filename = NULL;
310
311 cache->magic = CACHE_MAGIC;
312
313 /*
314 * RBT-type cache DB has its own mechanism of cache cleaning and doesn't
315 * need the control of the generic cleaner.
316 */
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);
322 }
323 if (result != ISC_R_SUCCESS)
324 goto cleanup_db;
325
326 result = dns_db_setcachestats(cache->db, cache->stats);
327 if (result != ISC_R_SUCCESS)
328 goto cleanup_db;
329
330
331 *cachep = cache;
332 return (ISC_R_SUCCESS);
333
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);
357 }
358
359 static void
cache_free(dns_cache_t * cache)360 cache_free(dns_cache_t *cache) {
361 int i;
362
363 REQUIRE(VALID_CACHE(cache));
364 REQUIRE(cache->references == 0);
365
366 isc_mem_setwater(cache->mctx, NULL, NULL, 0, 0);
367
368 if (cache->cleaner.task != NULL)
369 isc_task_detach(&cache->cleaner.task);
370
371 if (cache->cleaner.overmem_event != NULL)
372 isc_event_free(&cache->cleaner.overmem_event);
373
374 if (cache->cleaner.resched_event != NULL)
375 isc_event_free(&cache->cleaner.resched_event);
376
377 if (cache->cleaner.iterator != NULL)
378 dns_dbiterator_destroy(&cache->cleaner.iterator);
379
380 DESTROYLOCK(&cache->cleaner.lock);
381
382 if (cache->filename) {
383 isc_mem_free(cache->mctx, cache->filename);
384 cache->filename = NULL;
385 }
386
387 if (cache->db != NULL)
388 dns_db_detach(&cache->db);
389
390 if (cache->db_argv != NULL) {
391 /*
392 * We don't free db_argv[0] in "rbt" cache databases
393 * as it's a pointer to hmctx
394 */
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 *));
403 }
404
405 if (cache->db_type != NULL)
406 isc_mem_free(cache->mctx, cache->db_type);
407
408 if (cache->name != NULL)
409 isc_mem_free(cache->mctx, cache->name);
410
411 if (cache->stats != NULL)
412 isc_stats_detach(&cache->stats);
413
414 DESTROYLOCK(&cache->lock);
415 DESTROYLOCK(&cache->filelock);
416
417 cache->magic = 0;
418 isc_mem_detach(&cache->hmctx);
419 isc_mem_putanddetach(&cache->mctx, cache, sizeof(*cache));
420 }
421
422
423 void
dns_cache_attach(dns_cache_t * cache,dns_cache_t ** targetp)424 dns_cache_attach(dns_cache_t *cache, dns_cache_t **targetp) {
425
426 REQUIRE(VALID_CACHE(cache));
427 REQUIRE(targetp != NULL && *targetp == NULL);
428
429 LOCK(&cache->lock);
430 cache->references++;
431 UNLOCK(&cache->lock);
432
433 *targetp = cache;
434 }
435
436 void
dns_cache_detach(dns_cache_t ** cachep)437 dns_cache_detach(dns_cache_t **cachep) {
438 dns_cache_t *cache;
439 isc_boolean_t free_cache = ISC_FALSE;
440
441 REQUIRE(cachep != NULL);
442 cache = *cachep;
443 REQUIRE(VALID_CACHE(cache));
444
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;
451 }
452
453 *cachep = NULL;
454
455 if (free_cache) {
456 /*
457 * When the cache is shut down, dump it to a file if one is
458 * specified.
459 */
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));
466
467 /*
468 * If the cleaner task exists, let it free the cache.
469 */
470 if (cache->live_tasks > 0) {
471 isc_task_shutdown(cache->cleaner.task);
472 free_cache = ISC_FALSE;
473 }
474 }
475
476 UNLOCK(&cache->lock);
477
478 if (free_cache)
479 cache_free(cache);
480 }
481
482 void
dns_cache_attachdb(dns_cache_t * cache,dns_db_t ** dbp)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);
487
488 LOCK(&cache->lock);
489 dns_db_attach(cache->db, dbp);
490 UNLOCK(&cache->lock);
491
492 }
493
494 isc_result_t
dns_cache_setfilename(dns_cache_t * cache,const char * filename)495 dns_cache_setfilename(dns_cache_t *cache, const char *filename) {
496 char *newname;
497
498 REQUIRE(VALID_CACHE(cache));
499 REQUIRE(filename != NULL);
500
501 newname = isc_mem_strdup(cache->mctx, filename);
502 if (newname == NULL)
503 return (ISC_R_NOMEMORY);
504
505 LOCK(&cache->filelock);
506 if (cache->filename)
507 isc_mem_free(cache->mctx, cache->filename);
508 cache->filename = newname;
509 UNLOCK(&cache->filelock);
510
511 return (ISC_R_SUCCESS);
512 }
513
514 isc_result_t
dns_cache_load(dns_cache_t * cache)515 dns_cache_load(dns_cache_t *cache) {
516 isc_result_t result;
517
518 REQUIRE(VALID_CACHE(cache));
519
520 if (cache->filename == NULL)
521 return (ISC_R_SUCCESS);
522
523 LOCK(&cache->filelock);
524 result = dns_db_load(cache->db, cache->filename);
525 UNLOCK(&cache->filelock);
526
527 return (result);
528 }
529
530 isc_result_t
dns_cache_dump(dns_cache_t * cache)531 dns_cache_dump(dns_cache_t *cache) {
532 isc_result_t result;
533
534 REQUIRE(VALID_CACHE(cache));
535
536 if (cache->filename == NULL)
537 return (ISC_R_SUCCESS);
538
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);
544
545 }
546
547 void
dns_cache_setcleaninginterval(dns_cache_t * cache,unsigned int t)548 dns_cache_setcleaninginterval(dns_cache_t *cache, unsigned int t) {
549 isc_interval_t interval;
550 isc_result_t result;
551
552 LOCK(&cache->lock);
553
554 /*
555 * It may be the case that the cache has already shut down.
556 * If so, it has no timer.
557 */
558 if (cache->cleaner.cleaning_timer == NULL)
559 goto unlock;
560
561 cache->cleaner.cleaning_interval = t;
562
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,
569 0);
570 result = isc_timer_reset(cache->cleaner.cleaning_timer,
571 isc_timertype_ticker,
572 NULL, &interval, ISC_FALSE);
573 }
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));
579
580 unlock:
581 UNLOCK(&cache->lock);
582 }
583
584 unsigned int
dns_cache_getcleaninginterval(dns_cache_t * cache)585 dns_cache_getcleaninginterval(dns_cache_t *cache) {
586 unsigned int t;
587
588 REQUIRE(VALID_CACHE(cache));
589
590 LOCK(&cache->lock);
591 t = cache->cleaner.cleaning_interval;
592 UNLOCK(&cache->lock);
593
594 return (t);
595 }
596
597 const char *
dns_cache_getname(dns_cache_t * cache)598 dns_cache_getname(dns_cache_t *cache) {
599 REQUIRE(VALID_CACHE(cache));
600
601 return (cache->name);
602 }
603
604 /*
605 * Initialize the cache cleaner object at *cleaner.
606 * Space for the object must be allocated by the caller.
607 */
608
609 static isc_result_t
cache_cleaner_init(dns_cache_t * cache,isc_taskmgr_t * taskmgr,isc_timermgr_t * timermgr,cache_cleaner_t * cleaner)610 cache_cleaner_init(dns_cache_t *cache, isc_taskmgr_t *taskmgr,
611 isc_timermgr_t *timermgr, cache_cleaner_t *cleaner)
612 {
613 isc_result_t result;
614
615 result = isc_mutex_init(&cleaner->lock);
616 if (result != ISC_R_SUCCESS)
617 goto fail;
618
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;
625
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. */
631
632 result = dns_db_createiterator(cleaner->cache->db, ISC_FALSE,
633 &cleaner->iterator);
634 if (result != ISC_R_SUCCESS)
635 goto cleanup;
636
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;
645 }
646 cleaner->cache->live_tasks++;
647 isc_task_setname(cleaner->task, "cachecleaner", cleaner);
648
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;
657 }
658
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;
669 }
670
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;
679 }
680
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;
689 }
690 }
691
692 return (ISC_R_SUCCESS);
693
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);
708 }
709
710 static void
begin_cleaning(cache_cleaner_t * cleaner)711 begin_cleaning(cache_cleaner_t *cleaner) {
712 isc_result_t result = ISC_R_SUCCESS;
713
714 REQUIRE(CLEANER_IDLE(cleaner));
715
716 /*
717 * Create an iterator, if it does not already exist, and
718 * position it at the beginning of the cache.
719 */
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);
731 }
732 if (result != ISC_R_SUCCESS) {
733 /*
734 * If the result is ISC_R_NOMORE, the database is empty,
735 * so there is nothing to be cleaned.
736 */
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);
746 }
747 } else {
748 /*
749 * Pause the iterator to free its lock.
750 */
751 result = dns_dbiterator_pause(cleaner->iterator);
752 RUNTIME_CHECK(result == ISC_R_SUCCESS);
753
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);
760 }
761
762 return;
763 }
764
765 static void
end_cleaning(cache_cleaner_t * cleaner,isc_event_t * event)766 end_cleaning(cache_cleaner_t *cleaner, isc_event_t *event) {
767 isc_result_t result;
768
769 REQUIRE(CLEANER_BUSY(cleaner));
770 REQUIRE(event != NULL);
771
772 result = dns_dbiterator_pause(cleaner->iterator);
773 if (result != ISC_R_SUCCESS)
774 dns_dbiterator_destroy(&cleaner->iterator);
775
776 dns_cache_setcleaninginterval(cleaner->cache,
777 cleaner->cleaning_interval);
778
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));
782
783 cleaner->state = cleaner_s_idle;
784 cleaner->resched_event = event;
785 }
786
787 /*
788 * This is run once for every cache-cleaning-interval as defined in named.conf.
789 */
790 static void
cleaning_timer_action(isc_task_t * task,isc_event_t * event)791 cleaning_timer_action(isc_task_t *task, isc_event_t *event) {
792 cache_cleaner_t *cleaner = event->ev_arg;
793
794 UNUSED(task);
795
796 INSIST(task == cleaner->task);
797 INSIST(event->ev_type == ISC_TIMEREVENT_TICK);
798
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);
802
803 if (cleaner->state == cleaner_s_idle)
804 begin_cleaning(cleaner);
805
806 isc_event_free(&event);
807 }
808
809 /*
810 * This is called when the cache either surpasses its upper limit
811 * or shrinks beyond its lower limit.
812 */
813 static void
overmem_cleaning_action(isc_task_t * task,isc_event_t * event)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;
817
818 UNUSED(task);
819
820 INSIST(task == cleaner->task);
821 INSIST(event->ev_type == DNS_EVENT_CACHEOVERMEM);
822 INSIST(cleaner->overmem_event == NULL);
823
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);
828
829 LOCK(&cleaner->lock);
830
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)
836 /*
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.
843 */
844 cleaner->state = cleaner_s_done;
845 }
846
847 cleaner->overmem_event = event;
848
849 UNLOCK(&cleaner->lock);
850
851 if (want_cleaning)
852 begin_cleaning(cleaner);
853 }
854
855 /*
856 * Do incremental cleaning.
857 */
858 static void
incremental_cleaning_action(isc_task_t * task,isc_event_t * event)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;
864
865 UNUSED(task);
866
867 INSIST(task == cleaner->task);
868 INSIST(event->ev_type == DNS_EVENT_CACHECLEAN);
869
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;
881 }
882 UNLOCK(&cleaner->lock);
883 UNLOCK(&cleaner->cache->lock);
884 return;
885 }
886
887 INSIST(CLEANER_BUSY(cleaner));
888
889 n_names = cleaner->increment;
890
891 REQUIRE(DNS_DBITERATOR_VALID(cleaner->iterator));
892
893 isc_time_now(&start);
894 while (n_names-- > 0) {
895 dns_dbnode_t *node = NULL;
896
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));
903
904 end_cleaning(cleaner, event);
905 return;
906 }
907
908 /*
909 * The node was not needed, but was required by
910 * dns_dbiterator_current(). Give up its reference.
911 */
912 dns_db_detachnode(cleaner->cache->db, &node);
913
914 /*
915 * Step to the next node.
916 */
917 result = dns_dbiterator_next(cleaner->iterator);
918
919 if (result != ISC_R_SUCCESS) {
920 /*
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.
925 */
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;
944 }
945 }
946
947 end_cleaning(cleaner, event);
948 return;
949 }
950 }
951
952 /*
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.
957 */
958 result = dns_dbiterator_pause(cleaner->iterator);
959 RUNTIME_CHECK(result == ISC_R_SUCCESS);
960
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));
965
966 isc_task_send(task, &event);
967 INSIST(CLEANER_BUSY(cleaner));
968 return;
969 }
970
971 /*
972 * Do immediate cleaning.
973 */
974 isc_result_t
dns_cache_clean(dns_cache_t * cache,isc_stdtime_t now)975 dns_cache_clean(dns_cache_t *cache, isc_stdtime_t now) {
976 isc_result_t result;
977 dns_dbiterator_t *iterator = NULL;
978
979 REQUIRE(VALID_CACHE(cache));
980
981 result = dns_db_createiterator(cache->db, 0, &iterator);
982 if (result != ISC_R_SUCCESS)
983 return result;
984
985 result = dns_dbiterator_first(iterator);
986
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;
993
994 /*
995 * Check TTLs, mark expired rdatasets stale.
996 */
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));
1003 /*
1004 * Continue anyway.
1005 */
1006 }
1007
1008 /*
1009 * This is where the actual freeing takes place.
1010 */
1011 dns_db_detachnode(cache->db, &node);
1012
1013 result = dns_dbiterator_next(iterator);
1014 }
1015
1016 dns_dbiterator_destroy(&iterator);
1017
1018 if (result == ISC_R_NOMORE)
1019 result = ISC_R_SUCCESS;
1020
1021 return (result);
1022 }
1023
1024 static void
water(void * arg,int mark)1025 water(void *arg, int mark) {
1026 dns_cache_t *cache = arg;
1027 isc_boolean_t overmem = ISC_TF(mark == ISC_MEM_HIWATER);
1028
1029 REQUIRE(VALID_CACHE(cache));
1030
1031 LOCK(&cache->cleaner.lock);
1032
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);
1037 }
1038
1039 if (cache->cleaner.overmem_event != NULL)
1040 isc_task_send(cache->cleaner.task,
1041 &cache->cleaner.overmem_event);
1042
1043 UNLOCK(&cache->cleaner.lock);
1044 }
1045
1046 void
dns_cache_setcachesize(dns_cache_t * cache,size_t size)1047 dns_cache_setcachesize(dns_cache_t *cache, size_t size) {
1048 size_t hiwater, lowater;
1049
1050 REQUIRE(VALID_CACHE(cache));
1051
1052 /*
1053 * Impose a minimum cache size; pathological things happen if there
1054 * is too little room.
1055 */
1056 if (size != 0U && size < DNS_CACHE_MINSIZE)
1057 size = DNS_CACHE_MINSIZE;
1058
1059 LOCK(&cache->lock);
1060 cache->size = size;
1061 UNLOCK(&cache->lock);
1062
1063 hiwater = size - (size >> 3); /* Approximately 7/8ths. */
1064 lowater = size - (size >> 2); /* Approximately 3/4ths. */
1065
1066 /*
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().
1071 */
1072
1073 if (size == 0U || hiwater == 0U || lowater == 0U)
1074 /*
1075 * Disable cache memory limiting.
1076 */
1077 isc_mem_setwater(cache->mctx, water, cache, 0, 0);
1078 else
1079 /*
1080 * Establish new cache memory limits (either for the first
1081 * time, or replacing other limits).
1082 */
1083 isc_mem_setwater(cache->mctx, water, cache, hiwater, lowater);
1084 }
1085
1086 size_t
dns_cache_getcachesize(dns_cache_t * cache)1087 dns_cache_getcachesize(dns_cache_t *cache) {
1088 size_t size;
1089
1090 REQUIRE(VALID_CACHE(cache));
1091
1092 LOCK(&cache->lock);
1093 size = cache->size;
1094 UNLOCK(&cache->lock);
1095
1096 return (size);
1097 }
1098
1099 /*
1100 * The cleaner task is shutting down; do the necessary cleanup.
1101 */
1102 static void
cleaner_shutdown_action(isc_task_t * task,isc_event_t * event)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;
1106
1107 UNUSED(task);
1108
1109 INSIST(task == cache->cleaner.task);
1110 INSIST(event->ev_type == ISC_TASKEVENT_SHUTDOWN);
1111
1112 if (CLEANER_BUSY(&cache->cleaner))
1113 end_cleaning(&cache->cleaner, event);
1114 else
1115 isc_event_free(&event);
1116
1117 LOCK(&cache->lock);
1118
1119 cache->live_tasks--;
1120 INSIST(cache->live_tasks == 0);
1121
1122 if (cache->references == 0)
1123 should_free = ISC_TRUE;
1124
1125 /*
1126 * By detaching the timer in the context of its task,
1127 * we are guaranteed that there will be no further timer
1128 * events.
1129 */
1130 if (cache->cleaner.cleaning_timer != NULL)
1131 isc_timer_detach(&cache->cleaner.cleaning_timer);
1132
1133 /* Make sure we don't reschedule anymore. */
1134 (void)isc_task_purge(task, NULL, DNS_EVENT_CACHECLEAN, NULL);
1135
1136 UNLOCK(&cache->lock);
1137
1138 if (should_free)
1139 cache_free(cache);
1140 }
1141
1142 isc_result_t
dns_cache_flush(dns_cache_t * cache)1143 dns_cache_flush(dns_cache_t *cache) {
1144 dns_db_t *db = NULL;
1145 isc_result_t result;
1146
1147 result = cache_create_db(cache, &db);
1148 if (result != ISC_R_SUCCESS)
1149 return (result);
1150
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;
1162 }
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);
1168
1169 return (ISC_R_SUCCESS);
1170 }
1171
1172 static isc_result_t
clearnode(dns_db_t * db,dns_dbnode_t * node)1173 clearnode(dns_db_t *db, dns_dbnode_t *node) {
1174 isc_result_t result;
1175 dns_rdatasetiter_t *iter = NULL;
1176
1177 result = dns_db_allrdatasets(db, node, NULL, (isc_stdtime_t)0, &iter);
1178 if (result != ISC_R_SUCCESS)
1179 return (result);
1180
1181 for (result = dns_rdatasetiter_first(iter);
1182 result == ISC_R_SUCCESS;
1183 result = dns_rdatasetiter_next(iter))
1184 {
1185 dns_rdataset_t rdataset;
1186 dns_rdataset_init(&rdataset);
1187
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;
1194 }
1195
1196 if (result == ISC_R_NOMORE)
1197 result = ISC_R_SUCCESS;
1198
1199 dns_rdatasetiter_destroy(&iter);
1200 return (result);
1201 }
1202
1203 static isc_result_t
cleartree(dns_db_t * db,dns_name_t * name)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;
1210
1211 dns_fixedname_init(&fnodename);
1212 nodename = dns_fixedname_name(&fnodename);
1213
1214 result = dns_db_createiterator(db, 0, &iter);
1215 if (result != ISC_R_SUCCESS)
1216 goto cleanup;
1217
1218 result = dns_dbiterator_seek(iter, name);
1219 if (result != ISC_R_SUCCESS)
1220 goto cleanup;
1221
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;
1228 /*
1229 * Are we done?
1230 */
1231 if (! dns_name_issubdomain(nodename, name))
1232 goto cleanup;
1233
1234 /*
1235 * If clearnode fails record and move onto the next node.
1236 */
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);
1242 }
1243
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);
1253
1254 return (answer);
1255 }
1256
1257 isc_result_t
dns_cache_flushname(dns_cache_t * cache,dns_name_t * name)1258 dns_cache_flushname(dns_cache_t *cache, dns_name_t *name) {
1259 return (dns_cache_flushnode(cache, name, ISC_FALSE));
1260 }
1261
1262 isc_result_t
dns_cache_flushnode(dns_cache_t * cache,dns_name_t * name,isc_boolean_t tree)1263 dns_cache_flushnode(dns_cache_t *cache, dns_name_t *name,
1264 isc_boolean_t tree)
1265 {
1266 isc_result_t result;
1267 dns_dbnode_t *node = NULL;
1268 dns_db_t *db = NULL;
1269
1270 if (dns_name_equal(name, dns_rootname))
1271 return (dns_cache_flush(cache));
1272
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);
1279
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;
1287 }
1288 if (result != ISC_R_SUCCESS)
1289 goto cleanup_db;
1290 result = clearnode(cache->db, node);
1291 dns_db_detachnode(cache->db, &node);
1292 }
1293
1294 cleanup_db:
1295 dns_db_detach(&db);
1296 return (result);
1297 }
1298
1299 isc_stats_t *
dns_cache_getstats(dns_cache_t * cache)1300 dns_cache_getstats(dns_cache_t *cache) {
1301 REQUIRE(VALID_CACHE(cache));
1302 return (cache->stats);
1303 }
1304
1305 void
dns_cache_updatestats(dns_cache_t * cache,isc_result_t result)1306 dns_cache_updatestats(dns_cache_t *cache, isc_result_t result) {
1307 REQUIRE(VALID_CACHE(cache));
1308 if (cache->stats == NULL)
1309 return;
1310
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);
1325 }
1326 }
1327
1328 /*
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.
1332 */
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;
1342
1343 static void
getcounter(isc_statscounter_t counter,isc_uint64_t val,void * arg)1344 getcounter(isc_statscounter_t counter, isc_uint64_t val, void *arg) {
1345 cache_dumparg_t *dumparg = arg;
1346
1347 REQUIRE(counter < dumparg->ncounters);
1348 dumparg->countervalues[counter] = val;
1349 }
1350
1351 static void
getcounters(isc_stats_t * stats,isc_statsformat_t type,int ncounters,int * indices,isc_uint64_t * values)1352 getcounters(isc_stats_t *stats, isc_statsformat_t type, int ncounters,
1353 int *indices, isc_uint64_t *values)
1354 {
1355 cache_dumparg_t dumparg;
1356
1357 memset(values, 0, sizeof(values[0]) * ncounters);
1358
1359 dumparg.type = type;
1360 dumparg.ncounters = ncounters;
1361 dumparg.counterindices = indices;
1362 dumparg.countervalues = values;
1363
1364 isc_stats_dump(stats, getcounter, &dumparg, ISC_STATSDUMP_VERBOSE);
1365 }
1366
1367 void
dns_cache_dumpstats(dns_cache_t * cache,FILE * fp)1368 dns_cache_dumpstats(dns_cache_t *cache, FILE *fp) {
1369 int indices[dns_cachestatscounter_max];
1370 isc_uint64_t values[dns_cachestatscounter_max];
1371
1372 REQUIRE(VALID_CACHE(cache));
1373
1374 getcounters(cache->stats, isc_statsformat_file,
1375 dns_cachestatscounter_max, indices, values);
1376
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");
1399
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");
1406
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");
1413 }
1414
1415 #ifdef HAVE_LIBXML2
1416 #define TRY0(a) do { xmlrc = (a); if (xmlrc < 0) goto error; } while(/*CONSTCOND*/0)
1417 static int
renderstat(const char * name,isc_uint64_t value,xmlTextWriterPtr writer)1418 renderstat(const char *name, isc_uint64_t value, xmlTextWriterPtr writer) {
1419 int xmlrc;
1420
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 */
1428
1429 error:
1430 return (xmlrc);
1431 }
1432
1433 int
dns_cache_renderxml(dns_cache_t * cache,xmlTextWriterPtr writer)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;
1438
1439 REQUIRE(VALID_CACHE(cache));
1440
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));
1455
1456 TRY0(renderstat("CacheNodes", dns_db_nodecount(cache->db), writer));
1457 TRY0(renderstat("CacheBuckets", dns_db_hashsize(cache->db), writer));
1458
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));
1462
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);
1468 }
1469 #endif
1470
1471 #ifdef HAVE_JSON
1472 #define CHECKMEM(m) do { \
1473 if (m == NULL) { \
1474 result = ISC_R_NOMEMORY;\
1475 goto error;\
1476 } \
1477 } while(/*CONSTCOND*/0)
1478
1479 isc_result_t
dns_cache_renderjson(dns_cache_t * cache,json_object * cstats)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;
1485
1486 REQUIRE(VALID_CACHE(cache));
1487
1488 getcounters(cache->stats, isc_statsformat_file,
1489 dns_cachestatscounter_max, indices, values);
1490
1491 obj = json_object_new_int64(values[dns_cachestatscounter_hits]);
1492 CHECKMEM(obj);
1493 json_object_object_add(cstats, "CacheHits", obj);
1494
1495 obj = json_object_new_int64(values[dns_cachestatscounter_misses]);
1496 CHECKMEM(obj);
1497 json_object_object_add(cstats, "CacheMisses", obj);
1498
1499 obj = json_object_new_int64(values[dns_cachestatscounter_queryhits]);
1500 CHECKMEM(obj);
1501 json_object_object_add(cstats, "QueryHits", obj);
1502
1503 obj = json_object_new_int64(values[dns_cachestatscounter_querymisses]);
1504 CHECKMEM(obj);
1505 json_object_object_add(cstats, "QueryMisses", obj);
1506
1507 obj = json_object_new_int64(values[dns_cachestatscounter_deletelru]);
1508 CHECKMEM(obj);
1509 json_object_object_add(cstats, "DeleteLRU", obj);
1510
1511 obj = json_object_new_int64(values[dns_cachestatscounter_deletettl]);
1512 CHECKMEM(obj);
1513 json_object_object_add(cstats, "DeleteTTL", obj);
1514
1515 obj = json_object_new_int64(dns_db_nodecount(cache->db));
1516 CHECKMEM(obj);
1517 json_object_object_add(cstats, "CacheNodes", obj);
1518
1519 obj = json_object_new_int64(dns_db_hashsize(cache->db));
1520 CHECKMEM(obj);
1521 json_object_object_add(cstats, "CacheBuckets", obj);
1522
1523 obj = json_object_new_int64(isc_mem_total(cache->mctx));
1524 CHECKMEM(obj);
1525 json_object_object_add(cstats, "TreeMemTotal", obj);
1526
1527 obj = json_object_new_int64(isc_mem_inuse(cache->mctx));
1528 CHECKMEM(obj);
1529 json_object_object_add(cstats, "TreeMemInUse", obj);
1530
1531 obj = json_object_new_int64(isc_mem_maxinuse(cache->mctx));
1532 CHECKMEM(obj);
1533 json_object_object_add(cstats, "HeapMemMax", obj);
1534
1535 obj = json_object_new_int64(isc_mem_total(cache->hmctx));
1536 CHECKMEM(obj);
1537 json_object_object_add(cstats, "HeapMemTotal", obj);
1538
1539 obj = json_object_new_int64(isc_mem_inuse(cache->hmctx));
1540 CHECKMEM(obj);
1541 json_object_object_add(cstats, "HeapMemInUse", obj);
1542
1543 obj = json_object_new_int64(isc_mem_maxinuse(cache->hmctx));
1544 CHECKMEM(obj);
1545 json_object_object_add(cstats, "HeapMemMax", obj);
1546
1547 result = ISC_R_SUCCESS;
1548 error:
1549 return (result);
1550 }
1551 #endif
1552