diff --git a/lib/dns/qp.c b/lib/dns/qp.c index 4e70fa0dac..fec5059075 100644 --- a/lib/dns/qp.c +++ b/lib/dns/qp.c @@ -1059,7 +1059,7 @@ dns_qpmulti_memusage(dns_qpmulti_t *multi) { dns_qp_memusage_t memusage = dns_qp_memusage(qp); - if (qp->transaction_mode == QP_UPDATE) { + if (qp->transaction_mode == QP_UPDATE && qp->usage != NULL) { memusage.bytes -= QP_CHUNK_BYTES; memusage.bytes += qp->usage[qp->bump].used * sizeof(dns_qpnode_t); diff --git a/lib/dns/qpcache.c b/lib/dns/qpcache.c index ccea16dd38..edc2b0c6ec 100644 --- a/lib/dns/qpcache.c +++ b/lib/dns/qpcache.c @@ -223,8 +223,6 @@ struct dns_qpdb { dns_db_t common; /* Locks the data in this struct */ isc_rwlock_t lock; - /* Locks the tree structure (prevents nodes appearing/disappearing) */ - isc_rwlock_t tree_lock; /* Locks for individual tree nodes */ unsigned int node_lock_count; db_nodelock_t *node_locks; @@ -282,12 +280,13 @@ struct dns_qpdb { isc_mem_t *hmctx; isc_heap_t **heaps; - /* Locked by tree_lock. */ - dns_qp_t *tree; - dns_qp_t *nsec; + dns_qpmulti_t *tree; /* Main QP trie for data storage */ + dns_qpmulti_t *nsec; /* NSEC nodes only */ /* Unlocked */ unsigned int quantum; + + struct rcu_head rcu_head; }; /*% @@ -306,9 +305,22 @@ typedef struct { dns_slabheader_t *zonecut_header; dns_slabheader_t *zonecut_sigheader; dns_fixedname_t zonecut_name; + dns_qpread_t tree; + dns_qpread_t nsec; + dns_qpread_t nsec3; isc_stdtime_t now; } qpdb_search_t; +/*% + * Tree modification context. + */ +typedef struct { + bool writing; + dns_qpread_t qpr; + dns_qp_t *tree; + dns_qp_t *nsec; +} dbmod_t; + #ifdef DNS_DB_NODETRACE #define qpdata_ref(ptr) qpdata__ref(ptr, __func__, __FILE__, __LINE__) #define qpdata_unref(ptr) qpdata__unref(ptr, __func__, __FILE__, __LINE__) @@ -417,16 +429,12 @@ static dns_dbiteratormethods_t dbiterator_methods = { */ typedef struct qpdb_dbiterator { dns_dbiterator_t common; - bool paused; - isc_rwlocktype_t tree_locked; isc_result_t result; - dns_qpiter_t iter; + dns_qpsnap_t *tsnap; /* tree snapshot */ + dns_qpiter_t iter; /* iterator */ qpdata_t *node; } qpdb_dbiterator_t; -static void -free_qpdb(dns_qpdb_t *qpdb, bool log); - static dns_dbmethods_t qpdb_cachemethods; /*% @@ -588,14 +596,12 @@ clean_cache_node(dns_qpdb_t *qpdb, qpdata_t *node) { node->dirty = 0; } -/* - * tree_lock(write) must be held. - */ static void -delete_node(dns_qpdb_t *qpdb, qpdata_t *node) { +delete_node(qpdata_t *node, dbmod_t *modctx) { isc_result_t result = ISC_R_UNEXPECTED; INSIST(!ISC_LINK_LINKED(node, deadlink)); + INSIST(modctx->writing); if (isc_log_wouldlog(dns_lctx, ISC_LOG_DEBUG(1))) { char printname[DNS_NAME_FORMATSIZE]; @@ -612,7 +618,8 @@ delete_node(dns_qpdb_t *qpdb, qpdata_t *node) { * Delete the corresponding node from the auxiliary NSEC * tree before deleting from the main tree. */ - result = dns_qp_deletename(qpdb->nsec, &node->name, NULL, NULL); + result = dns_qp_deletename(modctx->nsec, &node->name, NULL, + NULL); if (result != ISC_R_SUCCESS) { isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE, ISC_LOG_WARNING, @@ -622,10 +629,12 @@ delete_node(dns_qpdb_t *qpdb, qpdata_t *node) { } /* FALLTHROUGH */ case DNS_DB_NSEC_NORMAL: - result = dns_qp_deletename(qpdb->tree, &node->name, NULL, NULL); + result = dns_qp_deletename(modctx->tree, &node->name, NULL, + NULL); break; case DNS_DB_NSEC_NSEC: - result = dns_qp_deletename(qpdb->nsec, &node->name, NULL, NULL); + result = dns_qp_deletename(modctx->nsec, &node->name, NULL, + NULL); break; } if (result != ISC_R_SUCCESS) { @@ -695,10 +704,7 @@ newref(dns_qpdb_t *qpdb, qpdata_t *node, */ static bool decref(dns_qpdb_t *qpdb, qpdata_t *node, isc_rwlocktype_t *nlocktypep, - isc_rwlocktype_t *tlocktypep, bool tryupgrade DNS__DB_FLARG) { - isc_result_t result; - bool locked = *tlocktypep != isc_rwlocktype_none; - bool write_locked = false; + dbmod_t *modctx DNS__DB_FLARG) { db_nodelock_t *nodelock = NULL; int bucket = node->locknum; bool no_reference = true; @@ -762,39 +768,6 @@ decref(dns_qpdb_t *qpdb, qpdata_t *node, isc_rwlocktype_t *nlocktypep, clean_cache_node(qpdb, node); } - /* - * Attempt to switch to a write lock on the tree. If this fails, - * we will add this node to a linked list of nodes in this locking - * bucket which we will free later. - * - * Locking hierarchy notwithstanding, we don't need to free - * the node lock before acquiring the tree write lock because - * we only do a trylock. - */ - /* We are allowed to upgrade the tree lock */ - - switch (*tlocktypep) { - case isc_rwlocktype_write: - result = ISC_R_SUCCESS; - break; - case isc_rwlocktype_read: - if (tryupgrade) { - result = TREE_TRYUPGRADE(&qpdb->tree_lock, tlocktypep); - } else { - result = ISC_R_LOCKBUSY; - } - break; - case isc_rwlocktype_none: - result = TREE_TRYWRLOCK(&qpdb->tree_lock, tlocktypep); - break; - default: - UNREACHABLE(); - } - RUNTIME_CHECK(result == ISC_R_SUCCESS || result == ISC_R_LOCKBUSY); - if (result == ISC_R_SUCCESS) { - write_locked = true; - } - refs = isc_refcount_decrement(&nodelock->references); #if DNS_DB_NODETRACE fprintf(stderr, @@ -804,32 +777,20 @@ decref(dns_qpdb_t *qpdb, qpdata_t *node, isc_rwlocktype_t *nlocktypep, UNUSED(refs); #endif - if (KEEP_NODE(node, qpdb)) { - goto restore_locks; - } - -#undef KEEP_NODE - - if (write_locked) { - /* - * We can now delete the node. - */ - delete_node(qpdb, node); - } else { - INSIST(node->data == NULL); - if (!ISC_LINK_LINKED(node, deadlink)) { - ISC_LIST_APPEND(qpdb->deadnodes[bucket], node, - deadlink); + if (!KEEP_NODE(node, qpdb)) { + if (modctx != NULL && modctx->writing) { + /* We can delete the node now. */ + delete_node(node, modctx); + } else { + /* We can't delete it now, but add it to deadnodes. */ + INSIST(node->data == NULL); + if (!ISC_LINK_LINKED(node, deadlink)) { + ISC_LIST_APPEND(qpdb->deadnodes[bucket], node, + deadlink); + } } } - -restore_locks: - /* - * Relock a read lock, or unlock the write lock if no lock was held. - */ - if (!locked && write_locked) { - TREE_UNLOCK(&qpdb->tree_lock, tlocktypep); - } +#undef KEEP_NODE qpdata_unref(node); return (no_reference); @@ -914,9 +875,6 @@ setttl(dns_slabheader_t *header, dns_ttl_t newttl) { return; } - /* - * This is a cache. Adjust the heaps if necessary. - */ if (header->heap == NULL || header->heap_index == 0 || newttl == oldttl) { return; @@ -937,7 +895,7 @@ setttl(dns_slabheader_t *header, dns_ttl_t newttl) { * Caller must hold the node (write) lock. */ static void -expireheader(dns_slabheader_t *header, isc_rwlocktype_t *tlocktypep, +expireheader(dns_slabheader_t *header, dbmod_t *modctx, dns_expire_t reason DNS__DB_FLARG) { setttl(header, 0); mark(header, DNS_SLABHEADERATTR_ANCIENT); @@ -953,8 +911,8 @@ expireheader(dns_slabheader_t *header, isc_rwlocktype_t *tlocktypep, * requirement of decref(). */ newref(qpdb, HEADERNODE(header), nlocktype DNS__DB_FLARG_PASS); - decref(qpdb, HEADERNODE(header), &nlocktype, tlocktypep, - true DNS__DB_FLARG_PASS); + decref(qpdb, HEADERNODE(header), &nlocktype, + modctx DNS__DB_FLARG_PASS); if (qpdb->cachestats == NULL) { return; @@ -1237,7 +1195,7 @@ check_stale_header(qpdata_t *node, dns_slabheader_t *header, { /* * We update the node's status only when we can - * get write access; otherwise, we leave others + * get write modctx; otherwise, we leave others * to this work. Periodical cleaning will * eventually take the job as the last resort. * We won't downgrade the lock, since other @@ -1248,10 +1206,9 @@ check_stale_header(qpdata_t *node, dns_slabheader_t *header, /* * header->down can be non-NULL if the * refcount has just decremented to 0 - * but decref() has not - * performed clean_cache_node(), in - * which case we need to purge the stale - * headers first. + * but decref() has not performed + * clean_cache_node(), in which case we + * need to purge the stale headers first. */ clean_stale_headers(header); if (*header_prev != NULL) { @@ -1474,7 +1431,7 @@ find_coveringnsec(qpdb_search_t *search, const dns_name_t *name, /* * Look for the node in the auxilary tree. */ - result = dns_qp_lookup(search->qpdb->nsec, name, NULL, &iter, NULL, + result = dns_qp_lookup(&search->nsec, name, NULL, &iter, NULL, (void **)&node, NULL); if (result != DNS_R_PARTIALMATCH) { return (ISC_R_NOTFOUND); @@ -1497,8 +1454,8 @@ find_coveringnsec(qpdb_search_t *search, const dns_name_t *name, * Lookup the predecessor in the main tree. */ node = NULL; - result = dns_qp_lookup(search->qpdb->tree, predecessor, fname, NULL, - NULL, (void **)&node, NULL); + result = dns_qp_lookup(&search->tree, predecessor, fname, NULL, NULL, + (void **)&node, NULL); if (result != ISC_R_SUCCESS) { return (ISC_R_NOTFOUND); } @@ -1551,9 +1508,10 @@ find_coveringnsec(qpdb_search_t *search, const dns_name_t *name, } static isc_result_t -find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version, - dns_rdatatype_t type, unsigned int options, isc_stdtime_t now, - dns_dbnode_t **nodep, dns_name_t *foundname, dns_rdataset_t *rdataset, +find(dns_db_t *db, const dns_name_t *name, + dns_dbversion_t *version ISC_ATTR_UNUSED, dns_rdatatype_t type, + unsigned int options, isc_stdtime_t now, dns_dbnode_t **nodep, + dns_name_t *foundname, dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset DNS__DB_FLARG) { qpdata_t *node = NULL; isc_result_t result; @@ -1563,7 +1521,6 @@ find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version, bool all_negative = true; bool empty_node; isc_rwlock_t *lock = NULL; - isc_rwlocktype_t tlocktype = isc_rwlocktype_none; isc_rwlocktype_t nlocktype = isc_rwlocktype_none; dns_slabheader_t *header = NULL; dns_slabheader_t *header_prev = NULL, *header_next = NULL; @@ -1573,10 +1530,7 @@ find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version, dns_slabheader_t *nsecheader = NULL, *nsecsig = NULL; dns_typepair_t sigtype, negtype; - UNUSED(version); - REQUIRE(VALID_QPDB((dns_qpdb_t *)db)); - REQUIRE(version == NULL); if (now == 0) { now = isc_stdtime_now(); @@ -1590,12 +1544,13 @@ find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version, }; dns_fixedname_init(&search.zonecut_name); - TREE_RDLOCK(&search.qpdb->tree_lock, &tlocktype); + dns_qpmulti_query(search.qpdb->tree, &search.tree); + dns_qpmulti_query(search.qpdb->nsec, &search.nsec); /* * Search down from the root of the tree. */ - result = dns_qp_lookup(search.qpdb->tree, name, foundname, NULL, + result = dns_qp_lookup(&search.tree, name, foundname, NULL, &search.chain, (void **)&node, NULL); /* @@ -1967,7 +1922,8 @@ node_exit: NODE_UNLOCK(lock, &nlocktype); tree_exit: - TREE_UNLOCK(&search.qpdb->tree_lock, &tlocktype); + dns_qpread_destroy(search.qpdb->tree, &search.tree); + dns_qpread_destroy(search.qpdb->nsec, &search.nsec); /* * If we found a zonecut but aren't going to use it, we have to @@ -1979,10 +1935,8 @@ tree_exit: lock = &(search.qpdb->node_locks[node->locknum].lock); NODE_RDLOCK(lock, &nlocktype); - decref(search.qpdb, node, &nlocktype, &tlocktype, - true DNS__DB_FLARG_PASS); + decref(search.qpdb, node, &nlocktype, NULL DNS__DB_FLARG_PASS); NODE_UNLOCK(lock, &nlocktype); - INSIST(tlocktype == isc_rwlocktype_none); } update_cachestats(search.qpdb, result); @@ -2001,7 +1955,6 @@ findzonecut(dns_db_t *db, const dns_name_t *name, unsigned int options, dns_slabheader_t *header = NULL; dns_slabheader_t *header_prev = NULL, *header_next = NULL; dns_slabheader_t *found = NULL, *foundsig = NULL; - isc_rwlocktype_t tlocktype = isc_rwlocktype_none; isc_rwlocktype_t nlocktype = isc_rwlocktype_none; bool dcnull = (dcname == NULL); @@ -2019,17 +1972,17 @@ findzonecut(dns_db_t *db, const dns_name_t *name, unsigned int options, }; dns_fixedname_init(&search.zonecut_name); + dns_qpmulti_query(search.qpdb->tree, &search.tree); + if (dcnull) { dcname = foundname; } - TREE_RDLOCK(&search.qpdb->tree_lock, &tlocktype); - /* * Search down from the root of the tree. */ - result = dns_qp_lookup(search.qpdb->tree, name, dcname, NULL, - &search.chain, (void **)&node, NULL); + result = dns_qp_lookup(&search.tree, name, dcname, NULL, &search.chain, + (void **)&node, NULL); if ((options & DNS_DBFIND_NOEXACT) != 0 && result == ISC_R_SUCCESS) { int len = dns_qpchain_length(&search.chain); if (len >= 2) { @@ -2151,7 +2104,7 @@ findzonecut(dns_db_t *db, const dns_name_t *name, unsigned int options, NODE_UNLOCK(lock, &nlocktype); tree_exit: - TREE_UNLOCK(&search.qpdb->tree_lock, &tlocktype); + dns_qpread_destroy(search.qpdb->tree, &search.tree); INSIST(!search.need_cleanup); @@ -2209,7 +2162,7 @@ findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, { /* * We update the node's status only when we - * can get write access. + * can get write modctx. * * We don't check if refcurrent(qpnode) == 0 * and try to free like we do in find(), @@ -2331,12 +2284,10 @@ expiredata(dns_db_t *db, dns_dbnode_t *node, void *data) { qpdata_t *qpnode = (qpdata_t *)node; dns_slabheader_t *header = data; isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - isc_rwlocktype_t tlocktype = isc_rwlocktype_none; NODE_WRLOCK(&qpdb->node_locks[qpnode->locknum].lock, &nlocktype); - expireheader(header, &tlocktype, dns_expire_flush DNS__DB_FILELINE); + expireheader(header, false, dns_expire_flush DNS__DB_FILELINE); NODE_UNLOCK(&qpdb->node_locks[qpnode->locknum].lock, &nlocktype); - INSIST(tlocktype == isc_rwlocktype_none); } static size_t @@ -2350,8 +2301,7 @@ rdataset_size(dns_slabheader_t *header) { } static size_t -expire_lru_headers(dns_qpdb_t *qpdb, unsigned int locknum, - isc_rwlocktype_t *tlocktypep, +expire_lru_headers(dns_qpdb_t *qpdb, unsigned int locknum, dbmod_t *modctx, size_t purgesize DNS__DB_FLARG) { dns_slabheader_t *header = NULL; size_t purged = 0; @@ -2371,8 +2321,7 @@ expire_lru_headers(dns_qpdb_t *qpdb, unsigned int locknum, * TTL will be reset to 0. */ ISC_LIST_UNLINK(qpdb->lru[locknum], header, link); - expireheader(header, tlocktypep, - dns_expire_lru DNS__DB_FLARG_PASS); + expireheader(header, modctx, dns_expire_lru DNS__DB_FLARG_PASS); purged += header_size; } @@ -2391,7 +2340,7 @@ expire_lru_headers(dns_qpdb_t *qpdb, unsigned int locknum, */ static void overmem(dns_qpdb_t *qpdb, dns_slabheader_t *newheader, - isc_rwlocktype_t *tlocktypep DNS__DB_FLARG) { + dbmod_t *modctx DNS__DB_FLARG) { uint32_t locknum_start = qpdb->lru_sweep++ % qpdb->node_lock_count; uint32_t locknum = locknum_start; /* Size of added data, possible node and possible ENT node. */ @@ -2405,7 +2354,7 @@ again: isc_rwlocktype_t nlocktype = isc_rwlocktype_none; NODE_WRLOCK(&qpdb->node_locks[locknum].lock, &nlocktype); - purged += expire_lru_headers(qpdb, locknum, tlocktypep, + purged += expire_lru_headers(qpdb, locknum, modctx, purgesize - purged DNS__DB_FLARG_PASS); @@ -2484,57 +2433,13 @@ set_index(void *what, unsigned int idx) { } static void -free_qpdb(dns_qpdb_t *qpdb, bool log) { - unsigned int i; - char buf[DNS_NAME_FORMATSIZE]; - dns_qp_t **treep = NULL; +free_qpdb_rcu(struct rcu_head *rcu_head) { + dns_qpdb_t *qpdb = caa_container_of(rcu_head, dns_qpdb_t, rcu_head); - /* - * We assume the number of remaining dead nodes is reasonably small; - * the overhead of unlinking all nodes here should be negligible. - */ - for (i = 0; i < qpdb->node_lock_count; i++) { - qpdata_t *node = NULL; - - node = ISC_LIST_HEAD(qpdb->deadnodes[i]); - while (node != NULL) { - ISC_LIST_UNLINK(qpdb->deadnodes[i], node, deadlink); - node = ISC_LIST_HEAD(qpdb->deadnodes[i]); - } - } - - qpdb->quantum = (qpdb->loop != NULL) ? 100 : 0; - - for (;;) { - /* - * pick the next tree to (start to) destroy - */ - treep = &qpdb->tree; - if (*treep == NULL) { - treep = &qpdb->nsec; - if (*treep == NULL) { - break; - } - } - - dns_qp_destroy(treep); - INSIST(*treep == NULL); - } - - if (log) { - if (dns_name_dynamic(&qpdb->common.origin)) { - dns_name_format(&qpdb->common.origin, buf, sizeof(buf)); - } else { - strlcpy(buf, "", sizeof(buf)); - } - isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, - DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1), - "done free_qpdb(%s)", buf); - } if (dns_name_dynamic(&qpdb->common.origin)) { dns_name_free(&qpdb->common.origin, qpdb->common.mctx); } - for (i = 0; i < qpdb->node_lock_count; i++) { + for (size_t i = 0; i < qpdb->node_lock_count; i++) { isc_refcount_destroy(&qpdb->node_locks[i].references); NODE_DESTROYLOCK(&qpdb->node_locks[i].lock); } @@ -2543,7 +2448,7 @@ free_qpdb(dns_qpdb_t *qpdb, bool log) { * Clean up LRU / re-signing order lists. */ if (qpdb->lru != NULL) { - for (i = 0; i < qpdb->node_lock_count; i++) { + for (size_t i = 0; i < qpdb->node_lock_count; i++) { INSIST(ISC_LIST_EMPTY(qpdb->lru[i])); } isc_mem_cput(qpdb->common.mctx, qpdb->lru, @@ -2554,7 +2459,7 @@ free_qpdb(dns_qpdb_t *qpdb, bool log) { * Clean up dead node buckets. */ if (qpdb->deadnodes != NULL) { - for (i = 0; i < qpdb->node_lock_count; i++) { + for (size_t i = 0; i < qpdb->node_lock_count; i++) { INSIST(ISC_LIST_EMPTY(qpdb->deadnodes[i])); } isc_mem_cput(qpdb->common.mctx, qpdb->deadnodes, @@ -2564,7 +2469,7 @@ free_qpdb(dns_qpdb_t *qpdb, bool log) { * Clean up heap objects. */ if (qpdb->heaps != NULL) { - for (i = 0; i < qpdb->node_lock_count; i++) { + for (size_t i = 0; i < qpdb->node_lock_count; i++) { isc_heap_destroy(&qpdb->heaps[i]); } isc_mem_cput(qpdb->hmctx, qpdb->heaps, qpdb->node_lock_count, @@ -2583,7 +2488,6 @@ free_qpdb(dns_qpdb_t *qpdb, bool log) { isc_mem_cput(qpdb->common.mctx, qpdb->node_locks, qpdb->node_lock_count, sizeof(db_nodelock_t)); - TREE_DESTROYLOCK(&qpdb->tree_lock); isc_refcount_destroy(&qpdb->common.references); if (qpdb->loop != NULL) { isc_loop_detach(&qpdb->loop); @@ -2601,6 +2505,43 @@ free_qpdb(dns_qpdb_t *qpdb, bool log) { isc_mem_putanddetach(&qpdb->common.mctx, qpdb, sizeof(*qpdb)); } +static void +free_qpdb(dns_qpdb_t *qpdb, bool log) { + char buf[DNS_NAME_FORMATSIZE]; + + /* + * We assume the number of remaining dead nodes is reasonably small; + * the overhead of unlinking all nodes here should be negligible. + */ + for (size_t i = 0; i < qpdb->node_lock_count; i++) { + qpdata_t *node = NULL; + + node = ISC_LIST_HEAD(qpdb->deadnodes[i]); + while (node != NULL) { + ISC_LIST_UNLINK(qpdb->deadnodes[i], node, deadlink); + node = ISC_LIST_HEAD(qpdb->deadnodes[i]); + } + } + + qpdb->quantum = (qpdb->loop != NULL) ? 100 : 0; + + dns_qpmulti_destroy(&qpdb->tree); + dns_qpmulti_destroy(&qpdb->nsec); + + if (log) { + if (dns_name_dynamic(&qpdb->common.origin)) { + dns_name_format(&qpdb->common.origin, buf, sizeof(buf)); + } else { + strlcpy(buf, "", sizeof(buf)); + } + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1), + "done free_qpdb(%s)", buf); + } + + call_rcu(&qpdb->rcu_head, free_qpdb_rcu); +} + static void qpdb_destroy(dns_db_t *arg) { dns_qpdb_t *qpdb = (dns_qpdb_t *)arg; @@ -2675,7 +2616,8 @@ mark_ancient(dns_slabheader_t *header) { * The caller must hold a tree write lock and bucketnum'th node (write) lock. */ static void -cleanup_dead_nodes(dns_qpdb_t *qpdb, int bucketnum DNS__DB_FLARG) { +cleanup_dead_nodes(dns_qpdb_t *qpdb, dbmod_t *modctx, + int bucketnum DNS__DB_FLARG) { qpdata_t *node = NULL; int count = 10; /* XXXJT: should be adjustable */ @@ -2696,7 +2638,7 @@ cleanup_dead_nodes(dns_qpdb_t *qpdb, int bucketnum DNS__DB_FLARG) { continue; } - delete_node(qpdb, node); + delete_node(node, modctx); node = ISC_LIST_HEAD(qpdb->deadnodes[bucketnum]); count--; @@ -2715,7 +2657,7 @@ cleanup_dead_nodes(dns_qpdb_t *qpdb, int bucketnum DNS__DB_FLARG) { */ static void reactivate_node(dns_qpdb_t *qpdb, qpdata_t *node, - isc_rwlocktype_t tlocktype DNS__DB_FLARG) { + dbmod_t *modctx DNS__DB_FLARG) { isc_rwlocktype_t nlocktype = isc_rwlocktype_none; isc_rwlock_t *nodelock = &qpdb->node_locks[node->locknum].lock; bool maybe_cleanup = false; @@ -2728,8 +2670,8 @@ reactivate_node(dns_qpdb_t *qpdb, qpdata_t *node, * Check if we can possibly cleanup the dead node. If so, upgrade * the node lock below to perform the cleanup. */ - if (!ISC_LIST_EMPTY(qpdb->deadnodes[node->locknum]) && - tlocktype == isc_rwlocktype_write) + if (!ISC_LIST_EMPTY(qpdb->deadnodes[node->locknum]) && modctx != NULL && + modctx->writing) { maybe_cleanup = true; } @@ -2745,7 +2687,7 @@ reactivate_node(dns_qpdb_t *qpdb, qpdata_t *node, deadlink); } if (maybe_cleanup) { - cleanup_dead_nodes(qpdb, + cleanup_dead_nodes(qpdb, modctx, node->locknum DNS__DB_FILELINE); } } @@ -2781,37 +2723,48 @@ findnode(dns_db_t *db, const dns_name_t *name, bool create, dns_qpdb_t *qpdb = (dns_qpdb_t *)db; qpdata_t *node = NULL; isc_result_t result; - isc_rwlocktype_t tlocktype = isc_rwlocktype_none; + dns_qpread_t qpr = { 0 }; + dns_qp_t *qp = NULL; - TREE_RDLOCK(&qpdb->tree_lock, &tlocktype); - result = dns_qp_lookup(qpdb->tree, name, NULL, NULL, NULL, - (void **)&node, NULL); + if (create) { + dns_qpmulti_write(qpdb->tree, &qp); + } else { + dns_qpmulti_query(qpdb->tree, &qpr); + qp = (dns_qp_t *)&qpr; + } + + result = dns_qp_lookup(qp, name, NULL, NULL, NULL, (void **)&node, + NULL); if (result != ISC_R_SUCCESS) { if (!create) { if (result == DNS_R_PARTIALMATCH) { result = ISC_R_NOTFOUND; } - goto unlock; + dns_qpread_destroy(qpdb->tree, &qpr); + return (result); } - /* - * Try to upgrade the lock and if that fails unlock then relock. - */ - TREE_FORCEUPGRADE(&qpdb->tree_lock, &tlocktype); - result = dns_qp_lookup(qpdb->tree, name, NULL, NULL, NULL, + + result = dns_qp_lookup(qp, name, NULL, NULL, NULL, (void **)&node, NULL); if (result != ISC_R_SUCCESS) { node = new_qpdata(qpdb, name); - result = dns_qp_insert(qpdb->tree, node, 0); + result = dns_qp_insert(qp, node, 0); INSIST(result == ISC_R_SUCCESS); qpdata_unref(node); } } - reactivate_node(qpdb, node, tlocktype DNS__DB_FLARG_PASS); + /* XXX: pass 'qp' so nodes can be cleaned up */ + reactivate_node(qpdb, node, isc_rwlocktype_none DNS__DB_FLARG_PASS); + + if (create) { + dns_qp_compact(qp, DNS_QPGC_MAYBE); + dns_qpmulti_commit(qpdb->tree, &qp); + } else { + dns_qpread_destroy(qpdb->tree, &qpr); + } *nodep = (dns_dbnode_t *)node; -unlock: - TREE_UNLOCK(&qpdb->tree_lock, &tlocktype); return (result); } @@ -2838,7 +2791,6 @@ detachnode(dns_db_t *db, dns_dbnode_t **targetp DNS__DB_FLARG) { bool inactive = false; db_nodelock_t *nodelock = NULL; isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - isc_rwlocktype_t tlocktype = isc_rwlocktype_none; REQUIRE(VALID_QPDB(qpdb)); REQUIRE(targetp != NULL && *targetp != NULL); @@ -2848,8 +2800,7 @@ detachnode(dns_db_t *db, dns_dbnode_t **targetp DNS__DB_FLARG) { NODE_RDLOCK(&nodelock->lock, &nlocktype); - if (decref(qpdb, node, &nlocktype, &tlocktype, true DNS__DB_FLARG_PASS)) - { + if (decref(qpdb, node, &nlocktype, NULL DNS__DB_FLARG_PASS)) { if (isc_refcount_current(&nodelock->references) == 0 && nodelock->exiting) { @@ -2858,7 +2809,6 @@ detachnode(dns_db_t *db, dns_dbnode_t **targetp DNS__DB_FLARG) { } NODE_UNLOCK(&nodelock->lock, &nlocktype); - INSIST(tlocktype == isc_rwlocktype_none); *targetp = NULL; @@ -2897,12 +2847,12 @@ createiterator(dns_db_t *db, unsigned int options ISC_ATTR_UNUSED, *qpdbiter = (qpdb_dbiterator_t){ .common.methods = &dbiterator_methods, .common.magic = DNS_DBITERATOR_MAGIC, - .paused = true, }; dns_db_attach(db, &qpdbiter->common.db); - dns_qpiter_init(qpdb->tree, &qpdbiter->iter); + dns_qpmulti_snapshot(qpdb->tree, &qpdbiter->tsnap); + dns_qpiter_init(qpdbiter->tsnap, &qpdbiter->iter); *iteratorp = (dns_dbiterator_t *)qpdbiter; return (ISC_R_SUCCESS); @@ -3437,13 +3387,13 @@ cleanup: } static void -expire_ttl_headers(dns_qpdb_t *qpdb, unsigned int locknum, - isc_rwlocktype_t *tlocktypep, isc_stdtime_t now, - bool cache_is_overmem DNS__DB_FLARG); +expire_ttl_headers(dns_qpdb_t *qpdb, unsigned int locknum, dbmod_t *modctx, + isc_stdtime_t now, bool cache_is_overmem DNS__DB_FLARG); static isc_result_t -addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, - isc_stdtime_t now, dns_rdataset_t *rdataset, unsigned int options, +addrdataset(dns_db_t *db, dns_dbnode_t *node, + dns_dbversion_t *version ISC_ATTR_UNUSED, isc_stdtime_t now, + dns_rdataset_t *rdataset, unsigned int options, dns_rdataset_t *addedrdataset DNS__DB_FLARG) { dns_qpdb_t *qpdb = (dns_qpdb_t *)db; qpdata_t *qpnode = (qpdata_t *)node; @@ -3451,15 +3401,15 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, dns_slabheader_t *newheader = NULL; isc_result_t result; bool delegating = false; - bool newnsec; - isc_rwlocktype_t tlocktype = isc_rwlocktype_none; - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; + bool newnsec = false; bool cache_is_overmem = false; + bool writing = false; + isc_rwlocktype_t nlocktype = isc_rwlocktype_none; dns_fixedname_t fixed; dns_name_t *name = NULL; + dbmod_t modctx; REQUIRE(VALID_QPDB(qpdb)); - REQUIRE(version == NULL); if (now == 0) { now = isc_stdtime_now(); @@ -3530,15 +3480,11 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, /* * Add to the auxiliary NSEC tree if we're adding an NSEC record. */ - TREE_RDLOCK(&qpdb->tree_lock, &tlocktype); if (qpnode->nsec != DNS_DB_NSEC_HAS_NSEC && rdataset->type == dns_rdatatype_nsec) { newnsec = true; - } else { - newnsec = false; } - TREE_UNLOCK(&qpdb->tree_lock, &tlocktype); /* * If we're adding a delegation type, adding to the auxiliary NSEC @@ -3551,11 +3497,23 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, cache_is_overmem = true; } if (delegating || newnsec || cache_is_overmem) { - TREE_WRLOCK(&qpdb->tree_lock, &tlocktype); + writing = true; + } + + modctx = (dbmod_t){ .writing = writing }; + + if (writing) { + dns_qpmulti_write(qpdb->tree, &modctx.tree); + if (newnsec) { + dns_qpmulti_write(qpdb->nsec, &modctx.nsec); + } + } else { + dns_qpmulti_query(qpdb->tree, &modctx.qpr); + modctx.tree = (dns_qp_t *)&modctx.qpr; } if (cache_is_overmem) { - overmem(qpdb, newheader, &tlocktype DNS__DB_FLARG_PASS); + overmem(qpdb, newheader, &modctx DNS__DB_FLARG_PASS); } NODE_WRLOCK(&qpdb->node_locks[qpnode->locknum].lock, &nlocktype); @@ -3567,27 +3525,19 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, true); } - if (tlocktype == isc_rwlocktype_write) { - cleanup_dead_nodes(qpdb, qpnode->locknum DNS__DB_FLARG_PASS); + if (writing) { + cleanup_dead_nodes(qpdb, &modctx, + qpnode->locknum DNS__DB_FLARG_PASS); } - expire_ttl_headers(qpdb, qpnode->locknum, &tlocktype, now, + expire_ttl_headers(qpdb, qpnode->locknum, &modctx, now, cache_is_overmem DNS__DB_FLARG_PASS); - /* - * If we've been holding a write lock on the tree just for - * cleaning, we can release it now. However, we still need the - * node lock. - */ - if (tlocktype == isc_rwlocktype_write && !delegating && !newnsec) { - TREE_UNLOCK(&qpdb->tree_lock, &tlocktype); - } - result = ISC_R_SUCCESS; if (newnsec) { qpdata_t *nsecnode = NULL; - result = dns_qp_getname(qpdb->nsec, name, (void **)&nsecnode, + result = dns_qp_getname(modctx.nsec, name, (void **)&nsecnode, NULL); if (result == ISC_R_SUCCESS) { result = ISC_R_SUCCESS; @@ -3595,7 +3545,7 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, INSIST(nsecnode == NULL); nsecnode = new_qpdata(qpdb, name); nsecnode->nsec = DNS_DB_NSEC_NSEC; - result = dns_qp_insert(qpdb->nsec, nsecnode, 0); + result = dns_qp_insert(modctx.nsec, nsecnode, 0); INSIST(result == ISC_R_SUCCESS); qpdata_detach(&nsecnode); } @@ -3612,10 +3562,16 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, NODE_UNLOCK(&qpdb->node_locks[qpnode->locknum].lock, &nlocktype); - if (tlocktype != isc_rwlocktype_none) { - TREE_UNLOCK(&qpdb->tree_lock, &tlocktype); + if (writing) { + dns_qp_compact(modctx.tree, DNS_QPGC_MAYBE); + dns_qpmulti_commit(qpdb->tree, &modctx.tree); + if (newnsec) { + dns_qp_compact(modctx.nsec, DNS_QPGC_MAYBE); + dns_qpmulti_commit(qpdb->nsec, &modctx.nsec); + } + } else { + dns_qpread_destroy(qpdb->tree, &modctx.qpr); } - INSIST(tlocktype == isc_rwlocktype_none); return (result); } @@ -3656,22 +3612,19 @@ static unsigned int nodecount(dns_db_t *db, dns_dbtree_t tree) { dns_qpdb_t *qpdb = (dns_qpdb_t *)db; dns_qp_memusage_t mu; - isc_rwlocktype_t tlocktype = isc_rwlocktype_none; REQUIRE(VALID_QPDB(qpdb)); - TREE_RDLOCK(&qpdb->tree_lock, &tlocktype); switch (tree) { case dns_dbtree_main: - mu = dns_qp_memusage(qpdb->tree); + mu = dns_qpmulti_memusage(qpdb->tree); break; case dns_dbtree_nsec: - mu = dns_qp_memusage(qpdb->nsec); + mu = dns_qpmulti_memusage(qpdb->nsec); break; default: UNREACHABLE(); } - TREE_UNLOCK(&qpdb->tree_lock, &tlocktype); return (mu.leaves); } @@ -3701,7 +3654,7 @@ getoriginnode(dns_db_t *db, dns_dbnode_t **nodep DNS__DB_FLARG) { REQUIRE(VALID_QPDB(qpdb)); REQUIRE(nodep != NULL && *nodep == NULL); - /* Note that the access to origin_node doesn't require a DB lock */ + /* Note that the modctx to origin_node doesn't require a DB lock */ onode = (qpdata_t *)qpdb->origin_node; if (onode != NULL) { newref(qpdb, onode, isc_rwlocktype_none DNS__DB_FLARG_PASS); @@ -3763,7 +3716,6 @@ dns__qpcache_create(isc_mem_t *mctx, const dns_name_t *origin, } isc_rwlock_init(&qpdb->lock); - TREE_INITLOCK(&qpdb->tree_lock); qpdb->node_locks = isc_mem_cget(mctx, qpdb->node_lock_count, sizeof(db_nodelock_t)); @@ -3820,8 +3772,8 @@ dns__qpcache_create(isc_mem_t *mctx, const dns_name_t *origin, /* * Make the qp tries. */ - dns_qp_create(mctx, &qpmethods, qpdb, &qpdb->tree); - dns_qp_create(mctx, &qpmethods, qpdb, &qpdb->nsec); + dns_qpmulti_create(mctx, &qpmethods, qpdb, &qpdb->tree); + dns_qpmulti_create(mctx, &qpmethods, qpdb, &qpdb->nsec); qpdb->common.magic = DNS_DB_MAGIC; qpdb->common.impmagic = QPDB_MAGIC; @@ -4044,8 +3996,7 @@ reference_iter_node(qpdb_dbiterator_t *qpdbiter DNS__DB_FLARG) { return; } - INSIST(qpdbiter->tree_locked != isc_rwlocktype_none); - reactivate_node(qpdb, node, qpdbiter->tree_locked DNS__DB_FLARG_PASS); + reactivate_node(qpdb, node, isc_rwlocktype_none DNS__DB_FLARG_PASS); } static void @@ -4054,70 +4005,32 @@ dereference_iter_node(qpdb_dbiterator_t *qpdbiter DNS__DB_FLARG) { qpdata_t *node = qpdbiter->node; isc_rwlock_t *lock = NULL; isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - isc_rwlocktype_t tlocktype = qpdbiter->tree_locked; if (node == NULL) { return; } - REQUIRE(tlocktype != isc_rwlocktype_write); - lock = &qpdb->node_locks[node->locknum].lock; NODE_RDLOCK(lock, &nlocktype); - decref(qpdb, node, &nlocktype, &qpdbiter->tree_locked, - false DNS__DB_FLARG_PASS); + decref(qpdb, node, &nlocktype, NULL DNS__DB_FLARG_PASS); NODE_UNLOCK(lock, &nlocktype); - INSIST(qpdbiter->tree_locked == tlocktype); - qpdbiter->node = NULL; } -static void -resume_iteration(qpdb_dbiterator_t *qpdbiter, bool continuing) { - dns_qpdb_t *qpdb = (dns_qpdb_t *)qpdbiter->common.db; - - REQUIRE(qpdbiter->paused); - REQUIRE(qpdbiter->tree_locked == isc_rwlocktype_none); - - TREE_RDLOCK(&qpdb->tree_lock, &qpdbiter->tree_locked); - - /* - * If we're being called from dbiterator_next or _prev, - * then we may need to reinitialize the iterator to the current - * name. The tree could have changed while it was unlocked, - * would make the iterator traversal inconsistent. - * - * As long as the iterator is holding a reference to - * qpdbiter->node, the node won't be removed from the tree, - * so the lookup should always succeed. - */ - if (continuing && qpdbiter->node != NULL) { - isc_result_t result; - result = dns_qp_lookup(qpdb->tree, NULL, NULL, &qpdbiter->iter, - NULL, NULL, NULL); - INSIST(result == ISC_R_SUCCESS); - } - - qpdbiter->paused = false; -} - static void dbiterator_destroy(dns_dbiterator_t **iteratorp DNS__DB_FLARG) { qpdb_dbiterator_t *qpdbiter = (qpdb_dbiterator_t *)(*iteratorp); - dns_qpdb_t *qpdb = (dns_qpdb_t *)qpdbiter->common.db; dns_db_t *db = NULL; - if (qpdbiter->tree_locked == isc_rwlocktype_read) { - TREE_UNLOCK(&qpdb->tree_lock, &qpdbiter->tree_locked); - } - INSIST(qpdbiter->tree_locked == isc_rwlocktype_none); - dereference_iter_node(qpdbiter DNS__DB_FLARG_PASS); dns_db_attach(qpdbiter->common.db, &db); dns_db_detach(&qpdbiter->common.db); + dns_qpdb_t *qpdb = (dns_qpdb_t *)db; + dns_qpsnap_destroy(qpdb->tree, &qpdbiter->tsnap); + isc_mem_put(db->mctx, qpdbiter, sizeof(*qpdbiter)); dns_db_detach(&db); @@ -4128,7 +4041,6 @@ static isc_result_t dbiterator_first(dns_dbiterator_t *iterator DNS__DB_FLARG) { isc_result_t result; qpdb_dbiterator_t *qpdbiter = (qpdb_dbiterator_t *)iterator; - dns_qpdb_t *qpdb = (dns_qpdb_t *)iterator->db; if (qpdbiter->result != ISC_R_SUCCESS && qpdbiter->result != ISC_R_NOTFOUND && @@ -4138,13 +4050,9 @@ dbiterator_first(dns_dbiterator_t *iterator DNS__DB_FLARG) { return (qpdbiter->result); } - if (qpdbiter->paused) { - resume_iteration(qpdbiter, false); - } - dereference_iter_node(qpdbiter DNS__DB_FLARG_PASS); - dns_qpiter_init(qpdb->tree, &qpdbiter->iter); + dns_qpiter_init(qpdbiter->tsnap, &qpdbiter->iter); result = dns_qpiter_next(&qpdbiter->iter, NULL, (void **)&qpdbiter->node, NULL); @@ -4157,10 +4065,6 @@ dbiterator_first(dns_dbiterator_t *iterator DNS__DB_FLARG) { qpdbiter->result = result; - if (result != ISC_R_SUCCESS) { - ENSURE(!qpdbiter->paused); - } - return (result); } @@ -4168,7 +4072,6 @@ static isc_result_t dbiterator_last(dns_dbiterator_t *iterator DNS__DB_FLARG) { isc_result_t result; qpdb_dbiterator_t *qpdbiter = (qpdb_dbiterator_t *)iterator; - dns_qpdb_t *qpdb = (dns_qpdb_t *)iterator->db; if (qpdbiter->result != ISC_R_SUCCESS && qpdbiter->result != ISC_R_NOTFOUND && @@ -4178,13 +4081,9 @@ dbiterator_last(dns_dbiterator_t *iterator DNS__DB_FLARG) { return (qpdbiter->result); } - if (qpdbiter->paused) { - resume_iteration(qpdbiter, false); - } - dereference_iter_node(qpdbiter DNS__DB_FLARG_PASS); - dns_qpiter_init(qpdb->tree, &qpdbiter->iter); + dns_qpiter_init(qpdbiter->tsnap, &qpdbiter->iter); result = dns_qpiter_prev(&qpdbiter->iter, NULL, (void **)&qpdbiter->node, NULL); @@ -4204,7 +4103,6 @@ dbiterator_seek(dns_dbiterator_t *iterator, const dns_name_t *name DNS__DB_FLARG) { isc_result_t result; qpdb_dbiterator_t *qpdbiter = (qpdb_dbiterator_t *)iterator; - dns_qpdb_t *qpdb = (dns_qpdb_t *)iterator->db; if (qpdbiter->result != ISC_R_SUCCESS && qpdbiter->result != ISC_R_NOTFOUND && @@ -4214,14 +4112,10 @@ dbiterator_seek(dns_dbiterator_t *iterator, return (qpdbiter->result); } - if (qpdbiter->paused) { - resume_iteration(qpdbiter, false); - } - dereference_iter_node(qpdbiter DNS__DB_FLARG_PASS); - result = dns_qp_lookup(qpdb->tree, name, NULL, &qpdbiter->iter, NULL, - (void **)&qpdbiter->node, NULL); + result = dns_qp_lookup(qpdbiter->tsnap, name, NULL, &qpdbiter->iter, + NULL, (void **)&qpdbiter->node, NULL); if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) { reference_iter_node(qpdbiter DNS__DB_FLARG_PASS); @@ -4245,10 +4139,6 @@ dbiterator_prev(dns_dbiterator_t *iterator DNS__DB_FLARG) { return (qpdbiter->result); } - if (qpdbiter->paused) { - resume_iteration(qpdbiter, false); - } - dereference_iter_node(qpdbiter DNS__DB_FLARG_PASS); result = dns_qpiter_prev(&qpdbiter->iter, NULL, @@ -4276,10 +4166,6 @@ dbiterator_next(dns_dbiterator_t *iterator DNS__DB_FLARG) { return (qpdbiter->result); } - if (qpdbiter->paused) { - resume_iteration(qpdbiter, false); - } - dereference_iter_node(qpdbiter DNS__DB_FLARG_PASS); result = dns_qpiter_next(&qpdbiter->iter, NULL, @@ -4306,10 +4192,6 @@ dbiterator_current(dns_dbiterator_t *iterator, dns_dbnode_t **nodep, REQUIRE(qpdbiter->result == ISC_R_SUCCESS); REQUIRE(node != NULL); - if (qpdbiter->paused) { - resume_iteration(qpdbiter, false); - } - if (name != NULL) { dns_name_copy(&node->name, name); } @@ -4321,29 +4203,7 @@ dbiterator_current(dns_dbiterator_t *iterator, dns_dbnode_t **nodep, } static isc_result_t -dbiterator_pause(dns_dbiterator_t *iterator) { - dns_qpdb_t *qpdb = (dns_qpdb_t *)iterator->db; - qpdb_dbiterator_t *qpdbiter = (qpdb_dbiterator_t *)iterator; - - if (qpdbiter->result != ISC_R_SUCCESS && - qpdbiter->result != ISC_R_NOTFOUND && - qpdbiter->result != DNS_R_PARTIALMATCH && - qpdbiter->result != ISC_R_NOMORE) - { - return (qpdbiter->result); - } - - if (qpdbiter->paused) { - return (ISC_R_SUCCESS); - } - - qpdbiter->paused = true; - - if (qpdbiter->tree_locked == isc_rwlocktype_read) { - TREE_UNLOCK(&qpdb->tree_lock, &qpdbiter->tree_locked); - } - INSIST(qpdbiter->tree_locked == isc_rwlocktype_none); - +dbiterator_pause(dns_dbiterator_t *iterator ISC_ATTR_UNUSED) { return (ISC_R_SUCCESS); } @@ -4389,9 +4249,8 @@ deletedata(dns_db_t *db ISC_ATTR_UNUSED, dns_dbnode_t *node ISC_ATTR_UNUSED, * Caller must be holding the node write lock. */ static void -expire_ttl_headers(dns_qpdb_t *qpdb, unsigned int locknum, - isc_rwlocktype_t *tlocktypep, isc_stdtime_t now, - bool cache_is_overmem DNS__DB_FLARG) { +expire_ttl_headers(dns_qpdb_t *qpdb, unsigned int locknum, dbmod_t *modctx, + isc_stdtime_t now, bool cache_is_overmem DNS__DB_FLARG) { isc_heap_t *heap = qpdb->heaps[locknum]; for (size_t i = 0; i < DNS_QPDB_EXPIRE_TTL_COUNT; i++) { @@ -4419,8 +4278,7 @@ expire_ttl_headers(dns_qpdb_t *qpdb, unsigned int locknum, return; } - expireheader(header, tlocktypep, - dns_expire_ttl DNS__DB_FLARG_PASS); + expireheader(header, modctx, dns_expire_ttl DNS__DB_FLARG_PASS); } }