Rewrite the GLUE cache in QP zone database

This is a second attempt to rewrite the GLUE cache to not use per
database version hash table.  Instead of keeping a hash table indexed by
the node, use a directly linked list of GLUE records for each
slabheader.  This was attempted before, but there was a data race caused
by the fact that the thread cleaning the GLUE records could be slower
than accessing the slab headers again and reinitializing the wait-free
stack.

The improved design builds on the previous design, but adds a new
dns_gluelist structure that has a pointer to the database version.

If a dns_gluelist belonging to a different (old) version is detected, it
is just detached from the slabheader and left for the closeversion() to
clean it up later.

(cherry picked from commit 29bde687b5)
This commit is contained in:
Ondřej Surý 2024-12-05 13:45:24 +01:00
parent ad952ffee6
commit 547f376f21
No known key found for this signature in database
GPG key ID: 2820F37E873DEA41
8 changed files with 339 additions and 463 deletions

View file

@ -41,6 +41,8 @@
#include <dns/rdataclass.h>
#include <dns/rdataset.h>
#include <dns/rdatasetiter.h>
#include <dns/rdataslab.h>
#include <dns/stats.h>
/***
*** Private Types
@ -1214,3 +1216,260 @@ dns__db_logtoomanyrecords(dns_db_t *db, const dns_name_t *name,
(db->attributes & DNS_DBATTR_CACHE) != 0 ? "cache" : "zone",
isc_result_totext(DNS_R_TOOMANYRECORDS), limit);
}
void
dns__db_free_glue(isc_mem_t *mctx, dns_glue_t *glue) {
while (glue != NULL) {
dns_glue_t *next = glue->next;
if (dns_rdataset_isassociated(&glue->rdataset_a)) {
dns_rdataset_disassociate(&glue->rdataset_a);
}
if (dns_rdataset_isassociated(&glue->sigrdataset_a)) {
dns_rdataset_disassociate(&glue->sigrdataset_a);
}
if (dns_rdataset_isassociated(&glue->rdataset_aaaa)) {
dns_rdataset_disassociate(&glue->rdataset_aaaa);
}
if (dns_rdataset_isassociated(&glue->sigrdataset_aaaa)) {
dns_rdataset_disassociate(&glue->sigrdataset_aaaa);
}
dns_rdataset_invalidate(&glue->rdataset_a);
dns_rdataset_invalidate(&glue->sigrdataset_a);
dns_rdataset_invalidate(&glue->rdataset_aaaa);
dns_rdataset_invalidate(&glue->sigrdataset_aaaa);
dns_name_free(&glue->name, mctx);
isc_mem_put(mctx, glue, sizeof(*glue));
glue = next;
}
}
void
dns__db_destroy_gluelist(dns_gluelist_t **gluelistp) {
REQUIRE(gluelistp != NULL);
if (*gluelistp == NULL) {
return;
}
dns_gluelist_t *gluelist = *gluelistp;
dns__db_free_glue(gluelist->mctx, gluelist->glue);
isc_mem_putanddetach(&gluelist->mctx, gluelist, sizeof(*gluelist));
}
void
dns__db_free_gluelist_rcu(struct rcu_head *rcu_head) {
dns_gluelist_t *gluelist = caa_container_of(rcu_head, dns_gluelist_t,
rcu_head);
dns__db_destroy_gluelist(&gluelist);
}
void
dns__db_cleanup_gluelists(struct cds_wfs_stack *glue_stack) {
struct cds_wfs_head *head = __cds_wfs_pop_all(glue_stack);
struct cds_wfs_node *node = NULL, *next = NULL;
rcu_read_lock();
cds_wfs_for_each_blocking_safe(head, node, next) {
dns_gluelist_t *gluelist =
caa_container_of(node, dns_gluelist_t, wfs_node);
dns_slabheader_t *header = rcu_xchg_pointer(&gluelist->header,
NULL);
(void)rcu_cmpxchg_pointer(&header->gluelist, gluelist, NULL);
call_rcu(&gluelist->rcu_head, dns__db_free_gluelist_rcu);
}
rcu_read_unlock();
}
#define IS_REQUIRED_GLUE(r) (((r)->attributes & DNS_RDATASETATTR_REQUIRED) != 0)
static void
addglue_to_message(dns_glue_t *ge, dns_message_t *msg) {
for (; ge != NULL; ge = ge->next) {
dns_name_t *name = NULL;
dns_rdataset_t *rdataset_a = NULL;
dns_rdataset_t *sigrdataset_a = NULL;
dns_rdataset_t *rdataset_aaaa = NULL;
dns_rdataset_t *sigrdataset_aaaa = NULL;
bool prepend_name = false;
dns_message_gettempname(msg, &name);
dns_name_copy(&ge->name, name);
if (dns_rdataset_isassociated(&ge->rdataset_a)) {
dns_message_gettemprdataset(msg, &rdataset_a);
}
if (dns_rdataset_isassociated(&ge->sigrdataset_a)) {
dns_message_gettemprdataset(msg, &sigrdataset_a);
}
if (dns_rdataset_isassociated(&ge->rdataset_aaaa)) {
dns_message_gettemprdataset(msg, &rdataset_aaaa);
}
if (dns_rdataset_isassociated(&ge->sigrdataset_aaaa)) {
dns_message_gettemprdataset(msg, &sigrdataset_aaaa);
}
if (rdataset_a != NULL) {
dns_rdataset_clone(&ge->rdataset_a, rdataset_a);
ISC_LIST_APPEND(name->list, rdataset_a, link);
if (IS_REQUIRED_GLUE(rdataset_a)) {
prepend_name = true;
}
}
if (sigrdataset_a != NULL) {
dns_rdataset_clone(&ge->sigrdataset_a, sigrdataset_a);
ISC_LIST_APPEND(name->list, sigrdataset_a, link);
}
if (rdataset_aaaa != NULL) {
dns_rdataset_clone(&ge->rdataset_aaaa, rdataset_aaaa);
ISC_LIST_APPEND(name->list, rdataset_aaaa, link);
if (IS_REQUIRED_GLUE(rdataset_aaaa)) {
prepend_name = true;
}
}
if (sigrdataset_aaaa != NULL) {
dns_rdataset_clone(&ge->sigrdataset_aaaa,
sigrdataset_aaaa);
ISC_LIST_APPEND(name->list, sigrdataset_aaaa, link);
}
dns_message_addname(msg, name, DNS_SECTION_ADDITIONAL);
/*
* When looking for required glue, dns_message_rendersection()
* only processes the first rdataset associated with the first
* name added to the ADDITIONAL section. dns_message_addname()
* performs an append on the list of names in a given section,
* so if any glue record was marked as required, we need to
* move the name it is associated with to the beginning of the
* list for the ADDITIONAL section or else required glue might
* not be rendered.
*/
if (prepend_name) {
ISC_LIST_UNLINK(msg->sections[DNS_SECTION_ADDITIONAL],
name, link);
ISC_LIST_PREPEND(msg->sections[DNS_SECTION_ADDITIONAL],
name, link);
}
}
}
static dns_gluelist_t *
new_gluelist(dns_db_t *db, dns_slabheader_t *header,
const dns_dbversion_t *dbversion) {
dns_gluelist_t *gluelist = isc_mem_get(db->mctx, sizeof(*gluelist));
*gluelist = (dns_gluelist_t){
.version = dbversion,
.header = header,
};
isc_mem_attach(db->mctx, &gluelist->mctx);
cds_wfs_node_init(&gluelist->wfs_node);
return gluelist;
}
static dns_gluelist_t *
create_gluelist(dns_db_t *db, dns_dbversion_t *dbversion, dns_dbnode_t *dbnode,
dns_rdataset_t *rdataset, dns_additionaldatafunc_t add) {
dns_slabheader_t *header = dns_slabheader_fromrdataset(rdataset);
dns_glue_additionaldata_ctx_t ctx = {
.db = db,
.version = dbversion,
.node = dbnode,
};
dns_gluelist_t *gluelist = new_gluelist(ctx.db, header, ctx.version);
/*
* Get the owner name of the NS RRset - it will be necessary for
* identifying required glue in glue_nsdname_cb() (by
* determining which NS records in the delegation are
* in-bailiwick).
*/
(void)dns_rdataset_additionaldata(rdataset, dns_rootname, add, &ctx);
gluelist->glue = ctx.glue;
return gluelist;
}
isc_result_t
dns__db_addglue(dns_db_t *db, dns_dbversion_t *dbversion,
dns_rdataset_t *rdataset, dns_message_t *msg,
dns_additionaldatafunc_t add,
struct cds_wfs_stack *glue_stack) {
dns_dbnode_t *dbnode = (dns_dbnode_t *)rdataset->slab.node;
dns_slabheader_t *header = dns_slabheader_fromrdataset(rdataset);
dns_glue_t *glue = NULL;
isc_result_t result = ISC_R_SUCCESS;
REQUIRE(rdataset->type == dns_rdatatype_ns);
rcu_read_lock();
dns_gluelist_t *gluelist = rcu_dereference(header->gluelist);
if (gluelist == NULL || gluelist->version != dbversion) {
/* No or old glue list was found in the table. */
dns_gluelist_t *xchg_gluelist = gluelist;
dns_gluelist_t *old_gluelist = (void *)-1;
dns_gluelist_t *new_gluelist =
create_gluelist(db, dbversion, dbnode, rdataset, add);
while (old_gluelist != xchg_gluelist &&
(xchg_gluelist == NULL ||
xchg_gluelist->version != dbversion))
{
old_gluelist = xchg_gluelist;
xchg_gluelist = rcu_cmpxchg_pointer(
&header->gluelist, old_gluelist, new_gluelist);
}
if (old_gluelist == xchg_gluelist) {
/* CAS was successful */
cds_wfs_push(glue_stack, &new_gluelist->wfs_node);
gluelist = new_gluelist;
} else {
dns__db_destroy_gluelist(&new_gluelist);
gluelist = xchg_gluelist;
}
}
glue = gluelist->glue;
if (glue != NULL) {
addglue_to_message(glue, msg);
result = ISC_R_NOTFOUND;
}
rcu_read_unlock();
return result;
}
dns_glue_t *
dns__db_new_glue(isc_mem_t *mctx, const dns_name_t *name) {
dns_glue_t *glue = isc_mem_get(mctx, sizeof(*glue));
*glue = (dns_glue_t){
.name = DNS_NAME_INITEMPTY,
};
dns_name_dup(name, mctx, &glue->name);
return glue;
}

View file

@ -120,21 +120,31 @@ ISC_LANG_BEGINDECLS
struct dns_glue {
struct dns_glue *next;
dns_fixedname_t fixedname;
dns_name_t name;
dns_rdataset_t rdataset_a;
dns_rdataset_t sigrdataset_a;
dns_rdataset_t rdataset_aaaa;
dns_rdataset_t sigrdataset_aaaa;
isc_mem_t *mctx;
struct rcu_head rcu_head;
};
typedef struct {
dns_glue_t *glue_list;
struct dns_gluelist {
isc_mem_t *mctx;
const dns_dbversion_t *version;
dns_slabheader_t *header;
struct dns_glue *glue;
struct rcu_head rcu_head;
struct cds_wfs_node wfs_node;
};
typedef struct dns_glue_additionaldata_ctx {
dns_db_t *db;
dns_dbversion_t *version;
dns_name_t *nodename;
dns_dbnode_t *node;
dns_glue_t *glue;
} dns_glue_additionaldata_ctx_t;
typedef struct {
@ -196,4 +206,20 @@ dns__db_logtoomanyrecords(dns_db_t *db, const dns_name_t *name,
* the addition is to create a new rdataset or to merge to an existing one.
*/
void
dns__db_free_glue(isc_mem_t *mctx, dns_glue_t *glue);
void
dns__db_destroy_gluelist(dns_gluelist_t **gluelistp);
void
dns__db_free_gluelist_rcu(struct rcu_head *rcu_head);
void
dns__db_cleanup_gluelists(struct cds_wfs_stack *glue_stack);
isc_result_t
dns__db_addglue(dns_db_t *db, dns_dbversion_t *dbversion,
dns_rdataset_t *rdataset, dns_message_t *msg,
dns_additionaldatafunc_t add, struct cds_wfs_stack *glue_stack);
dns_glue_t *
dns__db_new_glue(isc_mem_t *mctx, const dns_name_t *name);
ISC_LANG_ENDDECLS

View file

@ -132,9 +132,9 @@ struct dns_slabheader {
*/
unsigned char upper[32];
isc_heap_t *heap;
dns_glue_t *glue_list;
struct cds_wfs_node wfs_node;
isc_heap_t *heap;
dns_gluelist_t *gluelist;
};
enum {

View file

@ -94,6 +94,7 @@ typedef struct dns_forwarder dns_forwarder_t;
typedef struct dns_fwdtable dns_fwdtable_t;
typedef struct dns_geoip_databases dns_geoip_databases_t;
typedef struct dns_glue dns_glue_t;
typedef struct dns_gluelist dns_gluelist_t;
typedef struct dns_iptable dns_iptable_t;
typedef uint32_t dns_iterations_t;
typedef struct dns_kasp dns_kasp_t;

View file

@ -369,64 +369,6 @@ set_index(void *what, unsigned int idx) {
h->heap_index = idx;
}
static void
freeglue(dns_glue_t *glue_list) {
if (glue_list == (void *)-1) {
return;
}
dns_glue_t *glue = glue_list;
while (glue != NULL) {
dns_glue_t *next = glue->next;
if (dns_rdataset_isassociated(&glue->rdataset_a)) {
dns_rdataset_disassociate(&glue->rdataset_a);
}
if (dns_rdataset_isassociated(&glue->sigrdataset_a)) {
dns_rdataset_disassociate(&glue->sigrdataset_a);
}
if (dns_rdataset_isassociated(&glue->rdataset_aaaa)) {
dns_rdataset_disassociate(&glue->rdataset_aaaa);
}
if (dns_rdataset_isassociated(&glue->sigrdataset_aaaa)) {
dns_rdataset_disassociate(&glue->sigrdataset_aaaa);
}
dns_rdataset_invalidate(&glue->rdataset_a);
dns_rdataset_invalidate(&glue->sigrdataset_a);
dns_rdataset_invalidate(&glue->rdataset_aaaa);
dns_rdataset_invalidate(&glue->sigrdataset_aaaa);
isc_mem_putanddetach(&glue->mctx, glue, sizeof(*glue));
glue = next;
}
}
static void
free_gluelist_rcu(struct rcu_head *rcu_head) {
dns_glue_t *glue = caa_container_of(rcu_head, dns_glue_t, rcu_head);
freeglue(glue);
}
static void
free_gluetable(struct cds_wfs_stack *glue_stack) {
struct cds_wfs_head *head = __cds_wfs_pop_all(glue_stack);
struct cds_wfs_node *node = NULL, *next = NULL;
rcu_read_lock();
cds_wfs_for_each_blocking_safe(head, node, next) {
dns_slabheader_t *header =
caa_container_of(node, dns_slabheader_t, wfs_node);
dns_glue_t *glue = rcu_xchg_pointer(&header->glue_list, NULL);
call_rcu(&glue->rcu_head, free_gluelist_rcu);
}
rcu_read_unlock();
}
static void
free_db_rcu(struct rcu_head *rcu_head) {
qpzonedb_t *qpdb = caa_container_of(rcu_head, qpzonedb_t, rcu_head);
@ -513,7 +455,7 @@ qpdb_destroy(dns_db_t *arg) {
* node count below.
*/
if (qpdb->current_version != NULL) {
free_gluetable(&qpdb->current_version->glue_stack);
dns__db_cleanup_gluelists(&qpdb->current_version->glue_stack);
}
/*
@ -1475,7 +1417,7 @@ closeversion(dns_db_t *db, dns_dbversion_t **versionp,
if (cleanup_version != NULL) {
isc_refcount_destroy(&cleanup_version->references);
INSIST(EMPTY(cleanup_version->changed_list));
free_gluetable(&cleanup_version->glue_stack);
dns__db_cleanup_gluelists(&cleanup_version->glue_stack);
cds_wfs_destroy(&cleanup_version->glue_stack);
isc_rwlock_destroy(&cleanup_version->rwlock);
isc_mem_put(qpdb->common.mctx, cleanup_version,
@ -4020,10 +3962,6 @@ deletedata(dns_db_t *db ISC_ATTR_UNUSED, dns_dbnode_t *node ISC_ATTR_UNUSED,
RWUNLOCK(&qpdb->lock, isc_rwlocktype_write);
}
header->heap_index = 0;
if (header->glue_list) {
freeglue(header->glue_list);
}
}
/*
@ -4987,26 +4925,15 @@ nodefullname(dns_db_t *db, dns_dbnode_t *node, dns_name_t *name) {
return ISC_R_SUCCESS;
}
static dns_glue_t *
new_gluelist(isc_mem_t *mctx, dns_name_t *name) {
dns_glue_t *glue = isc_mem_get(mctx, sizeof(*glue));
*glue = (dns_glue_t){ 0 };
dns_name_t *gluename = dns_fixedname_initname(&glue->fixedname);
isc_mem_attach(mctx, &glue->mctx);
dns_name_copy(name, gluename);
return glue;
}
static isc_result_t
glue_nsdname_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype,
dns_rdataset_t *unused DNS__DB_FLARG) {
dns_rdataset_t *rdataset ISC_ATTR_UNUSED DNS__DB_FLARG) {
dns_glue_additionaldata_ctx_t *ctx = NULL;
isc_result_t result;
dns_fixedname_t fixedname_a;
dns_name_t *name_a = NULL;
dns_rdataset_t rdataset_a, sigrdataset_a;
const qpznode_t *node = NULL;
qpznode_t *node_a = NULL;
dns_fixedname_t fixedname_aaaa;
dns_name_t *name_aaaa = NULL;
@ -5014,8 +4941,6 @@ glue_nsdname_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype,
qpznode_t *node_aaaa = NULL;
dns_glue_t *glue = NULL;
UNUSED(unused);
/*
* NS records want addresses in additional records.
*/
@ -5023,6 +4948,8 @@ glue_nsdname_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype,
ctx = (dns_glue_additionaldata_ctx_t *)arg;
node = (qpznode_t *)ctx->node;
name_a = dns_fixedname_initname(&fixedname_a);
dns_rdataset_init(&rdataset_a);
dns_rdataset_init(&sigrdataset_a);
@ -5035,7 +4962,7 @@ glue_nsdname_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype,
DNS_DBFIND_GLUEOK, 0, (dns_dbnode_t **)&node_a, name_a,
&rdataset_a, &sigrdataset_a DNS__DB_FLARG_PASS);
if (result == DNS_R_GLUE) {
glue = new_gluelist(ctx->db->mctx, name_a);
glue = dns__db_new_glue(ctx->db->mctx, name_a);
dns_rdataset_init(&glue->rdataset_a);
dns_rdataset_init(&glue->sigrdataset_a);
@ -5055,7 +4982,7 @@ glue_nsdname_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype,
&sigrdataset_aaaa DNS__DB_FLARG_PASS);
if (result == DNS_R_GLUE) {
if (glue == NULL) {
glue = new_gluelist(ctx->db->mctx, name_aaaa);
glue = dns__db_new_glue(ctx->db->mctx, name_aaaa);
dns_rdataset_init(&glue->rdataset_a);
dns_rdataset_init(&glue->sigrdataset_a);
@ -5081,7 +5008,7 @@ glue_nsdname_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype,
* attributes for the first rdataset associated with the first name
* added to the ADDITIONAL section.
*/
if (glue != NULL && dns_name_issubdomain(name, ctx->nodename)) {
if (glue != NULL && dns_name_issubdomain(name, &node->name)) {
if (dns_rdataset_isassociated(&glue->rdataset_a)) {
glue->rdataset_a.attributes |=
DNS_RDATASETATTR_REQUIRED;
@ -5093,8 +5020,8 @@ glue_nsdname_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype,
}
if (glue != NULL) {
glue->next = ctx->glue_list;
ctx->glue_list = glue;
glue->next = ctx->glue;
ctx->glue = glue;
}
result = ISC_R_SUCCESS;
@ -5125,162 +5052,30 @@ glue_nsdname_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype,
return result;
}
#define IS_REQUIRED_GLUE(r) (((r)->attributes & DNS_RDATASETATTR_REQUIRED) != 0)
static void
addglue_to_message(dns_glue_t *ge, dns_message_t *msg) {
for (; ge != NULL; ge = ge->next) {
dns_name_t *name = NULL;
dns_rdataset_t *rdataset_a = NULL;
dns_rdataset_t *sigrdataset_a = NULL;
dns_rdataset_t *rdataset_aaaa = NULL;
dns_rdataset_t *sigrdataset_aaaa = NULL;
dns_name_t *gluename = dns_fixedname_name(&ge->fixedname);
bool prepend_name = false;
dns_message_gettempname(msg, &name);
dns_name_copy(gluename, name);
if (dns_rdataset_isassociated(&ge->rdataset_a)) {
dns_message_gettemprdataset(msg, &rdataset_a);
}
if (dns_rdataset_isassociated(&ge->sigrdataset_a)) {
dns_message_gettemprdataset(msg, &sigrdataset_a);
}
if (dns_rdataset_isassociated(&ge->rdataset_aaaa)) {
dns_message_gettemprdataset(msg, &rdataset_aaaa);
}
if (dns_rdataset_isassociated(&ge->sigrdataset_aaaa)) {
dns_message_gettemprdataset(msg, &sigrdataset_aaaa);
}
if (rdataset_a != NULL) {
dns_rdataset_clone(&ge->rdataset_a, rdataset_a);
ISC_LIST_APPEND(name->list, rdataset_a, link);
if (IS_REQUIRED_GLUE(rdataset_a)) {
prepend_name = true;
}
}
if (sigrdataset_a != NULL) {
dns_rdataset_clone(&ge->sigrdataset_a, sigrdataset_a);
ISC_LIST_APPEND(name->list, sigrdataset_a, link);
}
if (rdataset_aaaa != NULL) {
dns_rdataset_clone(&ge->rdataset_aaaa, rdataset_aaaa);
ISC_LIST_APPEND(name->list, rdataset_aaaa, link);
if (IS_REQUIRED_GLUE(rdataset_aaaa)) {
prepend_name = true;
}
}
if (sigrdataset_aaaa != NULL) {
dns_rdataset_clone(&ge->sigrdataset_aaaa,
sigrdataset_aaaa);
ISC_LIST_APPEND(name->list, sigrdataset_aaaa, link);
}
dns_message_addname(msg, name, DNS_SECTION_ADDITIONAL);
/*
* When looking for required glue, dns_message_rendersection()
* only processes the first rdataset associated with the first
* name added to the ADDITIONAL section. dns_message_addname()
* performs an append on the list of names in a given section,
* so if any glue record was marked as required, we need to
* move the name it is associated with to the beginning of the
* list for the ADDITIONAL section or else required glue might
* not be rendered.
*/
if (prepend_name) {
ISC_LIST_UNLINK(msg->sections[DNS_SECTION_ADDITIONAL],
name, link);
ISC_LIST_PREPEND(msg->sections[DNS_SECTION_ADDITIONAL],
name, link);
}
}
}
static dns_glue_t *
newglue(qpzonedb_t *qpdb, qpz_version_t *version, qpznode_t *node,
dns_rdataset_t *rdataset) {
dns_fixedname_t nodename;
dns_glue_additionaldata_ctx_t ctx = {
.db = (dns_db_t *)qpdb,
.version = (dns_dbversion_t *)version,
.nodename = dns_fixedname_initname(&nodename),
};
/*
* Get the owner name of the NS RRset - it will be necessary for
* identifying required glue in glue_nsdname_cb() (by
* determining which NS records in the delegation are
* in-bailiwick).
*/
dns_name_copy(&node->name, ctx.nodename);
(void)dns_rdataset_additionaldata(rdataset, dns_rootname,
glue_nsdname_cb, &ctx);
return ctx.glue_list;
}
static isc_result_t
addglue(dns_db_t *db, dns_dbversion_t *dbversion, dns_rdataset_t *rdataset,
dns_message_t *msg) {
qpzonedb_t *qpdb = (qpzonedb_t *)db;
qpz_version_t *version = dbversion;
qpznode_t *node = (qpznode_t *)rdataset->slab.node;
dns_slabheader_t *header = dns_slabheader_fromrdataset(rdataset);
isc_result_t result;
REQUIRE(rdataset->type == dns_rdatatype_ns);
REQUIRE(qpdb == (qpzonedb_t *)rdataset->slab.db);
REQUIRE(qpdb == version->qpdb);
REQUIRE(!IS_STUB(qpdb));
rcu_read_lock();
dns_glue_t *glue = rcu_dereference(header->glue_list);
if (glue == NULL) {
/* No cached glue was found in the table. Get new glue. */
glue = newglue(qpdb, version, node, rdataset);
/* Cache the glue or (void *)-1 if no glue was found. */
dns_glue_t *old_glue = rcu_cmpxchg_pointer(
&header->glue_list, NULL, (glue) ? glue : (void *)-1);
if (old_glue != NULL) {
/* Somebody else was faster */
freeglue(glue);
glue = old_glue;
} else if (glue != NULL) {
cds_wfs_push(&version->glue_stack, &header->wfs_node);
}
}
/* We have a cached result. Add it to the message and return. */
result = dns__db_addglue(db, dbversion, rdataset, msg, glue_nsdname_cb,
&version->glue_stack);
if (qpdb->gluecachestats != NULL) {
isc_stats_increment(
qpdb->gluecachestats,
(glue == (void *)-1)
? dns_gluecachestatscounter_hits_absent
: dns_gluecachestatscounter_hits_present);
}
isc_statscounter_t counter =
(result == ISC_R_SUCCESS)
? dns_gluecachestatscounter_hits_present
: dns_gluecachestatscounter_hits_absent;
/*
* (void *)-1 is a special value that means no glue is present in the
* zone.
*/
if (glue != (void *)-1) {
addglue_to_message(glue, msg);
isc_stats_increment(qpdb->gluecachestats, counter);
}
rcu_read_unlock();
return ISC_R_SUCCESS;
}

View file

@ -2091,18 +2091,6 @@ setgluecachestats(dns_db_t *db, isc_stats_t *stats) {
return ISC_R_SUCCESS;
}
static dns_glue_t *
new_gluelist(isc_mem_t *mctx, dns_name_t *name) {
dns_glue_t *glue = isc_mem_get(mctx, sizeof(*glue));
*glue = (dns_glue_t){ 0 };
dns_name_t *gluename = dns_fixedname_initname(&glue->fixedname);
isc_mem_attach(mctx, &glue->mctx);
dns_name_copy(name, gluename);
return glue;
}
static isc_result_t
glue_nsdname_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype,
dns_rdataset_t *unused DNS__DB_FLARG) {
@ -2117,6 +2105,8 @@ glue_nsdname_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype,
dns_rdataset_t rdataset_aaaa, sigrdataset_aaaa;
dns_rbtnode_t *node_aaaa = NULL;
dns_glue_t *glue = NULL;
dns_fixedname_t f_nodename;
dns_name_t *nodename = dns_fixedname_initname(&f_nodename);
UNUSED(unused);
@ -2127,6 +2117,8 @@ glue_nsdname_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype,
ctx = (dns_glue_additionaldata_ctx_t *)arg;
dns__rbtdb_nodefullname(ctx->db, ctx->node, nodename);
name_a = dns_fixedname_initname(&fixedname_a);
dns_rdataset_init(&rdataset_a);
dns_rdataset_init(&sigrdataset_a);
@ -2140,7 +2132,7 @@ glue_nsdname_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype,
name_a, &rdataset_a,
&sigrdataset_a DNS__DB_FLARG_PASS);
if (result == DNS_R_GLUE) {
glue = new_gluelist(ctx->db->mctx, name_a);
glue = dns__db_new_glue(ctx->db->mctx, name_a);
dns_rdataset_init(&glue->rdataset_a);
dns_rdataset_init(&glue->sigrdataset_a);
@ -2160,7 +2152,7 @@ glue_nsdname_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype,
&sigrdataset_aaaa DNS__DB_FLARG_PASS);
if (result == DNS_R_GLUE) {
if (glue == NULL) {
glue = new_gluelist(ctx->db->mctx, name_aaaa);
glue = dns__db_new_glue(ctx->db->mctx, name_aaaa);
dns_rdataset_init(&glue->rdataset_a);
dns_rdataset_init(&glue->sigrdataset_a);
@ -2186,7 +2178,10 @@ glue_nsdname_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype,
* attributes for the first rdataset associated with the first name
* added to the ADDITIONAL section.
*/
if (glue != NULL && dns_name_issubdomain(name, ctx->nodename)) {
isc_result_t dns_rbt_fullnamefromnode(dns_rbtnode_t * node,
dns_name_t * name);
if (glue != NULL && dns_name_issubdomain(name, nodename)) {
if (dns_rdataset_isassociated(&glue->rdataset_a)) {
glue->rdataset_a.attributes |=
DNS_RDATASETATTR_REQUIRED;
@ -2198,8 +2193,8 @@ glue_nsdname_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype,
}
if (glue != NULL) {
glue->next = ctx->glue_list;
ctx->glue_list = glue;
glue->next = ctx->glue;
ctx->glue = glue;
}
result = ISC_R_SUCCESS;
@ -2230,163 +2225,30 @@ glue_nsdname_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype,
return result;
}
#define IS_REQUIRED_GLUE(r) (((r)->attributes & DNS_RDATASETATTR_REQUIRED) != 0)
static void
addglue_to_message(dns_glue_t *ge, dns_message_t *msg) {
for (; ge != NULL; ge = ge->next) {
dns_name_t *name = NULL;
dns_rdataset_t *rdataset_a = NULL;
dns_rdataset_t *sigrdataset_a = NULL;
dns_rdataset_t *rdataset_aaaa = NULL;
dns_rdataset_t *sigrdataset_aaaa = NULL;
dns_name_t *gluename = dns_fixedname_name(&ge->fixedname);
bool prepend_name = false;
dns_message_gettempname(msg, &name);
dns_name_copy(gluename, name);
if (dns_rdataset_isassociated(&ge->rdataset_a)) {
dns_message_gettemprdataset(msg, &rdataset_a);
}
if (dns_rdataset_isassociated(&ge->sigrdataset_a)) {
dns_message_gettemprdataset(msg, &sigrdataset_a);
}
if (dns_rdataset_isassociated(&ge->rdataset_aaaa)) {
dns_message_gettemprdataset(msg, &rdataset_aaaa);
}
if (dns_rdataset_isassociated(&ge->sigrdataset_aaaa)) {
dns_message_gettemprdataset(msg, &sigrdataset_aaaa);
}
if (rdataset_a != NULL) {
dns_rdataset_clone(&ge->rdataset_a, rdataset_a);
ISC_LIST_APPEND(name->list, rdataset_a, link);
if (IS_REQUIRED_GLUE(rdataset_a)) {
prepend_name = true;
}
}
if (sigrdataset_a != NULL) {
dns_rdataset_clone(&ge->sigrdataset_a, sigrdataset_a);
ISC_LIST_APPEND(name->list, sigrdataset_a, link);
}
if (rdataset_aaaa != NULL) {
dns_rdataset_clone(&ge->rdataset_aaaa, rdataset_aaaa);
ISC_LIST_APPEND(name->list, rdataset_aaaa, link);
if (IS_REQUIRED_GLUE(rdataset_aaaa)) {
prepend_name = true;
}
}
if (sigrdataset_aaaa != NULL) {
dns_rdataset_clone(&ge->sigrdataset_aaaa,
sigrdataset_aaaa);
ISC_LIST_APPEND(name->list, sigrdataset_aaaa, link);
}
dns_message_addname(msg, name, DNS_SECTION_ADDITIONAL);
/*
* When looking for required glue, dns_message_rendersection()
* only processes the first rdataset associated with the first
* name added to the ADDITIONAL section. dns_message_addname()
* performs an append on the list of names in a given section,
* so if any glue record was marked as required, we need to
* move the name it is associated with to the beginning of the
* list for the ADDITIONAL section or else required glue might
* not be rendered.
*/
if (prepend_name) {
ISC_LIST_UNLINK(msg->sections[DNS_SECTION_ADDITIONAL],
name, link);
ISC_LIST_PREPEND(msg->sections[DNS_SECTION_ADDITIONAL],
name, link);
}
}
}
static dns_glue_t *
newglue(dns_rbtdb_t *rbtdb, dns_rbtdb_version_t *rbtversion,
dns_rbtnode_t *node, dns_rdataset_t *rdataset) {
dns_fixedname_t nodename;
dns_glue_additionaldata_ctx_t ctx = {
.db = (dns_db_t *)rbtdb,
.version = (dns_dbversion_t *)rbtversion,
.nodename = dns_fixedname_initname(&nodename),
};
/*
* Get the owner name of the NS RRset - it will be necessary for
* identifying required glue in glue_nsdname_cb() (by
* determining which NS records in the delegation are
* in-bailiwick).
*/
dns__rbtdb_nodefullname((dns_db_t *)rbtdb, node, ctx.nodename);
(void)dns_rdataset_additionaldata(rdataset, dns_rootname,
glue_nsdname_cb, &ctx);
return ctx.glue_list;
}
static isc_result_t
addglue(dns_db_t *db, dns_dbversion_t *version, dns_rdataset_t *rdataset,
addglue(dns_db_t *db, dns_dbversion_t *dbversion, dns_rdataset_t *rdataset,
dns_message_t *msg) {
dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
dns_rbtdb_version_t *rbtversion = version;
dns_rbtnode_t *node = (dns_rbtnode_t *)rdataset->slab.node;
dns_slabheader_t *header = dns_slabheader_fromrdataset(rdataset);
dns_rbtdb_version_t *rbtversion = dbversion;
isc_result_t result;
REQUIRE(rdataset->type == dns_rdatatype_ns);
REQUIRE(rbtdb == (dns_rbtdb_t *)rdataset->slab.db);
REQUIRE(rbtdb == rbtversion->rbtdb);
REQUIRE(!IS_CACHE(rbtdb) && !IS_STUB(rbtdb));
rcu_read_lock();
dns_glue_t *glue = rcu_dereference(header->glue_list);
if (glue == NULL) {
/* No cached glue was found in the table. Get new glue. */
glue = newglue(rbtdb, rbtversion, node, rdataset);
/* Cache the glue or (void *)-1 if no glue was found. */
dns_glue_t *old_glue = rcu_cmpxchg_pointer(
&header->glue_list, NULL, (glue) ? glue : (void *)-1);
if (old_glue != NULL) {
/* Somebody else was faster */
dns__rbtdb_freeglue(glue);
glue = old_glue;
} else if (glue != NULL) {
cds_wfs_push(&rbtversion->glue_stack,
&header->wfs_node);
}
}
/* We have a cached result. Add it to the message and return. */
result = dns__db_addglue(db, dbversion, rdataset, msg, glue_nsdname_cb,
&rbtversion->glue_stack);
if (rbtdb->gluecachestats != NULL) {
isc_stats_increment(
rbtdb->gluecachestats,
(glue == (void *)-1)
? dns_gluecachestatscounter_hits_absent
: dns_gluecachestatscounter_hits_present);
}
isc_statscounter_t counter =
(result == ISC_R_SUCCESS)
? dns_gluecachestatscounter_hits_present
: dns_gluecachestatscounter_hits_absent;
/*
* (void *)-1 is a special value that means no glue is present in the
* zone.
*/
if (glue != (void *)-1) {
addglue_to_message(glue, msg);
isc_stats_increment(rbtdb->gluecachestats, counter);
}
rcu_read_unlock();
return ISC_R_SUCCESS;
}

View file

@ -166,8 +166,6 @@ static void
delete_callback(void *data, void *arg);
static void
prune_tree(void *arg);
static void
free_gluetable(dns_rbtdb_version_t *version);
static void
rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp DNS__DB_FLARG);
@ -623,7 +621,7 @@ dns__rbtdb_destroy(dns_db_t *arg) {
* node count below.
*/
if (rbtdb->current_version != NULL) {
free_gluetable(rbtdb->current_version);
dns__db_cleanup_gluelists(&rbtdb->current_version->glue_stack);
}
/*
@ -1947,7 +1945,7 @@ dns__rbtdb_closeversion(dns_db_t *db, dns_dbversion_t **versionp,
if (cleanup_version != NULL) {
isc_refcount_destroy(&cleanup_version->references);
INSIST(EMPTY(cleanup_version->changed_list));
free_gluetable(cleanup_version);
dns__db_cleanup_gluelists(&cleanup_version->glue_stack);
cds_wfs_destroy(&cleanup_version->glue_stack);
isc_rwlock_destroy(&cleanup_version->rwlock);
isc_mem_put(rbtdb->common.mctx, cleanup_version,
@ -4873,64 +4871,6 @@ dbiterator_origin(dns_dbiterator_t *iterator, dns_name_t *name) {
return ISC_R_SUCCESS;
}
void
dns__rbtdb_freeglue(dns_glue_t *glue_list) {
if (glue_list == (void *)-1) {
return;
}
dns_glue_t *glue = glue_list;
while (glue != NULL) {
dns_glue_t *next = glue->next;
if (dns_rdataset_isassociated(&glue->rdataset_a)) {
dns_rdataset_disassociate(&glue->rdataset_a);
}
if (dns_rdataset_isassociated(&glue->sigrdataset_a)) {
dns_rdataset_disassociate(&glue->sigrdataset_a);
}
if (dns_rdataset_isassociated(&glue->rdataset_aaaa)) {
dns_rdataset_disassociate(&glue->rdataset_aaaa);
}
if (dns_rdataset_isassociated(&glue->sigrdataset_aaaa)) {
dns_rdataset_disassociate(&glue->sigrdataset_aaaa);
}
dns_rdataset_invalidate(&glue->rdataset_a);
dns_rdataset_invalidate(&glue->sigrdataset_a);
dns_rdataset_invalidate(&glue->rdataset_aaaa);
dns_rdataset_invalidate(&glue->sigrdataset_aaaa);
isc_mem_putanddetach(&glue->mctx, glue, sizeof(*glue));
glue = next;
}
}
static void
free_gluelist_rcu(struct rcu_head *rcu_head) {
dns_glue_t *glue = caa_container_of(rcu_head, dns_glue_t, rcu_head);
dns__rbtdb_freeglue(glue);
}
static void
free_gluetable(dns_rbtdb_version_t *rbtversion) {
struct cds_wfs_head *head = __cds_wfs_pop_all(&rbtversion->glue_stack);
struct cds_wfs_node *node = NULL, *next = NULL;
rcu_read_lock();
cds_wfs_for_each_blocking_safe(head, node, next) {
dns_slabheader_t *header =
caa_container_of(node, dns_slabheader_t, wfs_node);
dns_glue_t *glue = rcu_xchg_pointer(&header->glue_list, NULL);
call_rcu(&glue->rcu_head, free_gluelist_rcu);
}
rcu_read_unlock();
}
void
dns__rbtdb_deletedata(dns_db_t *db ISC_ATTR_UNUSED,
dns_dbnode_t *node ISC_ATTR_UNUSED, void *data) {
@ -4958,10 +4898,6 @@ dns__rbtdb_deletedata(dns_db_t *db ISC_ATTR_UNUSED,
if (header->closest != NULL) {
dns_slabheader_freeproof(db->mctx, &header->closest);
}
} else {
if (header->glue_list) {
dns__rbtdb_freeglue(header->glue_list);
}
}
}

View file

@ -1081,15 +1081,12 @@ dns_slabheader_reset(dns_slabheader_t *h, dns_db_t *db, dns_dbnode_t *node) {
ISC_LINK_INIT(h, link);
h->heap_index = 0;
h->heap = NULL;
h->glue_list = NULL;
h->db = db;
h->node = node;
atomic_init(&h->attributes, 0);
atomic_init(&h->last_refresh_fail_ts, 0);
cds_wfs_node_init(&h->wfs_node);
STATIC_ASSERT((sizeof(h->attributes) == 2),
"The .attributes field of dns_slabheader_t needs to be "
"16-bit int type exactly.");