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