isc_mutex_t lock;
isc_mem_t *mctx; /* Main cache memory */
isc_mem_t *hmctx; /* Heap memory */
+ isc_mem_t *tmctx; /* Tree memory */
isc_taskmgr_t *taskmgr;
char *name;
isc_refcount_t references;
water(void *arg, int mark);
static isc_result_t
-cache_create_db(dns_cache_t *cache, dns_db_t **db) {
+cache_create_db(dns_cache_t *cache, dns_db_t **dbp, isc_mem_t **tmctxp,
+ isc_mem_t **hmctxp) {
isc_result_t result;
isc_task_t *dbtask = NULL;
isc_task_t *prunetask = NULL;
+ dns_db_t *db = NULL;
+ isc_mem_t *tmctx = NULL;
+ isc_mem_t *hmctx = NULL;
+
+ REQUIRE(dbp != NULL && *dbp == NULL);
+ REQUIRE(tmctxp != NULL && *tmctxp == NULL);
+ REQUIRE(hmctxp != NULL && *hmctxp == NULL);
- result = dns_db_create(cache->mctx, cache->db_type, dns_rootname,
+ /*
+ * This will be the cache memory context, which is subject
+ * to cleaning when the configured memory limits are exceeded.
+ */
+ isc_mem_create(&tmctx);
+ isc_mem_setname(tmctx, "cache");
+
+ /*
+ * This will be passed to RBTDB to use for heaps. This is separate
+ * from the main cache memory because it can grow quite large under
+ * heavy load and could otherwise cause the cache to be cleaned too
+ * aggressively.
+ */
+ isc_mem_create(&hmctx);
+ isc_mem_setname(hmctx, "cache_heap");
+
+ if (strcmp(cache->db_type, "rbt") == 0) {
+ cache->db_argv[0] = (char *)hmctx;
+ }
+ result = dns_db_create(tmctx, cache->db_type, dns_rootname,
dns_dbtype_cache, cache->rdclass, cache->db_argc,
- cache->db_argv, db);
+ cache->db_argv, &db);
if (result != ISC_R_SUCCESS) {
- return (result);
+ goto cleanup_mctx;
}
- dns_db_setservestalettl(*db, cache->serve_stale_ttl);
- dns_db_setservestalerefresh(*db, cache->serve_stale_refresh);
+ dns_db_setservestalettl(db, cache->serve_stale_ttl);
+ dns_db_setservestalerefresh(db, cache->serve_stale_refresh);
if (cache->taskmgr == NULL) {
+ *dbp = db;
+ *tmctxp = tmctx;
+ *hmctxp = hmctx;
return (ISC_R_SUCCESS);
}
}
isc_task_setname(prunetask, "cache_prunetask", NULL);
- dns_db_settask(*db, dbtask, prunetask);
+ dns_db_settask(db, dbtask, prunetask);
isc_task_detach(&prunetask);
isc_task_detach(&dbtask);
+ *dbp = db;
+ *tmctxp = tmctx;
+ *hmctxp = hmctx;
+
return (ISC_R_SUCCESS);
cleanup_dbtask:
isc_task_detach(&dbtask);
cleanup_db:
- dns_db_detach(db);
+ dns_db_detach(&db);
+cleanup_mctx:
+ isc_mem_detach(&tmctx);
+ isc_mem_detach(&hmctx);
return (result);
}
+static void
+cache_free(dns_cache_t *cache) {
+ REQUIRE(VALID_CACHE(cache));
+
+ isc_refcount_destroy(&cache->references);
+ isc_refcount_destroy(&cache->live_tasks);
+
+ isc_mem_clearwater(cache->tmctx);
+
+ if (cache->cleaner.task != NULL) {
+ isc_task_detach(&cache->cleaner.task);
+ }
+
+ if (cache->cleaner.overmem_event != NULL) {
+ isc_event_free(&cache->cleaner.overmem_event);
+ }
+
+ if (cache->cleaner.resched_event != NULL) {
+ isc_event_free(&cache->cleaner.resched_event);
+ }
+
+ if (cache->cleaner.iterator != NULL) {
+ dns_dbiterator_destroy(&cache->cleaner.iterator);
+ }
+
+ isc_mutex_destroy(&cache->cleaner.lock);
+
+ if (cache->db != NULL) {
+ dns_db_detach(&cache->db);
+ }
+
+ if (cache->db_argv != NULL) {
+ /*
+ * We don't free db_argv[0] in "rbt" cache databases
+ * as it's a pointer to hmctx
+ */
+ int extra = 0;
+ if (strcmp(cache->db_type, "rbt") == 0) {
+ extra = 1;
+ cache->db_argv[0] = NULL;
+ }
+ for (int i = extra; i < cache->db_argc; i++) {
+ if (cache->db_argv[i] != NULL) {
+ isc_mem_free(cache->mctx, cache->db_argv[i]);
+ }
+ }
+ isc_mem_put(cache->mctx, cache->db_argv,
+ cache->db_argc * sizeof(char *));
+ }
+
+ if (cache->db_type != NULL) {
+ isc_mem_free(cache->mctx, cache->db_type);
+ }
+
+ if (cache->name != NULL) {
+ isc_mem_free(cache->mctx, cache->name);
+ }
+
+ if (cache->stats != NULL) {
+ isc_stats_detach(&cache->stats);
+ }
+
+ if (cache->taskmgr != NULL) {
+ isc_taskmgr_detach(&cache->taskmgr);
+ }
+
+ isc_mutex_destroy(&cache->lock);
+
+ cache->magic = 0;
+ if (cache->hmctx != NULL) {
+ isc_mem_detach(&cache->hmctx);
+ }
+ if (cache->tmctx != NULL) {
+ isc_mem_detach(&cache->tmctx);
+ }
+ isc_mem_putanddetach(&cache->mctx, cache, sizeof(*cache));
+}
+
isc_result_t
-dns_cache_create(isc_mem_t *cmctx, isc_mem_t *hmctx, isc_taskmgr_t *taskmgr,
+dns_cache_create(isc_mem_t *cmctx, isc_taskmgr_t *taskmgr,
isc_timermgr_t *timermgr, dns_rdataclass_t rdclass,
const char *cachename, const char *db_type,
unsigned int db_argc, char **db_argv, dns_cache_t **cachep) {
REQUIRE(cachep != NULL);
REQUIRE(*cachep == NULL);
REQUIRE(cmctx != NULL);
- REQUIRE(hmctx != NULL);
REQUIRE(taskmgr != NULL || strcmp(db_type, "rbt") != 0);
REQUIRE(cachename != NULL);
cache = isc_mem_get(cmctx, sizeof(*cache));
+ *cache = (dns_cache_t){
+ .db_type = isc_mem_strdup(cmctx, db_type),
+ .rdclass = rdclass,
+ .db_argc = db_argc,
+ .name = cachename == NULL ? NULL
+ : isc_mem_strdup(cmctx, cachename),
+ .magic = CACHE_MAGIC,
+ };
- cache->mctx = cache->hmctx = NULL;
isc_mem_attach(cmctx, &cache->mctx);
- isc_mem_attach(hmctx, &cache->hmctx);
- cache->taskmgr = NULL;
if (taskmgr != NULL) {
isc_taskmgr_attach(taskmgr, &cache->taskmgr);
}
- cache->name = NULL;
- if (cachename != NULL) {
- cache->name = isc_mem_strdup(cmctx, cachename);
- }
-
isc_mutex_init(&cache->lock);
-
isc_refcount_init(&cache->references, 1);
isc_refcount_init(&cache->live_tasks, 1);
- cache->rdclass = rdclass;
- cache->serve_stale_ttl = 0;
- cache->stats = NULL;
result = isc_stats_create(cmctx, &cache->stats,
dns_cachestatscounter_max);
if (result != ISC_R_SUCCESS) {
- goto cleanup_lock;
+ goto cleanup;
}
- cache->db_type = isc_mem_strdup(cmctx, db_type);
-
/*
* For databases of type "rbt" we pass hmctx to dns_db_create()
* via cache->db_argv, followed by the rest of the arguments in
*/
if (strcmp(cache->db_type, "rbt") == 0) {
extra = 1;
+ cache->db_argc++;
}
- cache->db_argc = db_argc + extra;
- cache->db_argv = NULL;
-
if (cache->db_argc != 0) {
cache->db_argv = isc_mem_get(cmctx,
cache->db_argc * sizeof(char *));
cache->db_argv[i] = NULL;
}
- cache->db_argv[0] = (char *)hmctx;
for (i = extra; i < cache->db_argc; i++) {
cache->db_argv[i] = isc_mem_strdup(cmctx,
db_argv[i - extra]);
/*
* Create the database
*/
- cache->db = NULL;
- result = cache_create_db(cache, &cache->db);
+ result = cache_create_db(cache, &cache->db, &cache->tmctx,
+ &cache->hmctx);
if (result != ISC_R_SUCCESS) {
- goto cleanup_dbargv;
+ goto cleanup;
}
- cache->magic = CACHE_MAGIC;
/*
* RBT-type cache DB has its own mechanism of cache cleaning and doesn't
&cache->cleaner);
}
if (result != ISC_R_SUCCESS) {
- goto cleanup_db;
+ goto cleanup;
}
result = dns_db_setcachestats(cache->db, cache->stats);
if (result != ISC_R_SUCCESS) {
- goto cleanup_db;
+ goto cleanup;
}
*cachep = cache;
return (ISC_R_SUCCESS);
-cleanup_db:
- dns_db_detach(&cache->db);
-cleanup_dbargv:
- for (i = extra; i < cache->db_argc; i++) {
- if (cache->db_argv[i] != NULL) {
- isc_mem_free(cmctx, cache->db_argv[i]);
- }
- }
- if (cache->db_argv != NULL) {
- isc_mem_put(cmctx, cache->db_argv,
- cache->db_argc * sizeof(char *));
- }
- isc_mem_free(cmctx, cache->db_type);
- isc_stats_detach(&cache->stats);
-cleanup_lock:
- isc_mutex_destroy(&cache->lock);
- if (cache->name != NULL) {
- isc_mem_free(cmctx, cache->name);
- }
- if (cache->taskmgr != NULL) {
- isc_taskmgr_detach(&cache->taskmgr);
- }
- isc_mem_detach(&cache->hmctx);
- isc_mem_putanddetach(&cache->mctx, cache, sizeof(*cache));
+cleanup:
+ cache_free(cache);
return (result);
}
-static void
-cache_free(dns_cache_t *cache) {
- REQUIRE(VALID_CACHE(cache));
-
- isc_refcount_destroy(&cache->references);
- isc_refcount_destroy(&cache->live_tasks);
-
- isc_mem_clearwater(cache->mctx);
-
- if (cache->cleaner.task != NULL) {
- isc_task_detach(&cache->cleaner.task);
- }
-
- if (cache->cleaner.overmem_event != NULL) {
- isc_event_free(&cache->cleaner.overmem_event);
- }
-
- if (cache->cleaner.resched_event != NULL) {
- isc_event_free(&cache->cleaner.resched_event);
- }
-
- if (cache->cleaner.iterator != NULL) {
- dns_dbiterator_destroy(&cache->cleaner.iterator);
- }
-
- isc_mutex_destroy(&cache->cleaner.lock);
-
- if (cache->db != NULL) {
- dns_db_detach(&cache->db);
- }
-
- if (cache->db_argv != NULL) {
- /*
- * We don't free db_argv[0] in "rbt" cache databases
- * as it's a pointer to hmctx
- */
- int extra = 0;
- if (strcmp(cache->db_type, "rbt") == 0) {
- extra = 1;
- }
- for (int i = extra; i < cache->db_argc; i++) {
- if (cache->db_argv[i] != NULL) {
- isc_mem_free(cache->mctx, cache->db_argv[i]);
- }
- }
- isc_mem_put(cache->mctx, cache->db_argv,
- cache->db_argc * sizeof(char *));
- }
-
- if (cache->db_type != NULL) {
- isc_mem_free(cache->mctx, cache->db_type);
- }
-
- if (cache->name != NULL) {
- isc_mem_free(cache->mctx, cache->name);
- }
-
- if (cache->stats != NULL) {
- isc_stats_detach(&cache->stats);
- }
-
- if (cache->taskmgr != NULL) {
- isc_taskmgr_detach(&cache->taskmgr);
- }
-
- isc_mutex_destroy(&cache->lock);
-
- cache->magic = 0;
- isc_mem_detach(&cache->hmctx);
- isc_mem_putanddetach(&cache->mctx, cache, sizeof(*cache));
-}
-
void
dns_cache_attach(dns_cache_t *cache, dns_cache_t **targetp) {
REQUIRE(VALID_CACHE(cache));
if (overmem != cache->cleaner.overmem) {
dns_db_overmem(cache->db, overmem);
cache->cleaner.overmem = overmem;
- isc_mem_waterack(cache->mctx, mark);
+ isc_mem_waterack(cache->tmctx, mark);
}
if (cache->cleaner.overmem_event != NULL) {
UNLOCK(&cache->cleaner.lock);
}
+static void
+updatewater(dns_cache_t *cache) {
+ size_t hi = cache->size - (cache->size >> 3); /* ~ 7/8ths. */
+ size_t lo = cache->size - (cache->size >> 2); /* ~ 3/4ths. */
+ if (cache->size == 0U || hi == 0U || lo == 0U) {
+ isc_mem_clearwater(cache->tmctx);
+ } else {
+ isc_mem_setwater(cache->tmctx, water, cache, hi, lo);
+ }
+}
+
void
dns_cache_setcachesize(dns_cache_t *cache, size_t size) {
- size_t hiwater, lowater;
-
REQUIRE(VALID_CACHE(cache));
/*
LOCK(&cache->lock);
cache->size = size;
+ updatewater(cache);
UNLOCK(&cache->lock);
-
- hiwater = size - (size >> 3); /* Approximately 7/8ths. */
- lowater = size - (size >> 2); /* Approximately 3/4ths. */
-
- /*
- * If the cache was overmem and cleaning, but now with the new limits
- * it is no longer in an overmem condition, then the next
- * isc_mem_put for cache memory will do the right thing and trigger
- * water().
- */
-
- if (size == 0U || hiwater == 0U || lowater == 0U) {
- /*
- * Disable cache memory limiting.
- */
- isc_mem_clearwater(cache->mctx);
- } else {
- /*
- * Establish new cache memory limits (either for the first
- * time, or replacing other limits).
- */
- isc_mem_setwater(cache->mctx, water, cache, hiwater, lowater);
- }
}
size_t
dns_db_t *db = NULL, *olddb;
dns_dbiterator_t *dbiterator = NULL, *olddbiterator = NULL;
isc_result_t result;
+ isc_mem_t *tmctx = NULL, *oldtmctx;
+ isc_mem_t *hmctx = NULL, *oldhmctx;
- result = cache_create_db(cache, &db);
+ result = cache_create_db(cache, &db, &tmctx, &hmctx);
if (result != ISC_R_SUCCESS) {
return (result);
}
result = dns_db_createiterator(db, false, &dbiterator);
if (result != ISC_R_SUCCESS) {
dns_db_detach(&db);
+ isc_mem_detach(&tmctx);
+ isc_mem_detach(&hmctx);
return (result);
}
LOCK(&cache->lock);
LOCK(&cache->cleaner.lock);
+ isc_mem_clearwater(cache->tmctx);
if (cache->cleaner.state == cleaner_s_idle) {
olddbiterator = cache->cleaner.iterator;
cache->cleaner.iterator = dbiterator;
}
cache->cleaner.replaceiterator = true;
}
+ oldhmctx = cache->hmctx;
+ cache->hmctx = hmctx;
+ oldtmctx = cache->tmctx;
+ cache->tmctx = tmctx;
+ updatewater(cache);
olddb = cache->db;
cache->db = db;
dns_db_setcachestats(cache->db, cache->stats);
dns_dbiterator_destroy(&olddbiterator);
}
dns_db_detach(&olddb);
+ isc_mem_detach(&oldhmctx);
+ isc_mem_detach(&oldtmctx);
return (ISC_R_SUCCESS);
}
fprintf(fp, "%20" PRIu64 " %s\n", (uint64_t)dns_db_hashsize(cache->db),
"cache database hash buckets");
- fprintf(fp, "%20" PRIu64 " %s\n", (uint64_t)isc_mem_total(cache->mctx),
+ fprintf(fp, "%20" PRIu64 " %s\n", (uint64_t)isc_mem_total(cache->tmctx),
"cache tree memory total");
- fprintf(fp, "%20" PRIu64 " %s\n", (uint64_t)isc_mem_inuse(cache->mctx),
+ fprintf(fp, "%20" PRIu64 " %s\n", (uint64_t)isc_mem_inuse(cache->tmctx),
"cache tree memory in use");
fprintf(fp, "%20" PRIu64 " %s\n",
- (uint64_t)isc_mem_maxinuse(cache->mctx),
+ (uint64_t)isc_mem_maxinuse(cache->tmctx),
"cache tree highest memory in use");
fprintf(fp, "%20" PRIu64 " %s\n", (uint64_t)isc_mem_total(cache->hmctx),
dns_db_nodecount(cache->db, dns_dbtree_nsec), writer));
TRY0(renderstat("CacheBuckets", dns_db_hashsize(cache->db), writer));
- TRY0(renderstat("TreeMemTotal", isc_mem_total(cache->mctx), writer));
- TRY0(renderstat("TreeMemInUse", isc_mem_inuse(cache->mctx), writer));
- TRY0(renderstat("TreeMemMax", isc_mem_maxinuse(cache->mctx), writer));
+ TRY0(renderstat("TreeMemTotal", isc_mem_total(cache->tmctx), writer));
+ TRY0(renderstat("TreeMemInUse", isc_mem_inuse(cache->tmctx), writer));
+ TRY0(renderstat("TreeMemMax", isc_mem_maxinuse(cache->tmctx), writer));
TRY0(renderstat("HeapMemTotal", isc_mem_total(cache->hmctx), writer));
TRY0(renderstat("HeapMemInUse", isc_mem_inuse(cache->hmctx), writer));
CHECKMEM(obj);
json_object_object_add(cstats, "CacheBuckets", obj);
- obj = json_object_new_int64(isc_mem_total(cache->mctx));
+ obj = json_object_new_int64(isc_mem_total(cache->tmctx));
CHECKMEM(obj);
json_object_object_add(cstats, "TreeMemTotal", obj);
- obj = json_object_new_int64(isc_mem_inuse(cache->mctx));
+ obj = json_object_new_int64(isc_mem_inuse(cache->tmctx));
CHECKMEM(obj);
json_object_object_add(cstats, "TreeMemInUse", obj);
- obj = json_object_new_int64(isc_mem_maxinuse(cache->mctx));
+ obj = json_object_new_int64(isc_mem_maxinuse(cache->tmctx));
CHECKMEM(obj);
json_object_object_add(cstats, "TreeMemMax", obj);