Previously, rbtdb->task had quantum of 1 because it was originally used
just for freeing RBTDB contents, which can happen on a "best effort"
basis (does not need to be prioritized). However, when tree pruning was
implemented, it also started sending events to that task, enabling the
latter to become clogged up with a significant event backlog because it
only pruned a single RBTDB node per event.
To prioritize tree pruning (as it is necessary for enforcing the
configured memory use limit for the cache memory context), create a
second task with a virtually unlimited quantum (UINT_MAX) and send the
tree-pruning events to this new task, to ensure that all nodes scheduled
for pruning will be processed before further nodes are queued in a
similar fashion.
This change enables dropping the prunenodes list and restoring the
originally-used logic that allocates and sends a separate event for each
node to prune.
(cherry picked from commit
231b2375e5b9b98096711f5e883911134adb6392)
}
static void
-settask(dns_db_t *db, isc_task_t *task) {
+settask(dns_db_t *db, isc_task_t *task, isc_task_t *prunetask) {
sampledb_t *sampledb = (sampledb_t *)db;
REQUIRE(VALID_SAMPLEDB(sampledb));
- dns_db_settask(sampledb->rbtdb, task);
+ dns_db_settask(sampledb->rbtdb, task, prunetask);
}
static isc_result_t
isc_result_t result;
dns_cache_t *cache;
int i, extra = 0;
- isc_task_t *dbtask;
REQUIRE(cachep != NULL);
REQUIRE(*cachep == NULL);
goto cleanup_dbargv;
}
if (taskmgr != NULL) {
- dbtask = NULL;
+ isc_task_t *dbtask = NULL;
+ isc_task_t *prunetask = NULL;
+
result = isc_task_create(taskmgr, 1, &dbtask);
if (result != ISC_R_SUCCESS) {
goto cleanup_db;
}
-
isc_task_setname(dbtask, "cache_dbtask", NULL);
- dns_db_settask(cache->db, dbtask);
+
+ result = isc_task_create(taskmgr, UINT_MAX, &prunetask);
+ if (result != ISC_R_SUCCESS) {
+ isc_task_detach(&dbtask);
+ goto cleanup_db;
+ }
+ isc_task_setname(prunetask, "cache_prunetask", NULL);
+
+ dns_db_settask(cache->db, dbtask, prunetask);
isc_task_detach(&dbtask);
+ isc_task_detach(&prunetask);
}
cache->filename = NULL;
}
void
-dns_db_settask(dns_db_t *db, isc_task_t *task) {
+dns_db_settask(dns_db_t *db, isc_task_t *task, isc_task_t *prunetask) {
REQUIRE(DNS_DB_VALID(db));
- (db->methods->settask)(db, task);
+ (db->methods->settask)(db, task, prunetask);
}
isc_result_t
unsigned int (*nodecount)(dns_db_t *db);
bool (*ispersistent)(dns_db_t *db);
void (*overmem)(dns_db_t *db, bool overmem);
- void (*settask)(dns_db_t *db, isc_task_t *);
+ void (*settask)(dns_db_t *db, isc_task_t *, isc_task_t *);
isc_result_t (*getoriginnode)(dns_db_t *db, dns_dbnode_t **nodep);
void (*transfernode)(dns_db_t *db, dns_dbnode_t **sourcep,
dns_dbnode_t **targetp);
*/
void
-dns_db_settask(dns_db_t *db, isc_task_t *task);
+dns_db_settask(dns_db_t *db, isc_task_t *task, isc_task_t *prunetask);
/*%<
* If task is set then the final detach maybe performed asynchronously.
*
* Requires:
* \li 'db' is a valid database.
- * \li 'task' to be valid or NULL.
+ * \li 'task' to be valid or NULL (default task to send events to).
+ * \li 'prunetask' to be valid or NULL (task to send tree-pruning events to).
*/
bool
*/
ISC_LINK(dns_rbtnode_t) deadlink;
- /*%
- * This linked list is used to store nodes from which tree pruning can
- * be started.
- */
- ISC_LINK(dns_rbtnode_t) prunelink;
-
/*@{*/
/*!
* These values are used in the RBT DB implementation. The appropriate
HASHVAL(node) = 0;
ISC_LINK_INIT(node, deadlink);
- ISC_LINK_INIT(node, prunelink);
LOCKNUM(node) = 0;
WILD(node) = 0;
rbtdb_version_t *future_version;
rbtdb_versionlist_t open_versions;
isc_task_t *task;
+ isc_task_t *prunetask;
dns_dbnode_t *soanode;
dns_dbnode_t *nsnode;
*/
rbtnodelist_t *deadnodes;
- /* List of nodes from which recursive tree pruning can be started from.
- * Locked by tree_lock. */
- rbtnodelist_t *prunenodes;
-
/*
* Heaps. These are used for TTL based expiry in a cache,
* or for zone resigning in a zone DB. hmctx is the memory
}
}
- for (i = 0; i < rbtdb->node_lock_count; i++) {
- node = ISC_LIST_HEAD(rbtdb->prunenodes[i]);
- while (node != NULL) {
- ISC_LIST_UNLINK(rbtdb->prunenodes[i], node, prunelink);
- node = ISC_LIST_HEAD(rbtdb->prunenodes[i]);
- }
- }
-
if (event == NULL) {
rbtdb->quantum = (rbtdb->task != NULL) ? 100 : 0;
}
isc_mem_put(rbtdb->common.mctx, rbtdb->deadnodes,
rbtdb->node_lock_count * sizeof(rbtnodelist_t));
}
- /*
- * Clean up prune node buckets.
- */
- if (rbtdb->prunenodes != NULL) {
- for (i = 0; i < rbtdb->node_lock_count; i++) {
- INSIST(ISC_LIST_EMPTY(rbtdb->prunenodes[i]));
- }
- isc_mem_put(rbtdb->common.mctx, rbtdb->prunenodes,
- rbtdb->node_lock_count *
- sizeof(rbtdb->prunenodes[i]));
- }
/*
* Clean up heap objects.
*/
if (rbtdb->task != NULL) {
isc_task_detach(&rbtdb->task);
}
+ if (rbtdb->prunetask != NULL) {
+ isc_task_detach(&rbtdb->prunetask);
+ }
RBTDB_DESTROYLOCK(&rbtdb->lock);
rbtdb->common.magic = 0;
isc_result_t result = ISC_R_UNEXPECTED;
INSIST(!ISC_LINK_LINKED(node, deadlink));
- INSIST(!ISC_LINK_LINKED(node, prunelink));
if (isc_log_wouldlog(dns_lctx, ISC_LOG_DEBUG(1))) {
char printname[DNS_NAME_FORMATSIZE];
node->left == NULL && node->right == NULL);
}
-/*%
- * The tree lock must be held when this function is called as it reads and
- * updates rbtdb->prunenodes.
- */
static void
send_to_prune_tree(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
isc_rwlocktype_t nlocktype) {
}
static void
-settask(dns_db_t *db, isc_task_t *task) {
+settask(dns_db_t *db, isc_task_t *task, isc_task_t *prunetask) {
dns_rbtdb_t *rbtdb;
rbtdb = (dns_rbtdb_t *)db;
if (task != NULL) {
isc_task_attach(task, &rbtdb->task);
}
+ if (rbtdb->prunetask != NULL) {
+ isc_task_detach(&rbtdb->prunetask);
+ }
+ if (prunetask != NULL) {
+ isc_task_attach(prunetask, &rbtdb->prunetask);
+ }
RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write);
}
ISC_LIST_INIT(rbtdb->deadnodes[i]);
}
- rbtdb->prunenodes = isc_mem_get(
- mctx, rbtdb->node_lock_count * sizeof(rbtdb->prunenodes[0]));
- for (i = 0; i < (int)rbtdb->node_lock_count; i++) {
- ISC_LIST_INIT(rbtdb->prunenodes[i]);
- }
-
rbtdb->active = rbtdb->node_lock_count;
for (i = 0; i < (int)(rbtdb->node_lock_count); i++) {
isc_refcount_init(&rbtdb->references, 1);
rbtdb->attributes = 0;
rbtdb->task = NULL;
+ rbtdb->prunetask = NULL;
rbtdb->serve_stale_ttl = 0;
/*
}
static void
-settask(dns_db_t *db, isc_task_t *task) {
+settask(dns_db_t *db, isc_task_t *task, isc_task_t *prunetask) {
UNUSED(db);
UNUSED(task);
+ UNUSED(prunetask);
}
static dns_dbmethods_t sdb_methods = {
}
static void
-settask(dns_db_t *db, isc_task_t *task) {
+settask(dns_db_t *db, isc_task_t *task, isc_task_t *prunetask) {
UNUSED(db);
UNUSED(task);
+ UNUSED(prunetask);
}
/*
isc_result_totext(result));
goto cleanup;
}
- dns_db_settask(db, zone->task);
+ dns_db_settask(db, zone->task, zone->task);
if (zone->type == dns_zone_primary ||
zone->type == dns_zone_secondary || zone->type == dns_zone_mirror)
dns_result_totext(result));
goto cleanup;
}
- dns_db_settask(stub->db, zone->task);
+ dns_db_settask(stub->db, zone->task, zone->task);
}
result = dns_db_newversion(stub->db, &stub->version);
isc_task_attach(task, &zone->task);
ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
if (zone->db != NULL) {
- dns_db_settask(zone->db, zone->task);
+ dns_db_settask(zone->db, zone->task, zone->task);
}
ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
UNLOCK_ZONE(zone);
zone_detachdb(zone);
}
zone_attachdb(zone, db);
- dns_db_settask(zone->db, zone->task);
+ dns_db_settask(zone->db, zone->task, zone->task);
DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED | DNS_ZONEFLG_NEEDNOTIFY);
return (ISC_R_SUCCESS);