]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Process the combined LRU lists in LRU order
authorMark Andrews <marka@isc.org>
Thu, 23 Nov 2023 04:47:35 +0000 (15:47 +1100)
committerMark Andrews <marka@isc.org>
Thu, 7 Dec 2023 02:59:04 +0000 (02:59 +0000)
Only cleanup headers that are less than equal to the rbt's last_used
time.  Adjust the rbt's last_used time when the target cleaning was
not achieved to the oldest value of the remaining set of headers.

When updating delegating NS and glue records last_used was not being
updated when it should have been.

When adding zero TTL records to the tail of the LRU lists set
last_used to rbtdb->last_used + 1 rather than now.  This appoximately
preserves the lists LRU order.

lib/dns/rbt-cachedb.c
lib/dns/rbtdb.c
lib/dns/rbtdb_p.h

index d57eb2a6cc7fdc92933f7371f5dd25cf09e96124..51d56c4ede742cb39760db2c919a10103bc5dca9 100644 (file)
@@ -1635,7 +1635,9 @@ expire_lru_headers(dns_rbtdb_t *rbtdb, unsigned int locknum,
        size_t purged = 0;
 
        for (header = ISC_LIST_TAIL(rbtdb->lru[locknum]);
-            header != NULL && purged <= purgesize; header = header_prev)
+            header != NULL && header->last_used <= rbtdb->last_used &&
+            purged <= purgesize;
+            header = header_prev)
        {
                size_t header_size = rdataset_size(header);
                header_prev = ISC_LIST_PREV(header, link);
@@ -1662,25 +1664,22 @@ expire_lru_headers(dns_rbtdb_t *rbtdb, unsigned int locknum,
  * we clean up entries up to the size of newly added rdata that triggered
  * the overmem; this is accessible via newheader.
  *
- * 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).
+ * The LRU lists tails are processed in LRU order to the nearest second.
+ *
+ * A write lock on the tree must be held.
  */
 void
 dns__cachedb_overmem(dns_rbtdb_t *rbtdb, dns_slabheader_t *newheader,
-                    unsigned int locknum_start,
                     isc_rwlocktype_t *tlocktypep DNS__DB_FLARG) {
-       unsigned int locknum;
+       uint32_t locknum_start = rbtdb->lru_sweep++ % rbtdb->node_lock_count;
+       uint32_t locknum = locknum_start;
        size_t purgesize = rdataset_size(newheader);
        size_t purged = 0;
+       isc_stdtime_t min_last_used = 0;
+       size_t max_passes = 8;
 
-       for (locknum = (locknum_start + 1) % rbtdb->node_lock_count;
-            locknum != locknum_start && purged <= purgesize;
-            locknum = (locknum + 1) % rbtdb->node_lock_count)
-       {
+again:
+       do {
                isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
                NODE_WRLOCK(&rbtdb->node_locks[locknum].lock, &nlocktype);
 
@@ -1688,6 +1687,30 @@ dns__cachedb_overmem(dns_rbtdb_t *rbtdb, dns_slabheader_t *newheader,
                                             purgesize -
                                                     purged DNS__DB_FLARG_PASS);
 
+               /*
+                * Work out the oldest remaining last_used values of the list
+                * tails as we walk across the array of lru lists.
+                */
+               dns_slabheader_t *header = ISC_LIST_TAIL(rbtdb->lru[locknum]);
+               if (header != NULL &&
+                   (min_last_used == 0 || header->last_used < min_last_used))
+               {
+                       min_last_used = header->last_used;
+               }
                NODE_UNLOCK(&rbtdb->node_locks[locknum].lock, &nlocktype);
+               locknum = (locknum + 1) % rbtdb->node_lock_count;
+       } while (locknum != locknum_start && purged <= purgesize);
+
+       /*
+        * Update rbtdb->last_used if we have walked all the list tails and have
+        * not freed the required amount of memory.
+        */
+       if (purged < purgesize) {
+               if (min_last_used != 0) {
+                       rbtdb->last_used = min_last_used;
+                       if (max_passes-- > 0) {
+                               goto again;
+                       }
+               }
        }
 }
index 78464170b069a2ddd55080b8282b638f7717bd27..900b580058dc14d9e8b2590e82211e62637136b2 100644 (file)
@@ -2757,6 +2757,15 @@ find_header:
                        if (header->ttl > newheader->ttl) {
                                dns__rbtdb_setttl(header, newheader->ttl);
                        }
+                       if (header->last_used != now) {
+                               ISC_LIST_UNLINK(
+                                       rbtdb->lru[HEADER_NODE(header)->locknum],
+                                       header, link);
+                               header->last_used = now;
+                               ISC_LIST_PREPEND(
+                                       rbtdb->lru[HEADER_NODE(header)->locknum],
+                                       header, link);
+                       }
                        if (header->noqname == NULL &&
                            newheader->noqname != NULL)
                        {
@@ -2811,6 +2820,15 @@ find_header:
                        if (header->ttl > newheader->ttl) {
                                dns__rbtdb_setttl(header, newheader->ttl);
                        }
+                       if (header->last_used != now) {
+                               ISC_LIST_UNLINK(
+                                       rbtdb->lru[HEADER_NODE(header)->locknum],
+                                       header, link);
+                               header->last_used = now;
+                               ISC_LIST_PREPEND(
+                                       rbtdb->lru[HEADER_NODE(header)->locknum],
+                                       header, link);
+                       }
                        if (header->noqname == NULL &&
                            newheader->noqname != NULL)
                        {
@@ -2839,6 +2857,8 @@ find_header:
                        idx = HEADER_NODE(newheader)->locknum;
                        if (IS_CACHE(rbtdb)) {
                                if (ZEROTTL(newheader)) {
+                                       newheader->last_used =
+                                               rbtdb->last_used + 1;
                                        ISC_LIST_APPEND(rbtdb->lru[idx],
                                                        newheader, link);
                                } else {
@@ -2882,6 +2902,8 @@ find_header:
                                isc_heap_insert(rbtdb->heaps[idx], newheader);
                                newheader->heap = rbtdb->heaps[idx];
                                if (ZEROTTL(newheader)) {
+                                       newheader->last_used =
+                                               rbtdb->last_used + 1;
                                        ISC_LIST_APPEND(rbtdb->lru[idx],
                                                        newheader, link);
                                } else {
@@ -3263,7 +3285,7 @@ dns__rbtdb_addrdataset(dns_db_t *db, dns_dbnode_t *node,
        }
 
        if (cache_is_overmem) {
-               dns__cachedb_overmem(rbtdb, newheader, rbtnode->locknum,
+               dns__cachedb_overmem(rbtdb, newheader,
                                     &tlocktype DNS__DB_FLARG_PASS);
        }
 
index ddc98fe7f2e8f86858ef3dc2de52751039bcef6b..1aa5672533ada3550b2dbb5b1c0b98c56ee6e69e 100644 (file)
@@ -273,12 +273,23 @@ struct dns_rbtdb {
        uint32_t serve_stale_refresh;
 
        /*
-        * This is a linked list used to implement the LRU cache.  There will
-        * be node_lock_count linked lists here.  Nodes in bucket 1 will be
-        * placed on the linked list lru[1].
+        * This is an array of linked lists used to implement the LRU cache.
+        * There will be node_lock_count linked lists here.  Nodes in bucket 1
+        * will be placed on the linked list lru[1].
         */
        dns_slabheaderlist_t *lru;
 
+       /*
+        * Start point % node_lock_count for next LRU cleanup.
+        */
+       atomic_uint lru_sweep;
+
+       /*
+        * When performing LRU cleaning limit cleaning to headers that were
+        * last used at or before this.
+        */
+       _Atomic(isc_stdtime_t) last_used;
+
        /*%
         * Temporary storage for stale cache nodes and dynamically deleted
         * nodes that await being cleaned up.
@@ -607,7 +618,6 @@ dns__cachedb_expireheader(dns_slabheader_t *header,
                          dns_expire_t reason DNS__DB_FLARG);
 void
 dns__cachedb_overmem(dns_rbtdb_t *rbtdb, dns_slabheader_t *newheader,
-                    unsigned int locknum_start,
                     isc_rwlocktype_t *tlocktypep DNS__DB_FLARG);
 
 ISC_LANG_ENDDECLS