Process the combined LRU lists in LRU order
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.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user