From: Ondřej Surý Date: Thu, 13 Oct 2022 10:00:54 +0000 (+0200) Subject: Refactor the dns_adb unit X-Git-Tag: v9.19.8~34^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=50f357cb36cf9ed01f423bfd4f716e29edeb221b;p=thirdparty%2Fbind9.git Refactor the dns_adb unit The dns_adb unit has been refactored to be much simpler. Following changes have been made: 1. Simplify the ADB to always allow GLUE and hints There were only two places where dns_adb_createfind() was used - in the dns_resolver unit where hints and GLUE addresses were ok, and in the dns_zone where dns_adb_createfind() would be called without DNS_ADBFIND_HINTOK and DNS_ADBFIND_GLUEOK set. Simplify the logic by allowing hint and GLUE addresses when looking up the nameserver addresses to notify. The difference is negligible and would cause a difference in the notified addresses only when there's mismatch between the parent and child addresses and we haven't cached the child addresses yet. 2. Drop the namebuckets and entrybuckets Formerly, the namebuckets and entrybuckets were used to reduced the lock contention when accessing the double-linked lists stored in each bucket. In the previous refactoring, the custom hashtable for the buckets has been replaced with isc_ht/isc_hashmap, so only a single item (mostly, see below) would end up in each bucket. Removing the entrybuckets has been straightforward, the only matching was done on the isc_sockaddr_t member of the dns_adbentry. Removing the zonebuckets required GLUEOK and HINTOK bits to be removed because the find could match entries with-or-without the bits set, and creating a custom key that stores the DNS_ADBFIND_STARTATZONE in the first byte of the key, so we can do a straightforward lookup into the hashtable without traversing a list that contains items with different flags. 3. Remove unassociated entries from ADB database Previously, the adbentries could live in the ADB database even after unlinking them from dns_adbnames. Such entries would show up as "Unassociated entries" in the ADB dump. The benefit of keeping such entries is little - the chance that we link such entry to a adbname is small, and it's simpler to evict unlinked entries from the ADB cache (and the hashtable) than create second LRU cleaning mechanism. Unlinked ADB entries are now directly deleted from the hash table (hashmap) upon destruction. 4. Cleanup expired entries from the hash table When buckets were still in place, the code would keep the buckets always allocated and never shrink the hash table (hashmap). With proper reference counting in place, we can delete the adbnames from the hash table and the LRU list. 5. Stop purging the names early when we hit the time limit Because the LRU list is now time ordered, we can stop purging the names when we find a first entry that doesn't fullfil our time-based eviction criteria because no further entry on the LRU list will meet the criteria. Future work: 1. Lock contention In this commit, the focus was on correctness of the data structure, but in the future, the lock contention in the ADB database needs to be addressed. Currently, we use simple mutex to lock the hash tables, because we almost always need to use a write lock for properly purging the hashtables. The ADB database needs to be sharded (similar to the effect that buckets had in the past). Each shard would contain own hashmap and own LRU list. 2. Time-based purging The ADB names and entries stay intact when there are no lookups. When we add separate shards, a timer needs to be added for time-based cleaning in case there's no traffic hashing to the inactive shard. 3. Revisit the 30 minutes limit The ADB cache is capped at 30 minutes. This needs to be revisited, and at least the limit should be configurable (in both directions). --- diff --git a/bin/tests/system/cacheclean/tests.sh b/bin/tests/system/cacheclean/tests.sh index 3669bba816c..1192dd97d77 100755 --- a/bin/tests/system/cacheclean/tests.sh +++ b/bin/tests/system/cacheclean/tests.sh @@ -234,7 +234,6 @@ mv ns2/named_dump.db.test$n ns2/named_dump.db.test$n.a sed -n '/plain success\/timeout/,/Unassociated entries/p' \ ns2/named_dump.db.test$n.a > sed.out.$n.a grep 'plain success/timeout' sed.out.$n.a > /dev/null 2>&1 || ret=1 -grep 'Unassociated entries' sed.out.$n.a > /dev/null 2>&1 || ret=1 grep 'ns.flushtest.example' sed.out.$n.a > /dev/null 2>&1 || ret=1 $RNDC $RNDCOPTS flushtree flushtest.example || ret=1 dump_cache @@ -242,7 +241,6 @@ mv ns2/named_dump.db.test$n ns2/named_dump.db.test$n.b sed -n '/plain success\/timeout/,/Unassociated entries/p' \ ns2/named_dump.db.test$n.b > sed.out.$n.b grep 'plain success/timeout' sed.out.$n.b > /dev/null 2>&1 || ret=1 -grep 'Unassociated entries' sed.out.$n.b > /dev/null 2>&1 || ret=1 grep 'ns.flushtest.example' sed.out.$n.b > /dev/null 2>&1 && ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=`expr $status + $ret` diff --git a/lib/dns/adb.c b/lib/dns/adb.c index 0f1c7c62e0d..42c87c82b6c 100644 --- a/lib/dns/adb.c +++ b/lib/dns/adb.c @@ -26,8 +26,9 @@ #include #include +#include #include -#include +#include #include #include #include @@ -49,11 +50,6 @@ #include #include -/* Detailed logging of attach/detach */ -#ifndef ADB_TRACE -#undef ADB_TRACE -#endif - #define DNS_ADB_MAGIC ISC_MAGIC('D', 'a', 'd', 'b') #define DNS_ADB_VALID(x) ISC_MAGIC_VALID(x, DNS_ADB_MAGIC) #define DNS_ADBNAME_MAGIC ISC_MAGIC('a', 'd', 'b', 'N') @@ -101,8 +97,6 @@ typedef struct dns_adblameinfo dns_adblameinfo_t; typedef ISC_LIST(dns_adbentry_t) dns_adbentrylist_t; typedef struct dns_adbfetch dns_adbfetch_t; typedef struct dns_adbfetch6 dns_adbfetch6_t; -typedef ISC_LIST(dns_adbnamebucket_t) dns_adbnamebucketlist_t; -typedef ISC_LIST(dns_adbentrybucket_t) dns_adbentrybucketlist_t; /*% dns adb structure */ struct dns_adb { @@ -119,17 +113,18 @@ struct dns_adb { isc_refcount_t references; - dns_adbnamebucketlist_t namebuckets_lru; - isc_hashmap_t *namebuckets; - isc_rwlock_t names_lock; + dns_adbnamelist_t names_lru; + isc_stdtime_t names_last_update; + isc_hashmap_t *names; + isc_mutex_t names_lock; - dns_adbentrybucketlist_t entrybuckets_lru; - isc_hashmap_t *entrybuckets; - isc_rwlock_t entries_lock; + isc_hashmap_t *entries; + isc_mutex_t entries_lock; isc_stats_t *stats; atomic_bool exiting; + atomic_bool is_overmem; uint32_t quota; uint32_t atr_freq; @@ -138,22 +133,36 @@ struct dns_adb { double atr_discount; }; +typedef struct adbnamekey adbnamekey_t; +struct adbnamekey { + size_t size; + union { + struct { + bool start_at_zone; + uint8_t name[DNS_NAME_MAXWIRE]; + }; + char key[sizeof(bool) + DNS_NAME_MAXWIRE]; + }; +} __attribute__((__packed__)); + /*% * dns_adbname structure: * * This is the structure representing a nameserver name; it can be looked - * up via the adb->namebuckets hash table. It holds references to fetches + * up via the adb->names hash table. It holds references to fetches * for A and AAAA records while they are ongoing (fetch_a, fetch_aaaa), and * lists of records pointing to address information when the fetches are * complete (v4, v6). */ struct dns_adbname { unsigned int magic; - dns_name_t name; + isc_refcount_t references; dns_adb_t *adb; + isc_buffer_t buffer; + adbnamekey_t key; + dns_name_t name; unsigned int partial_result; unsigned int flags; - dns_adbnamebucket_t *bucket; dns_name_t target; isc_stdtime_t expire_target; isc_stdtime_t expire_v4; @@ -165,25 +174,25 @@ struct dns_adbname { unsigned int fetch_err; unsigned int fetch6_err; dns_adbfindlist_t finds; - /* for LRU-based management */ + isc_mutex_t lock; isc_stdtime_t last_used; + /* for LRU-based management */ - ISC_LINK(dns_adbname_t) plink; + ISC_LINK(dns_adbname_t) link; }; -/*% - * dns_adbnamebucket structure: - * - * Hash bucket for dns_adbname objects. - */ -struct dns_adbnamebucket { - dns_adbnamelist_t names; - isc_mutex_t lock; - isc_refcount_t references; - ISC_LINK(dns_adbnamebucket_t) link; - size_t keysize; - uint8_t key[]; -}; +#if DNS_ADB_TRACE +#define dns_adbname_ref(ptr) dns_adbname__ref(ptr, __func__, __FILE__, __LINE__) +#define dns_adbname_unref(ptr) \ + dns_adbname__unref(ptr, __func__, __FILE__, __LINE__) +#define dns_adbname_attach(ptr, ptrp) \ + dns_adbname__attach(ptr, ptrp, __func__, __FILE__, __LINE__) +#define dns_adbname_detach(ptrp) \ + dns_adbname__detach(ptrp, __func__, __FILE__, __LINE__) +ISC_REFCOUNT_TRACE_DECL(dns_adbname); +#else +ISC_REFCOUNT_DECL(dns_adbname); +#endif /*% * dns_adbfetch structure: @@ -230,7 +239,7 @@ struct dns_adblameinfo { * dns_adbentry structure: * * This is the structure representing a nameserver address; it can be looked - * up via the adb->entrybuckets hash table. Also, each dns_adbnamehook and + * up via the adb->entries hash table. Also, each dns_adbnamehook and * and dns_adbaddrinfo object will contain a pointer to one of these. * * The structure holds quite a bit of information about addresses, @@ -240,12 +249,13 @@ struct dns_adblameinfo { struct dns_adbentry { unsigned int magic; + isc_mutex_t lock; + isc_stdtime_t last_used; + isc_refcount_t references; dns_adb_t *adb; - dns_adbentrybucket_t *bucket; - unsigned int nh; unsigned int flags; unsigned int srtt; unsigned int completed; @@ -275,46 +285,45 @@ struct dns_adbentry { * entry. */ + /* FIXME */ ISC_LIST(dns_adblameinfo_t) lameinfo; - ISC_LINK(dns_adbentry_t) plink; -}; -/*% dns_adbentrybucket_t structure: - * - * Hash bucket for dns_adbentry objects. - */ -struct dns_adbentrybucket { - dns_adbentrylist_t entries; - isc_mutex_t lock; - isc_refcount_t references; - ISC_LINK(dns_adbentrybucket_t) link; - size_t keysize; - uint8_t key[]; + ISC_LINK(dns_adbentry_t) link; }; +#if DNS_ADB_TRACE +#define dns_adbentry_ref(ptr) \ + dns_adbentry__ref(ptr, __func__, __FILE__, __LINE__) +#define dns_adbentry_unref(ptr) \ + dns_adbentry__unref(ptr, __func__, __FILE__, __LINE__) +#define dns_adbentry_attach(ptr, ptrp) \ + dns_adbentry__attach(ptr, ptrp, __func__, __FILE__, __LINE__) +#define dns_adbentry_detach(ptrp) \ + dns_adbentry__detach(ptrp, __func__, __FILE__, __LINE__) +ISC_REFCOUNT_TRACE_DECL(dns_adbentry); +#else +ISC_REFCOUNT_DECL(dns_adbentry); +#endif + /* * Internal functions (and prototypes). */ static dns_adbname_t * -new_adbname(dns_adb_t *, const dns_name_t *); -static dns_adbnamebucket_t * -new_adbnamebucket(dns_adb_t *, const uint8_t *key, const size_t keysize); +new_adbname(dns_adb_t *adb, const dns_name_t *, bool start_at_zone); static void -free_adbname(dns_adbname_t **); +destroy_adbname(dns_adbname_t *); static dns_adbnamehook_t * -new_adbnamehook(dns_adb_t *, dns_adbentry_t *); +new_adbnamehook(dns_adb_t *adb); static void -free_adbnamehook(dns_adb_t *, dns_adbnamehook_t **); +free_adbnamehook(dns_adb_t *adb, dns_adbnamehook_t **namehookp); static dns_adblameinfo_t * new_adblameinfo(dns_adb_t *, const dns_name_t *, dns_rdatatype_t); static void free_adblameinfo(dns_adb_t *, dns_adblameinfo_t **); static dns_adbentry_t * -new_adbentry(dns_adb_t *); +new_adbentry(dns_adb_t *adb, const isc_sockaddr_t *addr); static void -free_adbentry(dns_adbentry_t **); -static dns_adbentrybucket_t * -new_adbentrybucket(dns_adb_t *adb, const uint8_t *key, const size_t keysize); +destroy_adbentry(dns_adbentry_t *entry); static dns_adbfind_t * new_adbfind(dns_adb_t *, in_port_t); static void @@ -328,14 +337,10 @@ free_adbfetch(dns_adb_t *, dns_adbfetch_t **); static void purge_stale_names(dns_adb_t *adb, isc_stdtime_t now); static void -get_namebucket(dns_adb_t *, const dns_name_t *, isc_stdtime_t now, - dns_adbnamebucket_t **); -static dns_adbname_t * -get_name(dns_adbnamebucket_t *, const dns_name_t *, unsigned int); -static void -get_entrybucket(dns_adb_t *, const isc_sockaddr_t *, dns_adbentrybucket_t **); +get_attached_name(dns_adb_t *, const dns_name_t *, bool start_at_zone, + isc_stdtime_t now, dns_adbname_t **adbnamep); static dns_adbentry_t * -get_entry(dns_adbentrybucket_t *, const isc_sockaddr_t *, isc_stdtime_t); +get_attached_entry(dns_adb_t *adb, const isc_sockaddr_t *addr); static void dump_adb(dns_adb_t *, FILE *, bool debug, isc_stdtime_t); static void @@ -353,8 +358,6 @@ static void clean_finds_at_name(dns_adbname_t *, isc_eventtype_t, unsigned int); static void maybe_expire_namehooks(dns_adbname_t *, isc_stdtime_t); -static void -maybe_expire_entry(dns_adbentry_t **, isc_stdtime_t); static isc_result_t dbfind_name(dns_adbname_t *, isc_stdtime_t, dns_rdatatype_t); static isc_result_t @@ -365,17 +368,7 @@ destroy(dns_adb_t *); static void shutdown_names(dns_adb_t *); static void -shutdown_entries(dns_adb_t *); -static void -link_name(dns_adbnamebucket_t *, dns_adbname_t *); -static void -unlink_name(dns_adbname_t *); -static void -link_entry(dns_adbentrybucket_t *, dns_adbentry_t *); -static void -unlink_entry(dns_adbentry_t *); -static void -expire_name(dns_adbname_t **, isc_eventtype_t); +expire_name(dns_adbname_t *adbname, isc_eventtype_t); static void water(void *, int); static void @@ -395,12 +388,8 @@ log_quota(dns_adbentry_t *entry, const char *fmt, ...) ISC_FORMAT_PRINTF(2, 3); #define FIND_EVENTFREED(h) (((h)->flags & FIND_EVENT_FREED) != 0) #define NAME_IS_DEAD 0x40000000 -#define NAME_HINT_OK DNS_ADBFIND_HINTOK -#define NAME_GLUE_OK DNS_ADBFIND_GLUEOK #define NAME_STARTATZONE DNS_ADBFIND_STARTATZONE #define NAME_DEAD(n) (((n)->flags & NAME_IS_DEAD) != 0) -#define NAME_GLUEOK(n) (((n)->flags & NAME_GLUE_OK) != 0) -#define NAME_HINTOK(n) (((n)->flags & NAME_HINT_OK) != 0) /* * To the name, address classes are all that really exist. If it has a @@ -425,8 +414,6 @@ log_quota(dns_adbentry_t *entry, const char *fmt, ...) ISC_FORMAT_PRINTF(2, 3); #define FIND_WANTEMPTYEVENT(fn) (((fn)->options & DNS_ADBFIND_EMPTYEVENT) != 0) #define FIND_AVOIDFETCHES(fn) (((fn)->options & DNS_ADBFIND_AVOIDFETCHES) != 0) #define FIND_STARTATZONE(fn) (((fn)->options & DNS_ADBFIND_STARTATZONE) != 0) -#define FIND_HINTOK(fn) (((fn)->options & DNS_ADBFIND_HINTOK) != 0) -#define FIND_GLUEOK(fn) (((fn)->options & DNS_ADBFIND_GLUEOK) != 0) #define FIND_HAS_ADDRS(fn) (!ISC_LIST_EMPTY((fn)->list)) #define FIND_RETURNLAME(fn) (((fn)->options & DNS_ADBFIND_RETURNLAME) != 0) #define FIND_NOFETCH(fn) (((fn)->options & DNS_ADBFIND_NOFETCH) != 0) @@ -445,9 +432,6 @@ log_quota(dns_adbentry_t *entry, const char *fmt, ...) ISC_FORMAT_PRINTF(2, 3); * glue, and compare this to the appropriate bits set in o, to see if * this is ok. */ -#define GLUE_OK(nf, o) (!NAME_GLUEOK(nf) || (((o)&DNS_ADBFIND_GLUEOK) != 0)) -#define HINT_OK(nf, o) (!NAME_HINTOK(nf) || (((o)&DNS_ADBFIND_HINTOK) != 0)) -#define GLUEHINT_OK(nf, o) (GLUE_OK(nf, o) || HINT_OK(nf, o)) #define STARTATZONE_MATCHES(nf, o) \ (((nf)->flags & NAME_STARTATZONE) == ((o)&DNS_ADBFIND_STARTATZONE)) @@ -541,64 +525,8 @@ ttlclamp(dns_ttl_t ttl) { return (ttl); } -#define entry_attach(s, t) _entry_attach(s, t, __func__, __FILE__, __LINE__) - -static void -_entry_attach(dns_adbentry_t *source, dns_adbentry_t **targetp, - const char *func, const char *file, unsigned int line) { - uint_fast32_t refs; - - REQUIRE(DNS_ADBENTRY_VALID(source)); - REQUIRE(targetp != NULL && *targetp == NULL); - - refs = isc_refcount_increment(&source->references); -#ifdef ADB_TRACE - fprintf(stderr, "%s:%s:%u:%s(%p, %p) = %" PRIuFAST32 "\n", func, file, - line, __func__, source, targetp, refs + 1); -#else - UNUSED(func); - UNUSED(file); - UNUSED(line); - UNUSED(refs); -#endif /* ADB_TRACE */ - - *targetp = source; -} - -#define entry_detach(ep) _entry_detach(ep, __func__, __FILE__, __LINE__) - -static void -_entry_detach(dns_adbentry_t **entryp, const char *func, const char *file, - unsigned int line) { - dns_adbentry_t *entry = NULL; - uint_fast32_t refs; - - REQUIRE(entryp != NULL); - - entry = *entryp; - *entryp = NULL; - - REQUIRE(DNS_ADBENTRY_VALID(entry)); - - refs = isc_refcount_decrement(&entry->references); - -#ifdef ADB_TRACE - fprintf(stderr, "%s:%s:%u:%s(%p, %p), %" PRIuFAST32 "\n", func, file, - line, __func__, entry, entryp, refs - 1); -#else - UNUSED(func); - UNUSED(file); - UNUSED(line); -#endif /* ADB_TRACE */ - - if (refs == 1) { - unlink_entry(entry); - free_adbentry(&entry); - } -} - /* - * Requires the name's bucket to be locked and that no entry buckets be locked. + * Requires the name to be locked and that no entries to be locked. * * This code handles A and AAAA rdatasets only. */ @@ -607,7 +535,6 @@ import_rdataset(dns_adbname_t *adbname, dns_rdataset_t *rdataset, isc_stdtime_t now) { isc_result_t result; dns_adb_t *adb = NULL; - bool new_addresses_added = false; dns_rdatatype_t rdtype; REQUIRE(DNS_ADBNAME_VALID(adbname)); @@ -623,7 +550,7 @@ import_rdataset(dns_adbname_t *adbname, dns_rdataset_t *rdataset, for (result = dns_rdataset_first(rdataset); result == ISC_R_SUCCESS; result = dns_rdataset_next(rdataset)) { - dns_adbentrybucket_t *ebucket = NULL; + /* FIXME: Move to a separate function */ dns_adbnamehooklist_t *hookhead = NULL; dns_adbentry_t *entry = NULL; dns_adbnamehook_t *nh = NULL; @@ -633,128 +560,99 @@ import_rdataset(dns_adbname_t *adbname, dns_rdataset_t *rdataset, struct in6_addr in6a; dns_rdataset_current(rdataset, &rdata); - if (rdtype == dns_rdatatype_a) { + switch (rdtype) { + case dns_rdatatype_a: INSIST(rdata.length == 4); memmove(&ina.s_addr, rdata.data, 4); isc_sockaddr_fromin(&sockaddr, &ina, 0); hookhead = &adbname->v4; - } else { + break; + case dns_rdatatype_aaaa: INSIST(rdata.length == 16); memmove(in6a.s6_addr, rdata.data, 16); isc_sockaddr_fromin6(&sockaddr, &in6a, 0); hookhead = &adbname->v6; + break; + default: + UNREACHABLE(); } - nh = new_adbnamehook(adb, NULL); + entry = get_attached_entry(adb, &sockaddr); - get_entrybucket(adb, &sockaddr, &ebucket); - INSIST(ebucket != NULL); - LOCK(&ebucket->lock); + INSIST(entry != NULL); - entry = get_entry(ebucket, &sockaddr, now); - if (entry == NULL) { - entry = new_adbentry(adb); - entry->sockaddr = sockaddr; - entry_attach(entry, &nh->entry); - link_entry(ebucket, entry); - entry->nh = 1; - } else { - dns_adbnamehook_t *anh = NULL; - for (anh = ISC_LIST_HEAD(*hookhead); anh != NULL; - anh = ISC_LIST_NEXT(anh, plink)) - { - if (anh->entry == entry) { - break; - } - } - if (anh == NULL) { - /* Move entry to the head of the LRU list */ - ISC_LIST_UNLINK(ebucket->entries, entry, plink); - ISC_LIST_PREPEND(ebucket->entries, entry, - plink); - entry_attach(entry, &nh->entry); - entry->nh++; - } else { - free_adbnamehook(adb, &nh); + dns_adbnamehook_t *anh = NULL; + for (anh = ISC_LIST_HEAD(*hookhead); anh != NULL; + anh = ISC_LIST_NEXT(anh, plink)) + { + if (anh->entry == entry) { + break; } } - UNLOCK(&ebucket->lock); - - new_addresses_added = true; - if (nh != NULL) { + if (anh == NULL) { + nh = new_adbnamehook(adb); + dns_adbentry_attach(entry, &nh->entry); ISC_LIST_APPEND(*hookhead, nh, plink); } + + dns_adbentry_detach(&entry); } if (result == ISC_R_NOMORE) { result = ISC_R_SUCCESS; } + INSIST(result == ISC_R_SUCCESS); - if (rdataset->trust == dns_trust_glue || - rdataset->trust == dns_trust_additional) - { + switch (rdataset->trust) { + case dns_trust_glue: + case dns_trust_additional: rdataset->ttl = ADB_CACHE_MINIMUM; - } else if (rdataset->trust == dns_trust_ultimate) { + break; + case dns_trust_ultimate: rdataset->ttl = 0; - } else { + break; + default: rdataset->ttl = ttlclamp(rdataset->ttl); } - if (rdtype == dns_rdatatype_a) { - DP(NCACHE_LEVEL, "expire_v4 set to MIN(%u,%u) import_rdataset", - adbname->expire_v4, now + rdataset->ttl); + switch (rdtype) { + case dns_rdatatype_a: + DP(NCACHE_LEVEL, + "expire_v4 set to MIN(%u,%u,%u) import_rdataset", + adbname->expire_v4, now + ADB_ENTRY_WINDOW, + now + rdataset->ttl); adbname->expire_v4 = ISC_MIN( adbname->expire_v4, ISC_MIN(now + ADB_ENTRY_WINDOW, now + rdataset->ttl)); - } else { - DP(NCACHE_LEVEL, "expire_v6 set to MIN(%u,%u) import_rdataset", - adbname->expire_v6, now + rdataset->ttl); + break; + case dns_rdatatype_aaaa: + DP(NCACHE_LEVEL, + "expire_v6 set to MIN(%u,%u,%u) import_rdataset", + adbname->expire_v6, now + ADB_ENTRY_WINDOW, + now + rdataset->ttl); adbname->expire_v6 = ISC_MIN( adbname->expire_v6, ISC_MIN(now + ADB_ENTRY_WINDOW, now + rdataset->ttl)); + break; + default: + UNREACHABLE(); } - if (new_addresses_added) { - /* - * Lie a little here. This is more or less so code that cares - * can find out if any new information was added or not. - */ - return (ISC_R_SUCCESS); - } - - return (result); + return (ISC_R_SUCCESS); } /* - * Requires the name's bucket to be locked. + * Requires the name to be locked. */ static void -expire_name(dns_adbname_t **n, isc_eventtype_t evtype) { - dns_adb_t *adb = NULL; - dns_adbname_t *adbname = NULL; - - REQUIRE(n != NULL); - - adbname = *n; - *n = NULL; - +expire_name(dns_adbname_t *adbname, isc_eventtype_t evtype) { REQUIRE(DNS_ADBNAME_VALID(adbname)); + REQUIRE(DNS_ADB_VALID(adbname->adb)); - adb = adbname->adb; - - REQUIRE(DNS_ADB_VALID(adb)); + isc_result_t result; + dns_adb_t *adb = adbname->adb; DP(DEF_LEVEL, "killing name %p", adbname); - /* - * If we're dead already and have no active fetch, unlink - * and free the name. - */ - if (NAME_DEAD(adbname) && !NAME_FETCH(adbname)) { - unlink_name(adbname); - free_adbname(&adbname); - return; - } - /* * Clean up the name's various contents. These functions * are destructive in that they will always empty the lists @@ -765,16 +663,6 @@ expire_name(dns_adbname_t **n, isc_eventtype_t evtype) { clean_namehooks(adb, &adbname->v6); clean_target(adb, &adbname->target); - /* - * If no fetches are running, unlink and free the name now; - * otherwise cancel them. - */ - if (!NAME_FETCH(adbname)) { - unlink_name(adbname); - free_adbname(&adbname); - return; - } - if (NAME_FETCH_A(adbname)) { dns_resolver_cancelfetch(adbname->fetch_a->fetch); } @@ -783,188 +671,87 @@ expire_name(dns_adbname_t **n, isc_eventtype_t evtype) { dns_resolver_cancelfetch(adbname->fetch_aaaa->fetch); } + adbname->flags |= NAME_IS_DEAD; + /* - * Mark the name as dead and move it from 'names' to 'deadnames'; - * we'll clean it up later. + * Remove the adbname from the hashtable... */ - if (!NAME_DEAD(adbname)) { - adbname->flags |= NAME_IS_DEAD; - } + result = isc_hashmap_delete(adb->names, NULL, &adbname->key.key, + adbname->key.size); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + /* ... and LRU list */ + ISC_LIST_UNLINK(adb->names_lru, adbname, link); + + dns_adbname_detach(&adbname); } /* - * Requires the name's bucket to be locked and no entry buckets be locked. + * Requires the name to be locked and no entries to be locked. */ static void -maybe_expire_namehooks(dns_adbname_t *name, isc_stdtime_t now) { - dns_adb_t *adb = NULL; - - REQUIRE(DNS_ADBNAME_VALID(name)); - - adb = name->adb; +maybe_expire_namehooks(dns_adbname_t *adbname, isc_stdtime_t now) { + REQUIRE(DNS_ADBNAME_VALID(adbname)); + REQUIRE(DNS_ADB_VALID(adbname->adb)); - REQUIRE(DNS_ADB_VALID(adb)); + dns_adb_t *adb = adbname->adb; /* * Check to see if we need to remove the v4 addresses */ - if (!NAME_FETCH_A(name) && EXPIRE_OK(name->expire_v4, now)) { - if (NAME_HAS_V4(name)) { - DP(DEF_LEVEL, "expiring v4 for name %p", name); - clean_namehooks(adb, &name->v4); - name->partial_result &= ~DNS_ADBFIND_INET; + if (!NAME_FETCH_A(adbname) && EXPIRE_OK(adbname->expire_v4, now)) { + if (NAME_HAS_V4(adbname)) { + DP(DEF_LEVEL, "expiring v4 for name %p", adbname); + clean_namehooks(adb, &adbname->v4); + adbname->partial_result &= ~DNS_ADBFIND_INET; } - name->expire_v4 = INT_MAX; - name->fetch_err = FIND_ERR_UNEXPECTED; + adbname->expire_v4 = INT_MAX; + adbname->fetch_err = FIND_ERR_UNEXPECTED; } /* * Check to see if we need to remove the v6 addresses */ - if (!NAME_FETCH_AAAA(name) && EXPIRE_OK(name->expire_v6, now)) { - if (NAME_HAS_V6(name)) { - DP(DEF_LEVEL, "expiring v6 for name %p", name); - clean_namehooks(adb, &name->v6); - name->partial_result &= ~DNS_ADBFIND_INET6; + if (!NAME_FETCH_AAAA(adbname) && EXPIRE_OK(adbname->expire_v6, now)) { + if (NAME_HAS_V6(adbname)) { + DP(DEF_LEVEL, "expiring v6 for name %p", adbname); + clean_namehooks(adb, &adbname->v6); + adbname->partial_result &= ~DNS_ADBFIND_INET6; } - name->expire_v6 = INT_MAX; - name->fetch6_err = FIND_ERR_UNEXPECTED; + adbname->expire_v6 = INT_MAX; + adbname->fetch6_err = FIND_ERR_UNEXPECTED; } /* * Check to see if we need to remove the alias target. */ - if (EXPIRE_OK(name->expire_target, now)) { - clean_target(adb, &name->target); - name->expire_target = INT_MAX; + if (EXPIRE_OK(adbname->expire_target, now)) { + clean_target(adb, &adbname->target); + adbname->expire_target = INT_MAX; } } -/* - * Requires the name's bucket to be locked. - */ -static void -link_name(dns_adbnamebucket_t *nbucket, dns_adbname_t *name) { - REQUIRE(name->bucket == NULL); - REQUIRE(!ISC_LINK_LINKED(name, plink)); - - name->bucket = nbucket; - - ISC_LIST_PREPEND(nbucket->names, name, plink); - isc_refcount_increment0(&nbucket->references); -} - -/* - * Requires the name's bucket to be locked. - */ -static void -unlink_name(dns_adbname_t *name) { - dns_adbnamebucket_t *nbucket = NULL; - - REQUIRE(DNS_ADBNAME_VALID(name)); - REQUIRE(ISC_LINK_LINKED(name, plink)); - - nbucket = name->bucket; - name->bucket = NULL; - - REQUIRE(nbucket != NULL); - - ISC_LIST_UNLINK(nbucket->names, name, plink); - - isc_refcount_decrement(&nbucket->references); -} - -/* - * Requires the entry's bucket to be locked. - */ -static void -link_entry(dns_adbentrybucket_t *ebucket, dns_adbentry_t *entry) { - REQUIRE(entry != NULL && entry->bucket == NULL); - REQUIRE(!ISC_LINK_LINKED(entry, plink)); - - DP(DEF_LEVEL, "link ADB entry %p to bucket %p", entry, ebucket); - - entry->bucket = ebucket; - ISC_LIST_PREPEND(ebucket->entries, entry, plink); - isc_refcount_increment0(&ebucket->references); -} - -/* - * Requires the entry's bucket to be locked. - */ -static void -unlink_entry(dns_adbentry_t *entry) { - dns_adbentrybucket_t *ebucket = NULL; - - REQUIRE(DNS_ADBENTRY_VALID(entry)); - REQUIRE(ISC_LINK_LINKED(entry, plink)); - - ebucket = entry->bucket; - entry->bucket = NULL; - - REQUIRE(ebucket != NULL); - - DP(DEF_LEVEL, "unlink ADB entry %p from bucket %p", entry, ebucket); - - ISC_LIST_UNLINK(ebucket->entries, entry, plink); - - isc_refcount_decrement(&ebucket->references); -} - static void shutdown_names(dns_adb_t *adb) { - dns_adbnamebucket_t *nbucket = NULL; + dns_adbname_t *next = NULL; - RWLOCK(&adb->names_lock, isc_rwlocktype_read); - for (nbucket = ISC_LIST_HEAD(adb->namebuckets_lru); nbucket != NULL; - nbucket = ISC_LIST_NEXT(nbucket, link)) + LOCK(&adb->names_lock); + for (dns_adbname_t *name = ISC_LIST_HEAD(adb->names_lru); name != NULL; + name = next) { - dns_adbname_t *name = NULL; - - LOCK(&nbucket->lock); + next = ISC_LIST_NEXT(name, link); /* * Run through the list. For each name, clean up finds * found there, and cancel any fetches running. When * all the fetches are canceled, the name will destroy * itself. */ - name = ISC_LIST_HEAD(nbucket->names); - while (name != NULL) { - dns_adbname_t *next_name = ISC_LIST_NEXT(name, plink); - expire_name(&name, DNS_EVENT_ADBSHUTDOWN); - name = next_name; - } - - UNLOCK(&nbucket->lock); - } - RWUNLOCK(&adb->names_lock, isc_rwlocktype_read); -} - -static void -shutdown_entries(dns_adb_t *adb) { - dns_adbentrybucket_t *ebucket = NULL; - - RWLOCK(&adb->entries_lock, isc_rwlocktype_read); - for (ebucket = ISC_LIST_HEAD(adb->entrybuckets_lru); ebucket != NULL; - ebucket = ISC_LIST_NEXT(ebucket, link)) - { - dns_adbentry_t *entry = NULL; - dns_adbentry_t *next_entry = NULL; - - LOCK(&ebucket->lock); - for (entry = ISC_LIST_HEAD(ebucket->entries); entry != NULL; - entry = next_entry) - { - next_entry = ISC_LIST_NEXT(entry, plink); - entry_detach(&entry); - } - UNLOCK(&ebucket->lock); + expire_name(name, DNS_EVENT_ADBSHUTDOWN); } - RWUNLOCK(&adb->entries_lock, isc_rwlocktype_read); + UNLOCK(&adb->names_lock); } /* - * The bucket for the name containing the 'namehooks' list must be locked. + * The name containing the 'namehooks' list must be locked. */ static void clean_namehooks(dns_adb_t *adb, dns_adbnamehooklist_t *namehooks) { @@ -973,23 +760,9 @@ clean_namehooks(dns_adb_t *adb, dns_adbnamehooklist_t *namehooks) { namehook = ISC_LIST_HEAD(*namehooks); while (namehook != NULL) { INSIST(DNS_ADBNAMEHOOK_VALID(namehook)); + INSIST(DNS_ADBENTRY_VALID(namehook->entry)); - /* - * Clean up the entry if needed. - */ - if (namehook->entry != NULL) { - dns_adbentrybucket_t *ebucket = NULL; - - INSIST(DNS_ADBENTRY_VALID(namehook->entry)); - - ebucket = namehook->entry->bucket; - INSIST(ebucket != NULL); - - LOCK(&ebucket->lock); - namehook->entry->nh--; - entry_detach(&namehook->entry); - UNLOCK(&ebucket->lock); - } + dns_adbentry_detach(&namehook->entry); /* * Free the namehook @@ -1094,7 +867,7 @@ event_freefind(isc_event_t *event) { } /* - * Assumes the name's bucket is locked. + * The name must be locked. */ static void clean_finds_at_name(dns_adbname_t *name, isc_eventtype_t evtype, @@ -1178,93 +951,84 @@ clean_finds_at_name(dns_adbname_t *name, isc_eventtype_t evtype, } static dns_adbname_t * -new_adbname(dns_adb_t *adb, const dns_name_t *dnsname) { +new_adbname(dns_adb_t *adb, const dns_name_t *dnsname, bool start_at_zone) { dns_adbname_t *name = NULL; name = isc_mem_get(adb->mctx, sizeof(*name)); *name = (dns_adbname_t){ + .adb = dns_adb_ref(adb), .expire_v4 = INT_MAX, .expire_v6 = INT_MAX, .expire_target = INT_MAX, .fetch_err = FIND_ERR_UNEXPECTED, .fetch6_err = FIND_ERR_UNEXPECTED, + .v4 = ISC_LIST_INITIALIZER, + .v6 = ISC_LIST_INITIALIZER, + .finds = ISC_LIST_INITIALIZER, + .link = ISC_LINK_INITIALIZER, + .magic = DNS_ADBNAME_MAGIC, }; - dns_adb_attach(adb, &name->adb); +#if DNS_ADB_TRACE + fprintf(stderr, "dns_adbname__init:%s:%s:%d:%p->references = 1\n", + __func__, __FILE__, __LINE__ + 1, name); +#endif + isc_refcount_init(&name->references, 1); + + isc_mutex_init(&name->lock); dns_name_init(&name->name, NULL); - dns_name_dup(dnsname, adb->mctx, &name->name); + isc_buffer_init(&name->buffer, name->key.name, DNS_NAME_MAXWIRE); + dns_name_setbuffer(&name->name, &name->buffer); + dns_name_copy(dnsname, &name->name); dns_name_init(&name->target, NULL); - ISC_LIST_INIT(name->v4); - ISC_LIST_INIT(name->v6); - - ISC_LIST_INIT(name->finds); - ISC_LINK_INIT(name, plink); - - name->magic = DNS_ADBNAME_MAGIC; + name->key.size = dnsname->length + sizeof(bool); + if (start_at_zone) { + name->flags |= NAME_STARTATZONE; + name->key.start_at_zone = true; + } inc_adbstats(adb, dns_adbstats_namescnt); - return (name); } -static void -free_adbname(dns_adbname_t **namep) { - dns_adb_t *adb = NULL; - dns_adbname_t *name = NULL; +#if DNS_ADB_TRACE +ISC_REFCOUNT_TRACE_IMPL(dns_adbname, destroy_adbname); +#else +ISC_REFCOUNT_IMPL(dns_adbname, destroy_adbname); +#endif - REQUIRE(namep != NULL && DNS_ADBNAME_VALID(*namep)); +static void +destroy_adbname(dns_adbname_t *name) { + REQUIRE(DNS_ADBNAME_VALID(name)); - name = *namep; - *namep = NULL; + dns_adb_t *adb = name->adb; REQUIRE(!NAME_HAS_V4(name)); REQUIRE(!NAME_HAS_V6(name)); REQUIRE(!NAME_FETCH(name)); REQUIRE(ISC_LIST_EMPTY(name->finds)); - REQUIRE(!ISC_LINK_LINKED(name, plink)); - REQUIRE(name->bucket == NULL); + REQUIRE(!ISC_LINK_LINKED(name, link)); name->magic = 0; - adb = name->adb; - dns_name_free(&name->name, adb->mctx); - dec_adbstats(adb, dns_adbstats_namescnt); - isc_mem_put(adb->mctx, name, sizeof(*name)); - dns_adb_detach(&adb); -} - -static dns_adbnamebucket_t * -new_adbnamebucket(dns_adb_t *adb, const uint8_t *key, const size_t keysize) { - dns_adbnamebucket_t *nbucket = NULL; + isc_mutex_destroy(&name->lock); - nbucket = isc_mem_get(adb->mctx, sizeof(*nbucket) + keysize); - *nbucket = (dns_adbnamebucket_t){ - .keysize = keysize, - }; - - memmove(nbucket->key, key, keysize); - - ISC_LIST_INIT(nbucket->names); - ISC_LINK_INIT(nbucket, link); - - isc_mutex_init(&nbucket->lock); - isc_refcount_init(&nbucket->references, 0); + isc_mem_put(adb->mctx, name, sizeof(*name)); - return (nbucket); + dec_adbstats(adb, dns_adbstats_namescnt); + dns_adb_detach(&adb); } static dns_adbnamehook_t * -new_adbnamehook(dns_adb_t *adb, dns_adbentry_t *entry) { - dns_adbnamehook_t *nh = NULL; - - nh = isc_mem_get(adb->mctx, sizeof(*nh)); - *nh = (dns_adbnamehook_t){ .entry = entry }; - - ISC_LINK_INIT(nh, plink); +new_adbnamehook(dns_adb_t *adb) { + dns_adbnamehook_t *nh = isc_mem_get(adb->mctx, sizeof(*nh)); + *nh = (dns_adbnamehook_t){ + .plink = ISC_LINK_INITIALIZER, + .magic = DNS_ADBNAMEHOOK_MAGIC, + }; - nh->magic = DNS_ADBNAMEHOOK_MAGIC; return (nh); } @@ -1319,24 +1083,29 @@ free_adblameinfo(dns_adb_t *adb, dns_adblameinfo_t **lameinfo) { } static dns_adbentry_t * -new_adbentry(dns_adb_t *adb) { +new_adbentry(dns_adb_t *adb, const isc_sockaddr_t *addr) { dns_adbentry_t *entry = NULL; entry = isc_mem_get(adb->mctx, sizeof(*entry)); *entry = (dns_adbentry_t){ .srtt = isc_random_uniform(0x1f) + 1, + .sockaddr = *addr, + .link = ISC_LINK_INITIALIZER, + .lameinfo = ISC_LIST_INITIALIZER, + .magic = DNS_ADBENTRY_MAGIC, }; - dns_adb_attach(adb, &entry->adb); - +#if DNS_ADB_TRACE + fprintf(stderr, "dns_adbentry__init:%s:%s:%d:%p->references = 1\n", + __func__, __FILE__, __LINE__ + 1, entry); +#endif isc_refcount_init(&entry->references, 1); + isc_mutex_init(&entry->lock); + atomic_init(&entry->active, 0); atomic_init(&entry->quota, adb->quota); - ISC_LIST_INIT(entry->lameinfo); - ISC_LINK_INIT(entry, plink); - - entry->magic = DNS_ADBENTRY_MAGIC; + dns_adb_attach(adb, &entry->adb); inc_adbstats(adb, dns_adbstats_entriescnt); @@ -1344,20 +1113,17 @@ new_adbentry(dns_adb_t *adb) { } static void -free_adbentry(dns_adbentry_t **entryp) { - dns_adb_t *adb = NULL; - dns_adbentry_t *entry = NULL; - dns_adblameinfo_t *li = NULL; - - REQUIRE(entryp != NULL && DNS_ADBENTRY_VALID(*entryp)); - - entry = *entryp; - *entryp = NULL; +destroy_adbentry(dns_adbentry_t *entry) { + REQUIRE(DNS_ADBENTRY_VALID(entry)); - REQUIRE(entry->bucket == NULL); - REQUIRE(!ISC_LINK_LINKED(entry, plink)); + dns_adblameinfo_t *li = NULL; + dns_adb_t *adb = entry->adb; - adb = entry->adb; + LOCK(&adb->entries_lock); + isc_result_t result = isc_hashmap_delete( + adb->entries, NULL, &entry->sockaddr, sizeof(entry->sockaddr)); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + UNLOCK(&adb->entries_lock); entry->magic = 0; @@ -1372,32 +1138,21 @@ free_adbentry(dns_adbentry_t **entryp) { li = ISC_LIST_HEAD(entry->lameinfo); } + isc_mutex_destroy(&entry->lock); isc_refcount_destroy(&entry->references); - dec_adbstats(adb, dns_adbstats_entriescnt); isc_mem_put(adb->mctx, entry, sizeof(*entry)); - dns_adb_detach(&adb); -} - -static dns_adbentrybucket_t * -new_adbentrybucket(dns_adb_t *adb, const uint8_t *key, const size_t keysize) { - dns_adbentrybucket_t *ebucket = NULL; - - ebucket = isc_mem_get(adb->mctx, sizeof(*ebucket) + keysize); - *ebucket = (dns_adbentrybucket_t){ - .keysize = keysize, - }; - - memmove(ebucket->key, key, keysize); - ISC_LIST_INIT(ebucket->entries); - ISC_LINK_INIT(ebucket, link); - - isc_mutex_init(&ebucket->lock); - isc_refcount_init(&ebucket->references, 0); + dec_adbstats(adb, dns_adbstats_entriescnt); - return (ebucket); + dns_adb_detach(&adb); } +#if DNS_ADB_TRACE +ISC_REFCOUNT_TRACE_IMPL(dns_adbentry, destroy_adbentry); +#else +ISC_REFCOUNT_IMPL(dns_adbentry, destroy_adbentry); +#endif + static dns_adbfind_t * new_adbfind(dns_adb_t *adb, in_port_t port) { dns_adbfind_t *find = NULL; @@ -1486,17 +1241,18 @@ new_adbaddrinfo(dns_adb_t *adb, dns_adbentry_t *entry, in_port_t port) { dns_adbaddrinfo_t *ai = NULL; ai = isc_mem_get(adb->mctx, sizeof(*ai)); - *ai = (dns_adbaddrinfo_t){ .srtt = entry->srtt, - .flags = entry->flags, - .dscp = -1 }; + *ai = (dns_adbaddrinfo_t){ + .srtt = entry->srtt, + .flags = entry->flags, + .dscp = -1, + .publink = ISC_LINK_INITIALIZER, + .sockaddr = entry->sockaddr, + .entry = dns_adbentry_ref(entry), + .magic = DNS_ADBADDRINFO_MAGIC, + }; - ISC_LINK_INIT(ai, publink); - entry_attach(entry, &ai->entry); - ai->sockaddr = entry->sockaddr; isc_sockaddr_setport(&ai->sockaddr, port); - ai->magic = DNS_ADBADDRINFO_MAGIC; - return (ai); } @@ -1513,140 +1269,119 @@ free_adbaddrinfo(dns_adb_t *adb, dns_adbaddrinfo_t **ainfo) { ai->magic = 0; - entry_detach(&ai->entry); + dns_adbentry_detach(&ai->entry); isc_mem_put(adb->mctx, ai, sizeof(*ai)); } /* - * Search for the name bucket in the hash table. + * Search for the name in the hash table. */ static void -get_namebucket(dns_adb_t *adb, const dns_name_t *name, isc_stdtime_t now, - dns_adbnamebucket_t **nbucketp) { +get_attached_name(dns_adb_t *adb, const dns_name_t *name, bool start_at_zone, + isc_stdtime_t now, dns_adbname_t **adbnamep) { isc_result_t result; - dns_adbnamebucket_t *nbucket = NULL; + dns_adbname_t *adbname = NULL; uint32_t hashval; + isc_time_t timenow; + isc_stdtime_t last_update; + adbnamekey_t key; - REQUIRE(nbucketp != NULL && *nbucketp == NULL); - - hashval = isc_hashmap_hash(adb->namebuckets, name->ndata, name->length); - - RWLOCK(&adb->names_lock, isc_rwlocktype_write); - /* - * First, see if there are stale names at the - * end of the list, and purge them if so. - */ - purge_stale_names(adb, now); - result = isc_hashmap_find(adb->namebuckets, &hashval, name->ndata, - name->length, (void **)&nbucket); - if (result == ISC_R_NOTFOUND) { - /* - * Allocate a new bucket and add it to the hash table. - */ - nbucket = new_adbnamebucket(adb, name->ndata, name->length); - result = isc_hashmap_add(adb->namebuckets, &hashval, - nbucket->key, nbucket->keysize, - nbucket); - } else { - ISC_LIST_UNLINK(adb->namebuckets_lru, nbucket, link); - } - ISC_LIST_PREPEND(adb->namebuckets_lru, nbucket, link); - RWUNLOCK(&adb->names_lock, isc_rwlocktype_write); - INSIST(result == ISC_R_SUCCESS); - - *nbucketp = nbucket; -} - -/* - * Get the name from its bucket. The bucket must be locked. - */ -static dns_adbname_t * -get_name(dns_adbnamebucket_t *nbucket, const dns_name_t *name, - unsigned int options) { - dns_adbname_t *adbname = ISC_LIST_HEAD(nbucket->names); - while (adbname != NULL) { - if (!NAME_DEAD(adbname)) { - if (dns_name_equal(name, &adbname->name) && - GLUEHINT_OK(adbname, options) && - STARTATZONE_MATCHES(adbname, options)) - { - return (adbname); - } - } - adbname = ISC_LIST_NEXT(adbname, plink); - } + REQUIRE(adbnamep != NULL && *adbnamep == NULL); - return (NULL); -} + isc_time_set(&timenow, now, 0); -/* - * Search for the entry bucket for this address in the hash table. - */ -static void -get_entrybucket(dns_adb_t *adb, const isc_sockaddr_t *addr, - dns_adbentrybucket_t **ebucketp) { - isc_result_t result; - dns_adbentrybucket_t *ebucket = NULL; - uint32_t hashval; + key.start_at_zone = start_at_zone; + memmove(&key.name, name->ndata, name->length); + key.size = name->length + sizeof(bool); - REQUIRE(ebucketp != NULL && *ebucketp == NULL); + hashval = isc_hashmap_hash(adb->names, &key.key, key.size); - hashval = isc_hashmap_hash(adb->entrybuckets, - (const unsigned char *)addr, sizeof(*addr)); + LOCK(&adb->names_lock); + last_update = adb->names_last_update; + if (now - last_update > ADB_STALE_MARGIN || + atomic_load_relaxed(&adb->is_overmem)) + { + last_update = adb->names_last_update = now; - RWLOCK(&adb->entries_lock, isc_rwlocktype_write); - result = isc_hashmap_find(adb->entrybuckets, &hashval, - (const unsigned char *)addr, sizeof(*addr), - (void **)&ebucket); - if (result == ISC_R_NOTFOUND) { - /* - * Allocate a new bucket and add it to the hash table. - */ - ebucket = new_adbentrybucket(adb, (const unsigned char *)addr, - sizeof(*addr)); - result = isc_hashmap_add(adb->entrybuckets, &hashval, - ebucket->key, ebucket->keysize, - ebucket); - } else { - ISC_LIST_UNLINK(adb->entrybuckets_lru, ebucket, link); + purge_stale_names(adb, now); } - ISC_LIST_PREPEND(adb->entrybuckets_lru, ebucket, link); - RWUNLOCK(&adb->entries_lock, isc_rwlocktype_write); - INSIST(result == ISC_R_SUCCESS); - *ebucketp = ebucket; + result = isc_hashmap_find(adb->names, &hashval, key.key, key.size, + (void **)&adbname); + switch (result) { + case ISC_R_NOTFOUND: + /* Allocate a new name and add it to the hash table. */ + adbname = new_adbname(adb, name, start_at_zone); + result = isc_hashmap_add(adb->names, &hashval, + &adbname->key.key, adbname->key.size, + adbname); + INSIST(result == ISC_R_SUCCESS); + + ISC_LIST_PREPEND(adb->names_lru, adbname, link); + adbname->last_used = now; + break; + case ISC_R_SUCCESS: + LOCK(&adbname->lock); + if (adbname->last_used <= last_update) { + adbname->last_used = now; + + ISC_LIST_UNLINK(adb->names_lru, adbname, link); + ISC_LIST_PREPEND(adb->names_lru, adbname, link); + } + UNLOCK(&adbname->lock); + break; + default: + UNREACHABLE(); + } + /* + * The refcount is now 2 and the final detach will happen in + * expire_name() - the unused adbname stored in the hashtable and lru + * has always refcount == 1 + */ + dns_adbname_ref(adbname); + + UNLOCK(&adb->names_lock); + + *adbnamep = adbname; } /* - * Find the entry from its bucket. The bucket must be locked. + * Find the entry in the adb->entries hashtable. */ static dns_adbentry_t * -get_entry(dns_adbentrybucket_t *ebucket, const isc_sockaddr_t *addr, - isc_stdtime_t now) { - dns_adbentry_t *entry = NULL, *entry_next = NULL; - - /* Search the list, while cleaning up expired entries. */ - for (entry = ISC_LIST_HEAD(ebucket->entries); entry != NULL; - entry = entry_next) - { - entry_next = ISC_LIST_NEXT(entry, plink); +get_attached_entry(dns_adb_t *adb, const isc_sockaddr_t *addr) { + isc_result_t result; + dns_adbentry_t *entry = NULL; + uint32_t hashval = isc_hashmap_hash( + adb->entries, (const unsigned char *)addr, sizeof(*addr)); - /* Address entries expire after 30 minutes. */ - maybe_expire_entry(&entry, now); - if (entry != NULL && isc_sockaddr_equal(addr, &entry->sockaddr)) - { - ISC_LIST_UNLINK(ebucket->entries, entry, plink); - ISC_LIST_PREPEND(ebucket->entries, entry, plink); - return (entry); - } + LOCK(&adb->entries_lock); + result = isc_hashmap_find(adb->entries, &hashval, + (const unsigned char *)addr, sizeof(*addr), + (void **)&entry); + switch (result) { + case ISC_R_NOTFOUND: + /* Allocate a new entry and add it to the hash table. */ + entry = new_adbentry(adb, addr); + result = isc_hashmap_add(adb->entries, &hashval, + &entry->sockaddr, + sizeof(entry->sockaddr), entry); + INSIST(result == ISC_R_SUCCESS); + break; + case ISC_R_SUCCESS: + dns_adbentry_ref(entry); + break; + default: + UNREACHABLE(); } + UNLOCK(&adb->entries_lock); - return (NULL); + return (entry); } /* - * The entry's bucket must be locked. + * The entry must be locked. */ static bool entry_is_lame(dns_adb_t *adb, dns_adbentry_t *entry, const dns_name_t *qname, @@ -1713,17 +1448,14 @@ copy_namehook_lists(dns_adb_t *adb, dns_adbfind_t *find, const dns_name_t *qname, dns_rdatatype_t qtype, dns_adbname_t *name, isc_stdtime_t now) { dns_adbnamehook_t *namehook = NULL; - dns_adbaddrinfo_t *addrinfo = NULL; dns_adbentry_t *entry = NULL; - dns_adbentrybucket_t *ebucket = NULL; if ((find->options & DNS_ADBFIND_INET) != 0) { namehook = ISC_LIST_HEAD(name->v4); while (namehook != NULL) { + dns_adbaddrinfo_t *addrinfo = NULL; entry = namehook->entry; - ebucket = entry->bucket; - INSIST(ebucket != NULL); - LOCK(&ebucket->lock); + LOCK(&entry->lock); if (dns_adbentry_overquota(entry)) { find->options |= (DNS_ADBFIND_LAMEPRUNED | @@ -1743,12 +1475,9 @@ copy_namehook_lists(dns_adb_t *adb, dns_adbfind_t *find, /* * Found a valid entry. Add it to the find's list. */ - entry_attach(entry, &(dns_adbentry_t *){ NULL }); ISC_LIST_APPEND(find->list, addrinfo, publink); - addrinfo = NULL; nextv4: - UNLOCK(&ebucket->lock); - ebucket = NULL; + UNLOCK(&entry->lock); namehook = ISC_LIST_NEXT(namehook, plink); } } @@ -1756,10 +1485,9 @@ copy_namehook_lists(dns_adb_t *adb, dns_adbfind_t *find, if ((find->options & DNS_ADBFIND_INET6) != 0) { namehook = ISC_LIST_HEAD(name->v6); while (namehook != NULL) { + dns_adbaddrinfo_t *addrinfo = NULL; entry = namehook->entry; - ebucket = entry->bucket; - INSIST(ebucket != NULL); - LOCK(&ebucket->lock); + LOCK(&entry->lock); if (dns_adbentry_overquota(entry)) { find->options |= (DNS_ADBFIND_LAMEPRUNED | @@ -1778,52 +1506,42 @@ copy_namehook_lists(dns_adb_t *adb, dns_adbfind_t *find, /* * Found a valid entry. Add it to the find's list. */ - entry_attach(entry, &(dns_adbentry_t *){ NULL }); ISC_LIST_APPEND(find->list, addrinfo, publink); - addrinfo = NULL; nextv6: - UNLOCK(&ebucket->lock); - ebucket = NULL; + UNLOCK(&entry->lock); namehook = ISC_LIST_NEXT(namehook, plink); } } - - if (ebucket != NULL) { - UNLOCK(&ebucket->lock); - } } /* - * The name's bucket must be locked. + * The name must be locked and write lock on adb->names_lock must be held. */ -static void -maybe_expire_name(dns_adbname_t **namep, isc_stdtime_t now) { - dns_adbname_t *name = NULL; - - REQUIRE(namep != NULL && DNS_ADBNAME_VALID(*namep)); - - name = *namep; +static bool +maybe_expire_name(dns_adbname_t *adbname, isc_stdtime_t now) { + REQUIRE(DNS_ADBNAME_VALID(adbname)); /* Leave this name alone if it still has active namehooks... */ - if (NAME_HAS_V4(name) || NAME_HAS_V6(name)) { - return; + if (NAME_HAS_V4(adbname) || NAME_HAS_V6(adbname)) { + return (false); } /* ...an active fetch in progres... */ - if (NAME_FETCH(name)) { - return; + if (NAME_FETCH(adbname)) { + return (false); } /* ... or is not yet expired. */ - if (!EXPIRE_OK(name->expire_v4, now) || - !EXPIRE_OK(name->expire_v6, now) || - !EXPIRE_OK(name->expire_target, now)) + if (!EXPIRE_OK(adbname->expire_v4, now) || + !EXPIRE_OK(adbname->expire_v6, now) || + !EXPIRE_OK(adbname->expire_target, now)) { - return; + return (false); } - *namep = NULL; - expire_name(&name, DNS_EVENT_ADBEXPIRED); + expire_name(adbname, DNS_EVENT_ADBEXPIRED); + + return (true); } /*% @@ -1838,10 +1556,10 @@ maybe_expire_name(dns_adbname_t **namep, isc_stdtime_t now) { */ static void purge_stale_names(dns_adb_t *adb, isc_stdtime_t now) { - dns_adbnamebucket_t *nbucket = NULL; - bool overmem = isc_mem_isovermem(adb->mctx); + bool overmem = atomic_load_relaxed(&adb->is_overmem); int max_removed = overmem ? 2 : 1; int scans = 0, removed = 0; + dns_adbname_t *prev = NULL; /* * We limit the number of scanned entries to 10 (arbitrary choice) @@ -1850,187 +1568,98 @@ purge_stale_names(dns_adb_t *adb, isc_stdtime_t now) { * happen). */ - for (nbucket = ISC_LIST_TAIL(adb->namebuckets_lru); - nbucket != NULL && removed < max_removed && scans < 10; - nbucket = ISC_LIST_PREV(nbucket, link)) + for (dns_adbname_t *adbname = ISC_LIST_TAIL(adb->names_lru); + adbname != NULL && removed < max_removed && scans < 10; + adbname = prev) { - dns_adbname_t *adbname = NULL; - dns_adbname_t *next_adbname = NULL; + bool expired = false; - LOCK(&nbucket->lock); + prev = ISC_LIST_PREV(adbname, link); + + dns_adbname_ref(adbname); + LOCK(&adbname->lock); scans++; - for (adbname = ISC_LIST_HEAD(nbucket->names); adbname != NULL; - adbname = next_adbname) - { - next_adbname = ISC_LIST_PREV(adbname, plink); + /* + * Remove the name if it's expired or unused, + * has no address data. + */ + maybe_expire_namehooks(adbname, now); + expired = maybe_expire_name(adbname, now); + if (expired) { + removed++; + goto next; + } + + if (overmem) { + expire_name(adbname, DNS_EVENT_ADBCANCELED); + removed++; + goto next; + } + if (adbname->last_used + ADB_STALE_MARGIN <= now) { + expire_name(adbname, DNS_EVENT_ADBCANCELED); + removed++; + } else { /* - * Remove the name if it's expired or unused, - * has no address data. + * we won't expire anything on the LRU list as the + * .last_used + ADB_STALE_MARGIN will always be bigger + * than `now` for all previous entries, so we just stop + * the scanning */ - maybe_expire_namehooks(adbname, now); - maybe_expire_name(&adbname, now); - if (adbname == NULL) { - removed++; - continue; - } - - if (overmem || - adbname->last_used + ADB_STALE_MARGIN <= now) - { - expire_name(&adbname, DNS_EVENT_ADBCANCELED); - removed++; - } + prev = NULL; } - UNLOCK(&nbucket->lock); - } -} - -/* - * The entry's bucket must be locked. - */ -static void -maybe_expire_entry(dns_adbentry_t **entryp, isc_stdtime_t now) { - dns_adbentry_t *entry = NULL; - - REQUIRE(entryp != NULL && DNS_ADBENTRY_VALID(*entryp)); - - entry = *entryp; - - if (isc_refcount_current(&entry->references) > 1) { - return; - } - - if (entry->expires == 0 || entry->expires > now) { - return; + next: + UNLOCK(&adbname->lock); + dns_adbname_detach(&adbname); } - - /* - * The entry is not in use. Delete it. - */ - *entryp = NULL; - DP(DEF_LEVEL, "killing entry %p", entry); - INSIST(ISC_LINK_LINKED(entry, plink)); - entry_detach(&entry); } -/* - * A read lock must be held on adb->names_lock. - */ static void -cleanup_names(dns_adbnamebucket_t *nbucket, isc_stdtime_t now) { - dns_adbname_t *name = NULL; +cleanup_names(dns_adb_t *adb, isc_stdtime_t now) { + dns_adbname_t *next = NULL; - DP(CLEAN_LEVEL, "cleaning name bucket %p", nbucket); + LOCK(&adb->names_lock); + for (dns_adbname_t *adbname = ISC_LIST_HEAD(adb->names_lru); + adbname != NULL; adbname = next) + { + next = ISC_LIST_NEXT(adbname, link); - LOCK(&nbucket->lock); - name = ISC_LIST_HEAD(nbucket->names); - while (name != NULL) { - dns_adbname_t *next_name = ISC_LIST_NEXT(name, plink); + dns_adbname_ref(adbname); + LOCK(&adbname->lock); /* * Name hooks expire after the address record's TTL * or 30 minutes, whichever is shorter. If after cleaning * those up there are no name hooks left, and no active * fetches, we can remove this name from the bucket. */ - maybe_expire_namehooks(name, now); - maybe_expire_name(&name, now); - name = next_name; - } - UNLOCK(&nbucket->lock); -} - -/* - * A read lock must be held on adb->entries_lock. - */ -static void -cleanup_entries(dns_adbentrybucket_t *ebucket, isc_stdtime_t now) { - dns_adbentry_t *entry = NULL; - - DP(CLEAN_LEVEL, "cleaning entry bucket %p", ebucket); - - LOCK(&ebucket->lock); - entry = ISC_LIST_HEAD(ebucket->entries); - while (entry != NULL) { - dns_adbentry_t *next = ISC_LIST_NEXT(entry, plink); - - /* Address entries expire after 30 minutes. */ - maybe_expire_entry(&entry, now); - entry = next; - } - UNLOCK(&ebucket->lock); -} - -static void -clean_hashes(dns_adb_t *adb, isc_stdtime_t now) { - dns_adbnamebucket_t *nbucket = NULL; - dns_adbentrybucket_t *ebucket = NULL; - - RWLOCK(&adb->names_lock, isc_rwlocktype_read); - for (nbucket = ISC_LIST_HEAD(adb->namebuckets_lru); nbucket != NULL; - nbucket = ISC_LIST_NEXT(nbucket, link)) - { - cleanup_names(nbucket, now); - } - RWUNLOCK(&adb->names_lock, isc_rwlocktype_read); - - RWLOCK(&adb->entries_lock, isc_rwlocktype_read); - for (ebucket = ISC_LIST_HEAD(adb->entrybuckets_lru); ebucket != NULL; - ebucket = ISC_LIST_NEXT(ebucket, link)) - { - cleanup_entries(ebucket, now); + maybe_expire_namehooks(adbname, now); + (void)maybe_expire_name(adbname, now); + UNLOCK(&adbname->lock); + dns_adbname_detach(&adbname); } - RWUNLOCK(&adb->entries_lock, isc_rwlocktype_read); + UNLOCK(&adb->names_lock); } static void destroy(dns_adb_t *adb) { - isc_result_t result; - isc_hashmap_iter_t *it = NULL; - DP(DEF_LEVEL, "destroying ADB %p", adb); adb->magic = 0; - RWLOCK(&adb->names_lock, isc_rwlocktype_write); - isc_hashmap_iter_create(adb->namebuckets, &it); - for (result = isc_hashmap_iter_first(it); result == ISC_R_SUCCESS; - result = isc_hashmap_iter_delcurrent_next(it)) - { - dns_adbnamebucket_t *nbucket = NULL; - isc_hashmap_iter_current(it, (void **)&nbucket); - cleanup_names(nbucket, INT_MAX); - isc_mutex_destroy(&nbucket->lock); - isc_refcount_destroy(&nbucket->references); - ISC_LIST_UNLINK(adb->namebuckets_lru, nbucket, link); - isc_mem_put(adb->mctx, nbucket, - sizeof(*nbucket) + nbucket->keysize); - } - isc_hashmap_iter_destroy(&it); - isc_hashmap_destroy(&adb->namebuckets); - RWUNLOCK(&adb->names_lock, isc_rwlocktype_write); - isc_rwlock_destroy(&adb->names_lock); + LOCK(&adb->names_lock); + INSIST(isc_hashmap_count(adb->names) == 0); + isc_hashmap_destroy(&adb->names); + UNLOCK(&adb->names_lock); + isc_mutex_destroy(&adb->names_lock); - RWLOCK(&adb->entries_lock, isc_rwlocktype_write); - isc_hashmap_iter_create(adb->entrybuckets, &it); - for (result = isc_hashmap_iter_first(it); result == ISC_R_SUCCESS; - result = isc_hashmap_iter_delcurrent_next(it)) - { - dns_adbentrybucket_t *ebucket = NULL; - isc_hashmap_iter_current(it, (void **)&ebucket); - cleanup_entries(ebucket, INT_MAX); - isc_mutex_destroy(&ebucket->lock); - isc_refcount_destroy(&ebucket->references); - ISC_LIST_UNLINK(adb->entrybuckets_lru, ebucket, link); - isc_mem_put(adb->mctx, ebucket, - sizeof(*ebucket) + ebucket->keysize); - } - isc_hashmap_iter_destroy(&it); - isc_hashmap_destroy(&adb->entrybuckets); - RWUNLOCK(&adb->entries_lock, isc_rwlocktype_write); - isc_rwlock_destroy(&adb->entries_lock); + LOCK(&adb->entries_lock); + /* There are no unassociated entries */ + INSIST(isc_hashmap_count(adb->entries) == 0); + isc_hashmap_destroy(&adb->entries); + UNLOCK(&adb->entries_lock); + isc_mutex_destroy(&adb->entries_lock); isc_mutex_destroy(&adb->lock); isc_refcount_destroy(&adb->references); @@ -2046,6 +1675,12 @@ destroy(dns_adb_t *adb) { isc_mem_putanddetach(&adb->mctx, adb, sizeof(dns_adb_t)); } +#if DNS_ADB_TRACE +ISC_REFCOUNT_TRACE_IMPL(dns_adb, destroy); +#else +ISC_REFCOUNT_IMPL(dns_adb, destroy); +#endif + /* * Public functions. */ @@ -2071,20 +1706,23 @@ dns_adb_create(isc_mem_t *mem, dns_view_t *view, isc_loopmgr_t *loopmgr, * Initialize things here that cannot fail, and especially things * that must be NULL for the error return to work properly. */ +#if DNS_ADB_TRACE + fprintf(stderr, "dns_adb__init:%s:%s:%d:%p->references = 1\n", __func__, + __FILE__, __LINE__ + 1, adb); +#endif isc_refcount_init(&adb->references, 1); dns_view_weakattach(view, &adb->view); dns_resolver_attach(view->resolver, &adb->res); isc_mem_attach(mem, &adb->mctx); - ISC_LIST_INIT(adb->namebuckets_lru); + ISC_LIST_INIT(adb->names_lru); isc_hashmap_create(adb->mctx, ADB_HASH_BITS, - ISC_HASHMAP_CASE_INSENSITIVE, &adb->namebuckets); - isc_rwlock_init(&adb->names_lock, 0, 0); + ISC_HASHMAP_CASE_INSENSITIVE, &adb->names); + isc_mutex_init(&adb->names_lock); - ISC_LIST_INIT(adb->entrybuckets_lru); isc_hashmap_create(adb->mctx, ADB_HASH_BITS, ISC_HASHMAP_CASE_SENSITIVE, - &adb->entrybuckets); - isc_rwlock_init(&adb->entries_lock, 0, 0); + &adb->entries); + isc_mutex_init(&adb->entries_lock); isc_mutex_init(&adb->lock); @@ -2106,10 +1744,8 @@ dns_adb_create(isc_mem_t *mem, dns_view_t *view, isc_loopmgr_t *loopmgr, goto free_tasks; } - set_adbstat(adb, isc_hashmap_count(adb->namebuckets), - dns_adbstats_nnames); - set_adbstat(adb, isc_hashmap_count(adb->entrybuckets), - dns_adbstats_nentries); + set_adbstat(adb, 0, dns_adbstats_nnames); + set_adbstat(adb, 0, dns_adbstats_nentries); /* * Normal return. @@ -2128,13 +1764,12 @@ free_tasks: isc_mutex_destroy(&adb->lock); - isc_rwlock_destroy(&adb->entries_lock); - isc_hashmap_destroy(&adb->entrybuckets); - INSIST(ISC_LIST_EMPTY(adb->entrybuckets_lru)); + isc_mutex_destroy(&adb->entries_lock); + isc_hashmap_destroy(&adb->entries); - isc_rwlock_destroy(&adb->names_lock); - isc_hashmap_destroy(&adb->namebuckets); - INSIST(ISC_LIST_EMPTY(adb->namebuckets_lru)); + isc_mutex_destroy(&adb->names_lock); + isc_hashmap_destroy(&adb->names); + INSIST(ISC_LIST_EMPTY(adb->names_lru)); dns_resolver_detach(&adb->res); dns_view_weakdetach(&adb->view); @@ -2143,54 +1778,6 @@ free_tasks: return (result); } -void -dns__adb_attach(dns_adb_t *adb, dns_adb_t **adbp, const char *func, - const char *file, unsigned int line) { - uint_fast32_t refs; - - REQUIRE(DNS_ADB_VALID(adb)); - REQUIRE(adbp != NULL && *adbp == NULL); - - refs = isc_refcount_increment(&adb->references); -#ifdef ADB_TRACE - fprintf(stderr, "%s:%s:%u:%s(%p, %p) = %" PRIuFAST32 "\n", func, file, - line, __func__, adb, adbp, refs + 1); -#else - UNUSED(func); - UNUSED(file); - UNUSED(line); - UNUSED(refs); -#endif /* ADB_TRACE */ - - *adbp = adb; -} - -void -dns__adb_detach(dns_adb_t **adbp, const char *func, const char *file, - unsigned int line) { - dns_adb_t *adb = NULL; - uint_fast32_t refs; - - REQUIRE(adbp != NULL && DNS_ADB_VALID(*adbp)); - - adb = *adbp; - *adbp = NULL; - - refs = isc_refcount_decrement(&adb->references); -#ifdef ADB_TRACE - fprintf(stderr, "%s:%s:%u:%s(%p, %p), %" PRIuFAST32 "\n", func, file, - line, __func__, adb, adbp, refs - 1); -#else - UNUSED(func); - UNUSED(file); - UNUSED(line); -#endif /* ADB_TRACE */ - - if (refs == 1) { - destroy(adb); - } -} - void dns_adb_shutdown(dns_adb_t *adb) { if (!atomic_compare_exchange_strong(&adb->exiting, &(bool){ false }, @@ -2204,7 +1791,6 @@ dns_adb_shutdown(dns_adb_t *adb) { isc_mem_clearwater(adb->mctx); shutdown_names(adb); - shutdown_entries(adb); } /* @@ -2234,7 +1820,6 @@ dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, isc_result_t result = ISC_R_UNEXPECTED; dns_adbfind_t *find = NULL; dns_adbname_t *adbname = NULL; - dns_adbnamebucket_t *nbucket = NULL; bool want_event = true; bool start_at_zone = false; bool alias = false; @@ -2283,32 +1868,10 @@ dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, /* * Try to see if we know anything about this name at all. */ - get_namebucket(adb, name, now, &nbucket); - INSIST(nbucket != NULL); - LOCK(&nbucket->lock); - adbname = get_name(nbucket, name, find->options); - if (adbname == NULL) { - /* - * Nothing found. Allocate a new adbname structure for - * this name. - */ + get_attached_name(adb, name, FIND_STARTATZONE(find), now, &adbname); + INSIST(adbname != NULL); - adbname = new_adbname(adb, name); - link_name(nbucket, adbname); - if (FIND_HINTOK(find)) { - adbname->flags |= NAME_HINT_OK; - } - if (FIND_GLUEOK(find)) { - adbname->flags |= NAME_GLUE_OK; - } - if (FIND_STARTATZONE(find)) { - adbname->flags |= NAME_STARTATZONE; - } - } else { - /* Move this name forward in the LRU list */ - ISC_LIST_UNLINK(nbucket->names, adbname, plink); - ISC_LIST_PREPEND(nbucket->names, adbname, plink); - } + LOCK(&adbname->lock); adbname->last_used = now; @@ -2323,9 +1886,7 @@ dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, * Do we know that the name is an alias? */ if (!EXPIRE_OK(adbname->expire_target, now)) { - /* - * Yes, it is. - */ + /* Yes, it is. */ DP(DEF_LEVEL, "dns_adb_createfind: name %s (%p) is an alias (cached)", namebuf, adbname); @@ -2559,7 +2120,9 @@ post_copy: *findp = find; - UNLOCK(&nbucket->lock); + UNLOCK(&adbname->lock); + dns_adbname_detach(&adbname); + return (result); } @@ -2590,7 +2153,6 @@ dns_adb_destroyfind(dns_adbfind_t **findp) { */ ai = ISC_LIST_HEAD(find->list); while (ai != NULL) { - entry_detach(&(dns_adbentry_t *){ ai->entry }); ISC_LIST_UNLINK(find->list, ai, publink); free_adbaddrinfo(adb, &ai); ai = ISC_LIST_HEAD(find->list); @@ -2643,22 +2205,21 @@ dns_adb_cancelfind(dns_adbfind_t *find) { UNLOCK(&find->lock); } else { /* - * Release the find lock, then acquire the bucket and find + * Release the find lock, then acquire the name and find * locks in that order, to match locking hierarchy * elsewhere. */ UNLOCK(&find->lock); - LOCK(&adbname->bucket->lock); + LOCK(&adbname->lock); LOCK(&find->lock); - if (find->adbname != NULL) { - ISC_LIST_UNLINK(adbname->finds, find, plink); - find->adbname = NULL; - } + ISC_LIST_UNLINK(adbname->finds, find, plink); + find->adbname = NULL; + find_sendevent(find); UNLOCK(&find->lock); - UNLOCK(&adbname->bucket->lock); + UNLOCK(&adbname->lock); } } @@ -2674,7 +2235,7 @@ dns_adb_dump(dns_adb_t *adb, FILE *f) { } isc_stdtime_get(&now); - clean_hashes(adb, now); + cleanup_names(adb, now); dump_adb(adb, f, false, now); } @@ -2691,9 +2252,6 @@ dump_ttl(FILE *f, const char *legend, isc_stdtime_t value, isc_stdtime_t now) { */ static void dump_adb(dns_adb_t *adb, FILE *f, bool debug, isc_stdtime_t now) { - dns_adbnamebucket_t *nbucket = NULL; - dns_adbentrybucket_t *ebucket = NULL; - fprintf(f, ";\n; Address database dump\n;\n"); fprintf(f, "; [edns success/timeout]\n"); fprintf(f, "; [plain success/timeout]\n;\n"); @@ -2705,80 +2263,44 @@ dump_adb(dns_adb_t *adb, FILE *f, bool debug, isc_stdtime_t now) { /* * Ensure this operation is applied to both hash tables at once. */ - RWLOCK(&adb->names_lock, isc_rwlocktype_read); - for (nbucket = ISC_LIST_HEAD(adb->namebuckets_lru); nbucket != NULL; - nbucket = ISC_LIST_NEXT(nbucket, link)) + LOCK(&adb->names_lock); + for (dns_adbname_t *name = ISC_LIST_HEAD(adb->names_lru); name != NULL; + name = ISC_LIST_NEXT(name, link)) { - dns_adbname_t *name = NULL; - - LOCK(&nbucket->lock); - if (debug) { - static int n = 0; - fprintf(f, "; bucket %d\n", n); - n++; - } - + LOCK(&name->lock); /* * Dump the names */ - for (name = ISC_LIST_HEAD(nbucket->names); name != NULL; - name = ISC_LIST_NEXT(name, plink)) - { - if (debug) { - fprintf(f, "; name %p (flags %08x)\n", name, - name->flags); - } - fprintf(f, "; "); - dns_name_print(&name->name, f); - if (dns_name_countlabels(&name->target) > 0) { - fprintf(f, " alias "); - dns_name_print(&name->target, f); - } - - dump_ttl(f, "v4", name->expire_v4, now); - dump_ttl(f, "v6", name->expire_v6, now); - dump_ttl(f, "target", name->expire_target, now); - - fprintf(f, " [v4 %s] [v6 %s]", - errnames[name->fetch_err], - errnames[name->fetch6_err]); - - fprintf(f, "\n"); - - print_namehook_list(f, "v4", adb, &name->v4, debug, - now); - print_namehook_list(f, "v6", adb, &name->v6, debug, - now); - - if (debug) { - print_fetch_list(f, name); - print_find_list(f, name); - } + if (debug) { + fprintf(f, "; name %p (flags %08x)\n", name, + name->flags); + } + fprintf(f, "; "); + dns_name_print(&name->name, f); + if (dns_name_countlabels(&name->target) > 0) { + fprintf(f, " alias "); + dns_name_print(&name->target, f); } - UNLOCK(&nbucket->lock); - } - RWUNLOCK(&adb->names_lock, isc_rwlocktype_read); - fprintf(f, ";\n; Unassociated entries\n;\n"); + dump_ttl(f, "v4", name->expire_v4, now); + dump_ttl(f, "v6", name->expire_v6, now); + dump_ttl(f, "target", name->expire_target, now); - RWLOCK(&adb->entries_lock, isc_rwlocktype_read); - for (ebucket = ISC_LIST_HEAD(adb->entrybuckets_lru); ebucket != NULL; - ebucket = ISC_LIST_NEXT(ebucket, link)) - { - dns_adbentry_t *entry = NULL; + fprintf(f, " [v4 %s] [v6 %s]", errnames[name->fetch_err], + errnames[name->fetch6_err]); - LOCK(&ebucket->lock); + fprintf(f, "\n"); - for (entry = ISC_LIST_HEAD(ebucket->entries); entry != NULL; - entry = ISC_LIST_NEXT(entry, plink)) - { - if (entry->nh == 0) { - dump_entry(f, adb, entry, debug, now); - } + print_namehook_list(f, "v4", adb, &name->v4, debug, now); + print_namehook_list(f, "v6", adb, &name->v6, debug, now); + + if (debug) { + print_fetch_list(f, name); + print_find_list(f, name); } - UNLOCK(&ebucket->lock); + UNLOCK(&name->lock); } - RWUNLOCK(&adb->entries_lock, isc_rwlocktype_read); + UNLOCK(&adb->names_lock); } static void @@ -2853,8 +2375,8 @@ dumpfind(dns_adbfind_t *find, FILE *f) { fprintf(f, ";\tqpending %08x partial %08x options %08x flags %08x\n", find->query_pending, find->partial_result, find->options, find->flags); - fprintf(f, ";\tname bucket %p, name %p, event sender %p\n", - find->adbname->bucket, find->adbname, find->event.ev_sender); + fprintf(f, ";\name %p, event sender %p\n", find->adbname, + find->event.ev_sender); ai = ISC_LIST_HEAD(find->list); if (ai != NULL) { @@ -2902,9 +2424,9 @@ print_namehook_list(FILE *f, const char *legend, dns_adb_t *adb, if (debug) { fprintf(f, ";\tHook(%s) %p\n", legend, nh); } - LOCK(&nh->entry->bucket->lock); + LOCK(&nh->entry->lock); dump_entry(f, adb, nh->entry, debug, now); - UNLOCK(&nh->entry->bucket->lock); + UNLOCK(&nh->entry->lock); } } @@ -2949,40 +2471,41 @@ putstr(isc_buffer_t **b, const char *str) { isc_result_t dns_adb_dumpquota(dns_adb_t *adb, isc_buffer_t **buf) { - dns_adbentrybucket_t *ebucket = NULL; - REQUIRE(DNS_ADB_VALID(adb)); - RWLOCK(&adb->entries_lock, isc_rwlocktype_read); - for (ebucket = ISC_LIST_HEAD(adb->entrybuckets_lru); ebucket != NULL; - ebucket = ISC_LIST_NEXT(ebucket, link)) + isc_hashmap_iter_t *it = NULL; + isc_result_t result; + + LOCK(&adb->entries_lock); + isc_hashmap_iter_create(adb->entries, &it); + for (result = isc_hashmap_iter_first(it); result == ISC_R_SUCCESS; + result = isc_hashmap_iter_next(it)) { dns_adbentry_t *entry = NULL; + isc_hashmap_iter_current(it, (void **)&entry); - LOCK(&ebucket->lock); - for (entry = ISC_LIST_HEAD(ebucket->entries); entry != NULL; - entry = ISC_LIST_NEXT(entry, plink)) - { - char addrbuf[ISC_NETADDR_FORMATSIZE]; - char text[ISC_NETADDR_FORMATSIZE + BUFSIZ]; - isc_netaddr_t netaddr; + LOCK(&entry->lock); + char addrbuf[ISC_NETADDR_FORMATSIZE]; + char text[ISC_NETADDR_FORMATSIZE + BUFSIZ]; + isc_netaddr_t netaddr; - if (entry->atr == 0.0 && entry->quota == adb->quota) { - continue; - } + if (entry->atr == 0.0 && entry->quota == adb->quota) { + goto unlock; + } - isc_netaddr_fromsockaddr(&netaddr, &entry->sockaddr); - isc_netaddr_format(&netaddr, addrbuf, sizeof(addrbuf)); + isc_netaddr_fromsockaddr(&netaddr, &entry->sockaddr); + isc_netaddr_format(&netaddr, addrbuf, sizeof(addrbuf)); - snprintf(text, sizeof(text), - "\n- quota %s (%" PRIuFAST32 "/%d) atr %0.2f", - addrbuf, atomic_load_relaxed(&entry->quota), - adb->quota, entry->atr); - putstr(buf, text); - } - UNLOCK(&ebucket->lock); + snprintf(text, sizeof(text), + "\n- quota %s (%" PRIuFAST32 "/%d) atr %0.2f", addrbuf, + atomic_load_relaxed(&entry->quota), adb->quota, + entry->atr); + putstr(buf, text); + unlock: + UNLOCK(&entry->lock); } - RWUNLOCK(&adb->entries_lock, isc_rwlocktype_read); + isc_hashmap_iter_destroy(&it); + UNLOCK(&adb->entries_lock); return (ISC_R_SUCCESS); } @@ -3020,12 +2543,10 @@ dbfind_name(dns_adbname_t *adbname, isc_stdtime_t now, dns_rdatatype_t rdtype) { * the configuration on which server we should send queries to. */ result = dns_view_find(adb->view, &adbname->name, rdtype, now, - NAME_GLUEOK(adbname) ? DNS_DBFIND_GLUEOK : 0, - NAME_HINTOK(adbname), + DNS_DBFIND_GLUEOK, true, ((adbname->flags & NAME_STARTATZONE) != 0), NULL, NULL, fname, &rdataset, NULL); - /* XXXVIX this switch statement is too sparse to gen a jump table. */ switch (result) { case DNS_R_GLUE: case DNS_R_HINT: @@ -3106,12 +2627,6 @@ dbfind_name(dns_adbname_t *adbname, isc_stdtime_t now, dns_rdatatype_t rdtype) { break; case DNS_R_CNAME: case DNS_R_DNAME: - /* - * Clear the hint and glue flags, so this will match - * more often. - */ - adbname->flags &= ~(DNS_ADBFIND_GLUEOK | DNS_ADBFIND_HINTOK); - rdataset.ttl = ttlclamp(rdataset.ttl); clean_target(adb, &adbname->target); adbname->expire_target = INT_MAX; @@ -3146,7 +2661,6 @@ fetch_callback(isc_task_t *task, isc_event_t *ev) { dns_adbname_t *name = NULL; dns_adb_t *adb = NULL; dns_adbfetch_t *fetch = NULL; - dns_adbnamebucket_t *nbucket = NULL; isc_eventtype_t ev_status; isc_stdtime_t now; isc_result_t result; @@ -3162,8 +2676,7 @@ fetch_callback(isc_task_t *task, isc_event_t *ev) { REQUIRE(DNS_ADB_VALID(adb)); - nbucket = name->bucket; - LOCK(&nbucket->lock); + LOCK(&name->lock); INSIST(NAME_FETCH_A(name) || NAME_FETCH_AAAA(name)); address_type = 0; @@ -3308,12 +2821,11 @@ out: dns_resolver_destroyfetch(&fetch->fetch); free_adbfetch(adb, &fetch); isc_event_free(&ev); - if (ev_status == DNS_EVENT_ADBCANCELED) { - expire_name(&name, ev_status); - } else { + if (ev_status != DNS_EVENT_ADBCANCELED) { clean_finds_at_name(name, ev_status, address_type); } - UNLOCK(&nbucket->lock); + UNLOCK(&name->lock); + dns_adbname_detach(&name); dns_adb_detach(&adb); } @@ -3378,6 +2890,8 @@ fetch_name(dns_adbname_t *adbname, bool start_at_zone, unsigned int depth, goto cleanup; } + dns_adbname_ref(adbname); + if (type == dns_rdatatype_a) { adbname->fetch_a = fetch; inc_resstats(adb, dns_resstatscounter_gluefetchv4); @@ -3402,17 +2916,16 @@ isc_result_t dns_adb_marklame(dns_adb_t *adb, dns_adbaddrinfo_t *addr, const dns_name_t *qname, dns_rdatatype_t qtype, isc_stdtime_t expire_time) { - isc_result_t result = ISC_R_SUCCESS; - dns_adblameinfo_t *li = NULL; - dns_adbentrybucket_t *ebucket = NULL; - REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(DNS_ADBADDRINFO_VALID(addr)); REQUIRE(qname != NULL); - ebucket = addr->entry->bucket; - LOCK(&ebucket->lock); - li = ISC_LIST_HEAD(addr->entry->lameinfo); + isc_result_t result = ISC_R_SUCCESS; + dns_adblameinfo_t *li = NULL; + dns_adbentry_t *entry = addr->entry; + + LOCK(&entry->lock); + li = ISC_LIST_HEAD(entry->lameinfo); while (li != NULL && (li->qtype != qtype || !dns_name_equal(qname, &li->qname))) { @@ -3430,44 +2943,40 @@ dns_adb_marklame(dns_adb_t *adb, dns_adbaddrinfo_t *addr, ISC_LIST_PREPEND(addr->entry->lameinfo, li, plink); unlock: - UNLOCK(&ebucket->lock); + UNLOCK(&entry->lock); return (result); } void dns_adb_adjustsrtt(dns_adb_t *adb, dns_adbaddrinfo_t *addr, unsigned int rtt, unsigned int factor) { - dns_adbentrybucket_t *ebucket = NULL; - isc_stdtime_t now = 0; - REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(DNS_ADBADDRINFO_VALID(addr)); REQUIRE(factor <= 10); - ebucket = addr->entry->bucket; - LOCK(&ebucket->lock); + isc_stdtime_t now = 0; + dns_adbentry_t *entry = addr->entry; + LOCK(&entry->lock); - if (addr->entry->expires == 0 || factor == DNS_ADB_RTTADJAGE) { + if (entry->expires == 0 || factor == DNS_ADB_RTTADJAGE) { isc_stdtime_get(&now); } adjustsrtt(addr, rtt, factor, now); - UNLOCK(&ebucket->lock); + UNLOCK(&entry->lock); } void dns_adb_agesrtt(dns_adb_t *adb, dns_adbaddrinfo_t *addr, isc_stdtime_t now) { - dns_adbentrybucket_t *ebucket = NULL; - REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(DNS_ADBADDRINFO_VALID(addr)); - ebucket = addr->entry->bucket; - LOCK(&ebucket->lock); + dns_adbentry_t *entry = addr->entry; + LOCK(&entry->lock); adjustsrtt(addr, 0, DNS_ADB_RTTADJAGE, now); - UNLOCK(&ebucket->lock); + UNLOCK(&entry->lock); } static void @@ -3501,19 +3010,18 @@ adjustsrtt(dns_adbaddrinfo_t *addr, unsigned int rtt, unsigned int factor, void dns_adb_changeflags(dns_adb_t *adb, dns_adbaddrinfo_t *addr, unsigned int bits, unsigned int mask) { - dns_adbentrybucket_t *ebucket = NULL; - isc_stdtime_t now; - REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(DNS_ADBADDRINFO_VALID(addr)); - ebucket = addr->entry->bucket; - LOCK(&ebucket->lock); + isc_stdtime_t now; + dns_adbentry_t *entry = addr->entry; + + LOCK(&entry->lock); - addr->entry->flags = (addr->entry->flags & ~mask) | (bits & mask); - if (addr->entry->expires == 0) { + entry->flags = (entry->flags & ~mask) | (bits & mask); + if (entry->expires == 0) { isc_stdtime_get(&now); - addr->entry->expires = now + ADB_ENTRY_WINDOW; + entry->expires = now + ADB_ENTRY_WINDOW; } /* @@ -3522,7 +3030,7 @@ dns_adb_changeflags(dns_adb_t *adb, dns_adbaddrinfo_t *addr, unsigned int bits, */ addr->flags = (addr->flags & ~mask) | (bits & mask); - UNLOCK(&ebucket->lock); + UNLOCK(&entry->lock); } /* @@ -3606,35 +3114,31 @@ maybe_adjust_quota(dns_adb_t *adb, dns_adbaddrinfo_t *addr, bool timeout) { void dns_adb_plainresponse(dns_adb_t *adb, dns_adbaddrinfo_t *addr) { - dns_adbentrybucket_t *ebucket = NULL; - REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(DNS_ADBADDRINFO_VALID(addr)); - ebucket = addr->entry->bucket; - LOCK(&ebucket->lock); + dns_adbentry_t *entry = addr->entry; + LOCK(&entry->lock); maybe_adjust_quota(adb, addr, false); - addr->entry->plain++; - if (addr->entry->plain == 0xff) { - addr->entry->edns >>= 1; - addr->entry->ednsto >>= 1; - addr->entry->plain >>= 1; - addr->entry->plainto >>= 1; + entry->plain++; + if (entry->plain == 0xff) { + entry->edns >>= 1; + entry->ednsto >>= 1; + entry->plain >>= 1; + entry->plainto >>= 1; } - UNLOCK(&ebucket->lock); + UNLOCK(&entry->lock); } void dns_adb_timeout(dns_adb_t *adb, dns_adbaddrinfo_t *addr) { - dns_adbentrybucket_t *ebucket = NULL; - REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(DNS_ADBADDRINFO_VALID(addr)); - ebucket = addr->entry->bucket; - LOCK(&ebucket->lock); + dns_adbentry_t *entry = addr->entry; + LOCK(&entry->lock); maybe_adjust_quota(adb, addr, true); @@ -3645,40 +3149,37 @@ dns_adb_timeout(dns_adb_t *adb, dns_adbaddrinfo_t *addr) { addr->entry->plain >>= 1; addr->entry->plainto >>= 1; } - UNLOCK(&ebucket->lock); + UNLOCK(&entry->lock); } void dns_adb_ednsto(dns_adb_t *adb, dns_adbaddrinfo_t *addr) { - dns_adbentrybucket_t *ebucket = NULL; - REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(DNS_ADBADDRINFO_VALID(addr)); - ebucket = addr->entry->bucket; - LOCK(&ebucket->lock); + dns_adbentry_t *entry = addr->entry; + LOCK(&entry->lock); maybe_adjust_quota(adb, addr, true); - addr->entry->ednsto++; + entry->ednsto++; if (addr->entry->ednsto == 0xff) { - addr->entry->edns >>= 1; - addr->entry->ednsto >>= 1; - addr->entry->plain >>= 1; - addr->entry->plainto >>= 1; + entry->edns >>= 1; + entry->ednsto >>= 1; + entry->plain >>= 1; + entry->plainto >>= 1; } - UNLOCK(&ebucket->lock); + UNLOCK(&entry->lock); } void dns_adb_setudpsize(dns_adb_t *adb, dns_adbaddrinfo_t *addr, unsigned int size) { - dns_adbentrybucket_t *ebucket = NULL; - REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(DNS_ADBADDRINFO_VALID(addr)); - ebucket = addr->entry->bucket; - LOCK(&ebucket->lock); + dns_adbentry_t *entry = addr->entry; + + LOCK(&entry->lock); if (size < 512U) { size = 512U; } @@ -3688,28 +3189,27 @@ dns_adb_setudpsize(dns_adb_t *adb, dns_adbaddrinfo_t *addr, unsigned int size) { maybe_adjust_quota(adb, addr, false); - addr->entry->edns++; - if (addr->entry->edns == 0xff) { - addr->entry->edns >>= 1; - addr->entry->ednsto >>= 1; - addr->entry->plain >>= 1; - addr->entry->plainto >>= 1; + entry->edns++; + if (entry->edns == 0xff) { + entry->edns >>= 1; + entry->ednsto >>= 1; + entry->plain >>= 1; + entry->plainto >>= 1; } - UNLOCK(&ebucket->lock); + UNLOCK(&entry->lock); } unsigned int dns_adb_getudpsize(dns_adb_t *adb, dns_adbaddrinfo_t *addr) { - dns_adbentrybucket_t *ebucket = NULL; - unsigned int size; - REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(DNS_ADBADDRINFO_VALID(addr)); - ebucket = addr->entry->bucket; - LOCK(&ebucket->lock); - size = addr->entry->udpsize; - UNLOCK(&ebucket->lock); + unsigned int size; + dns_adbentry_t *entry = addr->entry; + + LOCK(&entry->lock); + size = entry->udpsize; + UNLOCK(&entry->lock); return (size); } @@ -3717,53 +3217,49 @@ dns_adb_getudpsize(dns_adb_t *adb, dns_adbaddrinfo_t *addr) { void dns_adb_setcookie(dns_adb_t *adb, dns_adbaddrinfo_t *addr, const unsigned char *cookie, size_t len) { - dns_adbentrybucket_t *ebucket = NULL; - REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(DNS_ADBADDRINFO_VALID(addr)); - ebucket = addr->entry->bucket; - LOCK(&ebucket->lock); + dns_adbentry_t *entry = addr->entry; + + LOCK(&entry->lock); - if (addr->entry->cookie != NULL && - (cookie == NULL || len != addr->entry->cookielen)) + if (entry->cookie != NULL && + (cookie == NULL || len != entry->cookielen)) { - isc_mem_put(adb->mctx, addr->entry->cookie, - addr->entry->cookielen); - addr->entry->cookie = NULL; - addr->entry->cookielen = 0; + isc_mem_put(adb->mctx, entry->cookie, entry->cookielen); + entry->cookie = NULL; + entry->cookielen = 0; } - if (addr->entry->cookie == NULL && cookie != NULL && len != 0U) { - addr->entry->cookie = isc_mem_get(adb->mctx, len); - addr->entry->cookielen = (uint16_t)len; + if (entry->cookie == NULL && cookie != NULL && len != 0U) { + entry->cookie = isc_mem_get(adb->mctx, len); + entry->cookielen = (uint16_t)len; } - if (addr->entry->cookie != NULL) { - memmove(addr->entry->cookie, cookie, len); + if (entry->cookie != NULL) { + memmove(entry->cookie, cookie, len); } - UNLOCK(&ebucket->lock); + UNLOCK(&entry->lock); } size_t dns_adb_getcookie(dns_adb_t *adb, dns_adbaddrinfo_t *addr, unsigned char *cookie, size_t len) { - dns_adbentrybucket_t *ebucket = NULL; - REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(DNS_ADBADDRINFO_VALID(addr)); - ebucket = addr->entry->bucket; - LOCK(&ebucket->lock); - if (cookie != NULL && addr->entry->cookie != NULL && - len >= addr->entry->cookielen) + dns_adbentry_t *entry = addr->entry; + + LOCK(&entry->lock); + if (cookie != NULL && entry->cookie != NULL && len >= entry->cookielen) { - memmove(cookie, addr->entry->cookie, addr->entry->cookielen); - len = addr->entry->cookielen; + memmove(cookie, entry->cookie, entry->cookielen); + len = entry->cookielen; } else { len = 0; } - UNLOCK(&ebucket->lock); + UNLOCK(&entry->lock); return (len); } @@ -3771,44 +3267,31 @@ dns_adb_getcookie(dns_adb_t *adb, dns_adbaddrinfo_t *addr, isc_result_t dns_adb_findaddrinfo(dns_adb_t *adb, const isc_sockaddr_t *sa, dns_adbaddrinfo_t **addrp, isc_stdtime_t now) { + REQUIRE(DNS_ADB_VALID(adb)); + REQUIRE(addrp != NULL && *addrp == NULL); + UNUSED(now); + isc_result_t result = ISC_R_SUCCESS; - dns_adbentrybucket_t *ebucket = NULL; dns_adbentry_t *entry = NULL; dns_adbaddrinfo_t *addr = NULL; in_port_t port; - REQUIRE(DNS_ADB_VALID(adb)); - REQUIRE(addrp != NULL && *addrp == NULL); - if (atomic_load(&adb->exiting)) { return (ISC_R_SHUTTINGDOWN); } - get_entrybucket(adb, sa, &ebucket); - INSIST(ebucket != NULL); - LOCK(&ebucket->lock); + entry = get_attached_entry(adb, sa); + INSIST(entry != NULL); - entry = get_entry(ebucket, sa, now); - if (entry == NULL) { - /* - * We don't know anything about this address. - */ - entry = new_adbentry(adb); - entry->sockaddr = *sa; - link_entry(ebucket, entry); - DP(ENTER_LEVEL, "findaddrinfo: new entry %p", entry); - } else { - /* Move entry to the head of the LRU list */ - ISC_LIST_UNLINK(ebucket->entries, entry, plink); - ISC_LIST_PREPEND(ebucket->entries, entry, plink); - DP(ENTER_LEVEL, "findaddrinfo: found entry %p", entry); - } + LOCK(&entry->lock); port = isc_sockaddr_getport(sa); addr = new_adbaddrinfo(adb, entry, port); *addrp = addr; - UNLOCK(&ebucket->lock); + UNLOCK(&entry->lock); + dns_adbentry_detach(&entry); + return (result); } @@ -3846,7 +3329,7 @@ dns_adb_flush(dns_adb_t *adb) { return; } - clean_hashes(adb, INT_MAX); + cleanup_names(adb, INT_MAX); #ifdef DUMP_ADB_AFTER_CLEANING dump_adb(adb, stdout, true, INT_MAX); #endif /* ifdef DUMP_ADB_AFTER_CLEANING */ @@ -3855,9 +3338,9 @@ dns_adb_flush(dns_adb_t *adb) { void dns_adb_flushname(dns_adb_t *adb, const dns_name_t *name) { dns_adbname_t *adbname = NULL; - dns_adbname_t *nextname = NULL; - dns_adbnamebucket_t *nbucket = NULL; isc_result_t result; + bool start_at_zone = false; + adbnamekey_t key; REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(name != NULL); @@ -3866,31 +3349,36 @@ dns_adb_flushname(dns_adb_t *adb, const dns_name_t *name) { return; } - RWLOCK(&adb->names_lock, isc_rwlocktype_read); - result = isc_hashmap_find(adb->namebuckets, NULL, name->ndata, - name->length, (void **)&nbucket); - if (result != ISC_R_SUCCESS) { - RWUNLOCK(&adb->names_lock, isc_rwlocktype_read); - return; - } + LOCK(&adb->names_lock); +again: + /* + * Delete both entries - without and with NAME_STARTATZONE set. + */ + key.start_at_zone = start_at_zone; + memmove(&key.name, name->ndata, name->length); + key.size = name->length + sizeof(bool); - LOCK(&nbucket->lock); - adbname = ISC_LIST_HEAD(nbucket->names); - while (adbname != NULL) { - nextname = ISC_LIST_NEXT(adbname, plink); - if (!NAME_DEAD(adbname) && dns_name_equal(name, &adbname->name)) - { - expire_name(&adbname, DNS_EVENT_ADBCANCELED); + result = isc_hashmap_find(adb->names, NULL, key.key, key.size, + (void **)&adbname); + if (result == ISC_R_SUCCESS) { + dns_adbname_ref(adbname); + LOCK(&adbname->lock); + if (dns_name_equal(name, &adbname->name)) { + expire_name(adbname, DNS_EVENT_ADBCANCELED); } - adbname = nextname; + UNLOCK(&adbname->lock); + dns_adbname_detach(&adbname); + } + if (!start_at_zone) { + start_at_zone = true; + goto again; } - UNLOCK(&nbucket->lock); - RWUNLOCK(&adb->names_lock, isc_rwlocktype_read); + UNLOCK(&adb->names_lock); } void dns_adb_flushnames(dns_adb_t *adb, const dns_name_t *name) { - dns_adbnamebucket_t *nbucket = NULL; + dns_adbname_t *next = NULL; REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(name != NULL); @@ -3899,46 +3387,32 @@ dns_adb_flushnames(dns_adb_t *adb, const dns_name_t *name) { return; } - RWLOCK(&adb->names_lock, isc_rwlocktype_read); - for (nbucket = ISC_LIST_HEAD(adb->namebuckets_lru); nbucket != NULL; - nbucket = ISC_LIST_NEXT(nbucket, link)) + LOCK(&adb->names_lock); + for (dns_adbname_t *adbname = ISC_LIST_HEAD(adb->names_lru); + adbname != NULL; adbname = next) { - dns_adbname_t *adbname = NULL, *nextname = NULL; - - LOCK(&nbucket->lock); - adbname = ISC_LIST_HEAD(nbucket->names); - while (adbname != NULL) { - nextname = ISC_LIST_NEXT(adbname, plink); - if (!NAME_DEAD(adbname) && - dns_name_issubdomain(&adbname->name, name)) - { - expire_name(&adbname, DNS_EVENT_ADBCANCELED); - } - adbname = nextname; + next = ISC_LIST_NEXT(adbname, link); + dns_adbname_ref(adbname); + LOCK(&adbname->lock); + if (dns_name_issubdomain(&adbname->name, name)) { + expire_name(adbname, DNS_EVENT_ADBCANCELED); } - UNLOCK(&nbucket->lock); + UNLOCK(&adbname->lock); + dns_adbname_detach(&adbname); } - RWUNLOCK(&adb->names_lock, isc_rwlocktype_read); + UNLOCK(&adb->names_lock); } static void water(void *arg, int mark) { dns_adb_t *adb = arg; - bool overmem = (mark == ISC_MEM_HIWATER); - - /* - * We're going to change the way to handle overmem condition: - * use isc_mem_isovermem() instead of storing the state via this - * callback, since the latter way tends to cause race - * conditions. To minimize the change, and in case we re-enable - * the callback approach, however, keep this function at the - * moment. - */ REQUIRE(DNS_ADB_VALID(adb)); + atomic_store_release(&adb->is_overmem, (mark == ISC_MEM_HIWATER)); + DP(ISC_LOG_DEBUG(1), "adb reached %s water mark", - overmem ? "high" : "low"); + (mark == ISC_MEM_HIWATER) ? "high" : "low"); } void diff --git a/lib/dns/include/dns/adb.h b/lib/dns/include/dns/adb.h index 524f669f2d9..3a0a51e2d4e 100644 --- a/lib/dns/include/dns/adb.h +++ b/lib/dns/include/dns/adb.h @@ -66,6 +66,9 @@ *** Imports ***/ +/* Define to 1 for detailed reference tracing */ +#undef DNS_ADB_TRACE + #include #include @@ -92,9 +95,7 @@ ISC_LANG_BEGINDECLS *** TYPES ***/ -typedef struct dns_adbname dns_adbname_t; -typedef struct dns_adbnamebucket dns_adbnamebucket_t; -typedef struct dns_adbentrybucket dns_adbentrybucket_t; +typedef struct dns_adbname dns_adbname_t; /*! *\brief @@ -146,11 +147,6 @@ struct dns_adbfind { * Fetches will start using the closest zone data or use the root servers. * This is useful for reestablishing glue that has expired. * - * _GLUEOK: - * _HINTOK: - * Glue or hints are ok. These are used when matching names already - * in the adb, and when dns databases are searched. - * * _RETURNLAME: * Return lame servers in a find, so that all addresses are returned. * @@ -183,16 +179,6 @@ struct dns_adbfind { * This is useful for reestablishing glue that has expired. */ #define DNS_ADBFIND_STARTATZONE 0x00000020 -/*% - * Glue or hints are ok. These are used when matching names already - * in the adb, and when dns databases are searched. - */ -#define DNS_ADBFIND_GLUEOK 0x00000040 -/*% - * Glue or hints are ok. These are used when matching names already - * in the adb, and when dns databases are searched. - */ -#define DNS_ADBFIND_HINTOK 0x00000080 /*% * Return lame servers in a find, so that all addresses are returned. */ @@ -281,32 +267,16 @@ dns_adb_create(isc_mem_t *mem, dns_view_t *view, isc_loopmgr_t *loopmgr, *\li #ISC_R_NOMEMORY after resource allocation failure. */ -#define dns_adb_attach(a, ap) \ - dns__adb_attach(a, ap, __func__, __FILE__, __LINE__) -void -dns__adb_attach(dns_adb_t *adb, dns_adb_t **adbp, const char *func, - const char *file, unsigned int line); -/*% - * Attach to an 'adb' to 'adbp'. - * - * Requires: - *\li 'adb' to be a valid dns_adb_t, created via dns_adb_create(). - *\li 'adbp' to be a valid pointer to a *dns_adb_t which is initialized - * to NULL. - */ - -#define dns_adb_detach(ap) dns__adb_detach(ap, __func__, __FILE__, __LINE__) -void -dns__adb_detach(dns_adb_t **adb, const char *func, const char *file, - unsigned int line); -/*% - * Delete the ADB. Sets *ADB to NULL. Cancels any outstanding requests. - * - * Requires: - * - *\li 'adb' be non-NULL and '*adb' be a valid dns_adb_t, created via - * dns_adb_create(). - */ +#if DNS_ADB_TRACE +#define dns_adb_ref(ptr) dns_adb__ref(ptr, __func__, __FILE__, __LINE__) +#define dns_adb_unref(ptr) dns_adb__unref(ptr, __func__, __FILE__, __LINE__) +#define dns_adb_attach(ptr, ptrp) \ + dns_adb__attach(ptr, ptrp, __func__, __FILE__, __LINE__) +#define dns_adb_detach(ptrp) dns_adb__detach(ptrp, __func__, __FILE__, __LINE__) +ISC_REFCOUNT_TRACE_DECL(dns_adb); +#else +ISC_REFCOUNT_DECL(dns_adb); +#endif void dns_adb_shutdown(dns_adb_t *adb); diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index c14cbf216a7..34f03703ae5 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -3444,8 +3444,6 @@ findname(fetchctx_t *fctx, const dns_name_t *name, in_port_t port, if (dns_name_issubdomain(name, fctx->domain)) { options |= DNS_ADBFIND_STARTATZONE; } - options |= DNS_ADBFIND_GLUEOK; - options |= DNS_ADBFIND_HINTOK; /* * See what we know about this address. diff --git a/lib/isc/include/isc/refcount.h b/lib/isc/include/isc/refcount.h index 80ba888d06d..6b1f7cdecef 100644 --- a/lib/isc/include/isc/refcount.h +++ b/lib/isc/include/isc/refcount.h @@ -150,8 +150,8 @@ isc_refcount_decrement(isc_refcount_t *target) { } while (0) #define ISC_REFCOUNT_TRACE_DECL(name) \ - void name##__ref(name##_t *ptr, const char *func, const char *file, \ - unsigned int line); \ + name##_t *name##__ref(name##_t *ptr, const char *func, \ + const char *file, unsigned int line); \ void name##__unref(name##_t *ptr, const char *func, const char *file, \ unsigned int line); \ void name##__attach(name##_t *ptr, name##_t **ptrp, const char *func, \ @@ -160,31 +160,37 @@ isc_refcount_decrement(isc_refcount_t *target) { const char *file, unsigned int line) #define ISC_REFCOUNT_TRACE_IMPL(name, destroy) \ - void name##__ref(name##_t *ptr, const char *func, const char *file, \ - unsigned int line) { \ - uint_fast32_t refs; \ + name##_t *name##__ref(name##_t *ptr, const char *func, \ + const char *file, unsigned int line) { \ REQUIRE(ptr != NULL); \ - refs = isc_refcount_increment(&ptr->references); \ + uint_fast32_t refs = \ + isc_refcount_increment(&ptr->references) + 1; \ fprintf(stderr, \ "%s:%s:%s:%u:%p->references = %" PRIuFAST32 "\n", \ - __func__, func, file, line, ptr, refs + 1); \ + __func__, func, file, line, ptr, refs); \ + return (ptr); \ } \ \ void name##__unref(name##_t *ptr, const char *func, const char *file, \ unsigned int line) { \ - uint_fast32_t refs; \ REQUIRE(ptr != NULL); \ - if ((refs = isc_refcount_decrement(&ptr->references)) == 1) { \ + uint_fast32_t refs = \ + isc_refcount_decrement(&ptr->references) - 1; \ + if (refs == 0) { \ destroy(ptr); \ } \ fprintf(stderr, \ "%s:%s:%s:%u:%p->references = %" PRIuFAST32 "\n", \ - __func__, func, file, line, ptr, refs - 1); \ + __func__, func, file, line, ptr, refs); \ } \ void name##__attach(name##_t *ptr, name##_t **ptrp, const char *func, \ const char *file, unsigned int line) { \ REQUIRE(ptrp != NULL && *ptrp == NULL); \ - name##__ref(ptr, func, file, line); \ + uint_fast32_t refs = \ + isc_refcount_increment(&ptr->references) + 1; \ + fprintf(stderr, \ + "%s:%s:%s:%u:%p->references = %" PRIuFAST32 "\n", \ + __func__, func, file, line, ptr, refs); \ *ptrp = ptr; \ } \ \ @@ -193,19 +199,27 @@ isc_refcount_decrement(isc_refcount_t *target) { REQUIRE(ptrp != NULL && *ptrp != NULL); \ name##_t *ptr = *ptrp; \ *ptrp = NULL; \ - name##__unref(ptr, func, file, line); \ + uint_fast32_t refs = \ + isc_refcount_decrement(&ptr->references) - 1; \ + if (refs == 0) { \ + destroy(ptr); \ + } \ + fprintf(stderr, \ + "%s:%s:%s:%u:%p->references = %" PRIuFAST32 "\n", \ + __func__, func, file, line, ptr, refs); \ } -#define ISC_REFCOUNT_DECL(name) \ - void name##_ref(name##_t *ptr); \ - void name##_unref(name##_t *ptr); \ - void name##_attach(name##_t *ptr, name##_t **ptrp); \ - void name##_detach(name##_t **ptrp) +#define ISC_REFCOUNT_DECL(name) \ + name##_t *name##_ref(name##_t *ptr); \ + void name##_unref(name##_t *ptr); \ + void name##_attach(name##_t *ptr, name##_t **ptrp); \ + void name##_detach(name##_t **ptrp) #define ISC_REFCOUNT_IMPL(name, destroy) \ - void name##_ref(name##_t *ptr) { \ + name##_t *name##_ref(name##_t *ptr) { \ REQUIRE(ptr != NULL); \ isc_refcount_increment(&ptr->references); \ + return (ptr); \ } \ \ void name##_unref(name##_t *ptr) { \