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