00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include <config.h>
00020
00021 #include <isc/atomic.h>
00022 #include <isc/event.h>
00023 #include <isc/hash.h>
00024 #include <isc/magic.h>
00025 #include <isc/mem.h>
00026 #include <isc/mutex.h>
00027 #include <isc/random.h>
00028 #include <isc/refcount.h>
00029 #include <isc/rwlock.h>
00030 #include <isc/serial.h>
00031 #include <isc/task.h>
00032 #include <isc/time.h>
00033 #include <isc/timer.h>
00034
00035 #include <dns/acache.h>
00036 #include <dns/db.h>
00037 #include <dns/events.h>
00038 #include <dns/log.h>
00039 #include <dns/message.h>
00040 #include <dns/name.h>
00041 #include <dns/rdataset.h>
00042 #include <dns/result.h>
00043 #include <dns/zone.h>
00044
00045 #define ACACHE_MAGIC ISC_MAGIC('A', 'C', 'H', 'E')
00046 #define DNS_ACACHE_VALID(acache) ISC_MAGIC_VALID(acache, ACACHE_MAGIC)
00047
00048 #define ACACHEENTRY_MAGIC ISC_MAGIC('A', 'C', 'E', 'T')
00049 #define DNS_ACACHEENTRY_VALID(entry) ISC_MAGIC_VALID(entry, ACACHEENTRY_MAGIC)
00050
00051 #define DBBUCKETS 67
00052
00053 #if 0
00054 #define ATRACE(m) isc_log_write(dns_lctx, \
00055 DNS_LOGCATEGORY_DATABASE, \
00056 DNS_LOGMODULE_ACACHE, \
00057 ISC_LOG_DEBUG(3), \
00058 "acache %p: %s", acache, (m))
00059 #define AATRACE(a,m) isc_log_write(dns_lctx, \
00060 DNS_LOGCATEGORY_DATABASE, \
00061 DNS_LOGMODULE_ACACHE, \
00062 ISC_LOG_DEBUG(3), \
00063 "acache %p: %s", (a), (m))
00064 #else
00065 #define ATRACE(m)
00066 #define AATRACE(a, m)
00067 #endif
00068
00069
00070
00071
00072
00073
00074
00075
00076 #define DNS_ACACHE_MINSIZE 2097152U
00077 #define DNS_ACACHE_CLEANERINCREMENT 1000
00078
00079 #define DEFAULT_ACACHE_ENTRY_LOCK_COUNT 1009
00080
00081 #if defined(ISC_RWLOCK_USEATOMIC) && defined(ISC_PLATFORM_HAVEATOMICSTORE)
00082 #define ACACHE_USE_RWLOCK 1
00083 #endif
00084
00085 #ifdef ACACHE_USE_RWLOCK
00086 #define ACACHE_INITLOCK(l) isc_rwlock_init((l), 0, 0)
00087 #define ACACHE_DESTROYLOCK(l) isc_rwlock_destroy(l)
00088 #define ACACHE_LOCK(l, t) RWLOCK((l), (t))
00089 #define ACACHE_UNLOCK(l, t) RWUNLOCK((l), (t))
00090
00091 #define acache_storetime(entry, t) \
00092 (isc_atomic_store((isc_int32_t *)&(entry)->lastused, (t)))
00093 #else
00094 #define ACACHE_INITLOCK(l) isc_mutex_init(l)
00095 #define ACACHE_DESTROYLOCK(l) DESTROYLOCK(l)
00096 #define ACACHE_LOCK(l, t) LOCK(l)
00097 #define ACACHE_UNLOCK(l, t) UNLOCK(l)
00098
00099 #define acache_storetime(entry, t) ((entry)->lastused = (t))
00100 #endif
00101
00102
00103 typedef struct dbentry {
00104 ISC_LINK(struct dbentry) link;
00105
00106 dns_db_t *db;
00107 ISC_LIST(dns_acacheentry_t) originlist;
00108 ISC_LIST(dns_acacheentry_t) referlist;
00109 } dbentry_t;
00110
00111 typedef ISC_LIST(dbentry_t) dbentrylist_t;
00112
00113 typedef struct acache_cleaner acache_cleaner_t;
00114
00115 typedef enum {
00116 cleaner_s_idle,
00117 cleaner_s_busy,
00118 cleaner_s_done
00119 } cleaner_state_t;
00120
00121
00122
00123
00124 #define CLEANER_IDLE(c) ((c)->state == cleaner_s_idle && \
00125 (c)->resched_event != NULL)
00126 #define CLEANER_BUSY(c) ((c)->state == cleaner_s_busy && \
00127 (c)->resched_event == NULL)
00128
00129 struct acache_cleaner {
00130 isc_mutex_t lock;
00131
00132
00133
00134
00135 dns_acache_t *acache;
00136 unsigned int cleaning_interval;
00137
00138
00139
00140 isc_stdtime_t last_cleanup_time;
00141
00142
00143 isc_timer_t *cleaning_timer;
00144 isc_event_t *resched_event;
00145
00146 isc_event_t *overmem_event;
00147
00148 dns_acacheentry_t *current_entry;
00149
00150
00151 int increment;
00152
00153
00154 unsigned long ncleaned;
00155
00156 cleaner_state_t state;
00157 isc_boolean_t overmem;
00158
00159 };
00160
00161 struct dns_acachestats {
00162 unsigned int hits;
00163 unsigned int queries;
00164 unsigned int misses;
00165 unsigned int adds;
00166 unsigned int deleted;
00167 unsigned int cleaned;
00168 unsigned int cleaner_runs;
00169 unsigned int overmem;
00170 unsigned int overmem_nocreates;
00171 unsigned int nomem;
00172 };
00173
00174
00175
00176
00177
00178 struct dns_acache {
00179 unsigned int magic;
00180
00181 isc_mem_t *mctx;
00182 isc_refcount_t refs;
00183
00184 #ifdef ACACHE_USE_RWLOCK
00185 isc_rwlock_t *entrylocks;
00186 #else
00187 isc_mutex_t *entrylocks;
00188 #endif
00189
00190 isc_mutex_t lock;
00191
00192 int live_cleaners;
00193 acache_cleaner_t cleaner;
00194 ISC_LIST(dns_acacheentry_t) entries;
00195 unsigned int dbentries;
00196 dbentrylist_t dbbucket[DBBUCKETS];
00197
00198 isc_boolean_t shutting_down;
00199
00200 isc_task_t *task;
00201 isc_event_t cevent;
00202 isc_boolean_t cevent_sent;
00203
00204 dns_acachestats_t stats;
00205 };
00206
00207 struct dns_acacheentry {
00208 unsigned int magic;
00209
00210 unsigned int locknum;
00211 isc_refcount_t references;
00212
00213 dns_acache_t *acache;
00214
00215
00216 ISC_LINK(dns_acacheentry_t) link;
00217 ISC_LINK(dns_acacheentry_t) olink;
00218 ISC_LINK(dns_acacheentry_t) rlink;
00219
00220 dns_db_t *origdb;
00221
00222
00223
00224 dns_zone_t *zone;
00225
00226 dns_db_t *db;
00227 dns_dbversion_t *version;
00228 dns_dbnode_t *node;
00229
00230 dns_name_t *foundname;
00231
00232
00233
00234 void (*callback)(dns_acacheentry_t *, void **);
00235 void *cbarg;
00236
00237
00238 isc_stdtime32_t lastused;
00239 };
00240
00241
00242
00243
00244 static inline isc_boolean_t check_noentry(dns_acache_t *acache);
00245 static void destroy(dns_acache_t *acache);
00246 static void shutdown_entries(dns_acache_t *acache);
00247 static void shutdown_buckets(dns_acache_t *acache);
00248 static void destroy_entry(dns_acacheentry_t *ent);
00249 static inline void unlink_dbentries(dns_acache_t *acache,
00250 dns_acacheentry_t *ent);
00251 static inline isc_result_t finddbent(dns_acache_t *acache,
00252 dns_db_t *db, dbentry_t **dbentryp);
00253 static inline void clear_entry(dns_acache_t *acache, dns_acacheentry_t *entry);
00254 static isc_result_t acache_cleaner_init(dns_acache_t *acache,
00255 isc_timermgr_t *timermgr,
00256 acache_cleaner_t *cleaner);
00257 static void acache_cleaning_timer_action(isc_task_t *task, isc_event_t *event);
00258 static void acache_incremental_cleaning_action(isc_task_t *task,
00259 isc_event_t *event);
00260 static void acache_overmem_cleaning_action(isc_task_t *task,
00261 isc_event_t *event);
00262 static void acache_cleaner_shutdown_action(isc_task_t *task,
00263 isc_event_t *event);
00264
00265
00266
00267
00268
00269 static void
00270 reset_stats(dns_acache_t *acache) {
00271 acache->stats.hits = 0;
00272 acache->stats.queries = 0;
00273 acache->stats.misses = 0;
00274 acache->stats.adds = 0;
00275 acache->stats.deleted = 0;
00276 acache->stats.cleaned = 0;
00277 acache->stats.overmem = 0;
00278 acache->stats.overmem_nocreates = 0;
00279 acache->stats.nomem = 0;
00280 }
00281
00282
00283
00284
00285 static inline isc_boolean_t
00286 check_noentry(dns_acache_t *acache) {
00287 if (ISC_LIST_EMPTY(acache->entries) && acache->dbentries == 0) {
00288 return (ISC_TRUE);
00289 }
00290
00291 return (ISC_FALSE);
00292 }
00293
00294
00295
00296
00297 static void
00298 shutdown_entries(dns_acache_t *acache) {
00299 dns_acacheentry_t *entry, *entry_next;
00300
00301 REQUIRE(DNS_ACACHE_VALID(acache));
00302 INSIST(acache->shutting_down);
00303
00304
00305
00306
00307 for (entry = ISC_LIST_HEAD(acache->entries);
00308 entry != NULL;
00309 entry = entry_next) {
00310 entry_next = ISC_LIST_NEXT(entry, link);
00311
00312 ACACHE_LOCK(&acache->entrylocks[entry->locknum],
00313 isc_rwlocktype_write);
00314
00315
00316
00317
00318
00319 if (acache->cleaner.current_entry != entry)
00320 ISC_LIST_UNLINK(acache->entries, entry, link);
00321 unlink_dbentries(acache, entry);
00322 if (entry->callback != NULL) {
00323 (entry->callback)(entry, &entry->cbarg);
00324 entry->callback = NULL;
00325 }
00326
00327 ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
00328 isc_rwlocktype_write);
00329
00330 if (acache->cleaner.current_entry != entry)
00331 dns_acache_detachentry(&entry);
00332 }
00333 }
00334
00335
00336
00337
00338 static void
00339 shutdown_buckets(dns_acache_t *acache) {
00340 int i;
00341 dbentry_t *dbent;
00342
00343 REQUIRE(DNS_ACACHE_VALID(acache));
00344 INSIST(acache->shutting_down);
00345
00346 for (i = 0; i < DBBUCKETS; i++) {
00347 while ((dbent = ISC_LIST_HEAD(acache->dbbucket[i])) != NULL) {
00348 INSIST(ISC_LIST_EMPTY(dbent->originlist) &&
00349 ISC_LIST_EMPTY(dbent->referlist));
00350 ISC_LIST_UNLINK(acache->dbbucket[i], dbent, link);
00351
00352 dns_db_detach(&dbent->db);
00353
00354 isc_mem_put(acache->mctx, dbent, sizeof(*dbent));
00355
00356 acache->dbentries--;
00357 }
00358 }
00359
00360 INSIST(acache->dbentries == 0);
00361 }
00362
00363 static void
00364 shutdown_task(isc_task_t *task, isc_event_t *ev) {
00365 dns_acache_t *acache;
00366
00367 UNUSED(task);
00368
00369 acache = ev->ev_arg;
00370 INSIST(DNS_ACACHE_VALID(acache));
00371
00372 isc_event_free(&ev);
00373
00374 LOCK(&acache->lock);
00375
00376 shutdown_entries(acache);
00377 shutdown_buckets(acache);
00378
00379 UNLOCK(&acache->lock);
00380
00381 dns_acache_detach(&acache);
00382 }
00383
00384
00385 static inline void
00386 unlink_dbentries(dns_acache_t *acache, dns_acacheentry_t *ent) {
00387 isc_result_t result;
00388 dbentry_t *dbent;
00389
00390 if (ISC_LINK_LINKED(ent, olink)) {
00391 INSIST(ent->origdb != NULL);
00392 dbent = NULL;
00393 result = finddbent(acache, ent->origdb, &dbent);
00394 INSIST(result == ISC_R_SUCCESS);
00395
00396 ISC_LIST_UNLINK(dbent->originlist, ent, olink);
00397 }
00398 if (ISC_LINK_LINKED(ent, rlink)) {
00399 INSIST(ent->db != NULL);
00400 dbent = NULL;
00401 result = finddbent(acache, ent->db, &dbent);
00402 INSIST(result == ISC_R_SUCCESS);
00403
00404 ISC_LIST_UNLINK(dbent->referlist, ent, rlink);
00405 }
00406 }
00407
00408
00409 static void
00410 destroy_entry(dns_acacheentry_t *entry) {
00411 dns_acache_t *acache;
00412
00413 REQUIRE(DNS_ACACHEENTRY_VALID(entry));
00414
00415 acache = entry->acache;
00416 REQUIRE(DNS_ACACHE_VALID(acache));
00417
00418
00419
00420
00421
00422 clear_entry(acache, entry);
00423
00424 isc_mem_put(acache->mctx, entry, sizeof(*entry));
00425
00426 dns_acache_detach(&acache);
00427 }
00428
00429 static void
00430 destroy(dns_acache_t *acache) {
00431 int i;
00432
00433 REQUIRE(DNS_ACACHE_VALID(acache));
00434
00435 ATRACE("destroy");
00436
00437 isc_mem_setwater(acache->mctx, NULL, NULL, 0, 0);
00438
00439 if (acache->cleaner.overmem_event != NULL)
00440 isc_event_free(&acache->cleaner.overmem_event);
00441
00442 if (acache->cleaner.resched_event != NULL)
00443 isc_event_free(&acache->cleaner.resched_event);
00444
00445 if (acache->task != NULL)
00446 isc_task_detach(&acache->task);
00447
00448 for (i = 0; i < DEFAULT_ACACHE_ENTRY_LOCK_COUNT; i++)
00449 ACACHE_DESTROYLOCK(&acache->entrylocks[i]);
00450 isc_mem_put(acache->mctx, acache->entrylocks,
00451 sizeof(*acache->entrylocks) *
00452 DEFAULT_ACACHE_ENTRY_LOCK_COUNT);
00453
00454 DESTROYLOCK(&acache->cleaner.lock);
00455
00456 DESTROYLOCK(&acache->lock);
00457 acache->magic = 0;
00458
00459 isc_mem_putanddetach(&acache->mctx, acache, sizeof(*acache));
00460 }
00461
00462 static inline isc_result_t
00463 finddbent(dns_acache_t *acache, dns_db_t *db, dbentry_t **dbentryp) {
00464 int bucket;
00465 dbentry_t *dbentry;
00466
00467 REQUIRE(DNS_ACACHE_VALID(acache));
00468 REQUIRE(db != NULL);
00469 REQUIRE(dbentryp != NULL && *dbentryp == NULL);
00470
00471
00472
00473
00474
00475 bucket = isc_hash_calc((const unsigned char *)&db,
00476 sizeof(db), ISC_TRUE) % DBBUCKETS;
00477
00478 for (dbentry = ISC_LIST_HEAD(acache->dbbucket[bucket]);
00479 dbentry != NULL;
00480 dbentry = ISC_LIST_NEXT(dbentry, link)) {
00481 if (dbentry->db == db)
00482 break;
00483 }
00484
00485 *dbentryp = dbentry;
00486
00487 if (dbentry == NULL)
00488 return (ISC_R_NOTFOUND);
00489 else
00490 return (ISC_R_SUCCESS);
00491 }
00492
00493 static inline void
00494 clear_entry(dns_acache_t *acache, dns_acacheentry_t *entry) {
00495 REQUIRE(DNS_ACACHE_VALID(acache));
00496 REQUIRE(DNS_ACACHEENTRY_VALID(entry));
00497
00498
00499
00500
00501
00502 if (entry->foundname) {
00503 dns_rdataset_t *rdataset, *rdataset_next;
00504
00505 for (rdataset = ISC_LIST_HEAD(entry->foundname->list);
00506 rdataset != NULL;
00507 rdataset = rdataset_next) {
00508 rdataset_next = ISC_LIST_NEXT(rdataset, link);
00509 ISC_LIST_UNLINK(entry->foundname->list,
00510 rdataset, link);
00511 dns_rdataset_disassociate(rdataset);
00512 isc_mem_put(acache->mctx, rdataset, sizeof(*rdataset));
00513 }
00514 if (dns_name_dynamic(entry->foundname))
00515 dns_name_free(entry->foundname, acache->mctx);
00516 isc_mem_put(acache->mctx, entry->foundname,
00517 sizeof(*entry->foundname));
00518 entry->foundname = NULL;
00519 }
00520
00521 if (entry->node != NULL) {
00522 INSIST(entry->db != NULL);
00523 dns_db_detachnode(entry->db, &entry->node);
00524 }
00525 if (entry->version != NULL) {
00526 INSIST(entry->db != NULL);
00527 dns_db_closeversion(entry->db, &entry->version, ISC_FALSE);
00528 }
00529 if (entry->db != NULL)
00530 dns_db_detach(&entry->db);
00531 if (entry->zone != NULL)
00532 dns_zone_detach(&entry->zone);
00533
00534 if (entry->origdb != NULL)
00535 dns_db_detach(&entry->origdb);
00536 }
00537
00538 static isc_result_t
00539 acache_cleaner_init(dns_acache_t *acache, isc_timermgr_t *timermgr,
00540 acache_cleaner_t *cleaner)
00541 {
00542 int result;
00543
00544 ATRACE("acache cleaner init");
00545
00546 result = isc_mutex_init(&cleaner->lock);
00547 if (result != ISC_R_SUCCESS)
00548 goto fail;
00549
00550 cleaner->increment = DNS_ACACHE_CLEANERINCREMENT;
00551 cleaner->state = cleaner_s_idle;
00552 cleaner->acache = acache;
00553 cleaner->overmem = ISC_FALSE;
00554
00555 cleaner->cleaning_timer = NULL;
00556 cleaner->resched_event = NULL;
00557 cleaner->overmem_event = NULL;
00558 cleaner->current_entry = NULL;
00559
00560 if (timermgr != NULL) {
00561 cleaner->acache->live_cleaners++;
00562
00563 result = isc_task_onshutdown(acache->task,
00564 acache_cleaner_shutdown_action,
00565 acache);
00566 if (result != ISC_R_SUCCESS) {
00567 UNEXPECTED_ERROR(__FILE__, __LINE__,
00568 "acache cleaner: "
00569 "isc_task_onshutdown() failed: %s",
00570 dns_result_totext(result));
00571 goto cleanup;
00572 }
00573
00574 cleaner->cleaning_interval = 0;
00575 isc_stdtime_get(&cleaner->last_cleanup_time);
00576 result = isc_timer_create(timermgr, isc_timertype_inactive,
00577 NULL, NULL,
00578 acache->task,
00579 acache_cleaning_timer_action,
00580 cleaner, &cleaner->cleaning_timer);
00581 if (result != ISC_R_SUCCESS) {
00582 UNEXPECTED_ERROR(__FILE__, __LINE__,
00583 "isc_timer_create() failed: %s",
00584 dns_result_totext(result));
00585 result = ISC_R_UNEXPECTED;
00586 goto cleanup;
00587 }
00588
00589 cleaner->resched_event =
00590 isc_event_allocate(acache->mctx, cleaner,
00591 DNS_EVENT_ACACHECLEAN,
00592 acache_incremental_cleaning_action,
00593 cleaner, sizeof(isc_event_t));
00594 if (cleaner->resched_event == NULL) {
00595 result = ISC_R_NOMEMORY;
00596 goto cleanup;
00597 }
00598
00599 cleaner->overmem_event =
00600 isc_event_allocate(acache->mctx, cleaner,
00601 DNS_EVENT_ACACHEOVERMEM,
00602 acache_overmem_cleaning_action,
00603 cleaner, sizeof(isc_event_t));
00604 if (cleaner->overmem_event == NULL) {
00605 result = ISC_R_NOMEMORY;
00606 goto cleanup;
00607 }
00608 }
00609
00610 return (ISC_R_SUCCESS);
00611
00612 cleanup:
00613 if (cleaner->overmem_event != NULL)
00614 isc_event_free(&cleaner->overmem_event);
00615 if (cleaner->resched_event != NULL)
00616 isc_event_free(&cleaner->resched_event);
00617 if (cleaner->cleaning_timer != NULL)
00618 isc_timer_detach(&cleaner->cleaning_timer);
00619 cleaner->acache->live_cleaners--;
00620 DESTROYLOCK(&cleaner->lock);
00621 fail:
00622 return (result);
00623 }
00624
00625 static void
00626 begin_cleaning(acache_cleaner_t *cleaner) {
00627 dns_acacheentry_t *head;
00628 dns_acache_t *acache = cleaner->acache;
00629
00630
00631
00632
00633
00634
00635
00636 REQUIRE(CLEANER_IDLE(cleaner));
00637 INSIST(DNS_ACACHE_VALID(acache));
00638 INSIST(cleaner->current_entry == NULL);
00639
00640 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
00641 DNS_LOGMODULE_ACACHE, ISC_LOG_DEBUG(1),
00642 "begin acache cleaning, mem inuse %lu",
00643 (unsigned long)isc_mem_inuse(cleaner->acache->mctx));
00644
00645 LOCK(&acache->lock);
00646
00647 head = ISC_LIST_HEAD(acache->entries);
00648 if (head != NULL)
00649 dns_acache_attachentry(head, &cleaner->current_entry);
00650
00651 UNLOCK(&acache->lock);
00652
00653 if (cleaner->current_entry != NULL) {
00654 cleaner->ncleaned = 0;
00655 cleaner->state = cleaner_s_busy;
00656 isc_task_send(acache->task, &cleaner->resched_event);
00657 }
00658
00659 return;
00660 }
00661
00662 static void
00663 end_cleaning(acache_cleaner_t *cleaner, isc_event_t *event) {
00664 dns_acache_t *acache = cleaner->acache;
00665
00666 REQUIRE(CLEANER_BUSY(cleaner));
00667 REQUIRE(event != NULL);
00668 REQUIRE(DNS_ACACHEENTRY_VALID(cleaner->current_entry));
00669
00670
00671
00672 LOCK(&acache->lock);
00673
00674
00675
00676
00677
00678
00679 if (isc_refcount_current(&cleaner->current_entry->references) == 1) {
00680 INSIST(cleaner->current_entry->callback == NULL);
00681
00682 if (ISC_LINK_LINKED(cleaner->current_entry, link)) {
00683 ISC_LIST_UNLINK(acache->entries,
00684 cleaner->current_entry, link);
00685 }
00686 }
00687 dns_acache_detachentry(&cleaner->current_entry);
00688
00689 if (cleaner->overmem)
00690 acache->stats.overmem++;
00691 acache->stats.cleaned += cleaner->ncleaned;
00692 acache->stats.cleaner_runs++;
00693
00694 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE,
00695 ISC_LOG_NOTICE,
00696 "acache %p stats: hits=%d misses=%d queries=%d "
00697 "adds=%d deleted=%d "
00698 "cleaned=%d cleaner_runs=%d overmem=%d "
00699 "overmem_nocreates=%d nomem=%d",
00700 acache,
00701 acache->stats.hits, acache->stats.misses,
00702 acache->stats.queries,
00703 acache->stats.adds, acache->stats.deleted,
00704 acache->stats.cleaned, acache->stats.cleaner_runs,
00705 acache->stats.overmem, acache->stats.overmem_nocreates,
00706 acache->stats.nomem);
00707 reset_stats(acache);
00708
00709 isc_stdtime_get(&cleaner->last_cleanup_time);
00710
00711 UNLOCK(&acache->lock);
00712
00713 dns_acache_setcleaninginterval(cleaner->acache,
00714 cleaner->cleaning_interval);
00715
00716 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE,
00717 ISC_LOG_DEBUG(1), "end acache cleaning, "
00718 "%lu entries cleaned, mem inuse %lu",
00719 cleaner->ncleaned,
00720 (unsigned long)isc_mem_inuse(cleaner->acache->mctx));
00721
00722 if (cleaner->overmem) {
00723 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
00724 DNS_LOGMODULE_ACACHE, ISC_LOG_NOTICE,
00725 "acache is still in overmem state "
00726 "after cleaning");
00727 }
00728
00729 cleaner->ncleaned = 0;
00730 cleaner->state = cleaner_s_idle;
00731 cleaner->resched_event = event;
00732 }
00733
00734
00735
00736
00737
00738 static void
00739 acache_cleaning_timer_action(isc_task_t *task, isc_event_t *event) {
00740 acache_cleaner_t *cleaner = event->ev_arg;
00741
00742 UNUSED(task);
00743
00744 INSIST(event->ev_type == ISC_TIMEREVENT_TICK);
00745
00746 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE,
00747 ISC_LOG_DEBUG(1), "acache cleaning timer fired, "
00748 "cleaner state = %d", cleaner->state);
00749
00750 if (cleaner->state == cleaner_s_idle)
00751 begin_cleaning(cleaner);
00752
00753 isc_event_free(&event);
00754 }
00755
00756
00757 static inline isc_boolean_t
00758 entry_stale(acache_cleaner_t *cleaner, dns_acacheentry_t *entry,
00759 isc_stdtime32_t now32, unsigned int interval)
00760 {
00761
00762
00763
00764
00765 if (entry->callback == NULL)
00766 return (ISC_TRUE);
00767
00768 if (interval > cleaner->cleaning_interval)
00769 interval = cleaner->cleaning_interval;
00770
00771 if (entry->lastused + interval < now32)
00772 return (ISC_TRUE);
00773
00774
00775
00776
00777
00778
00779 if (cleaner->overmem) {
00780 unsigned int passed;
00781 isc_uint32_t val;
00782
00783 if (isc_serial_ge(now32, entry->lastused))
00784 passed = now32 - entry->lastused;
00785 else
00786 passed = 0;
00787
00788 if (passed > interval / 2)
00789 return (ISC_TRUE);
00790 isc_random_get(&val);
00791 if (passed > interval / 4)
00792 return (ISC_TF(val % 4 == 0));
00793 return (ISC_TF(val % 8 == 0));
00794 }
00795
00796 return (ISC_FALSE);
00797 }
00798
00799
00800
00801
00802 static void
00803 acache_incremental_cleaning_action(isc_task_t *task, isc_event_t *event) {
00804 acache_cleaner_t *cleaner = event->ev_arg;
00805 dns_acache_t *acache = cleaner->acache;
00806 dns_acacheentry_t *entry, *next = NULL;
00807 int n_entries;
00808 isc_stdtime32_t now32, last32;
00809 isc_stdtime_t now;
00810 unsigned int interval;
00811
00812 INSIST(DNS_ACACHE_VALID(acache));
00813 INSIST(task == acache->task);
00814 INSIST(event->ev_type == DNS_EVENT_ACACHECLEAN);
00815
00816 if (cleaner->state == cleaner_s_done) {
00817 cleaner->state = cleaner_s_busy;
00818 end_cleaning(cleaner, event);
00819 return;
00820 }
00821
00822 INSIST(CLEANER_BUSY(cleaner));
00823
00824 n_entries = cleaner->increment;
00825
00826 isc_stdtime_get(&now);
00827 isc_stdtime_convert32(now, &now32);
00828
00829 LOCK(&acache->lock);
00830
00831 entry = cleaner->current_entry;
00832 isc_stdtime_convert32(cleaner->last_cleanup_time, &last32);
00833 if (isc_serial_ge(now32, last32))
00834 interval = now32 - last32;
00835 else
00836 interval = 0;
00837
00838 while (n_entries-- > 0) {
00839 isc_boolean_t is_stale = ISC_FALSE;
00840
00841 INSIST(entry != NULL);
00842
00843 next = ISC_LIST_NEXT(entry, link);
00844
00845 ACACHE_LOCK(&acache->entrylocks[entry->locknum],
00846 isc_rwlocktype_write);
00847
00848 is_stale = entry_stale(cleaner, entry, now32, interval);
00849 if (is_stale) {
00850 ISC_LIST_UNLINK(acache->entries, entry, link);
00851 unlink_dbentries(acache, entry);
00852 if (entry->callback != NULL)
00853 (entry->callback)(entry, &entry->cbarg);
00854 entry->callback = NULL;
00855
00856 cleaner->ncleaned++;
00857 }
00858
00859 ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
00860 isc_rwlocktype_write);
00861
00862 if (is_stale)
00863 dns_acache_detachentry(&entry);
00864
00865 if (next == NULL) {
00866 if (cleaner->overmem) {
00867 entry = ISC_LIST_HEAD(acache->entries);
00868 if (entry != NULL) {
00869
00870
00871
00872
00873
00874
00875
00876
00877 isc_log_write(dns_lctx,
00878 DNS_LOGCATEGORY_DATABASE,
00879 DNS_LOGMODULE_ACACHE,
00880 ISC_LOG_DEBUG(1),
00881 "acache cleaner: "
00882 "still overmem, "
00883 "reset and try again");
00884 next = entry;
00885 continue;
00886 }
00887 }
00888
00889 UNLOCK(&acache->lock);
00890 end_cleaning(cleaner, event);
00891 return;
00892 }
00893
00894 entry = next;
00895 }
00896
00897
00898
00899
00900
00901
00902
00903 INSIST(next != NULL);
00904 dns_acache_detachentry(&cleaner->current_entry);
00905 dns_acache_attachentry(next, &cleaner->current_entry);
00906
00907 UNLOCK(&acache->lock);
00908
00909 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE,
00910 ISC_LOG_DEBUG(1), "acache cleaner: checked %d entries, "
00911 "mem inuse %lu, sleeping", cleaner->increment,
00912 (unsigned long)isc_mem_inuse(cleaner->acache->mctx));
00913
00914 isc_task_send(task, &event);
00915 INSIST(CLEANER_BUSY(cleaner));
00916
00917 return;
00918 }
00919
00920
00921
00922
00923
00924 static void
00925 acache_overmem_cleaning_action(isc_task_t *task, isc_event_t *event) {
00926 acache_cleaner_t *cleaner = event->ev_arg;
00927 isc_boolean_t want_cleaning = ISC_FALSE;
00928
00929 UNUSED(task);
00930
00931 INSIST(event->ev_type == DNS_EVENT_ACACHEOVERMEM);
00932 INSIST(cleaner->overmem_event == NULL);
00933
00934 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE,
00935 ISC_LOG_DEBUG(1), "overmem_cleaning_action called, "
00936 "overmem = %d, state = %d", cleaner->overmem,
00937 cleaner->state);
00938
00939 LOCK(&cleaner->lock);
00940
00941 if (cleaner->overmem) {
00942 if (cleaner->state == cleaner_s_idle)
00943 want_cleaning = ISC_TRUE;
00944 } else {
00945 if (cleaner->state == cleaner_s_busy)
00946
00947
00948
00949
00950
00951
00952
00953
00954 cleaner->state = cleaner_s_done;
00955 }
00956
00957 cleaner->overmem_event = event;
00958
00959 UNLOCK(&cleaner->lock);
00960
00961 if (want_cleaning)
00962 begin_cleaning(cleaner);
00963 }
00964
00965 static void
00966 water(void *arg, int mark) {
00967 dns_acache_t *acache = arg;
00968 isc_boolean_t overmem = ISC_TF(mark == ISC_MEM_HIWATER);
00969
00970 REQUIRE(DNS_ACACHE_VALID(acache));
00971
00972 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
00973 DNS_LOGMODULE_ACACHE, ISC_LOG_DEBUG(1),
00974 "acache memory reaches %s watermark, mem inuse %lu",
00975 overmem ? "high" : "low",
00976 (unsigned long)isc_mem_inuse(acache->mctx));
00977
00978 LOCK(&acache->cleaner.lock);
00979
00980 if (acache->cleaner.overmem != overmem) {
00981 acache->cleaner.overmem = overmem;
00982
00983 if (acache->cleaner.overmem_event != NULL)
00984 isc_task_send(acache->task,
00985 &acache->cleaner.overmem_event);
00986 isc_mem_waterack(acache->mctx, mark);
00987 }
00988
00989 UNLOCK(&acache->cleaner.lock);
00990 }
00991
00992
00993
00994
00995 static void
00996 acache_cleaner_shutdown_action(isc_task_t *task, isc_event_t *event) {
00997 dns_acache_t *acache = event->ev_arg;
00998 isc_boolean_t should_free = ISC_FALSE;
00999
01000 INSIST(task == acache->task);
01001 INSIST(event->ev_type == ISC_TASKEVENT_SHUTDOWN);
01002 INSIST(DNS_ACACHE_VALID(acache));
01003
01004 ATRACE("acache cleaner shutdown");
01005
01006 if (CLEANER_BUSY(&acache->cleaner))
01007 end_cleaning(&acache->cleaner, event);
01008 else
01009 isc_event_free(&event);
01010
01011 LOCK(&acache->lock);
01012
01013 acache->live_cleaners--;
01014 INSIST(acache->live_cleaners == 0);
01015
01016 if (isc_refcount_current(&acache->refs) == 0) {
01017 INSIST(check_noentry(acache) == ISC_TRUE);
01018 should_free = ISC_TRUE;
01019 }
01020
01021
01022
01023
01024
01025
01026 if (acache->cleaner.cleaning_timer != NULL)
01027 isc_timer_detach(&acache->cleaner.cleaning_timer);
01028
01029
01030 (void)isc_task_purge(task, NULL, DNS_EVENT_ACACHECLEAN, NULL);
01031
01032 UNLOCK(&acache->lock);
01033
01034 if (should_free)
01035 destroy(acache);
01036 }
01037
01038
01039
01040
01041
01042 isc_result_t
01043 dns_acache_create(dns_acache_t **acachep, isc_mem_t *mctx,
01044 isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr)
01045 {
01046 int i;
01047 isc_result_t result;
01048 dns_acache_t *acache;
01049
01050 REQUIRE(acachep != NULL && *acachep == NULL);
01051 REQUIRE(mctx != NULL);
01052 REQUIRE(taskmgr != NULL);
01053
01054 acache = isc_mem_get(mctx, sizeof(*acache));
01055 if (acache == NULL)
01056 return (ISC_R_NOMEMORY);
01057
01058 ATRACE("create");
01059
01060 result = isc_refcount_init(&acache->refs, 1);
01061 if (result != ISC_R_SUCCESS) {
01062 isc_mem_put(mctx, acache, sizeof(*acache));
01063 return (result);
01064 }
01065
01066 result = isc_mutex_init(&acache->lock);
01067 if (result != ISC_R_SUCCESS) {
01068 isc_refcount_decrement(&acache->refs, NULL);
01069 isc_refcount_destroy(&acache->refs);
01070 isc_mem_put(mctx, acache, sizeof(*acache));
01071 return (result);
01072 }
01073
01074 acache->mctx = NULL;
01075 isc_mem_attach(mctx, &acache->mctx);
01076 ISC_LIST_INIT(acache->entries);
01077
01078 acache->shutting_down = ISC_FALSE;
01079
01080 acache->task = NULL;
01081 acache->entrylocks = NULL;
01082
01083 result = isc_task_create(taskmgr, 1, &acache->task);
01084 if (result != ISC_R_SUCCESS) {
01085 UNEXPECTED_ERROR(__FILE__, __LINE__,
01086 "isc_task_create() failed(): %s",
01087 dns_result_totext(result));
01088 result = ISC_R_UNEXPECTED;
01089 goto cleanup;
01090 }
01091 isc_task_setname(acache->task, "acachetask", acache);
01092 ISC_EVENT_INIT(&acache->cevent, sizeof(acache->cevent), 0, NULL,
01093 DNS_EVENT_ACACHECONTROL, shutdown_task, NULL,
01094 NULL, NULL, NULL);
01095 acache->cevent_sent = ISC_FALSE;
01096
01097 acache->dbentries = 0;
01098 for (i = 0; i < DBBUCKETS; i++)
01099 ISC_LIST_INIT(acache->dbbucket[i]);
01100
01101 acache->entrylocks = isc_mem_get(mctx, sizeof(*acache->entrylocks) *
01102 DEFAULT_ACACHE_ENTRY_LOCK_COUNT);
01103 if (acache->entrylocks == NULL) {
01104 result = ISC_R_NOMEMORY;
01105 goto cleanup;
01106 }
01107 for (i = 0; i < DEFAULT_ACACHE_ENTRY_LOCK_COUNT; i++) {
01108 result = ACACHE_INITLOCK(&acache->entrylocks[i]);
01109 if (result != ISC_R_SUCCESS) {
01110 while (i-- > 0)
01111 ACACHE_DESTROYLOCK(&acache->entrylocks[i]);
01112 isc_mem_put(mctx, acache->entrylocks,
01113 sizeof(*acache->entrylocks) *
01114 DEFAULT_ACACHE_ENTRY_LOCK_COUNT);
01115 acache->entrylocks = NULL;
01116 goto cleanup;
01117 }
01118 }
01119
01120 acache->live_cleaners = 0;
01121 result = acache_cleaner_init(acache, timermgr, &acache->cleaner);
01122 if (result != ISC_R_SUCCESS)
01123 goto cleanup;
01124
01125 acache->stats.cleaner_runs = 0;
01126 reset_stats(acache);
01127
01128 acache->magic = ACACHE_MAGIC;
01129
01130 *acachep = acache;
01131 return (ISC_R_SUCCESS);
01132
01133 cleanup:
01134 if (acache->task != NULL)
01135 isc_task_detach(&acache->task);
01136 DESTROYLOCK(&acache->lock);
01137 isc_refcount_decrement(&acache->refs, NULL);
01138 isc_refcount_destroy(&acache->refs);
01139 if (acache->entrylocks != NULL) {
01140 for (i = 0; i < DEFAULT_ACACHE_ENTRY_LOCK_COUNT; i++)
01141 ACACHE_DESTROYLOCK(&acache->entrylocks[i]);
01142 isc_mem_put(mctx, acache->entrylocks,
01143 sizeof(*acache->entrylocks) *
01144 DEFAULT_ACACHE_ENTRY_LOCK_COUNT);
01145 }
01146 isc_mem_put(mctx, acache, sizeof(*acache));
01147 isc_mem_detach(&mctx);
01148
01149 return (result);
01150 }
01151
01152 void
01153 dns_acache_attach(dns_acache_t *source, dns_acache_t **targetp) {
01154 REQUIRE(DNS_ACACHE_VALID(source));
01155 REQUIRE(targetp != NULL && *targetp == NULL);
01156
01157 AATRACE(source, "attach");
01158
01159 isc_refcount_increment(&source->refs, NULL);
01160
01161 *targetp = source;
01162 }
01163
01164 void
01165 dns_acache_countquerymiss(dns_acache_t *acache) {
01166 acache->stats.misses++;
01167 acache->stats.queries++;
01168 }
01169
01170 void
01171 dns_acache_detach(dns_acache_t **acachep) {
01172 dns_acache_t *acache;
01173 unsigned int refs;
01174 isc_boolean_t should_free = ISC_FALSE;
01175
01176 REQUIRE(acachep != NULL && DNS_ACACHE_VALID(*acachep));
01177 acache = *acachep;
01178
01179 ATRACE("detach");
01180
01181 isc_refcount_decrement(&acache->refs, &refs);
01182 if (refs == 0) {
01183 INSIST(check_noentry(acache) == ISC_TRUE);
01184 should_free = ISC_TRUE;
01185 }
01186
01187 *acachep = NULL;
01188
01189
01190
01191
01192 if (should_free && acache->live_cleaners > 0) {
01193 isc_task_shutdown(acache->task);
01194 should_free = ISC_FALSE;
01195 }
01196
01197 if (should_free)
01198 destroy(acache);
01199 }
01200
01201 void
01202 dns_acache_shutdown(dns_acache_t *acache) {
01203 REQUIRE(DNS_ACACHE_VALID(acache));
01204
01205 LOCK(&acache->lock);
01206
01207 ATRACE("shutdown");
01208
01209 if (!acache->shutting_down) {
01210 isc_event_t *event;
01211 dns_acache_t *acache_evarg = NULL;
01212
01213 INSIST(!acache->cevent_sent);
01214
01215 acache->shutting_down = ISC_TRUE;
01216
01217 isc_mem_setwater(acache->mctx, NULL, NULL, 0, 0);
01218
01219
01220
01221
01222
01223 dns_acache_attach(acache, &acache_evarg);
01224 event = &acache->cevent;
01225 event->ev_arg = acache_evarg;
01226 isc_task_send(acache->task, &event);
01227 acache->cevent_sent = ISC_TRUE;
01228 }
01229
01230 UNLOCK(&acache->lock);
01231 }
01232
01233 isc_result_t
01234 dns_acache_setdb(dns_acache_t *acache, dns_db_t *db) {
01235 int bucket;
01236 dbentry_t *dbentry;
01237 isc_result_t result = ISC_R_SUCCESS;
01238
01239 REQUIRE(DNS_ACACHE_VALID(acache));
01240 REQUIRE(db != NULL);
01241
01242 ATRACE("setdb");
01243
01244 LOCK(&acache->lock);
01245
01246 dbentry = NULL;
01247 result = finddbent(acache, db, &dbentry);
01248 if (result == ISC_R_SUCCESS) {
01249 result = ISC_R_EXISTS;
01250 goto end;
01251 }
01252 result = ISC_R_SUCCESS;
01253
01254 dbentry = isc_mem_get(acache->mctx, sizeof(*dbentry));
01255 if (dbentry == NULL) {
01256 result = ISC_R_NOMEMORY;
01257 goto end;
01258 }
01259
01260 ISC_LINK_INIT(dbentry, link);
01261 ISC_LIST_INIT(dbentry->originlist);
01262 ISC_LIST_INIT(dbentry->referlist);
01263
01264 dbentry->db = NULL;
01265 dns_db_attach(db, &dbentry->db);
01266
01267 bucket = isc_hash_calc((const unsigned char *)&db,
01268 sizeof(db), ISC_TRUE) % DBBUCKETS;
01269
01270 ISC_LIST_APPEND(acache->dbbucket[bucket], dbentry, link);
01271
01272 acache->dbentries++;
01273
01274 end:
01275 UNLOCK(&acache->lock);
01276
01277 return (result);
01278 }
01279
01280 isc_result_t
01281 dns_acache_putdb(dns_acache_t *acache, dns_db_t *db) {
01282 int bucket;
01283 isc_result_t result;
01284 dbentry_t *dbentry;
01285 dns_acacheentry_t *entry;
01286
01287 REQUIRE(DNS_ACACHE_VALID(acache));
01288 REQUIRE(db != NULL);
01289
01290 ATRACE("putdb");
01291
01292 LOCK(&acache->lock);
01293
01294 dbentry = NULL;
01295 result = finddbent(acache, db, &dbentry);
01296 if (result != ISC_R_SUCCESS) {
01297
01298
01299
01300 UNLOCK(&acache->lock);
01301 return (ISC_R_NOTFOUND);
01302 }
01303
01304
01305
01306
01307
01308
01309
01310 while ((entry = ISC_LIST_HEAD(dbentry->originlist)) != NULL) {
01311 ACACHE_LOCK(&acache->entrylocks[entry->locknum],
01312 isc_rwlocktype_write);
01313
01314
01315
01316
01317
01318 ISC_LIST_UNLINK(dbentry->originlist, entry, olink);
01319 if (acache->cleaner.current_entry != entry)
01320 ISC_LIST_UNLINK(acache->entries, entry, link);
01321 unlink_dbentries(acache, entry);
01322
01323 if (entry->callback != NULL)
01324 (entry->callback)(entry, &entry->cbarg);
01325 entry->callback = NULL;
01326
01327 ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
01328 isc_rwlocktype_write);
01329
01330 if (acache->cleaner.current_entry != entry)
01331 dns_acache_detachentry(&entry);
01332 }
01333 while ((entry = ISC_LIST_HEAD(dbentry->referlist)) != NULL) {
01334 ACACHE_LOCK(&acache->entrylocks[entry->locknum],
01335 isc_rwlocktype_write);
01336
01337 ISC_LIST_UNLINK(dbentry->referlist, entry, rlink);
01338 if (acache->cleaner.current_entry != entry)
01339 ISC_LIST_UNLINK(acache->entries, entry, link);
01340 unlink_dbentries(acache, entry);
01341
01342 if (entry->callback != NULL)
01343 (entry->callback)(entry, &entry->cbarg);
01344 entry->callback = NULL;
01345
01346 ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
01347 isc_rwlocktype_write);
01348
01349 if (acache->cleaner.current_entry != entry)
01350 dns_acache_detachentry(&entry);
01351 }
01352
01353 INSIST(ISC_LIST_EMPTY(dbentry->originlist) &&
01354 ISC_LIST_EMPTY(dbentry->referlist));
01355
01356 bucket = isc_hash_calc((const unsigned char *)&db,
01357 sizeof(db), ISC_TRUE) % DBBUCKETS;
01358 ISC_LIST_UNLINK(acache->dbbucket[bucket], dbentry, link);
01359 dns_db_detach(&dbentry->db);
01360
01361 isc_mem_put(acache->mctx, dbentry, sizeof(*dbentry));
01362
01363 acache->dbentries--;
01364
01365 acache->stats.deleted++;
01366
01367 UNLOCK(&acache->lock);
01368
01369 return (ISC_R_SUCCESS);
01370 }
01371
01372 isc_result_t
01373 dns_acache_createentry(dns_acache_t *acache, dns_db_t *origdb,
01374 void (*callback)(dns_acacheentry_t *, void **),
01375 void *cbarg, dns_acacheentry_t **entryp)
01376 {
01377 dns_acacheentry_t *newentry;
01378 isc_result_t result;
01379 isc_uint32_t r;
01380
01381 REQUIRE(DNS_ACACHE_VALID(acache));
01382 REQUIRE(entryp != NULL && *entryp == NULL);
01383 REQUIRE(origdb != NULL);
01384
01385
01386
01387
01388
01389
01390
01391
01392
01393
01394
01395
01396
01397 if (acache->cleaner.overmem) {
01398 acache->stats.overmem_nocreates++;
01399 return (ISC_R_NORESOURCES);
01400 }
01401
01402 newentry = isc_mem_get(acache->mctx, sizeof(*newentry));
01403 if (newentry == NULL) {
01404 acache->stats.nomem++;
01405 return (ISC_R_NOMEMORY);
01406 }
01407
01408 isc_random_get(&r);
01409 newentry->locknum = r % DEFAULT_ACACHE_ENTRY_LOCK_COUNT;
01410
01411 result = isc_refcount_init(&newentry->references, 1);
01412 if (result != ISC_R_SUCCESS) {
01413 isc_mem_put(acache->mctx, newentry, sizeof(*newentry));
01414 return (result);
01415 };
01416
01417 ISC_LINK_INIT(newentry, link);
01418 ISC_LINK_INIT(newentry, olink);
01419 ISC_LINK_INIT(newentry, rlink);
01420
01421 newentry->acache = NULL;
01422 dns_acache_attach(acache, &newentry->acache);
01423
01424 newentry->zone = NULL;
01425 newentry->db = NULL;
01426 newentry->version = NULL;
01427 newentry->node = NULL;
01428 newentry->foundname = NULL;
01429
01430 newentry->callback = callback;
01431 newentry->cbarg = cbarg;
01432 newentry->origdb = NULL;
01433 dns_db_attach(origdb, &newentry->origdb);
01434
01435 isc_stdtime_get(&newentry->lastused);
01436
01437 newentry->magic = ACACHEENTRY_MAGIC;
01438
01439 *entryp = newentry;
01440
01441 return (ISC_R_SUCCESS);
01442 }
01443
01444 isc_result_t
01445 dns_acache_getentry(dns_acacheentry_t *entry, dns_zone_t **zonep,
01446 dns_db_t **dbp, dns_dbversion_t **versionp,
01447 dns_dbnode_t **nodep, dns_name_t *fname,
01448 dns_message_t *msg, isc_stdtime_t now)
01449 {
01450 isc_result_t result = ISC_R_SUCCESS;
01451 dns_rdataset_t *erdataset;
01452 isc_stdtime32_t now32;
01453 dns_acache_t *acache;
01454 int locknum;
01455
01456 REQUIRE(DNS_ACACHEENTRY_VALID(entry));
01457 REQUIRE(zonep == NULL || *zonep == NULL);
01458 REQUIRE(dbp != NULL && *dbp == NULL);
01459 REQUIRE(versionp != NULL && *versionp == NULL);
01460 REQUIRE(nodep != NULL && *nodep == NULL);
01461 REQUIRE(fname != NULL);
01462 REQUIRE(msg != NULL);
01463 acache = entry->acache;
01464 REQUIRE(DNS_ACACHE_VALID(acache));
01465
01466 locknum = entry->locknum;
01467 ACACHE_LOCK(&acache->entrylocks[locknum], isc_rwlocktype_read);
01468
01469 isc_stdtime_convert32(now, &now32);
01470 acache_storetime(entry, now32);
01471
01472 if (entry->zone != NULL && zonep != NULL)
01473 dns_zone_attach(entry->zone, zonep);
01474
01475 if (entry->db == NULL) {
01476 *dbp = NULL;
01477 *versionp = NULL;
01478 } else {
01479 dns_db_attach(entry->db, dbp);
01480 dns_db_attachversion(entry->db, entry->version, versionp);
01481 }
01482 if (entry->node == NULL)
01483 *nodep = NULL;
01484 else {
01485 dns_db_attachnode(entry->db, entry->node, nodep);
01486
01487 INSIST(entry->foundname != NULL);
01488 dns_name_copy(entry->foundname, fname, NULL);
01489 for (erdataset = ISC_LIST_HEAD(entry->foundname->list);
01490 erdataset != NULL;
01491 erdataset = ISC_LIST_NEXT(erdataset, link)) {
01492 dns_rdataset_t *ardataset;
01493
01494 ardataset = NULL;
01495 result = dns_message_gettemprdataset(msg, &ardataset);
01496 if (result != ISC_R_SUCCESS) {
01497 ACACHE_UNLOCK(&acache->entrylocks[locknum],
01498 isc_rwlocktype_read);
01499 goto fail;
01500 }
01501
01502
01503
01504
01505
01506
01507
01508 dns_rdataset_init(ardataset);
01509 dns_rdataset_clone(erdataset, ardataset);
01510 ISC_LIST_APPEND(fname->list, ardataset, link);
01511 }
01512 }
01513
01514 entry->acache->stats.hits++;
01515 entry->acache->stats.queries++;
01516
01517 ACACHE_UNLOCK(&acache->entrylocks[locknum], isc_rwlocktype_read);
01518
01519 return (result);
01520
01521 fail:
01522 while ((erdataset = ISC_LIST_HEAD(fname->list)) != NULL) {
01523 ISC_LIST_UNLINK(fname->list, erdataset, link);
01524 dns_rdataset_disassociate(erdataset);
01525 dns_message_puttemprdataset(msg, &erdataset);
01526 }
01527 if (*nodep != NULL)
01528 dns_db_detachnode(*dbp, nodep);
01529 if (*versionp != NULL)
01530 dns_db_closeversion(*dbp, versionp, ISC_FALSE);
01531 if (*dbp != NULL)
01532 dns_db_detach(dbp);
01533 if (zonep != NULL && *zonep != NULL)
01534 dns_zone_detach(zonep);
01535
01536 return (result);
01537 }
01538
01539 isc_result_t
01540 dns_acache_setentry(dns_acache_t *acache, dns_acacheentry_t *entry,
01541 dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version,
01542 dns_dbnode_t *node, dns_name_t *fname)
01543 {
01544 isc_result_t result;
01545 dbentry_t *odbent;
01546 dbentry_t *rdbent = NULL;
01547 isc_boolean_t close_version = ISC_FALSE;
01548 dns_acacheentry_t *dummy_entry = NULL;
01549
01550 REQUIRE(DNS_ACACHE_VALID(acache));
01551 REQUIRE(DNS_ACACHEENTRY_VALID(entry));
01552
01553 LOCK(&acache->lock);
01554 ACACHE_LOCK(&acache->entrylocks[entry->locknum], isc_rwlocktype_write);
01555
01556
01557 if (zone != NULL)
01558 dns_zone_attach(zone, &entry->zone);
01559
01560 if (db != NULL)
01561 dns_db_attach(db, &entry->db);
01562
01563
01564
01565
01566 if (version == NULL) {
01567 if (db != NULL) {
01568 dns_db_currentversion(db, &version);
01569 close_version = ISC_TRUE;
01570 }
01571 }
01572 if (version != NULL) {
01573 INSIST(db != NULL);
01574 dns_db_attachversion(db, version, &entry->version);
01575 }
01576 if (close_version)
01577 dns_db_closeversion(db, &version, ISC_FALSE);
01578
01579 if (node != NULL) {
01580 INSIST(db != NULL);
01581 dns_db_attachnode(db, node, &entry->node);
01582 }
01583
01584
01585
01586
01587
01588
01589
01590
01591 if (node != NULL) {
01592 dns_rdataset_t *ardataset, *crdataset;
01593
01594 entry->foundname = isc_mem_get(acache->mctx,
01595 sizeof(*entry->foundname));
01596
01597 if (entry->foundname == NULL) {
01598 result = ISC_R_NOMEMORY;
01599 goto fail;
01600 }
01601 dns_name_init(entry->foundname, NULL);
01602 result = dns_name_dup(fname, acache->mctx,
01603 entry->foundname);
01604 if (result != ISC_R_SUCCESS)
01605 goto fail;
01606
01607 for (ardataset = ISC_LIST_HEAD(fname->list);
01608 ardataset != NULL;
01609 ardataset = ISC_LIST_NEXT(ardataset, link)) {
01610 crdataset = isc_mem_get(acache->mctx,
01611 sizeof(*crdataset));
01612 if (crdataset == NULL) {
01613 result = ISC_R_NOMEMORY;
01614 goto fail;
01615 }
01616
01617 dns_rdataset_init(crdataset);
01618 dns_rdataset_clone(ardataset, crdataset);
01619 ISC_LIST_APPEND(entry->foundname->list, crdataset,
01620 link);
01621 }
01622 }
01623
01624 odbent = NULL;
01625 result = finddbent(acache, entry->origdb, &odbent);
01626 if (result != ISC_R_SUCCESS)
01627 goto fail;
01628 if (db != NULL) {
01629 rdbent = NULL;
01630 result = finddbent(acache, db, &rdbent);
01631 if (result != ISC_R_SUCCESS)
01632 goto fail;
01633 }
01634
01635 ISC_LIST_APPEND(acache->entries, entry, link);
01636 ISC_LIST_APPEND(odbent->originlist, entry, olink);
01637 if (rdbent != NULL)
01638 ISC_LIST_APPEND(rdbent->referlist, entry, rlink);
01639
01640
01641
01642
01643
01644 dns_acache_attachentry(entry, &dummy_entry);
01645
01646 ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
01647 isc_rwlocktype_write);
01648
01649 acache->stats.adds++;
01650 UNLOCK(&acache->lock);
01651
01652 return (ISC_R_SUCCESS);
01653
01654 fail:
01655 clear_entry(acache, entry);
01656
01657 ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
01658 isc_rwlocktype_write);
01659 UNLOCK(&acache->lock);
01660
01661 return (result);
01662 }
01663
01664 isc_boolean_t
01665 dns_acache_cancelentry(dns_acacheentry_t *entry) {
01666 dns_acache_t *acache;
01667 isc_boolean_t callback_active;
01668
01669 REQUIRE(DNS_ACACHEENTRY_VALID(entry));
01670
01671 acache = entry->acache;
01672
01673 INSIST(DNS_ACACHE_VALID(entry->acache));
01674
01675 LOCK(&acache->lock);
01676 ACACHE_LOCK(&acache->entrylocks[entry->locknum], isc_rwlocktype_write);
01677
01678 callback_active = ISC_TF(entry->cbarg != NULL);
01679
01680
01681
01682
01683
01684
01685
01686 unlink_dbentries(acache, entry);
01687 clear_entry(entry->acache, entry);
01688
01689 entry->callback = NULL;
01690 entry->cbarg = NULL;
01691
01692 ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
01693 isc_rwlocktype_write);
01694 UNLOCK(&acache->lock);
01695
01696 return (callback_active);
01697 }
01698
01699 void
01700 dns_acache_attachentry(dns_acacheentry_t *source,
01701 dns_acacheentry_t **targetp)
01702 {
01703 REQUIRE(DNS_ACACHEENTRY_VALID(source));
01704 REQUIRE(targetp != NULL && *targetp == NULL);
01705
01706 isc_refcount_increment(&source->references, NULL);
01707
01708 *targetp = source;
01709 }
01710
01711 void
01712 dns_acache_detachentry(dns_acacheentry_t **entryp) {
01713 dns_acacheentry_t *entry;
01714 unsigned int refs;
01715
01716 REQUIRE(entryp != NULL && DNS_ACACHEENTRY_VALID(*entryp));
01717 entry = *entryp;
01718
01719 isc_refcount_decrement(&entry->references, &refs);
01720
01721
01722
01723
01724
01725 if (refs == 0) {
01726 INSIST(!ISC_LINK_LINKED(entry, link));
01727 (*entryp)->acache->stats.deleted++;
01728 destroy_entry(entry);
01729 }
01730
01731 *entryp = NULL;
01732 }
01733
01734 void
01735 dns_acache_setcleaninginterval(dns_acache_t *acache, unsigned int t) {
01736 isc_interval_t interval;
01737 isc_result_t result;
01738
01739 REQUIRE(DNS_ACACHE_VALID(acache));
01740
01741 ATRACE("dns_acache_setcleaninginterval");
01742
01743 LOCK(&acache->lock);
01744
01745
01746
01747
01748
01749 if (acache->cleaner.cleaning_timer == NULL)
01750 goto unlock;
01751
01752 acache->cleaner.cleaning_interval = t;
01753
01754 if (t == 0) {
01755 result = isc_timer_reset(acache->cleaner.cleaning_timer,
01756 isc_timertype_inactive,
01757 NULL, NULL, ISC_TRUE);
01758 } else {
01759 isc_interval_set(&interval, acache->cleaner.cleaning_interval,
01760 0);
01761 result = isc_timer_reset(acache->cleaner.cleaning_timer,
01762 isc_timertype_ticker,
01763 NULL, &interval, ISC_FALSE);
01764 }
01765 if (result != ISC_R_SUCCESS)
01766 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
01767 DNS_LOGMODULE_ACACHE, ISC_LOG_WARNING,
01768 "could not set acache cleaning interval: %s",
01769 isc_result_totext(result));
01770 else
01771 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
01772 DNS_LOGMODULE_ACACHE, ISC_LOG_NOTICE,
01773 "acache %p cleaning interval set to %d.",
01774 acache, t);
01775
01776 unlock:
01777 UNLOCK(&acache->lock);
01778 }
01779
01780
01781
01782
01783
01784 void
01785 dns_acache_setcachesize(dns_acache_t *acache, size_t size) {
01786 size_t hiwater, lowater;
01787
01788 REQUIRE(DNS_ACACHE_VALID(acache));
01789
01790 if (size != 0U && size < DNS_ACACHE_MINSIZE)
01791 size = DNS_ACACHE_MINSIZE;
01792
01793 hiwater = size - (size >> 3);
01794 lowater = size - (size >> 2);
01795
01796 if (size == 0U || hiwater == 0U || lowater == 0U)
01797 isc_mem_setwater(acache->mctx, water, acache, 0, 0);
01798 else
01799 isc_mem_setwater(acache->mctx, water, acache,
01800 hiwater, lowater);
01801 }