diff options
Diffstat (limited to 'contrib/bind9/lib/dns/acache.c')
-rw-r--r-- | contrib/bind9/lib/dns/acache.c | 1778 |
1 files changed, 0 insertions, 1778 deletions
diff --git a/contrib/bind9/lib/dns/acache.c b/contrib/bind9/lib/dns/acache.c deleted file mode 100644 index 5787a5a..0000000 --- a/contrib/bind9/lib/dns/acache.c +++ /dev/null @@ -1,1778 +0,0 @@ -/* - * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC") - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE - * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -/* $Id: acache.c,v 1.3.2.16 2006/07/19 00:34:56 marka Exp $ */ - -#include <config.h> - -#include <isc/atomic.h> -#include <isc/event.h> -#include <isc/hash.h> -#include <isc/magic.h> -#include <isc/mem.h> -#include <isc/mutex.h> -#include <isc/random.h> -#include <isc/refcount.h> -#include <isc/rwlock.h> -#include <isc/task.h> -#include <isc/time.h> -#include <isc/timer.h> - -#include <dns/acache.h> -#include <dns/db.h> -#include <dns/events.h> -#include <dns/log.h> -#include <dns/message.h> -#include <dns/name.h> -#include <dns/rdataset.h> -#include <dns/result.h> -#include <dns/zone.h> - -#define ACACHE_MAGIC ISC_MAGIC('A', 'C', 'H', 'E') -#define DNS_ACACHE_VALID(acache) ISC_MAGIC_VALID(acache, ACACHE_MAGIC) - -#define ACACHEENTRY_MAGIC ISC_MAGIC('A', 'C', 'E', 'T') -#define DNS_ACACHEENTRY_VALID(entry) ISC_MAGIC_VALID(entry, ACACHEENTRY_MAGIC) - -#define DBBUCKETS 67 - -#if 0 -#define ATRACE(m) isc_log_write(dns_lctx, \ - DNS_LOGCATEGORY_DATABASE, \ - DNS_LOGMODULE_ACACHE, \ - ISC_LOG_DEBUG(3), \ - "acache %p: %s", acache, (m)) -#define AATRACE(a,m) isc_log_write(dns_lctx, \ - DNS_LOGCATEGORY_DATABASE, \ - DNS_LOGMODULE_ACACHE, \ - ISC_LOG_DEBUG(3), \ - "acache %p: %s", (a), (m)) -#else -#define ATRACE(m) -#define AATRACE(a, m) -#endif - -/* - * The following variables control incremental cleaning. - * MINSIZE is how many bytes is the floor for dns_acache_setcachesize(). - * CLEANERINCREMENT is how many entries are examined in one pass. - * (XXX simply derived from definitions in cache.c There may be better - * constants here.) - */ -#define DNS_ACACHE_MINSIZE 2097152 /* Bytes. 2097152 = 2 MB */ -#define DNS_ACACHE_CLEANERINCREMENT 1000 /* Number of entries. */ - -#define DEFAULT_ACACHE_ENTRY_LOCK_COUNT 1009 /*%< Should be prime. */ - -#if defined(ISC_RWLOCK_USEATOMIC) && defined(ISC_PLATFORM_HAVEATOMICSTORE) -#define ACACHE_USE_RWLOCK 1 -#endif - -#ifdef ACACHE_USE_RWLOCK -#define ACACHE_INITLOCK(l) isc_rwlock_init((l), 0, 0) -#define ACACHE_DESTROYLOCK(l) isc_rwlock_destroy(l) -#define ACACHE_LOCK(l, t) RWLOCK((l), (t)) -#define ACACHE_UNLOCK(l, t) RWUNLOCK((l), (t)) - -#define acache_storetime(entry, t) \ - (isc_atomic_store((isc_int32_t *)&(entry)->lastused, (t))) -#else -#define ACACHE_INITLOCK(l) isc_mutex_init(l) -#define ACACHE_DESTROYLOCK(l) DESTROYLOCK(l) -#define ACACHE_LOCK(l, t) LOCK(l) -#define ACACHE_UNLOCK(l, t) UNLOCK(l) - -#define acache_storetime(entry, t) ((entry)->lastused = (t)) -#endif - -/* Locked by acache lock */ -typedef struct dbentry { - ISC_LINK(struct dbentry) link; - - dns_db_t *db; - ISC_LIST(dns_acacheentry_t) originlist; - ISC_LIST(dns_acacheentry_t) referlist; -} dbentry_t; - -typedef ISC_LIST(dbentry_t) dbentrylist_t; - -typedef struct acache_cleaner acache_cleaner_t; - -typedef enum { - cleaner_s_idle, /* Waiting for cleaning-interval to expire. */ - cleaner_s_busy, /* Currently cleaning. */ - cleaner_s_done /* Freed enough memory after being overmem. */ -} cleaner_state_t; - -/* - * Convenience macros for comprehensive assertion checking. - */ -#define CLEANER_IDLE(c) ((c)->state == cleaner_s_idle && \ - (c)->resched_event != NULL) -#define CLEANER_BUSY(c) ((c)->state == cleaner_s_busy && \ - (c)->resched_event == NULL) - -struct acache_cleaner { - isc_mutex_t lock; - /* - * Locks overmem_event, overmem. (See cache.c) - */ - - dns_acache_t *acache; - unsigned int cleaning_interval; /* The cleaning-interval - from named.conf, - in seconds. */ - - isc_stdtime_t last_cleanup_time; /* The time when the last - cleanup task completed */ - - isc_timer_t *cleaning_timer; - isc_event_t *resched_event; /* Sent by cleaner task to - itself to reschedule */ - isc_event_t *overmem_event; - - dns_acacheentry_t *current_entry; /* The bookmark entry to - restart the cleaning. - Locked by acache lock. */ - int increment; /* Number of entries to - clean in one increment */ - - unsigned long ncleaned; /* Number of entries cleaned - up (for logging purposes) */ - cleaner_state_t state; /* Idle/Busy/Done. */ - isc_boolean_t overmem; /* The acache is in an overmem - state. */ -}; - -struct dns_acachestats { - unsigned int hits; - unsigned int queries; - unsigned int misses; - unsigned int adds; - unsigned int deleted; - unsigned int cleaned; - unsigned int cleaner_runs; - unsigned int overmem; - unsigned int overmem_nocreates; - unsigned int nomem; -}; - -/* - * The actual acache object. - */ - -struct dns_acache { - unsigned int magic; - - isc_mem_t *mctx; - isc_refcount_t refs; - -#ifdef ACACHE_USE_RWLOCK - isc_rwlock_t *entrylocks; -#else - isc_mutex_t *entrylocks; -#endif - - isc_mutex_t lock; - - int live_cleaners; - acache_cleaner_t cleaner; - ISC_LIST(dns_acacheentry_t) entries; - unsigned int dbentries; - dbentrylist_t dbbucket[DBBUCKETS]; - - isc_boolean_t shutting_down; - - isc_task_t *task; - isc_event_t cevent; - isc_boolean_t cevent_sent; - - dns_acachestats_t stats; -}; - -struct dns_acacheentry { - unsigned int magic; - - unsigned int locknum; - isc_refcount_t references; - - dns_acache_t *acache; - - /* Data for Management of cache entries */ - ISC_LINK(dns_acacheentry_t) link; - ISC_LINK(dns_acacheentry_t) olink; - ISC_LINK(dns_acacheentry_t) rlink; - - dns_db_t *origdb; /* reference to the DB - holding this entry */ - - /* Cache data */ - dns_zone_t *zone; /* zone this entry - belongs to */ - dns_db_t *db; /* DB this entry belongs to */ - dns_dbversion_t *version; /* the version of the DB */ - dns_dbnode_t *node; /* node this entry - belongs to */ - dns_name_t *foundname; /* corresponding DNS name - and rdataset */ - - /* Callback function and its argument */ - void (*callback)(dns_acacheentry_t *, void **); - void *cbarg; - - /* Timestamp of the last time this entry is referred to */ - isc_stdtime32_t lastused; -}; - -/* - * Internal functions (and prototypes). - */ -static inline isc_boolean_t check_noentry(dns_acache_t *acache); -static void destroy(dns_acache_t *acache); -static void shutdown_entries(dns_acache_t *acache); -static void shutdown_buckets(dns_acache_t *acache); -static void destroy_entry(dns_acacheentry_t *ent); -static inline void unlink_dbentries(dns_acache_t *acache, - dns_acacheentry_t *ent); -static inline isc_result_t finddbent(dns_acache_t *acache, - dns_db_t *db, dbentry_t **dbentryp); -static inline void clear_entry(dns_acache_t *acache, dns_acacheentry_t *entry); -static isc_result_t acache_cleaner_init(dns_acache_t *acache, - isc_timermgr_t *timermgr, - acache_cleaner_t *cleaner); -static void acache_cleaning_timer_action(isc_task_t *task, isc_event_t *event); -static void acache_incremental_cleaning_action(isc_task_t *task, - isc_event_t *event); -static void acache_overmem_cleaning_action(isc_task_t *task, - isc_event_t *event); -static void acache_cleaner_shutdown_action(isc_task_t *task, - isc_event_t *event); - -/* - * acache should be locked. If it is not, the stats can get out of whack, - * which is not a big deal for us since this is for debugging / stats - */ -static void -reset_stats(dns_acache_t *acache) { - acache->stats.hits = 0; - acache->stats.queries = 0; - acache->stats.misses = 0; - acache->stats.adds = 0; - acache->stats.deleted = 0; - acache->stats.cleaned = 0; - acache->stats.overmem = 0; - acache->stats.overmem_nocreates = 0; - acache->stats.nomem = 0; -} - -/* - * The acache must be locked before calling. - */ -static inline isc_boolean_t -check_noentry(dns_acache_t *acache) { - if (ISC_LIST_EMPTY(acache->entries) && acache->dbentries == 0) { - return (ISC_TRUE); - } - - return (ISC_FALSE); -} - -/* - * The acache must be locked before calling. - */ -static void -shutdown_entries(dns_acache_t *acache) { - dns_acacheentry_t *entry, *entry_next; - - REQUIRE(DNS_ACACHE_VALID(acache)); - INSIST(acache->shutting_down); - - /* - * Release the dependency of all entries, and detach them. - */ - for (entry = ISC_LIST_HEAD(acache->entries); - entry != NULL; - entry = entry_next) { - entry_next = ISC_LIST_NEXT(entry, link); - - ACACHE_LOCK(&acache->entrylocks[entry->locknum], - isc_rwlocktype_write); - - /* - * If the cleaner holds this entry, it will be unlinked and - * freed in the cleaner later. - */ - if (acache->cleaner.current_entry != entry) - ISC_LIST_UNLINK(acache->entries, entry, link); - unlink_dbentries(acache, entry); - if (entry->callback != NULL) { - (entry->callback)(entry, &entry->cbarg); - entry->callback = NULL; - } - - ACACHE_UNLOCK(&acache->entrylocks[entry->locknum], - isc_rwlocktype_write); - - if (acache->cleaner.current_entry != entry) - dns_acache_detachentry(&entry); - } -} - -/* - * The acache must be locked before calling. - */ -static void -shutdown_buckets(dns_acache_t *acache) { - int i; - dbentry_t *dbent; - - REQUIRE(DNS_ACACHE_VALID(acache)); - INSIST(acache->shutting_down); - - for (i = 0; i < DBBUCKETS; i++) { - while ((dbent = ISC_LIST_HEAD(acache->dbbucket[i])) != NULL) { - INSIST(ISC_LIST_EMPTY(dbent->originlist) && - ISC_LIST_EMPTY(dbent->referlist)); - ISC_LIST_UNLINK(acache->dbbucket[i], dbent, link); - - dns_db_detach(&dbent->db); - - isc_mem_put(acache->mctx, dbent, sizeof(*dbent)); - - acache->dbentries--; - } - } - - INSIST(acache->dbentries == 0); -} - -static void -shutdown_task(isc_task_t *task, isc_event_t *ev) { - dns_acache_t *acache; - - UNUSED(task); - - acache = ev->ev_arg; - INSIST(DNS_ACACHE_VALID(acache)); - - isc_event_free(&ev); - - LOCK(&acache->lock); - - shutdown_entries(acache); - shutdown_buckets(acache); - - UNLOCK(&acache->lock); - - dns_acache_detach(&acache); -} - -/* The acache and the entry must be locked before calling. */ -static inline void -unlink_dbentries(dns_acache_t *acache, dns_acacheentry_t *ent) { - isc_result_t result; - dbentry_t *dbent; - - if (ISC_LINK_LINKED(ent, olink)) { - INSIST(ent->origdb != NULL); - dbent = NULL; - result = finddbent(acache, ent->origdb, &dbent); - INSIST(result == ISC_R_SUCCESS); - - ISC_LIST_UNLINK(dbent->originlist, ent, olink); - } - if (ISC_LINK_LINKED(ent, rlink)) { - INSIST(ent->db != NULL); - dbent = NULL; - result = finddbent(acache, ent->db, &dbent); - INSIST(result == ISC_R_SUCCESS); - - ISC_LIST_UNLINK(dbent->referlist, ent, rlink); - } -} - -/* There must not be a reference to this entry. */ -static void -destroy_entry(dns_acacheentry_t *entry) { - dns_acache_t *acache; - - REQUIRE(DNS_ACACHEENTRY_VALID(entry)); - - acache = entry->acache; - REQUIRE(DNS_ACACHE_VALID(acache)); - - /* - * Since there is no reference to this entry, it is safe to call - * clear_entry() here. - */ - clear_entry(acache, entry); - - isc_mem_put(acache->mctx, entry, sizeof(*entry)); - - dns_acache_detach(&acache); -} - -static void -destroy(dns_acache_t *acache) { - int i; - - REQUIRE(DNS_ACACHE_VALID(acache)); - - ATRACE("destroy"); - - isc_mem_setwater(acache->mctx, NULL, NULL, 0, 0); - - if (acache->cleaner.overmem_event != NULL) - isc_event_free(&acache->cleaner.overmem_event); - - if (acache->cleaner.resched_event != NULL) - isc_event_free(&acache->cleaner.resched_event); - - if (acache->task != NULL) - isc_task_detach(&acache->task); - - 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_t -finddbent(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 void -clear_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_t -acache_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 void -begin_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 void -end_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 void -acache_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_t -entry_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 void -acache_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. - */ - INSIST(next != NULL && next != cleaner->current_entry); - dns_acache_detachentry(&cleaner->current_entry); - dns_acache_attachentry(next, &cleaner->current_entry); - - UNLOCK(&acache->lock); - - isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE, - ISC_LOG_DEBUG(1), "acache cleaner: checked %d entries, " - "mem inuse %lu, sleeping", cleaner->increment, - (unsigned long)isc_mem_inuse(cleaner->acache->mctx)); - - isc_task_send(task, &event); - INSIST(CLEANER_BUSY(cleaner)); - - return; -} - -/* - * This is called when the acache either surpasses its upper limit - * or shrinks beyond its lower limit. - */ -static void -acache_overmem_cleaning_action(isc_task_t *task, isc_event_t *event) { - acache_cleaner_t *cleaner = event->ev_arg; - isc_boolean_t want_cleaning = ISC_FALSE; - - UNUSED(task); - - INSIST(event->ev_type == DNS_EVENT_ACACHEOVERMEM); - INSIST(cleaner->overmem_event == NULL); - - isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE, - ISC_LOG_DEBUG(1), "overmem_cleaning_action called, " - "overmem = %d, state = %d", cleaner->overmem, - cleaner->state); - - LOCK(&cleaner->lock); - - if (cleaner->overmem) { - if (cleaner->state == cleaner_s_idle) - want_cleaning = ISC_TRUE; - } else { - if (cleaner->state == cleaner_s_busy) - /* - * end_cleaning() can't be called here because - * then both cleaner->overmem_event and - * cleaner->resched_event will point to this - * event. Set the state to done, and then - * when the acache_incremental_cleaning_action() event - * is posted, it will handle the end_cleaning. - */ - cleaner->state = cleaner_s_done; - } - - cleaner->overmem_event = event; - - UNLOCK(&cleaner->lock); - - if (want_cleaning) - begin_cleaning(cleaner); -} - -static void -water(void *arg, int mark) { - dns_acache_t *acache = arg; - isc_boolean_t overmem = ISC_TF(mark == ISC_MEM_HIWATER); - - REQUIRE(DNS_ACACHE_VALID(acache)); - - isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, - DNS_LOGMODULE_ACACHE, ISC_LOG_DEBUG(1), - "acache memory reaches %s watermark, mem inuse %lu", - overmem ? "high" : "low", - (unsigned long)isc_mem_inuse(acache->mctx)); - - LOCK(&acache->cleaner.lock); - - acache->cleaner.overmem = overmem; - - if (acache->cleaner.overmem_event != NULL) - isc_task_send(acache->task, &acache->cleaner.overmem_event); - - UNLOCK(&acache->cleaner.lock); -} - -/* - * The cleaner task is shutting down; do the necessary cleanup. - */ -static void -acache_cleaner_shutdown_action(isc_task_t *task, isc_event_t *event) { - dns_acache_t *acache = event->ev_arg; - isc_boolean_t should_free = ISC_FALSE; - - INSIST(task == acache->task); - INSIST(event->ev_type == ISC_TASKEVENT_SHUTDOWN); - INSIST(DNS_ACACHE_VALID(acache)); - - ATRACE("acache cleaner shutdown"); - - if (CLEANER_BUSY(&acache->cleaner)) - end_cleaning(&acache->cleaner, event); - else - isc_event_free(&event); - - LOCK(&acache->lock); - - acache->live_cleaners--; - INSIST(acache->live_cleaners == 0); - - if (isc_refcount_current(&acache->refs) == 0) { - INSIST(check_noentry(acache) == ISC_TRUE); - should_free = ISC_TRUE; - } - - /* - * By detaching the timer in the context of its task, - * we are guaranteed that there will be no further timer - * events. - */ - if (acache->cleaner.cleaning_timer != NULL) - isc_timer_detach(&acache->cleaner.cleaning_timer); - - /* Make sure we don't reschedule anymore. */ - (void)isc_task_purge(task, NULL, DNS_EVENT_ACACHECLEAN, NULL); - - UNLOCK(&acache->lock); - - if (should_free) - destroy(acache); -} - -/* - * Public functions. - */ - -isc_result_t -dns_acache_create(dns_acache_t **acachep, isc_mem_t *mctx, - isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr) -{ - int i; - isc_result_t result; - dns_acache_t *acache; - - REQUIRE(acachep != NULL && *acachep == NULL); - REQUIRE(mctx != NULL); - REQUIRE(taskmgr != NULL); - - acache = isc_mem_get(mctx, sizeof(*acache)); - if (acache == NULL) - return (ISC_R_NOMEMORY); - - ATRACE("create"); - - result = isc_refcount_init(&acache->refs, 1); - if (result != ISC_R_SUCCESS) { - isc_mem_put(mctx, acache, sizeof(*acache)); - return (result); - } - - result = isc_mutex_init(&acache->lock); - if (result != ISC_R_SUCCESS) { - isc_refcount_decrement(&acache->refs, NULL); - isc_refcount_destroy(&acache->refs); - isc_mem_put(mctx, acache, sizeof(*acache)); - return (result); - } - - acache->mctx = NULL; - isc_mem_attach(mctx, &acache->mctx); - ISC_LIST_INIT(acache->entries); - - acache->shutting_down = ISC_FALSE; - - acache->task = NULL; - acache->entrylocks = NULL; - - result = isc_task_create(taskmgr, 1, &acache->task); - if (result != ISC_R_SUCCESS) { - UNEXPECTED_ERROR(__FILE__, __LINE__, - "isc_task_create() failed(): %s", - dns_result_totext(result)); - result = ISC_R_UNEXPECTED; - goto cleanup; - } - isc_task_setname(acache->task, "acachetask", acache); - ISC_EVENT_INIT(&acache->cevent, sizeof(acache->cevent), 0, NULL, - DNS_EVENT_ACACHECONTROL, shutdown_task, NULL, - NULL, NULL, NULL); - acache->cevent_sent = ISC_FALSE; - - acache->dbentries = 0; - for (i = 0; i < DBBUCKETS; i++) - ISC_LIST_INIT(acache->dbbucket[i]); - - acache->entrylocks = isc_mem_get(mctx, sizeof(*acache->entrylocks) * - DEFAULT_ACACHE_ENTRY_LOCK_COUNT); - if (acache->entrylocks == NULL) { - result = ISC_R_NOMEMORY; - goto cleanup; - } - for (i = 0; i < DEFAULT_ACACHE_ENTRY_LOCK_COUNT; i++) { - result = ACACHE_INITLOCK(&acache->entrylocks[i]); - if (result != ISC_R_SUCCESS) { - while (i-- > 0) - ACACHE_DESTROYLOCK(&acache->entrylocks[i]); - isc_mem_put(mctx, acache->entrylocks, - sizeof(*acache->entrylocks) * - DEFAULT_ACACHE_ENTRY_LOCK_COUNT); - acache->entrylocks = NULL; - goto cleanup; - } - } - - acache->live_cleaners = 0; - result = acache_cleaner_init(acache, timermgr, &acache->cleaner); - if (result != ISC_R_SUCCESS) - goto cleanup; - - acache->stats.cleaner_runs = 0; - reset_stats(acache); - - acache->magic = ACACHE_MAGIC; - - *acachep = acache; - return (ISC_R_SUCCESS); - - cleanup: - if (acache->task != NULL) - isc_task_detach(&acache->task); - DESTROYLOCK(&acache->lock); - isc_refcount_decrement(&acache->refs, NULL); - isc_refcount_destroy(&acache->refs); - if (acache->entrylocks != NULL) { - for (i = 0; i < DEFAULT_ACACHE_ENTRY_LOCK_COUNT; i++) - ACACHE_DESTROYLOCK(&acache->entrylocks[i]); - isc_mem_put(mctx, acache->entrylocks, - sizeof(*acache->entrylocks) * - DEFAULT_ACACHE_ENTRY_LOCK_COUNT); - } - isc_mem_put(mctx, acache, sizeof(*acache)); - isc_mem_detach(&mctx); - - return (result); -} - -void -dns_acache_attach(dns_acache_t *source, dns_acache_t **targetp) { - REQUIRE(DNS_ACACHE_VALID(source)); - REQUIRE(targetp != NULL && *targetp == NULL); - - AATRACE(source, "attach"); - - isc_refcount_increment(&source->refs, NULL); - - *targetp = source; -} - -void -dns_acache_countquerymiss(dns_acache_t *acache) { - acache->stats.misses++; /* XXXSK danger: unlocked! */ - acache->stats.queries++; /* XXXSK danger: unlocked! */ -} - -void -dns_acache_detach(dns_acache_t **acachep) { - dns_acache_t *acache; - unsigned int refs; - isc_boolean_t should_free = ISC_FALSE; - - REQUIRE(acachep != NULL && DNS_ACACHE_VALID(*acachep)); - acache = *acachep; - - ATRACE("detach"); - - isc_refcount_decrement(&acache->refs, &refs); - if (refs == 0) { - INSIST(check_noentry(acache) == ISC_TRUE); - should_free = ISC_TRUE; - } - - *acachep = NULL; - - /* - * If we're exiting and the cleaner task exists, let it free the cache. - */ - if (should_free && acache->live_cleaners > 0) { - isc_task_shutdown(acache->task); - should_free = ISC_FALSE; - } - - if (should_free) - destroy(acache); -} - -void -dns_acache_shutdown(dns_acache_t *acache) { - REQUIRE(DNS_ACACHE_VALID(acache)); - - LOCK(&acache->lock); - - ATRACE("shutdown"); - - if (!acache->shutting_down) { - isc_event_t *event; - dns_acache_t *acache_evarg = NULL; - - INSIST(!acache->cevent_sent); - - acache->shutting_down = ISC_TRUE; - - isc_mem_setwater(acache->mctx, NULL, NULL, 0, 0); - - /* - * Self attach the object in order to prevent it from being - * destroyed while waiting for the event. - */ - dns_acache_attach(acache, &acache_evarg); - event = &acache->cevent; - event->ev_arg = acache_evarg; - isc_task_send(acache->task, &event); - acache->cevent_sent = ISC_TRUE; - } - - UNLOCK(&acache->lock); -} - -isc_result_t -dns_acache_setdb(dns_acache_t *acache, dns_db_t *db) { - int bucket; - dbentry_t *dbentry; - isc_result_t result = ISC_R_SUCCESS; - - REQUIRE(DNS_ACACHE_VALID(acache)); - REQUIRE(db != NULL); - - ATRACE("setdb"); - - LOCK(&acache->lock); - - dbentry = NULL; - result = finddbent(acache, db, &dbentry); - if (result == ISC_R_SUCCESS) { - result = ISC_R_EXISTS; - goto end; - } - result = ISC_R_SUCCESS; - - dbentry = isc_mem_get(acache->mctx, sizeof(*dbentry)); - if (dbentry == NULL) { - result = ISC_R_NOMEMORY; - goto end; - } - - ISC_LINK_INIT(dbentry, link); - ISC_LIST_INIT(dbentry->originlist); - ISC_LIST_INIT(dbentry->referlist); - - dbentry->db = NULL; - dns_db_attach(db, &dbentry->db); - - bucket = isc_hash_calc((const unsigned char *)&db, - sizeof(db), ISC_TRUE) % DBBUCKETS; - - ISC_LIST_APPEND(acache->dbbucket[bucket], dbentry, link); - - acache->dbentries++; - - end: - UNLOCK(&acache->lock); - - return (result); -} - -isc_result_t -dns_acache_putdb(dns_acache_t *acache, dns_db_t *db) { - int bucket; - isc_result_t result; - dbentry_t *dbentry; - dns_acacheentry_t *entry; - - REQUIRE(DNS_ACACHE_VALID(acache)); - REQUIRE(db != NULL); - - ATRACE("putdb"); - - LOCK(&acache->lock); - - dbentry = NULL; - result = finddbent(acache, db, &dbentry); - if (result != ISC_R_SUCCESS) { - /* - * The entry may have not been created due to memory shortage. - */ - UNLOCK(&acache->lock); - return (ISC_R_NOTFOUND); - } - - /* - * Release corresponding cache entries: for each entry, release all - * links the entry has, and then callback to the entry holder (if any). - * If no other external references exist (this can happen if the - * original holder has canceled callback,) destroy it here. - */ - while ((entry = ISC_LIST_HEAD(dbentry->originlist)) != NULL) { - ACACHE_LOCK(&acache->entrylocks[entry->locknum], - isc_rwlocktype_write); - - /* - * Releasing olink first would avoid finddbent() in - * unlink_dbentries(). - */ - ISC_LIST_UNLINK(dbentry->originlist, entry, olink); - if (acache->cleaner.current_entry != entry) - ISC_LIST_UNLINK(acache->entries, entry, link); - unlink_dbentries(acache, entry); - - if (entry->callback != NULL) - (entry->callback)(entry, &entry->cbarg); - entry->callback = NULL; - - ACACHE_UNLOCK(&acache->entrylocks[entry->locknum], - isc_rwlocktype_write); - - if (acache->cleaner.current_entry != entry) - dns_acache_detachentry(&entry); - } - while ((entry = ISC_LIST_HEAD(dbentry->referlist)) != NULL) { - ACACHE_LOCK(&acache->entrylocks[entry->locknum], - isc_rwlocktype_write); - - ISC_LIST_UNLINK(dbentry->referlist, entry, rlink); - if (acache->cleaner.current_entry != entry) - ISC_LIST_UNLINK(acache->entries, entry, link); - unlink_dbentries(acache, entry); - - if (entry->callback != NULL) - (entry->callback)(entry, &entry->cbarg); - entry->callback = NULL; - - ACACHE_UNLOCK(&acache->entrylocks[entry->locknum], - isc_rwlocktype_write); - - if (acache->cleaner.current_entry != entry) - dns_acache_detachentry(&entry); - } - - INSIST(ISC_LIST_EMPTY(dbentry->originlist) && - ISC_LIST_EMPTY(dbentry->referlist)); - - bucket = isc_hash_calc((const unsigned char *)&db, - sizeof(db), ISC_TRUE) % DBBUCKETS; - ISC_LIST_UNLINK(acache->dbbucket[bucket], dbentry, link); - dns_db_detach(&dbentry->db); - - isc_mem_put(acache->mctx, dbentry, sizeof(*dbentry)); - - acache->dbentries--; - - acache->stats.deleted++; - - UNLOCK(&acache->lock); - - return (ISC_R_SUCCESS); -} - -isc_result_t -dns_acache_createentry(dns_acache_t *acache, dns_db_t *origdb, - void (*callback)(dns_acacheentry_t *, void **), - void *cbarg, dns_acacheentry_t **entryp) -{ - dns_acacheentry_t *newentry; - isc_result_t result; - isc_uint32_t r; - - REQUIRE(DNS_ACACHE_VALID(acache)); - REQUIRE(entryp != NULL && *entryp == NULL); - REQUIRE(origdb != NULL); - - /* - * Should we exceed our memory limit for some reason (for - * example, if the cleaner does not run aggressively enough), - * then we will not create additional entries. - * - * XXXSK: It might be better to lock the acache->cleaner->lock, - * but locking may be an expensive bottleneck. If we misread - * the value, we will occasionally refuse to create a few - * cache entries, or create a few that we should not. I do not - * expect this to happen often, and it will not have very bad - * effects when it does. So no lock for now. - */ - if (acache->cleaner.overmem) { - acache->stats.overmem_nocreates++; /* XXXSK danger: unlocked! */ - return (ISC_R_NORESOURCES); - } - - newentry = isc_mem_get(acache->mctx, sizeof(*newentry)); - if (newentry == NULL) { - acache->stats.nomem++; /* XXXMLG danger: unlocked! */ - return (ISC_R_NOMEMORY); - } - - isc_random_get(&r); - newentry->locknum = r % DEFAULT_ACACHE_ENTRY_LOCK_COUNT; - - result = isc_refcount_init(&newentry->references, 1); - if (result != ISC_R_SUCCESS) { - isc_mem_put(acache->mctx, newentry, sizeof(*newentry)); - return (result); - }; - - ISC_LINK_INIT(newentry, link); - ISC_LINK_INIT(newentry, olink); - ISC_LINK_INIT(newentry, rlink); - - newentry->acache = NULL; - dns_acache_attach(acache, &newentry->acache); - - newentry->zone = NULL; - newentry->db = NULL; - newentry->version = NULL; - newentry->node = NULL; - newentry->foundname = NULL; - - newentry->callback = callback; - newentry->cbarg = cbarg; - newentry->origdb = NULL; - dns_db_attach(origdb, &newentry->origdb); - - isc_stdtime_get(&newentry->lastused); - - newentry->magic = ACACHEENTRY_MAGIC; - - *entryp = newentry; - - return (ISC_R_SUCCESS); -} - -isc_result_t -dns_acache_getentry(dns_acacheentry_t *entry, dns_zone_t **zonep, - dns_db_t **dbp, dns_dbversion_t **versionp, - dns_dbnode_t **nodep, dns_name_t *fname, - dns_message_t *msg, isc_stdtime_t now) -{ - isc_result_t result = ISC_R_SUCCESS; - dns_rdataset_t *erdataset; - isc_stdtime32_t now32; - dns_acache_t *acache; - int locknum; - - REQUIRE(DNS_ACACHEENTRY_VALID(entry)); - REQUIRE(zonep == NULL || *zonep == NULL); - REQUIRE(dbp != NULL && *dbp == NULL); - REQUIRE(versionp != NULL && *versionp == NULL); - REQUIRE(nodep != NULL && *nodep == NULL); - REQUIRE(fname != NULL); - REQUIRE(msg != NULL); - acache = entry->acache; - REQUIRE(DNS_ACACHE_VALID(acache)); - - locknum = entry->locknum; - ACACHE_LOCK(&acache->entrylocks[locknum], isc_rwlocktype_read); - - isc_stdtime_convert32(now, &now32); - acache_storetime(entry, now32); - - if (entry->zone != NULL && zonep != NULL) - dns_zone_attach(entry->zone, zonep); - - if (entry->db == NULL) { - *dbp = NULL; - *versionp = NULL; - } else { - dns_db_attach(entry->db, dbp); - dns_db_attachversion(entry->db, entry->version, versionp); - } - if (entry->node == NULL) - *nodep = NULL; - else { - dns_db_attachnode(entry->db, entry->node, nodep); - - INSIST(entry->foundname != NULL); - dns_name_copy(entry->foundname, fname, NULL); - for (erdataset = ISC_LIST_HEAD(entry->foundname->list); - erdataset != NULL; - erdataset = ISC_LIST_NEXT(erdataset, link)) { - dns_rdataset_t *ardataset; - - ardataset = NULL; - result = dns_message_gettemprdataset(msg, &ardataset); - if (result != ISC_R_SUCCESS) { - ACACHE_UNLOCK(&acache->entrylocks[locknum], - isc_rwlocktype_read); - goto fail; - } - - /* - * XXXJT: if we simply clone the rdataset, we'll get - * lost wrt cyclic ordering. We'll need an additional - * trick to get the latest counter from the original - * header. - */ - dns_rdataset_init(ardataset); - dns_rdataset_clone(erdataset, ardataset); - ISC_LIST_APPEND(fname->list, ardataset, link); - } - } - - entry->acache->stats.hits++; /* XXXMLG danger: unlocked! */ - entry->acache->stats.queries++; - - ACACHE_UNLOCK(&acache->entrylocks[locknum], isc_rwlocktype_read); - - return (result); - - fail: - while ((erdataset = ISC_LIST_HEAD(fname->list)) != NULL) { - ISC_LIST_UNLINK(fname->list, erdataset, link); - dns_rdataset_disassociate(erdataset); - dns_message_puttemprdataset(msg, &erdataset); - } - if (*nodep != NULL) - dns_db_detachnode(*dbp, nodep); - if (*versionp != NULL) - dns_db_closeversion(*dbp, versionp, ISC_FALSE); - if (*dbp != NULL) - dns_db_detach(dbp); - if (zonep != NULL && *zonep != NULL) - dns_zone_detach(zonep); - - return (result); -} - -isc_result_t -dns_acache_setentry(dns_acache_t *acache, dns_acacheentry_t *entry, - dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version, - dns_dbnode_t *node, dns_name_t *fname) -{ - isc_result_t result; - dbentry_t *odbent; - dbentry_t *rdbent = NULL; - isc_boolean_t close_version = ISC_FALSE; - dns_acacheentry_t *dummy_entry = NULL; - - REQUIRE(DNS_ACACHE_VALID(acache)); - REQUIRE(DNS_ACACHEENTRY_VALID(entry)); - - LOCK(&acache->lock); /* XXX: need to lock it here for ordering */ - ACACHE_LOCK(&acache->entrylocks[entry->locknum], isc_rwlocktype_write); - - /* Set zone */ - if (zone != NULL) - dns_zone_attach(zone, &entry->zone); - /* Set DB */ - if (db != NULL) - dns_db_attach(db, &entry->db); - /* - * Set DB version. If the version is not given by the caller, - * which is the case for glue or cache DBs, use the current version. - */ - if (version == NULL) { - if (db != NULL) { - dns_db_currentversion(db, &version); - close_version = ISC_TRUE; - } - } - if (version != NULL) { - INSIST(db != NULL); - dns_db_attachversion(db, version, &entry->version); - } - if (close_version) - dns_db_closeversion(db, &version, ISC_FALSE); - /* Set DB node. */ - if (node != NULL) { - INSIST(db != NULL); - dns_db_attachnode(db, node, &entry->node); - } - - /* - * Set list of the corresponding rdatasets, if given. - * To minimize the overhead and memory consumption, we'll do this for - * positive cache only, in which case the DB node is non NULL. - * We do not want to cache incomplete information, so give up the - * entire entry when a memory shortage happen during the process. - */ - if (node != NULL) { - dns_rdataset_t *ardataset, *crdataset; - - entry->foundname = isc_mem_get(acache->mctx, - sizeof(*entry->foundname)); - - if (entry->foundname == NULL) { - result = ISC_R_NOMEMORY; - goto fail; - } - dns_name_init(entry->foundname, NULL); - result = dns_name_dup(fname, acache->mctx, - entry->foundname); - if (result != ISC_R_SUCCESS) - goto fail; - - for (ardataset = ISC_LIST_HEAD(fname->list); - ardataset != NULL; - ardataset = ISC_LIST_NEXT(ardataset, link)) { - crdataset = isc_mem_get(acache->mctx, - sizeof(*crdataset)); - if (crdataset == NULL) { - result = ISC_R_NOMEMORY; - goto fail; - } - - dns_rdataset_init(crdataset); - dns_rdataset_clone(ardataset, crdataset); - ISC_LIST_APPEND(entry->foundname->list, crdataset, - link); - } - } - - odbent = NULL; - result = finddbent(acache, entry->origdb, &odbent); - if (result != ISC_R_SUCCESS) - goto fail; - if (db != NULL) { - rdbent = NULL; - result = finddbent(acache, db, &rdbent); - if (result != ISC_R_SUCCESS) - goto fail; - } - - ISC_LIST_APPEND(acache->entries, entry, link); - ISC_LIST_APPEND(odbent->originlist, entry, olink); - if (rdbent != NULL) - ISC_LIST_APPEND(rdbent->referlist, entry, rlink); - - /* - * The additional cache needs an implicit reference to entries in its - * link. - */ - dns_acache_attachentry(entry, &dummy_entry); - - ACACHE_UNLOCK(&acache->entrylocks[entry->locknum], - isc_rwlocktype_write); - - acache->stats.adds++; - UNLOCK(&acache->lock); - - return (ISC_R_SUCCESS); - - fail: - clear_entry(acache, entry); - - ACACHE_UNLOCK(&acache->entrylocks[entry->locknum], - isc_rwlocktype_write); - UNLOCK(&acache->lock); - - return (result); -} - -void -dns_acache_cancelentry(dns_acacheentry_t *entry) { - dns_acache_t *acache = entry->acache; - - REQUIRE(DNS_ACACHEENTRY_VALID(entry)); - INSIST(DNS_ACACHE_VALID(acache)); - - LOCK(&acache->lock); - ACACHE_LOCK(&acache->entrylocks[entry->locknum], isc_rwlocktype_write); - - /* - * Release dependencies stored in this entry as much as possible. - * The main link cannot be released, since the acache object has - * a reference to this entry; the empty entry will be released in - * the next cleaning action. - */ - unlink_dbentries(acache, entry); - clear_entry(entry->acache, entry); - - entry->callback = NULL; - entry->cbarg = NULL; - - ACACHE_UNLOCK(&acache->entrylocks[entry->locknum], - isc_rwlocktype_write); - UNLOCK(&acache->lock); -} - -void -dns_acache_attachentry(dns_acacheentry_t *source, - dns_acacheentry_t **targetp) -{ - REQUIRE(DNS_ACACHEENTRY_VALID(source)); - REQUIRE(targetp != NULL && *targetp == NULL); - - isc_refcount_increment(&source->references, NULL); - - *targetp = source; -} - -void -dns_acache_detachentry(dns_acacheentry_t **entryp) { - dns_acacheentry_t *entry; - unsigned int refs; - - REQUIRE(entryp != NULL && DNS_ACACHEENTRY_VALID(*entryp)); - entry = *entryp; - - isc_refcount_decrement(&entry->references, &refs); - - /* - * If there are no references to the entry, the entry must have been - * unlinked and can be destroyed safely. - */ - if (refs == 0) { - INSIST(!ISC_LINK_LINKED(entry, link)); - (*entryp)->acache->stats.deleted++; - destroy_entry(entry); - } - - *entryp = NULL; -} - -void -dns_acache_setcleaninginterval(dns_acache_t *acache, unsigned int t) { - isc_interval_t interval; - isc_result_t result; - - REQUIRE(DNS_ACACHE_VALID(acache)); - - ATRACE("dns_acache_setcleaninginterval"); - - LOCK(&acache->lock); - - /* - * It may be the case that the acache has already shut down. - * If so, it has no timer. (Not sure if this can really happen.) - */ - if (acache->cleaner.cleaning_timer == NULL) - goto unlock; - - acache->cleaner.cleaning_interval = t; - - if (t == 0) { - result = isc_timer_reset(acache->cleaner.cleaning_timer, - isc_timertype_inactive, - NULL, NULL, ISC_TRUE); - } else { - isc_interval_set(&interval, acache->cleaner.cleaning_interval, - 0); - result = isc_timer_reset(acache->cleaner.cleaning_timer, - isc_timertype_ticker, - NULL, &interval, ISC_FALSE); - } - if (result != ISC_R_SUCCESS) - isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, - DNS_LOGMODULE_ACACHE, ISC_LOG_WARNING, - "could not set acache cleaning interval: %s", - isc_result_totext(result)); - else - isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, - DNS_LOGMODULE_ACACHE, ISC_LOG_NOTICE, - "acache %p cleaning interval set to %d.", - acache, t); - - unlock: - UNLOCK(&acache->lock); -} - -/* - * This function was derived from cache.c:dns_cache_setcachesize(). See the - * function for more details about the logic. - */ -void -dns_acache_setcachesize(dns_acache_t *acache, isc_uint32_t size) { - isc_uint32_t lowater; - isc_uint32_t hiwater; - - REQUIRE(DNS_ACACHE_VALID(acache)); - - if (size != 0 && size < DNS_ACACHE_MINSIZE) - size = DNS_ACACHE_MINSIZE; - - hiwater = size - (size >> 3); - lowater = size - (size >> 2); - - if (size == 0 || hiwater == 0 || lowater == 0) - isc_mem_setwater(acache->mctx, water, acache, 0, 0); - else - isc_mem_setwater(acache->mctx, water, acache, - hiwater, lowater); -} |