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