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