summaryrefslogtreecommitdiff
path: root/lib/dns
diff options
context:
space:
mode:
authorLaMont Jones <lamont@debian.org>2009-03-20 08:23:23 -0600
committerLaMont Jones <lamont@debian.org>2009-03-20 08:23:23 -0600
commit95cab7ab13c7aedf768b3273a9a736f23239c18f (patch)
tree8c3bda91eb3defc1c5ce1f40a58a6898a2c35cf7 /lib/dns
parent08ba6b17bb4653d8401e266e9249620c9fe89404 (diff)
downloadbind9-95cab7ab13c7aedf768b3273a9a736f23239c18f.tar.gz
9.6.0b1
Diffstat (limited to 'lib/dns')
-rw-r--r--lib/dns/adb.c13
-rw-r--r--lib/dns/rbtdb.c215
-rw-r--r--lib/dns/resolver.c5
-rw-r--r--lib/dns/zone.c10
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, &region);
- 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, &region);
+ 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)",