diff options
Diffstat (limited to 'lib/dns/adb.c')
-rw-r--r-- | lib/dns/adb.c | 168 |
1 files changed, 126 insertions, 42 deletions
diff --git a/lib/dns/adb.c b/lib/dns/adb.c index 10d51bc44143..c75ea59f751f 100644 --- a/lib/dns/adb.c +++ b/lib/dns/adb.c @@ -15,8 +15,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: adb.c,v 1.264 2011/12/05 17:10:51 each Exp $ */ - /*! \file * * \note @@ -157,7 +155,7 @@ struct dns_adb { unsigned int *entry_refcnt; isc_event_t cevent; - isc_boolean_t cevent_sent; + isc_boolean_t cevent_out; isc_boolean_t shutting_down; isc_eventlist_t whenshutdown; isc_event_t growentries; @@ -201,6 +199,7 @@ struct dns_adbfetch { unsigned int magic; dns_fetch_t *fetch; dns_rdataset_t rdataset; + unsigned int depth; }; /*% @@ -245,6 +244,7 @@ struct dns_adbentry { isc_sockaddr_t sockaddr; isc_stdtime_t expires; + isc_stdtime_t lastage; /*%< * A nonzero 'expires' field indicates that the entry should * persist until that time. This allows entries found @@ -300,8 +300,7 @@ static inline isc_boolean_t dec_entry_refcnt(dns_adb_t *, isc_boolean_t, static inline void violate_locking_hierarchy(isc_mutex_t *, isc_mutex_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 clean_finds_at_name(dns_adbname_t *, isc_eventtype_t, unsigned int); static isc_boolean_t check_expire_namehooks(dns_adbname_t *, isc_stdtime_t); static isc_boolean_t check_expire_entry(dns_adb_t *, dns_adbentry_t **, isc_stdtime_t); @@ -309,6 +308,7 @@ 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 isc_result_t fetch_name(dns_adbname_t *, isc_boolean_t, + unsigned int, isc_counter_t *qc, dns_rdatatype_t); static inline void check_exit(dns_adb_t *); static void destroy(dns_adb_t *); @@ -321,6 +321,9 @@ 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 water(void *, int); static void dump_entry(FILE *, dns_adbentry_t *, isc_boolean_t, isc_stdtime_t); +static void adjustsrtt(dns_adbaddrinfo_t *addr, unsigned int rtt, + unsigned int factor, isc_stdtime_t now); +static void shutdown_task(isc_task_t *task, isc_event_t *ev); /* * MUST NOT overlap DNS_ADBFIND_* flags! @@ -344,7 +347,7 @@ static void dump_entry(FILE *, dns_adbentry_t *, isc_boolean_t, isc_stdtime_t); * Private flag(s) for entries. * MUST NOT overlap FCTX_ADDRINFO_xxx and DNS_FETCHOPT_NOEDNS0. */ -#define ENTRY_IS_DEAD 0x80000000 +#define ENTRY_IS_DEAD 0x00400000 /* * To the name, address classes are all that really exist. If it has a @@ -1498,10 +1501,13 @@ check_exit(dns_adb_t *adb) { * If there aren't any external references either, we're * done. Send the control event to initiate shutdown. */ - INSIST(!adb->cevent_sent); /* Sanity check. */ + INSIST(!adb->cevent_out); /* Sanity check. */ + ISC_EVENT_INIT(&adb->cevent, sizeof(adb->cevent), 0, NULL, + DNS_EVENT_ADBCONTROL, shutdown_task, adb, + adb, NULL, NULL); event = &adb->cevent; isc_task_send(adb->task, &event); - adb->cevent_sent = ISC_TRUE; + adb->cevent_out = ISC_TRUE; } } @@ -1756,6 +1762,7 @@ new_adbentry(dns_adb_t *adb) { e->flags = 0; isc_random_get(&r); e->srtt = (r & 0x1f) + 1; + e->lastage = 0; e->expires = 0; ISC_LIST_INIT(e->lameinfo); ISC_LINK_INIT(e, plink); @@ -2430,10 +2437,9 @@ dns_adb_create(isc_mem_t *mem, dns_view_t *view, isc_timermgr_t *timermgr, adb->view = view; adb->taskmgr = taskmgr; adb->next_cleanbucket = 0; - ISC_EVENT_INIT(&adb->cevent, sizeof(adb->cevent), 0, NULL, - DNS_EVENT_ADBCONTROL, shutdown_task, adb, - adb, NULL, NULL); - adb->cevent_sent = ISC_FALSE; + ISC_EVENT_INIT(&adb->cevent, sizeof(adb->cevent), + 0, NULL, 0, NULL, NULL, NULL, NULL, NULL); + adb->cevent_out = ISC_FALSE; adb->shutting_down = ISC_FALSE; ISC_LIST_INIT(adb->whenshutdown); @@ -2467,7 +2473,7 @@ dns_adb_create(isc_mem_t *mem, dns_view_t *view, isc_timermgr_t *timermgr, "intializing table sizes to %u\n", nbuckets[11]); adb->nentries = nbuckets[11]; - adb->nnames= nbuckets[11]; + adb->nnames = nbuckets[11]; } @@ -2740,9 +2746,28 @@ dns_adb_whenshutdown(dns_adb_t *adb, isc_task_t *task, isc_event_t **eventp) { UNLOCK(&adb->lock); } +static void +shutdown_stage2(isc_task_t *task, isc_event_t *event) { + dns_adb_t *adb; + + UNUSED(task); + + adb = event->ev_arg; + INSIST(DNS_ADB_VALID(adb)); + + LOCK(&adb->lock); + INSIST(adb->shutting_down); + adb->cevent_out = ISC_FALSE; + (void)shutdown_names(adb); + (void)shutdown_entries(adb); + if (dec_adb_irefcnt(adb)) + check_exit(adb); + UNLOCK(&adb->lock); +} + void dns_adb_shutdown(dns_adb_t *adb) { - isc_boolean_t need_check_exit; + isc_event_t *event; /* * Shutdown 'adb'. @@ -2753,11 +2778,16 @@ dns_adb_shutdown(dns_adb_t *adb) { if (!adb->shutting_down) { adb->shutting_down = ISC_TRUE; isc_mem_setwater(adb->mctx, water, adb, 0, 0); - need_check_exit = shutdown_names(adb); - if (!need_check_exit) - need_check_exit = shutdown_entries(adb); - if (need_check_exit) - check_exit(adb); + /* + * Isolate shutdown_names and shutdown_entries calls. + */ + inc_adb_irefcnt(adb); + ISC_EVENT_INIT(&adb->cevent, sizeof(adb->cevent), 0, NULL, + DNS_EVENT_ADBCONTROL, shutdown_stage2, adb, + adb, NULL, NULL); + adb->cevent_out = ISC_TRUE; + event = &adb->cevent; + isc_task_send(adb->task, &event); } UNLOCK(&adb->lock); @@ -2770,6 +2800,19 @@ dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, isc_stdtime_t now, dns_name_t *target, in_port_t port, dns_adbfind_t **findp) { + return (dns_adb_createfind2(adb, task, action, arg, name, + qname, qtype, options, now, + target, port, 0, NULL, findp)); +} + +isc_result_t +dns_adb_createfind2(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, + void *arg, dns_name_t *name, dns_name_t *qname, + dns_rdatatype_t qtype, unsigned int options, + isc_stdtime_t now, dns_name_t *target, + in_port_t port, unsigned int depth, isc_counter_t *qc, + dns_adbfind_t **findp) +{ dns_adbfind_t *find; dns_adbname_t *adbname; int bucket; @@ -3000,7 +3043,7 @@ dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, * Start V4. */ if (WANT_INET(wanted_fetches) && - fetch_name(adbname, start_at_zone, + fetch_name(adbname, start_at_zone, depth, qc, dns_rdatatype_a) == ISC_R_SUCCESS) { DP(DEF_LEVEL, "dns_adb_createfind: started A fetch for name %p", @@ -3011,7 +3054,7 @@ dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, * Start V6. */ if (WANT_INET6(wanted_fetches) && - fetch_name(adbname, start_at_zone, + fetch_name(adbname, start_at_zone, depth, qc, dns_rdatatype_aaaa) == ISC_R_SUCCESS) { DP(DEF_LEVEL, "dns_adb_createfind: " @@ -3754,6 +3797,12 @@ fetch_callback(isc_task_t *task, isc_event_t *ev) { DP(DEF_LEVEL, "adb: fetch of '%s' %s failed: %s", buf, address_type == DNS_ADBFIND_INET ? "A" : "AAAA", dns_result_totext(dev->result)); + /* + * Don't record a failure unless this is the initial + * fetch of a chain. + */ + if (fetch->depth > 1) + goto out; /* XXXMLG Don't pound on bad servers. */ if (address_type == DNS_ADBFIND_INET) { name->expire_v4 = ISC_MIN(name->expire_v4, now + 300); @@ -3791,9 +3840,8 @@ fetch_callback(isc_task_t *task, isc_event_t *ev) { } static isc_result_t -fetch_name(dns_adbname_t *adbname, - isc_boolean_t start_at_zone, - dns_rdatatype_t type) +fetch_name(dns_adbname_t *adbname, isc_boolean_t start_at_zone, + unsigned int depth, isc_counter_t *qc, dns_rdatatype_t type) { isc_result_t result; dns_adbfetch_t *fetch = NULL; @@ -3838,12 +3886,14 @@ fetch_name(dns_adbname_t *adbname, result = ISC_R_NOMEMORY; goto cleanup; } - - result = dns_resolver_createfetch(adb->view->resolver, &adbname->name, - type, name, nameservers, NULL, - options, adb->task, fetch_callback, - adbname, &fetch->rdataset, NULL, - &fetch->fetch); + fetch->depth = depth; + + result = dns_resolver_createfetch3(adb->view->resolver, &adbname->name, + type, name, nameservers, NULL, + NULL, 0, options, depth, qc, + adb->task, fetch_callback, adbname, + &fetch->rdataset, NULL, + &fetch->fetch); if (result != ISC_R_SUCCESS) goto cleanup; @@ -3912,8 +3962,7 @@ dns_adb_adjustsrtt(dns_adb_t *adb, dns_adbaddrinfo_t *addr, unsigned int rtt, unsigned int factor) { int bucket; - unsigned int new_srtt; - isc_stdtime_t now; + isc_stdtime_t now = 0; REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(DNS_ADBADDRINFO_VALID(addr)); @@ -3922,21 +3971,53 @@ dns_adb_adjustsrtt(dns_adb_t *adb, dns_adbaddrinfo_t *addr, bucket = addr->entry->lock_bucket; LOCK(&adb->entrylocks[bucket]); - if (factor == DNS_ADB_RTTADJAGE) - new_srtt = addr->entry->srtt * 98 / 100; - else + if (addr->entry->expires == 0 || factor == DNS_ADB_RTTADJAGE) + isc_stdtime_get(&now); + adjustsrtt(addr, rtt, factor, now); + + UNLOCK(&adb->entrylocks[bucket]); +} + +void +dns_adb_agesrtt(dns_adb_t *adb, dns_adbaddrinfo_t *addr, isc_stdtime_t now) { + int bucket; + + REQUIRE(DNS_ADB_VALID(adb)); + REQUIRE(DNS_ADBADDRINFO_VALID(addr)); + + bucket = addr->entry->lock_bucket; + LOCK(&adb->entrylocks[bucket]); + + adjustsrtt(addr, 0, DNS_ADB_RTTADJAGE, now); + + UNLOCK(&adb->entrylocks[bucket]); +} + +static void +adjustsrtt(dns_adbaddrinfo_t *addr, unsigned int rtt, unsigned int factor, + isc_stdtime_t now) +{ + isc_uint64_t new_srtt; + + if (factor == DNS_ADB_RTTADJAGE) { + if (addr->entry->lastage != now) { + new_srtt = addr->entry->srtt; + new_srtt <<= 9; + new_srtt -= addr->entry->srtt; + new_srtt >>= 9; + addr->entry->lastage = now; + } else + new_srtt = addr->entry->srtt; + } else new_srtt = (addr->entry->srtt / 10 * factor) + (rtt / 10 * (10 - factor)); - addr->entry->srtt = new_srtt; - addr->srtt = new_srtt; + new_srtt &= 0xffffffff; + addr->entry->srtt = (unsigned int) new_srtt; + addr->srtt = (unsigned int) new_srtt; - if (addr->entry->expires == 0) { - isc_stdtime_get(&now); + if (addr->entry->expires == 0) addr->entry->expires = now + ADB_ENTRY_WINDOW; - } - - UNLOCK(&adb->entrylocks[bucket]); } void @@ -3949,6 +4030,9 @@ dns_adb_changeflags(dns_adb_t *adb, dns_adbaddrinfo_t *addr, REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(DNS_ADBADDRINFO_VALID(addr)); + REQUIRE((bits & ENTRY_IS_DEAD) == 0); + REQUIRE((mask & ENTRY_IS_DEAD) == 0); + bucket = addr->entry->lock_bucket; LOCK(&adb->entrylocks[bucket]); |