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