]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Convert slabheader to use the cds_list
authorOndřej Surý <ondrej@isc.org>
Fri, 12 Sep 2025 16:08:23 +0000 (18:08 +0200)
committerOndřej Surý <ondrej@isc.org>
Tue, 23 Sep 2025 21:18:44 +0000 (23:18 +0200)
This is the second commit in series that aims to reduce the node locking
by replacing the single-linked list of slabheader(s) with CDS linked list.
This commit doesn't do anything else beyond replacing .next link with
the cds_list_head.  RCU semantics is going to be added in the subsequent
commits.

lib/dns/include/dns/rdataslab.h
lib/dns/qpcache.c
lib/dns/qpzone.c
lib/dns/rdataslab.c

index bacec3bcf70cdf5fdf23c0fcd3918eaf6b9e0b0e..36f99c74f094f25546a7ce6266b9578c6b63f25a 100644 (file)
@@ -75,9 +75,9 @@ struct dns_slabheader_proof {
 typedef struct dns_slabtop dns_slabtop_t;
 struct dns_slabtop {
        struct cds_list_head types_link;
+       struct cds_list_head headers;
 
-       dns_slabheader_t *header;
-       dns_typepair_t    typepair;
+       dns_typepair_t typepair;
 
        /*% Used for SIEVE-LRU (cache) and changed_list (zone) */
        ISC_LINK(struct dns_slabtop) link;
@@ -117,10 +117,9 @@ struct dns_slabheader {
        dns_slabtop_t *top;
 
        /*%
-        * Points to the header for the next older version of
-        * this rdataset.
+        * Link to the other versions of this rdataset.
         */
-       struct dns_slabheader *down;
+       struct cds_list_head headers_link;
 
        /*%
         * The database node objects containing this rdataset, if any.
index e90277cb33cc29d6525fa7859f30c1e7743e5112..d64ba9c88024ce024d2c1b1778d5038b0a4407f3 100644 (file)
@@ -467,7 +467,17 @@ rdataset_size(dns_slabheader_t *header) {
 
 static dns_slabheader_t *
 first_header(dns_slabtop_t *top) {
-       return top->header;
+       dns_slabheader_t *header = NULL;
+       cds_list_for_each_entry(header, &top->headers, headers_link) {
+               return header;
+       }
+       return NULL;
+}
+
+static dns_slabheader_t *
+next_header(dns_slabheader_t *header) {
+       return cds_list_entry((header)->headers_link.next, dns_slabheader_t,
+                             headers_link);
 }
 
 static dns_slabheader_t *
@@ -548,16 +558,17 @@ qpcache_hit(qpcache_t *qpdb ISC_ATTR_UNUSED, dns_slabheader_t *header) {
 
 static void
 clean_cache_headers(dns_slabtop_t *top) {
-       if (top->header == NULL) {
+       dns_slabheader_t *parent = first_header(top);
+       if (parent == NULL) {
                return;
        }
 
-       dns_slabheader_t *header = top->header, *header_down = NULL;
-       for (header = header->down; header != NULL; header = header_down) {
-               header_down = header->down;
+       dns_slabheader_t *header = next_header(parent), *header_next = NULL;
+       cds_list_for_each_entry_safe_from(header, header_next, &top->headers,
+                                         headers_link) {
+               cds_list_del(&header->headers_link);
                dns_slabheader_destroy(&header);
        }
-       top->header->down = NULL;
 }
 
 static void
@@ -573,16 +584,22 @@ clean_cache_node(qpcache_t *qpdb, qpcnode_t *node) {
                 * If current top header is nonexistent, ancient, or stale
                 * and we are not keeping stale, we can clean it up too.
                 */
-               if (!EXISTS(top->header) || ANCIENT(top->header) ||
-                   (STALE(top->header) && !KEEPSTALE(qpdb)))
+               dns_slabheader_t *header = first_header(top);
+               if (header == NULL) {
+                       continue;
+               }
+
+               if (!EXISTS(header) || ANCIENT(header) ||
+                   (STALE(header) && !KEEPSTALE(qpdb)))
                {
-                       dns_slabheader_destroy(&top->header);
+                       cds_list_del(&header->headers_link);
+                       dns_slabheader_destroy(&header);
                }
 
                /*
                 * If current slabtop is empty, we can clean it up.
                 */
-               if (top->header == NULL) {
+               if (header == NULL) {
                        cds_list_del(&top->types_link);
 
                        if (ISC_LINK_LINKED(top, link)) {
@@ -594,7 +611,7 @@ clean_cache_node(qpcache_t *qpdb, qpcnode_t *node) {
                }
        }
 
-       node->dirty = 0;
+       node->dirty = false;
 }
 
 /*
@@ -2700,12 +2717,12 @@ add(qpcache_t *qpdb, qpcnode_t *qpnode, dns_slabheader_t *newheader,
 
                if (top->typepair == newheader->typepair) {
                        INSIST(oldheader == NULL);
-                       oldheader = top->header;
+                       oldheader = first_header(top);
                }
 
                if (sigpair != dns_rdatatype_none && top->typepair == sigpair) {
                        INSIST(oldsigheader == NULL);
-                       oldsigheader = top->header;
+                       oldsigheader = first_header(top);
                }
        }
 
@@ -2849,9 +2866,9 @@ add(qpcache_t *qpdb, qpcnode_t *qpnode, dns_slabheader_t *newheader,
                        return DNS_R_UNCHANGED;
                }
 
-               oldheader->top->header = newheader;
                newheader->top = oldheader->top;
-               newheader->down = oldheader;
+               cds_list_add(&newheader->headers_link,
+                            &oldheader->top->headers);
 
                ISC_SIEVE_UNLINK(qpdb->buckets[qpnode->locknum].sieve,
                                 oldheader->top, link);
@@ -2871,13 +2888,12 @@ add(qpcache_t *qpdb, qpcnode_t *qpnode, dns_slabheader_t *newheader,
                return DNS_R_UNCHANGED;
        } else {
                /* No rdatasets of the given type exist at the node. */
-               INSIST(newheader->down == NULL);
-
                dns_slabtop_t *newtop = dns_slabtop_new(
                        ((dns_db_t *)qpdb)->mctx, newheader->typepair);
-               newtop->header = newheader;
                newheader->top = newtop;
 
+               cds_list_add(&newheader->headers_link, &newtop->headers);
+
                qpcache_miss(qpdb, newheader, &nlocktype,
                             &tlocktype DNS__DB_FLARG_PASS);
 
@@ -3828,12 +3844,13 @@ qpcnode_destroy(qpcnode_t *qpnode) {
        qpcache_t *qpdb = qpnode->qpdb;
 
        DNS_SLABTOP_FOREACH(top, qpnode->data) {
-               dns_slabheader_t *down = NULL, *down_next = NULL;
-               for (down = top->header; down != NULL; down = down_next) {
-                       down_next = down->down;
-                       dns_slabheader_destroy(&down);
+               dns_slabheader_t *header = NULL, *header_next = NULL;
+               cds_list_for_each_entry_safe(header, header_next, &top->headers,
+                                            headers_link)
+               {
+                       cds_list_del(&header->headers_link);
+                       dns_slabheader_destroy(&header);
                }
-               top->header = NULL;
 
                if (ISC_LINK_LINKED(top, link)) {
                        ISC_SIEVE_UNLINK(qpdb->buckets[qpnode->locknum].sieve,
index 567603eba793293f5e0f7967e9216461f5263a10..f6d0e30ee192d567e80d2f84c6eb0adfce812ed4 100644 (file)
@@ -805,16 +805,48 @@ qpznode_acquire(qpznode_t *node DNS__DB_FLARG) {
        qpznode_erefs_increment(node DNS__DB_FLARG_PASS);
 }
 
+static dns_slabheader_t *
+first_header(dns_slabtop_t *top) {
+       dns_slabheader_t *header = NULL;
+       cds_list_for_each_entry(header, &top->headers, headers_link) {
+               return header;
+       }
+       return NULL;
+}
+
+static dns_slabheader_t *
+next_header(dns_slabheader_t *header) {
+       return cds_list_entry((header)->headers_link.next, dns_slabheader_t,
+                             headers_link);
+}
+
+static dns_slabheader_t *
+first_existing_header(dns_slabtop_t *top, uint32_t serial) {
+       dns_slabheader_t *header = NULL;
+       cds_list_for_each_entry(header, &top->headers, headers_link) {
+               if (header->serial <= serial && !IGNORE(header)) {
+                       if (EXISTS(header)) {
+                               return header;
+                       }
+                       break;
+               }
+       }
+       return NULL;
+}
+
 static void
 clean_multiple_headers(dns_slabtop_t *top) {
-       dns_slabheader_t *parent = top->header;
-       dns_slabheader_t *header = NULL, *header_down = NULL;
+       dns_slabheader_t *parent = first_header(top);
+       if (parent == NULL) {
+               return;
+       }
 
-       for (header = parent->down; header != NULL; header = header_down) {
-               header_down = header->down;
+       dns_slabheader_t *header = next_header(parent), *header_next = NULL;
+       cds_list_for_each_entry_safe_from(header, header_next, &top->headers,
+                                         headers_link) {
                INSIST(header->serial <= parent->serial);
                if (header->serial == parent->serial || IGNORE(header)) {
-                       parent->down = header->down;
+                       cds_list_del(&header->headers_link);
                        dns_slabheader_destroy(&header);
                } else {
                        parent = header;
@@ -824,23 +856,26 @@ clean_multiple_headers(dns_slabtop_t *top) {
 
 static void
 check_top_header(dns_slabtop_t *top) {
-       dns_slabheader_t *header = top->header;
-       if (IGNORE(header)) {
-               top->header = header->down;
+       dns_slabheader_t *header = first_header(top);
+       if (header != NULL && IGNORE(header)) {
+               cds_list_del(&header->headers_link);
                dns_slabheader_destroy(&header);
        }
 }
 
 static bool
 clean_multiple_versions(dns_slabtop_t *top, uint32_t least_serial) {
-       dns_slabheader_t *parent = top->header;
-       dns_slabheader_t *header = NULL, *header_down = NULL;
-       bool multiple = false;
+       dns_slabheader_t *parent = first_header(top);
+       if (parent == NULL) {
+               return false;
+       }
 
-       for (header = parent->down; header != NULL; header = header_down) {
-               header_down = header->down;
+       bool multiple = false;
+       dns_slabheader_t *header = next_header(parent), *header_next = NULL;
+       cds_list_for_each_entry_safe_from(header, header_next, &top->headers,
+                                         headers_link) {
                if (header->serial < least_serial) {
-                       parent->down = header->down;
+                       cds_list_del(&header->headers_link);
                        dns_slabheader_destroy(&header);
                } else {
                        multiple = true;
@@ -860,8 +895,6 @@ clean_zone_node(qpznode_t *node, uint32_t least_serial) {
        REQUIRE(least_serial != 0);
 
        DNS_SLABTOP_FOREACH(top, node->data) {
-               INSIST(top->header != NULL);
-
                /*
                 * First, we clean up any instances of multiple rdatasets
                 * with the same serial number, or that have the IGNORE
@@ -875,7 +908,7 @@ clean_zone_node(qpznode_t *node, uint32_t least_serial) {
                 */
                check_top_header(top);
 
-               if (top->header == NULL) {
+               if (first_header(top) == NULL) {
                        cds_list_del(&top->types_link);
                        dns_slabtop_destroy(node->mctx, &top);
                } else {
@@ -1013,28 +1046,6 @@ bindrdataset(qpzonedb_t *qpdb, qpznode_t *node, dns_slabheader_t *header,
        }
 }
 
-static dns_slabheader_t *
-first_header(dns_slabtop_t *top, uint32_t serial) {
-       for (dns_slabheader_t *header = top->header; header != NULL;
-            header = header->down)
-       {
-               if (header->serial <= serial && !IGNORE(header)) {
-                       return header;
-               }
-       }
-
-       return NULL;
-}
-
-static dns_slabheader_t *
-first_existing_header(dns_slabtop_t *top, uint32_t serial) {
-       dns_slabheader_t *header = first_header(top, serial);
-       if (header != NULL && EXISTS(header)) {
-               return header;
-       }
-       return NULL;
-}
-
 static void
 setnsec3parameters(dns_db_t *db, qpz_version_t *version) {
        qpznode_t *node = NULL;
@@ -1293,17 +1304,8 @@ rollback_node(qpznode_t *node, uint32_t serial) {
         * will be cleaned up; until that time, they will be ignored.
         */
        DNS_SLABTOP_FOREACH(top, node->data) {
-               dns_slabheader_t *header = top->header;
-
-               if (header->serial == serial) {
-                       DNS_SLABHEADER_SETATTR(header,
-                                              DNS_SLABHEADERATTR_IGNORE);
-                       make_dirty = true;
-               }
-
-               for (header = header->down; header != NULL;
-                    header = header->down)
-               {
+               dns_slabheader_t *header = NULL;
+               cds_list_for_each_entry(header, &top->headers, headers_link) {
                        if (header->serial == serial) {
                                DNS_SLABHEADER_SETATTR(
                                        header, DNS_SLABHEADERATTR_IGNORE);
@@ -1822,12 +1824,14 @@ add(qpzonedb_t *qpdb, qpznode_t *node, const dns_name_t *nodename,
         * IGNORE rdatasets between the top of the chain and the first real
         * data.  We skip over them.
         */
-       dns_slabheader_t *header = NULL, *header_prev = NULL;
+       dns_slabheader_t *header = NULL;
        if (foundtop != NULL) {
-               header = foundtop->header;
-               while (header != NULL && IGNORE(header)) {
-                       header_prev = header;
-                       header = header->down;
+               dns_slabheader_t *tmp = NULL;
+               cds_list_for_each_entry(tmp, &top->headers, headers_link) {
+                       if (!IGNORE(tmp)) {
+                               header = tmp;
+                               break;
+                       }
                }
        }
 
@@ -1895,11 +1899,10 @@ add(qpzonedb_t *qpdb, qpznode_t *node, const dns_name_t *nodename,
                        }
                }
 
-               INSIST(version->serial >= foundtop->header->serial);
+               INSIST(version->serial >= header->serial);
                INSIST(foundtop->typepair == newheader->typepair);
 
                if (loading) {
-                       newheader->down = NULL;
                        if (RESIGN(newheader)) {
                                resigninsert(newheader);
                                /* resigndelete not needed here */
@@ -1912,7 +1915,9 @@ add(qpzonedb_t *qpdb, qpznode_t *node, const dns_name_t *nodename,
                         * loading, we MUST clean up 'header' now.
                         */
                        newheader->top = foundtop;
-                       foundtop->header = newheader;
+                       cds_list_del(&header->headers_link);
+                       cds_list_add(&newheader->headers_link,
+                                    &foundtop->headers);
                        maybe_update_recordsandsize(false, version, header,
                                                    nodename->length);
 
@@ -1924,14 +1929,9 @@ add(qpzonedb_t *qpdb, qpznode_t *node, const dns_name_t *nodename,
                                             header DNS__DB_FLARG_PASS);
                        }
 
-                       if (header_prev != NULL) {
-                               header_prev->down = newheader;
-                       } else {
-                               foundtop->header = newheader;
-                       }
-
                        newheader->top = foundtop;
-                       newheader->down = header;
+                       cds_list_add(&newheader->headers_link,
+                                    &foundtop->headers);
 
                        node->dirty = true;
                        if (changed != NULL) {
@@ -1967,10 +1967,11 @@ add(qpzonedb_t *qpdb, qpznode_t *node, const dns_name_t *nodename,
                         * we INSIST on it.
                         */
                        INSIST(!loading);
-                       INSIST(version->serial >= foundtop->header->serial);
+
                        newheader->top = foundtop;
-                       newheader->down = foundtop->header;
-                       foundtop->header = newheader;
+                       cds_list_add(&newheader->headers_link,
+                                    &foundtop->headers);
+
                        if (changed != NULL) {
                                changed->dirty = true;
                        }
@@ -1991,7 +1992,8 @@ add(qpzonedb_t *qpdb, qpznode_t *node, const dns_name_t *nodename,
                                node->mctx, newheader->typepair);
 
                        newheader->top = newtop;
-                       newtop->header = newheader;
+                       cds_list_add(&newheader->headers_link,
+                                    &newtop->headers);
 
                        if (prio_type(newheader->typepair)) {
                                /* This is a priority type, prepend it */
@@ -2130,7 +2132,6 @@ loading_addrdataset(void *arg, const dns_name_t *name,
 
        newheader = (dns_slabheader_t *)region.base;
        dns_slabheader_reset(newheader, (dns_dbnode_t *)node);
-
        newheader->ttl = rdataset->ttl;
        atomic_store(&newheader->trust, rdataset->trust);
        newheader->serial = 1;
@@ -4766,9 +4767,12 @@ qpzone_subtractrdataset(dns_db_t *db, dns_dbnode_t *dbnode,
         */
        dns_slabheader_t *header = NULL;
        if (foundtop != NULL) {
-               header = foundtop->header;
-               while (header != NULL && IGNORE(header)) {
-                       header = header->down;
+               dns_slabheader_t *tmp = NULL;
+               cds_list_for_each_entry(tmp, &foundtop->headers, headers_link) {
+                       if (!IGNORE(tmp)) {
+                               header = tmp;
+                               break;
+                       }
                }
        }
        if (header != NULL && EXISTS(header)) {
@@ -4834,13 +4838,11 @@ qpzone_subtractrdataset(dns_db_t *db, dns_dbnode_t *dbnode,
                /*
                 * If we're here, we want to link newheader at the top.
                 */
-               INSIST(version->serial >= foundtop->header->serial);
                maybe_update_recordsandsize(false, version, header,
                                            nodename->length);
 
                newheader->top = foundtop;
-               newheader->down = foundtop->header;
-               foundtop->header = newheader;
+               cds_list_add(&newheader->headers_link, &foundtop->headers);
 
                node->dirty = true;
                changed->dirty = true;
@@ -5291,12 +5293,13 @@ static dns_dbnode_methods_t qpznode_methods = (dns_dbnode_methods_t){
 static void
 destroy_qpznode(qpznode_t *node) {
        DNS_SLABTOP_FOREACH(top, node->data) {
-               dns_slabheader_t *down = NULL, *down_next = NULL;
-               for (down = top->header; down != NULL; down = down_next) {
-                       down_next = down->down;
-                       dns_slabheader_destroy(&down);
+               dns_slabheader_t *header = NULL, *header_next = NULL;
+               cds_list_for_each_entry_safe(header, header_next, &top->headers,
+                                            headers_link)
+               {
+                       cds_list_del(&header->headers_link);
+                       dns_slabheader_destroy(&header);
                }
-               top->header = NULL;
 
                dns_slabtop_destroy(node->mctx, &top);
        }
index beb5f38d019c70713d5a0c8bac14472ce3aaed46..703c2f6b1ea0da157715e1fe4c963dc43694bc9e 100644 (file)
@@ -323,6 +323,7 @@ dns_rdataslab_fromrdataset(dns_rdataset_t *rdataset, isc_mem_t *mctx,
                }
 
                *new = (dns_slabheader_t){
+                       .headers_link = CDS_LIST_HEAD_INIT(new->headers_link),
                        .typepair = typepair,
                        .trust = rdataset->trust,
                        .ttl = rdataset->ttl,
@@ -1180,6 +1181,7 @@ dns_slabtop_new(isc_mem_t *mctx, dns_typepair_t typepair) {
        dns_slabtop_t *top = isc_mem_get(mctx, sizeof(*top));
        *top = (dns_slabtop_t){
                .types_link = CDS_LIST_HEAD_INIT(top->types_link),
+               .headers = CDS_LIST_HEAD_INIT(top->headers),
                .typepair = typepair,
                .link = ISC_LINK_INITIALIZER,
        };