* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: adb.c,v 1.181.2.4 2002/08/05 06:57:11 marka Exp $ */
+/* $Id: adb.c,v 1.181.2.4.4.1 2003/02/17 05:32:17 marka Exp $ */
/*
* Implementation notes
static inline dns_adbentry_t *new_adbentry(dns_adb_t *);
static inline void free_adbentry(dns_adb_t *, dns_adbentry_t **);
static inline dns_adbfind_t *new_adbfind(dns_adb_t *);
-static inline void free_adbfind(dns_adb_t *, dns_adbfind_t **);
+static inline isc_boolean_t free_adbfind(dns_adb_t *, dns_adbfind_t **);
static inline dns_adbaddrinfo_t *new_adbaddrinfo(dns_adb_t *, dns_adbentry_t *,
in_port_t);
static inline dns_adbfetch_t *new_adbfetch(dns_adb_t *);
isc_boolean_t debug);
static void print_find_list(FILE *, dns_adbname_t *);
static void print_fetch_list(FILE *, dns_adbname_t *);
-static inline void dec_adb_irefcnt(dns_adb_t *);
+static inline isc_boolean_t dec_adb_irefcnt(dns_adb_t *);
static inline void inc_adb_erefcnt(dns_adb_t *);
static inline void inc_entry_refcnt(dns_adb_t *, dns_adbentry_t *,
isc_boolean_t);
-static inline void dec_entry_refcnt(dns_adb_t *, dns_adbentry_t *,
+static inline isc_boolean_t dec_entry_refcnt(dns_adb_t *, dns_adbentry_t *,
isc_boolean_t);
static inline void violate_locking_hierarchy(isc_mutex_t *, isc_mutex_t *);
-static void clean_namehooks(dns_adb_t *, dns_adbnamehooklist_t *);
+static isc_boolean_t clean_namehooks(dns_adb_t *, dns_adbnamehooklist_t *);
static void clean_target(dns_adb_t *, dns_name_t *);
static void clean_finds_at_name(dns_adbname_t *, isc_eventtype_t,
unsigned int);
-static void check_expire_namehooks(dns_adbname_t *, isc_stdtime_t);
+static isc_boolean_t check_expire_namehooks(dns_adbname_t *, isc_stdtime_t);
static void cancel_fetches_at_name(dns_adbname_t *);
static isc_result_t dbfind_name(dns_adbname_t *, isc_stdtime_t,
dns_rdatatype_t);
static inline void check_exit(dns_adb_t *);
static void timer_cleanup(isc_task_t *, isc_event_t *);
static void destroy(dns_adb_t *);
-static void shutdown_names(dns_adb_t *);
-static void shutdown_entries(dns_adb_t *);
+static isc_boolean_t shutdown_names(dns_adb_t *);
+static isc_boolean_t shutdown_entries(dns_adb_t *);
static inline void link_name(dns_adb_t *, int, dns_adbname_t *);
-static inline void unlink_name(dns_adb_t *, dns_adbname_t *);
+static inline isc_boolean_t unlink_name(dns_adb_t *, dns_adbname_t *);
static inline void link_entry(dns_adb_t *, int, dns_adbentry_t *);
-static inline void unlink_entry(dns_adb_t *, dns_adbentry_t *);
-static void kill_name(dns_adbname_t **, isc_eventtype_t);
+static inline isc_boolean_t unlink_entry(dns_adb_t *, dns_adbentry_t *);
+static isc_boolean_t kill_name(dns_adbname_t **, isc_eventtype_t);
static void fetch_callback_a6(isc_task_t *, isc_event_t *);
static isc_result_t dbfind_a6(dns_adbname_t *, isc_stdtime_t);
/*
* Requires the name's bucket be locked.
*/
-static void
+static isc_boolean_t
kill_name(dns_adbname_t **n, isc_eventtype_t ev) {
dns_adbname_t *name;
+ isc_boolean_t result = ISC_FALSE;
+ isc_boolean_t result4, result6;
dns_adb_t *adb;
INSIST(n != NULL);
* away now or not.
*/
if (NAME_DEAD(name) && !NAME_FETCH(name)) {
- unlink_name(adb, name);
+ result = unlink_name(adb, name);
free_adbname(adb, &name);
- return;
+ if (result)
+ result = dec_adb_irefcnt(adb);
+ return (result);
}
/*
* in that they will always empty the list.
*/
clean_finds_at_name(name, ev, DNS_ADBFIND_ADDRESSMASK);
- clean_namehooks(adb, &name->v4);
- clean_namehooks(adb, &name->v6);
+ result4 = clean_namehooks(adb, &name->v4);
+ result6 = clean_namehooks(adb, &name->v6);
clean_target(adb, &name->target);
+ result = ISC_TF(result4 || result6);
/*
* If fetches are running, cancel them. If none are running, we can
* just kill the name here.
*/
if (!NAME_FETCH(name)) {
- unlink_name(adb, name);
+ INSIST(result == ISC_FALSE);
+ result = unlink_name(adb, name);
free_adbname(adb, &name);
+ if (result)
+ result = dec_adb_irefcnt(adb);
} else {
name->flags |= NAME_IS_DEAD;
cancel_fetches_at_name(name);
}
+ return (result);
}
/*
* Requires the name's bucket be locked and no entry buckets be locked.
*/
-static void
+static isc_boolean_t
check_expire_namehooks(dns_adbname_t *name, isc_stdtime_t now) {
dns_adb_t *adb;
+ isc_boolean_t result4 = ISC_FALSE;
+ isc_boolean_t result6 = ISC_FALSE;
INSIST(DNS_ADBNAME_VALID(name));
adb = name->adb;
if (!NAME_FETCH_V4(name) && EXPIRE_OK(name->expire_v4, now)) {
if (NAME_HAS_V4(name)) {
DP(DEF_LEVEL, "expiring v4 for name %p", name);
- clean_namehooks(adb, &name->v4);
+ result4 = clean_namehooks(adb, &name->v4);
name->partial_result &= ~DNS_ADBFIND_INET;
}
name->expire_v4 = INT_MAX;
if (!NAME_FETCH_V6(name) && EXPIRE_OK(name->expire_v6, now)) {
if (NAME_HAS_V6(name)) {
DP(DEF_LEVEL, "expiring v6 for name %p", name);
- clean_namehooks(adb, &name->v6);
+ result6 = clean_namehooks(adb, &name->v6);
name->partial_result &= ~DNS_ADBFIND_INET6;
}
name->expire_v6 = INT_MAX;
clean_target(adb, &name->target);
name->expire_target = INT_MAX;
}
+ return (ISC_TF(result4 || result6));
}
/*
/*
* Requires the name's bucket be locked.
*/
-static inline void
+static inline isc_boolean_t
unlink_name(dns_adb_t *adb, dns_adbname_t *name) {
int bucket;
+ isc_boolean_t result = ISC_FALSE;
bucket = name->lock_bucket;
INSIST(bucket != DNS_ADB_INVALIDBUCKET);
INSIST(adb->name_refcnt[bucket] > 0);
adb->name_refcnt[bucket]--;
if (adb->name_sd[bucket] && adb->name_refcnt[bucket] == 0)
- dec_adb_irefcnt(adb);
+ result = ISC_TRUE;
+ return (result);
}
/*
/*
* Requires the entry's bucket be locked.
*/
-static inline void
+static inline isc_boolean_t
unlink_entry(dns_adb_t *adb, dns_adbentry_t *entry) {
int bucket;
+ isc_boolean_t result = ISC_FALSE;
bucket = entry->lock_bucket;
INSIST(bucket != DNS_ADB_INVALIDBUCKET);
INSIST(adb->entry_refcnt[bucket] > 0);
adb->entry_refcnt[bucket]--;
if (adb->entry_sd[bucket] && adb->entry_refcnt[bucket] == 0)
- dec_adb_irefcnt(adb);
+ result = ISC_TRUE;
+ return (result);
}
static inline void
* The ADB _MUST_ be locked before calling. Also, exit conditions must be
* checked after calling this function.
*/
-static void
+static isc_boolean_t
shutdown_names(dns_adb_t *adb) {
int bucket;
+ isc_boolean_t result = ISC_FALSE;
dns_adbname_t *name;
dns_adbname_t *next_name;
* irefcnt ourselves, since it will not be
* automatically triggered by a name being unlinked.
*/
- dec_adb_irefcnt(adb);
+ INSIST(result == ISC_FALSE);
+ result = dec_adb_irefcnt(adb);
} else {
/*
* Run through the list. For each name, clean up finds
*/
while (name != NULL) {
next_name = ISC_LIST_NEXT(name, plink);
- kill_name(&name, DNS_EVENT_ADBSHUTDOWN);
+ INSIST(result == ISC_FALSE);
+ result = kill_name(&name,
+ DNS_EVENT_ADBSHUTDOWN);
name = next_name;
}
}
UNLOCK(&adb->namelocks[bucket]);
}
+ return (result);
}
/*
* The ADB _MUST_ be locked before calling. Also, exit conditions must be
* checked after calling this function.
*/
-static void
+static isc_boolean_t
shutdown_entries(dns_adb_t *adb) {
int bucket;
+ isc_boolean_t result = ISC_FALSE;
dns_adbentry_t *entry;
dns_adbentry_t *next_entry;
* irefcnt ourselves, since it will not be
* automatically triggered by an entry being unlinked.
*/
- dec_adb_irefcnt(adb);
+ result = dec_adb_irefcnt(adb);
} else {
/*
* Run through the list. Cleanup any entries not
next_entry = ISC_LIST_NEXT(entry, plink);
if (entry->refcnt == 0 &&
entry->expires != 0) {
- unlink_entry(adb, entry);
+ result = unlink_entry(adb, entry);
free_adbentry(adb, &entry);
+ if (result)
+ result = dec_adb_irefcnt(adb);
}
entry = next_entry;
}
UNLOCK(&adb->entrylocks[bucket]);
}
+ return (result);
}
/*
/*
* Assumes the name bucket is locked.
*/
-static void
+static isc_boolean_t
clean_namehooks(dns_adb_t *adb, dns_adbnamehooklist_t *namehooks) {
dns_adbentry_t *entry;
dns_adbnamehook_t *namehook;
int addr_bucket;
+ isc_boolean_t result = ISC_FALSE;
addr_bucket = DNS_ADB_INVALIDBUCKET;
namehook = ISC_LIST_HEAD(*namehooks);
LOCK(&adb->entrylocks[addr_bucket]);
}
- dec_entry_refcnt(adb, entry, ISC_FALSE);
+ result = dec_entry_refcnt(adb, entry, ISC_FALSE);
}
/*
if (addr_bucket != DNS_ADB_INVALIDBUCKET)
UNLOCK(&adb->entrylocks[addr_bucket]);
+ return (result);
}
static void
static inline void
check_exit(dns_adb_t *adb) {
isc_event_t *event;
- isc_task_t *etask;
- isc_boolean_t zeroirefcnt, zeroerefcnt;
-
/*
* The caller must be holding the adb lock.
*/
+ if (adb->shutting_down) {
+ /*
+ * If there aren't any external references either, we're
+ * done. Send the control event to initiate shutdown.
+ */
+ INSIST(!adb->cevent_sent); /* Sanity check. */
+ event = &adb->cevent;
+ isc_task_send(adb->task, &event);
+ adb->cevent_sent = ISC_TRUE;
+ }
+}
+
+static inline isc_boolean_t
+dec_adb_irefcnt(dns_adb_t *adb) {
+ isc_event_t *event;
+ isc_task_t *etask;
+ isc_boolean_t result = ISC_FALSE;
LOCK(&adb->reflock);
- zeroirefcnt = ISC_TF(adb->irefcnt == 0);
- zeroerefcnt = ISC_TF(adb->erefcnt == 0);
- UNLOCK(&adb->reflock);
- if (adb->shutting_down && zeroirefcnt &&
- isc_mempool_getallocated(adb->ahmp) == 0) {
- /*
- * We're now shutdown. Send any whenshutdown events.
- */
+ INSIST(adb->irefcnt > 0);
+ adb->irefcnt--;
+
+ if (adb->irefcnt == 0) {
event = ISC_LIST_HEAD(adb->whenshutdown);
while (event != NULL) {
ISC_LIST_UNLINK(adb->whenshutdown, event, ev_link);
isc_task_sendanddetach(&etask, &event);
event = ISC_LIST_HEAD(adb->whenshutdown);
}
-
- /*
- * If there aren't any external references either, we're
- * done. Send the control event to initiate shutdown.
- */
- if (zeroerefcnt) {
- INSIST(!adb->cevent_sent); /* Sanity check. */
- event = &adb->cevent;
- isc_task_send(adb->task, &event);
- adb->cevent_sent = ISC_TRUE;
- }
}
+
+ if (adb->irefcnt == 0 && adb->erefcnt == 0)
+ result = ISC_TRUE;
+ UNLOCK(&adb->reflock);
+ return (result);
}
static inline void
-dec_adb_irefcnt(dns_adb_t *adb) {
+inc_adb_irefcnt(dns_adb_t *adb) {
LOCK(&adb->reflock);
-
- INSIST(adb->irefcnt > 0);
- adb->irefcnt--;
-
+ adb->irefcnt++;
UNLOCK(&adb->reflock);
}
UNLOCK(&adb->entrylocks[bucket]);
}
-static inline void
+static inline isc_boolean_t
dec_entry_refcnt(dns_adb_t *adb, dns_adbentry_t *entry, isc_boolean_t lock) {
int bucket;
isc_boolean_t destroy_entry;
+ isc_boolean_t result = ISC_FALSE;
bucket = entry->lock_bucket;
if (entry->refcnt == 0 &&
(adb->entry_sd[bucket] || entry->expires == 0)) {
destroy_entry = ISC_TRUE;
- unlink_entry(adb, entry);
+ result = unlink_entry(adb, entry);
}
if (lock)
UNLOCK(&adb->entrylocks[bucket]);
if (!destroy_entry)
- return;
+ return (result);
entry->lock_bucket = DNS_ADB_INVALIDBUCKET;
free_adbentry(adb, &entry);
+ if (result)
+ result =dec_adb_irefcnt(adb);
+
+ return (result);
}
static inline dns_adbname_t *
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);
}
isc_mempool_put(adb->af6mp, f);
}
-static inline void
+static inline isc_boolean_t
free_adbfind(dns_adb_t *adb, dns_adbfind_t **findp) {
dns_adbfind_t *find;
DESTROYLOCK(&find->lock);
isc_mempool_put(adb->ahmp, find);
+ return (dec_adb_irefcnt(adb));
}
/*
/*
* name bucket must be locked; adb may be locked; no other locks held.
*/
-static void
+static isc_boolean_t
check_expire_name(dns_adbname_t **namep, isc_stdtime_t now) {
dns_adbname_t *name;
+ isc_result_t result = ISC_FALSE;
INSIST(namep != NULL && DNS_ADBNAME_VALID(*namep));
name = *namep;
if (NAME_HAS_V4(name) || NAME_HAS_V6(name))
- return;
+ return (result);
if (NAME_FETCH(name))
- return;
+ return (result);
if (!EXPIRE_OK(name->expire_v4, now))
- return;
+ return (result);
if (!EXPIRE_OK(name->expire_v6, now))
- return;
+ return (result);
if (!EXPIRE_OK(name->expire_target, now))
- return;
+ return (result);
/*
* The name is empty. Delete it.
*/
- kill_name(&name, DNS_EVENT_ADBEXPIRED);
+ result = kill_name(&name, DNS_EVENT_ADBEXPIRED);
*namep = NULL;
/*
* 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 (result);
}
/*
* entry bucket must be locked; adb may be locked; no other locks held.
*/
-static void
+static isc_boolean_t
check_expire_entry(dns_adb_t *adb, dns_adbentry_t **entryp, isc_stdtime_t now)
{
dns_adbentry_t *entry;
+ isc_boolean_t result = ISC_FALSE;
INSIST(entryp != NULL && DNS_ADBENTRY_VALID(*entryp));
entry = *entryp;
if (entry->refcnt != 0)
- return;
+ return (result);
if (entry->expires == 0 || entry->expires > now)
- return;
+ return (result);
/*
* The entry is not in use. Delete it.
*/
DP(DEF_LEVEL, "killing entry %p", entry);
INSIST(ISC_LINK_LINKED(entry, plink));
- unlink_entry(adb, entry);
+ result = unlink_entry(adb, entry);
free_adbentry(adb, &entry);
+ if (result)
+ dec_adb_irefcnt(adb);
*entryp = NULL;
+ return (result);
}
/*
* ADB must be locked, and no other locks held.
*/
-static void
+static isc_boolean_t
cleanup_names(dns_adb_t *adb, int bucket, isc_stdtime_t now) {
dns_adbname_t *name;
dns_adbname_t *next_name;
+ isc_result_t result = ISC_FALSE;
DP(CLEAN_LEVEL, "cleaning name bucket %d", bucket);
LOCK(&adb->namelocks[bucket]);
if (adb->name_sd[bucket]) {
UNLOCK(&adb->namelocks[bucket]);
- return;
+ return (result);
}
name = ISC_LIST_HEAD(adb->names[bucket]);
while (name != NULL) {
next_name = ISC_LIST_NEXT(name, plink);
- check_expire_namehooks(name, now);
- check_expire_name(&name, now);
+ INSIST(result == ISC_FALSE);
+ result = check_expire_namehooks(name, now);
+ if (!result)
+ result = check_expire_name(&name, now);
name = next_name;
}
UNLOCK(&adb->namelocks[bucket]);
+ return (result);
}
/*
* ADB must be locked, and no other locks held.
*/
-static void
+static isc_boolean_t
cleanup_entries(dns_adb_t *adb, int bucket, isc_stdtime_t now) {
dns_adbentry_t *entry, *next_entry;
+ isc_boolean_t result = ISC_FALSE;
DP(CLEAN_LEVEL, "cleaning entry bucket %d", bucket);
entry = ISC_LIST_HEAD(adb->entries[bucket]);
while (entry != NULL) {
next_entry = ISC_LIST_NEXT(entry, plink);
- check_expire_entry(adb, &entry, now);
+ INSIST(result == ISC_FALSE);
+ result = check_expire_entry(adb, &entry, now);
entry = next_entry;
}
UNLOCK(&adb->entrylocks[bucket]);
+ return (result);
}
static void
/*
* Call our cleanup routines.
*/
- cleanup_names(adb, adb->next_cleanbucket, now);
- cleanup_entries(adb, adb->next_cleanbucket, now);
+ INSIST(cleanup_names(adb, adb->next_cleanbucket, now) ==
+ ISC_FALSE);
+ INSIST(cleanup_entries(adb, adb->next_cleanbucket, now) ==
+ ISC_FALSE);
/*
* Set the next bucket to be cleaned.
void
dns_adb_detach(dns_adb_t **adbx) {
dns_adb_t *adb;
- isc_boolean_t zeroerefcnt;
+ isc_boolean_t need_exit_check;
REQUIRE(adbx != NULL && DNS_ADB_VALID(*adbx));
LOCK(&adb->reflock);
adb->erefcnt--;
- zeroerefcnt = ISC_TF(adb->erefcnt == 0);
+ need_exit_check = ISC_TF(adb->erefcnt == 0 && adb->irefcnt == 0);
UNLOCK(&adb->reflock);
- if (zeroerefcnt) {
+ if (need_exit_check) {
LOCK(&adb->lock);
- check_exit(adb);
INSIST(adb->shutting_down);
+ check_exit(adb);
UNLOCK(&adb->lock);
}
}
LOCK(&adb->reflock);
zeroirefcnt = ISC_TF(adb->irefcnt == 0);
- UNLOCK(&adb->reflock);
if (adb->shutting_down && zeroirefcnt &&
isc_mempool_getallocated(adb->ahmp) == 0) {
ISC_LIST_APPEND(adb->whenshutdown, event, ev_link);
}
+ UNLOCK(&adb->reflock);
UNLOCK(&adb->lock);
}
void
dns_adb_shutdown(dns_adb_t *adb) {
+ isc_boolean_t need_check_exit;
/*
* Shutdown 'adb'.
if (!adb->shutting_down) {
adb->shutting_down = ISC_TRUE;
- shutdown_names(adb);
- shutdown_entries(adb);
- check_exit(adb);
+ need_check_exit = shutdown_names(adb);
+ if (!need_check_exit)
+ need_check_exit = shutdown_entries(adb);
+ if (need_check_exit)
+ check_exit(adb);
}
UNLOCK(&adb->lock);
if (adb->name_sd[bucket]) {
DP(DEF_LEVEL,
"dns_adb_createfind: returning ISC_R_SHUTTINGDOWN");
- free_adbfind(adb, &find);
+ INSIST(free_adbfind(adb, &find) == ISC_FALSE);
result = ISC_R_SHUTTINGDOWN;
goto out;
}
if (adbname == NULL) {
adbname = new_adbname(adb, name);
if (adbname == NULL) {
- free_adbfind(adb, &find);
+ INSIST(free_adbfind(adb, &find) == ISC_FALSE);
result = ISC_R_NOMEMORY;
goto out;
}
/*
* Expire old entries, etc.
*/
- check_expire_namehooks(adbname, now);
+ INSIST(check_expire_namehooks(adbname, now) == ISC_FALSE);
/*
* Do we know that the name is an alias?
entry = ai->entry;
ai->entry = NULL;
INSIST(DNS_ADBENTRY_VALID(entry));
- dec_entry_refcnt(adb, entry, ISC_TRUE);
+ INSIST(dec_entry_refcnt(adb, entry, ISC_TRUE) == ISC_FALSE);
free_adbaddrinfo(adb, &ai);
ai = ISC_LIST_HEAD(find->list);
}
* lock is destroyed.
*/
LOCK(&adb->lock);
- free_adbfind(adb, &find);
- check_exit(adb);
+ if (free_adbfind(adb, &find))
+ check_exit(adb);
UNLOCK(&adb->lock);
}
free_adbfetch(adb, &fetch);
isc_event_free(&ev);
- kill_name(&name, DNS_EVENT_ADBCANCELED);
-
- /*
- * If we're shutting down and this bucket is empty, we need
- * to call check_exit() to see if we're done.
- */
- if (adb->name_sd[bucket] && adb->name_refcnt[bucket] == 0)
- want_check_exit = ISC_TRUE;
+ want_check_exit = kill_name(&name, DNS_EVENT_ADBCANCELED);
UNLOCK(&adb->namelocks[bucket]);
free_adbfetch6(adb, &fetch);
isc_event_free(&ev);
- kill_name(&name, DNS_EVENT_ADBCANCELED);
-
- /*
- * If we're shutting down and this bucket is empty, we need
- * to call check_exit() to see if we're done.
- */
- if (adb->name_sd[bucket] && adb->name_refcnt[bucket] == 0)
- want_check_exit = ISC_TRUE;
+ want_check_exit = kill_name(&name, DNS_EVENT_ADBCANCELED);
UNLOCK(&adb->namelocks[bucket]);
entry->expires = now + 1800; /* XXXRTH */
- dec_entry_refcnt(adb, entry, ISC_FALSE);
- if (adb->entry_sd[bucket] && adb->entry_refcnt[bucket] == 0)
- want_check_exit = ISC_TRUE;
+ want_check_exit = dec_entry_refcnt(adb, entry, ISC_FALSE);
UNLOCK(&adb->entrylocks[bucket]);
/*
* Call our cleanup routines.
*/
- cleanup_names(adb, i, INT_MAX);
- cleanup_entries(adb, i, INT_MAX);
+ INSIST(cleanup_names(adb, i, INT_MAX) == ISC_FALSE);
+ INSIST(cleanup_entries(adb, i, INT_MAX) == ISC_FALSE);
}
#ifdef DUMP_ADB_AFTER_CLEANING