1 /* $NetBSD: acache.c,v 1.7 2015/07/08 17:28:58 christos Exp $ */ 2 3 /* 4 * Copyright (C) 2004-2008, 2012, 2013, 2015 Internet Systems Consortium, Inc. ("ISC") 5 * 6 * Permission to use, copy, modify, and/or distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 11 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 12 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 13 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 14 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 15 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 16 * PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* Id: acache.c,v 1.22 2008/02/07 23:46:54 tbox Exp */ 20 21 #include <config.h> 22 23 #include <isc/atomic.h> 24 #include <isc/event.h> 25 #include <isc/hash.h> 26 #include <isc/magic.h> 27 #include <isc/mem.h> 28 #include <isc/mutex.h> 29 #include <isc/random.h> 30 #include <isc/refcount.h> 31 #include <isc/rwlock.h> 32 #include <isc/serial.h> 33 #include <isc/task.h> 34 #include <isc/time.h> 35 #include <isc/timer.h> 36 37 #include <dns/acache.h> 38 #include <dns/db.h> 39 #include <dns/events.h> 40 #include <dns/log.h> 41 #include <dns/message.h> 42 #include <dns/name.h> 43 #include <dns/rdataset.h> 44 #include <dns/result.h> 45 #include <dns/zone.h> 46 47 #define ACACHE_MAGIC ISC_MAGIC('A', 'C', 'H', 'E') 48 #define DNS_ACACHE_VALID(acache) ISC_MAGIC_VALID(acache, ACACHE_MAGIC) 49 50 #define ACACHEENTRY_MAGIC ISC_MAGIC('A', 'C', 'E', 'T') 51 #define DNS_ACACHEENTRY_VALID(entry) ISC_MAGIC_VALID(entry, ACACHEENTRY_MAGIC) 52 53 #define DBBUCKETS 67 54 55 #if 0 56 #define ATRACE(m) isc_log_write(dns_lctx, \ 57 DNS_LOGCATEGORY_DATABASE, \ 58 DNS_LOGMODULE_ACACHE, \ 59 ISC_LOG_DEBUG(3), \ 60 "acache %p: %s", acache, (m)) 61 #define AATRACE(a,m) isc_log_write(dns_lctx, \ 62 DNS_LOGCATEGORY_DATABASE, \ 63 DNS_LOGMODULE_ACACHE, \ 64 ISC_LOG_DEBUG(3), \ 65 "acache %p: %s", (a), (m)) 66 #else 67 #define ATRACE(m) 68 #define AATRACE(a, m) 69 #endif 70 71 /* 72 * The following variables control incremental cleaning. 73 * MINSIZE is how many bytes is the floor for dns_acache_setcachesize(). 74 * CLEANERINCREMENT is how many entries are examined in one pass. 75 * (XXX simply derived from definitions in cache.c There may be better 76 * constants here.) 77 */ 78 #define DNS_ACACHE_MINSIZE 2097152U /* Bytes. 2097152 = 2 MB */ 79 #define DNS_ACACHE_CLEANERINCREMENT 1000 /* Number of entries. */ 80 81 #define DEFAULT_ACACHE_ENTRY_LOCK_COUNT 1009 /*%< Should be prime. */ 82 83 #if defined(ISC_RWLOCK_USEATOMIC) && defined(ISC_PLATFORM_HAVEATOMICSTORE) 84 #define ACACHE_USE_RWLOCK 1 85 #endif 86 87 #ifdef ACACHE_USE_RWLOCK 88 #define ACACHE_INITLOCK(l) isc_rwlock_init((l), 0, 0) 89 #define ACACHE_DESTROYLOCK(l) isc_rwlock_destroy(l) 90 #define ACACHE_LOCK(l, t) RWLOCK((l), (t)) 91 #define ACACHE_UNLOCK(l, t) RWUNLOCK((l), (t)) 92 93 #define acache_storetime(entry, t) \ 94 (isc_atomic_store((isc_int32_t *)&(entry)->lastused, (t))) 95 #else 96 #define ACACHE_INITLOCK(l) isc_mutex_init(l) 97 #define ACACHE_DESTROYLOCK(l) DESTROYLOCK(l) 98 #define ACACHE_LOCK(l, t) LOCK(l) 99 #define ACACHE_UNLOCK(l, t) UNLOCK(l) 100 101 #define acache_storetime(entry, t) ((entry)->lastused = (t)) 102 #endif 103 104 /* Locked by acache lock */ 105 typedef struct dbentry { 106 ISC_LINK(struct dbentry) link; 107 108 dns_db_t *db; 109 ISC_LIST(dns_acacheentry_t) originlist; 110 ISC_LIST(dns_acacheentry_t) referlist; 111 } dbentry_t; 112 113 typedef ISC_LIST(dbentry_t) dbentrylist_t; 114 115 typedef struct acache_cleaner acache_cleaner_t; 116 117 typedef enum { 118 cleaner_s_idle, /* Waiting for cleaning-interval to expire. */ 119 cleaner_s_busy, /* Currently cleaning. */ 120 cleaner_s_done /* Freed enough memory after being overmem. */ 121 } cleaner_state_t; 122 123 /* 124 * Convenience macros for comprehensive assertion checking. 125 */ 126 #define CLEANER_IDLE(c) ((c)->state == cleaner_s_idle && \ 127 (c)->resched_event != NULL) 128 #define CLEANER_BUSY(c) ((c)->state == cleaner_s_busy && \ 129 (c)->resched_event == NULL) 130 131 struct acache_cleaner { 132 isc_mutex_t lock; 133 /* 134 * Locks overmem_event, overmem. (See cache.c) 135 */ 136 137 dns_acache_t *acache; 138 unsigned int cleaning_interval; /* The cleaning-interval 139 from named.conf, 140 in seconds. */ 141 142 isc_stdtime_t last_cleanup_time; /* The time when the last 143 cleanup task completed */ 144 145 isc_timer_t *cleaning_timer; 146 isc_event_t *resched_event; /* Sent by cleaner task to 147 itself to reschedule */ 148 isc_event_t *overmem_event; 149 150 dns_acacheentry_t *current_entry; /* The bookmark entry to 151 restart the cleaning. 152 Locked by acache lock. */ 153 int increment; /* Number of entries to 154 clean in one increment */ 155 156 unsigned long ncleaned; /* Number of entries cleaned 157 up (for logging purposes) */ 158 cleaner_state_t state; /* Idle/Busy/Done. */ 159 isc_boolean_t overmem; /* The acache is in an overmem 160 state. */ 161 }; 162 163 struct dns_acachestats { 164 unsigned int hits; 165 unsigned int queries; 166 unsigned int misses; 167 unsigned int adds; 168 unsigned int deleted; 169 unsigned int cleaned; 170 unsigned int cleaner_runs; 171 unsigned int overmem; 172 unsigned int overmem_nocreates; 173 unsigned int nomem; 174 }; 175 176 /* 177 * The actual acache object. 178 */ 179 180 struct dns_acache { 181 unsigned int magic; 182 183 isc_mem_t *mctx; 184 isc_refcount_t refs; 185 186 #ifdef ACACHE_USE_RWLOCK 187 isc_rwlock_t *entrylocks; 188 #else 189 isc_mutex_t *entrylocks; 190 #endif 191 192 isc_mutex_t lock; 193 194 int live_cleaners; 195 acache_cleaner_t cleaner; 196 ISC_LIST(dns_acacheentry_t) entries; 197 unsigned int dbentries; 198 dbentrylist_t dbbucket[DBBUCKETS]; 199 200 isc_boolean_t shutting_down; 201 202 isc_task_t *task; 203 isc_event_t cevent; 204 isc_boolean_t cevent_sent; 205 206 dns_acachestats_t stats; 207 }; 208 209 struct dns_acacheentry { 210 unsigned int magic; 211 212 unsigned int locknum; 213 isc_refcount_t references; 214 215 dns_acache_t *acache; 216 217 /* Data for Management of cache entries */ 218 ISC_LINK(dns_acacheentry_t) link; 219 ISC_LINK(dns_acacheentry_t) olink; 220 ISC_LINK(dns_acacheentry_t) rlink; 221 222 dns_db_t *origdb; /* reference to the DB 223 holding this entry */ 224 225 /* Cache data */ 226 dns_zone_t *zone; /* zone this entry 227 belongs to */ 228 dns_db_t *db; /* DB this entry belongs to */ 229 dns_dbversion_t *version; /* the version of the DB */ 230 dns_dbnode_t *node; /* node this entry 231 belongs to */ 232 dns_name_t *foundname; /* corresponding DNS name 233 and rdataset */ 234 235 /* Callback function and its argument */ 236 void (*callback)(dns_acacheentry_t *, void **); 237 void *cbarg; 238 239 /* Timestamp of the last time this entry is referred to */ 240 isc_stdtime32_t lastused; 241 }; 242 243 /* 244 * Internal functions (and prototypes). 245 */ 246 static inline isc_boolean_t check_noentry(dns_acache_t *acache); 247 static void destroy(dns_acache_t *acache); 248 static void shutdown_entries(dns_acache_t *acache); 249 static void shutdown_buckets(dns_acache_t *acache); 250 static void destroy_entry(dns_acacheentry_t *ent); 251 static inline void unlink_dbentries(dns_acache_t *acache, 252 dns_acacheentry_t *ent); 253 static inline isc_result_t finddbent(dns_acache_t *acache, 254 dns_db_t *db, dbentry_t **dbentryp); 255 static inline void clear_entry(dns_acache_t *acache, dns_acacheentry_t *entry); 256 static isc_result_t acache_cleaner_init(dns_acache_t *acache, 257 isc_timermgr_t *timermgr, 258 acache_cleaner_t *cleaner); 259 static void acache_cleaning_timer_action(isc_task_t *task, isc_event_t *event); 260 static void acache_incremental_cleaning_action(isc_task_t *task, 261 isc_event_t *event); 262 static void acache_overmem_cleaning_action(isc_task_t *task, 263 isc_event_t *event); 264 static void acache_cleaner_shutdown_action(isc_task_t *task, 265 isc_event_t *event); 266 267 /* 268 * acache should be locked. If it is not, the stats can get out of whack, 269 * which is not a big deal for us since this is for debugging / stats 270 */ 271 static void 272 reset_stats(dns_acache_t *acache) { 273 acache->stats.hits = 0; 274 acache->stats.queries = 0; 275 acache->stats.misses = 0; 276 acache->stats.adds = 0; 277 acache->stats.deleted = 0; 278 acache->stats.cleaned = 0; 279 acache->stats.overmem = 0; 280 acache->stats.overmem_nocreates = 0; 281 acache->stats.nomem = 0; 282 } 283 284 /* 285 * The acache must be locked before calling. 286 */ 287 static inline isc_boolean_t 288 check_noentry(dns_acache_t *acache) { 289 if (ISC_LIST_EMPTY(acache->entries) && acache->dbentries == 0) { 290 return (ISC_TRUE); 291 } 292 293 return (ISC_FALSE); 294 } 295 296 /* 297 * The acache must be locked before calling. 298 */ 299 static void 300 shutdown_entries(dns_acache_t *acache) { 301 dns_acacheentry_t *entry, *entry_next; 302 303 REQUIRE(DNS_ACACHE_VALID(acache)); 304 INSIST(acache->shutting_down); 305 306 /* 307 * Release the dependency of all entries, and detach them. 308 */ 309 for (entry = ISC_LIST_HEAD(acache->entries); 310 entry != NULL; 311 entry = entry_next) { 312 entry_next = ISC_LIST_NEXT(entry, link); 313 314 ACACHE_LOCK(&acache->entrylocks[entry->locknum], 315 isc_rwlocktype_write); 316 317 /* 318 * If the cleaner holds this entry, it will be unlinked and 319 * freed in the cleaner later. 320 */ 321 if (acache->cleaner.current_entry != entry) 322 ISC_LIST_UNLINK(acache->entries, entry, link); 323 unlink_dbentries(acache, entry); 324 if (entry->callback != NULL) { 325 (entry->callback)(entry, &entry->cbarg); 326 entry->callback = NULL; 327 } 328 329 ACACHE_UNLOCK(&acache->entrylocks[entry->locknum], 330 isc_rwlocktype_write); 331 332 if (acache->cleaner.current_entry != entry) 333 dns_acache_detachentry(&entry); 334 } 335 } 336 337 /* 338 * The acache must be locked before calling. 339 */ 340 static void 341 shutdown_buckets(dns_acache_t *acache) { 342 int i; 343 dbentry_t *dbent; 344 345 REQUIRE(DNS_ACACHE_VALID(acache)); 346 INSIST(acache->shutting_down); 347 348 for (i = 0; i < DBBUCKETS; i++) { 349 while ((dbent = ISC_LIST_HEAD(acache->dbbucket[i])) != NULL) { 350 INSIST(ISC_LIST_EMPTY(dbent->originlist) && 351 ISC_LIST_EMPTY(dbent->referlist)); 352 ISC_LIST_UNLINK(acache->dbbucket[i], dbent, link); 353 354 dns_db_detach(&dbent->db); 355 356 isc_mem_put(acache->mctx, dbent, sizeof(*dbent)); 357 358 acache->dbentries--; 359 } 360 } 361 362 INSIST(acache->dbentries == 0); 363 } 364 365 static void 366 shutdown_task(isc_task_t *task, isc_event_t *ev) { 367 dns_acache_t *acache; 368 369 UNUSED(task); 370 371 acache = ev->ev_arg; 372 INSIST(DNS_ACACHE_VALID(acache)); 373 374 isc_event_free(&ev); 375 376 LOCK(&acache->lock); 377 378 shutdown_entries(acache); 379 shutdown_buckets(acache); 380 381 UNLOCK(&acache->lock); 382 383 dns_acache_detach(&acache); 384 } 385 386 /* The acache and the entry must be locked before calling. */ 387 static inline void 388 unlink_dbentries(dns_acache_t *acache, dns_acacheentry_t *ent) { 389 isc_result_t result; 390 dbentry_t *dbent; 391 392 if (ISC_LINK_LINKED(ent, olink)) { 393 INSIST(ent->origdb != NULL); 394 dbent = NULL; 395 result = finddbent(acache, ent->origdb, &dbent); 396 INSIST(result == ISC_R_SUCCESS); 397 398 ISC_LIST_UNLINK(dbent->originlist, ent, olink); 399 } 400 if (ISC_LINK_LINKED(ent, rlink)) { 401 INSIST(ent->db != NULL); 402 dbent = NULL; 403 result = finddbent(acache, ent->db, &dbent); 404 INSIST(result == ISC_R_SUCCESS); 405 406 ISC_LIST_UNLINK(dbent->referlist, ent, rlink); 407 } 408 } 409 410 /* There must not be a reference to this entry. */ 411 static void 412 destroy_entry(dns_acacheentry_t *entry) { 413 dns_acache_t *acache; 414 415 REQUIRE(DNS_ACACHEENTRY_VALID(entry)); 416 417 acache = entry->acache; 418 REQUIRE(DNS_ACACHE_VALID(acache)); 419 420 /* 421 * Since there is no reference to this entry, it is safe to call 422 * clear_entry() here. 423 */ 424 clear_entry(acache, entry); 425 426 isc_mem_put(acache->mctx, entry, sizeof(*entry)); 427 428 dns_acache_detach(&acache); 429 } 430 431 static void 432 destroy(dns_acache_t *acache) { 433 int i; 434 435 REQUIRE(DNS_ACACHE_VALID(acache)); 436 437 ATRACE("destroy"); 438 439 isc_mem_setwater(acache->mctx, NULL, NULL, 0, 0); 440 441 if (acache->cleaner.overmem_event != NULL) 442 isc_event_free(&acache->cleaner.overmem_event); 443 444 if (acache->cleaner.resched_event != NULL) 445 isc_event_free(&acache->cleaner.resched_event); 446 447 if (acache->task != NULL) 448 isc_task_detach(&acache->task); 449 450 for (i = 0; i < DEFAULT_ACACHE_ENTRY_LOCK_COUNT; i++) 451 ACACHE_DESTROYLOCK(&acache->entrylocks[i]); 452 isc_mem_put(acache->mctx, acache->entrylocks, 453 sizeof(*acache->entrylocks) * 454 DEFAULT_ACACHE_ENTRY_LOCK_COUNT); 455 456 DESTROYLOCK(&acache->cleaner.lock); 457 458 DESTROYLOCK(&acache->lock); 459 acache->magic = 0; 460 461 isc_mem_putanddetach(&acache->mctx, acache, sizeof(*acache)); 462 } 463 464 static inline isc_result_t 465 finddbent(dns_acache_t *acache, dns_db_t *db, dbentry_t **dbentryp) { 466 int bucket; 467 dbentry_t *dbentry; 468 469 REQUIRE(DNS_ACACHE_VALID(acache)); 470 REQUIRE(db != NULL); 471 REQUIRE(dbentryp != NULL && *dbentryp == NULL); 472 473 /* 474 * The caller must be holding the acache lock. 475 */ 476 477 bucket = isc_hash_calc((const unsigned char *)&db, 478 sizeof(db), ISC_TRUE) % DBBUCKETS; 479 480 for (dbentry = ISC_LIST_HEAD(acache->dbbucket[bucket]); 481 dbentry != NULL; 482 dbentry = ISC_LIST_NEXT(dbentry, link)) { 483 if (dbentry->db == db) 484 break; 485 } 486 487 *dbentryp = dbentry; 488 489 if (dbentry == NULL) 490 return (ISC_R_NOTFOUND); 491 else 492 return (ISC_R_SUCCESS); 493 } 494 495 static inline void 496 clear_entry(dns_acache_t *acache, dns_acacheentry_t *entry) { 497 REQUIRE(DNS_ACACHE_VALID(acache)); 498 REQUIRE(DNS_ACACHEENTRY_VALID(entry)); 499 500 /* 501 * The caller must be holing the entry lock. 502 */ 503 504 if (entry->foundname) { 505 dns_rdataset_t *rdataset, *rdataset_next; 506 507 for (rdataset = ISC_LIST_HEAD(entry->foundname->list); 508 rdataset != NULL; 509 rdataset = rdataset_next) { 510 rdataset_next = ISC_LIST_NEXT(rdataset, link); 511 ISC_LIST_UNLINK(entry->foundname->list, 512 rdataset, link); 513 dns_rdataset_disassociate(rdataset); 514 isc_mem_put(acache->mctx, rdataset, sizeof(*rdataset)); 515 } 516 if (dns_name_dynamic(entry->foundname)) 517 dns_name_free(entry->foundname, acache->mctx); 518 isc_mem_put(acache->mctx, entry->foundname, 519 sizeof(*entry->foundname)); 520 entry->foundname = NULL; 521 } 522 523 if (entry->node != NULL) { 524 INSIST(entry->db != NULL); 525 dns_db_detachnode(entry->db, &entry->node); 526 } 527 if (entry->version != NULL) { 528 INSIST(entry->db != NULL); 529 dns_db_closeversion(entry->db, &entry->version, ISC_FALSE); 530 } 531 if (entry->db != NULL) 532 dns_db_detach(&entry->db); 533 if (entry->zone != NULL) 534 dns_zone_detach(&entry->zone); 535 536 if (entry->origdb != NULL) 537 dns_db_detach(&entry->origdb); 538 } 539 540 static isc_result_t 541 acache_cleaner_init(dns_acache_t *acache, isc_timermgr_t *timermgr, 542 acache_cleaner_t *cleaner) 543 { 544 int result; 545 546 ATRACE("acache cleaner init"); 547 548 result = isc_mutex_init(&cleaner->lock); 549 if (result != ISC_R_SUCCESS) 550 goto fail; 551 552 cleaner->increment = DNS_ACACHE_CLEANERINCREMENT; 553 cleaner->state = cleaner_s_idle; 554 cleaner->acache = acache; 555 cleaner->overmem = ISC_FALSE; 556 557 cleaner->cleaning_timer = NULL; 558 cleaner->resched_event = NULL; 559 cleaner->overmem_event = NULL; 560 cleaner->current_entry = NULL; 561 562 if (timermgr != NULL) { 563 cleaner->acache->live_cleaners++; 564 565 result = isc_task_onshutdown(acache->task, 566 acache_cleaner_shutdown_action, 567 acache); 568 if (result != ISC_R_SUCCESS) { 569 UNEXPECTED_ERROR(__FILE__, __LINE__, 570 "acache cleaner: " 571 "isc_task_onshutdown() failed: %s", 572 dns_result_totext(result)); 573 goto cleanup; 574 } 575 576 cleaner->cleaning_interval = 0; /* Initially turned off. */ 577 isc_stdtime_get(&cleaner->last_cleanup_time); 578 result = isc_timer_create(timermgr, isc_timertype_inactive, 579 NULL, NULL, 580 acache->task, 581 acache_cleaning_timer_action, 582 cleaner, &cleaner->cleaning_timer); 583 if (result != ISC_R_SUCCESS) { 584 UNEXPECTED_ERROR(__FILE__, __LINE__, 585 "isc_timer_create() failed: %s", 586 dns_result_totext(result)); 587 result = ISC_R_UNEXPECTED; 588 goto cleanup; 589 } 590 591 cleaner->resched_event = 592 isc_event_allocate(acache->mctx, cleaner, 593 DNS_EVENT_ACACHECLEAN, 594 acache_incremental_cleaning_action, 595 cleaner, sizeof(isc_event_t)); 596 if (cleaner->resched_event == NULL) { 597 result = ISC_R_NOMEMORY; 598 goto cleanup; 599 } 600 601 cleaner->overmem_event = 602 isc_event_allocate(acache->mctx, cleaner, 603 DNS_EVENT_ACACHEOVERMEM, 604 acache_overmem_cleaning_action, 605 cleaner, sizeof(isc_event_t)); 606 if (cleaner->overmem_event == NULL) { 607 result = ISC_R_NOMEMORY; 608 goto cleanup; 609 } 610 } 611 612 return (ISC_R_SUCCESS); 613 614 cleanup: 615 if (cleaner->overmem_event != NULL) 616 isc_event_free(&cleaner->overmem_event); 617 if (cleaner->resched_event != NULL) 618 isc_event_free(&cleaner->resched_event); 619 if (cleaner->cleaning_timer != NULL) 620 isc_timer_detach(&cleaner->cleaning_timer); 621 cleaner->acache->live_cleaners--; 622 DESTROYLOCK(&cleaner->lock); 623 fail: 624 return (result); 625 } 626 627 static void 628 begin_cleaning(acache_cleaner_t *cleaner) { 629 dns_acacheentry_t *head; 630 dns_acache_t *acache = cleaner->acache; 631 632 /* 633 * This function does not have to lock the cleaner, since critical 634 * parameters (except current_entry, which is locked by acache lock,) 635 * are only used in a single task context. 636 */ 637 638 REQUIRE(CLEANER_IDLE(cleaner)); 639 INSIST(DNS_ACACHE_VALID(acache)); 640 INSIST(cleaner->current_entry == NULL); 641 642 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 643 DNS_LOGMODULE_ACACHE, ISC_LOG_DEBUG(1), 644 "begin acache cleaning, mem inuse %lu", 645 (unsigned long)isc_mem_inuse(cleaner->acache->mctx)); 646 647 LOCK(&acache->lock); 648 649 head = ISC_LIST_HEAD(acache->entries); 650 if (head != NULL) 651 dns_acache_attachentry(head, &cleaner->current_entry); 652 653 UNLOCK(&acache->lock); 654 655 if (cleaner->current_entry != NULL) { 656 cleaner->ncleaned = 0; 657 cleaner->state = cleaner_s_busy; 658 isc_task_send(acache->task, &cleaner->resched_event); 659 } 660 661 return; 662 } 663 664 static void 665 end_cleaning(acache_cleaner_t *cleaner, isc_event_t *event) { 666 dns_acache_t *acache = cleaner->acache; 667 668 REQUIRE(CLEANER_BUSY(cleaner)); 669 REQUIRE(event != NULL); 670 REQUIRE(DNS_ACACHEENTRY_VALID(cleaner->current_entry)); 671 672 /* No need to lock the cleaner (see begin_cleaning()). */ 673 674 LOCK(&acache->lock); 675 676 /* 677 * Even if the cleaner has the last reference to the entry, which means 678 * the entry has been unused, it may still be linked if unlinking the 679 * entry has been delayed due to the reference. 680 */ 681 if (isc_refcount_current(&cleaner->current_entry->references) == 1) { 682 INSIST(cleaner->current_entry->callback == NULL); 683 684 if (ISC_LINK_LINKED(cleaner->current_entry, link)) { 685 ISC_LIST_UNLINK(acache->entries, 686 cleaner->current_entry, link); 687 } 688 } 689 dns_acache_detachentry(&cleaner->current_entry); 690 691 if (cleaner->overmem) 692 acache->stats.overmem++; 693 acache->stats.cleaned += cleaner->ncleaned; 694 acache->stats.cleaner_runs++; 695 696 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE, 697 ISC_LOG_NOTICE, 698 "acache %p stats: hits=%d misses=%d queries=%d " 699 "adds=%d deleted=%d " 700 "cleaned=%d cleaner_runs=%d overmem=%d " 701 "overmem_nocreates=%d nomem=%d", 702 acache, 703 acache->stats.hits, acache->stats.misses, 704 acache->stats.queries, 705 acache->stats.adds, acache->stats.deleted, 706 acache->stats.cleaned, acache->stats.cleaner_runs, 707 acache->stats.overmem, acache->stats.overmem_nocreates, 708 acache->stats.nomem); 709 reset_stats(acache); 710 711 isc_stdtime_get(&cleaner->last_cleanup_time); 712 713 UNLOCK(&acache->lock); 714 715 dns_acache_setcleaninginterval(cleaner->acache, 716 cleaner->cleaning_interval); 717 718 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE, 719 ISC_LOG_DEBUG(1), "end acache cleaning, " 720 "%lu entries cleaned, mem inuse %lu", 721 cleaner->ncleaned, 722 (unsigned long)isc_mem_inuse(cleaner->acache->mctx)); 723 724 if (cleaner->overmem) { 725 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 726 DNS_LOGMODULE_ACACHE, ISC_LOG_NOTICE, 727 "acache is still in overmem state " 728 "after cleaning"); 729 } 730 731 cleaner->ncleaned = 0; 732 cleaner->state = cleaner_s_idle; 733 cleaner->resched_event = event; 734 } 735 736 /* 737 * This is run once for every acache-cleaning-interval as defined 738 * in named.conf. 739 */ 740 static void 741 acache_cleaning_timer_action(isc_task_t *task, isc_event_t *event) { 742 acache_cleaner_t *cleaner = event->ev_arg; 743 744 UNUSED(task); 745 746 INSIST(event->ev_type == ISC_TIMEREVENT_TICK); 747 748 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE, 749 ISC_LOG_DEBUG(1), "acache cleaning timer fired, " 750 "cleaner state = %d", cleaner->state); 751 752 if (cleaner->state == cleaner_s_idle) 753 begin_cleaning(cleaner); 754 755 isc_event_free(&event); 756 } 757 758 /* The caller must hold entry lock. */ 759 static inline isc_boolean_t 760 entry_stale(acache_cleaner_t *cleaner, dns_acacheentry_t *entry, 761 isc_stdtime32_t now32, unsigned int interval) 762 { 763 /* 764 * If the callback has been canceled, we definitely do not need the 765 * entry. 766 */ 767 if (entry->callback == NULL) 768 return (ISC_TRUE); 769 770 if (interval > cleaner->cleaning_interval) 771 interval = cleaner->cleaning_interval; 772 773 if (entry->lastused + interval < now32) 774 return (ISC_TRUE); 775 776 /* 777 * If the acache is in the overmem state, probabilistically decide if 778 * the entry should be purged, based on the time passed from its last 779 * use and the cleaning interval. 780 */ 781 if (cleaner->overmem) { 782 unsigned int passed; 783 isc_uint32_t val; 784 785 if (isc_serial_ge(now32, entry->lastused)) 786 passed = now32 - entry->lastused; /* <= interval */ 787 else 788 passed = 0; 789 790 if (passed > interval / 2) 791 return (ISC_TRUE); 792 isc_random_get(&val); 793 if (passed > interval / 4) 794 return (ISC_TF(val % 4 == 0)); 795 return (ISC_TF(val % 8 == 0)); 796 } 797 798 return (ISC_FALSE); 799 } 800 801 /* 802 * Do incremental cleaning. 803 */ 804 static void 805 acache_incremental_cleaning_action(isc_task_t *task, isc_event_t *event) { 806 acache_cleaner_t *cleaner = event->ev_arg; 807 dns_acache_t *acache = cleaner->acache; 808 dns_acacheentry_t *entry, *next = NULL; 809 int n_entries; 810 isc_stdtime32_t now32, last32; 811 isc_stdtime_t now; 812 unsigned int interval; 813 814 INSIST(DNS_ACACHE_VALID(acache)); 815 INSIST(task == acache->task); 816 INSIST(event->ev_type == DNS_EVENT_ACACHECLEAN); 817 818 if (cleaner->state == cleaner_s_done) { 819 cleaner->state = cleaner_s_busy; 820 end_cleaning(cleaner, event); 821 return; 822 } 823 824 INSIST(CLEANER_BUSY(cleaner)); 825 826 n_entries = cleaner->increment; 827 828 isc_stdtime_get(&now); 829 isc_stdtime_convert32(now, &now32); 830 831 LOCK(&acache->lock); 832 833 entry = cleaner->current_entry; 834 isc_stdtime_convert32(cleaner->last_cleanup_time, &last32); 835 if (isc_serial_ge(now32, last32)) 836 interval = now32 - last32; 837 else 838 interval = 0; 839 840 while (n_entries-- > 0) { 841 isc_boolean_t is_stale = ISC_FALSE; 842 843 INSIST(entry != NULL); 844 845 next = ISC_LIST_NEXT(entry, link); 846 847 ACACHE_LOCK(&acache->entrylocks[entry->locknum], 848 isc_rwlocktype_write); 849 850 is_stale = entry_stale(cleaner, entry, now32, interval); 851 if (is_stale) { 852 ISC_LIST_UNLINK(acache->entries, entry, link); 853 unlink_dbentries(acache, entry); 854 if (entry->callback != NULL) 855 (entry->callback)(entry, &entry->cbarg); 856 entry->callback = NULL; 857 858 cleaner->ncleaned++; 859 } 860 861 ACACHE_UNLOCK(&acache->entrylocks[entry->locknum], 862 isc_rwlocktype_write); 863 864 if (is_stale) 865 dns_acache_detachentry(&entry); 866 867 if (next == NULL) { 868 if (cleaner->overmem) { 869 entry = ISC_LIST_HEAD(acache->entries); 870 if (entry != NULL) { 871 /* 872 * If we are still in the overmem 873 * state, keep cleaning. In case we 874 * exit from the loop immediately after 875 * this, reset next to the head entry 876 * as we'll expect it will be never 877 * NULL. 878 */ 879 isc_log_write(dns_lctx, 880 DNS_LOGCATEGORY_DATABASE, 881 DNS_LOGMODULE_ACACHE, 882 ISC_LOG_DEBUG(1), 883 "acache cleaner: " 884 "still overmem, " 885 "reset and try again"); 886 next = entry; 887 continue; 888 } 889 } 890 891 UNLOCK(&acache->lock); 892 end_cleaning(cleaner, event); 893 return; 894 } 895 896 entry = next; 897 } 898 899 /* 900 * We have successfully performed a cleaning increment but have 901 * not gone through the entire cache. Remember the entry that will 902 * be the starting point in the next clean-up, and reschedule another 903 * batch. If it fails, just try to continue anyway. 904 */ 905 INSIST(next != NULL); 906 dns_acache_detachentry(&cleaner->current_entry); 907 dns_acache_attachentry(next, &cleaner->current_entry); 908 909 UNLOCK(&acache->lock); 910 911 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE, 912 ISC_LOG_DEBUG(1), "acache cleaner: checked %d entries, " 913 "mem inuse %lu, sleeping", cleaner->increment, 914 (unsigned long)isc_mem_inuse(cleaner->acache->mctx)); 915 916 isc_task_send(task, &event); 917 INSIST(CLEANER_BUSY(cleaner)); 918 919 return; 920 } 921 922 /* 923 * This is called when the acache either surpasses its upper limit 924 * or shrinks beyond its lower limit. 925 */ 926 static void 927 acache_overmem_cleaning_action(isc_task_t *task, isc_event_t *event) { 928 acache_cleaner_t *cleaner = event->ev_arg; 929 isc_boolean_t want_cleaning = ISC_FALSE; 930 931 UNUSED(task); 932 933 INSIST(event->ev_type == DNS_EVENT_ACACHEOVERMEM); 934 INSIST(cleaner->overmem_event == NULL); 935 936 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE, 937 ISC_LOG_DEBUG(1), "overmem_cleaning_action called, " 938 "overmem = %d, state = %d", cleaner->overmem, 939 cleaner->state); 940 941 LOCK(&cleaner->lock); 942 943 if (cleaner->overmem) { 944 if (cleaner->state == cleaner_s_idle) 945 want_cleaning = ISC_TRUE; 946 } else { 947 if (cleaner->state == cleaner_s_busy) 948 /* 949 * end_cleaning() can't be called here because 950 * then both cleaner->overmem_event and 951 * cleaner->resched_event will point to this 952 * event. Set the state to done, and then 953 * when the acache_incremental_cleaning_action() event 954 * is posted, it will handle the end_cleaning. 955 */ 956 cleaner->state = cleaner_s_done; 957 } 958 959 cleaner->overmem_event = event; 960 961 UNLOCK(&cleaner->lock); 962 963 if (want_cleaning) 964 begin_cleaning(cleaner); 965 } 966 967 static void 968 water(void *arg, int mark) { 969 dns_acache_t *acache = arg; 970 isc_boolean_t overmem = ISC_TF(mark == ISC_MEM_HIWATER); 971 972 REQUIRE(DNS_ACACHE_VALID(acache)); 973 974 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 975 DNS_LOGMODULE_ACACHE, ISC_LOG_DEBUG(1), 976 "acache memory reaches %s watermark, mem inuse %lu", 977 overmem ? "high" : "low", 978 (unsigned long)isc_mem_inuse(acache->mctx)); 979 980 LOCK(&acache->cleaner.lock); 981 982 if (acache->cleaner.overmem != overmem) { 983 acache->cleaner.overmem = overmem; 984 985 if (acache->cleaner.overmem_event != NULL) 986 isc_task_send(acache->task, 987 &acache->cleaner.overmem_event); 988 isc_mem_waterack(acache->mctx, mark); 989 } 990 991 UNLOCK(&acache->cleaner.lock); 992 } 993 994 /* 995 * The cleaner task is shutting down; do the necessary cleanup. 996 */ 997 static void 998 acache_cleaner_shutdown_action(isc_task_t *task, isc_event_t *event) { 999 dns_acache_t *acache = event->ev_arg; 1000 isc_boolean_t should_free = ISC_FALSE; 1001 1002 INSIST(task == acache->task); 1003 INSIST(event->ev_type == ISC_TASKEVENT_SHUTDOWN); 1004 INSIST(DNS_ACACHE_VALID(acache)); 1005 1006 ATRACE("acache cleaner shutdown"); 1007 1008 if (CLEANER_BUSY(&acache->cleaner)) 1009 end_cleaning(&acache->cleaner, event); 1010 else 1011 isc_event_free(&event); 1012 1013 LOCK(&acache->lock); 1014 1015 acache->live_cleaners--; 1016 INSIST(acache->live_cleaners == 0); 1017 1018 if (isc_refcount_current(&acache->refs) == 0) { 1019 INSIST(check_noentry(acache) == ISC_TRUE); 1020 should_free = ISC_TRUE; 1021 } 1022 1023 /* 1024 * By detaching the timer in the context of its task, 1025 * we are guaranteed that there will be no further timer 1026 * events. 1027 */ 1028 if (acache->cleaner.cleaning_timer != NULL) 1029 isc_timer_detach(&acache->cleaner.cleaning_timer); 1030 1031 /* Make sure we don't reschedule anymore. */ 1032 (void)isc_task_purge(task, NULL, DNS_EVENT_ACACHECLEAN, NULL); 1033 1034 UNLOCK(&acache->lock); 1035 1036 if (should_free) 1037 destroy(acache); 1038 } 1039 1040 /* 1041 * Public functions. 1042 */ 1043 1044 isc_result_t 1045 dns_acache_create(dns_acache_t **acachep, isc_mem_t *mctx, 1046 isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr) 1047 { 1048 int i; 1049 isc_result_t result; 1050 dns_acache_t *acache; 1051 1052 REQUIRE(acachep != NULL && *acachep == NULL); 1053 REQUIRE(mctx != NULL); 1054 REQUIRE(taskmgr != NULL); 1055 1056 acache = isc_mem_get(mctx, sizeof(*acache)); 1057 if (acache == NULL) 1058 return (ISC_R_NOMEMORY); 1059 1060 ATRACE("create"); 1061 1062 result = isc_refcount_init(&acache->refs, 1); 1063 if (result != ISC_R_SUCCESS) { 1064 isc_mem_put(mctx, acache, sizeof(*acache)); 1065 return (result); 1066 } 1067 1068 result = isc_mutex_init(&acache->lock); 1069 if (result != ISC_R_SUCCESS) { 1070 isc_refcount_decrement(&acache->refs, NULL); 1071 isc_refcount_destroy(&acache->refs); 1072 isc_mem_put(mctx, acache, sizeof(*acache)); 1073 return (result); 1074 } 1075 1076 acache->mctx = NULL; 1077 isc_mem_attach(mctx, &acache->mctx); 1078 ISC_LIST_INIT(acache->entries); 1079 1080 acache->shutting_down = ISC_FALSE; 1081 1082 acache->task = NULL; 1083 acache->entrylocks = NULL; 1084 1085 result = isc_task_create(taskmgr, 1, &acache->task); 1086 if (result != ISC_R_SUCCESS) { 1087 UNEXPECTED_ERROR(__FILE__, __LINE__, 1088 "isc_task_create() failed(): %s", 1089 dns_result_totext(result)); 1090 result = ISC_R_UNEXPECTED; 1091 goto cleanup; 1092 } 1093 isc_task_setname(acache->task, "acachetask", acache); 1094 ISC_EVENT_INIT(&acache->cevent, sizeof(acache->cevent), 0, NULL, 1095 DNS_EVENT_ACACHECONTROL, shutdown_task, NULL, 1096 NULL, NULL, NULL); 1097 acache->cevent_sent = ISC_FALSE; 1098 1099 acache->dbentries = 0; 1100 for (i = 0; i < DBBUCKETS; i++) 1101 ISC_LIST_INIT(acache->dbbucket[i]); 1102 1103 acache->entrylocks = isc_mem_get(mctx, sizeof(*acache->entrylocks) * 1104 DEFAULT_ACACHE_ENTRY_LOCK_COUNT); 1105 if (acache->entrylocks == NULL) { 1106 result = ISC_R_NOMEMORY; 1107 goto cleanup; 1108 } 1109 for (i = 0; i < DEFAULT_ACACHE_ENTRY_LOCK_COUNT; i++) { 1110 result = ACACHE_INITLOCK(&acache->entrylocks[i]); 1111 if (result != ISC_R_SUCCESS) { 1112 while (i-- > 0) 1113 ACACHE_DESTROYLOCK(&acache->entrylocks[i]); 1114 isc_mem_put(mctx, acache->entrylocks, 1115 sizeof(*acache->entrylocks) * 1116 DEFAULT_ACACHE_ENTRY_LOCK_COUNT); 1117 acache->entrylocks = NULL; 1118 goto cleanup; 1119 } 1120 } 1121 1122 acache->live_cleaners = 0; 1123 result = acache_cleaner_init(acache, timermgr, &acache->cleaner); 1124 if (result != ISC_R_SUCCESS) 1125 goto cleanup; 1126 1127 acache->stats.cleaner_runs = 0; 1128 reset_stats(acache); 1129 1130 acache->magic = ACACHE_MAGIC; 1131 1132 *acachep = acache; 1133 return (ISC_R_SUCCESS); 1134 1135 cleanup: 1136 if (acache->task != NULL) 1137 isc_task_detach(&acache->task); 1138 DESTROYLOCK(&acache->lock); 1139 isc_refcount_decrement(&acache->refs, NULL); 1140 isc_refcount_destroy(&acache->refs); 1141 if (acache->entrylocks != NULL) { 1142 for (i = 0; i < DEFAULT_ACACHE_ENTRY_LOCK_COUNT; i++) 1143 ACACHE_DESTROYLOCK(&acache->entrylocks[i]); 1144 isc_mem_put(mctx, acache->entrylocks, 1145 sizeof(*acache->entrylocks) * 1146 DEFAULT_ACACHE_ENTRY_LOCK_COUNT); 1147 } 1148 isc_mem_put(mctx, acache, sizeof(*acache)); 1149 isc_mem_detach(&mctx); 1150 1151 return (result); 1152 } 1153 1154 void 1155 dns_acache_attach(dns_acache_t *source, dns_acache_t **targetp) { 1156 REQUIRE(DNS_ACACHE_VALID(source)); 1157 REQUIRE(targetp != NULL && *targetp == NULL); 1158 1159 AATRACE(source, "attach"); 1160 1161 isc_refcount_increment(&source->refs, NULL); 1162 1163 *targetp = source; 1164 } 1165 1166 void 1167 dns_acache_countquerymiss(dns_acache_t *acache) { 1168 acache->stats.misses++; /* XXXSK danger: unlocked! */ 1169 acache->stats.queries++; /* XXXSK danger: unlocked! */ 1170 } 1171 1172 void 1173 dns_acache_detach(dns_acache_t **acachep) { 1174 dns_acache_t *acache; 1175 unsigned int refs; 1176 isc_boolean_t should_free = ISC_FALSE; 1177 1178 REQUIRE(acachep != NULL && DNS_ACACHE_VALID(*acachep)); 1179 acache = *acachep; 1180 1181 ATRACE("detach"); 1182 1183 isc_refcount_decrement(&acache->refs, &refs); 1184 if (refs == 0) { 1185 INSIST(check_noentry(acache) == ISC_TRUE); 1186 should_free = ISC_TRUE; 1187 } 1188 1189 *acachep = NULL; 1190 1191 /* 1192 * If we're exiting and the cleaner task exists, let it free the cache. 1193 */ 1194 if (should_free && acache->live_cleaners > 0) { 1195 isc_task_shutdown(acache->task); 1196 should_free = ISC_FALSE; 1197 } 1198 1199 if (should_free) 1200 destroy(acache); 1201 } 1202 1203 void 1204 dns_acache_shutdown(dns_acache_t *acache) { 1205 REQUIRE(DNS_ACACHE_VALID(acache)); 1206 1207 LOCK(&acache->lock); 1208 1209 ATRACE("shutdown"); 1210 1211 if (!acache->shutting_down) { 1212 isc_event_t *event; 1213 dns_acache_t *acache_evarg = NULL; 1214 1215 INSIST(!acache->cevent_sent); 1216 1217 acache->shutting_down = ISC_TRUE; 1218 1219 isc_mem_setwater(acache->mctx, NULL, NULL, 0, 0); 1220 1221 /* 1222 * Self attach the object in order to prevent it from being 1223 * destroyed while waiting for the event. 1224 */ 1225 dns_acache_attach(acache, &acache_evarg); 1226 event = &acache->cevent; 1227 event->ev_arg = acache_evarg; 1228 isc_task_send(acache->task, &event); 1229 acache->cevent_sent = ISC_TRUE; 1230 } 1231 1232 UNLOCK(&acache->lock); 1233 } 1234 1235 isc_result_t 1236 dns_acache_setdb(dns_acache_t *acache, dns_db_t *db) { 1237 int bucket; 1238 dbentry_t *dbentry; 1239 isc_result_t result = ISC_R_SUCCESS; 1240 1241 REQUIRE(DNS_ACACHE_VALID(acache)); 1242 REQUIRE(db != NULL); 1243 1244 ATRACE("setdb"); 1245 1246 LOCK(&acache->lock); 1247 1248 dbentry = NULL; 1249 result = finddbent(acache, db, &dbentry); 1250 if (result == ISC_R_SUCCESS) { 1251 result = ISC_R_EXISTS; 1252 goto end; 1253 } 1254 result = ISC_R_SUCCESS; 1255 1256 dbentry = isc_mem_get(acache->mctx, sizeof(*dbentry)); 1257 if (dbentry == NULL) { 1258 result = ISC_R_NOMEMORY; 1259 goto end; 1260 } 1261 1262 ISC_LINK_INIT(dbentry, link); 1263 ISC_LIST_INIT(dbentry->originlist); 1264 ISC_LIST_INIT(dbentry->referlist); 1265 1266 dbentry->db = NULL; 1267 dns_db_attach(db, &dbentry->db); 1268 1269 bucket = isc_hash_calc((const unsigned char *)&db, 1270 sizeof(db), ISC_TRUE) % DBBUCKETS; 1271 1272 ISC_LIST_APPEND(acache->dbbucket[bucket], dbentry, link); 1273 1274 acache->dbentries++; 1275 1276 end: 1277 UNLOCK(&acache->lock); 1278 1279 return (result); 1280 } 1281 1282 isc_result_t 1283 dns_acache_putdb(dns_acache_t *acache, dns_db_t *db) { 1284 int bucket; 1285 isc_result_t result; 1286 dbentry_t *dbentry; 1287 dns_acacheentry_t *entry; 1288 1289 REQUIRE(DNS_ACACHE_VALID(acache)); 1290 REQUIRE(db != NULL); 1291 1292 ATRACE("putdb"); 1293 1294 LOCK(&acache->lock); 1295 1296 dbentry = NULL; 1297 result = finddbent(acache, db, &dbentry); 1298 if (result != ISC_R_SUCCESS) { 1299 /* 1300 * The entry may have not been created due to memory shortage. 1301 */ 1302 UNLOCK(&acache->lock); 1303 return (ISC_R_NOTFOUND); 1304 } 1305 1306 /* 1307 * Release corresponding cache entries: for each entry, release all 1308 * links the entry has, and then callback to the entry holder (if any). 1309 * If no other external references exist (this can happen if the 1310 * original holder has canceled callback,) destroy it here. 1311 */ 1312 while ((entry = ISC_LIST_HEAD(dbentry->originlist)) != NULL) { 1313 ACACHE_LOCK(&acache->entrylocks[entry->locknum], 1314 isc_rwlocktype_write); 1315 1316 /* 1317 * Releasing olink first would avoid finddbent() in 1318 * unlink_dbentries(). 1319 */ 1320 ISC_LIST_UNLINK(dbentry->originlist, entry, olink); 1321 if (acache->cleaner.current_entry != entry) 1322 ISC_LIST_UNLINK(acache->entries, entry, link); 1323 unlink_dbentries(acache, entry); 1324 1325 if (entry->callback != NULL) 1326 (entry->callback)(entry, &entry->cbarg); 1327 entry->callback = NULL; 1328 1329 ACACHE_UNLOCK(&acache->entrylocks[entry->locknum], 1330 isc_rwlocktype_write); 1331 1332 if (acache->cleaner.current_entry != entry) 1333 dns_acache_detachentry(&entry); 1334 } 1335 while ((entry = ISC_LIST_HEAD(dbentry->referlist)) != NULL) { 1336 ACACHE_LOCK(&acache->entrylocks[entry->locknum], 1337 isc_rwlocktype_write); 1338 1339 ISC_LIST_UNLINK(dbentry->referlist, entry, rlink); 1340 if (acache->cleaner.current_entry != entry) 1341 ISC_LIST_UNLINK(acache->entries, entry, link); 1342 unlink_dbentries(acache, entry); 1343 1344 if (entry->callback != NULL) 1345 (entry->callback)(entry, &entry->cbarg); 1346 entry->callback = NULL; 1347 1348 ACACHE_UNLOCK(&acache->entrylocks[entry->locknum], 1349 isc_rwlocktype_write); 1350 1351 if (acache->cleaner.current_entry != entry) 1352 dns_acache_detachentry(&entry); 1353 } 1354 1355 INSIST(ISC_LIST_EMPTY(dbentry->originlist) && 1356 ISC_LIST_EMPTY(dbentry->referlist)); 1357 1358 bucket = isc_hash_calc((const unsigned char *)&db, 1359 sizeof(db), ISC_TRUE) % DBBUCKETS; 1360 ISC_LIST_UNLINK(acache->dbbucket[bucket], dbentry, link); 1361 dns_db_detach(&dbentry->db); 1362 1363 isc_mem_put(acache->mctx, dbentry, sizeof(*dbentry)); 1364 1365 acache->dbentries--; 1366 1367 acache->stats.deleted++; 1368 1369 UNLOCK(&acache->lock); 1370 1371 return (ISC_R_SUCCESS); 1372 } 1373 1374 isc_result_t 1375 dns_acache_createentry(dns_acache_t *acache, dns_db_t *origdb, 1376 void (*callback)(dns_acacheentry_t *, void **), 1377 void *cbarg, dns_acacheentry_t **entryp) 1378 { 1379 dns_acacheentry_t *newentry; 1380 isc_result_t result; 1381 isc_uint32_t r; 1382 1383 REQUIRE(DNS_ACACHE_VALID(acache)); 1384 REQUIRE(entryp != NULL && *entryp == NULL); 1385 REQUIRE(origdb != NULL); 1386 1387 /* 1388 * Should we exceed our memory limit for some reason (for 1389 * example, if the cleaner does not run aggressively enough), 1390 * then we will not create additional entries. 1391 * 1392 * XXXSK: It might be better to lock the acache->cleaner->lock, 1393 * but locking may be an expensive bottleneck. If we misread 1394 * the value, we will occasionally refuse to create a few 1395 * cache entries, or create a few that we should not. I do not 1396 * expect this to happen often, and it will not have very bad 1397 * effects when it does. So no lock for now. 1398 */ 1399 if (acache->cleaner.overmem) { 1400 acache->stats.overmem_nocreates++; /* XXXSK danger: unlocked! */ 1401 return (ISC_R_NORESOURCES); 1402 } 1403 1404 newentry = isc_mem_get(acache->mctx, sizeof(*newentry)); 1405 if (newentry == NULL) { 1406 acache->stats.nomem++; /* XXXMLG danger: unlocked! */ 1407 return (ISC_R_NOMEMORY); 1408 } 1409 1410 isc_random_get(&r); 1411 newentry->locknum = r % DEFAULT_ACACHE_ENTRY_LOCK_COUNT; 1412 1413 result = isc_refcount_init(&newentry->references, 1); 1414 if (result != ISC_R_SUCCESS) { 1415 isc_mem_put(acache->mctx, newentry, sizeof(*newentry)); 1416 return (result); 1417 }; 1418 1419 ISC_LINK_INIT(newentry, link); 1420 ISC_LINK_INIT(newentry, olink); 1421 ISC_LINK_INIT(newentry, rlink); 1422 1423 newentry->acache = NULL; 1424 dns_acache_attach(acache, &newentry->acache); 1425 1426 newentry->zone = NULL; 1427 newentry->db = NULL; 1428 newentry->version = NULL; 1429 newentry->node = NULL; 1430 newentry->foundname = NULL; 1431 1432 newentry->callback = callback; 1433 newentry->cbarg = cbarg; 1434 newentry->origdb = NULL; 1435 dns_db_attach(origdb, &newentry->origdb); 1436 1437 isc_stdtime_get(&newentry->lastused); 1438 1439 newentry->magic = ACACHEENTRY_MAGIC; 1440 1441 *entryp = newentry; 1442 1443 return (ISC_R_SUCCESS); 1444 } 1445 1446 isc_result_t 1447 dns_acache_getentry(dns_acacheentry_t *entry, dns_zone_t **zonep, 1448 dns_db_t **dbp, dns_dbversion_t **versionp, 1449 dns_dbnode_t **nodep, dns_name_t *fname, 1450 dns_message_t *msg, isc_stdtime_t now) 1451 { 1452 isc_result_t result = ISC_R_SUCCESS; 1453 dns_rdataset_t *erdataset; 1454 isc_stdtime32_t now32; 1455 dns_acache_t *acache; 1456 int locknum; 1457 1458 REQUIRE(DNS_ACACHEENTRY_VALID(entry)); 1459 REQUIRE(zonep == NULL || *zonep == NULL); 1460 REQUIRE(dbp != NULL && *dbp == NULL); 1461 REQUIRE(versionp != NULL && *versionp == NULL); 1462 REQUIRE(nodep != NULL && *nodep == NULL); 1463 REQUIRE(fname != NULL); 1464 REQUIRE(msg != NULL); 1465 acache = entry->acache; 1466 REQUIRE(DNS_ACACHE_VALID(acache)); 1467 1468 locknum = entry->locknum; 1469 ACACHE_LOCK(&acache->entrylocks[locknum], isc_rwlocktype_read); 1470 1471 isc_stdtime_convert32(now, &now32); 1472 acache_storetime(entry, now32); 1473 1474 if (entry->zone != NULL && zonep != NULL) 1475 dns_zone_attach(entry->zone, zonep); 1476 1477 if (entry->db == NULL) { 1478 *dbp = NULL; 1479 *versionp = NULL; 1480 } else { 1481 dns_db_attach(entry->db, dbp); 1482 dns_db_attachversion(entry->db, entry->version, versionp); 1483 } 1484 if (entry->node == NULL) 1485 *nodep = NULL; 1486 else { 1487 dns_db_attachnode(entry->db, entry->node, nodep); 1488 1489 INSIST(entry->foundname != NULL); 1490 dns_name_copy(entry->foundname, fname, NULL); 1491 for (erdataset = ISC_LIST_HEAD(entry->foundname->list); 1492 erdataset != NULL; 1493 erdataset = ISC_LIST_NEXT(erdataset, link)) { 1494 dns_rdataset_t *ardataset; 1495 1496 ardataset = NULL; 1497 result = dns_message_gettemprdataset(msg, &ardataset); 1498 if (result != ISC_R_SUCCESS) { 1499 ACACHE_UNLOCK(&acache->entrylocks[locknum], 1500 isc_rwlocktype_read); 1501 goto fail; 1502 } 1503 1504 /* 1505 * XXXJT: if we simply clone the rdataset, we'll get 1506 * lost wrt cyclic ordering. We'll need an additional 1507 * trick to get the latest counter from the original 1508 * header. 1509 */ 1510 dns_rdataset_clone(erdataset, ardataset); 1511 ISC_LIST_APPEND(fname->list, ardataset, link); 1512 } 1513 } 1514 1515 entry->acache->stats.hits++; /* XXXMLG danger: unlocked! */ 1516 entry->acache->stats.queries++; 1517 1518 ACACHE_UNLOCK(&acache->entrylocks[locknum], isc_rwlocktype_read); 1519 1520 return (result); 1521 1522 fail: 1523 while ((erdataset = ISC_LIST_HEAD(fname->list)) != NULL) { 1524 ISC_LIST_UNLINK(fname->list, erdataset, link); 1525 dns_rdataset_disassociate(erdataset); 1526 dns_message_puttemprdataset(msg, &erdataset); 1527 } 1528 if (*nodep != NULL) 1529 dns_db_detachnode(*dbp, nodep); 1530 if (*versionp != NULL) 1531 dns_db_closeversion(*dbp, versionp, ISC_FALSE); 1532 if (*dbp != NULL) 1533 dns_db_detach(dbp); 1534 if (zonep != NULL && *zonep != NULL) 1535 dns_zone_detach(zonep); 1536 1537 return (result); 1538 } 1539 1540 isc_result_t 1541 dns_acache_setentry(dns_acache_t *acache, dns_acacheentry_t *entry, 1542 dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version, 1543 dns_dbnode_t *node, dns_name_t *fname) 1544 { 1545 isc_result_t result; 1546 dbentry_t *odbent; 1547 dbentry_t *rdbent = NULL; 1548 isc_boolean_t close_version = ISC_FALSE; 1549 dns_acacheentry_t *dummy_entry = NULL; 1550 1551 REQUIRE(DNS_ACACHE_VALID(acache)); 1552 REQUIRE(DNS_ACACHEENTRY_VALID(entry)); 1553 1554 LOCK(&acache->lock); /* XXX: need to lock it here for ordering */ 1555 ACACHE_LOCK(&acache->entrylocks[entry->locknum], isc_rwlocktype_write); 1556 1557 /* Set zone */ 1558 if (zone != NULL) 1559 dns_zone_attach(zone, &entry->zone); 1560 /* Set DB */ 1561 if (db != NULL) 1562 dns_db_attach(db, &entry->db); 1563 /* 1564 * Set DB version. If the version is not given by the caller, 1565 * which is the case for glue or cache DBs, use the current version. 1566 */ 1567 if (version == NULL) { 1568 if (db != NULL) { 1569 dns_db_currentversion(db, &version); 1570 close_version = ISC_TRUE; 1571 } 1572 } 1573 if (version != NULL) { 1574 INSIST(db != NULL); 1575 dns_db_attachversion(db, version, &entry->version); 1576 } 1577 if (close_version) 1578 dns_db_closeversion(db, &version, ISC_FALSE); 1579 /* Set DB node. */ 1580 if (node != NULL) { 1581 INSIST(db != NULL); 1582 dns_db_attachnode(db, node, &entry->node); 1583 } 1584 1585 /* 1586 * Set list of the corresponding rdatasets, if given. 1587 * To minimize the overhead and memory consumption, we'll do this for 1588 * positive cache only, in which case the DB node is non NULL. 1589 * We do not want to cache incomplete information, so give up the 1590 * entire entry when a memory shortage happen during the process. 1591 */ 1592 if (node != NULL) { 1593 dns_rdataset_t *ardataset, *crdataset; 1594 1595 entry->foundname = isc_mem_get(acache->mctx, 1596 sizeof(*entry->foundname)); 1597 1598 if (entry->foundname == NULL) { 1599 result = ISC_R_NOMEMORY; 1600 goto fail; 1601 } 1602 dns_name_init(entry->foundname, NULL); 1603 result = dns_name_dup(fname, acache->mctx, 1604 entry->foundname); 1605 if (result != ISC_R_SUCCESS) 1606 goto fail; 1607 1608 for (ardataset = ISC_LIST_HEAD(fname->list); 1609 ardataset != NULL; 1610 ardataset = ISC_LIST_NEXT(ardataset, link)) { 1611 crdataset = isc_mem_get(acache->mctx, 1612 sizeof(*crdataset)); 1613 if (crdataset == NULL) { 1614 result = ISC_R_NOMEMORY; 1615 goto fail; 1616 } 1617 1618 dns_rdataset_init(crdataset); 1619 dns_rdataset_clone(ardataset, crdataset); 1620 ISC_LIST_APPEND(entry->foundname->list, crdataset, 1621 link); 1622 } 1623 } 1624 1625 odbent = NULL; 1626 result = finddbent(acache, entry->origdb, &odbent); 1627 if (result != ISC_R_SUCCESS) 1628 goto fail; 1629 if (db != NULL) { 1630 rdbent = NULL; 1631 result = finddbent(acache, db, &rdbent); 1632 if (result != ISC_R_SUCCESS) 1633 goto fail; 1634 } 1635 1636 ISC_LIST_APPEND(acache->entries, entry, link); 1637 ISC_LIST_APPEND(odbent->originlist, entry, olink); 1638 if (rdbent != NULL) 1639 ISC_LIST_APPEND(rdbent->referlist, entry, rlink); 1640 1641 /* 1642 * The additional cache needs an implicit reference to entries in its 1643 * link. 1644 */ 1645 dns_acache_attachentry(entry, &dummy_entry); 1646 1647 ACACHE_UNLOCK(&acache->entrylocks[entry->locknum], 1648 isc_rwlocktype_write); 1649 1650 acache->stats.adds++; 1651 UNLOCK(&acache->lock); 1652 1653 return (ISC_R_SUCCESS); 1654 1655 fail: 1656 clear_entry(acache, entry); 1657 1658 ACACHE_UNLOCK(&acache->entrylocks[entry->locknum], 1659 isc_rwlocktype_write); 1660 UNLOCK(&acache->lock); 1661 1662 return (result); 1663 } 1664 1665 isc_boolean_t 1666 dns_acache_cancelentry(dns_acacheentry_t *entry) { 1667 dns_acache_t *acache; 1668 isc_boolean_t callback_active; 1669 1670 REQUIRE(DNS_ACACHEENTRY_VALID(entry)); 1671 1672 acache = entry->acache; 1673 1674 INSIST(DNS_ACACHE_VALID(entry->acache)); 1675 1676 LOCK(&acache->lock); 1677 ACACHE_LOCK(&acache->entrylocks[entry->locknum], isc_rwlocktype_write); 1678 1679 callback_active = ISC_TF(entry->cbarg != NULL); 1680 1681 /* 1682 * Release dependencies stored in this entry as much as possible. 1683 * The main link cannot be released, since the acache object has 1684 * a reference to this entry; the empty entry will be released in 1685 * the next cleaning action. 1686 */ 1687 unlink_dbentries(acache, entry); 1688 clear_entry(entry->acache, entry); 1689 1690 entry->callback = NULL; 1691 entry->cbarg = NULL; 1692 1693 ACACHE_UNLOCK(&acache->entrylocks[entry->locknum], 1694 isc_rwlocktype_write); 1695 UNLOCK(&acache->lock); 1696 1697 return (callback_active); 1698 } 1699 1700 void 1701 dns_acache_attachentry(dns_acacheentry_t *source, 1702 dns_acacheentry_t **targetp) 1703 { 1704 REQUIRE(DNS_ACACHEENTRY_VALID(source)); 1705 REQUIRE(targetp != NULL && *targetp == NULL); 1706 1707 isc_refcount_increment(&source->references, NULL); 1708 1709 *targetp = source; 1710 } 1711 1712 void 1713 dns_acache_detachentry(dns_acacheentry_t **entryp) { 1714 dns_acacheentry_t *entry; 1715 unsigned int refs; 1716 1717 REQUIRE(entryp != NULL && DNS_ACACHEENTRY_VALID(*entryp)); 1718 entry = *entryp; 1719 1720 isc_refcount_decrement(&entry->references, &refs); 1721 1722 /* 1723 * If there are no references to the entry, the entry must have been 1724 * unlinked and can be destroyed safely. 1725 */ 1726 if (refs == 0) { 1727 INSIST(!ISC_LINK_LINKED(entry, link)); 1728 (*entryp)->acache->stats.deleted++; 1729 destroy_entry(entry); 1730 } 1731 1732 *entryp = NULL; 1733 } 1734 1735 void 1736 dns_acache_setcleaninginterval(dns_acache_t *acache, unsigned int t) { 1737 isc_interval_t interval; 1738 isc_result_t result; 1739 1740 REQUIRE(DNS_ACACHE_VALID(acache)); 1741 1742 ATRACE("dns_acache_setcleaninginterval"); 1743 1744 LOCK(&acache->lock); 1745 1746 /* 1747 * It may be the case that the acache has already shut down. 1748 * If so, it has no timer. (Not sure if this can really happen.) 1749 */ 1750 if (acache->cleaner.cleaning_timer == NULL) 1751 goto unlock; 1752 1753 acache->cleaner.cleaning_interval = t; 1754 1755 if (t == 0) { 1756 result = isc_timer_reset(acache->cleaner.cleaning_timer, 1757 isc_timertype_inactive, 1758 NULL, NULL, ISC_TRUE); 1759 } else { 1760 isc_interval_set(&interval, acache->cleaner.cleaning_interval, 1761 0); 1762 result = isc_timer_reset(acache->cleaner.cleaning_timer, 1763 isc_timertype_ticker, 1764 NULL, &interval, ISC_FALSE); 1765 } 1766 if (result != ISC_R_SUCCESS) 1767 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1768 DNS_LOGMODULE_ACACHE, ISC_LOG_WARNING, 1769 "could not set acache cleaning interval: %s", 1770 isc_result_totext(result)); 1771 else 1772 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1773 DNS_LOGMODULE_ACACHE, ISC_LOG_NOTICE, 1774 "acache %p cleaning interval set to %d.", 1775 acache, t); 1776 1777 unlock: 1778 UNLOCK(&acache->lock); 1779 } 1780 1781 /* 1782 * This function was derived from cache.c:dns_cache_setcachesize(). See the 1783 * function for more details about the logic. 1784 */ 1785 void 1786 dns_acache_setcachesize(dns_acache_t *acache, size_t size) { 1787 size_t hiwater, lowater; 1788 1789 REQUIRE(DNS_ACACHE_VALID(acache)); 1790 1791 if (size != 0U && size < DNS_ACACHE_MINSIZE) 1792 size = DNS_ACACHE_MINSIZE; 1793 1794 hiwater = size - (size >> 3); 1795 lowater = size - (size >> 2); 1796 1797 if (size == 0U || hiwater == 0U || lowater == 0U) 1798 isc_mem_setwater(acache->mctx, water, acache, 0, 0); 1799 else 1800 isc_mem_setwater(acache->mctx, water, acache, 1801 hiwater, lowater); 1802 } 1803