diff --git a/lib/dns/adb.c b/lib/dns/adb.c index 9471bbfca0..027378136e 100644 --- a/lib/dns/adb.c +++ b/lib/dns/adb.c @@ -24,6 +24,8 @@ #include #include +#include +#include #include #include #include @@ -79,11 +81,6 @@ #define ADB_STALE_MARGIN 1800 #endif /* ifndef ADB_STALE_MARGIN */ -#define FREE_ITEMS 64 /*%< free count for memory pools */ -#define FILL_COUNT 16 /*%< fill count for memory pools */ - -#define DNS_ADB_INVALIDBUCKET (-1) /*%< invalid bucket address */ - #define DNS_ADB_MINADBSIZE (1024U * 1024U) /*%< 1 Megabyte */ typedef ISC_LIST(dns_adbname_t) dns_adbnamelist_t; @@ -99,8 +96,6 @@ struct dns_adb { unsigned int magic; isc_mutex_t lock; - isc_mutex_t reflock; /*%< Covers irefcnt, erefcnt */ - isc_mutex_t overmemlock; /*%< Covers overmem */ isc_mem_t *mctx; dns_view_t *view; @@ -111,48 +106,23 @@ struct dns_adb { isc_interval_t tick_interval; int next_cleanbucket; - unsigned int irefcnt; - unsigned int erefcnt; + isc_refcount_t irefcnt; + isc_refcount_t erefcnt; isc_refcount_t ahrefcnt; isc_refcount_t nhrefcnt; /*! * Bucketized locks and lists for names. - * - * XXXRTH Have a per-bucket structure that contains all of these? */ - unsigned int nnames; - isc_mutex_t namescntlock; - unsigned int namescnt; - dns_adbnamelist_t *names; - dns_adbnamelist_t *deadnames; - isc_mutex_t *namelocks; - bool *name_sd; - unsigned int *name_refcnt; - - /*! - * Bucketized locks and lists for entries. - * - * XXXRTH Have a per-bucket structure that contains all of these? - */ - unsigned int nentries; - isc_mutex_t entriescntlock; - unsigned int entriescnt; - dns_adbentrylist_t *entries; - dns_adbentrylist_t *deadentries; - isc_mutex_t *entrylocks; - bool *entry_sd; /*%< shutting down */ - unsigned int *entry_refcnt; + /* FIXME: This needs RWLOCK */ + isc_ht_t *namebuckets; + isc_ht_t *entrybuckets; isc_event_t cevent; bool cevent_out; atomic_bool shutting_down; isc_eventlist_t whenshutdown; - isc_event_t growentries; - bool growentries_sent; - isc_event_t grownames; - bool grownames_sent; uint32_t quota; uint32_t atr_freq; @@ -172,7 +142,7 @@ struct dns_adbname { dns_adb_t *adb; unsigned int partial_result; unsigned int flags; - int lock_bucket; + dns_adbnamebucket_t *bucket; dns_name_t target; isc_stdtime_t expire_target; isc_stdtime_t expire_v4; @@ -191,6 +161,15 @@ struct dns_adbname { ISC_LINK(dns_adbname_t) plink; }; +/*% dns_adbnamebucket structure */ +struct dns_adbnamebucket { + dns_adbnamelist_t names; + dns_adbnamelist_t deadnames; + isc_mutex_t lock; + bool shuttingdown; + isc_refcount_t references; +}; + /*% The adbfetch structure */ struct dns_adbfetch { unsigned int magic; @@ -233,9 +212,10 @@ struct dns_adblameinfo { struct dns_adbentry { unsigned int magic; - int lock_bucket; - unsigned int refcnt; - unsigned int nh; + isc_refcount_t references; + + dns_adb_t *adb; + dns_adbentrybucket_t *bucket; unsigned int flags; unsigned int srtt; @@ -246,6 +226,7 @@ struct dns_adbentry { unsigned char plainto; unsigned char edns; unsigned char ednsto; + unsigned int nh; uint8_t mode; atomic_uint_fast32_t quota; @@ -263,18 +244,29 @@ struct dns_adbentry { * persist until that time. This allows entries found * using dns_adb_findaddrinfo() to persist for a limited time * even though they are not necessarily associated with a - * name. + * entry. */ ISC_LIST(dns_adblameinfo_t) lameinfo; ISC_LINK(dns_adbentry_t) plink; }; +/*% dns_adbentrybucket_t structure */ +struct dns_adbentrybucket { + dns_adbentrylist_t entries; + dns_adbentrylist_t deadentries; + isc_mutex_t lock; + bool shuttingdown; + isc_refcount_t references; +}; + /* * 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 *); static void free_adbname(dns_adb_t *, dns_adbname_t **); static dns_adbnamehook_t * @@ -288,7 +280,9 @@ free_adblameinfo(dns_adb_t *, dns_adblameinfo_t **); static dns_adbentry_t * new_adbentry(dns_adb_t *); static void -free_adbentry(dns_adb_t *, dns_adbentry_t **); +free_adbentry(dns_adbentry_t **); +static dns_adbentrybucket_t * +new_adbentrybucket(dns_adb_t *adb); static dns_adbfind_t * new_adbfind(dns_adb_t *); static bool @@ -300,9 +294,11 @@ new_adbfetch(dns_adb_t *); static void free_adbfetch(dns_adb_t *, dns_adbfetch_t **); static dns_adbname_t * -find_name_and_lock(dns_adb_t *, const dns_name_t *, unsigned int, int *); +find_name_and_lock(dns_adb_t *, const dns_name_t *, unsigned int, + dns_adbnamebucket_t **); static dns_adbentry_t * -find_entry_and_lock(dns_adb_t *, const isc_sockaddr_t *, int *, isc_stdtime_t); +find_entry_and_lock(dns_adb_t *, const isc_sockaddr_t *, isc_stdtime_t, + dns_adbentrybucket_t **); static void dump_adb(dns_adb_t *, FILE *, bool debug, isc_stdtime_t); static void @@ -319,9 +315,9 @@ inc_adb_irefcnt(dns_adb_t *); static void inc_adb_erefcnt(dns_adb_t *); static void -inc_entry_refcnt(dns_adb_t *, dns_adbentry_t *, bool); +inc_entry_refcnt(dns_adbentry_t *, bool); static bool -dec_entry_refcnt(dns_adb_t *, bool, dns_adbentry_t *, bool); +dec_entry_refcnt(dns_adbentry_t *, bool); static void violate_locking_hierarchy(isc_mutex_t *, isc_mutex_t *); static bool @@ -333,7 +329,7 @@ clean_finds_at_name(dns_adbname_t *, isc_eventtype_t, unsigned int); static bool check_expire_namehooks(dns_adbname_t *, isc_stdtime_t); static bool -check_expire_entry(dns_adb_t *, dns_adbentry_t **, isc_stdtime_t); +check_expire_entry(dns_adbentry_t **, isc_stdtime_t); static void cancel_fetches_at_name(dns_adbname_t *); static isc_result_t @@ -345,18 +341,18 @@ static void check_exit(dns_adb_t *); static void destroy(dns_adb_t *); -static bool +static void shutdown_names(dns_adb_t *); -static bool +static void shutdown_entries(dns_adb_t *); static void -link_name(dns_adb_t *, int, dns_adbname_t *); +link_name(dns_adbnamebucket_t *, dns_adbname_t *); static bool -unlink_name(dns_adb_t *, dns_adbname_t *); +unlink_name(dns_adbname_t *); static void -link_entry(dns_adb_t *, int, dns_adbentry_t *); +link_entry(dns_adbentrybucket_t *, dns_adbentry_t *); static bool -unlink_entry(dns_adb_t *, dns_adbentry_t *); +unlink_entry(dns_adbentry_t *); static bool kill_name(dns_adbname_t **, isc_eventtype_t); static void @@ -543,346 +539,6 @@ ttlclamp(dns_ttl_t ttl) { return (ttl); } -/* - * Hashing is most efficient if the number of buckets is prime. - * The sequence below is the closest previous primes to 2^n and - * 1.5 * 2^n, for values of n from 10 to 28. (The tables will - * no longer grow beyond 2^28 entries.) - */ -static const unsigned nbuckets[] = { - 1021, 1531, 2039, 3067, 4093, 6143, - 8191, 12281, 16381, 24571, 32749, 49193, - 65521, 98299, 131071, 199603, 262139, 393209, - 524287, 768431, 1048573, 1572853, 2097143, 3145721, - 4194301, 6291449, 8388593, 12582893, 16777213, 25165813, - 33554393, 50331599, 67108859, 100663291, 134217689, 201326557, - 268535431, 0 -}; - -static void -grow_entries(isc_task_t *task, isc_event_t *ev) { - dns_adb_t *adb = (dns_adb_t *)ev->ev_arg; - dns_adbentry_t *e = NULL; - dns_adbentrylist_t *newdeadentries = NULL; - dns_adbentrylist_t *newentries = NULL; - bool *newentry_sd = NULL; - isc_mutex_t *newentrylocks = NULL; - isc_result_t result; - unsigned int *newentry_refcnt = NULL; - unsigned int i, n, bucket; - - REQUIRE(DNS_ADB_VALID(adb)); - - isc_event_free(&ev); - - result = isc_task_beginexclusive(task); - if (result != ISC_R_SUCCESS) { - goto check_exit; - } - - i = 0; - while (nbuckets[i] != 0 && adb->nentries >= nbuckets[i]) { - i++; - } - if (nbuckets[i] != 0) { - n = nbuckets[i]; - } else { - goto done; - } - - DP(ISC_LOG_INFO, "adb: grow_entries to %u starting", n); - - /* - * Are we shutting down? - */ - for (i = 0; i < adb->nentries; i++) { - if (adb->entry_sd[i]) { - goto cleanup; - - /* - * Grab all the resources we need. - */ - } - } - - /* - * Grab all the resources we need. - */ - newentries = isc_mem_get(adb->mctx, sizeof(*newentries) * n); - newdeadentries = isc_mem_get(adb->mctx, sizeof(*newdeadentries) * n); - newentrylocks = isc_mem_get(adb->mctx, sizeof(*newentrylocks) * n); - newentry_sd = isc_mem_get(adb->mctx, sizeof(*newentry_sd) * n); - newentry_refcnt = isc_mem_get(adb->mctx, sizeof(*newentry_refcnt) * n); - - /* - * Initialise the new resources. - */ - isc_mutexblock_init(newentrylocks, n); - - for (i = 0; i < n; i++) { - ISC_LIST_INIT(newentries[i]); - ISC_LIST_INIT(newdeadentries[i]); - newentry_sd[i] = false; - newentry_refcnt[i] = 0; - adb->irefcnt++; - } - - /* - * Move entries to new arrays. - */ - for (i = 0; i < adb->nentries; i++) { - e = ISC_LIST_HEAD(adb->entries[i]); - while (e != NULL) { - ISC_LIST_UNLINK(adb->entries[i], e, plink); - bucket = isc_sockaddr_hash(&e->sockaddr, true) % n; - e->lock_bucket = bucket; - ISC_LIST_APPEND(newentries[bucket], e, plink); - INSIST(adb->entry_refcnt[i] > 0); - adb->entry_refcnt[i]--; - newentry_refcnt[bucket]++; - e = ISC_LIST_HEAD(adb->entries[i]); - } - e = ISC_LIST_HEAD(adb->deadentries[i]); - while (e != NULL) { - ISC_LIST_UNLINK(adb->deadentries[i], e, plink); - bucket = isc_sockaddr_hash(&e->sockaddr, true) % n; - e->lock_bucket = bucket; - ISC_LIST_APPEND(newdeadentries[bucket], e, plink); - INSIST(adb->entry_refcnt[i] > 0); - adb->entry_refcnt[i]--; - newentry_refcnt[bucket]++; - e = ISC_LIST_HEAD(adb->deadentries[i]); - } - INSIST(adb->entry_refcnt[i] == 0); - adb->irefcnt--; - } - - /* - * Cleanup old resources. - */ - isc_mutexblock_destroy(adb->entrylocks, adb->nentries); - isc_mem_put(adb->mctx, adb->entries, - sizeof(*adb->entries) * adb->nentries); - isc_mem_put(adb->mctx, adb->deadentries, - sizeof(*adb->deadentries) * adb->nentries); - isc_mem_put(adb->mctx, adb->entrylocks, - sizeof(*adb->entrylocks) * adb->nentries); - isc_mem_put(adb->mctx, adb->entry_sd, - sizeof(*adb->entry_sd) * adb->nentries); - isc_mem_put(adb->mctx, adb->entry_refcnt, - sizeof(*adb->entry_refcnt) * adb->nentries); - - /* - * Install new resources. - */ - adb->entries = newentries; - adb->deadentries = newdeadentries; - adb->entrylocks = newentrylocks; - adb->entry_sd = newentry_sd; - adb->entry_refcnt = newentry_refcnt; - adb->nentries = n; - - set_adbstat(adb, adb->nentries, dns_adbstats_nentries); - - /* - * Only on success do we set adb->growentries_sent to false. - * This will prevent us being continuously being called on error. - */ - adb->growentries_sent = false; - goto done; - -cleanup: - if (newentries != NULL) { - isc_mem_put(adb->mctx, newentries, sizeof(*newentries) * n); - } - if (newdeadentries != NULL) { - isc_mem_put(adb->mctx, newdeadentries, - sizeof(*newdeadentries) * n); - } - if (newentrylocks != NULL) { - isc_mem_put(adb->mctx, newentrylocks, - sizeof(*newentrylocks) * n); - } - if (newentry_sd != NULL) { - isc_mem_put(adb->mctx, newentry_sd, sizeof(*newentry_sd) * n); - } - if (newentry_refcnt != NULL) { - isc_mem_put(adb->mctx, newentry_refcnt, - sizeof(*newentry_refcnt) * n); - } -done: - isc_task_endexclusive(task); - -check_exit: - LOCK(&adb->lock); - if (dec_adb_irefcnt(adb)) { - check_exit(adb); - } - UNLOCK(&adb->lock); - DP(ISC_LOG_INFO, "adb: grow_entries finished"); -} - -static void -grow_names(isc_task_t *task, isc_event_t *ev) { - dns_adb_t *adb = ev->ev_arg; - dns_adbname_t *name = NULL; - dns_adbnamelist_t *newdeadnames = NULL; - dns_adbnamelist_t *newnames = NULL; - bool *newname_sd = NULL; - isc_mutex_t *newnamelocks = NULL; - isc_result_t result; - unsigned int *newname_refcnt = NULL; - unsigned int i, n; - unsigned int bucket; - - REQUIRE(DNS_ADB_VALID(adb)); - - isc_event_free(&ev); - - result = isc_task_beginexclusive(task); - if (result != ISC_R_SUCCESS) { - goto check_exit; - } - - i = 0; - while (nbuckets[i] != 0 && adb->nnames >= nbuckets[i]) { - i++; - } - if (nbuckets[i] != 0) { - n = nbuckets[i]; - } else { - goto done; - } - - DP(ISC_LOG_INFO, "adb: grow_names to %u starting", n); - - /* - * Are we shutting down? - */ - for (i = 0; i < adb->nnames; i++) { - if (adb->name_sd[i]) { - goto cleanup; - - /* - * Grab all the resources we need. - */ - } - } - - /* - * Grab all the resources we need. - */ - newnames = isc_mem_get(adb->mctx, sizeof(*newnames) * n); - newdeadnames = isc_mem_get(adb->mctx, sizeof(*newdeadnames) * n); - newnamelocks = isc_mem_get(adb->mctx, sizeof(*newnamelocks) * n); - newname_sd = isc_mem_get(adb->mctx, sizeof(*newname_sd) * n); - newname_refcnt = isc_mem_get(adb->mctx, sizeof(*newname_refcnt) * n); - - /* - * Initialise the new resources. - */ - isc_mutexblock_init(newnamelocks, n); - - for (i = 0; i < n; i++) { - ISC_LIST_INIT(newnames[i]); - ISC_LIST_INIT(newdeadnames[i]); - newname_sd[i] = false; - newname_refcnt[i] = 0; - adb->irefcnt++; - } - - /* - * Move names to new arrays. - */ - for (i = 0; i < adb->nnames; i++) { - name = ISC_LIST_HEAD(adb->names[i]); - while (name != NULL) { - ISC_LIST_UNLINK(adb->names[i], name, plink); - bucket = dns_name_fullhash(&name->name, true) % n; - name->lock_bucket = bucket; - ISC_LIST_APPEND(newnames[bucket], name, plink); - INSIST(adb->name_refcnt[i] > 0); - adb->name_refcnt[i]--; - newname_refcnt[bucket]++; - name = ISC_LIST_HEAD(adb->names[i]); - } - name = ISC_LIST_HEAD(adb->deadnames[i]); - while (name != NULL) { - ISC_LIST_UNLINK(adb->deadnames[i], name, plink); - bucket = dns_name_fullhash(&name->name, true) % n; - name->lock_bucket = bucket; - ISC_LIST_APPEND(newdeadnames[bucket], name, plink); - INSIST(adb->name_refcnt[i] > 0); - adb->name_refcnt[i]--; - newname_refcnt[bucket]++; - name = ISC_LIST_HEAD(adb->deadnames[i]); - } - INSIST(adb->name_refcnt[i] == 0); - adb->irefcnt--; - } - - /* - * Cleanup old resources. - */ - isc_mutexblock_destroy(adb->namelocks, adb->nnames); - isc_mem_put(adb->mctx, adb->names, sizeof(*adb->names) * adb->nnames); - isc_mem_put(adb->mctx, adb->deadnames, - sizeof(*adb->deadnames) * adb->nnames); - isc_mem_put(adb->mctx, adb->namelocks, - sizeof(*adb->namelocks) * adb->nnames); - isc_mem_put(adb->mctx, adb->name_sd, - sizeof(*adb->name_sd) * adb->nnames); - isc_mem_put(adb->mctx, adb->name_refcnt, - sizeof(*adb->name_refcnt) * adb->nnames); - - /* - * Install new resources. - */ - adb->names = newnames; - adb->deadnames = newdeadnames; - adb->namelocks = newnamelocks; - adb->name_sd = newname_sd; - adb->name_refcnt = newname_refcnt; - adb->nnames = n; - - set_adbstat(adb, adb->nnames, dns_adbstats_nnames); - - /* - * Only on success do we set adb->grownames_sent to false. - * This will prevent us being continuously being called on error. - */ - adb->grownames_sent = false; - goto done; - -cleanup: - if (newnames != NULL) { - isc_mem_put(adb->mctx, newnames, sizeof(*newnames) * n); - } - if (newdeadnames != NULL) { - isc_mem_put(adb->mctx, newdeadnames, sizeof(*newdeadnames) * n); - } - if (newnamelocks != NULL) { - isc_mem_put(adb->mctx, newnamelocks, sizeof(*newnamelocks) * n); - } - if (newname_sd != NULL) { - isc_mem_put(adb->mctx, newname_sd, sizeof(*newname_sd) * n); - } - if (newname_refcnt != NULL) { - isc_mem_put(adb->mctx, newname_refcnt, - sizeof(*newname_refcnt) * n); - } -done: - isc_task_endexclusive(task); - -check_exit: - LOCK(&adb->lock); - if (dec_adb_irefcnt(adb)) { - check_exit(adb); - } - UNLOCK(&adb->lock); - DP(ISC_LOG_INFO, "adb: grow_names finished"); -} - /* * Requires the adbname bucket be locked and that no entry buckets be locked. * @@ -899,11 +555,11 @@ import_rdataset(dns_adbname_t *adbname, dns_rdataset_t *rdataset, struct in_addr ina; struct in6_addr in6a; isc_sockaddr_t sockaddr; - dns_adbentry_t *foundentry = NULL; /* NO CLEAN UP! */ - int addr_bucket; + dns_adbentry_t *entry = NULL; /* NO CLEAN UP! */ bool new_addresses_added; dns_rdatatype_t rdtype; dns_adbnamehooklist_t *hookhead = NULL; + dns_adbentrybucket_t *bucket = NULL; REQUIRE(DNS_ADBNAME_VALID(adbname)); @@ -914,7 +570,6 @@ import_rdataset(dns_adbname_t *adbname, dns_rdataset_t *rdataset, rdtype = rdataset->type; REQUIRE((rdtype == dns_rdatatype_a) || (rdtype == dns_rdatatype_aaaa)); - addr_bucket = DNS_ADB_INVALIDBUCKET; new_addresses_added = false; result = dns_rdataset_first(rdataset); @@ -935,31 +590,28 @@ import_rdataset(dns_adbname_t *adbname, dns_rdataset_t *rdataset, INSIST(nh == NULL); nh = new_adbnamehook(adb, NULL); - foundentry = find_entry_and_lock(adb, &sockaddr, &addr_bucket, - now); - if (foundentry == NULL) { - dns_adbentry_t *entry = NULL; - + entry = find_entry_and_lock(adb, &sockaddr, now, &bucket); + if (entry == NULL) { entry = new_adbentry(adb); entry->sockaddr = sockaddr; - entry->refcnt = 1; entry->nh = 1; nh->entry = entry; - link_entry(adb, addr_bucket, entry); + link_entry(bucket, entry); + entry->nh = 1; } else { for (anh = ISC_LIST_HEAD(*hookhead); anh != NULL; anh = ISC_LIST_NEXT(anh, plink)) { - if (anh->entry == foundentry) { + if (anh->entry == entry) { break; } } if (anh == NULL) { - foundentry->refcnt++; - foundentry->nh++; - nh->entry = foundentry; + entry->nh++; + inc_entry_refcnt(entry, false); + nh->entry = entry; } else { free_adbnamehook(adb, &nh); } @@ -973,8 +625,8 @@ import_rdataset(dns_adbname_t *adbname, dns_rdataset_t *rdataset, result = dns_rdataset_next(rdataset); } - if (addr_bucket != DNS_ADB_INVALIDBUCKET) { - UNLOCK(&adb->entrylocks[addr_bucket]); + if (bucket != NULL) { + UNLOCK(&bucket->lock); } if (rdataset->trust == dns_trust_glue || @@ -1017,35 +669,31 @@ import_rdataset(dns_adbname_t *adbname, dns_rdataset_t *rdataset, */ static bool kill_name(dns_adbname_t **n, isc_eventtype_t ev) { - dns_adbname_t *name = NULL; - bool ret = false; - bool ret4 = false, ret6 = false; - int bucket; + dns_adbname_t *adbname = NULL; dns_adb_t *adb = NULL; + bool ret4 = false, ret6 = false; + bool ret = false; REQUIRE(n != NULL); - name = *n; + adbname = *n; *n = NULL; - REQUIRE(DNS_ADBNAME_VALID(name)); + REQUIRE(DNS_ADBNAME_VALID(adbname)); - adb = name->adb; + adb = adbname->adb; REQUIRE(DNS_ADB_VALID(adb)); - DP(DEF_LEVEL, "killing name %p", name); + DP(DEF_LEVEL, "killing name %p", adbname); /* * If we're dead already, just check to see if we should go * away now or not. */ - if (NAME_DEAD(name) && !NAME_FETCH(name)) { - ret = unlink_name(adb, name); - free_adbname(adb, &name); - if (ret) { - ret = dec_adb_irefcnt(adb); - } + if (NAME_DEAD(adbname) && !NAME_FETCH(adbname)) { + ret = unlink_name(adbname); + free_adbname(adb, &adbname); return (ret); } @@ -1053,33 +701,31 @@ kill_name(dns_adbname_t **n, isc_eventtype_t ev) { * Clean up the name's various lists. These two are destructive * in that they will always empty the list. */ - clean_finds_at_name(name, ev, DNS_ADBFIND_ADDRESSMASK); - ret4 = clean_namehooks(adb, &name->v4); - ret6 = clean_namehooks(adb, &name->v6); - clean_target(adb, &name->target); + clean_finds_at_name(adbname, ev, DNS_ADBFIND_ADDRESSMASK); + ret4 = clean_namehooks(adb, &adbname->v4); + ret6 = clean_namehooks(adb, &adbname->v6); + clean_target(adb, &adbname->target); ret = (ret4 || ret6); /* * If fetches are running, cancel them. If none are running, we can * just kill the name here. */ - if (!NAME_FETCH(name)) { - INSIST(!ret); - ret = unlink_name(adb, name); - free_adbname(adb, &name); - if (ret) { - ret = dec_adb_irefcnt(adb); - } - } else { - cancel_fetches_at_name(name); - if (!NAME_DEAD(name)) { - bucket = name->lock_bucket; - ISC_LIST_UNLINK(adb->names[bucket], name, plink); - ISC_LIST_APPEND(adb->deadnames[bucket], name, plink); - name->flags |= NAME_IS_DEAD; - } + if (!NAME_FETCH(adbname)) { + ret = unlink_name(adbname); + free_adbname(adb, &adbname); + return (ret); } - return (ret); + + cancel_fetches_at_name(adbname); + if (!NAME_DEAD(adbname)) { + dns_adbnamebucket_t *bucket = adbname->bucket; + ISC_LIST_UNLINK(bucket->names, adbname, plink); + ISC_LIST_APPEND(bucket->deadnames, adbname, plink); + adbname->flags |= NAME_IS_DEAD; + } + + return (false); } /* @@ -1129,6 +775,7 @@ check_expire_namehooks(dns_adbname_t *name, isc_stdtime_t now) { clean_target(adb, &name->target); name->expire_target = INT_MAX; } + return (ret4 || ret6); } @@ -1136,99 +783,82 @@ check_expire_namehooks(dns_adbname_t *name, isc_stdtime_t now) { * Requires the name's bucket be locked. */ static void -link_name(dns_adb_t *adb, int bucket, dns_adbname_t *name) { - REQUIRE(name->lock_bucket == DNS_ADB_INVALIDBUCKET); +link_name(dns_adbnamebucket_t *bucket, dns_adbname_t *name) { + REQUIRE(name->bucket == NULL); - ISC_LIST_PREPEND(adb->names[bucket], name, plink); - name->lock_bucket = bucket; - adb->name_refcnt[bucket]++; + name->bucket = bucket; + + ISC_LIST_PREPEND(bucket->names, name, plink); + + isc_refcount_increment0(&bucket->references); } /* * Requires the name's bucket be locked. */ static bool -unlink_name(dns_adb_t *adb, dns_adbname_t *name) { - int bucket; - bool ret = false; +unlink_name(dns_adbname_t *name) { + dns_adbnamebucket_t *bucket = NULL; REQUIRE(DNS_ADBNAME_VALID(name)); - bucket = name->lock_bucket; + bucket = name->bucket; - REQUIRE(bucket != DNS_ADB_INVALIDBUCKET); + REQUIRE(bucket != NULL); if (NAME_DEAD(name)) { - ISC_LIST_UNLINK(adb->deadnames[bucket], name, plink); + ISC_LIST_UNLINK(bucket->deadnames, name, plink); } else { - ISC_LIST_UNLINK(adb->names[bucket], name, plink); + ISC_LIST_UNLINK(bucket->names, name, plink); } - name->lock_bucket = DNS_ADB_INVALIDBUCKET; - INSIST(adb->name_refcnt[bucket] > 0); - adb->name_refcnt[bucket]--; - if (adb->name_sd[bucket] && adb->name_refcnt[bucket] == 0) { - ret = true; + + name->bucket = NULL; + + if (isc_refcount_decrement(&bucket->references) == 1) { + return (bucket->shuttingdown); } - return (ret); + return (false); } /* * Requires the entry's bucket be locked. */ static void -link_entry(dns_adb_t *adb, int bucket, dns_adbentry_t *entry) { - dns_adbentry_t *e = NULL; - int i; +link_entry(dns_adbentrybucket_t *bucket, dns_adbentry_t *entry) { + REQUIRE(entry != NULL && entry->bucket == NULL); - if (isc_mem_isovermem(adb->mctx)) { - for (i = 0; i < 2; i++) { - e = ISC_LIST_TAIL(adb->entries[bucket]); - if (e == NULL) { - break; - } - if (e->refcnt == 0) { - unlink_entry(adb, e); - free_adbentry(adb, &e); - continue; - } - INSIST((e->flags & ENTRY_IS_DEAD) == 0); - e->flags |= ENTRY_IS_DEAD; - ISC_LIST_UNLINK(adb->entries[bucket], e, plink); - ISC_LIST_PREPEND(adb->deadentries[bucket], e, plink); - } - } + entry->bucket = bucket; - ISC_LIST_PREPEND(adb->entries[bucket], entry, plink); - entry->lock_bucket = bucket; - adb->entry_refcnt[bucket]++; + ISC_LIST_PREPEND(bucket->entries, entry, plink); + isc_refcount_increment0(&bucket->references); } /* * Requires the entry's bucket be locked. */ static bool -unlink_entry(dns_adb_t *adb, dns_adbentry_t *entry) { - int bucket; - bool ret = false; +unlink_entry(dns_adbentry_t *entry) { + dns_adbentrybucket_t *bucket = NULL; REQUIRE(DNS_ADBENTRY_VALID(entry)); - bucket = entry->lock_bucket; + bucket = entry->bucket; - REQUIRE(bucket != DNS_ADB_INVALIDBUCKET); + REQUIRE(bucket != NULL); if ((entry->flags & ENTRY_IS_DEAD) != 0) { - ISC_LIST_UNLINK(adb->deadentries[bucket], entry, plink); + ISC_LIST_UNLINK(bucket->deadentries, entry, plink); } else { - ISC_LIST_UNLINK(adb->entries[bucket], entry, plink); + ISC_LIST_UNLINK(bucket->entries, entry, plink); } - entry->lock_bucket = DNS_ADB_INVALIDBUCKET; - INSIST(adb->entry_refcnt[bucket] > 0); - adb->entry_refcnt[bucket]--; - if (adb->entry_sd[bucket] && adb->entry_refcnt[bucket] == 0) { - ret = true; + + entry->bucket = NULL; + + if (isc_refcount_decrement(&bucket->references) == 1) { + return (bucket->shuttingdown); } - return (ret); + + return (false); } static void @@ -1244,90 +874,85 @@ violate_locking_hierarchy(isc_mutex_t *have, isc_mutex_t *want) { * The ADB _MUST_ be locked before calling. Also, exit conditions must be * checked after calling this function. */ -static bool +static void shutdown_names(dns_adb_t *adb) { - unsigned int bucket; - bool ret = false; - dns_adbname_t *name = NULL; - dns_adbname_t *next_name = NULL; + isc_result_t result; + isc_ht_iter_t *it = NULL; - for (bucket = 0; bucket < adb->nnames; bucket++) { - LOCK(&adb->namelocks[bucket]); - adb->name_sd[bucket] = true; + isc_ht_iter_create(adb->namebuckets, &it); + for (result = isc_ht_iter_first(it); result == ISC_R_SUCCESS; + result = isc_ht_iter_next(it)) + { + dns_adbnamebucket_t *bucket = NULL; + dns_adbname_t *name = NULL; + dns_adbname_t *next_name = NULL; - name = ISC_LIST_HEAD(adb->names[bucket]); - if (name == NULL) { - /* - * This bucket has no names. We must decrement the - * irefcnt ourselves, since it will not be - * automatically triggered by a name being unlinked. - */ - INSIST(!ret); - ret = dec_adb_irefcnt(adb); - } else { - /* - * 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. - */ - while (name != NULL) { - next_name = ISC_LIST_NEXT(name, plink); - INSIST(!ret); - ret = kill_name(&name, DNS_EVENT_ADBSHUTDOWN); - name = next_name; - } + isc_ht_iter_current(it, (void **)&bucket); + INSIST(bucket != NULL); + + LOCK(&bucket->lock); + bucket->shuttingdown = true; + + /* + * 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(bucket->names); + while (name != NULL) { + next_name = ISC_LIST_NEXT(name, plink); + kill_name(&name, DNS_EVENT_ADBSHUTDOWN); + name = next_name; } - UNLOCK(&adb->namelocks[bucket]); + UNLOCK(&bucket->lock); } - return (ret); + + isc_ht_iter_destroy(&it); } /* * The ADB _MUST_ be locked before calling. Also, exit conditions must be * checked after calling this function. */ -static bool +static void shutdown_entries(dns_adb_t *adb) { - unsigned int bucket; - bool ret = false; - dns_adbentry_t *entry = NULL; - dns_adbentry_t *next_entry; + isc_result_t result; + isc_ht_iter_t *iter = NULL; - for (bucket = 0; bucket < adb->nentries; bucket++) { - LOCK(&adb->entrylocks[bucket]); - adb->entry_sd[bucket] = true; + isc_ht_iter_create(adb->entrybuckets, &iter); + for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS; + result = isc_ht_iter_next(iter)) + { + dns_adbentrybucket_t *bucket = NULL; + dns_adbentry_t *entry = NULL; + dns_adbentry_t *next_entry = NULL; - entry = ISC_LIST_HEAD(adb->entries[bucket]); - if (adb->entry_refcnt[bucket] == 0) { - /* - * This bucket has no entries. We must decrement the - * irefcnt ourselves, since it will not be - * automatically triggered by an entry being unlinked. - */ - ret = dec_adb_irefcnt(adb); - } else { + isc_ht_iter_current(iter, (void **)&bucket); + INSIST(bucket != NULL); + + LOCK(&bucket->lock); + bucket->shuttingdown = true; + + entry = ISC_LIST_HEAD(bucket->entries); + while (entry != NULL) { /* * Run through the list. Cleanup any entries not * associated with names, and which are not in use. */ - while (entry != NULL) { - next_entry = ISC_LIST_NEXT(entry, plink); - if (entry->refcnt == 0 && entry->expires != 0) { - ret = unlink_entry(adb, entry); - free_adbentry(adb, &entry); - if (ret) { - ret = dec_adb_irefcnt(adb); - } - } - entry = next_entry; + next_entry = ISC_LIST_NEXT(entry, plink); + if (entry->expires != 0) { + unlink_entry(entry); + dec_entry_refcnt(entry, false); } + entry = next_entry; } - UNLOCK(&adb->entrylocks[bucket]); + UNLOCK(&bucket->lock); } - return (ret); + + isc_ht_iter_destroy(&iter); } /* @@ -1350,12 +975,10 @@ cancel_fetches_at_name(dns_adbname_t *name) { static bool clean_namehooks(dns_adb_t *adb, dns_adbnamehooklist_t *namehooks) { dns_adbentry_t *entry = NULL; + dns_adbentrybucket_t *bucket = NULL; dns_adbnamehook_t *namehook = NULL; - int addr_bucket; bool ret = false; - bool overmem = isc_mem_isovermem(adb->mctx); - addr_bucket = DNS_ADB_INVALIDBUCKET; namehook = ISC_LIST_HEAD(*namehooks); while (namehook != NULL) { INSIST(DNS_ADBNAMEHOOK_VALID(namehook)); @@ -1367,17 +990,17 @@ clean_namehooks(dns_adb_t *adb, dns_adbnamehooklist_t *namehooks) { if (entry != NULL) { INSIST(DNS_ADBENTRY_VALID(entry)); - if (addr_bucket != entry->lock_bucket) { - if (addr_bucket != DNS_ADB_INVALIDBUCKET) { - UNLOCK(&adb->entrylocks[addr_bucket]); + if (bucket != entry->bucket) { + if (bucket != NULL) { + UNLOCK(&bucket->lock); } - addr_bucket = entry->lock_bucket; - INSIST(addr_bucket != DNS_ADB_INVALIDBUCKET); - LOCK(&adb->entrylocks[addr_bucket]); + bucket = entry->bucket; + INSIST(bucket != NULL); + LOCK(&bucket->lock); } entry->nh--; - ret = dec_entry_refcnt(adb, overmem, entry, false); + ret = dec_entry_refcnt(entry, false); } /* @@ -1390,9 +1013,10 @@ clean_namehooks(dns_adb_t *adb, dns_adbnamehooklist_t *namehooks) { namehook = ISC_LIST_HEAD(*namehooks); } - if (addr_bucket != DNS_ADB_INVALIDBUCKET) { - UNLOCK(&adb->entrylocks[addr_bucket]); + if (bucket != NULL) { + UNLOCK(&bucket->lock); } + return (ret); } @@ -1548,7 +1172,7 @@ clean_finds_at_name(dns_adbname_t *name, isc_eventtype_t evtype, */ ISC_LIST_UNLINK(name->finds, find, plink); find->adbname = NULL; - find->name_bucket = DNS_ADB_INVALIDBUCKET; + find->bucket = NULL; INSIST(!FIND_EVENTSENT(find)); @@ -1604,12 +1228,7 @@ dec_adb_irefcnt(dns_adb_t *adb) { isc_task_t *etask = NULL; bool ret = false; - LOCK(&adb->reflock); - - REQUIRE(adb->irefcnt > 0); - adb->irefcnt--; - - if (adb->irefcnt == 0) { + if (isc_refcount_decrement(&adb->irefcnt) == 1) { event = ISC_LIST_HEAD(adb->whenshutdown); while (event != NULL) { ISC_LIST_UNLINK(adb->whenshutdown, event, ev_link); @@ -1618,87 +1237,76 @@ dec_adb_irefcnt(dns_adb_t *adb) { isc_task_sendanddetach(&etask, &event); event = ISC_LIST_HEAD(adb->whenshutdown); } + if (isc_refcount_current(&adb->erefcnt) == 0) { + ret = true; + } } - if (adb->irefcnt == 0 && adb->erefcnt == 0) { - ret = true; - } - UNLOCK(&adb->reflock); return (ret); } static void inc_adb_irefcnt(dns_adb_t *adb) { - LOCK(&adb->reflock); - adb->irefcnt++; - UNLOCK(&adb->reflock); + isc_refcount_increment0(&adb->irefcnt); } static void inc_adb_erefcnt(dns_adb_t *adb) { - LOCK(&adb->reflock); - adb->erefcnt++; - UNLOCK(&adb->reflock); + isc_refcount_increment(&adb->erefcnt); } static void -inc_entry_refcnt(dns_adb_t *adb, dns_adbentry_t *entry, bool lock) { - int bucket; - - bucket = entry->lock_bucket; +inc_entry_refcnt(dns_adbentry_t *entry, bool lock) { + dns_adbentrybucket_t *bucket = entry->bucket; if (lock) { - LOCK(&adb->entrylocks[bucket]); + LOCK(&bucket->lock); } - entry->refcnt++; + isc_refcount_increment(&entry->references); if (lock) { - UNLOCK(&adb->entrylocks[bucket]); + UNLOCK(&bucket->lock); } } static bool -dec_entry_refcnt(dns_adb_t *adb, bool overmem, dns_adbentry_t *entry, - bool lock) { - int bucket; - bool destroy_entry; +dec_entry_refcnt(dns_adbentry_t *entry, bool lock) { + dns_adbentrybucket_t *bucket = NULL; + bool destroy_entry = false; + bool overmem = false; bool ret = false; REQUIRE(DNS_ADBENTRY_VALID(entry)); - bucket = entry->lock_bucket; + bucket = entry->bucket; + overmem = isc_mem_isovermem(entry->adb->mctx); if (lock) { - LOCK(&adb->entrylocks[bucket]); + LOCK(&bucket->lock); } - REQUIRE(entry->refcnt > 0); - entry->refcnt--; - destroy_entry = false; - if (entry->refcnt == 0 && - (adb->entry_sd[bucket] || entry->expires == 0 || overmem || - (entry->flags & ENTRY_IS_DEAD) != 0)) - { - destroy_entry = true; - ret = unlink_entry(adb, entry); + if (isc_refcount_decrement(&entry->references) == 1) { + if (bucket->shuttingdown || entry->expires == 0 || overmem || + (entry->flags & ENTRY_IS_DEAD) != 0) + { + destroy_entry = true; + ret = unlink_entry(entry); + } } if (lock) { - UNLOCK(&adb->entrylocks[bucket]); + UNLOCK(&bucket->lock); } if (!destroy_entry) { return (ret); } - entry->lock_bucket = DNS_ADB_INVALIDBUCKET; + entry->bucket = NULL; - free_adbentry(adb, &entry); - if (ret) { - ret = dec_adb_irefcnt(adb); - } + free_adbentry(&entry); return (ret); } @@ -1708,40 +1316,30 @@ new_adbname(dns_adb_t *adb, const dns_name_t *dnsname) { dns_adbname_t *name = NULL; name = isc_mem_get(adb->mctx, sizeof(*name)); + *name = (dns_adbname_t){ + .adb = adb, + .expire_v4 = INT_MAX, + .expire_v6 = INT_MAX, + .expire_target = INT_MAX, + .fetch_err = FIND_ERR_UNEXPECTED, + .fetch6_err = FIND_ERR_UNEXPECTED, + }; dns_name_init(&name->name, NULL); dns_name_dup(dnsname, adb->mctx, &name->name); dns_name_init(&name->target, NULL); - name->magic = DNS_ADBNAME_MAGIC; - name->adb = adb; - name->partial_result = 0; - name->flags = 0; - name->expire_v4 = INT_MAX; - name->expire_v6 = INT_MAX; - name->expire_target = INT_MAX; - name->chains = 0; - name->lock_bucket = DNS_ADB_INVALIDBUCKET; + ISC_LIST_INIT(name->v4); ISC_LIST_INIT(name->v6); - name->fetch_a = NULL; - name->fetch_aaaa = NULL; - name->fetch_err = FIND_ERR_UNEXPECTED; - name->fetch6_err = FIND_ERR_UNEXPECTED; + ISC_LIST_INIT(name->finds); ISC_LINK_INIT(name, plink); - LOCK(&adb->namescntlock); - adb->namescnt++; + name->magic = DNS_ADBNAME_MAGIC; + + inc_adb_irefcnt(adb); + inc_adbstats(adb, dns_adbstats_namescnt); - if (!adb->grownames_sent && adb->excl != NULL && - adb->namescnt > (adb->nnames * 8)) - { - isc_event_t *event = &adb->grownames; - inc_adb_irefcnt(adb); - isc_task_send(adb->excl, &event); - adb->grownames_sent = true; - } - UNLOCK(&adb->namescntlock); return (name); } @@ -1760,7 +1358,7 @@ free_adbname(dns_adb_t *adb, dns_adbname_t **name) { REQUIRE(!NAME_FETCH(n)); REQUIRE(ISC_LIST_EMPTY(n->finds)); REQUIRE(!ISC_LINK_LINKED(n, plink)); - REQUIRE(n->lock_bucket == DNS_ADB_INVALIDBUCKET); + REQUIRE(n->bucket == NULL); REQUIRE(n->adb == adb); n->magic = 0; @@ -1768,10 +1366,24 @@ free_adbname(dns_adb_t *adb, dns_adbname_t **name) { isc_mem_put(adb->mctx, n, sizeof(*n)); - LOCK(&adb->namescntlock); - adb->namescnt--; + dec_adb_irefcnt(adb); dec_adbstats(adb, dns_adbstats_namescnt); - UNLOCK(&adb->namescntlock); +} + +static dns_adbnamebucket_t * +new_adbnamebucket(dns_adb_t *adb) { + dns_adbnamebucket_t *bucket = NULL; + + bucket = isc_mem_get(adb->mctx, sizeof(*bucket)); + *bucket = (dns_adbnamebucket_t){ .shuttingdown = false }; + + ISC_LIST_INIT(bucket->names); + ISC_LIST_INIT(bucket->deadnames); + + isc_mutex_init(&bucket->lock); + isc_refcount_init(&bucket->references, 0); + + return (bucket); } static dns_adbnamehook_t * @@ -1844,60 +1456,43 @@ new_adbentry(dns_adb_t *adb) { dns_adbentry_t *e = NULL; e = isc_mem_get(adb->mctx, sizeof(*e)); + *e = (dns_adbentry_t){ + .adb = adb, + .srtt = isc_random_uniform(0x1f) + 1, + }; - e->magic = DNS_ADBENTRY_MAGIC; - e->lock_bucket = DNS_ADB_INVALIDBUCKET; - e->refcnt = 0; - e->nh = 0; - e->flags = 0; - e->udpsize = 0; - e->edns = 0; - e->ednsto = 0; - e->completed = 0; - e->timeouts = 0; - e->plain = 0; - e->plainto = 0; - e->cookie = NULL; - e->cookielen = 0; - e->srtt = (isc_random_uniform(0x1f)) + 1; - e->lastage = 0; - e->expires = 0; + isc_refcount_init(&e->references, 1); atomic_init(&e->active, 0); - e->mode = 0; atomic_init(&e->quota, adb->quota); - e->atr = 0.0; + ISC_LIST_INIT(e->lameinfo); ISC_LINK_INIT(e, plink); - LOCK(&adb->entriescntlock); - adb->entriescnt++; + + e->magic = DNS_ADBENTRY_MAGIC; + + inc_adb_irefcnt(adb); + inc_adbstats(adb, dns_adbstats_entriescnt); - if (!adb->growentries_sent && adb->excl != NULL && - adb->entriescnt > (adb->nentries * 8)) - { - isc_event_t *event = &adb->growentries; - inc_adb_irefcnt(adb); - isc_task_send(adb->excl, &event); - adb->growentries_sent = true; - } - UNLOCK(&adb->entriescntlock); return (e); } static void -free_adbentry(dns_adb_t *adb, dns_adbentry_t **entry) { +free_adbentry(dns_adbentry_t **entry) { dns_adbentry_t *e = NULL; dns_adblameinfo_t *li = NULL; + dns_adb_t *adb = NULL; REQUIRE(entry != NULL && DNS_ADBENTRY_VALID(*entry)); e = *entry; *entry = NULL; - REQUIRE(e->lock_bucket == DNS_ADB_INVALIDBUCKET); - REQUIRE(e->refcnt == 0); + REQUIRE(e->bucket == NULL); REQUIRE(!ISC_LINK_LINKED(e, plink)); + adb = e->adb; + e->magic = 0; if (e->cookie != NULL) { @@ -1912,10 +1507,25 @@ free_adbentry(dns_adb_t *adb, dns_adbentry_t **entry) { } isc_mem_put(adb->mctx, e, sizeof(*e)); - LOCK(&adb->entriescntlock); - adb->entriescnt--; + + dec_adb_irefcnt(adb); dec_adbstats(adb, dns_adbstats_entriescnt); - UNLOCK(&adb->entriescntlock); +} + +static dns_adbentrybucket_t * +new_adbentrybucket(dns_adb_t *adb) { + dns_adbentrybucket_t *bucket = NULL; + + bucket = isc_mem_get(adb->mctx, sizeof(*bucket)); + *bucket = (dns_adbentrybucket_t){ .shuttingdown = false }; + + ISC_LIST_INIT(bucket->entries); + ISC_LIST_INIT(bucket->deadentries); + + isc_mutex_init(&bucket->lock); + isc_refcount_init(&bucket->references, 0); + + return (bucket); } static dns_adbfind_t * @@ -1923,34 +1533,22 @@ new_adbfind(dns_adb_t *adb) { dns_adbfind_t *h = NULL; h = isc_mem_get(adb->mctx, sizeof(*h)); + *h = (dns_adbfind_t){ + .adb = adb, + .result_v4 = ISC_R_UNEXPECTED, + .result_v6 = ISC_R_UNEXPECTED, + }; isc_refcount_increment0(&adb->ahrefcnt); - - /* - * Public members. - */ - h->magic = 0; - h->adb = adb; - h->partial_result = 0; - h->options = 0; - h->flags = 0; - h->result_v4 = ISC_R_UNEXPECTED; - h->result_v6 = ISC_R_UNEXPECTED; ISC_LINK_INIT(h, publink); ISC_LINK_INIT(h, plink); ISC_LIST_INIT(h->list); - h->adbname = NULL; - h->name_bucket = DNS_ADB_INVALIDBUCKET; - - /* - * private members - */ isc_mutex_init(&h->lock); - ISC_EVENT_INIT(&h->event, sizeof(isc_event_t), 0, 0, 0, NULL, NULL, NULL, NULL, h); - inc_adb_irefcnt(adb); + h->magic = DNS_ADBFIND_MAGIC; + return (h); } @@ -1959,10 +1557,7 @@ new_adbfetch(dns_adb_t *adb) { dns_adbfetch_t *f = NULL; f = isc_mem_get(adb->mctx, sizeof(*f)); - - f->magic = 0; - f->fetch = NULL; - + *f = (dns_adbfetch_t){ 0 }; dns_rdataset_init(&f->rdataset); f->magic = DNS_ADBFETCH_MAGIC; @@ -2000,7 +1595,7 @@ free_adbfind(dns_adb_t *adb, dns_adbfind_t **findp) { REQUIRE(!FIND_HAS_ADDRS(find)); REQUIRE(!ISC_LINK_LINKED(find, publink)); REQUIRE(!ISC_LINK_LINKED(find, plink)); - REQUIRE(find->name_bucket == DNS_ADB_INVALIDBUCKET); + REQUIRE(find->bucket == NULL); REQUIRE(find->adbname == NULL); find->magic = 0; @@ -2022,15 +1617,16 @@ 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, + .entry = entry, + .dscp = -1 }; - ai->magic = DNS_ADBADDRINFO_MAGIC; + ISC_LINK_INIT(ai, publink); ai->sockaddr = entry->sockaddr; isc_sockaddr_setport(&ai->sockaddr, port); - ai->srtt = entry->srtt; - ai->flags = entry->flags; - ai->entry = entry; - ai->dscp = -1; - ISC_LINK_INIT(ai, publink); + + ai->magic = DNS_ADBADDRINFO_MAGIC; return (ai); } @@ -2053,30 +1649,51 @@ free_adbaddrinfo(dns_adb_t *adb, dns_adbaddrinfo_t **ainfo) { } /* - * Search for the name. NOTE: The bucket is kept locked on both + * Search for the name. NOTE: The bucket is kept locked on both * success and failure, so it must always be unlocked by the caller! * - * On the first call to this function, *bucketp must be set to - * DNS_ADB_INVALIDBUCKET. + * On the first call to this function, *bucketp must be set to NULL. */ static dns_adbname_t * find_name_and_lock(dns_adb_t *adb, const dns_name_t *name, unsigned int options, - int *bucketp) { + dns_adbnamebucket_t **bucketp) { + isc_result_t result; dns_adbname_t *adbname = NULL; - int bucket; + dns_adbnamebucket_t *bucket = NULL; - bucket = dns_name_fullhash(name, false) % adb->nnames; + result = isc_ht_find(adb->namebuckets, name->ndata, name->length, + (void **)&bucket); + if (result == ISC_R_NOTFOUND) { + /* + * Allocate a new bucket and add it to the hash table. + */ + bucket = new_adbnamebucket(adb); + result = isc_ht_add(adb->namebuckets, name->ndata, name->length, + bucket); + if (result == ISC_R_EXISTS) { + /* + * Some other thread got in ahead of us; + * redo the lookup. + */ + isc_mem_put(adb->mctx, bucket, sizeof(*bucket)); + result = isc_ht_find(adb->namebuckets, name->ndata, + name->length, (void **)&bucket); + INSIST(result == ISC_R_SUCCESS); + } + } - if (*bucketp == DNS_ADB_INVALIDBUCKET) { - LOCK(&adb->namelocks[bucket]); + INSIST(result == ISC_R_SUCCESS); + + if (*bucketp == NULL) { + LOCK(&bucket->lock); *bucketp = bucket; } else if (*bucketp != bucket) { - UNLOCK(&adb->namelocks[*bucketp]); - LOCK(&adb->namelocks[bucket]); + UNLOCK(&(*bucketp)->lock); + LOCK(&bucket->lock); *bucketp = bucket; } - adbname = ISC_LIST_HEAD(adb->names[bucket]); + adbname = ISC_LIST_HEAD(bucket->names); while (adbname != NULL) { if (!NAME_DEAD(adbname)) { if (dns_name_equal(name, &adbname->name) && @@ -2097,40 +1714,60 @@ find_name_and_lock(dns_adb_t *adb, const dns_name_t *name, unsigned int options, * success and failure, so it must always be unlocked by the caller. * * On the first call to this function, *bucketp must be set to - * DNS_ADB_INVALIDBUCKET. This will cause a lock to occur. On - * later calls (within the same "lock path") it can be left alone, so - * if this function is called multiple times locking is only done if - * the bucket changes. + * NULL. This will cause a lock to occur. On later calls (within the + * same "lock path") it can be left alone, so if this function is + * called multiple times locking is only done if the bucket changes. */ static dns_adbentry_t * -find_entry_and_lock(dns_adb_t *adb, const isc_sockaddr_t *addr, int *bucketp, - isc_stdtime_t now) { +find_entry_and_lock(dns_adb_t *adb, const isc_sockaddr_t *addr, + isc_stdtime_t now, dns_adbentrybucket_t **bucketp) { + isc_result_t result; dns_adbentry_t *entry = NULL, *entry_next = NULL; - int bucket; + dns_adbentrybucket_t *bucket = NULL; - bucket = isc_sockaddr_hash(addr, true) % adb->nentries; + result = isc_ht_find(adb->entrybuckets, (const unsigned char *)addr, + sizeof(*addr), (void **)&bucket); + if (result == ISC_R_NOTFOUND) { + /* + * Allocate a new bucket and add it to the hash table. + */ + bucket = new_adbentrybucket(adb); + result = isc_ht_add(adb->entrybuckets, + (const unsigned char *)addr, sizeof(*addr), + bucket); + if (result == ISC_R_EXISTS) { + /* + * Some other thread got in ahead of us; + * redo the lookup. + */ + isc_mem_put(adb->mctx, bucket, sizeof(*bucket)); + result = isc_ht_find(adb->entrybuckets, + (const unsigned char *)addr, + sizeof(*addr), (void **)&bucket); + INSIST(result == ISC_R_SUCCESS); + } + } - if (*bucketp == DNS_ADB_INVALIDBUCKET) { - LOCK(&adb->entrylocks[bucket]); + if (*bucketp == NULL) { + LOCK(&bucket->lock); *bucketp = bucket; } else if (*bucketp != bucket) { - UNLOCK(&adb->entrylocks[*bucketp]); - LOCK(&adb->entrylocks[bucket]); + UNLOCK(&(*bucketp)->lock); + LOCK(&bucket->lock); *bucketp = bucket; } /* Search the list, while cleaning up expired entries. */ - for (entry = ISC_LIST_HEAD(adb->entries[bucket]); entry != NULL; - entry = entry_next) - { + for (entry = ISC_LIST_HEAD(bucket->entries); entry != NULL; + entry = entry_next) { entry_next = ISC_LIST_NEXT(entry, plink); - (void)check_expire_entry(adb, &entry, now); + (void)check_expire_entry(&entry, now); if (entry != NULL && (entry->expires == 0 || entry->expires > now) && isc_sockaddr_equal(addr, &entry->sockaddr)) { - ISC_LIST_UNLINK(adb->entries[bucket], entry, plink); - ISC_LIST_PREPEND(adb->entries[bucket], entry, plink); + ISC_LIST_UNLINK(bucket->entries, entry, plink); + ISC_LIST_PREPEND(bucket->entries, entry, plink); return (entry); } } @@ -2208,17 +1845,15 @@ copy_namehook_lists(dns_adb_t *adb, dns_adbfind_t *find, dns_adbnamehook_t *namehook = NULL; dns_adbaddrinfo_t *addrinfo = NULL; dns_adbentry_t *entry = NULL; - int bucket; - - bucket = DNS_ADB_INVALIDBUCKET; + dns_adbentrybucket_t *bucket = NULL; if ((find->options & DNS_ADBFIND_INET) != 0) { namehook = ISC_LIST_HEAD(name->v4); while (namehook != NULL) { entry = namehook->entry; - bucket = entry->lock_bucket; - INSIST(bucket != DNS_ADB_INVALIDBUCKET); - LOCK(&adb->entrylocks[bucket]); + bucket = entry->bucket; + INSIST(bucket != NULL); + LOCK(&bucket->lock); if (dns_adbentry_overquota(entry)) { find->options |= (DNS_ADBFIND_LAMEPRUNED | @@ -2237,12 +1872,12 @@ copy_namehook_lists(dns_adb_t *adb, dns_adbfind_t *find, /* * Found a valid entry. Add it to the find's list. */ - inc_entry_refcnt(adb, entry, false); + inc_entry_refcnt(entry, false); ISC_LIST_APPEND(find->list, addrinfo, publink); addrinfo = NULL; nextv4: - UNLOCK(&adb->entrylocks[bucket]); - bucket = DNS_ADB_INVALIDBUCKET; + UNLOCK(&bucket->lock); + bucket = NULL; namehook = ISC_LIST_NEXT(namehook, plink); } } @@ -2251,9 +1886,9 @@ copy_namehook_lists(dns_adb_t *adb, dns_adbfind_t *find, namehook = ISC_LIST_HEAD(name->v6); while (namehook != NULL) { entry = namehook->entry; - bucket = entry->lock_bucket; - INSIST(bucket != DNS_ADB_INVALIDBUCKET); - LOCK(&adb->entrylocks[bucket]); + bucket = entry->bucket; + INSIST(bucket != NULL); + LOCK(&bucket->lock); if (dns_adbentry_overquota(entry)) { find->options |= (DNS_ADBFIND_LAMEPRUNED | @@ -2271,18 +1906,18 @@ copy_namehook_lists(dns_adb_t *adb, dns_adbfind_t *find, /* * Found a valid entry. Add it to the find's list. */ - inc_entry_refcnt(adb, entry, false); + inc_entry_refcnt(entry, false); ISC_LIST_APPEND(find->list, addrinfo, publink); addrinfo = NULL; nextv6: - UNLOCK(&adb->entrylocks[bucket]); - bucket = DNS_ADB_INVALIDBUCKET; + UNLOCK(&bucket->lock); + bucket = NULL; namehook = ISC_LIST_NEXT(namehook, plink); } } - if (bucket != DNS_ADB_INVALIDBUCKET) { - UNLOCK(&adb->entrylocks[bucket]); + if (bucket != NULL) { + UNLOCK(&bucket->lock); } } @@ -2309,39 +1944,35 @@ shutdown_task(isc_task_t *task, isc_event_t *ev) { static bool check_expire_name(dns_adbname_t **namep, isc_stdtime_t now) { dns_adbname_t *name = NULL; - bool ret = false; REQUIRE(namep != NULL && DNS_ADBNAME_VALID(*namep)); name = *namep; if (NAME_HAS_V4(name) || NAME_HAS_V6(name)) { - return (ret); + return (false); } if (NAME_FETCH(name)) { - return (ret); + return (false); } if (!EXPIRE_OK(name->expire_v4, now)) { - return (ret); + return (false); } if (!EXPIRE_OK(name->expire_v6, now)) { - return (ret); + return (false); } if (!EXPIRE_OK(name->expire_target, now)) { - return (ret); + return (false); } /* * The name is empty. Delete it. - */ - *namep = NULL; - ret = kill_name(&name, DNS_EVENT_ADBEXPIRED); - - /* + * * Our caller, or one of its callers, will be calling check_exit() at * some point, so we don't need to do it here. */ - return (ret); + *namep = NULL; + return (kill_name(&name, DNS_EVENT_ADBEXPIRED)); } /*% @@ -2356,13 +1987,14 @@ check_expire_name(dns_adbname_t **namep, isc_stdtime_t now) { * Name bucket must be locked; adb may be locked; no other locks held. */ static void -check_stale_name(dns_adb_t *adb, int bucket, isc_stdtime_t now) { +check_stale_name(dns_adb_t *adb, dns_adbnamebucket_t *bucket, + isc_stdtime_t now) { int victims, max_victims; dns_adbname_t *victim = NULL, *next_victim = NULL; bool overmem = isc_mem_isovermem(adb->mctx); int scans = 0; - REQUIRE(bucket != DNS_ADB_INVALIDBUCKET); + REQUIRE(bucket != NULL); max_victims = overmem ? 2 : 1; @@ -2372,7 +2004,7 @@ check_stale_name(dns_adb_t *adb, int bucket, isc_stdtime_t now) { * tail entries that have fetches (this should be rare, but could * happen). */ - victim = ISC_LIST_TAIL(adb->names[bucket]); + victim = ISC_LIST_TAIL(bucket->names); for (victims = 0; victim != NULL && victims < max_victims && scans < 10; victim = next_victim) { @@ -2404,14 +2036,15 @@ check_stale_name(dns_adb_t *adb, int bucket, isc_stdtime_t now) { * Entry bucket must be locked; adb may be locked; no other locks held. */ static bool -check_expire_entry(dns_adb_t *adb, dns_adbentry_t **entryp, isc_stdtime_t now) { +check_expire_entry(dns_adbentry_t **entryp, isc_stdtime_t now) { dns_adbentry_t *entry = NULL; bool ret = false; REQUIRE(entryp != NULL && DNS_ADBENTRY_VALID(*entryp)); + entry = *entryp; - if (entry->refcnt != 0) { + if (isc_refcount_current(&entry->references) != 0) { return (ret); } @@ -2425,11 +2058,9 @@ check_expire_entry(dns_adb_t *adb, dns_adbentry_t **entryp, isc_stdtime_t now) { *entryp = NULL; DP(DEF_LEVEL, "killing entry %p", entry); INSIST(ISC_LINK_LINKED(entry, plink)); - ret = unlink_entry(adb, entry); - free_adbentry(adb, &entry); - if (ret) { - dec_adb_irefcnt(adb); - } + ret = unlink_entry(entry); + dec_entry_refcnt(entry, false); + return (ret); } @@ -2437,22 +2068,21 @@ check_expire_entry(dns_adb_t *adb, dns_adbentry_t **entryp, isc_stdtime_t now) { * ADB must be locked, and no other locks held. */ static bool -cleanup_names(dns_adb_t *adb, int bucket, isc_stdtime_t now) { +cleanup_names(dns_adbnamebucket_t *bucket, isc_stdtime_t now) { dns_adbname_t *name = NULL; - dns_adbname_t *next_name; bool ret = false; - DP(CLEAN_LEVEL, "cleaning name bucket %d", bucket); + DP(CLEAN_LEVEL, "cleaning name bucket %p", bucket); - LOCK(&adb->namelocks[bucket]); - if (adb->name_sd[bucket]) { - UNLOCK(&adb->namelocks[bucket]); + LOCK(&bucket->lock); + if (bucket->shuttingdown) { + UNLOCK(&bucket->lock); return (ret); } - name = ISC_LIST_HEAD(adb->names[bucket]); + name = ISC_LIST_HEAD(bucket->names); while (name != NULL) { - next_name = ISC_LIST_NEXT(name, plink); + dns_adbname_t *next_name = ISC_LIST_NEXT(name, plink); INSIST(!ret); ret = check_expire_namehooks(name, now); if (!ret) { @@ -2460,7 +2090,8 @@ cleanup_names(dns_adb_t *adb, int bucket, isc_stdtime_t now) { } name = next_name; } - UNLOCK(&adb->namelocks[bucket]); + UNLOCK(&bucket->lock); + return (ret); } @@ -2468,26 +2099,56 @@ cleanup_names(dns_adb_t *adb, int bucket, isc_stdtime_t now) { * ADB must be locked, and no other locks held. */ static bool -cleanup_entries(dns_adb_t *adb, int bucket, isc_stdtime_t now) { - dns_adbentry_t *entry, *next_entry; +cleanup_entries(dns_adbentrybucket_t *bucket, isc_stdtime_t now) { + dns_adbentry_t *entry = NULL, *next_entry = NULL; bool ret = false; - DP(CLEAN_LEVEL, "cleaning entry bucket %d", bucket); + DP(CLEAN_LEVEL, "cleaning entry bucket %p", bucket); - LOCK(&adb->entrylocks[bucket]); - entry = ISC_LIST_HEAD(adb->entries[bucket]); + LOCK(&bucket->lock); + entry = ISC_LIST_HEAD(bucket->entries); while (entry != NULL) { next_entry = ISC_LIST_NEXT(entry, plink); INSIST(!ret); - ret = check_expire_entry(adb, &entry, now); + ret = check_expire_entry(&entry, now); entry = next_entry; } - UNLOCK(&adb->entrylocks[bucket]); + UNLOCK(&bucket->lock); + return (ret); } +static void +clean_hashes(dns_adb_t *adb, isc_stdtime_t now) { + isc_result_t result; + isc_ht_iter_t *it = NULL; + + isc_ht_iter_create(adb->namebuckets, &it); + for (result = isc_ht_iter_first(it); result == ISC_R_SUCCESS; + result = isc_ht_iter_next(it)) + { + dns_adbnamebucket_t *bucket = NULL; + isc_ht_iter_current(it, (void **)&bucket); + RUNTIME_CHECK(!cleanup_names(bucket, now)); + } + isc_ht_iter_destroy(&it); + + isc_ht_iter_create(adb->entrybuckets, &it); + for (result = isc_ht_iter_first(it); result == ISC_R_SUCCESS; + result = isc_ht_iter_next(it)) + { + dns_adbentrybucket_t *bucket = NULL; + isc_ht_iter_current(it, (void **)&bucket); + RUNTIME_CHECK(!cleanup_entries(bucket, now)); + } + isc_ht_iter_destroy(&it); +} + static void destroy(dns_adb_t *adb) { + isc_result_t result; + isc_ht_iter_t *it = NULL; + adb->magic = 0; isc_task_detach(&adb->task); @@ -2495,34 +2156,35 @@ destroy(dns_adb_t *adb) { isc_task_detach(&adb->excl); } - isc_mutexblock_destroy(adb->entrylocks, adb->nentries); - isc_mem_put(adb->mctx, adb->entries, - sizeof(*adb->entries) * adb->nentries); - isc_mem_put(adb->mctx, adb->deadentries, - sizeof(*adb->deadentries) * adb->nentries); - isc_mem_put(adb->mctx, adb->entrylocks, - sizeof(*adb->entrylocks) * adb->nentries); - isc_mem_put(adb->mctx, adb->entry_sd, - sizeof(*adb->entry_sd) * adb->nentries); - isc_mem_put(adb->mctx, adb->entry_refcnt, - sizeof(*adb->entry_refcnt) * adb->nentries); + if (adb->namebuckets != NULL) { + isc_ht_iter_create(adb->namebuckets, &it); + for (result = isc_ht_iter_first(it); result == ISC_R_SUCCESS; + result = isc_ht_iter_delcurrent_next(it)) + { + dns_adbnamebucket_t *bucket = NULL; + isc_ht_iter_current(it, (void **)&bucket); + RUNTIME_CHECK(!cleanup_names(bucket, INT_MAX)); + isc_mem_put(adb->mctx, bucket, sizeof(*bucket)); + } + isc_ht_iter_destroy(&it); + isc_ht_destroy(&adb->namebuckets); + } - isc_mutexblock_destroy(adb->namelocks, adb->nnames); - isc_mem_put(adb->mctx, adb->names, sizeof(*adb->names) * adb->nnames); - isc_mem_put(adb->mctx, adb->deadnames, - sizeof(*adb->deadnames) * adb->nnames); - isc_mem_put(adb->mctx, adb->namelocks, - sizeof(*adb->namelocks) * adb->nnames); - isc_mem_put(adb->mctx, adb->name_sd, - sizeof(*adb->name_sd) * adb->nnames); - isc_mem_put(adb->mctx, adb->name_refcnt, - sizeof(*adb->name_refcnt) * adb->nnames); + if (adb->entrybuckets != NULL) { + isc_ht_iter_create(adb->entrybuckets, &it); + for (result = isc_ht_iter_first(it); result == ISC_R_SUCCESS; + result = isc_ht_iter_delcurrent_next(it)) + { + dns_adbentrybucket_t *bucket = NULL; + isc_ht_iter_current(it, (void **)&bucket); + RUNTIME_CHECK(!cleanup_entries(bucket, INT_MAX)); + isc_mem_put(adb->mctx, bucket, sizeof(*bucket)); + } + isc_ht_iter_destroy(&it); + isc_ht_destroy(&adb->entrybuckets); + } - isc_mutex_destroy(&adb->reflock); isc_mutex_destroy(&adb->lock); - isc_mutex_destroy(&adb->overmemlock); - isc_mutex_destroy(&adb->entriescntlock); - isc_mutex_destroy(&adb->namescntlock); isc_mem_putanddetach(&adb->mctx, adb, sizeof(dns_adb_t)); } @@ -2536,7 +2198,6 @@ dns_adb_create(isc_mem_t *mem, dns_view_t *view, isc_timermgr_t *timermgr, isc_taskmgr_t *taskmgr, dns_adb_t **newadb) { dns_adb_t *adb = NULL; isc_result_t result; - unsigned int i; REQUIRE(mem != NULL); REQUIRE(view != NULL); @@ -2547,119 +2208,30 @@ dns_adb_create(isc_mem_t *mem, dns_view_t *view, isc_timermgr_t *timermgr, UNUSED(timermgr); adb = isc_mem_get(mem, sizeof(dns_adb_t)); + *adb = (dns_adb_t){ + .erefcnt = 1, + .view = view, + .taskmgr = taskmgr, + }; /* * Initialize things here that cannot fail, and especially things * that must be NULL for the error return to work properly. */ - adb->magic = 0; - adb->erefcnt = 1; - adb->irefcnt = 0; - adb->task = NULL; - adb->excl = NULL; - adb->mctx = NULL; - adb->view = view; - adb->taskmgr = taskmgr; - adb->next_cleanbucket = 0; ISC_EVENT_INIT(&adb->cevent, sizeof(adb->cevent), 0, NULL, 0, NULL, NULL, NULL, NULL, NULL); - adb->cevent_out = false; - atomic_init(&adb->shutting_down, false); ISC_LIST_INIT(adb->whenshutdown); - - adb->nentries = nbuckets[0]; - adb->entriescnt = 0; - adb->entries = NULL; - adb->deadentries = NULL; - adb->entry_sd = NULL; - adb->entry_refcnt = NULL; - adb->entrylocks = NULL; - ISC_EVENT_INIT(&adb->growentries, sizeof(adb->growentries), 0, NULL, - DNS_EVENT_ADBGROWENTRIES, grow_entries, adb, adb, NULL, - NULL); - adb->growentries_sent = false; - - adb->quota = 0; - adb->atr_freq = 0; - adb->atr_low = 0.0; - adb->atr_high = 0.0; - adb->atr_discount = 0.0; - - adb->nnames = nbuckets[0]; - adb->namescnt = 0; - adb->names = NULL; - adb->deadnames = NULL; - adb->name_sd = NULL; - adb->name_refcnt = NULL; - adb->namelocks = NULL; - ISC_EVENT_INIT(&adb->grownames, sizeof(adb->grownames), 0, NULL, - DNS_EVENT_ADBGROWNAMES, grow_names, adb, adb, NULL, - NULL); - adb->grownames_sent = false; - - result = isc_taskmgr_excltask(adb->taskmgr, &adb->excl); - if (result != ISC_R_SUCCESS) { - DP(DEF_LEVEL, - "adb: task-exclusive mode unavailable, " - "initializing table sizes to %u\n", - nbuckets[11]); - adb->nentries = nbuckets[11]; - adb->nnames = nbuckets[11]; - } + atomic_init(&adb->shutting_down, false); isc_mem_attach(mem, &adb->mctx); + isc_ht_init(&adb->namebuckets, adb->mctx, 1, ISC_HT_CASE_INSENSITIVE); + isc_ht_init(&adb->entrybuckets, adb->mctx, 1, ISC_HT_CASE_INSENSITIVE); + + result = isc_taskmgr_excltask(adb->taskmgr, &adb->excl); + INSIST(result == ISC_R_SUCCESS); + isc_mutex_init(&adb->lock); - isc_mutex_init(&adb->reflock); - isc_mutex_init(&adb->overmemlock); - isc_mutex_init(&adb->entriescntlock); - isc_mutex_init(&adb->namescntlock); - -#define ALLOCENTRY(adb, el) \ - do { \ - (adb)->el = isc_mem_get((adb)->mctx, \ - sizeof(*(adb)->el) * (adb)->nentries); \ - } while (0) - ALLOCENTRY(adb, entries); - ALLOCENTRY(adb, deadentries); - ALLOCENTRY(adb, entrylocks); - ALLOCENTRY(adb, entry_sd); - ALLOCENTRY(adb, entry_refcnt); -#undef ALLOCENTRY - -#define ALLOCNAME(adb, el) \ - do { \ - (adb)->el = isc_mem_get((adb)->mctx, \ - sizeof(*(adb)->el) * (adb)->nnames); \ - } while (0) - ALLOCNAME(adb, names); - ALLOCNAME(adb, deadnames); - ALLOCNAME(adb, namelocks); - ALLOCNAME(adb, name_sd); - ALLOCNAME(adb, name_refcnt); -#undef ALLOCNAME - - /* - * Initialize the bucket locks for names and elements. - * May as well initialize the list heads, too. - */ - isc_mutexblock_init(adb->namelocks, adb->nnames); - - for (i = 0; i < adb->nnames; i++) { - ISC_LIST_INIT(adb->names[i]); - ISC_LIST_INIT(adb->deadnames[i]); - adb->name_sd[i] = false; - adb->name_refcnt[i] = 0; - adb->irefcnt++; - } - for (i = 0; i < adb->nentries; i++) { - ISC_LIST_INIT(adb->entries[i]); - ISC_LIST_INIT(adb->deadentries[i]); - adb->entry_sd[i] = false; - adb->entry_refcnt[i] = 0; - adb->irefcnt++; - } - isc_mutexblock_init(adb->entrylocks, adb->nentries); isc_refcount_init(&adb->ahrefcnt, 0); isc_refcount_init(&adb->nhrefcnt, 0); @@ -2679,8 +2251,9 @@ dns_adb_create(isc_mem_t *mem, dns_view_t *view, isc_timermgr_t *timermgr, goto fail2; } - set_adbstat(adb, adb->nentries, dns_adbstats_nentries); - set_adbstat(adb, adb->nnames, dns_adbstats_nnames); + set_adbstat(adb, isc_ht_count(adb->namebuckets), dns_adbstats_nnames); + set_adbstat(adb, isc_ht_count(adb->entrybuckets), + dns_adbstats_nentries); /* * Normal return. @@ -2694,55 +2267,10 @@ fail2: isc_task_detach(&adb->task); } - /* clean up entrylocks */ - isc_mutexblock_destroy(adb->entrylocks, adb->nentries); - isc_mutexblock_destroy(adb->namelocks, adb->nnames); - - if (adb->entries != NULL) { - isc_mem_put(adb->mctx, adb->entries, - sizeof(*adb->entries) * adb->nentries); - } - if (adb->deadentries != NULL) { - isc_mem_put(adb->mctx, adb->deadentries, - sizeof(*adb->deadentries) * adb->nentries); - } - if (adb->entrylocks != NULL) { - isc_mem_put(adb->mctx, adb->entrylocks, - sizeof(*adb->entrylocks) * adb->nentries); - } - if (adb->entry_sd != NULL) { - isc_mem_put(adb->mctx, adb->entry_sd, - sizeof(*adb->entry_sd) * adb->nentries); - } - if (adb->entry_refcnt != NULL) { - isc_mem_put(adb->mctx, adb->entry_refcnt, - sizeof(*adb->entry_refcnt) * adb->nentries); - } - if (adb->names != NULL) { - isc_mem_put(adb->mctx, adb->names, - sizeof(*adb->names) * adb->nnames); - } - if (adb->deadnames != NULL) { - isc_mem_put(adb->mctx, adb->deadnames, - sizeof(*adb->deadnames) * adb->nnames); - } - if (adb->namelocks != NULL) { - isc_mem_put(adb->mctx, adb->namelocks, - sizeof(*adb->namelocks) * adb->nnames); - } - if (adb->name_sd != NULL) { - isc_mem_put(adb->mctx, adb->name_sd, - sizeof(*adb->name_sd) * adb->nnames); - } - if (adb->name_refcnt != NULL) { - isc_mem_put(adb->mctx, adb->name_refcnt, - sizeof(*adb->name_refcnt) * adb->nnames); + if (adb->namebuckets != NULL) { + isc_ht_destroy(&adb->namebuckets); } - isc_mutex_destroy(&adb->namescntlock); - isc_mutex_destroy(&adb->entriescntlock); - isc_mutex_destroy(&adb->overmemlock); - isc_mutex_destroy(&adb->reflock); isc_mutex_destroy(&adb->lock); if (adb->excl != NULL) { isc_task_detach(&adb->excl); @@ -2764,32 +2292,25 @@ dns_adb_attach(dns_adb_t *adb, dns_adb_t **adbx) { void dns_adb_detach(dns_adb_t **adbx) { dns_adb_t *adb = NULL; - bool need_exit_check; REQUIRE(adbx != NULL && DNS_ADB_VALID(*adbx)); adb = *adbx; *adbx = NULL; - LOCK(&adb->reflock); - INSIST(adb->erefcnt > 0); - adb->erefcnt--; - need_exit_check = (adb->erefcnt == 0 && adb->irefcnt == 0); - UNLOCK(&adb->reflock); - - if (need_exit_check) { - LOCK(&adb->lock); - INSIST(atomic_load(&adb->shutting_down)); - check_exit(adb); - UNLOCK(&adb->lock); + if (isc_refcount_decrement(&adb->erefcnt) == 1) { + if (isc_refcount_current(&adb->irefcnt) == 0) { + LOCK(&adb->lock); + INSIST(atomic_load(&adb->shutting_down)); + check_exit(adb); + UNLOCK(&adb->lock); + } } } void dns_adb_whenshutdown(dns_adb_t *adb, isc_task_t *task, isc_event_t **eventp) { - isc_task_t *tclone = NULL; isc_event_t *event = NULL; - bool zeroirefcnt; /* * Send '*eventp' to 'task' when 'adb' has shutdown. @@ -2802,11 +2323,9 @@ dns_adb_whenshutdown(dns_adb_t *adb, isc_task_t *task, isc_event_t **eventp) { *eventp = NULL; LOCK(&adb->lock); - LOCK(&adb->reflock); - zeroirefcnt = (adb->irefcnt == 0); - - if (atomic_load(&adb->shutting_down) && zeroirefcnt && + if (atomic_load(&adb->shutting_down) && + isc_refcount_current(&adb->irefcnt) == 0 && isc_refcount_current(&adb->ahrefcnt) == 0) { /* @@ -2815,13 +2334,11 @@ dns_adb_whenshutdown(dns_adb_t *adb, isc_task_t *task, isc_event_t **eventp) { event->ev_sender = adb; isc_task_send(task, &event); } else { - tclone = NULL; - isc_task_attach(task, &tclone); - event->ev_sender = tclone; + isc_task_attach(task, &(isc_task_t *){ NULL }); + event->ev_sender = task; ISC_LIST_APPEND(adb->whenshutdown, event, ev_link); } - UNLOCK(&adb->reflock); UNLOCK(&adb->lock); } @@ -2836,8 +2353,8 @@ shutdown_stage2(isc_task_t *task, isc_event_t *event) { LOCK(&adb->lock); INSIST(atomic_load(&adb->shutting_down)); adb->cevent_out = false; - (void)shutdown_names(adb); - (void)shutdown_entries(adb); + shutdown_names(adb); + shutdown_entries(adb); if (dec_adb_irefcnt(adb)) { check_exit(adb); } @@ -2881,7 +2398,7 @@ dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, dns_adbfind_t **findp) { dns_adbfind_t *find = NULL; dns_adbname_t *adbname = NULL; - int bucket; + dns_adbnamebucket_t *bucket = NULL; bool want_event = true; bool start_at_zone = false; bool alias = false; @@ -2959,10 +2476,9 @@ 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. */ - bucket = DNS_ADB_INVALIDBUCKET; adbname = find_name_and_lock(adb, name, find->options, &bucket); - INSIST(bucket != DNS_ADB_INVALIDBUCKET); - if (adb->name_sd[bucket]) { + INSIST(bucket != NULL); + if (bucket->shuttingdown) { DP(DEF_LEVEL, "dns_adb_createfind: returning " "ISC_R_SHUTTINGDOWN"); RUNTIME_CHECK(!free_adbfind(adb, &find)); @@ -2981,7 +2497,7 @@ dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, check_stale_name(adb, bucket, now); adbname = new_adbname(adb, name); - link_name(adb, bucket, adbname); + link_name(bucket, adbname); if (FIND_HINTOK(find)) { adbname->flags |= NAME_HINT_OK; } @@ -2993,8 +2509,8 @@ dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, } } else { /* Move this name forward in the LRU list */ - ISC_LIST_UNLINK(adb->names[bucket], adbname, plink); - ISC_LIST_PREPEND(adb->names[bucket], adbname, plink); + ISC_LIST_UNLINK(bucket->names, adbname, plink); + ISC_LIST_PREPEND(bucket->names, adbname, plink); } adbname->last_used = now; @@ -3182,7 +2698,7 @@ post_copy: if (want_event) { bool empty; find->adbname = adbname; - find->name_bucket = bucket; + find->bucket = bucket; empty = ISC_LIST_EMPTY(adbname->finds); ISC_LIST_APPEND(adbname->finds, find, plink); find->query_pending = (query_pending & wanted_addresses); @@ -3232,7 +2748,7 @@ out: *findp = find; } - UNLOCK(&adb->namelocks[bucket]); + UNLOCK(&bucket->lock); return (result); } @@ -3241,9 +2757,7 @@ dns_adb_destroyfind(dns_adbfind_t **findp) { dns_adbfind_t *find = NULL; dns_adbentry_t *entry = NULL; dns_adbaddrinfo_t *ai = NULL; - int bucket; dns_adb_t *adb = NULL; - bool overmem; REQUIRE(findp != NULL && DNS_ADBFIND_VALID(*findp)); @@ -3255,12 +2769,10 @@ dns_adb_destroyfind(dns_adbfind_t **findp) { DP(DEF_LEVEL, "dns_adb_destroyfind on find %p", find); adb = find->adb; + REQUIRE(DNS_ADB_VALID(adb)); - REQUIRE(FIND_EVENTFREED(find)); - - bucket = find->name_bucket; - INSIST(bucket == DNS_ADB_INVALIDBUCKET); + REQUIRE(find->bucket == NULL); UNLOCK(&find->lock); @@ -3269,14 +2781,14 @@ dns_adb_destroyfind(dns_adbfind_t **findp) { * Return the find to the memory pool, and decrement the adb's * reference count. */ - overmem = isc_mem_isovermem(adb->mctx); ai = ISC_LIST_HEAD(find->list); while (ai != NULL) { ISC_LIST_UNLINK(find->list, ai, publink); entry = ai->entry; ai->entry = NULL; + INSIST(DNS_ADBENTRY_VALID(entry)); - RUNTIME_CHECK(!dec_entry_refcnt(adb, overmem, entry, true)); + RUNTIME_CHECK(!dec_entry_refcnt(entry, true)); free_adbaddrinfo(adb, &ai); ai = ISC_LIST_HEAD(find->list); } @@ -3300,8 +2812,8 @@ dns_adb_cancelfind(dns_adbfind_t *find) { isc_event_t *ev = NULL; isc_task_t *task = NULL; dns_adb_t *adb = NULL; - int bucket; - int unlock_bucket; + dns_adbnamebucket_t *bucket = NULL; + dns_adbnamebucket_t *unlock_bucket = NULL; LOCK(&find->lock); @@ -3313,8 +2825,8 @@ dns_adb_cancelfind(dns_adbfind_t *find) { REQUIRE(!FIND_EVENTFREED(find)); REQUIRE(FIND_WANTEVENT(find)); - bucket = find->name_bucket; - if (bucket == DNS_ADB_INVALIDBUCKET) { + bucket = find->bucket; + if (bucket == NULL) { goto cleanup; } @@ -3322,15 +2834,15 @@ dns_adb_cancelfind(dns_adbfind_t *find) { * We need to get the adbname's lock to unlink the find. */ unlock_bucket = bucket; - violate_locking_hierarchy(&find->lock, &adb->namelocks[unlock_bucket]); - bucket = find->name_bucket; - if (bucket != DNS_ADB_INVALIDBUCKET) { + violate_locking_hierarchy(&find->lock, &unlock_bucket->lock); + bucket = find->bucket; + if (bucket != NULL) { ISC_LIST_UNLINK(find->adbname->finds, find, plink); find->adbname = NULL; - find->name_bucket = DNS_ADB_INVALIDBUCKET; + find->bucket = NULL; } - UNLOCK(&adb->namelocks[unlock_bucket]); - bucket = DNS_ADB_INVALIDBUCKET; + UNLOCK(&unlock_bucket->lock); + bucket = NULL; POST(bucket); cleanup: @@ -3356,7 +2868,6 @@ cleanup: void dns_adb_dump(dns_adb_t *adb, FILE *f) { - unsigned int i; isc_stdtime_t now; REQUIRE(DNS_ADB_VALID(adb)); @@ -3371,14 +2882,7 @@ dns_adb_dump(dns_adb_t *adb, FILE *f) { LOCK(&adb->lock); isc_stdtime_get(&now); - - for (i = 0; i < adb->nnames; i++) { - RUNTIME_CHECK(!cleanup_names(adb, i, now)); - } - for (i = 0; i < adb->nentries; i++) { - RUNTIME_CHECK(!cleanup_entries(adb, i, now)); - } - + clean_hashes(adb, now); dump_adb(adb, f, false, now); UNLOCK(&adb->lock); } @@ -3393,55 +2897,43 @@ 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_adbname_t *name = NULL; - dns_adbentry_t *entry = NULL; + isc_result_t result; + isc_ht_iter_t *it = NULL; fprintf(f, ";\n; Address database dump\n;\n"); fprintf(f, "; [edns success/timeout]\n"); fprintf(f, "; [plain success/timeout]\n;\n"); if (debug) { - LOCK(&adb->reflock); fprintf(f, - "; addr %p, erefcnt %u, irefcnt %u, finds out " + "; addr %p, erefcnt %" PRIuFAST32 + ", irefcnt %" PRIuFAST32 ", finds out " "%" PRIuFAST32 "\n", - adb, adb->erefcnt, adb->irefcnt, + adb, isc_refcount_current(&adb->erefcnt), + isc_refcount_current(&adb->irefcnt), isc_refcount_current(&adb->nhrefcnt)); - UNLOCK(&adb->reflock); } -/* - * In TSAN mode we need to lock the locks individually, as TSAN - * can't handle more than 64 locks locked by one thread. - * In regular mode we want a consistent dump so we need to - * lock everything. - */ -#ifndef __SANITIZE_THREAD__ - for (size_t i = 0; i < adb->nnames; i++) { - LOCK(&adb->namelocks[i]); - } - for (size_t i = 0; i < adb->nentries; i++) { - LOCK(&adb->entrylocks[i]); - } -#endif /* ifndef __SANITIZE_THREAD__ */ + isc_ht_iter_create(adb->namebuckets, &it); + for (result = isc_ht_iter_first(it); result == ISC_R_SUCCESS; + result = isc_ht_iter_next(it)) + { + dns_adbnamebucket_t *bucket = NULL; + dns_adbname_t *name = NULL; - /* - * Dump the names - */ - for (size_t i = 0; i < adb->nnames; i++) { -#ifdef __SANITIZE_THREAD__ - LOCK(&adb->namelocks[i]); -#endif /* ifdef __SANITIZE_THREAD__ */ - name = ISC_LIST_HEAD(adb->names[i]); - if (name == NULL) { -#ifdef __SANITIZE_THREAD__ - UNLOCK(&adb->namelocks[i]); -#endif /* ifdef __SANITIZE_THREAD__ */ - continue; - } + isc_ht_iter_current(it, (void **)&bucket); + LOCK(&bucket->lock); if (debug) { - fprintf(f, "; bucket %zu\n", i); + static int n = 0; + fprintf(f, "; bucket %d\n", n); + n++; } - for (; name != NULL; name = ISC_LIST_NEXT(name, plink)) { + + /* + * Dump the names + */ + for (name = ISC_LIST_HEAD(bucket->names); name != NULL; + name = ISC_LIST_NEXT(name, plink)) + { if (debug) { fprintf(f, "; name %p (flags %08x)\n", name, name->flags); @@ -3473,40 +2965,32 @@ dump_adb(dns_adb_t *adb, FILE *f, bool debug, isc_stdtime_t now) { print_find_list(f, name); } } -#ifdef __SANITIZE_THREAD__ - UNLOCK(&adb->namelocks[i]); -#endif /* ifdef __SANITIZE_THREAD__ */ + UNLOCK(&bucket->lock); } + isc_ht_iter_destroy(&it); fprintf(f, ";\n; Unassociated entries\n;\n"); - for (size_t i = 0; i < adb->nentries; i++) { -#ifdef __SANITIZE_THREAD__ - LOCK(&adb->entrylocks[i]); -#endif /* ifdef __SANITIZE_THREAD__ */ - entry = ISC_LIST_HEAD(adb->entries[i]); - while (entry != NULL) { + isc_ht_iter_create(adb->entrybuckets, &it); + for (result = isc_ht_iter_first(it); result == ISC_R_SUCCESS; + result = isc_ht_iter_next(it)) + { + dns_adbentrybucket_t *bucket = NULL; + dns_adbentry_t *entry = NULL; + + isc_ht_iter_current(it, (void **)&bucket); + LOCK(&bucket->lock); + + for (entry = ISC_LIST_HEAD(bucket->entries); entry != NULL; + entry = ISC_LIST_NEXT(entry, plink)) + { if (entry->nh == 0) { dump_entry(f, adb, entry, debug, now); } - entry = ISC_LIST_NEXT(entry, plink); } -#ifdef __SANITIZE_THREAD__ - UNLOCK(&adb->entrylocks[i]); -#endif /* ifdef __SANITIZE_THREAD__ */ + UNLOCK(&bucket->lock); } - -#ifndef __SANITIZE_THREAD__ - /* - * Unlock everything - */ - for (ssize_t i = adb->nentries - 1; i >= 0; i--) { - UNLOCK(&adb->entrylocks[i]); - } - for (ssize_t i = adb->nnames - 1; i >= 0; i--) { - UNLOCK(&adb->namelocks[i]); - } -#endif /* ifndef __SANITIZE_THREAD__ */ + isc_ht_iter_destroy(&it); } static void @@ -3521,7 +3005,8 @@ dump_entry(FILE *f, dns_adb_t *adb, dns_adbentry_t *entry, bool debug, isc_netaddr_format(&netaddr, addrbuf, sizeof(addrbuf)); if (debug) { - fprintf(f, ";\t%p: refcnt %u\n", entry, entry->refcnt); + fprintf(f, ";\t%p: refcnt %" PRIuFAST32 "\n", entry, + isc_refcount_current(&entry->references)); } fprintf(f, @@ -3580,8 +3065,8 @@ dns_adb_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 %d, name %p, event sender %p\n", - find->name_bucket, find->adbname, find->event.ev_sender); + fprintf(f, ";\tname bucket %p, name %p, event sender %p\n", + find->bucket, find->adbname, find->event.ev_sender); ai = ISC_LIST_HEAD(find->list); if (ai != NULL) { @@ -3813,11 +3298,11 @@ dbfind_name(dns_adbname_t *adbname, isc_stdtime_t now, dns_rdatatype_t rdtype) { static void fetch_callback(isc_task_t *task, isc_event_t *ev) { - dns_fetchevent_t *dev = NULL; + dns_fetchevent_t *dev = (dns_fetchevent_t *)ev; dns_adbname_t *name = NULL; dns_adb_t *adb = NULL; dns_adbfetch_t *fetch = NULL; - int bucket; + dns_adbnamebucket_t *bucket = NULL; isc_eventtype_t ev_status; isc_stdtime_t now; isc_result_t result; @@ -3834,8 +3319,8 @@ fetch_callback(isc_task_t *task, isc_event_t *ev) { REQUIRE(DNS_ADB_VALID(adb)); - bucket = name->lock_bucket; - LOCK(&adb->namelocks[bucket]); + bucket = name->bucket; + LOCK(&bucket->lock); INSIST(NAME_FETCH_A(name) || NAME_FETCH_AAAA(name)); address_type = 0; @@ -3879,7 +3364,7 @@ fetch_callback(isc_task_t *task, isc_event_t *ev) { want_check_exit = kill_name(&name, DNS_EVENT_ADBCANCELED); - UNLOCK(&adb->namelocks[bucket]); + UNLOCK(&bucket->lock); if (want_check_exit) { LOCK(&adb->lock); @@ -3996,7 +3481,7 @@ out: clean_finds_at_name(name, ev_status, address_type); - UNLOCK(&adb->namelocks[bucket]); + UNLOCK(&bucket->lock); } static isc_result_t @@ -4083,16 +3568,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) { - dns_adblameinfo_t *li = NULL; - int bucket; isc_result_t result = ISC_R_SUCCESS; + dns_adblameinfo_t *li = NULL; + dns_adbentrybucket_t *bucket = NULL; REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(DNS_ADBADDRINFO_VALID(addr)); REQUIRE(qname != NULL); - bucket = addr->entry->lock_bucket; - LOCK(&adb->entrylocks[bucket]); + bucket = addr->entry->bucket; + LOCK(&bucket->lock); li = ISC_LIST_HEAD(addr->entry->lameinfo); while (li != NULL && (li->qtype != qtype || !dns_name_equal(qname, &li->qname))) { @@ -4108,46 +3593,46 @@ dns_adb_marklame(dns_adb_t *adb, dns_adbaddrinfo_t *addr, li->lame_timer = expire_time; ISC_LIST_PREPEND(addr->entry->lameinfo, li, plink); -unlock: - UNLOCK(&adb->entrylocks[bucket]); +unlock: + UNLOCK(&bucket->lock); return (result); } void dns_adb_adjustsrtt(dns_adb_t *adb, dns_adbaddrinfo_t *addr, unsigned int rtt, unsigned int factor) { - int bucket; + dns_adbentrybucket_t *bucket = NULL; isc_stdtime_t now = 0; REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(DNS_ADBADDRINFO_VALID(addr)); REQUIRE(factor <= 10); - bucket = addr->entry->lock_bucket; - LOCK(&adb->entrylocks[bucket]); + bucket = addr->entry->bucket; + LOCK(&bucket->lock); if (addr->entry->expires == 0 || factor == DNS_ADB_RTTADJAGE) { isc_stdtime_get(&now); } adjustsrtt(addr, rtt, factor, now); - UNLOCK(&adb->entrylocks[bucket]); + UNLOCK(&bucket->lock); } void dns_adb_agesrtt(dns_adb_t *adb, dns_adbaddrinfo_t *addr, isc_stdtime_t now) { - int bucket; + dns_adbentrybucket_t *bucket = NULL; REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(DNS_ADBADDRINFO_VALID(addr)); - bucket = addr->entry->lock_bucket; - LOCK(&adb->entrylocks[bucket]); + bucket = addr->entry->bucket; + LOCK(&bucket->lock); adjustsrtt(addr, 0, DNS_ADB_RTTADJAGE, now); - UNLOCK(&adb->entrylocks[bucket]); + UNLOCK(&bucket->lock); } static void @@ -4181,7 +3666,7 @@ 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) { - int bucket; + dns_adbentrybucket_t *bucket = NULL; isc_stdtime_t now; REQUIRE(DNS_ADB_VALID(adb)); @@ -4190,8 +3675,8 @@ dns_adb_changeflags(dns_adb_t *adb, dns_adbaddrinfo_t *addr, unsigned int bits, REQUIRE((bits & ENTRY_IS_DEAD) == 0); REQUIRE((mask & ENTRY_IS_DEAD) == 0); - bucket = addr->entry->lock_bucket; - LOCK(&adb->entrylocks[bucket]); + bucket = addr->entry->bucket; + LOCK(&bucket->lock); addr->entry->flags = (addr->entry->flags & ~mask) | (bits & mask); if (addr->entry->expires == 0) { @@ -4205,7 +3690,7 @@ dns_adb_changeflags(dns_adb_t *adb, dns_adbaddrinfo_t *addr, unsigned int bits, */ addr->flags = (addr->flags & ~mask) | (bits & mask); - UNLOCK(&adb->entrylocks[bucket]); + UNLOCK(&bucket->lock); } /* @@ -4289,13 +3774,13 @@ 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) { - int bucket; + dns_adbentrybucket_t *bucket = NULL; REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(DNS_ADBADDRINFO_VALID(addr)); - bucket = addr->entry->lock_bucket; - LOCK(&adb->entrylocks[bucket]); + bucket = addr->entry->bucket; + LOCK(&bucket->lock); maybe_adjust_quota(adb, addr, false); @@ -4306,18 +3791,18 @@ dns_adb_plainresponse(dns_adb_t *adb, dns_adbaddrinfo_t *addr) { addr->entry->plain >>= 1; addr->entry->plainto >>= 1; } - UNLOCK(&adb->entrylocks[bucket]); + UNLOCK(&bucket->lock); } void dns_adb_timeout(dns_adb_t *adb, dns_adbaddrinfo_t *addr) { - int bucket; + dns_adbentrybucket_t *bucket = NULL; REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(DNS_ADBADDRINFO_VALID(addr)); - bucket = addr->entry->lock_bucket; - LOCK(&adb->entrylocks[bucket]); + bucket = addr->entry->bucket; + LOCK(&bucket->lock); maybe_adjust_quota(adb, addr, true); @@ -4328,18 +3813,18 @@ dns_adb_timeout(dns_adb_t *adb, dns_adbaddrinfo_t *addr) { addr->entry->plain >>= 1; addr->entry->plainto >>= 1; } - UNLOCK(&adb->entrylocks[bucket]); + UNLOCK(&bucket->lock); } void dns_adb_ednsto(dns_adb_t *adb, dns_adbaddrinfo_t *addr) { - int bucket; + dns_adbentrybucket_t *bucket = NULL; REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(DNS_ADBADDRINFO_VALID(addr)); - bucket = addr->entry->lock_bucket; - LOCK(&adb->entrylocks[bucket]); + bucket = addr->entry->bucket; + LOCK(&bucket->lock); maybe_adjust_quota(adb, addr, true); @@ -4350,18 +3835,18 @@ dns_adb_ednsto(dns_adb_t *adb, dns_adbaddrinfo_t *addr) { addr->entry->plain >>= 1; addr->entry->plainto >>= 1; } - UNLOCK(&adb->entrylocks[bucket]); + UNLOCK(&bucket->lock); } void dns_adb_setudpsize(dns_adb_t *adb, dns_adbaddrinfo_t *addr, unsigned int size) { - int bucket; + dns_adbentrybucket_t *bucket = NULL; REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(DNS_ADBADDRINFO_VALID(addr)); - bucket = addr->entry->lock_bucket; - LOCK(&adb->entrylocks[bucket]); + bucket = addr->entry->bucket; + LOCK(&bucket->lock); if (size < 512U) { size = 512U; } @@ -4378,21 +3863,21 @@ dns_adb_setudpsize(dns_adb_t *adb, dns_adbaddrinfo_t *addr, unsigned int size) { addr->entry->plain >>= 1; addr->entry->plainto >>= 1; } - UNLOCK(&adb->entrylocks[bucket]); + UNLOCK(&bucket->lock); } unsigned int dns_adb_getudpsize(dns_adb_t *adb, dns_adbaddrinfo_t *addr) { - int bucket; + dns_adbentrybucket_t *bucket = NULL; unsigned int size; REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(DNS_ADBADDRINFO_VALID(addr)); - bucket = addr->entry->lock_bucket; - LOCK(&adb->entrylocks[bucket]); + bucket = addr->entry->bucket; + LOCK(&bucket->lock); size = addr->entry->udpsize; - UNLOCK(&adb->entrylocks[bucket]); + UNLOCK(&bucket->lock); return (size); } @@ -4400,13 +3885,13 @@ 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) { - int bucket; + dns_adbentrybucket_t *bucket = NULL; REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(DNS_ADBADDRINFO_VALID(addr)); - bucket = addr->entry->lock_bucket; - LOCK(&adb->entrylocks[bucket]); + bucket = addr->entry->bucket; + LOCK(&bucket->lock); if (addr->entry->cookie != NULL && (cookie == NULL || len != addr->entry->cookielen)) @@ -4425,19 +3910,19 @@ dns_adb_setcookie(dns_adb_t *adb, dns_adbaddrinfo_t *addr, if (addr->entry->cookie != NULL) { memmove(addr->entry->cookie, cookie, len); } - UNLOCK(&adb->entrylocks[bucket]); + UNLOCK(&bucket->lock); } size_t dns_adb_getcookie(dns_adb_t *adb, dns_adbaddrinfo_t *addr, unsigned char *cookie, size_t len) { - int bucket; + dns_adbentrybucket_t *bucket = NULL; REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(DNS_ADBADDRINFO_VALID(addr)); - bucket = addr->entry->lock_bucket; - LOCK(&adb->entrylocks[bucket]); + bucket = addr->entry->bucket; + LOCK(&bucket->lock); if (cookie != NULL && addr->entry->cookie != NULL && len >= addr->entry->cookielen) { @@ -4446,7 +3931,7 @@ dns_adb_getcookie(dns_adb_t *adb, dns_adbaddrinfo_t *addr, } else { len = 0; } - UNLOCK(&adb->entrylocks[bucket]); + UNLOCK(&bucket->lock); return (len); } @@ -4454,32 +3939,30 @@ 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) { - int bucket; + isc_result_t result = ISC_R_SUCCESS; + dns_adbentrybucket_t *bucket = NULL; dns_adbentry_t *entry = NULL; dns_adbaddrinfo_t *addr = NULL; - isc_result_t result; in_port_t port; REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(addrp != NULL && *addrp == NULL); - UNUSED(now); + entry = find_entry_and_lock(adb, sa, now, &bucket); + INSIST(bucket != NULL); - result = ISC_R_SUCCESS; - bucket = DNS_ADB_INVALIDBUCKET; - entry = find_entry_and_lock(adb, sa, &bucket, now); - INSIST(bucket != DNS_ADB_INVALIDBUCKET); - if (adb->entry_sd[bucket]) { + if (bucket->shuttingdown) { result = ISC_R_SHUTTINGDOWN; goto unlock; } + if (entry == NULL) { /* * We don't know anything about this address. */ entry = new_adbentry(adb); entry->sockaddr = *sa; - link_entry(adb, bucket, entry); + link_entry(bucket, entry); DP(ENTER_LEVEL, "findaddrinfo: new entry %p", entry); } else { DP(ENTER_LEVEL, "findaddrinfo: found entry %p", entry); @@ -4487,12 +3970,10 @@ dns_adb_findaddrinfo(dns_adb_t *adb, const isc_sockaddr_t *sa, port = isc_sockaddr_getport(sa); addr = new_adbaddrinfo(adb, entry, port); - inc_entry_refcnt(adb, entry, false); *addrp = addr; unlock: - UNLOCK(&adb->entrylocks[bucket]); - + UNLOCK(&bucket->lock); return (result); } @@ -4500,32 +3981,31 @@ void dns_adb_freeaddrinfo(dns_adb_t *adb, dns_adbaddrinfo_t **addrp) { dns_adbaddrinfo_t *addr = NULL; dns_adbentry_t *entry = NULL; - int bucket; + dns_adbentrybucket_t *bucket = NULL; isc_stdtime_t now; bool want_check_exit = false; - bool overmem; REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(addrp != NULL); + addr = *addrp; *addrp = NULL; + REQUIRE(DNS_ADBADDRINFO_VALID(addr)); entry = addr->entry; REQUIRE(DNS_ADBENTRY_VALID(entry)); - overmem = isc_mem_isovermem(adb->mctx); - - bucket = addr->entry->lock_bucket; - LOCK(&adb->entrylocks[bucket]); + bucket = addr->entry->bucket; + LOCK(&bucket->lock); if (entry->expires == 0) { isc_stdtime_get(&now); entry->expires = now + ADB_ENTRY_WINDOW; } - want_check_exit = dec_entry_refcnt(adb, overmem, entry, false); + want_check_exit = dec_entry_refcnt(entry, false); - UNLOCK(&adb->entrylocks[bucket]); + UNLOCK(&bucket->lock); addr->entry = NULL; free_adbaddrinfo(adb, &addr); @@ -4539,21 +4019,11 @@ dns_adb_freeaddrinfo(dns_adb_t *adb, dns_adbaddrinfo_t **addrp) { void dns_adb_flush(dns_adb_t *adb) { - unsigned int i; - REQUIRE(DNS_ADB_VALID(adb)); LOCK(&adb->lock); - /* - * Call our cleanup routines. - */ - for (i = 0; i < adb->nnames; i++) { - RUNTIME_CHECK(!cleanup_names(adb, i, INT_MAX)); - } - for (i = 0; i < adb->nentries; i++) { - RUNTIME_CHECK(!cleanup_entries(adb, i, INT_MAX)); - } + clean_hashes(adb, INT_MAX); #ifdef DUMP_ADB_AFTER_CLEANING dump_adb(adb, stdout, true, INT_MAX); @@ -4566,15 +4036,23 @@ void dns_adb_flushname(dns_adb_t *adb, const dns_name_t *name) { dns_adbname_t *adbname = NULL; dns_adbname_t *nextname = NULL; - unsigned int bucket; + dns_adbnamebucket_t *bucket = NULL; + isc_result_t result; REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(name != NULL); LOCK(&adb->lock); - bucket = dns_name_hash(name, false) % adb->nnames; - LOCK(&adb->namelocks[bucket]); - adbname = ISC_LIST_HEAD(adb->names[bucket]); + + result = isc_ht_find(adb->namebuckets, name->ndata, name->length, + (void **)&bucket); + if (result != ISC_R_SUCCESS) { + UNLOCK(&adb->lock); + return; + } + + LOCK(&bucket->lock); + adbname = ISC_LIST_HEAD(bucket->names); while (adbname != NULL) { nextname = ISC_LIST_NEXT(adbname, plink); if (!NAME_DEAD(adbname) && dns_name_equal(name, &adbname->name)) @@ -4584,40 +4062,52 @@ dns_adb_flushname(dns_adb_t *adb, const dns_name_t *name) { } adbname = nextname; } - UNLOCK(&adb->namelocks[bucket]); + UNLOCK(&bucket->lock); UNLOCK(&adb->lock); } void dns_adb_flushnames(dns_adb_t *adb, const dns_name_t *name) { - dns_adbname_t *adbname = NULL, *nextname = NULL; - unsigned int i; + isc_result_t result; + isc_ht_iter_t *iter = NULL; REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(name != NULL); + isc_ht_iter_create(adb->namebuckets, &iter); + LOCK(&adb->lock); - for (i = 0; i < adb->nnames; i++) { - LOCK(&adb->namelocks[i]); - adbname = ISC_LIST_HEAD(adb->names[i]); + for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS; + result = isc_ht_iter_next(iter)) + { + dns_adbnamebucket_t *bucket = NULL; + dns_adbname_t *adbname = NULL, *nextname = NULL; + + isc_ht_iter_current(iter, (void **)&bucket); + LOCK(&bucket->lock); + adbname = ISC_LIST_HEAD(bucket->names); while (adbname != NULL) { - bool ret; nextname = ISC_LIST_NEXT(adbname, plink); if (!NAME_DEAD(adbname) && dns_name_issubdomain(&adbname->name, name)) { - ret = kill_name(&adbname, - DNS_EVENT_ADBCANCELED); + bool ret = kill_name(&adbname, + DNS_EVENT_ADBCANCELED); RUNTIME_CHECK(!ret); } adbname = nextname; } - UNLOCK(&adb->namelocks[i]); + UNLOCK(&bucket->lock); } UNLOCK(&adb->lock); + + isc_ht_iter_destroy(&iter); } 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, @@ -4626,9 +4116,6 @@ water(void *arg, int mark) { * approach, however, keep this function at the moment. */ - dns_adb_t *adb = arg; - bool overmem = (mark == ISC_MEM_HIWATER); - REQUIRE(DNS_ADB_VALID(adb)); DP(ISC_LOG_DEBUG(1), "adb reached %s water mark", diff --git a/lib/dns/include/dns/adb.h b/lib/dns/include/dns/adb.h index cd5092d342..34636adfda 100644 --- a/lib/dns/include/dns/adb.h +++ b/lib/dns/include/dns/adb.h @@ -92,7 +92,9 @@ ISC_LANG_BEGINDECLS *** TYPES ***/ -typedef struct dns_adbname dns_adbname_t; +typedef struct dns_adbname dns_adbname_t; +typedef struct dns_adbnamebucket dns_adbnamebucket_t; +typedef struct dns_adbentrybucket dns_adbentrybucket_t; /*! *\brief @@ -114,13 +116,13 @@ struct dns_adbfind { ISC_LINK(dns_adbfind_t) publink; /*%< RW: client use */ /* Private */ - isc_mutex_t lock; /* locks all below */ - in_port_t port; - int name_bucket; - unsigned int flags; - dns_adbname_t *adbname; - dns_adb_t *adb; - isc_event_t event; + isc_mutex_t lock; /* locks all below */ + in_port_t port; + dns_adbnamebucket_t *bucket; + unsigned int flags; + dns_adbname_t *adbname; + dns_adb_t *adb; + isc_event_t event; ISC_LINK(dns_adbfind_t) plink; };