1/* 2 * Copyright (C) 2004-2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") 3 * Copyright (C) 1999-2003 Internet Software Consortium. 4 * 5 * Permission to use, copy, modify, and/or distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15 * PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18/* $Id$ */ 19 20/*! \file */ 21 22#include <config.h> 23 24#include <isc/mem.h> 25#include <isc/string.h> 26#include <isc/task.h> 27#include <isc/time.h> 28#include <isc/timer.h> 29#include <isc/util.h> 30 31#include <dns/cache.h> 32#include <dns/db.h> 33#include <dns/dbiterator.h> 34#include <dns/events.h> 35#include <dns/lib.h> 36#include <dns/log.h> 37#include <dns/masterdump.h> 38#include <dns/rdata.h> 39#include <dns/rdataset.h> 40#include <dns/rdatasetiter.h> 41#include <dns/result.h> 42 43#include "rbtdb.h" 44 45#define CACHE_MAGIC ISC_MAGIC('$', '$', '$', '$') 46#define VALID_CACHE(cache) ISC_MAGIC_VALID(cache, CACHE_MAGIC) 47 48/*! 49 * Control incremental cleaning. 50 * DNS_CACHE_MINSIZE is how many bytes is the floor for dns_cache_setcachesize(). 51 * See also DNS_CACHE_CLEANERINCREMENT 52 */ 53#define DNS_CACHE_MINSIZE 2097152 /*%< Bytes. 2097152 = 2 MB */ 54/*! 55 * Control incremental cleaning. 56 * CLEANERINCREMENT is how many nodes are examined in one pass. 57 * See also DNS_CACHE_MINSIZE 58 */ 59#define DNS_CACHE_CLEANERINCREMENT 1000U /*%< Number of nodes. */ 60 61/*** 62 *** Types 63 ***/ 64 65/* 66 * A cache_cleaner_t encapsulates the state of the periodic 67 * cache cleaning. 68 */ 69 70typedef struct cache_cleaner cache_cleaner_t; 71 72typedef enum { 73 cleaner_s_idle, /*%< Waiting for cleaning-interval to expire. */ 74 cleaner_s_busy, /*%< Currently cleaning. */ 75 cleaner_s_done /*%< Freed enough memory after being overmem. */ 76} cleaner_state_t; 77 78/* 79 * Convenience macros for comprehensive assertion checking. 80 */ 81#define CLEANER_IDLE(c) ((c)->state == cleaner_s_idle && \ 82 (c)->resched_event != NULL) 83#define CLEANER_BUSY(c) ((c)->state == cleaner_s_busy && \ 84 (c)->iterator != NULL && \ 85 (c)->resched_event == NULL) 86 87/*% 88 * Accesses to a cache cleaner object are synchronized through 89 * task/event serialization, or locked from the cache object. 90 */ 91struct cache_cleaner { 92 isc_mutex_t lock; 93 /*%< 94 * Locks overmem_event, overmem. Note: never allocate memory 95 * while holding this lock - that could lead to deadlock since 96 * the lock is take by water() which is called from the memory 97 * allocator. 98 */ 99 100 dns_cache_t *cache; 101 isc_task_t *task; 102 unsigned int cleaning_interval; /*% The cleaning-interval from 103 named.conf, in seconds. */ 104 isc_timer_t *cleaning_timer; 105 isc_event_t *resched_event; /*% Sent by cleaner task to 106 itself to reschedule */ 107 isc_event_t *overmem_event; 108 109 dns_dbiterator_t *iterator; 110 unsigned int increment; /*% Number of names to 111 clean in one increment */ 112 cleaner_state_t state; /*% Idle/Busy. */ 113 isc_boolean_t overmem; /*% The cache is in an overmem state. */ 114 isc_boolean_t replaceiterator; 115}; 116 117/*% 118 * The actual cache object. 119 */ 120 121struct dns_cache { 122 /* Unlocked. */ 123 unsigned int magic; 124 isc_mutex_t lock; 125 isc_mutex_t filelock; 126 isc_mem_t *mctx; /* Main cache memory */ 127 isc_mem_t *hmctx; /* Heap memory */ 128 char *name; 129 130 /* Locked by 'lock'. */ 131 int references; 132 int live_tasks; 133 dns_rdataclass_t rdclass; 134 dns_db_t *db; 135 cache_cleaner_t cleaner; 136 char *db_type; 137 int db_argc; 138 char **db_argv; 139 isc_uint32_t size; 140 141 /* Locked by 'filelock'. */ 142 char *filename; 143 /* Access to the on-disk cache file is also locked by 'filelock'. */ 144}; 145 146/*** 147 *** Functions 148 ***/ 149 150static isc_result_t 151cache_cleaner_init(dns_cache_t *cache, isc_taskmgr_t *taskmgr, 152 isc_timermgr_t *timermgr, cache_cleaner_t *cleaner); 153 154static void 155cleaning_timer_action(isc_task_t *task, isc_event_t *event); 156 157static void 158incremental_cleaning_action(isc_task_t *task, isc_event_t *event); 159 160static void 161cleaner_shutdown_action(isc_task_t *task, isc_event_t *event); 162 163static void 164overmem_cleaning_action(isc_task_t *task, isc_event_t *event); 165 166static inline isc_result_t 167cache_create_db(dns_cache_t *cache, dns_db_t **db) { 168 return (dns_db_create(cache->mctx, cache->db_type, dns_rootname, 169 dns_dbtype_cache, cache->rdclass, 170 cache->db_argc, cache->db_argv, db)); 171} 172 173isc_result_t 174dns_cache_create(isc_mem_t *cmctx, isc_taskmgr_t *taskmgr, 175 isc_timermgr_t *timermgr, dns_rdataclass_t rdclass, 176 const char *db_type, unsigned int db_argc, char **db_argv, 177 dns_cache_t **cachep) 178{ 179 return (dns_cache_create3(cmctx, cmctx, taskmgr, timermgr, rdclass, "", 180 db_type, db_argc, db_argv, cachep)); 181} 182 183isc_result_t 184dns_cache_create2(isc_mem_t *cmctx, 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 return (dns_cache_create3(cmctx, cmctx, taskmgr, timermgr, rdclass, 190 cachename, db_type, db_argc, db_argv, 191 cachep)); 192} 193 194isc_result_t 195dns_cache_create3(isc_mem_t *cmctx, isc_mem_t *hmctx, isc_taskmgr_t *taskmgr, 196 isc_timermgr_t *timermgr, dns_rdataclass_t rdclass, 197 const char *cachename, const char *db_type, 198 unsigned int db_argc, char **db_argv, dns_cache_t **cachep) 199{ 200 isc_result_t result; 201 dns_cache_t *cache; 202 int i, extra = 0; 203 isc_task_t *dbtask; 204 205 REQUIRE(cachep != NULL); 206 REQUIRE(*cachep == NULL); 207 REQUIRE(cmctx != NULL); 208 REQUIRE(hmctx != NULL); 209 REQUIRE(cachename != NULL); 210 211 cache = isc_mem_get(cmctx, sizeof(*cache)); 212 if (cache == NULL) 213 return (ISC_R_NOMEMORY); 214 215 cache->mctx = cache->hmctx = NULL; 216 isc_mem_attach(cmctx, &cache->mctx); 217 isc_mem_attach(hmctx, &cache->hmctx); 218 219 cache->name = NULL; 220 if (cachename != NULL) { 221 cache->name = isc_mem_strdup(cmctx, cachename); 222 if (cache->name == NULL) { 223 result = ISC_R_NOMEMORY; 224 goto cleanup_mem; 225 } 226 } 227 228 result = isc_mutex_init(&cache->lock); 229 if (result != ISC_R_SUCCESS) 230 goto cleanup_mem; 231 232 result = isc_mutex_init(&cache->filelock); 233 if (result != ISC_R_SUCCESS) 234 goto cleanup_lock; 235 236 cache->references = 1; 237 cache->live_tasks = 0; 238 cache->rdclass = rdclass; 239 240 cache->db_type = isc_mem_strdup(cmctx, db_type); 241 if (cache->db_type == NULL) { 242 result = ISC_R_NOMEMORY; 243 goto cleanup_filelock; 244 } 245 246 /* 247 * For databases of type "rbt" we pass hmctx to dns_db_create() 248 * via cache->db_argv, followed by the rest of the arguments in 249 * db_argv (of which there really shouldn't be any). 250 */ 251 if (strcmp(cache->db_type, "rbt") == 0) 252 extra = 1; 253 254 cache->db_argc = db_argc + extra; 255 cache->db_argv = NULL; 256 257 if (cache->db_argc != 0) { 258 cache->db_argv = isc_mem_get(cmctx, 259 cache->db_argc * sizeof(char *)); 260 if (cache->db_argv == NULL) { 261 result = ISC_R_NOMEMORY; 262 goto cleanup_dbtype; 263 } 264 265 for (i = 0; i < cache->db_argc; i++) 266 cache->db_argv[i] = NULL; 267 268 cache->db_argv[0] = (char *) hmctx; 269 for (i = extra; i < cache->db_argc; i++) { 270 cache->db_argv[i] = isc_mem_strdup(cmctx, 271 db_argv[i - extra]); 272 if (cache->db_argv[i] == NULL) { 273 result = ISC_R_NOMEMORY; 274 goto cleanup_dbargv; 275 } 276 } 277 } 278 279 /* 280 * Create the database 281 */ 282 cache->db = NULL; 283 result = cache_create_db(cache, &cache->db); 284 if (result != ISC_R_SUCCESS) 285 goto cleanup_dbargv; 286 if (taskmgr != NULL) { 287 dbtask = NULL; 288 result = isc_task_create(taskmgr, 1, &dbtask); 289 if (result != ISC_R_SUCCESS) 290 goto cleanup_db; 291 dns_db_settask(cache->db, dbtask); 292 isc_task_detach(&dbtask); 293 } 294 295 cache->filename = NULL; 296 297 cache->magic = CACHE_MAGIC; 298 299 /* 300 * RBT-type cache DB has its own mechanism of cache cleaning and doesn't 301 * need the control of the generic cleaner. 302 */ 303 if (strcmp(db_type, "rbt") == 0) 304 result = cache_cleaner_init(cache, NULL, NULL, &cache->cleaner); 305 else { 306 result = cache_cleaner_init(cache, taskmgr, timermgr, 307 &cache->cleaner); 308 } 309 if (result != ISC_R_SUCCESS) 310 goto cleanup_db; 311 312 *cachep = cache; 313 return (ISC_R_SUCCESS); 314 315 cleanup_db: 316 dns_db_detach(&cache->db); 317 cleanup_dbargv: 318 for (i = extra; i < cache->db_argc; i++) 319 if (cache->db_argv[i] != NULL) 320 isc_mem_free(cmctx, cache->db_argv[i]); 321 if (cache->db_argv != NULL) 322 isc_mem_put(cmctx, cache->db_argv, 323 cache->db_argc * sizeof(char *)); 324 cleanup_dbtype: 325 isc_mem_free(cmctx, cache->db_type); 326 cleanup_filelock: 327 DESTROYLOCK(&cache->filelock); 328 cleanup_lock: 329 DESTROYLOCK(&cache->lock); 330 cleanup_mem: 331 if (cache->name != NULL) 332 isc_mem_free(cmctx, cache->name); 333 isc_mem_detach(&cache->hmctx); 334 isc_mem_putanddetach(&cache->mctx, cache, sizeof(*cache)); 335 return (result); 336} 337 338static void 339cache_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 DESTROYLOCK(&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 DESTROYLOCK(&cache->lock); 391 DESTROYLOCK(&cache->filelock); 392 393 cache->magic = 0; 394 isc_mem_detach(&cache->hmctx); 395 isc_mem_putanddetach(&cache->mctx, cache, sizeof(*cache)); 396} 397 398 399void 400dns_cache_attach(dns_cache_t *cache, dns_cache_t **targetp) { 401 402 REQUIRE(VALID_CACHE(cache)); 403 REQUIRE(targetp != NULL && *targetp == NULL); 404 405 LOCK(&cache->lock); 406 cache->references++; 407 UNLOCK(&cache->lock); 408 409 *targetp = cache; 410} 411 412void 413dns_cache_detach(dns_cache_t **cachep) { 414 dns_cache_t *cache; 415 isc_boolean_t free_cache = ISC_FALSE; 416 417 REQUIRE(cachep != NULL); 418 cache = *cachep; 419 REQUIRE(VALID_CACHE(cache)); 420 421 LOCK(&cache->lock); 422 REQUIRE(cache->references > 0); 423 cache->references--; 424 if (cache->references == 0) { 425 cache->cleaner.overmem = ISC_FALSE; 426 free_cache = ISC_TRUE; 427 } 428 429 *cachep = NULL; 430 431 if (free_cache) { 432 /* 433 * When the cache is shut down, dump it to a file if one is 434 * specified. 435 */ 436 isc_result_t result = dns_cache_dump(cache); 437 if (result != ISC_R_SUCCESS) 438 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 439 DNS_LOGMODULE_CACHE, ISC_LOG_WARNING, 440 "error dumping cache: %s ", 441 isc_result_totext(result)); 442 443 /* 444 * If the cleaner task exists, let it free the cache. 445 */ 446 if (cache->live_tasks > 0) { 447 isc_task_shutdown(cache->cleaner.task); 448 free_cache = ISC_FALSE; 449 } 450 } 451 452 UNLOCK(&cache->lock); 453 454 if (free_cache) 455 cache_free(cache); 456} 457 458void 459dns_cache_attachdb(dns_cache_t *cache, dns_db_t **dbp) { 460 REQUIRE(VALID_CACHE(cache)); 461 REQUIRE(dbp != NULL && *dbp == NULL); 462 REQUIRE(cache->db != NULL); 463 464 LOCK(&cache->lock); 465 dns_db_attach(cache->db, dbp); 466 UNLOCK(&cache->lock); 467 468} 469 470isc_result_t 471dns_cache_setfilename(dns_cache_t *cache, const char *filename) { 472 char *newname; 473 474 REQUIRE(VALID_CACHE(cache)); 475 REQUIRE(filename != NULL); 476 477 newname = isc_mem_strdup(cache->mctx, filename); 478 if (newname == NULL) 479 return (ISC_R_NOMEMORY); 480 481 LOCK(&cache->filelock); 482 if (cache->filename) 483 isc_mem_free(cache->mctx, cache->filename); 484 cache->filename = newname; 485 UNLOCK(&cache->filelock); 486 487 return (ISC_R_SUCCESS); 488} 489 490#ifdef BIND9 491isc_result_t 492dns_cache_load(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 LOCK(&cache->filelock); 501 result = dns_db_load(cache->db, cache->filename); 502 UNLOCK(&cache->filelock); 503 504 return (result); 505} 506#endif /* BIND9 */ 507 508isc_result_t 509dns_cache_dump(dns_cache_t *cache) { 510#ifdef BIND9 511 isc_result_t result; 512#endif 513 514 REQUIRE(VALID_CACHE(cache)); 515 516 if (cache->filename == NULL) 517 return (ISC_R_SUCCESS); 518 519#ifdef BIND9 520 LOCK(&cache->filelock); 521 result = dns_master_dump(cache->mctx, cache->db, NULL, 522 &dns_master_style_cache, cache->filename); 523 UNLOCK(&cache->filelock); 524 return (result); 525#else 526 return (ISC_R_NOTIMPLEMENTED); 527#endif 528 529} 530 531void 532dns_cache_setcleaninginterval(dns_cache_t *cache, unsigned int t) { 533 isc_interval_t interval; 534 isc_result_t result; 535 536 LOCK(&cache->lock); 537 538 /* 539 * It may be the case that the cache has already shut down. 540 * If so, it has no timer. 541 */ 542 if (cache->cleaner.cleaning_timer == NULL) 543 goto unlock; 544 545 cache->cleaner.cleaning_interval = t; 546 547 if (t == 0) { 548 result = isc_timer_reset(cache->cleaner.cleaning_timer, 549 isc_timertype_inactive, 550 NULL, NULL, ISC_TRUE); 551 } else { 552 isc_interval_set(&interval, cache->cleaner.cleaning_interval, 553 0); 554 result = isc_timer_reset(cache->cleaner.cleaning_timer, 555 isc_timertype_ticker, 556 NULL, &interval, ISC_FALSE); 557 } 558 if (result != ISC_R_SUCCESS) 559 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 560 DNS_LOGMODULE_CACHE, ISC_LOG_WARNING, 561 "could not set cache cleaning interval: %s", 562 isc_result_totext(result)); 563 564 unlock: 565 UNLOCK(&cache->lock); 566} 567 568unsigned int 569dns_cache_getcleaninginterval(dns_cache_t *cache) { 570 unsigned int t; 571 572 REQUIRE(VALID_CACHE(cache)); 573 574 LOCK(&cache->lock); 575 t = cache->cleaner.cleaning_interval; 576 UNLOCK(&cache->lock); 577 578 return (t); 579} 580 581const char * 582dns_cache_getname(dns_cache_t *cache) { 583 REQUIRE(VALID_CACHE(cache)); 584 585 return (cache->name); 586} 587 588/* 589 * Initialize the cache cleaner object at *cleaner. 590 * Space for the object must be allocated by the caller. 591 */ 592 593static isc_result_t 594cache_cleaner_init(dns_cache_t *cache, isc_taskmgr_t *taskmgr, 595 isc_timermgr_t *timermgr, cache_cleaner_t *cleaner) 596{ 597 isc_result_t result; 598 599 result = isc_mutex_init(&cleaner->lock); 600 if (result != ISC_R_SUCCESS) 601 goto fail; 602 603 cleaner->increment = DNS_CACHE_CLEANERINCREMENT; 604 cleaner->state = cleaner_s_idle; 605 cleaner->cache = cache; 606 cleaner->iterator = NULL; 607 cleaner->overmem = ISC_FALSE; 608 cleaner->replaceiterator = ISC_FALSE; 609 610 cleaner->task = NULL; 611 cleaner->cleaning_timer = NULL; 612 cleaner->resched_event = NULL; 613 cleaner->overmem_event = NULL; 614 cleaner->cleaning_interval = 0; /* Initially turned off. */ 615 616 result = dns_db_createiterator(cleaner->cache->db, ISC_FALSE, 617 &cleaner->iterator); 618 if (result != ISC_R_SUCCESS) 619 goto cleanup; 620 621 if (taskmgr != NULL && timermgr != NULL) { 622 result = isc_task_create(taskmgr, 1, &cleaner->task); 623 if (result != ISC_R_SUCCESS) { 624 UNEXPECTED_ERROR(__FILE__, __LINE__, 625 "isc_task_create() failed: %s", 626 dns_result_totext(result)); 627 result = ISC_R_UNEXPECTED; 628 goto cleanup; 629 } 630 cleaner->cache->live_tasks++; 631 isc_task_setname(cleaner->task, "cachecleaner", cleaner); 632 633 result = isc_task_onshutdown(cleaner->task, 634 cleaner_shutdown_action, cache); 635 if (result != ISC_R_SUCCESS) { 636 UNEXPECTED_ERROR(__FILE__, __LINE__, 637 "cache cleaner: " 638 "isc_task_onshutdown() failed: %s", 639 dns_result_totext(result)); 640 goto cleanup; 641 } 642 643 result = isc_timer_create(timermgr, isc_timertype_inactive, 644 NULL, NULL, cleaner->task, 645 cleaning_timer_action, cleaner, 646 &cleaner->cleaning_timer); 647 if (result != ISC_R_SUCCESS) { 648 UNEXPECTED_ERROR(__FILE__, __LINE__, 649 "isc_timer_create() failed: %s", 650 dns_result_totext(result)); 651 result = ISC_R_UNEXPECTED; 652 goto cleanup; 653 } 654 655 cleaner->resched_event = 656 isc_event_allocate(cache->mctx, cleaner, 657 DNS_EVENT_CACHECLEAN, 658 incremental_cleaning_action, 659 cleaner, sizeof(isc_event_t)); 660 if (cleaner->resched_event == NULL) { 661 result = ISC_R_NOMEMORY; 662 goto cleanup; 663 } 664 665 cleaner->overmem_event = 666 isc_event_allocate(cache->mctx, cleaner, 667 DNS_EVENT_CACHEOVERMEM, 668 overmem_cleaning_action, 669 cleaner, sizeof(isc_event_t)); 670 if (cleaner->overmem_event == NULL) { 671 result = ISC_R_NOMEMORY; 672 goto cleanup; 673 } 674 } 675 676 return (ISC_R_SUCCESS); 677 678 cleanup: 679 if (cleaner->overmem_event != NULL) 680 isc_event_free(&cleaner->overmem_event); 681 if (cleaner->resched_event != NULL) 682 isc_event_free(&cleaner->resched_event); 683 if (cleaner->cleaning_timer != NULL) 684 isc_timer_detach(&cleaner->cleaning_timer); 685 if (cleaner->task != NULL) 686 isc_task_detach(&cleaner->task); 687 if (cleaner->iterator != NULL) 688 dns_dbiterator_destroy(&cleaner->iterator); 689 DESTROYLOCK(&cleaner->lock); 690 fail: 691 return (result); 692} 693 694static void 695begin_cleaning(cache_cleaner_t *cleaner) { 696 isc_result_t result = ISC_R_SUCCESS; 697 698 REQUIRE(CLEANER_IDLE(cleaner)); 699 700 /* 701 * Create an iterator, if it does not already exist, and 702 * position it at the beginning of the cache. 703 */ 704 if (cleaner->iterator == NULL) 705 result = dns_db_createiterator(cleaner->cache->db, ISC_FALSE, 706 &cleaner->iterator); 707 if (result != ISC_R_SUCCESS) 708 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 709 DNS_LOGMODULE_CACHE, ISC_LOG_WARNING, 710 "cache cleaner could not create " 711 "iterator: %s", isc_result_totext(result)); 712 else { 713 dns_dbiterator_setcleanmode(cleaner->iterator, ISC_TRUE); 714 result = dns_dbiterator_first(cleaner->iterator); 715 } 716 if (result != ISC_R_SUCCESS) { 717 /* 718 * If the result is ISC_R_NOMORE, the database is empty, 719 * so there is nothing to be cleaned. 720 */ 721 if (result != ISC_R_NOMORE && cleaner->iterator != NULL) { 722 UNEXPECTED_ERROR(__FILE__, __LINE__, 723 "cache cleaner: " 724 "dns_dbiterator_first() failed: %s", 725 dns_result_totext(result)); 726 dns_dbiterator_destroy(&cleaner->iterator); 727 } else if (cleaner->iterator != NULL) { 728 result = dns_dbiterator_pause(cleaner->iterator); 729 RUNTIME_CHECK(result == ISC_R_SUCCESS); 730 } 731 } else { 732 /* 733 * Pause the iterator to free its lock. 734 */ 735 result = dns_dbiterator_pause(cleaner->iterator); 736 RUNTIME_CHECK(result == ISC_R_SUCCESS); 737 738 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 739 DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1), 740 "begin cache cleaning, mem inuse %lu", 741 (unsigned long)isc_mem_inuse(cleaner->cache->mctx)); 742 cleaner->state = cleaner_s_busy; 743 isc_task_send(cleaner->task, &cleaner->resched_event); 744 } 745 746 return; 747} 748 749static void 750end_cleaning(cache_cleaner_t *cleaner, isc_event_t *event) { 751 isc_result_t result; 752 753 REQUIRE(CLEANER_BUSY(cleaner)); 754 REQUIRE(event != NULL); 755 756 result = dns_dbiterator_pause(cleaner->iterator); 757 if (result != ISC_R_SUCCESS) 758 dns_dbiterator_destroy(&cleaner->iterator); 759 760 dns_cache_setcleaninginterval(cleaner->cache, 761 cleaner->cleaning_interval); 762 763 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE, 764 ISC_LOG_DEBUG(1), "end cache cleaning, mem inuse %lu", 765 (unsigned long)isc_mem_inuse(cleaner->cache->mctx)); 766 767 cleaner->state = cleaner_s_idle; 768 cleaner->resched_event = event; 769} 770 771/* 772 * This is run once for every cache-cleaning-interval as defined in named.conf. 773 */ 774static void 775cleaning_timer_action(isc_task_t *task, isc_event_t *event) { 776 cache_cleaner_t *cleaner = event->ev_arg; 777 778 UNUSED(task); 779 780 INSIST(task == cleaner->task); 781 INSIST(event->ev_type == ISC_TIMEREVENT_TICK); 782 783 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE, 784 ISC_LOG_DEBUG(1), "cache cleaning timer fired, " 785 "cleaner state = %d", cleaner->state); 786 787 if (cleaner->state == cleaner_s_idle) 788 begin_cleaning(cleaner); 789 790 isc_event_free(&event); 791} 792 793/* 794 * This is called when the cache either surpasses its upper limit 795 * or shrinks beyond its lower limit. 796 */ 797static void 798overmem_cleaning_action(isc_task_t *task, isc_event_t *event) { 799 cache_cleaner_t *cleaner = event->ev_arg; 800 isc_boolean_t want_cleaning = ISC_FALSE; 801 802 UNUSED(task); 803 804 INSIST(task == cleaner->task); 805 INSIST(event->ev_type == DNS_EVENT_CACHEOVERMEM); 806 INSIST(cleaner->overmem_event == NULL); 807 808 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE, 809 ISC_LOG_DEBUG(1), "overmem_cleaning_action called, " 810 "overmem = %d, state = %d", cleaner->overmem, 811 cleaner->state); 812 813 LOCK(&cleaner->lock); 814 815 if (cleaner->overmem) { 816 if (cleaner->state == cleaner_s_idle) 817 want_cleaning = ISC_TRUE; 818 } else { 819 if (cleaner->state == cleaner_s_busy) 820 /* 821 * end_cleaning() can't be called here because 822 * then both cleaner->overmem_event and 823 * cleaner->resched_event will point to this 824 * event. Set the state to done, and then 825 * when the incremental_cleaning_action() event 826 * is posted, it will handle the end_cleaning. 827 */ 828 cleaner->state = cleaner_s_done; 829 } 830 831 cleaner->overmem_event = event; 832 833 UNLOCK(&cleaner->lock); 834 835 if (want_cleaning) 836 begin_cleaning(cleaner); 837} 838 839/* 840 * Do incremental cleaning. 841 */ 842static void 843incremental_cleaning_action(isc_task_t *task, isc_event_t *event) { 844 cache_cleaner_t *cleaner = event->ev_arg; 845 isc_result_t result; 846 unsigned int n_names; 847 isc_time_t start; 848 849 UNUSED(task); 850 851 INSIST(task == cleaner->task); 852 INSIST(event->ev_type == DNS_EVENT_CACHECLEAN); 853 854 if (cleaner->state == cleaner_s_done) { 855 cleaner->state = cleaner_s_busy; 856 end_cleaning(cleaner, event); 857 LOCK(&cleaner->cache->lock); 858 LOCK(&cleaner->lock); 859 if (cleaner->replaceiterator) { 860 dns_dbiterator_destroy(&cleaner->iterator); 861 (void) dns_db_createiterator(cleaner->cache->db, 862 ISC_FALSE, 863 &cleaner->iterator); 864 cleaner->replaceiterator = ISC_FALSE; 865 } 866 UNLOCK(&cleaner->lock); 867 UNLOCK(&cleaner->cache->lock); 868 return; 869 } 870 871 INSIST(CLEANER_BUSY(cleaner)); 872 873 n_names = cleaner->increment; 874 875 REQUIRE(DNS_DBITERATOR_VALID(cleaner->iterator)); 876 877 isc_time_now(&start); 878 while (n_names-- > 0) { 879 dns_dbnode_t *node = NULL; 880 881 result = dns_dbiterator_current(cleaner->iterator, &node, 882 NULL); 883 if (result != ISC_R_SUCCESS) { 884 UNEXPECTED_ERROR(__FILE__, __LINE__, 885 "cache cleaner: dns_dbiterator_current() " 886 "failed: %s", dns_result_totext(result)); 887 888 end_cleaning(cleaner, event); 889 return; 890 } 891 892 /* 893 * The node was not needed, but was required by 894 * dns_dbiterator_current(). Give up its reference. 895 */ 896 dns_db_detachnode(cleaner->cache->db, &node); 897 898 /* 899 * Step to the next node. 900 */ 901 result = dns_dbiterator_next(cleaner->iterator); 902 903 if (result != ISC_R_SUCCESS) { 904 /* 905 * Either the end was reached (ISC_R_NOMORE) or 906 * some error was signaled. If the cache is still 907 * overmem and no error was encountered, 908 * keep trying to clean it, otherwise stop cleaning. 909 */ 910 if (result != ISC_R_NOMORE) 911 UNEXPECTED_ERROR(__FILE__, __LINE__, 912 "cache cleaner: " 913 "dns_dbiterator_next() " 914 "failed: %s", 915 dns_result_totext(result)); 916 else if (cleaner->overmem) { 917 result = dns_dbiterator_first(cleaner-> 918 iterator); 919 if (result == ISC_R_SUCCESS) { 920 isc_log_write(dns_lctx, 921 DNS_LOGCATEGORY_DATABASE, 922 DNS_LOGMODULE_CACHE, 923 ISC_LOG_DEBUG(1), 924 "cache cleaner: " 925 "still overmem, " 926 "reset and try again"); 927 continue; 928 } 929 } 930 931 end_cleaning(cleaner, event); 932 return; 933 } 934 } 935 936 /* 937 * We have successfully performed a cleaning increment but have 938 * not gone through the entire cache. Free the iterator locks 939 * and reschedule another batch. If it fails, just try to continue 940 * anyway. 941 */ 942 result = dns_dbiterator_pause(cleaner->iterator); 943 RUNTIME_CHECK(result == ISC_R_SUCCESS); 944 945 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE, 946 ISC_LOG_DEBUG(1), "cache cleaner: checked %u nodes, " 947 "mem inuse %lu, sleeping", cleaner->increment, 948 (unsigned long)isc_mem_inuse(cleaner->cache->mctx)); 949 950 isc_task_send(task, &event); 951 INSIST(CLEANER_BUSY(cleaner)); 952 return; 953} 954 955/* 956 * Do immediate cleaning. 957 */ 958isc_result_t 959dns_cache_clean(dns_cache_t *cache, isc_stdtime_t now) { 960 isc_result_t result; 961 dns_dbiterator_t *iterator = NULL; 962 963 REQUIRE(VALID_CACHE(cache)); 964 965 result = dns_db_createiterator(cache->db, 0, &iterator); 966 if (result != ISC_R_SUCCESS) 967 return result; 968 969 result = dns_dbiterator_first(iterator); 970 971 while (result == ISC_R_SUCCESS) { 972 dns_dbnode_t *node = NULL; 973 result = dns_dbiterator_current(iterator, &node, 974 (dns_name_t *)NULL); 975 if (result != ISC_R_SUCCESS) 976 break; 977 978 /* 979 * Check TTLs, mark expired rdatasets stale. 980 */ 981 result = dns_db_expirenode(cache->db, node, now); 982 if (result != ISC_R_SUCCESS) { 983 UNEXPECTED_ERROR(__FILE__, __LINE__, 984 "cache cleaner: dns_db_expirenode() " 985 "failed: %s", 986 dns_result_totext(result)); 987 /* 988 * Continue anyway. 989 */ 990 } 991 992 /* 993 * This is where the actual freeing takes place. 994 */ 995 dns_db_detachnode(cache->db, &node); 996 997 result = dns_dbiterator_next(iterator); 998 } 999 1000 dns_dbiterator_destroy(&iterator); 1001 1002 if (result == ISC_R_NOMORE) 1003 result = ISC_R_SUCCESS; 1004 1005 return (result); 1006} 1007 1008static void 1009water(void *arg, int mark) { 1010 dns_cache_t *cache = arg; 1011 isc_boolean_t overmem = ISC_TF(mark == ISC_MEM_HIWATER); 1012 1013 REQUIRE(VALID_CACHE(cache)); 1014 1015 LOCK(&cache->cleaner.lock); 1016 1017 if (overmem != cache->cleaner.overmem) { 1018 dns_db_overmem(cache->db, overmem); 1019 cache->cleaner.overmem = overmem; 1020 isc_mem_waterack(cache->mctx, mark); 1021 } 1022 1023 if (cache->cleaner.overmem_event != NULL) 1024 isc_task_send(cache->cleaner.task, 1025 &cache->cleaner.overmem_event); 1026 1027 UNLOCK(&cache->cleaner.lock); 1028} 1029 1030void 1031dns_cache_setcachesize(dns_cache_t *cache, isc_uint32_t size) { 1032 isc_uint32_t lowater; 1033 isc_uint32_t hiwater; 1034 1035 REQUIRE(VALID_CACHE(cache)); 1036 1037 /* 1038 * Impose a minimum cache size; pathological things happen if there 1039 * is too little room. 1040 */ 1041 if (size != 0 && size < DNS_CACHE_MINSIZE) 1042 size = DNS_CACHE_MINSIZE; 1043 1044 LOCK(&cache->lock); 1045 cache->size = size; 1046 UNLOCK(&cache->lock); 1047 1048 hiwater = size - (size >> 3); /* Approximately 7/8ths. */ 1049 lowater = size - (size >> 2); /* Approximately 3/4ths. */ 1050 1051 /* 1052 * If the cache was overmem and cleaning, but now with the new limits 1053 * it is no longer in an overmem condition, then the next 1054 * isc_mem_put for cache memory will do the right thing and trigger 1055 * water(). 1056 */ 1057 1058 if (size == 0 || hiwater == 0 || lowater == 0) 1059 /* 1060 * Disable cache memory limiting. 1061 */ 1062 isc_mem_setwater(cache->mctx, water, cache, 0, 0); 1063 else 1064 /* 1065 * Establish new cache memory limits (either for the first 1066 * time, or replacing other limits). 1067 */ 1068 isc_mem_setwater(cache->mctx, water, cache, hiwater, lowater); 1069} 1070 1071isc_uint32_t 1072dns_cache_getcachesize(dns_cache_t *cache) { 1073 isc_uint32_t size; 1074 1075 REQUIRE(VALID_CACHE(cache)); 1076 1077 LOCK(&cache->lock); 1078 size = cache->size; 1079 UNLOCK(&cache->lock); 1080 1081 return (size); 1082} 1083 1084/* 1085 * The cleaner task is shutting down; do the necessary cleanup. 1086 */ 1087static void 1088cleaner_shutdown_action(isc_task_t *task, isc_event_t *event) { 1089 dns_cache_t *cache = event->ev_arg; 1090 isc_boolean_t should_free = ISC_FALSE; 1091 1092 UNUSED(task); 1093 1094 INSIST(task == cache->cleaner.task); 1095 INSIST(event->ev_type == ISC_TASKEVENT_SHUTDOWN); 1096 1097 if (CLEANER_BUSY(&cache->cleaner)) 1098 end_cleaning(&cache->cleaner, event); 1099 else 1100 isc_event_free(&event); 1101 1102 LOCK(&cache->lock); 1103 1104 cache->live_tasks--; 1105 INSIST(cache->live_tasks == 0); 1106 1107 if (cache->references == 0) 1108 should_free = ISC_TRUE; 1109 1110 /* 1111 * By detaching the timer in the context of its task, 1112 * we are guaranteed that there will be no further timer 1113 * events. 1114 */ 1115 if (cache->cleaner.cleaning_timer != NULL) 1116 isc_timer_detach(&cache->cleaner.cleaning_timer); 1117 1118 /* Make sure we don't reschedule anymore. */ 1119 (void)isc_task_purge(task, NULL, DNS_EVENT_CACHECLEAN, NULL); 1120 1121 UNLOCK(&cache->lock); 1122 1123 if (should_free) 1124 cache_free(cache); 1125} 1126 1127isc_result_t 1128dns_cache_flush(dns_cache_t *cache) { 1129 dns_db_t *db = NULL; 1130 isc_result_t result; 1131 1132 result = cache_create_db(cache, &db); 1133 if (result != ISC_R_SUCCESS) 1134 return (result); 1135 1136 LOCK(&cache->lock); 1137 LOCK(&cache->cleaner.lock); 1138 if (cache->cleaner.state == cleaner_s_idle) { 1139 if (cache->cleaner.iterator != NULL) 1140 dns_dbiterator_destroy(&cache->cleaner.iterator); 1141 (void) dns_db_createiterator(db, ISC_FALSE, 1142 &cache->cleaner.iterator); 1143 } else { 1144 if (cache->cleaner.state == cleaner_s_busy) 1145 cache->cleaner.state = cleaner_s_done; 1146 cache->cleaner.replaceiterator = ISC_TRUE; 1147 } 1148 dns_db_detach(&cache->db); 1149 cache->db = db; 1150 UNLOCK(&cache->cleaner.lock); 1151 UNLOCK(&cache->lock); 1152 1153 return (ISC_R_SUCCESS); 1154} 1155 1156isc_result_t 1157dns_cache_flushname(dns_cache_t *cache, dns_name_t *name) { 1158 isc_result_t result; 1159 dns_rdatasetiter_t *iter = NULL; 1160 dns_dbnode_t *node = NULL; 1161 dns_db_t *db = NULL; 1162 1163 LOCK(&cache->lock); 1164 if (cache->db != NULL) 1165 dns_db_attach(cache->db, &db); 1166 UNLOCK(&cache->lock); 1167 if (db == NULL) 1168 return (ISC_R_SUCCESS); 1169 result = dns_db_findnode(cache->db, name, ISC_FALSE, &node); 1170 if (result == ISC_R_NOTFOUND) { 1171 result = ISC_R_SUCCESS; 1172 goto cleanup_db; 1173 } 1174 if (result != ISC_R_SUCCESS) 1175 goto cleanup_db; 1176 1177 result = dns_db_allrdatasets(cache->db, node, NULL, 1178 (isc_stdtime_t)0, &iter); 1179 if (result != ISC_R_SUCCESS) 1180 goto cleanup_node; 1181 1182 for (result = dns_rdatasetiter_first(iter); 1183 result == ISC_R_SUCCESS; 1184 result = dns_rdatasetiter_next(iter)) 1185 { 1186 dns_rdataset_t rdataset; 1187 dns_rdataset_init(&rdataset); 1188 1189 dns_rdatasetiter_current(iter, &rdataset); 1190 result = dns_db_deleterdataset(cache->db, node, NULL, 1191 rdataset.type, rdataset.covers); 1192 dns_rdataset_disassociate(&rdataset); 1193 if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED) 1194 break; 1195 } 1196 if (result == ISC_R_NOMORE) 1197 result = ISC_R_SUCCESS; 1198 1199 dns_rdatasetiter_destroy(&iter); 1200 1201 cleanup_node: 1202 dns_db_detachnode(cache->db, &node); 1203 1204 cleanup_db: 1205 dns_db_detach(&db); 1206 return (result); 1207} 1208