Commit e34efacc authored by Michael Graff's avatar Michael Graff
Browse files

merge adb_race branch

parent e903df2f
......@@ -4,16 +4,7 @@ a6_remove open jinmei
blabel-cleanup
ipv6_6to4 feedback marka
libbind_clean open jinmei
rt3598 closed explorer
rt3625 closed explorer
rt3746 open marka
rt3892 closed explorer
rt4090 closed explorer
rt4112 closed explorer
adb_race review explorer
rt4319 closed explorer
rt3907 closed explorer
rt3507 closed ogud (pull down by explorer)
v6source
rt3445 closed
......@@ -26,3 +17,12 @@ rt3502 closed
ipv6-improvements closed
ipv6-scope closed
marka_google closed
rt3598 closed explorer
rt3625 closed explorer
rt3892 closed explorer
rt4090 closed explorer
rt4112 closed explorer
rt4319 closed explorer
rt3907 closed explorer
rt3507 closed ogud (pull down by explorer)
adb_race closed explorer
......@@ -15,7 +15,7 @@
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: adb.c,v 1.198 2002/09/12 00:28:52 marka Exp $ */
/* $Id: adb.c,v 1.199 2002/11/13 00:07:04 explorer Exp $ */
/*
* Implementation notes
......@@ -282,7 +282,7 @@ static inline void free_adbzoneinfo(dns_adb_t *, dns_adbzoneinfo_t **);
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 *);
......@@ -302,18 +302,19 @@ static void print_namehook_list(FILE *, const char *legend,
isc_stdtime_t now);
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_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,
isc_boolean_t);
static void cancel_fetches_at_name(dns_adbname_t *);
static isc_result_t dbfind_name(dns_adbname_t *, isc_stdtime_t,
......@@ -324,13 +325,13 @@ static isc_result_t fetch_name_a6(dns_adbname_t *, isc_boolean_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);
static void water(void *arg, int mark);
......@@ -677,9 +678,11 @@ import_a6(dns_a6context_t *a6ctx) {
/*
* 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);
......@@ -696,9 +699,11 @@ kill_name(dns_adbname_t **n, isc_eventtype_t ev) {
* 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);
}
/*
......@@ -706,32 +711,39 @@ kill_name(dns_adbname_t **n, isc_eventtype_t ev) {
* 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,
isc_boolean_t overmem)
{
dns_adb_t *adb;
isc_boolean_t expire;
isc_boolean_t result4 = ISC_FALSE;
isc_boolean_t result6 = ISC_FALSE;
INSIST(DNS_ADBNAME_VALID(name));
adb = name->adb;
......@@ -753,7 +765,7 @@ check_expire_namehooks(dns_adbname_t *name, isc_stdtime_t now,
(expire || 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;
......@@ -766,7 +778,7 @@ check_expire_namehooks(dns_adbname_t *name, isc_stdtime_t now,
(expire || 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;
......@@ -779,6 +791,7 @@ check_expire_namehooks(dns_adbname_t *name, isc_stdtime_t now,
clean_target(adb, &name->target);
name->expire_target = INT_MAX;
}
return (ISC_TF(result4 || result6));
}
/*
......@@ -796,9 +809,10 @@ link_name(dns_adb_t *adb, int bucket, dns_adbname_t *name) {
/*
* 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);
......@@ -808,7 +822,8 @@ unlink_name(dns_adb_t *adb, dns_adbname_t *name) {
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);
}
/*
......@@ -824,9 +839,10 @@ link_entry(dns_adb_t *adb, int bucket, dns_adbentry_t *entry) {
/*
* 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);
......@@ -836,7 +852,8 @@ unlink_entry(dns_adb_t *adb, dns_adbentry_t *entry) {
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
......@@ -852,9 +869,10 @@ 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 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;
......@@ -869,7 +887,8 @@ shutdown_names(dns_adb_t *adb) {
* 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
......@@ -879,22 +898,26 @@ shutdown_names(dns_adb_t *adb) {
*/
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;
......@@ -909,7 +932,7 @@ shutdown_entries(dns_adb_t *adb) {
* 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
......@@ -919,8 +942,10 @@ shutdown_entries(dns_adb_t *adb) {
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;
}
......@@ -928,6 +953,7 @@ shutdown_entries(dns_adb_t *adb) {
UNLOCK(&adb->entrylocks[bucket]);
}
return (result);
}
/*
......@@ -955,11 +981,12 @@ cancel_fetches_at_name(dns_adbname_t *name) {
/*
* 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);
......@@ -980,7 +1007,7 @@ clean_namehooks(dns_adb_t *adb, dns_adbnamehooklist_t *namehooks) {
LOCK(&adb->entrylocks[addr_bucket]);
}
dec_entry_refcnt(adb, entry, ISC_FALSE);
result = dec_entry_refcnt(adb, entry, ISC_FALSE);
}
/*
......@@ -995,6 +1022,7 @@ clean_namehooks(dns_adb_t *adb, dns_adbnamehooklist_t *namehooks) {
if (addr_bucket != DNS_ADB_INVALIDBUCKET)
UNLOCK(&adb->entrylocks[addr_bucket]);
return (result);
}
static void
......@@ -1183,23 +1211,33 @@ clean_finds_at_name(dns_adbname_t *name, isc_eventtype_t evtype,
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);
......@@ -1208,27 +1246,18 @@ check_exit(dns_adb_t *adb) {
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);
}
......@@ -1254,10 +1283,11 @@ inc_entry_refcnt(dns_adb_t *adb, dns_adbentry_t *entry, isc_boolean_t lock) {
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;
......@@ -1271,18 +1301,22 @@ dec_entry_refcnt(dns_adb_t *adb, dns_adbentry_t *entry, isc_boolean_t lock) {
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 *
......@@ -1497,6 +1531,7 @@ new_adbfind(dns_adb_t *adb) {
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);
}
......@@ -1686,7 +1721,7 @@ free_adbfetch6(dns_adb_t *adb, dns_adbfetch6_t **fetch) {
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;
......@@ -1704,6 +1739,7 @@ free_adbfind(dns_adb_t *adb, dns_adbfind_t **findp) {
DESTROYLOCK(&find->lock);
isc_mempool_put(adb->ahmp, find);
return (dec_adb_irefcnt(adb));
}
/*
......@@ -1959,50 +1995,53 @@ shutdown_task(isc_task_t *task, isc_event_t *ev) {
/*
* 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 expire;
isc_boolean_t result = ISC_FALSE;
INSIST(entryp != NULL && DNS_ADBENTRY_VALID(*entryp));
entry = *entryp;
if (entry->refcnt != 0)
return;
return (result);
if (adb->overmem) {
isc_uint32_t val;
......@@ -2014,49 +2053,57 @@ check_expire_entry(dns_adb_t *adb, dns_adbentry_t **entryp, isc_stdtime_t now)
expire = ISC_FALSE;
if (entry->expires == 0 || (! expire && 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, adb->overmem);
check_expire_name(&name, now);
INSIST(result == ISC_FALSE);
result = check_expire_namehooks(name, now, adb->overmem);
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);
......@@ -2064,10 +2111,12 @@ cleanup_entries(dns_adb_t *adb, int bucket, isc_stdtime_t now) {
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
......@@ -2090,8 +2139,10 @@ timer_cleanup(isc_task_t *task, isc_event_t *ev) {
/*
* 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.
......@@ -2346,7 +2397,7 @@ dns_adb_attach(dns_adb_t *adb, dns_adb_t **adbx) {
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));
......@@ -2357,13 +2408,13 @@ dns_adb_detach(dns_adb_t **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);