diff options
author | LaMont Jones <lamont@debian.org> | 2009-03-20 08:23:23 -0600 |
---|---|---|
committer | LaMont Jones <lamont@debian.org> | 2009-03-20 08:23:23 -0600 |
commit | 95cab7ab13c7aedf768b3273a9a736f23239c18f (patch) | |
tree | 8c3bda91eb3defc1c5ce1f40a58a6898a2c35cf7 /lib/dns | |
parent | 08ba6b17bb4653d8401e266e9249620c9fe89404 (diff) | |
download | bind9-95cab7ab13c7aedf768b3273a9a736f23239c18f.tar.gz |
9.6.0b1
Diffstat (limited to 'lib/dns')
-rw-r--r-- | lib/dns/adb.c | 13 | ||||
-rw-r--r-- | lib/dns/rbtdb.c | 215 | ||||
-rw-r--r-- | lib/dns/resolver.c | 5 | ||||
-rw-r--r-- | lib/dns/zone.c | 10 |
4 files changed, 143 insertions, 100 deletions
diff --git a/lib/dns/adb.c b/lib/dns/adb.c index 96c42603..a41515cd 100644 --- a/lib/dns/adb.c +++ b/lib/dns/adb.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: adb.c,v 1.241 2008/05/03 05:07:13 marka Exp $ */ +/* $Id: adb.c,v 1.243 2008/10/17 03:23:13 marka Exp $ */ /*! \file * @@ -1737,8 +1737,11 @@ copy_namehook_lists(dns_adb_t *adb, dns_adbfind_t *find, dns_name_t *qname, bucket = entry->lock_bucket; LOCK(&adb->entrylocks[bucket]); - if (entry_is_lame(adb, entry, qname, qtype, now)) + if (!FIND_RETURNLAME(find) + && entry_is_lame(adb, entry, qname, qtype, now)) { + find->options |= DNS_ADBFIND_LAMEPRUNED; goto nextv6; + } addrinfo = new_adbaddrinfo(adb, entry, find->port); if (addrinfo == NULL) { find->partial_result |= DNS_ADBFIND_INET6; @@ -3127,8 +3130,10 @@ fetch_callback(isc_task_t *task, isc_event_t *ev) { address_type = DNS_ADBFIND_INET6; fetch = name->fetch_aaaa; name->fetch_aaaa = NULL; - } - INSIST(address_type != 0); + } else + fetch = NULL; + + INSIST(address_type != 0 && fetch != NULL); dns_resolver_destroyfetch(&fetch->fetch); dev->fetch = NULL; diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c index 362c68d5..8d0010e5 100644 --- a/lib/dns/rbtdb.c +++ b/lib/dns/rbtdb.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rbtdb.c,v 1.264 2008/09/24 03:16:57 tbox Exp $ */ +/* $Id: rbtdb.c,v 1.269 2008/10/29 05:53:12 marka Exp $ */ /*! \file */ @@ -322,7 +322,26 @@ struct acachectl { (((header)->attributes & RDATASET_ATTR_OPTOUT) != 0) #define DEFAULT_NODE_LOCK_COUNT 7 /*%< Should be prime. */ -#define DEFAULT_CACHE_NODE_LOCK_COUNT 1009 /*%< Should be prime. */ + +/*% + * Number of buckets for cache DB entries (locks, LRU lists, TTL heaps). + * There is a tradeoff issue about configuring this value: if this is too + * small, it may cause heavier contention between threads; if this is too large, + * LRU purge algorithm won't work well (entries tend to be purged prematurely). + * The default value should work well for most environments, but this can + * also be configurable at compilation time via the + * DNS_RBTDB_CACHE_NODE_LOCK_COUNT variable. This value must be larger than + * 1 due to the assumption of overmem_purge(). + */ +#ifdef DNS_RBTDB_CACHE_NODE_LOCK_COUNT +#if DNS_RBTDB_CACHE_NODE_LOCK_COUNT <= 1 +#error "DNS_RBTDB_CACHE_NODE_LOCK_COUNT must be larger 1" +#else +#define DEFAULT_CACHE_NODE_LOCK_COUNT DNS_RBTDB_CACHE_NODE_LOCK_COUNT +#endif +#else +#define DEFAULT_CACHE_NODE_LOCK_COUNT 16 +#endif /* DNS_RBTDB_CACHE_NODE_LOCK_COUNT */ typedef struct { nodelock_t lock; @@ -340,6 +359,12 @@ typedef struct rbtdb_changed { typedef ISC_LIST(rbtdb_changed_t) rbtdb_changedlist_t; +typedef enum { + dns_db_insecure, + dns_db_partial, + dns_db_secure +} dns_db_secure_t; + typedef struct rbtdb_version { /* Not locked */ rbtdb_serial_t serial; @@ -355,7 +380,7 @@ typedef struct rbtdb_version { rbtdb_changedlist_t changed_list; rdatasetheaderlist_t resigned_list; ISC_LINK(struct rbtdb_version) link; - isc_boolean_t secure; + dns_db_secure_t secure; isc_boolean_t havensec3; /* NSEC3 parameters */ dns_hash_t hash; @@ -367,12 +392,6 @@ typedef struct rbtdb_version { typedef ISC_LIST(rbtdb_version_t) rbtdb_versionlist_t; -typedef enum { - dns_db_insecure, - dns_db_partial, - dns_db_secure -} dns_db_secure_t; - typedef struct { /* Unlocked. */ dns_db_t common; @@ -499,8 +518,10 @@ static inline isc_boolean_t need_headerupdate(rdatasetheader_t *header, isc_stdtime_t now); static void update_header(dns_rbtdb_t *rbtdb, rdatasetheader_t *header, isc_stdtime_t now); -static void check_stale_cache(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, - isc_stdtime_t now, isc_boolean_t tree_locked); +static void expire_header(dns_rbtdb_t *rbtdb, rdatasetheader_t *header, + isc_boolean_t tree_locked); +static void overmem_purge(dns_rbtdb_t *rbtdb, unsigned int locknum_start, + isc_stdtime_t now, isc_boolean_t tree_locked); static isc_result_t resign_insert(dns_rbtdb_t *rbtdb, int idx, rdatasetheader_t *newheader); @@ -3032,23 +3053,28 @@ matchparams(rdatasetheader_t *header, rbtdb_search_t *search) #else raw += 2; #endif - rdlen = raw[0] * 256 + raw[1]; + while (count-- > 0) { + rdlen = raw[0] * 256 + raw[1]; #if DNS_RDATASET_FIXED - raw += 4; + raw += 4; #else - raw += 2; + raw += 2; #endif - region.base = raw; - region.length = rdlen; - dns_rdata_fromregion(&rdata, search->rbtdb->common.rdclass, - dns_rdatatype_nsec3, ®ion); - result = dns_rdata_tostruct(&rdata, &nsec3, NULL); - INSIST(result == ISC_R_SUCCESS); - if (nsec3.hash == search->rbtversion->hash && - nsec3.iterations == search->rbtversion->iterations && - nsec3.salt_length == search->rbtversion->salt_length && - memcmp(nsec3.salt, search->rbtversion->salt, nsec3.salt_length) == 0) - return (ISC_TRUE); + region.base = raw; + region.length = rdlen; + dns_rdata_fromregion(&rdata, search->rbtdb->common.rdclass, + dns_rdatatype_nsec3, ®ion); + raw += rdlen; + result = dns_rdata_tostruct(&rdata, &nsec3, NULL); + INSIST(result == ISC_R_SUCCESS); + if (nsec3.hash == search->rbtversion->hash && + nsec3.iterations == search->rbtversion->iterations && + nsec3.salt_length == search->rbtversion->salt_length && + memcmp(nsec3.salt, search->rbtversion->salt, + nsec3.salt_length) == 0) + return (ISC_TRUE); + dns_rdata_reset(&rdata); + } return (ISC_FALSE); } @@ -3204,7 +3230,7 @@ find_closest_nsec(rbtdb_search_t *search, dns_dbnode_t **nodep, if (result == ISC_R_NOMORE && wraps) { result = dns_rbtnodechain_last(&search->chain, tree, NULL, NULL); - if (result == ISC_R_SUCCESS) { + if (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN) { wraps = ISC_FALSE; goto again; } @@ -5776,6 +5802,7 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, rbtdb_version_t *rbtversion = version; isc_region_t region; rdatasetheader_t *newheader; + rdatasetheader_t *header; isc_result_t result; isc_boolean_t delegating; isc_boolean_t tree_locked = ISC_FALSE; @@ -5871,6 +5898,9 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_write); } + if (IS_CACHE(rbtdb) && rbtdb->overmem) + overmem_purge(rbtdb, rbtnode->locknum, now, tree_locked); + NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock, isc_rwlocktype_write); @@ -5882,7 +5912,10 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, if (IS_CACHE(rbtdb)) { if (tree_locked) cleanup_dead_nodes(rbtdb, rbtnode->locknum); - check_stale_cache(rbtdb, rbtnode, now, tree_locked); + + header = isc_heap_element(rbtdb->heaps[rbtnode->locknum], 1); + if (header && header->rdh_ttl <= now - RBTDB_VIRTUAL) + expire_header(rbtdb, header, tree_locked); /* * If we've been holding a write lock on the tree just for @@ -6740,11 +6773,20 @@ dns_rbtdb_create if (result != ISC_R_SUCCESS) goto cleanup_lock; + /* + * Initialize node_lock_count in a generic way to support future + * extension which allows the user to specify this value on creation. + * Note that when specified for a cache DB it must be larger than 1 + * as commented with the definition of DEFAULT_CACHE_NODE_LOCK_COUNT. + */ if (rbtdb->node_lock_count == 0) { if (IS_CACHE(rbtdb)) rbtdb->node_lock_count = DEFAULT_CACHE_NODE_LOCK_COUNT; else rbtdb->node_lock_count = DEFAULT_NODE_LOCK_COUNT; + } else if (rbtdb->node_lock_count < 2 && IS_CACHE(rbtdb)) { + result = ISC_R_RANGE; + goto cleanup_tree_lock; } INSIST(rbtdb->node_lock_count < (1 << DNS_RBT_LOCKLENGTH)); rbtdb->node_locks = isc_mem_get(mctx, rbtdb->node_lock_count * @@ -8323,79 +8365,70 @@ update_header(dns_rbtdb_t *rbtdb, rdatasetheader_t *header, } /*% - * Examine the tail entry of the LRU list to see if it expires or is stale - * (unused for some period). If so, it's marked as stale and possibly freed. - * If the DB is in the overmem condition, the tail and the next to tail entries - * will be unconditionally marked. We don't care about a race on 'overmem' - * at the risk of causing some collateral damage or a small delay in starting - * cleanup, so we don't bother to lock rbtdb. - * - * Caller must hold the node (write) lock. - * - * We can get away with locking only one node here, since it will lock all - * other nodes in that lock pool bucket. + * Purge some expired and/or stale (i.e. unused for some period) cache entries + * under an overmem condition. To recover from this condition quickly, up to + * 2 entries will be purged. This process is triggered while adding a new + * entry, and we specifically avoid purging entries in the same LRU bucket as + * the one to which the new entry will belong. Otherwise, we might purge + * entries of the same name of different RR types while adding RRsets from a + * single response (consider the case where we're adding A and AAAA glue records + * of the same NS name). */ static void -check_stale_cache(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, - isc_stdtime_t now, isc_boolean_t tree_locked) +overmem_purge(dns_rbtdb_t *rbtdb, unsigned int locknum_start, + isc_stdtime_t now, isc_boolean_t tree_locked) { - rdatasetheader_t *victim; - isc_boolean_t overmem = rbtdb->overmem; - int victims = 0; - - /* - * Check for TTL-based expiry. - */ - victim = isc_heap_element(rbtdb->heaps[rbtnode->locknum], 1); - if (victim != NULL && victim->rdh_ttl <= now - RBTDB_VIRTUAL) { - INSIST(victim->node->locknum == rbtnode->locknum); - victims++; - - set_ttl(rbtdb, victim, 0); - victim->attributes |= RDATASET_ATTR_STALE; - victim->node->dirty = 1; + rdatasetheader_t *header, *header_prev; + unsigned int locknum; + int purgecount = 2; + + for (locknum = (locknum_start + 1) % rbtdb->node_lock_count; + locknum != locknum_start && purgecount > 0; + locknum = (locknum + 1) % rbtdb->node_lock_count) { + NODE_LOCK(&rbtdb->node_locks[locknum].lock, + isc_rwlocktype_write); + + header = isc_heap_element(rbtdb->heaps[locknum], 1); + if (header && header->rdh_ttl <= now - RBTDB_VIRTUAL) { + expire_header(rbtdb, header, tree_locked); + purgecount--; + } - if (dns_rbtnode_refcurrent(victim->node) == 0) { - INSIST(rbtnode != victim->node); - /* - * If no one else is using the node, we can - * clean it up now. We first need to gain - * a new reference to the node to meet a - * requirement of decrement_reference(). - */ - new_reference(rbtdb, victim->node); - decrement_reference(rbtdb, victim->node, 0, - isc_rwlocktype_write, - tree_locked ? isc_rwlocktype_write : - isc_rwlocktype_none); + for (header = ISC_LIST_TAIL(rbtdb->rdatasets[locknum]); + header != NULL && purgecount > 0; + header = header_prev) { + header_prev = ISC_LIST_PREV(header, lru_link); + expire_header(rbtdb, header, tree_locked); + purgecount--; } + + NODE_UNLOCK(&rbtdb->node_locks[locknum].lock, + isc_rwlocktype_write); } +} + +static void +expire_header(dns_rbtdb_t *rbtdb, rdatasetheader_t *header, + isc_boolean_t tree_locked) +{ + set_ttl(rbtdb, header, 0); + header->attributes |= RDATASET_ATTR_STALE; + header->node->dirty = 1; /* - * If we are over memory, delete the end entry from the LRU. + * Caller must hold the node (write) lock. */ - victim = ISC_LIST_TAIL(rbtdb->rdatasets[rbtnode->locknum]); - if (victim != NULL && overmem) { - INSIST(victim->node->locknum == rbtnode->locknum); - victims++; - - set_ttl(rbtdb, victim, 0); - victim->attributes |= RDATASET_ATTR_STALE; - victim->node->dirty = 1; - if (dns_rbtnode_refcurrent(victim->node) == 0) { - INSIST(rbtnode != victim->node); - /* - * If no one else is using the node, we can - * clean it up now. We first need to gain - * a new reference to the node to meet a - * requirement of decrement_reference(). - */ - new_reference(rbtdb, victim->node); - decrement_reference(rbtdb, victim->node, 0, - isc_rwlocktype_write, - tree_locked ? isc_rwlocktype_write : - isc_rwlocktype_none); - } + if (dns_rbtnode_refcurrent(header->node) == 0) { + /* + * If no one else is using the node, we can clean it up now. + * We first need to gain a new reference to the node to meet a + * requirement of decrement_reference(). + */ + new_reference(rbtdb, header->node); + decrement_reference(rbtdb, header->node, 0, + isc_rwlocktype_write, + tree_locked ? isc_rwlocktype_write : + isc_rwlocktype_none); } } diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index e195ac17..5c9f348a 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: resolver.c,v 1.381 2008/09/24 02:46:22 marka Exp $ */ +/* $Id: resolver.c,v 1.382 2008/10/17 21:58:09 jinmei Exp $ */ /*! \file */ @@ -394,6 +394,8 @@ static isc_result_t ncache_adderesult(dns_message_t *message, isc_result_t *eresultp); static void validated(isc_task_t *task, isc_event_t *event); static void maybe_destroy(fetchctx_t *fctx); +static void add_bad(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, + isc_result_t reason); /*% * Increment resolver-related statistics counters. @@ -978,6 +980,7 @@ process_sendevent(resquery_t *query, isc_event_t *event) { /* * No route to remote. */ + add_bad(fctx, query->addrinfo, sevent->result); fctx_cancelquery(&query, NULL, NULL, ISC_TRUE); retry = ISC_TRUE; break; diff --git a/lib/dns/zone.c b/lib/dns/zone.c index fcf1d8e8..9c53ab4a 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: zone.c,v 1.482 2008/09/24 03:16:58 tbox Exp $ */ +/* $Id: zone.c,v 1.483 2008/10/24 00:28:00 marka Exp $ */ /*! \file */ @@ -1883,14 +1883,16 @@ zone_check_glue(dns_zone_t *zone, dns_db_t *db, dns_name_t *name, if (result == DNS_R_NXRRSET || result == DNS_R_NXDOMAIN || result == DNS_R_EMPTYNAME || result == DNS_R_DELEGATION) { const char *what; - if (dns_name_issubdomain(name, owner)) + isc_boolean_t required = ISC_FALSE; + if (dns_name_issubdomain(name, owner)) { what = "REQUIRED GLUE "; - else if (result == DNS_R_DELEGATION) + required = ISC_TRUE; + } else if (result == DNS_R_DELEGATION) what = "SIBLING GLUE "; else what = ""; - if (result != DNS_R_DELEGATION || + if (result != DNS_R_DELEGATION || required || DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKSIBLING)) { dns_zone_log(zone, level, "%s/NS '%s' has no %s" "address records (A or AAAA)", |