From: Ondřej Surý Date: Mon, 19 Jun 2023 13:43:02 +0000 (+0200) Subject: Refactor dns_badcache to use cds_lfht lock-free hashtable X-Git-Tag: v9.19.16~14^2~4 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=4dacdde28f49b9a21862c2c00be673eba71342e7;p=thirdparty%2Fbind9.git Refactor dns_badcache to use cds_lfht lock-free hashtable The dns_badcache unit had (yet another) own locked hashtable implementation. Replace the hashtable used by dns_badcache with lock-free cds_lfht implementation from liburcu. --- diff --git a/configure.ac b/configure.ac index 78a4c5986a0..928b1489fa1 100644 --- a/configure.ac +++ b/configure.ac @@ -236,7 +236,7 @@ AC_MSG_RESULT([$RCU_FLAVOR]) # not add it automatically - we need to add it explicitly in such case. # PKG_CHECK_MODULES([LIBURCU], [$RCU_FLAVOR >= 0.13.0 liburcu-cds >= 0.13.0], [], - [PKG_CHECK_MODULES([LIBURCU], [$RCU_FLAVOR liburcu-cds], + [PKG_CHECK_MODULES([LIBURCU], [$RCU_FLAVOR >= 0.10.0 liburcu-cds >= 0.10.0], [LIBURCU_LIBS="$LIBURCU_LIBS -lurcu-common"])]) AC_DEFINE_UNQUOTED([RCU_FLAVOR], ["$RCU_FLAVOR"], [Chosen Userspace-RCU flavor]) diff --git a/lib/dns/badcache.c b/lib/dns/badcache.c index 4fdf4c56649..c134873069b 100644 --- a/lib/dns/badcache.c +++ b/lib/dns/badcache.c @@ -22,9 +22,14 @@ #include #include #include +#include #include +#include +#include #include +#include #include +#include #include #include @@ -37,476 +42,353 @@ typedef struct dns_bcentry dns_bcentry_t; struct dns_badcache { unsigned int magic; - isc_rwlock_t lock; isc_mem_t *mctx; - - isc_mutex_t *tlocks; - dns_bcentry_t **table; - - atomic_uint_fast32_t count; - atomic_uint_fast32_t sweep; - - unsigned int minsize; - unsigned int size; + struct cds_lfht *ht; + atomic_bool purge_in_progress; }; #define BADCACHE_MAGIC ISC_MAGIC('B', 'd', 'C', 'a') #define VALID_BADCACHE(m) ISC_MAGIC_VALID(m, BADCACHE_MAGIC) +#define BADCACHE_INIT_SIZE (1 << 10) /* Must be power of 2 */ +#define BADCACHE_MIN_SIZE (1 << 8) /* Must be power of 2 */ + struct dns_bcentry { - dns_bcentry_t *next; + isc_mem_t *mctx; dns_rdatatype_t type; - isc_time_t expire; - uint32_t flags; - unsigned int hashval; + _Atomic(isc_stdtime_t) expire; + atomic_uint_fast32_t flags; dns_fixedname_t fname; dns_name_t *name; + + struct cds_lfht_node ht_node; + struct rcu_head rcu_head; }; static void -badcache_resize(dns_badcache_t *bc, isc_time_t *now); +bcentry_print(dns_bcentry_t *bad, isc_stdtime_t now, FILE *fp); -isc_result_t -dns_badcache_init(isc_mem_t *mctx, unsigned int size, dns_badcache_t **bcp) { - dns_badcache_t *bc = NULL; - unsigned int i; +static void +bcentry_destroy(struct rcu_head *rcu_head); - REQUIRE(bcp != NULL && *bcp == NULL); +dns_badcache_t * +dns_badcache_new(isc_mem_t *mctx) { REQUIRE(mctx != NULL); - bc = isc_mem_get(mctx, sizeof(*bc)); - + dns_badcache_t *bc = isc_mem_get(mctx, sizeof(*bc)); *bc = (dns_badcache_t){ - .size = size, - .minsize = size, + .magic = BADCACHE_MAGIC, }; - isc_mem_attach(mctx, &bc->mctx); - isc_rwlock_init(&bc->lock); - - bc->table = isc_mem_getx(bc->mctx, sizeof(bc->table[0]) * size, - ISC_MEM_ZERO); - bc->tlocks = isc_mem_getx(bc->mctx, sizeof(bc->tlocks[0]) * size, - ISC_MEM_ZERO); - for (i = 0; i < size; i++) { - isc_mutex_init(&bc->tlocks[i]); - } + bc->ht = cds_lfht_new(BADCACHE_INIT_SIZE, BADCACHE_MIN_SIZE, 0, + CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL); + INSIST(bc->ht != NULL); - bc->magic = BADCACHE_MAGIC; + isc_mem_attach(mctx, &bc->mctx); - *bcp = bc; - return (ISC_R_SUCCESS); + return (bc); } void dns_badcache_destroy(dns_badcache_t **bcp) { - dns_badcache_t *bc; - unsigned int i; - REQUIRE(bcp != NULL && *bcp != NULL); - bc = *bcp; - *bcp = NULL; - - dns_badcache_flush(bc); + REQUIRE(VALID_BADCACHE(*bcp)); + dns_badcache_t *bc = *bcp; + *bcp = NULL; bc->magic = 0; - isc_rwlock_destroy(&bc->lock); - for (i = 0; i < bc->size; i++) { - isc_mutex_destroy(&bc->tlocks[i]); + + dns_bcentry_t *bad = NULL; + struct cds_lfht_iter iter; + cds_lfht_for_each_entry(bc->ht, &iter, bad, ht_node) { + INSIST(!cds_lfht_del(bc->ht, &bad->ht_node)); + bcentry_destroy(&bad->rcu_head); } - isc_mem_put(bc->mctx, bc->table, sizeof(bc->table[0]) * bc->size); - isc_mem_put(bc->mctx, bc->tlocks, sizeof(bc->tlocks[0]) * bc->size); + RUNTIME_CHECK(!cds_lfht_destroy(bc->ht, NULL)); + isc_mem_putanddetach(&bc->mctx, bc, sizeof(dns_badcache_t)); } +static int +bcentry_match(struct cds_lfht_node *ht_node, const void *key) { + const dns_name_t *name = key; + dns_bcentry_t *bad = caa_container_of(ht_node, dns_bcentry_t, ht_node); + + return (dns_name_equal(bad->name, name)); +} + +static dns_bcentry_t * +bcentry_new(dns_badcache_t *bc, const dns_name_t *name, + const dns_rdatatype_t type, const uint32_t flags, + const isc_stdtime_t expire) { + dns_bcentry_t *bad = isc_mem_get(bc->mctx, sizeof(*bad)); + *bad = (dns_bcentry_t){ + .type = type, + .flags = flags, + .expire = expire, + }; + isc_mem_attach(bc->mctx, &bad->mctx); + + bad->name = dns_fixedname_initname(&bad->fname); + dns_name_copy(name, bad->name); + + return (bad); +} + static void -badcache_resize(dns_badcache_t *bc, isc_time_t *now) { - dns_bcentry_t **newtable, *bad, *next; - isc_mutex_t *newlocks; - unsigned int newsize, i; - bool grow; +bcentry_destroy(struct rcu_head *rcu_head) { + dns_bcentry_t *bad = caa_container_of(rcu_head, dns_bcentry_t, + rcu_head); - RWLOCK(&bc->lock, isc_rwlocktype_write); + isc_mem_putanddetach(&bad->mctx, bad, sizeof(*bad)); +} +static void +bcentry_evict(struct cds_lfht *ht, dns_bcentry_t *bad) { /* - * XXXWPK we will have a thundering herd problem here, - * as all threads will wait on the RWLOCK when there's - * a need to resize badcache. - * However, it happens so rarely it should not be a - * performance issue. This is because we double the - * size every time we grow it, and we don't shrink - * unless the number of entries really shrunk. In a - * high load situation, the number of badcache entries - * will eventually stabilize. + * The hashtable isn't locked in a traditional sense, so multiple + * threads can lookup and evict the same record at the same time. + * + * This is amplified by the bcentry_purge_next() that walks a few more + * records in the hashtable and evicts them if they are expired. + * + * We need to destroy the bcentry only once - from the thread that has + * deleted the entry from the hashtable, all other calls to this + * function were redundant. */ - if (atomic_load_relaxed(&bc->count) > bc->size * 8) { - grow = true; - } else if (atomic_load_relaxed(&bc->count) < bc->size * 2 && - bc->size > bc->minsize) - { - grow = false; - } else { - /* Someone resized it already, bail. */ - RWUNLOCK(&bc->lock, isc_rwlocktype_write); - return; + if (!cds_lfht_del(ht, &bad->ht_node)) { + call_rcu(&bad->rcu_head, bcentry_destroy); } +} - if (grow) { - newsize = bc->size * 2 + 1; - } else { - newsize = (bc->size - 1) / 2; -#ifdef __clang_analyzer__ - /* - * XXXWPK there's a bug in clang static analyzer - - * `value % newsize` is considered undefined even though - * we check if newsize is larger than 0. This helps. - */ - newsize += 1; -#endif +static bool +bcentry_alive(struct cds_lfht *ht, dns_bcentry_t *bad, isc_stdtime_t now) { + if (cds_lfht_is_node_deleted(&bad->ht_node)) { + return (false); + } else if (atomic_load_relaxed(&bad->expire) < now) { + bcentry_evict(ht, bad); + return (false); } - RUNTIME_CHECK(newsize > 0); - newtable = isc_mem_getx(bc->mctx, sizeof(dns_bcentry_t *) * newsize, - ISC_MEM_ZERO); + return (true); +} - newlocks = isc_mem_get(bc->mctx, sizeof(isc_mutex_t) * newsize); +#define cds_lfht_for_each_entry_next(ht, iter, pos, member) \ + for (cds_lfht_next(ht, iter), \ + pos = cds_lfht_entry(cds_lfht_iter_get_node(iter), \ + __typeof__(*(pos)), member); \ + pos != NULL; /**/ \ + cds_lfht_next(ht, iter), \ + pos = cds_lfht_entry(cds_lfht_iter_get_node(iter), \ + __typeof__(*(pos)), member)) - /* Copy existing mutexes */ - for (i = 0; i < newsize && i < bc->size; i++) { - newlocks[i] = bc->tlocks[i]; - } - /* Initialize additional mutexes if we're growing */ - for (i = bc->size; i < newsize; i++) { - isc_mutex_init(&newlocks[i]); - } - /* Destroy extra mutexes if we're shrinking */ - for (i = newsize; i < bc->size; i++) { - isc_mutex_destroy(&bc->tlocks[i]); - } - - for (i = 0; atomic_load_relaxed(&bc->count) > 0 && i < bc->size; i++) { - for (bad = bc->table[i]; bad != NULL; bad = next) { - next = bad->next; - if (isc_time_compare(&bad->expire, now) < 0) { - isc_mem_put(bc->mctx, bad, sizeof(*bad)); - atomic_fetch_sub_relaxed(&bc->count, 1); - } else { - bad->next = newtable[bad->hashval % newsize]; - newtable[bad->hashval % newsize] = bad; - } +static void +bcentry_purge_next(struct cds_lfht *ht, struct cds_lfht_iter *iter, + isc_stdtime_t now) { + /* Lazy-purge the table */ + size_t count = 10; + dns_bcentry_t *bad; + cds_lfht_for_each_entry_next(ht, iter, bad, ht_node) { + if (!bcentry_alive(ht, bad, now)) { + break; + } + if (--count == 0) { + break; } - bc->table[i] = NULL; } - - isc_mem_put(bc->mctx, bc->tlocks, sizeof(isc_mutex_t) * bc->size); - bc->tlocks = newlocks; - - isc_mem_put(bc->mctx, bc->table, sizeof(*bc->table) * bc->size); - bc->size = newsize; - bc->table = newtable; - - RWUNLOCK(&bc->lock, isc_rwlocktype_write); } void dns_badcache_add(dns_badcache_t *bc, const dns_name_t *name, dns_rdatatype_t type, bool update, uint32_t flags, - isc_time_t *expire) { - unsigned int hashval, hash; - dns_bcentry_t *bad, *prev, *next; - isc_time_t now; - bool resize = false; - + isc_stdtime_t expire) { REQUIRE(VALID_BADCACHE(bc)); REQUIRE(name != NULL); - REQUIRE(expire != NULL); - - RWLOCK(&bc->lock, isc_rwlocktype_read); - - now = isc_time_now(); - - hashval = dns_name_hash(name); - hash = hashval % bc->size; - LOCK(&bc->tlocks[hash]); - prev = NULL; - for (bad = bc->table[hash]; bad != NULL; bad = next) { - next = bad->next; - if (bad->type == type && dns_name_equal(name, bad->name)) { - if (update) { - bad->expire = *expire; - bad->flags = flags; - } - break; - } - if (isc_time_compare(&bad->expire, &now) < 0) { - if (prev == NULL) { - bc->table[hash] = bad->next; - } else { - prev->next = bad->next; - } - isc_mem_put(bc->mctx, bad, sizeof(*bad)); - atomic_fetch_sub_relaxed(&bc->count, 1); - } else { - prev = bad; - } + + isc_stdtime_t now = isc_stdtime_now(); + if (expire < now) { + expire = now; } - if (bad == NULL) { - unsigned int count; - isc_buffer_t buffer; - - bad = isc_mem_get(bc->mctx, sizeof(*bad)); - *bad = (dns_bcentry_t){ .type = type, - .hashval = hashval, - .expire = *expire, - .flags = flags, - .next = bc->table[hash] }; - - isc_buffer_init(&buffer, bad + 1, name->length); - bad->name = dns_fixedname_initname(&bad->fname); - dns_name_copy(name, bad->name); - bc->table[hash] = bad; - - count = atomic_fetch_add_relaxed(&bc->count, 1); - if ((count > bc->size * 8) || - (count < bc->size * 2 && bc->size > bc->minsize)) - { - resize = true; + rcu_read_lock(); + struct cds_lfht *ht = rcu_dereference(bc->ht); + INSIST(ht != NULL); + + dns_bcentry_t *bad = NULL; + uint32_t hashval = dns_name_hash(name); + + struct cds_lfht_iter iter; + dns_bcentry_t *found = NULL; + cds_lfht_for_each_entry_duplicate(ht, hashval, bcentry_match, name, + &iter, bad, ht_node) { + if (bcentry_alive(ht, bad, now) && bad->type == type) { + found = bad; + /* + * We could bail-out on first match, but: + * 1. there could be duplicate .type entries + * 2. we want to check expire for all entries + */ } - } else { - bad->expire = *expire; } - UNLOCK(&bc->tlocks[hash]); - RWUNLOCK(&bc->lock, isc_rwlocktype_read); - if (resize) { - badcache_resize(bc, &now); + if (found == NULL) { + /* + * In theory, this could result in multiple entries for the same + * type, but we don't care much, as they are going to be roughly + * the same, and the last will always trump and the former + * entries will expire (see above). + */ + bad = bcentry_new(bc, name, type, flags, expire); + cds_lfht_add(ht, hashval, &bad->ht_node); + } else if (update) { + atomic_store_relaxed(&found->expire, expire); + atomic_store_relaxed(&found->flags, flags); } + + rcu_read_unlock(); } -bool +isc_result_t dns_badcache_find(dns_badcache_t *bc, const dns_name_t *name, - dns_rdatatype_t type, uint32_t *flagp, isc_time_t *now) { - dns_bcentry_t *bad, *prev, *next; - bool answer = false; - unsigned int i; - unsigned int hash; - + dns_rdatatype_t type, uint32_t *flagp, isc_stdtime_t now) { REQUIRE(VALID_BADCACHE(bc)); REQUIRE(name != NULL); - REQUIRE(now != NULL); - RWLOCK(&bc->lock, isc_rwlocktype_read); + isc_result_t result = ISC_R_NOTFOUND; - /* - * XXXMUKS: dns_name_equal() is expensive as it does a - * octet-by-octet comparison, and it can be made better in two - * ways here. First, lowercase the names (use - * dns_name_downcase() instead of dns_name_copy() in - * dns_badcache_add()) so that dns_name_caseequal() can be used - * which the compiler will emit as SIMD instructions. Second, - * don't put multiple copies of the same name in the chain (or - * multiple names will have to be matched for equality), but use - * name->link to store the type specific part. - */ + rcu_read_lock(); + struct cds_lfht *ht = rcu_dereference(bc->ht); + INSIST(ht != NULL); - if (atomic_load_relaxed(&bc->count) == 0) { - goto skip; - } + dns_bcentry_t *bad = NULL; + uint32_t hashval = dns_name_hash(name); - hash = dns_name_hash(name) % bc->size; - prev = NULL; - LOCK(&bc->tlocks[hash]); - for (bad = bc->table[hash]; bad != NULL; bad = next) { - next = bad->next; - /* - * Search the hash list. Clean out expired records as we go. - */ - if (isc_time_compare(&bad->expire, now) < 0) { - if (prev != NULL) { - prev->next = bad->next; - } else { - bc->table[hash] = bad->next; - } - - isc_mem_put(bc->mctx, bad, sizeof(*bad)); - atomic_fetch_sub(&bc->count, 1); - continue; - } - if (bad->type == type && dns_name_equal(name, bad->name)) { - if (flagp != NULL) { - *flagp = bad->flags; - } - answer = true; - break; + struct cds_lfht_iter iter; + dns_bcentry_t *found = NULL; + cds_lfht_for_each_entry_duplicate(ht, hashval, bcentry_match, name, + &iter, bad, ht_node) { + if (bad->type == type && bcentry_alive(ht, bad, now)) { + found = bad; } - prev = bad; } - UNLOCK(&bc->tlocks[hash]); -skip: - /* - * Slow sweep to clean out stale records. - */ - i = atomic_fetch_add(&bc->sweep, 1) % bc->size; - if (isc_mutex_trylock(&bc->tlocks[i]) == ISC_R_SUCCESS) { - bad = bc->table[i]; - if (bad != NULL && isc_time_compare(&bad->expire, now) < 0) { - bc->table[i] = bad->next; - isc_mem_put(bc->mctx, bad, sizeof(*bad)); - atomic_fetch_sub_relaxed(&bc->count, 1); + if (found) { + result = ISC_R_SUCCESS; + if (flagp != NULL) { + *flagp = atomic_load_relaxed(&found->flags); } - UNLOCK(&bc->tlocks[i]); + + bcentry_purge_next(ht, &iter, now); } - RWUNLOCK(&bc->lock, isc_rwlocktype_read); - return (answer); + rcu_read_unlock(); + + return (result); } void dns_badcache_flush(dns_badcache_t *bc) { - dns_bcentry_t *entry, *next; - unsigned int i; - - RWLOCK(&bc->lock, isc_rwlocktype_write); REQUIRE(VALID_BADCACHE(bc)); - for (i = 0; atomic_load_relaxed(&bc->count) > 0 && i < bc->size; i++) { - for (entry = bc->table[i]; entry != NULL; entry = next) { - next = entry->next; - isc_mem_put(bc->mctx, entry, sizeof(*entry)); - atomic_fetch_sub_relaxed(&bc->count, 1); - } - bc->table[i] = NULL; + struct cds_lfht *ht = + cds_lfht_new(BADCACHE_INIT_SIZE, BADCACHE_MIN_SIZE, 0, + CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL); + INSIST(ht != NULL); + + /* First swap the hashtables */ + rcu_read_lock(); + ht = rcu_xchg_pointer(&bc->ht, ht); + rcu_read_unlock(); + + /* Make sure nobody is using the old hash table */ + synchronize_rcu(); + + /* Flush the old hash table */ + dns_bcentry_t *bad = NULL; + struct cds_lfht_iter iter; + cds_lfht_for_each_entry(ht, &iter, bad, ht_node) { + INSIST(!cds_lfht_del(ht, &bad->ht_node)); + bcentry_destroy(&bad->rcu_head); } - RWUNLOCK(&bc->lock, isc_rwlocktype_write); + RUNTIME_CHECK(!cds_lfht_destroy(ht, NULL)); } void dns_badcache_flushname(dns_badcache_t *bc, const dns_name_t *name) { - dns_bcentry_t *bad, *prev, *next; - isc_time_t now; - unsigned int hash; - REQUIRE(VALID_BADCACHE(bc)); REQUIRE(name != NULL); - RWLOCK(&bc->lock, isc_rwlocktype_read); - - now = isc_time_now(); - hash = dns_name_hash(name) % bc->size; - LOCK(&bc->tlocks[hash]); - prev = NULL; - for (bad = bc->table[hash]; bad != NULL; bad = next) { - int n; - next = bad->next; - n = isc_time_compare(&bad->expire, &now); - if (n < 0 || dns_name_equal(name, bad->name)) { - if (prev == NULL) { - bc->table[hash] = bad->next; - } else { - prev->next = bad->next; - } - - isc_mem_put(bc->mctx, bad, sizeof(*bad)); - atomic_fetch_sub_relaxed(&bc->count, 1); - } else { - prev = bad; - } + rcu_read_lock(); + struct cds_lfht *ht = rcu_dereference(bc->ht); + INSIST(ht != NULL); + + dns_bcentry_t *bad = NULL; + uint32_t hashval = dns_name_hash(name); + + struct cds_lfht_iter iter; + cds_lfht_for_each_entry_duplicate(ht, hashval, bcentry_match, name, + &iter, bad, ht_node) { + bcentry_evict(ht, bad); } - UNLOCK(&bc->tlocks[hash]); - RWUNLOCK(&bc->lock, isc_rwlocktype_read); + rcu_read_unlock(); } void dns_badcache_flushtree(dns_badcache_t *bc, const dns_name_t *name) { - dns_bcentry_t *bad, *prev, *next; - unsigned int i; - int n; - isc_time_t now; + dns_bcentry_t *bad; + isc_stdtime_t now = isc_stdtime_now(); REQUIRE(VALID_BADCACHE(bc)); REQUIRE(name != NULL); - /* - * We write lock the tree to avoid relocking every node - * individually. - */ - RWLOCK(&bc->lock, isc_rwlocktype_write); - - now = isc_time_now(); - - for (i = 0; atomic_load_relaxed(&bc->count) > 0 && i < bc->size; i++) { - prev = NULL; - for (bad = bc->table[i]; bad != NULL; bad = next) { - next = bad->next; - n = isc_time_compare(&bad->expire, &now); - if (n < 0 || dns_name_issubdomain(bad->name, name)) { - if (prev == NULL) { - bc->table[i] = bad->next; - } else { - prev->next = bad->next; - } - - isc_mem_put(bc->mctx, bad, sizeof(*bad)); - atomic_fetch_sub_relaxed(&bc->count, 1); - } else { - prev = bad; - } + rcu_read_lock(); + struct cds_lfht *ht = rcu_dereference(bc->ht); + INSIST(ht != NULL); + + struct cds_lfht_iter iter; + cds_lfht_for_each_entry(ht, &iter, bad, ht_node) { + if (dns_name_issubdomain(bad->name, name)) { + bcentry_evict(ht, bad); + } else if (!bcentry_alive(ht, bad, now)) { + /* Flush all the expired entries */ } } - RWUNLOCK(&bc->lock, isc_rwlocktype_write); + rcu_read_unlock(); } -void -dns_badcache_print(dns_badcache_t *bc, const char *cachename, FILE *fp) { +static void +bcentry_print(dns_bcentry_t *bad, isc_stdtime_t now, FILE *fp) { char namebuf[DNS_NAME_FORMATSIZE]; char typebuf[DNS_RDATATYPE_FORMATSIZE]; - dns_bcentry_t *bad, *next, *prev; - isc_time_t now; - unsigned int i; - uint64_t t; + + dns_name_format(bad->name, namebuf, sizeof(namebuf)); + dns_rdatatype_format(bad->type, typebuf, sizeof(typebuf)); + fprintf(fp, "; %s/%s [ttl %" PRIu32 "]\n", namebuf, typebuf, + atomic_load_relaxed(&bad->expire) - now); +} + +void +dns_badcache_print(dns_badcache_t *bc, const char *cachename, FILE *fp) { + dns_bcentry_t *bad; + isc_stdtime_t now = isc_stdtime_now(); REQUIRE(VALID_BADCACHE(bc)); - REQUIRE(cachename != NULL); REQUIRE(fp != NULL); - /* - * We write lock the tree to avoid relocking every node - * individually. - */ - RWLOCK(&bc->lock, isc_rwlocktype_write); fprintf(fp, ";\n; %s\n;\n", cachename); - now = isc_time_now(); - for (i = 0; atomic_load_relaxed(&bc->count) > 0 && i < bc->size; i++) { - prev = NULL; - for (bad = bc->table[i]; bad != NULL; bad = next) { - next = bad->next; - if (isc_time_compare(&bad->expire, &now) < 0) { - if (prev != NULL) { - prev->next = bad->next; - } else { - bc->table[i] = bad->next; - } - - isc_mem_put(bc->mctx, bad, sizeof(*bad)); - atomic_fetch_sub_relaxed(&bc->count, 1); - continue; - } - prev = bad; - dns_name_format(bad->name, namebuf, sizeof(namebuf)); - dns_rdatatype_format(bad->type, typebuf, - sizeof(typebuf)); - t = isc_time_microdiff(&bad->expire, &now); - t /= 1000; - fprintf(fp, - "; %s/%s [ttl " - "%" PRIu64 "]\n", - namebuf, typebuf, t); + rcu_read_lock(); + struct cds_lfht *ht = rcu_dereference(bc->ht); + INSIST(ht != NULL); + + struct cds_lfht_iter iter; + cds_lfht_for_each_entry(ht, &iter, bad, ht_node) { + if (bcentry_alive(ht, bad, now)) { + bcentry_print(bad, now, fp); } } - RWUNLOCK(&bc->lock, isc_rwlocktype_write); + + rcu_read_unlock(); } diff --git a/lib/dns/include/dns/badcache.h b/lib/dns/include/dns/badcache.h index 1021ba34a87..03eb2ec3b9a 100644 --- a/lib/dns/include/dns/badcache.h +++ b/lib/dns/include/dns/badcache.h @@ -45,6 +45,9 @@ #include #include +#include +#include + #include ISC_LANG_BEGINDECLS @@ -53,8 +56,8 @@ ISC_LANG_BEGINDECLS *** Functions ***/ -isc_result_t -dns_badcache_init(isc_mem_t *mctx, unsigned int size, dns_badcache_t **bcp); +dns_badcache_t * +dns_badcache_new(isc_mem_t *mctx); /*% * Allocate and initialize a badcache and store it in '*bcp'. * @@ -76,7 +79,7 @@ dns_badcache_destroy(dns_badcache_t **bcp); void dns_badcache_add(dns_badcache_t *bc, const dns_name_t *name, dns_rdatatype_t type, bool update, uint32_t flags, - isc_time_t *expire); + isc_stdtime_t expire); /*% * Adds a badcache entry to the badcache 'bc' for name 'name' and * type 'type'. If an entry already exists, then it will be updated if @@ -89,14 +92,14 @@ dns_badcache_add(dns_badcache_t *bc, const dns_name_t *name, * \li expire != NULL */ -bool +isc_result_t dns_badcache_find(dns_badcache_t *bc, const dns_name_t *name, - dns_rdatatype_t type, uint32_t *flagp, isc_time_t *now); + dns_rdatatype_t type, uint32_t *flagp, isc_stdtime_t now); /*% - * Returns true if a record is found in the badcache 'bc' matching + * Returns ISC_R_SUCCESS if a record is found in the badcache 'bc' matching * 'name' and 'type', with an expiration date later than 'now'. * If 'flagp' is not NULL, then '*flagp' is updated to the flags - * that were stored in the badcache entry. Returns false if + * that were stored in the badcache entry. Returns ISC_R_NOTFOUND if * no matching record is found. * * Requires: diff --git a/lib/dns/include/dns/resolver.h b/lib/dns/include/dns/resolver.h index 5faa165e028..aeee2fa620d 100644 --- a/lib/dns/include/dns/resolver.h +++ b/lib/dns/include/dns/resolver.h @@ -588,7 +588,7 @@ dns_resolver_addbadcache(dns_resolver_t *resolver, const dns_name_t *name, * \li name to be valid. */ -bool +isc_result_t dns_resolver_getbadcache(dns_resolver_t *resolver, const dns_name_t *name, dns_rdatatype_t type, isc_time_t *now); /*%< diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index 201979df61b..0af07b8273e 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -236,7 +236,6 @@ STATIC_ASSERT(NS_PROCESSING_LIMIT > NS_RR_LIMIT, */ #define MAX_EDNS0_TIMEOUTS 3 -#define DNS_RESOLVER_BADCACHESIZE 1021 #define DNS_RESOLVER_BADCACHETTL(fctx) \ (((fctx)->res->lame_ttl > 30) ? (fctx)->res->lame_ttl : 30) @@ -9906,7 +9905,6 @@ dns_resolver_create(dns_view_t *view, isc_loopmgr_t *loopmgr, isc_tlsctx_cache_t *tlsctx_cache, dns_dispatch_t *dispatchv4, dns_dispatch_t *dispatchv6, dns_resolver_t **resp) { - isc_result_t result = ISC_R_SUCCESS; dns_resolver_t *res = NULL; /* @@ -9951,11 +9949,7 @@ dns_resolver_create(dns_view_t *view, isc_loopmgr_t *loopmgr, #endif isc_refcount_init(&res->references, 1); - result = dns_badcache_init(res->mctx, DNS_RESOLVER_BADCACHESIZE, - &res->badcache); - if (result != ISC_R_SUCCESS) { - goto cleanup; - } + res->badcache = dns_badcache_new(res->mctx); /* This needs to be case sensitive to not lowercase options and type */ isc_hashmap_create(view->mctx, RES_DOMAIN_HASH_BITS, @@ -9984,11 +9978,6 @@ dns_resolver_create(dns_view_t *view, isc_loopmgr_t *loopmgr, *resp = res; return (ISC_R_SUCCESS); - -cleanup: - dns_view_weakdetach(&res->view); - isc_mem_putanddetach(&res->mctx, res, sizeof(*res)); - return (result); } static void @@ -10730,14 +10719,15 @@ dns_resolver_addbadcache(dns_resolver_t *resolver, const dns_name_t *name, #endif /* ifdef ENABLE_AFL */ { dns_badcache_add(resolver->badcache, name, type, false, 0, - expire); + isc_time_seconds(expire)); } } -bool +isc_result_t dns_resolver_getbadcache(dns_resolver_t *resolver, const dns_name_t *name, dns_rdatatype_t type, isc_time_t *now) { - return (dns_badcache_find(resolver->badcache, name, type, NULL, now)); + return (dns_badcache_find(resolver->badcache, name, type, NULL, + isc_time_seconds(now))); } void diff --git a/lib/dns/validator.c b/lib/dns/validator.c index 3447d0482b3..0887c07ad97 100644 --- a/lib/dns/validator.c +++ b/lib/dns/validator.c @@ -857,7 +857,9 @@ view_find(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type) { disassociate_rdatasets(val); - if (dns_resolver_getbadcache(val->view->resolver, name, type, &now)) { + result = dns_resolver_getbadcache(val->view->resolver, name, type, + &now); + if (result == ISC_R_SUCCESS) { dns_name_format(name, namebuf, sizeof(namebuf)); dns_rdatatype_format(type, typebuf, sizeof(typebuf)); validator_log(val, ISC_LOG_INFO, "bad cache hit (%s/%s)", diff --git a/lib/dns/view.c b/lib/dns/view.c index 0399ad73f8b..1fe0c16dabd 100644 --- a/lib/dns/view.c +++ b/lib/dns/view.c @@ -71,8 +71,7 @@ goto cleanup; \ } while (0) -#define DNS_VIEW_DELONLYHASH 111 -#define DNS_VIEW_FAILCACHESIZE 1021 +#define DNS_VIEW_DELONLYHASH 111 /*% * Default EDNS0 buffer size @@ -146,11 +145,7 @@ dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, const char *name, dns_tsigkeyring_create(view->mctx, &view->dynamickeys); - result = dns_badcache_init(view->mctx, DNS_VIEW_FAILCACHESIZE, - &view->failcache); - if (result != ISC_R_SUCCESS) { - goto cleanup_dynkeys; - } + view->failcache = dns_badcache_new(view->mctx); isc_mutex_init(&view->new_zone_lock); @@ -188,7 +183,6 @@ cleanup_new_zone_lock: isc_mutex_destroy(&view->new_zone_lock); dns_badcache_destroy(&view->failcache); -cleanup_dynkeys: if (view->dynamickeys != NULL) { dns_tsigkeyring_detach(&view->dynamickeys); } diff --git a/lib/ns/client.c b/lib/ns/client.c index d407cb35685..061d8760b7e 100644 --- a/lib/ns/client.c +++ b/lib/ns/client.c @@ -943,9 +943,10 @@ ns_client_error(ns_client_t *client, isc_result_t result) { isc_interval_set(&i, client->view->fail_ttl, 0); result = isc_time_nowplusinterval(&expire, &i); if (result == ISC_R_SUCCESS) { - dns_badcache_add( - client->view->failcache, client->query.qname, - client->query.qtype, true, flags, &expire); + dns_badcache_add(client->view->failcache, + client->query.qname, + client->query.qtype, true, flags, + isc_time_seconds(&expire)); } } diff --git a/lib/ns/query.c b/lib/ns/query.c index 73495820043..9d4ba5c13cc 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -7000,7 +7000,7 @@ cleanup: */ isc_result_t ns__query_sfcache(query_ctx_t *qctx) { - bool failcache; + isc_result_t failcache; uint32_t flags; /* @@ -7013,20 +7013,22 @@ ns__query_sfcache(query_ctx_t *qctx) { flags = 0; #ifdef ENABLE_AFL if (qctx->client->manager->sctx->fuzztype == isc_fuzz_resolver) { - failcache = false; - } else { + failcache = ISC_R_NOTFOUND; + } else +#endif /* ifdef ENABLE_AFL */ + { failcache = dns_badcache_find( qctx->view->failcache, qctx->client->query.qname, - qctx->qtype, &flags, &qctx->client->tnow); + qctx->qtype, &flags, + isc_time_seconds(&qctx->client->tnow)); } -#else /* ifdef ENABLE_AFL */ - failcache = dns_badcache_find(qctx->view->failcache, - qctx->client->query.qname, qctx->qtype, - &flags, &qctx->client->tnow); -#endif /* ifdef ENABLE_AFL */ - if (failcache && - (((flags & NS_FAILCACHE_CD) != 0) || - ((qctx->client->message->flags & DNS_MESSAGEFLAG_CD) == 0))) + + if (failcache != ISC_R_SUCCESS) { + return (ISC_R_COMPLETE); + } + + if (((flags & NS_FAILCACHE_CD) != 0) || + ((qctx->client->message->flags & DNS_MESSAGEFLAG_CD) == 0)) { if (isc_log_wouldlog(ns_lctx, ISC_LOG_DEBUG(1))) { char namebuf[DNS_NAME_FORMATSIZE]; diff --git a/tests/dns/Makefile.am b/tests/dns/Makefile.am index ea57322d975..da775f4995f 100644 --- a/tests/dns/Makefile.am +++ b/tests/dns/Makefile.am @@ -19,6 +19,7 @@ LDADD += \ check_PROGRAMS = \ acl_test \ + badcache_test \ db_test \ dbdiff_test \ dbiterator_test \ diff --git a/tests/dns/badcache_test.c b/tests/dns/badcache_test.c new file mode 100644 index 00000000000..dfe8adf6dff --- /dev/null +++ b/tests/dns/badcache_test.c @@ -0,0 +1,347 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include +#include /* IWYU pragma: keep */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define UNIT_TESTING +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +static uint32_t +crc32(const uint8_t *buf, size_t size) { + uint32_t crc; + crc = 0xFFFFFFFF; + while (size--) { + crc = crc ^ *buf++; + for (size_t j = 0; j < 8; j++) { + uint32_t mask = -(crc & 1); + crc = (crc >> 1) ^ (0xedb88320 & mask); + } + } + return (~crc); +} + +#define BADCACHE_TEST_FLAG 1 << 3 + +ISC_LOOP_TEST_IMPL(basic) { + dns_badcache_t *bc = NULL; + dns_fixedname_t fname = { 0 }; + dns_name_t *name = dns_fixedname_initname(&fname); + isc_stdtime_t now = isc_stdtime_now(); + isc_result_t result; + uint32_t flags = BADCACHE_TEST_FLAG; + + dns_name_fromstring2(name, "example.com.", NULL, 0, NULL); + + bc = dns_badcache_new(mctx); + dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now + 60); + + flags = 0; + result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now); + assert_int_equal(result, ISC_R_SUCCESS); + assert_int_equal(flags, BADCACHE_TEST_FLAG); + + flags = 0; + result = dns_badcache_find(bc, name, dns_rdatatype_a, &flags, now); + assert_int_equal(result, ISC_R_NOTFOUND); + assert_int_equal(flags, 0); + + dns_badcache_destroy(&bc); + + isc_loopmgr_shutdown(loopmgr); +} + +ISC_LOOP_TEST_IMPL(expire) { + dns_badcache_t *bc = NULL; + dns_fixedname_t fname = { 0 }; + dns_name_t *name = dns_fixedname_initname(&fname); + isc_stdtime_t now = isc_stdtime_now(); + isc_result_t result; + uint32_t flags = BADCACHE_TEST_FLAG; + + dns_name_fromstring2(name, "example.com.", NULL, 0, NULL); + + bc = dns_badcache_new(mctx); + dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now + 60); + dns_badcache_add(bc, name, dns_rdatatype_a, false, flags, now + 60); + + result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now); + assert_int_equal(result, ISC_R_SUCCESS); + assert_int_equal(flags, BADCACHE_TEST_FLAG); + + result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, + now + 61); + assert_int_equal(result, ISC_R_NOTFOUND); + + result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now); + assert_int_equal(result, ISC_R_NOTFOUND); + + result = dns_badcache_find(bc, name, dns_rdatatype_a, &flags, now); + assert_int_equal(result, ISC_R_SUCCESS); + assert_int_equal(flags, BADCACHE_TEST_FLAG); + + dns_badcache_add(bc, name, dns_rdatatype_a, true, flags, now + 120); + + result = dns_badcache_find(bc, name, dns_rdatatype_a, &flags, now + 61); + assert_int_equal(result, ISC_R_SUCCESS); + assert_int_equal(flags, BADCACHE_TEST_FLAG); + + dns_badcache_destroy(&bc); + + isc_loopmgr_shutdown(loopmgr); +} + +ISC_LOOP_TEST_IMPL(print) { + dns_badcache_t *bc = NULL; + dns_fixedname_t fname = { 0 }; + dns_name_t *name = dns_fixedname_initname(&fname); + isc_stdtime_t now = isc_stdtime_now(); + isc_stdtime_t expire = now + 60; + uint32_t flags = BADCACHE_TEST_FLAG; + FILE *file = NULL; + uint8_t buf[4096]; + size_t len; + + dns_name_fromstring2(name, "example.com.", NULL, 0, NULL); + + bc = dns_badcache_new(mctx); + dns_badcache_add(bc, name, dns_rdatatype_a, false, flags, expire); + dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, expire); + + file = fopen("./badcache.out", "w"); + dns_badcache_print(bc, "badcache", file); + fclose(file); + + file = fopen("./badcache.out", "r"); + len = fread(buf, sizeof(buf[0]), ARRAY_SIZE(buf), file); + assert_int_equal(len, 68); + fclose(file); + + /* Calculated as crc32 ./badcache.out */ + assert_int_equal(crc32(buf, len), 0x7c96678f); + + dns_badcache_destroy(&bc); + + isc_loopmgr_shutdown(loopmgr); +} + +ISC_LOOP_TEST_IMPL(flush) { + dns_badcache_t *bc = NULL; + dns_fixedname_t fname = { 0 }; + dns_name_t *name = dns_fixedname_initname(&fname); + isc_stdtime_t now = isc_stdtime_now(); + isc_result_t result; + uint32_t flags = BADCACHE_TEST_FLAG; + + dns_name_fromstring2(name, "example.com.", NULL, 0, NULL); + + bc = dns_badcache_new(mctx); + dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now + 60); + + result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now); + assert_int_equal(result, ISC_R_SUCCESS); + + dns_badcache_flush(bc); + + result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now); + assert_int_equal(result, ISC_R_NOTFOUND); + + dns_badcache_destroy(&bc); + + isc_loopmgr_shutdown(loopmgr); +} + +ISC_LOOP_TEST_IMPL(flushname) { + dns_badcache_t *bc = NULL; + dns_fixedname_t fname = { 0 }; + dns_name_t *name = dns_fixedname_initname(&fname); + isc_stdtime_t now = isc_stdtime_now(); + isc_result_t result; + uint32_t flags = BADCACHE_TEST_FLAG; + + bc = dns_badcache_new(mctx); + + dns_name_fromstring2(name, "example.com.", NULL, 0, NULL); + dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now + 60); + result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now); + assert_int_equal(result, ISC_R_SUCCESS); + + dns_name_fromstring2(name, "sub.example.com.", NULL, 0, NULL); + dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now + 60); + result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now); + assert_int_equal(result, ISC_R_SUCCESS); + + dns_name_fromstring2(name, "sub.sub.example.com.", NULL, 0, NULL); + dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now + 60); + result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now); + assert_int_equal(result, ISC_R_SUCCESS); + + dns_name_fromstring2(name, "sub.example.com.", NULL, 0, NULL); + dns_badcache_flushname(bc, name); + + result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now); + assert_int_equal(result, ISC_R_NOTFOUND); + result = dns_badcache_find(bc, name, dns_rdatatype_a, &flags, now); + assert_int_equal(result, ISC_R_NOTFOUND); + + dns_name_fromstring2(name, "sub.sub.example.com.", NULL, 0, NULL); + result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now); + assert_int_equal(result, ISC_R_SUCCESS); + + dns_name_fromstring2(name, "example.com.", NULL, 0, NULL); + result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now); + assert_int_equal(result, ISC_R_SUCCESS); + + dns_badcache_destroy(&bc); + + isc_loopmgr_shutdown(loopmgr); +} + +ISC_LOOP_TEST_IMPL(flushtree) { + dns_badcache_t *bc = NULL; + dns_fixedname_t fname = { 0 }; + dns_name_t *name = dns_fixedname_initname(&fname); + isc_stdtime_t now = isc_stdtime_now(); + isc_result_t result; + uint32_t flags = BADCACHE_TEST_FLAG; + + bc = dns_badcache_new(mctx); + + dns_name_fromstring2(name, "example.com.", NULL, 0, NULL); + dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now + 60); + result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now); + assert_int_equal(result, ISC_R_SUCCESS); + assert_int_equal(flags, BADCACHE_TEST_FLAG); + + dns_name_fromstring2(name, "sub.example.com.", NULL, 0, NULL); + dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now + 60); + result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now); + assert_int_equal(result, ISC_R_SUCCESS); + assert_int_equal(flags, BADCACHE_TEST_FLAG); + + dns_name_fromstring2(name, "sub.sub.example.com.", NULL, 0, NULL); + dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now + 60); + result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now); + assert_int_equal(result, ISC_R_SUCCESS); + assert_int_equal(flags, BADCACHE_TEST_FLAG); + + dns_name_fromstring2(name, "sub.example.com.", NULL, 0, NULL); + dns_badcache_flushtree(bc, name); + + dns_name_fromstring2(name, "sub.sub.example.com.", NULL, 0, NULL); + result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now); + assert_int_equal(result, ISC_R_NOTFOUND); + + dns_name_fromstring2(name, "sub.example.com.", NULL, 0, NULL); + result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now); + assert_int_equal(result, ISC_R_NOTFOUND); + + dns_name_fromstring2(name, "example.com.", NULL, 0, NULL); + result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, now); + assert_int_equal(result, ISC_R_SUCCESS); + assert_int_equal(flags, BADCACHE_TEST_FLAG); + + dns_badcache_destroy(&bc); + + isc_loopmgr_shutdown(loopmgr); +} + +ISC_LOOP_TEST_IMPL(purge) { + dns_badcache_t *bc = NULL; + dns_fixedname_t fname = { 0 }; + dns_name_t *name = dns_fixedname_initname(&fname); + isc_stdtime_t now = isc_stdtime_now(); + isc_result_t result; + uint32_t flags = BADCACHE_TEST_FLAG; + + bc = dns_badcache_new(mctx); + + dns_name_fromstring2(name, "example.com.", NULL, 0, NULL); + dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now); + result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, + now - 60); + assert_int_equal(result, ISC_R_SUCCESS); + + dns_name_fromstring2(name, "sub.example.com.", NULL, 0, NULL); + dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now); + result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, + now - 60); + assert_int_equal(result, ISC_R_SUCCESS); + + dns_name_fromstring2(name, "sub.sub.example.com.", NULL, 0, NULL); + dns_badcache_add(bc, name, dns_rdatatype_aaaa, false, flags, now); + result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, + now - 60); + assert_int_equal(result, ISC_R_SUCCESS); + + result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, + now + 30); + assert_int_equal(result, ISC_R_NOTFOUND); + + dns_name_fromstring2(name, "sub.sub.example.com.", NULL, 0, NULL); + result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, + now + 30); + assert_int_equal(result, ISC_R_NOTFOUND); + + dns_name_fromstring2(name, "sub.example.com.", NULL, 0, NULL); + result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, + now + 30); + assert_int_equal(result, ISC_R_NOTFOUND); + + dns_name_fromstring2(name, "example.com.", NULL, 0, NULL); + result = dns_badcache_find(bc, name, dns_rdatatype_aaaa, &flags, + now + 30); + assert_int_equal(result, ISC_R_NOTFOUND); + + dns_badcache_destroy(&bc); + + isc_loopmgr_shutdown(loopmgr); +} + +ISC_TEST_LIST_START +ISC_TEST_ENTRY_CUSTOM(basic, setup_managers, teardown_managers) +ISC_TEST_ENTRY_CUSTOM(expire, setup_managers, teardown_managers) +ISC_TEST_ENTRY_CUSTOM(print, setup_managers, teardown_managers) +ISC_TEST_ENTRY_CUSTOM(flush, setup_managers, teardown_managers) +ISC_TEST_ENTRY_CUSTOM(flushname, setup_managers, teardown_managers) +ISC_TEST_ENTRY_CUSTOM(flushtree, setup_managers, teardown_managers) +ISC_TEST_ENTRY_CUSTOM(purge, setup_managers, teardown_managers) +ISC_TEST_LIST_END + +ISC_TEST_MAIN diff --git a/tests/ns/query_test.c b/tests/ns/query_test.c index adfcc031007..6ef0fdd12b3 100644 --- a/tests/ns/query_test.c +++ b/tests/ns/query_test.c @@ -122,7 +122,8 @@ run_sfcache_test(const ns__query_sfcache_test_params_t *test) { dns_badcache_add(qctx->client->view->failcache, dns_rootname, dns_rdatatype_ns, true, - test->cache_entry_flags, &expire); + test->cache_entry_flags, + isc_time_seconds(&expire)); } /*