#include <isc/print.h>
#include <isc/random.h>
#include <isc/result.h>
+#include <isc/rwlock.h>
#include <isc/stats.h>
#include <isc/string.h>
#include <isc/task.h>
dns_adbnamelist_t names_lru;
isc_stdtime_t names_last_update;
isc_hashmap_t *names;
- isc_mutex_t names_lock;
+ isc_rwlock_t names_lock;
dns_adbentrylist_t entries_lru;
isc_stdtime_t entries_last_update;
isc_hashmap_t *entries;
- isc_mutex_t entries_lock;
+ isc_rwlock_t entries_lock;
isc_stats_t *stats;
static void
expire_name(dns_adbname_t *adbname, isc_eventtype_t evtype);
static bool
+entry_expired(dns_adbentry_t *adbentry, isc_stdtime_t now);
+static bool
maybe_expire_entry(dns_adbentry_t *adbentry, isc_stdtime_t now);
static void
expire_entry(dns_adbentry_t *adbentry);
shutdown_names(dns_adb_t *adb) {
dns_adbname_t *next = NULL;
- LOCK(&adb->names_lock);
+ RWLOCK(&adb->names_lock, isc_rwlocktype_write);
for (dns_adbname_t *name = ISC_LIST_HEAD(adb->names_lru); name != NULL;
name = next)
{
UNLOCK(&name->lock);
dns_adbname_detach(&name);
}
- UNLOCK(&adb->names_lock);
+ RWUNLOCK(&adb->names_lock, isc_rwlocktype_write);
}
static void
shutdown_entries(dns_adb_t *adb) {
dns_adbentry_t *next = NULL;
- LOCK(&adb->entries_lock);
+ RWLOCK(&adb->entries_lock, isc_rwlocktype_write);
for (dns_adbentry_t *adbentry = ISC_LIST_HEAD(adb->entries_lru);
adbentry != NULL; adbentry = next)
{
next = ISC_LIST_NEXT(adbentry, link);
expire_entry(adbentry);
}
- UNLOCK(&adb->entries_lock);
+ RWUNLOCK(&adb->entries_lock, isc_rwlocktype_write);
}
/*
isc_time_t timenow;
isc_stdtime_t last_update;
adbnamekey_t key;
+ isc_rwlocktype_t locktype = isc_rwlocktype_read;
isc_time_set(&timenow, now, 0);
hashval = isc_hashmap_hash(adb->names, &key.key, key.size);
- LOCK(&adb->names_lock);
+ RWLOCK(&adb->names_lock, locktype);
last_update = adb->names_last_update;
- if (last_update + ADB_STALE_MARGIN < now ||
+
+ if (last_update + ADB_STALE_MARGIN >= now ||
atomic_load_relaxed(&adb->is_overmem))
{
- last_update = adb->names_last_update = now;
-
+ last_update = now;
+ UPGRADELOCK(&adb->names_lock, locktype);
purge_stale_names(adb, now);
+ adb->names_last_update = last_update;
}
result = isc_hashmap_find(adb->names, &hashval, key.key, key.size,
(void **)&adbname);
switch (result) {
case ISC_R_NOTFOUND:
+ UPGRADELOCK(&adb->names_lock, locktype);
+
/* Allocate a new name and add it to the hash table. */
adbname = new_adbname(adb, name, start_at_zone);
result = isc_hashmap_add(adb->names, &hashval,
&adbname->key.key, adbname->key.size,
adbname);
+ if (result == ISC_R_EXISTS) {
+ destroy_adbname(adbname);
+ adbname = NULL;
+ result = isc_hashmap_find(adb->names, &hashval, key.key,
+ key.size, (void **)&adbname);
+ ISC_LIST_UNLINK(adb->names_lru, adbname, link);
+ }
INSIST(result == ISC_R_SUCCESS);
- dns_adbname_ref(adbname);
- LOCK(&adbname->lock); /* Must be unlocked by the caller */
- adbname->last_used = now;
-
- ISC_LIST_PREPEND(adb->names_lru, adbname, link);
break;
case ISC_R_SUCCESS:
- dns_adbname_ref(adbname);
- LOCK(&adbname->lock); /* Must be unlocked by the caller */
- if (adbname->last_used + ADB_CACHE_MINIMUM <= last_update) {
- adbname->last_used = now;
-
+ if (locktype == isc_rwlocktype_write) {
ISC_LIST_UNLINK(adb->names_lru, adbname, link);
- ISC_LIST_PREPEND(adb->names_lru, adbname, link);
}
break;
default:
UNREACHABLE();
}
+ dns_adbname_ref(adbname);
+
+ LOCK(&adbname->lock); /* Must be unlocked by the caller */
+ if (adbname->last_used + ADB_CACHE_MINIMUM <= last_update) {
+ adbname->last_used = now;
+ }
+ if (locktype == isc_rwlocktype_write) {
+ ISC_LIST_PREPEND(adb->names_lru, adbname, link);
+ }
+
/*
* The refcount is now 2 and the final detach will happen in
* expire_name() - the unused adbname stored in the hashtable and lru
* has always refcount == 1
*/
- UNLOCK(&adb->names_lock);
+ RWUNLOCK(&adb->names_lock, locktype);
return (adbname);
}
isc_stdtime_t last_update;
uint32_t hashval = isc_hashmap_hash(
adb->entries, (const unsigned char *)addr, sizeof(*addr));
+ isc_rwlocktype_t locktype = isc_rwlocktype_read;
isc_time_set(&timenow, now, 0);
- LOCK(&adb->entries_lock);
+ RWLOCK(&adb->entries_lock, locktype);
last_update = adb->entries_last_update;
+
if (now - last_update > ADB_STALE_MARGIN ||
atomic_load_relaxed(&adb->is_overmem))
{
- last_update = adb->entries_last_update = now;
+ last_update = now;
+
+ UPGRADELOCK(&adb->entries_lock, locktype);
purge_stale_entries(adb, now);
+ adb->entries_last_update = now;
}
+again:
result = isc_hashmap_find(adb->entries, &hashval,
(const unsigned char *)addr, sizeof(*addr),
(void **)&adbentry);
switch (result) {
case ISC_R_NOTFOUND: {
create:
+ UPGRADELOCK(&adb->entries_lock, locktype);
+
/* Allocate a new entry and add it to the hash table. */
adbentry = new_adbentry(adb, addr);
result = isc_hashmap_add(adb->entries, &hashval,
&adbentry->sockaddr,
sizeof(adbentry->sockaddr), adbentry);
- INSIST(result == ISC_R_SUCCESS);
-
- dns_adbentry_ref(adbentry);
- LOCK(&adbentry->lock); /* Must be unlocked by the caller */
- adbentry->last_used = now;
+ if (result == ISC_R_EXISTS) {
+ dns_adbentry_detach(&adbentry);
- ISC_LIST_PREPEND(adb->entries_lru, adbentry, link);
+ result = isc_hashmap_find(adb->entries, &hashval,
+ (const unsigned char *)addr,
+ sizeof(*addr),
+ (void **)&adbentry);
+ ISC_LIST_UNLINK(adb->entries_lru, adbentry, link);
+ }
+ INSIST(result == ISC_R_SUCCESS);
break;
}
case ISC_R_SUCCESS:
- /*
- * The dns_adbentry_ref() must stay here before trying to expire
- * the ADB entry, so it is not destroyed under the lock.
- */
- dns_adbentry_ref(adbentry);
- LOCK(&adbentry->lock); /* Must be unlocked by the caller */
+ if (locktype == isc_rwlocktype_write) {
+ ISC_LIST_UNLINK(adb->entries_lru, adbentry, link);
+ }
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ /*
+ * The dns_adbentry_ref() must stay here before trying to expire
+ * the ADB entry, so it is not destroyed under the lock.
+ */
+ dns_adbentry_ref(adbentry);
+ LOCK(&adbentry->lock); /* Must be unlocked by the caller */
+ switch (locktype) {
+ case isc_rwlocktype_read:
+ if (entry_expired(adbentry, now)) {
+ UNLOCK(&adbentry->lock);
+ dns_adbentry_detach(&adbentry);
+ goto again;
+ }
+ break;
+ case isc_rwlocktype_write:
if (maybe_expire_entry(adbentry, now)) {
UNLOCK(&adbentry->lock);
dns_adbentry_detach(&adbentry);
goto create;
}
- if (adbentry->last_used + ADB_CACHE_MINIMUM <= last_update) {
- adbentry->last_used = now;
-
- ISC_LIST_UNLINK(adb->entries_lru, adbentry, link);
- ISC_LIST_PREPEND(adb->entries_lru, adbentry, link);
- }
break;
default:
UNREACHABLE();
}
- UNLOCK(&adb->entries_lock);
+ if (adbentry->last_used + ADB_CACHE_MINIMUM <= last_update) {
+ adbentry->last_used = now;
+ }
+ if (locktype == isc_rwlocktype_write) {
+ ISC_LIST_PREPEND(adb->entries_lru, adbentry, link);
+ }
+
+ RWUNLOCK(&adb->entries_lock, locktype);
return (adbentry);
}
}
static bool
-maybe_expire_entry(dns_adbentry_t *adbentry, isc_stdtime_t now) {
- REQUIRE(DNS_ADBENTRY_VALID(adbentry));
-
+entry_expired(dns_adbentry_t *adbentry, isc_stdtime_t now) {
if (!ISC_LIST_EMPTY(adbentry->nhs)) {
return (false);
}
return (false);
}
- expire_entry(adbentry);
-
return (true);
}
+static bool
+maybe_expire_entry(dns_adbentry_t *adbentry, isc_stdtime_t now) {
+ REQUIRE(DNS_ADBENTRY_VALID(adbentry));
+
+ if (entry_expired(adbentry, now)) {
+ expire_entry(adbentry);
+ return (true);
+ }
+
+ return (false);
+}
+
/*%
* Examine the tail entry of the LRU list to see if it expires or is stale
* (unused for some period); if so, the name entry will be freed. If the ADB
cleanup_names(dns_adb_t *adb, isc_stdtime_t now) {
dns_adbname_t *next = NULL;
- LOCK(&adb->names_lock);
+ RWLOCK(&adb->names_lock, isc_rwlocktype_write);
for (dns_adbname_t *adbname = ISC_LIST_HEAD(adb->names_lru);
adbname != NULL; adbname = next)
{
UNLOCK(&adbname->lock);
dns_adbname_detach(&adbname);
}
- UNLOCK(&adb->names_lock);
+ RWUNLOCK(&adb->names_lock, isc_rwlocktype_write);
}
/*%
cleanup_entries(dns_adb_t *adb, isc_stdtime_t now) {
dns_adbentry_t *next = NULL;
- LOCK(&adb->entries_lock);
+ RWLOCK(&adb->entries_lock, isc_rwlocktype_write);
for (dns_adbentry_t *adbentry = ISC_LIST_HEAD(adb->entries_lru);
adbentry != NULL; adbentry = next)
{
UNLOCK(&adbentry->lock);
dns_adbentry_detach(&adbentry);
}
- UNLOCK(&adb->entries_lock);
+ RWUNLOCK(&adb->entries_lock, isc_rwlocktype_write);
}
static void
adb->magic = 0;
- LOCK(&adb->names_lock);
+ RWLOCK(&adb->names_lock, isc_rwlocktype_write);
INSIST(isc_hashmap_count(adb->names) == 0);
isc_hashmap_destroy(&adb->names);
- UNLOCK(&adb->names_lock);
- isc_mutex_destroy(&adb->names_lock);
+ RWUNLOCK(&adb->names_lock, isc_rwlocktype_write);
+ isc_rwlock_destroy(&adb->names_lock);
- LOCK(&adb->entries_lock);
+ RWLOCK(&adb->entries_lock, isc_rwlocktype_write);
/* There are no unassociated entries */
INSIST(isc_hashmap_count(adb->entries) == 0);
isc_hashmap_destroy(&adb->entries);
- UNLOCK(&adb->entries_lock);
- isc_mutex_destroy(&adb->entries_lock);
+ RWUNLOCK(&adb->entries_lock, isc_rwlocktype_write);
+ isc_rwlock_destroy(&adb->entries_lock);
isc_mem_destroy(&adb->hmctx);
isc_hashmap_create(adb->hmctx, ADB_HASH_BITS,
ISC_HASHMAP_CASE_INSENSITIVE, &adb->names);
- isc_mutex_init(&adb->names_lock);
+ isc_rwlock_init(&adb->names_lock);
isc_hashmap_create(adb->hmctx, ADB_HASH_BITS,
ISC_HASHMAP_CASE_SENSITIVE, &adb->entries);
- isc_mutex_init(&adb->entries_lock);
+ isc_rwlock_init(&adb->entries_lock);
isc_mutex_init(&adb->lock);
isc_mutex_destroy(&adb->lock);
- isc_mutex_destroy(&adb->entries_lock);
+ isc_rwlock_destroy(&adb->entries_lock);
isc_hashmap_destroy(&adb->entries);
INSIST(ISC_LIST_EMPTY(adb->entries_lru));
- isc_mutex_destroy(&adb->names_lock);
+ isc_rwlock_destroy(&adb->names_lock);
isc_hashmap_destroy(&adb->names);
INSIST(ISC_LIST_EMPTY(adb->names_lru));
/*
* Ensure this operation is applied to both hash tables at once.
*/
- LOCK(&adb->names_lock);
+ RWLOCK(&adb->names_lock, isc_rwlocktype_write);
for (dns_adbname_t *name = ISC_LIST_HEAD(adb->names_lru); name != NULL;
name = ISC_LIST_NEXT(name, link))
UNLOCK(&name->lock);
}
- LOCK(&adb->entries_lock);
+ RWLOCK(&adb->entries_lock, isc_rwlocktype_write);
fprintf(f, ";\n; Unassociated entries\n;\n");
for (dns_adbentry_t *adbentry = ISC_LIST_HEAD(adb->entries_lru);
adbentry != NULL; adbentry = ISC_LIST_NEXT(adbentry, link))
UNLOCK(&adbentry->lock);
}
- UNLOCK(&adb->entries_lock);
- UNLOCK(&adb->names_lock);
+ RWUNLOCK(&adb->entries_lock, isc_rwlocktype_write);
+ RWUNLOCK(&adb->names_lock, isc_rwlocktype_write);
}
static void
isc_hashmap_iter_t *it = NULL;
isc_result_t result;
- LOCK(&adb->entries_lock);
+ RWLOCK(&adb->entries_lock, isc_rwlocktype_read);
isc_hashmap_iter_create(adb->entries, &it);
for (result = isc_hashmap_iter_first(it); result == ISC_R_SUCCESS;
result = isc_hashmap_iter_next(it))
UNLOCK(&entry->lock);
}
isc_hashmap_iter_destroy(&it);
- UNLOCK(&adb->entries_lock);
+ RWUNLOCK(&adb->entries_lock, isc_rwlocktype_read);
return (ISC_R_SUCCESS);
}
return;
}
- LOCK(&adb->names_lock);
+ RWLOCK(&adb->names_lock, isc_rwlocktype_write);
again:
/*
* Delete both entries - without and with NAME_STARTATZONE set.
start_at_zone = true;
goto again;
}
- UNLOCK(&adb->names_lock);
+ RWUNLOCK(&adb->names_lock, isc_rwlocktype_write);
}
void
return;
}
- LOCK(&adb->names_lock);
+ RWLOCK(&adb->names_lock, isc_rwlocktype_write);
for (dns_adbname_t *adbname = ISC_LIST_HEAD(adb->names_lru);
adbname != NULL; adbname = next)
{
UNLOCK(&adbname->lock);
dns_adbname_detach(&adbname);
}
- UNLOCK(&adb->names_lock);
+ RWUNLOCK(&adb->names_lock, isc_rwlocktype_write);
}
static void