📄 acache.c
字号:
for (i = 0; i < DEFAULT_ACACHE_ENTRY_LOCK_COUNT; i++) ACACHE_DESTROYLOCK(&acache->entrylocks[i]); isc_mem_put(acache->mctx, acache->entrylocks, sizeof(*acache->entrylocks) * DEFAULT_ACACHE_ENTRY_LOCK_COUNT); DESTROYLOCK(&acache->cleaner.lock); DESTROYLOCK(&acache->lock); acache->magic = 0; isc_mem_putanddetach(&acache->mctx, acache, sizeof(*acache));}static inline isc_result_tfinddbent(dns_acache_t *acache, dns_db_t *db, dbentry_t **dbentryp) { int bucket; dbentry_t *dbentry; REQUIRE(DNS_ACACHE_VALID(acache)); REQUIRE(db != NULL); REQUIRE(dbentryp != NULL && *dbentryp == NULL); /* * The caller must be holding the acache lock. */ bucket = isc_hash_calc((const unsigned char *)&db, sizeof(db), ISC_TRUE) % DBBUCKETS; for (dbentry = ISC_LIST_HEAD(acache->dbbucket[bucket]); dbentry != NULL; dbentry = ISC_LIST_NEXT(dbentry, link)) { if (dbentry->db == db) break; } *dbentryp = dbentry; if (dbentry == NULL) return (ISC_R_NOTFOUND); else return (ISC_R_SUCCESS);}static inline voidclear_entry(dns_acache_t *acache, dns_acacheentry_t *entry) { REQUIRE(DNS_ACACHE_VALID(acache)); REQUIRE(DNS_ACACHEENTRY_VALID(entry)); /* * The caller must be holing the entry lock. */ if (entry->foundname) { dns_rdataset_t *rdataset, *rdataset_next; for (rdataset = ISC_LIST_HEAD(entry->foundname->list); rdataset != NULL; rdataset = rdataset_next) { rdataset_next = ISC_LIST_NEXT(rdataset, link); ISC_LIST_UNLINK(entry->foundname->list, rdataset, link); dns_rdataset_disassociate(rdataset); isc_mem_put(acache->mctx, rdataset, sizeof(*rdataset)); } if (dns_name_dynamic(entry->foundname)) dns_name_free(entry->foundname, acache->mctx); isc_mem_put(acache->mctx, entry->foundname, sizeof(*entry->foundname)); entry->foundname = NULL; } if (entry->node != NULL) { INSIST(entry->db != NULL); dns_db_detachnode(entry->db, &entry->node); } if (entry->version != NULL) { INSIST(entry->db != NULL); dns_db_closeversion(entry->db, &entry->version, ISC_FALSE); } if (entry->db != NULL) dns_db_detach(&entry->db); if (entry->zone != NULL) dns_zone_detach(&entry->zone); if (entry->origdb != NULL) dns_db_detach(&entry->origdb);}static isc_result_tacache_cleaner_init(dns_acache_t *acache, isc_timermgr_t *timermgr, acache_cleaner_t *cleaner){ int result; ATRACE("acache cleaner init"); result = isc_mutex_init(&cleaner->lock); if (result != ISC_R_SUCCESS) goto fail; cleaner->increment = DNS_ACACHE_CLEANERINCREMENT; cleaner->state = cleaner_s_idle; cleaner->acache = acache; cleaner->overmem = ISC_FALSE; cleaner->cleaning_timer = NULL; cleaner->resched_event = NULL; cleaner->overmem_event = NULL; cleaner->current_entry = NULL; if (timermgr != NULL) { cleaner->acache->live_cleaners++; result = isc_task_onshutdown(acache->task, acache_cleaner_shutdown_action, acache); if (result != ISC_R_SUCCESS) { UNEXPECTED_ERROR(__FILE__, __LINE__, "acache cleaner: " "isc_task_onshutdown() failed: %s", dns_result_totext(result)); goto cleanup; } cleaner->cleaning_interval = 0; /* Initially turned off. */ isc_stdtime_get(&cleaner->last_cleanup_time); result = isc_timer_create(timermgr, isc_timertype_inactive, NULL, NULL, acache->task, acache_cleaning_timer_action, cleaner, &cleaner->cleaning_timer); if (result != ISC_R_SUCCESS) { UNEXPECTED_ERROR(__FILE__, __LINE__, "isc_timer_create() failed: %s", dns_result_totext(result)); result = ISC_R_UNEXPECTED; goto cleanup; } cleaner->resched_event = isc_event_allocate(acache->mctx, cleaner, DNS_EVENT_ACACHECLEAN, acache_incremental_cleaning_action, cleaner, sizeof(isc_event_t)); if (cleaner->resched_event == NULL) { result = ISC_R_NOMEMORY; goto cleanup; } cleaner->overmem_event = isc_event_allocate(acache->mctx, cleaner, DNS_EVENT_ACACHEOVERMEM, acache_overmem_cleaning_action, cleaner, sizeof(isc_event_t)); if (cleaner->overmem_event == NULL) { result = ISC_R_NOMEMORY; goto cleanup; } } return (ISC_R_SUCCESS); cleanup: if (cleaner->overmem_event != NULL) isc_event_free(&cleaner->overmem_event); if (cleaner->resched_event != NULL) isc_event_free(&cleaner->resched_event); if (cleaner->cleaning_timer != NULL) isc_timer_detach(&cleaner->cleaning_timer); cleaner->acache->live_cleaners--; DESTROYLOCK(&cleaner->lock); fail: return (result);}static voidbegin_cleaning(acache_cleaner_t *cleaner) { dns_acacheentry_t *head; dns_acache_t *acache = cleaner->acache; /* * This function does not have to lock the cleaner, since critical * parameters (except current_entry, which is locked by acache lock,) * are only used in a single task context. */ REQUIRE(CLEANER_IDLE(cleaner)); INSIST(DNS_ACACHE_VALID(acache)); INSIST(cleaner->current_entry == NULL); isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE, ISC_LOG_DEBUG(1), "begin acache cleaning, mem inuse %lu", (unsigned long)isc_mem_inuse(cleaner->acache->mctx)); LOCK(&acache->lock); head = ISC_LIST_HEAD(acache->entries); if (head != NULL) dns_acache_attachentry(head, &cleaner->current_entry); UNLOCK(&acache->lock); if (cleaner->current_entry != NULL) { cleaner->ncleaned = 0; cleaner->state = cleaner_s_busy; isc_task_send(acache->task, &cleaner->resched_event); } return;}static voidend_cleaning(acache_cleaner_t *cleaner, isc_event_t *event) { dns_acache_t *acache = cleaner->acache; REQUIRE(CLEANER_BUSY(cleaner)); REQUIRE(event != NULL); REQUIRE(DNS_ACACHEENTRY_VALID(cleaner->current_entry)); /* No need to lock the cleaner (see begin_cleaning()). */ LOCK(&acache->lock); /* * Even if the cleaner has the last reference to the entry, which means * the entry has been unused, it may still be linked if unlinking the * entry has been delayed due to the reference. */ if (isc_refcount_current(&cleaner->current_entry->references) == 1) { INSIST(cleaner->current_entry->callback == NULL); if (ISC_LINK_LINKED(cleaner->current_entry, link)) { ISC_LIST_UNLINK(acache->entries, cleaner->current_entry, link); } } dns_acache_detachentry(&cleaner->current_entry); if (cleaner->overmem) acache->stats.overmem++; acache->stats.cleaned += cleaner->ncleaned; acache->stats.cleaner_runs++; isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE, ISC_LOG_NOTICE, "acache %p stats: hits=%d misses=%d queries=%d " "adds=%d deleted=%d " "cleaned=%d cleaner_runs=%d overmem=%d " "overmem_nocreates=%d nomem=%d", acache, acache->stats.hits, acache->stats.misses, acache->stats.queries, acache->stats.adds, acache->stats.deleted, acache->stats.cleaned, acache->stats.cleaner_runs, acache->stats.overmem, acache->stats.overmem_nocreates, acache->stats.nomem); reset_stats(acache); isc_stdtime_get(&cleaner->last_cleanup_time); UNLOCK(&acache->lock); dns_acache_setcleaninginterval(cleaner->acache, cleaner->cleaning_interval); isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE, ISC_LOG_DEBUG(1), "end acache cleaning, " "%lu entries cleaned, mem inuse %lu", cleaner->ncleaned, (unsigned long)isc_mem_inuse(cleaner->acache->mctx)); if (cleaner->overmem) { isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE, ISC_LOG_NOTICE, "acache is still in overmem state " "after cleaning"); } cleaner->ncleaned = 0; cleaner->state = cleaner_s_idle; cleaner->resched_event = event;}/* * This is run once for every acache-cleaning-interval as defined * in named.conf. */static voidacache_cleaning_timer_action(isc_task_t *task, isc_event_t *event) { acache_cleaner_t *cleaner = event->ev_arg; UNUSED(task); INSIST(event->ev_type == ISC_TIMEREVENT_TICK); isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE, ISC_LOG_DEBUG(1), "acache cleaning timer fired, " "cleaner state = %d", cleaner->state); if (cleaner->state == cleaner_s_idle) begin_cleaning(cleaner); isc_event_free(&event);}/* The caller must hold entry lock. */static inline isc_boolean_tentry_stale(acache_cleaner_t *cleaner, dns_acacheentry_t *entry, isc_stdtime32_t now32, unsigned int interval){ /* * If the callback has been canceled, we definitely do not need the * entry. */ if (entry->callback == NULL) return (ISC_TRUE); if (interval > cleaner->cleaning_interval) interval = cleaner->cleaning_interval; if (entry->lastused + interval < now32) return (ISC_TRUE); /* * If the acache is in the overmem state, probabilistically decide if * the entry should be purged, based on the time passed from its last * use and the cleaning interval. */ if (cleaner->overmem) { unsigned int passed = now32 - entry->lastused; /* <= interval */ isc_uint32_t val; if (passed > interval / 2) return (ISC_TRUE); isc_random_get(&val); if (passed > interval / 4) return (ISC_TF(val % 4 == 0)); return (ISC_TF(val % 8 == 0)); } return (ISC_FALSE);}/* * Do incremental cleaning. */static voidacache_incremental_cleaning_action(isc_task_t *task, isc_event_t *event) { acache_cleaner_t *cleaner = event->ev_arg; dns_acache_t *acache = cleaner->acache; dns_acacheentry_t *entry, *next = NULL; int n_entries; isc_stdtime32_t now32, last32; isc_stdtime_t now; unsigned int interval; INSIST(DNS_ACACHE_VALID(acache)); INSIST(task == acache->task); INSIST(event->ev_type == DNS_EVENT_ACACHECLEAN); if (cleaner->state == cleaner_s_done) { cleaner->state = cleaner_s_busy; end_cleaning(cleaner, event); return; } INSIST(CLEANER_BUSY(cleaner)); n_entries = cleaner->increment; isc_stdtime_get(&now); isc_stdtime_convert32(now, &now32); LOCK(&acache->lock); entry = cleaner->current_entry; isc_stdtime_convert32(cleaner->last_cleanup_time, &last32); INSIST(now32 > last32); interval = now32 - last32; while (n_entries-- > 0) { isc_boolean_t is_stale = ISC_FALSE; INSIST(entry != NULL); next = ISC_LIST_NEXT(entry, link); ACACHE_LOCK(&acache->entrylocks[entry->locknum], isc_rwlocktype_write); is_stale = entry_stale(cleaner, entry, now32, interval); if (is_stale) { ISC_LIST_UNLINK(acache->entries, entry, link); unlink_dbentries(acache, entry); if (entry->callback != NULL) (entry->callback)(entry, &entry->cbarg); entry->callback = NULL; cleaner->ncleaned++; } ACACHE_UNLOCK(&acache->entrylocks[entry->locknum], isc_rwlocktype_write); if (is_stale) dns_acache_detachentry(&entry); if (next == NULL) { if (cleaner->overmem) { entry = ISC_LIST_HEAD(acache->entries); if (entry != NULL) { /* * If we are still in the overmem * state, keep cleaning. */ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE, ISC_LOG_DEBUG(1), "acache cleaner: " "still overmem, " "reset and try again"); continue; } } UNLOCK(&acache->lock); end_cleaning(cleaner, event); return; } entry = next; } /* * We have successfully performed a cleaning increment but have * not gone through the entire cache. Remember the entry that will * be the starting point in the next clean-up, and reschedule another * batch. If it fails, just try to continue anyway. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -