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