Eliminate the "prev" pointer in the rdataset header.
rollback_node() incorrectly required that node->references == 0. We cannot assume that a node has no references when an update is rolled back. We now mark any rolled back rdatasets with the RDATASET_ATTR_IGNORE attribute. When the node eventually has a zero reference count, IGNOREd rdatasets will be cleaned up. In the meantime, they will be ignored.
This commit is contained in:
+98
-73
@@ -76,19 +76,17 @@ typedef struct rdatasetheader {
|
||||
isc_uint16_t attributes;
|
||||
/*
|
||||
* We don't use the LIST macros, because the LIST structure has
|
||||
* both head and tail pointers. We only have a head pointer in
|
||||
* the node to save space.
|
||||
*
|
||||
* XXXRTH we could probably do away with 'prev' with a little effort.
|
||||
* The effort would be worth it for the memory savings.
|
||||
* both head and tail pointers, and is doubly linked.
|
||||
*/
|
||||
struct rdatasetheader *prev;
|
||||
struct rdatasetheader *next;
|
||||
struct rdatasetheader *down;
|
||||
} rdatasetheader_t;
|
||||
|
||||
#define RDATASET_ATTR_NONEXISTENT 0x01
|
||||
#define RDATASET_ATTR_STALE 0x02
|
||||
#define RDATASET_ATTR_IGNORE 0x04
|
||||
|
||||
#define IGNORE(header) (((header)->attributes & RDATASET_ATTR_IGNORE) != 0)
|
||||
|
||||
#define DEFAULT_NODE_LOCK_COUNT 7 /* Should be prime. */
|
||||
|
||||
@@ -439,49 +437,40 @@ free_rdataset(isc_mem_t *mctx, rdatasetheader_t *rdataset) {
|
||||
}
|
||||
|
||||
static inline void
|
||||
rollback_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, rbtdb_serial_t serial) {
|
||||
rdatasetheader_t *header, *header_next;
|
||||
rollback_node(dns_rbtnode_t *node, rbtdb_serial_t serial) {
|
||||
rdatasetheader_t *header, *dcurrent;
|
||||
|
||||
/*
|
||||
* Caller must hold the node lock.
|
||||
*/
|
||||
|
||||
REQUIRE(node->references == 0);
|
||||
|
||||
for (header = node->data; header != NULL; header = header_next) {
|
||||
header_next = header->next;
|
||||
if (header->serial == serial) {
|
||||
if (header->down != NULL) {
|
||||
header->down->prev = header->prev;
|
||||
if (header->prev != NULL)
|
||||
header->prev->next = header->down;
|
||||
else
|
||||
node->data = header->down;
|
||||
header->down->next = header->next;
|
||||
if (header->next != NULL)
|
||||
header->next->prev = header->down;
|
||||
} else {
|
||||
if (header->prev != NULL)
|
||||
header->prev->next = header->next;
|
||||
else
|
||||
node->data = header->next;
|
||||
if (header->next != NULL)
|
||||
header->next->prev = header->prev;
|
||||
}
|
||||
free_rdataset(rbtdb->common.mctx, header);
|
||||
/*
|
||||
* We set the IGNORE attribute on rdatasets with serial number
|
||||
* 'serial'. When the reference count goes to zero, these rdatasets
|
||||
* will be cleaned up; until that time, they will be ignored.
|
||||
*/
|
||||
for (header = node->data; header != NULL; header = header->next) {
|
||||
if (header->serial == serial)
|
||||
header->attributes |= RDATASET_ATTR_IGNORE;
|
||||
for (dcurrent = header->down;
|
||||
dcurrent != NULL;
|
||||
dcurrent = dcurrent->down) {
|
||||
if (dcurrent->serial == serial)
|
||||
dcurrent->attributes |= RDATASET_ATTR_IGNORE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
clean_cache_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node) {
|
||||
rdatasetheader_t *current, *dcurrent, *top_next, *down_next;
|
||||
rdatasetheader_t *current, *dcurrent, *top_prev, *top_next, *down_next;
|
||||
isc_mem_t *mctx = rbtdb->common.mctx;
|
||||
|
||||
/*
|
||||
* Caller must be holding the node lock.
|
||||
*/
|
||||
|
||||
top_prev = NULL;
|
||||
for (current = node->data; current != NULL; current = top_next) {
|
||||
top_next = current->next;
|
||||
dcurrent = current->down;
|
||||
@@ -498,14 +487,13 @@ clean_cache_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node) {
|
||||
*/
|
||||
if ((current->attributes &
|
||||
(RDATASET_ATTR_NONEXISTENT|RDATASET_ATTR_STALE)) != 0) {
|
||||
if (current->prev != NULL)
|
||||
current->prev->next = current->next;
|
||||
if (top_prev != NULL)
|
||||
top_prev->next = current->next;
|
||||
else
|
||||
node->data = current->next;
|
||||
if (current->next != NULL)
|
||||
current->next->prev = current->prev;
|
||||
free_rdataset(mctx, current);
|
||||
}
|
||||
} else
|
||||
top_prev = current;
|
||||
}
|
||||
node->dirty = 0;
|
||||
}
|
||||
@@ -514,7 +502,8 @@ static inline void
|
||||
clean_zone_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
|
||||
rbtdb_serial_t least_serial)
|
||||
{
|
||||
rdatasetheader_t *current, *dcurrent, *top_next, *down_next, *dparent;
|
||||
rdatasetheader_t *current, *dcurrent, *down_next, *dparent;
|
||||
rdatasetheader_t *top_prev, *top_next;
|
||||
isc_mem_t *mctx = rbtdb->common.mctx;
|
||||
isc_boolean_t still_dirty = ISC_FALSE;
|
||||
|
||||
@@ -523,12 +512,15 @@ clean_zone_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
|
||||
*/
|
||||
REQUIRE(least_serial != 0);
|
||||
|
||||
top_prev = NULL;
|
||||
for (current = node->data; current != NULL; current = top_next) {
|
||||
top_next = current->next;
|
||||
|
||||
/*
|
||||
* Find the first rdataset less than the least serial, if
|
||||
* any. On the way down, clean up any instances of multiple
|
||||
* rdatasets with the same serial number.
|
||||
* rdatasets with the same serial number, or that have the
|
||||
* IGNORE attribute.
|
||||
*/
|
||||
dparent = current;
|
||||
for (dcurrent = current->down;
|
||||
@@ -538,14 +530,14 @@ clean_zone_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
|
||||
INSIST(dcurrent->serial <= dparent->serial);
|
||||
if (dcurrent->serial < least_serial)
|
||||
break;
|
||||
if (dcurrent->serial == dparent->serial) {
|
||||
if (dcurrent->down != NULL)
|
||||
dcurrent->down->prev = dparent;
|
||||
if (dcurrent->serial == dparent->serial ||
|
||||
IGNORE(dcurrent)) {
|
||||
dparent->down = dcurrent->down;
|
||||
free_rdataset(mctx, dcurrent);
|
||||
} else
|
||||
dparent = dcurrent;
|
||||
}
|
||||
|
||||
/*
|
||||
* If there is a such an rdataset, delete it and any older
|
||||
* versions.
|
||||
@@ -559,28 +551,61 @@ clean_zone_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
|
||||
} while (dcurrent != NULL);
|
||||
dparent->down = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* We've eliminated all IGNORE datasets with the possible
|
||||
* exception of current, which we now check.
|
||||
*/
|
||||
if (IGNORE(current)) {
|
||||
down_next = current->down;
|
||||
if (down_next == NULL) {
|
||||
if (top_prev != NULL)
|
||||
top_prev->next = current->next;
|
||||
else
|
||||
node->data = current->next;
|
||||
free_rdataset(mctx, current);
|
||||
/*
|
||||
* current no longer exists, so we can
|
||||
* just continue with the loop.
|
||||
*/
|
||||
continue;
|
||||
} else {
|
||||
/*
|
||||
* Pull up current->down, making it the new
|
||||
* current.
|
||||
*/
|
||||
if (top_prev != NULL)
|
||||
top_prev->next = down_next;
|
||||
else
|
||||
node->data = down_next;
|
||||
down_next->next = top_next;
|
||||
free_rdataset(mctx, current);
|
||||
current = down_next;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Note. The serial number of 'current' might be less than
|
||||
* least_serial too, but we cannot delete it because it is
|
||||
* the most recent version, unless it is a NONEXISTENT
|
||||
* rdataset.
|
||||
* rdataset or is IGNOREd.
|
||||
*/
|
||||
if (current->down != NULL)
|
||||
if (current->down != NULL) {
|
||||
still_dirty = ISC_TRUE;
|
||||
else {
|
||||
top_prev = current;
|
||||
} else {
|
||||
/*
|
||||
* If this is a NONEXISTENT rdataset, we can delete it.
|
||||
*/
|
||||
if ((current->attributes & RDATASET_ATTR_NONEXISTENT)
|
||||
!= 0) {
|
||||
if (current->prev != NULL)
|
||||
current->prev->next = current->next;
|
||||
if (top_prev != NULL)
|
||||
top_prev->next = current->next;
|
||||
else
|
||||
node->data = current->next;
|
||||
if (current->next != NULL)
|
||||
current->next->prev = current->prev;
|
||||
free_rdataset(mctx, current);
|
||||
}
|
||||
} else
|
||||
top_prev = current;
|
||||
}
|
||||
}
|
||||
if (!still_dirty)
|
||||
@@ -745,7 +770,6 @@ closeversion(dns_db_t *db, dns_dbversion_t **versionp, isc_boolean_t commit) {
|
||||
cleanup_version = version;
|
||||
rbtdb->future_version = NULL;
|
||||
}
|
||||
/* XXX wake up waiting updates */
|
||||
} else {
|
||||
if (version != rbtdb->current_version) {
|
||||
/*
|
||||
@@ -805,7 +829,7 @@ closeversion(dns_db_t *db, dns_dbversion_t **versionp, isc_boolean_t commit) {
|
||||
INSIST(rbtnode->references > 0);
|
||||
rbtnode->references--;
|
||||
if (rollback)
|
||||
rollback_node(rbtdb, rbtnode, serial);
|
||||
rollback_node(rbtnode, serial);
|
||||
|
||||
if (rbtnode->references == 0)
|
||||
no_references(rbtdb, rbtnode, least_serial);
|
||||
@@ -904,7 +928,8 @@ zone_zonecut_callback(dns_rbtnode_t *node, dns_name_t *name, void *arg) {
|
||||
if (header->type == dns_rdatatype_ns ||
|
||||
header->type == dns_rdatatype_dname) {
|
||||
do {
|
||||
if (header->serial <= search->serial) {
|
||||
if (header->serial <= search->serial &&
|
||||
!IGNORE(header)) {
|
||||
/*
|
||||
* Is this a "this rdataset doesn't
|
||||
* exist" record?
|
||||
@@ -1258,7 +1283,8 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
|
||||
* Look for an active, extant rdataset.
|
||||
*/
|
||||
do {
|
||||
if (header->serial <= search.serial) {
|
||||
if (header->serial <= search.serial &&
|
||||
!IGNORE(header)) {
|
||||
/*
|
||||
* Is this a "this rdataset doesn't
|
||||
* exist" record?
|
||||
@@ -1609,7 +1635,8 @@ cache_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
|
||||
rbtdb_search_t search;
|
||||
isc_boolean_t cname_ok = ISC_TRUE;
|
||||
isc_boolean_t empty_node;
|
||||
rdatasetheader_t *header, *header_next, *found, *nsheader, *nxtheader;
|
||||
rdatasetheader_t *header, *header_prev, *header_next;
|
||||
rdatasetheader_t *found, *nsheader, *nxtheader;
|
||||
|
||||
/*
|
||||
* XXXRTH Currently this code has no support for negative caching.
|
||||
@@ -1687,6 +1714,7 @@ cache_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
|
||||
nsheader = NULL;
|
||||
nxtheader = NULL;
|
||||
empty_node = ISC_TRUE;
|
||||
header_prev = NULL;
|
||||
for (header = node->data; header != NULL; header = header_next) {
|
||||
header_next = header->next;
|
||||
if (header->ttl <= now) {
|
||||
@@ -1698,17 +1726,16 @@ cache_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
|
||||
*/
|
||||
if (node->references == 0) {
|
||||
INSIST(header->down == NULL);
|
||||
if (header->prev != NULL)
|
||||
header->prev->next = header->next;
|
||||
if (header_prev != NULL)
|
||||
header_prev->next = header->next;
|
||||
else
|
||||
node->data = header->next;
|
||||
if (header->next != NULL)
|
||||
header->next->prev = header->prev;
|
||||
free_rdataset(search.rbtdb->common.mctx,
|
||||
header);
|
||||
} else {
|
||||
header->attributes |= RDATASET_ATTR_STALE;
|
||||
node->dirty = 1;
|
||||
header_prev = header;
|
||||
}
|
||||
} else if ((header->attributes & RDATASET_ATTR_NONEXISTENT)
|
||||
== 0) {
|
||||
@@ -1741,7 +1768,9 @@ cache_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
|
||||
*/
|
||||
nxtheader = header;
|
||||
}
|
||||
}
|
||||
header_prev = header;
|
||||
} else
|
||||
header_prev = header;
|
||||
}
|
||||
|
||||
if (empty_node) {
|
||||
@@ -1945,7 +1974,8 @@ zone_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
|
||||
for (header = rbtnode->data; header != NULL; header = header->next) {
|
||||
if (header->type == type) {
|
||||
do {
|
||||
if (header->serial <= serial) {
|
||||
if (header->serial <= serial &&
|
||||
!IGNORE(header)) {
|
||||
/*
|
||||
* Is this a "this rdataset doesn't
|
||||
* exist" record?
|
||||
@@ -2085,7 +2115,7 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion,
|
||||
rdatasetheader_t *newheader, isc_boolean_t merge, isc_boolean_t loading)
|
||||
{
|
||||
rbtdb_changed_t *changed = NULL;
|
||||
rdatasetheader_t *header;
|
||||
rdatasetheader_t *header, *header_prev;
|
||||
unsigned char *merged;
|
||||
dns_result_t result;
|
||||
|
||||
@@ -2103,9 +2133,11 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion,
|
||||
return (DNS_R_NOMEMORY);
|
||||
}
|
||||
|
||||
header_prev = NULL;
|
||||
for (header = rbtnode->data; header != NULL; header = header->next) {
|
||||
if (header->type == newheader->type)
|
||||
break;
|
||||
header_prev = header;
|
||||
}
|
||||
if (header != NULL) {
|
||||
/*
|
||||
@@ -2150,14 +2182,11 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion,
|
||||
}
|
||||
INSIST(rbtversion == NULL ||
|
||||
rbtversion->serial >= header->serial);
|
||||
newheader->prev = header->prev;
|
||||
if (header->prev != NULL)
|
||||
header->prev->next = newheader;
|
||||
if (header_prev != NULL)
|
||||
header_prev->next = newheader;
|
||||
else
|
||||
rbtnode->data = newheader;
|
||||
newheader->next = header->next;
|
||||
if (header->next != NULL)
|
||||
header->next->prev = newheader;
|
||||
if (loading) {
|
||||
/*
|
||||
* There are no other references to 'header' when
|
||||
@@ -2169,7 +2198,6 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion,
|
||||
free_rdataset(rbtdb->common.mctx, header);
|
||||
} else {
|
||||
newheader->down = header;
|
||||
header->prev = newheader;
|
||||
header->next = NULL;
|
||||
rbtnode->dirty = 1;
|
||||
if (changed != NULL)
|
||||
@@ -2179,10 +2207,7 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion,
|
||||
/*
|
||||
* The rdataset type doesn't exist at this node.
|
||||
*/
|
||||
newheader->prev = NULL;
|
||||
newheader->next = rbtnode->data;
|
||||
if (newheader->next != NULL)
|
||||
newheader->next->prev = newheader;
|
||||
newheader->down = NULL;
|
||||
rbtnode->data = newheader;
|
||||
}
|
||||
@@ -2720,7 +2745,7 @@ rdatasetiter_first(dns_rdatasetiter_t *iterator) {
|
||||
for (header = rbtnode->data; header != NULL; header = top_next) {
|
||||
top_next = header->next;
|
||||
do {
|
||||
if (header->serial <= serial) {
|
||||
if (header->serial <= serial && !IGNORE(header)) {
|
||||
/*
|
||||
* Is this a "this rdataset doesn't
|
||||
* exist" record?
|
||||
@@ -2774,7 +2799,7 @@ rdatasetiter_next(dns_rdatasetiter_t *iterator) {
|
||||
for (header = header->next; header != NULL; header = top_next) {
|
||||
top_next = header->next;
|
||||
do {
|
||||
if (header->serial <= serial) {
|
||||
if (header->serial <= serial && !IGNORE(header)) {
|
||||
/*
|
||||
* Is this a "this rdataset doesn't
|
||||
* exist" record?
|
||||
|
||||
Reference in New Issue
Block a user