summaryrefslogtreecommitdiff
path: root/lib/dns/cache.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dns/cache.c')
-rw-r--r--lib/dns/cache.c256
1 files changed, 192 insertions, 64 deletions
diff --git a/lib/dns/cache.c b/lib/dns/cache.c
index 60423212..51beaac0 100644
--- a/lib/dns/cache.c
+++ b/lib/dns/cache.c
@@ -1,21 +1,21 @@
/*
* Copyright (C) 1999, 2000 Internet Software Consortium.
- *
+ *
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
- * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
- * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
- * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
- * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
- * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
- * SOFTWARE.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+ * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: cache.c,v 1.23.2.2 2000/08/18 22:47:39 bwelling Exp $ */
+/* $Id: cache.c,v 1.30 2000/08/31 12:15:08 marka Exp $ */
#include <config.h>
@@ -41,7 +41,7 @@
/*
* A cache_cleaner_t encapsulsates the state of the periodic
- * cache cleaning.
+ * cache cleaning.
*/
typedef struct cache_cleaner cache_cleaner_t;
@@ -67,17 +67,19 @@ struct cache_cleaner {
unsigned int cleaning_interval; /* The cleaning-interval from
named.conf, in seconds. */
isc_timer_t *cleaning_timer;
- isc_event_t *resched_event; /* Sent by cleaner task to
+ isc_event_t *resched_event; /* Sent by cleaner task to
itself to reschedule */
+ isc_event_t *overmem_event;
dns_dbiterator_t *iterator;
- int increment; /* Number of names to
+ int increment; /* Number of names to
clean in one increment */
cleaner_state_t state; /* Idle/Busy. */
+ isc_boolean_t overmem; /* The cache is in a overmem state */
};
/*
- * The actual cache object.
+ * The actual cache object.
*/
struct dns_cache {
@@ -89,12 +91,12 @@ struct dns_cache {
/* Locked by 'lock'. */
int references;
- int live_tasks;
+ int live_tasks;
dns_rdataclass_t rdclass;
dns_db_t *db;
cache_cleaner_t cleaner;
- /* Locked by 'filelock'. */
+ /* Locked by 'filelock'. */
char * filename;
/* Access to the on-disk cache file is also locked by 'filelock'. */
};
@@ -117,7 +119,10 @@ incremental_cleaning_action(isc_task_t *task, isc_event_t *event);
static void
cleaner_shutdown_action(isc_task_t *task, isc_event_t *event);
-isc_result_t
+static void
+overmem_cleaning_action(isc_task_t *task, isc_event_t *event);
+
+isc_result_t
dns_cache_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
isc_timermgr_t *timermgr, dns_rdataclass_t rdclass,
const char *db_type, unsigned int db_argc, char **db_argv,
@@ -157,8 +162,8 @@ dns_cache_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
if (result != ISC_R_SUCCESS)
goto cleanup_mutex;
- cache->filename = NULL;
-
+ cache->filename = NULL;
+
cache->magic = CACHE_MAGIC;
result = cache_cleaner_init(cache, taskmgr, timermgr,
@@ -172,23 +177,28 @@ dns_cache_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
cleanup_db:
dns_db_detach(&cache->db);
cleanup_mutex:
- isc_mutex_destroy(&cache->lock);
+ DESTROYLOCK(&cache->lock);
cleanup_mem:
isc_mem_put(mctx, cache, sizeof *cache);
isc_mem_detach(&mctx);
return (result);
}
-static void
+static void
cache_free(dns_cache_t *cache) {
isc_mem_t *mctx;
REQUIRE(VALID_CACHE(cache));
REQUIRE(cache->references == 0);
+ isc_mem_setwater(cache->mctx, NULL, NULL, 0, 0);
+
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);
@@ -203,12 +213,12 @@ cache_free(dns_cache_t *cache) {
if (cache->db)
dns_db_detach(&cache->db);
- isc_mutex_destroy(&cache->lock);
- cache->magic = 0;
+ DESTROYLOCK(&cache->lock);
+ cache->magic = 0;
mctx = cache->mctx;
- isc_mem_put(cache->mctx, cache, sizeof *cache);
+ isc_mem_put(cache->mctx, cache, sizeof *cache);
isc_mem_detach(&mctx);
-}
+}
void
@@ -236,8 +246,10 @@ dns_cache_detach(dns_cache_t **cachep) {
LOCK(&cache->lock);
REQUIRE(cache->references > 0);
cache->references--;
- if (cache->references == 0)
+ if (cache->references == 0) {
+ cache->cleaner.overmem = ISC_FALSE;
free_cache = ISC_TRUE;
+ }
UNLOCK(&cache->lock);
*cachep = NULL;
if (free_cache) {
@@ -267,11 +279,11 @@ dns_cache_setfilename(dns_cache_t *cahce, char *filename) {
char *newname = isc_mem_strdup(filename);
if (newname == NULL)
return (ISC_R_NOMEMORY);
- LOCK(&cache->filelock);
+ LOCK(&cache->filelock);
if (cache->filename)
isc_mem_free(cache->mctx, cache->filename);
cache->filename = newname;
- UNLOCK(&cache->filelock);
+ UNLOCK(&cache->filelock);
return (ISC_R_SUCCESS);
}
@@ -283,7 +295,7 @@ dns_cache_load(dns_cache_t *cache) {
LOCK(&cache->filelock);
/* XXX handle TTLs in a way appropriate for the cache */
result = dns_db_load(cache->db, cache->filename);
- UNLOCK(&cache->filelock);
+ UNLOCK(&cache->filelock);
return (result);
}
@@ -323,12 +335,12 @@ dns_cache_setcleaninginterval(dns_cache_t *cache, unsigned int t) {
}
/*
- * Initialize the cache cleaner object at *cleaner.
+ * Initialize the cache cleaner object at *cleaner.
* Space for the object must be allocated by the caller.
*/
static isc_result_t
-cache_cleaner_init(dns_cache_t *cache, isc_taskmgr_t *taskmgr,
+cache_cleaner_init(dns_cache_t *cache, isc_taskmgr_t *taskmgr,
isc_timermgr_t *timermgr, cache_cleaner_t *cleaner)
{
isc_result_t result;
@@ -337,11 +349,13 @@ cache_cleaner_init(dns_cache_t *cache, isc_taskmgr_t *taskmgr,
cleaner->state = cleaner_s_idle;
cleaner->cache = cache;
cleaner->iterator = NULL;
-
+ cleaner->overmem = ISC_FALSE;
+
cleaner->task = NULL;
cleaner->cleaning_timer = NULL;
cleaner->resched_event = NULL;
-
+ cleaner->overmem_event = NULL;
+
if (taskmgr != NULL && timermgr != NULL) {
result = isc_task_create(taskmgr, 1, &cleaner->task);
if (result != ISC_R_SUCCESS) {
@@ -363,7 +377,7 @@ cache_cleaner_init(dns_cache_t *cache, isc_taskmgr_t *taskmgr,
dns_result_totext(result));
goto cleanup;
}
-
+
cleaner->cleaning_interval = 0; /* Initially turned off. */
result = isc_timer_create(timermgr, isc_timertype_inactive,
NULL, NULL,
@@ -377,7 +391,7 @@ cache_cleaner_init(dns_cache_t *cache, isc_taskmgr_t *taskmgr,
result = ISC_R_UNEXPECTED;
goto cleanup;
}
-
+
cleaner->resched_event =
isc_event_allocate(cache->mctx, cleaner,
DNS_EVENT_CACHECLEAN,
@@ -387,11 +401,23 @@ cache_cleaner_init(dns_cache_t *cache, isc_taskmgr_t *taskmgr,
result = ISC_R_NOMEMORY;
goto cleanup;
}
- }
+ cleaner->overmem_event =
+ isc_event_allocate(cache->mctx, cleaner,
+ DNS_EVENT_CACHEOVERMEM,
+ overmem_cleaning_action,
+ cleaner, sizeof(isc_event_t));
+ if (cleaner->overmem_event == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ }
+
return (ISC_R_SUCCESS);
-
+
cleanup:
+ if (cleaner->resched_event != NULL)
+ isc_event_free(&cleaner->resched_event);
if (cleaner->cleaning_timer != NULL)
isc_timer_detach(&cleaner->cleaning_timer);
if (cleaner->task != NULL)
@@ -439,7 +465,7 @@ begin_cleaning(cache_cleaner_t *cleaner) {
return;
destroyiter:
- dns_dbiterator_destroy(&cleaner->iterator);
+ dns_dbiterator_destroy(&cleaner->iterator);
idle:
ENSURE(CLEANER_IDLE(cleaner));
return;
@@ -479,6 +505,21 @@ cleaning_timer_action(isc_task_t *task, isc_event_t *event) {
isc_event_free(&event);
}
+static void
+overmem_cleaning_action(isc_task_t *task, isc_event_t *event) {
+ cache_cleaner_t *cleaner = event->ev_arg;
+
+ UNUSED(task);
+
+ INSIST(task == cleaner->task);
+ INSIST(event->ev_type == DNS_EVENT_CACHEOVERMEM);
+ INSIST(cleaner->overmem_event == NULL);
+
+ if (cleaner->state == cleaner_s_idle)
+ begin_cleaning(cleaner);
+ cleaner->overmem_event = event;
+}
+
/*
* Do incremental cleaning.
*/
@@ -509,65 +550,107 @@ incremental_cleaning_action(isc_task_t *task, isc_event_t *event) {
}
INSIST(node != NULL);
- /* Check TTLs, mark expired rdatasets stale. */
+ /*
+ * Check TTLs, mark expired rdatasets stale.
+ */
result = dns_db_expirenode(cleaner->cache->db, node, now);
if (result != ISC_R_SUCCESS) {
UNEXPECTED_ERROR(__FILE__, __LINE__,
- "cache cleaner: dns_db_expirenode() "
+ "cache cleaner: dns_db_expirenode() "
"failed: %s",
dns_result_totext(result));
- /* Continue anyway. */
+ /*
+ * Continue anyway.
+ */
}
- /* This is where the actual freeing takes place. */
+ /*
+ * This is where the actual freeing takes place.
+ */
dns_db_detachnode(cleaner->cache->db, &node);
-
- /* Step to the next node */
+
+ /*
+ * Step to the next node.
+ */
result = dns_dbiterator_next(cleaner->iterator);
if (result == ISC_R_NOMORE) {
- /* We have successfully cleaned the whole cache. */
+ /*
+ * We have successfully cleaned the whole cache.
+ */
goto idle;
}
if (result != ISC_R_SUCCESS) {
UNEXPECTED_ERROR(__FILE__, __LINE__,
- "cache cleaner: dns_dbiterator_next() "
- "failed: %s", dns_result_totext(result));
+ "cache cleaner: "
+ "dns_dbiterator_next() failed: %s",
+ dns_result_totext(result));
goto idle;
}
}
- /* We have successfully performed a cleaning increment. */
+#if 0
+ pause:
+#endif
+ /*
+ * We have successfully performed a cleaning increment.
+ */
result = dns_dbiterator_pause(cleaner->iterator);
if (result != ISC_R_SUCCESS && result != ISC_R_NOMORE) {
UNEXPECTED_ERROR(__FILE__, __LINE__,
"cache cleaner: dns_dbiterator_pause() "
"failed: %s", dns_result_totext(result));
- /* Try to continue. */
+ /*
+ * Try to continue.
+ */
}
- /* Still busy, reschedule. */
+ /*
+ * Still busy, reschedule.
+ */
isc_task_send(task, &event);
INSIST(CLEANER_BUSY(cleaner));
return;
idle:
- /* No longer busy; save the event for later use. */
+ /*
+ * No longer busy; save the event for later use.
+ */
end_cleaning(cleaner, event);
- INSIST(CLEANER_IDLE(cleaner));
+ INSIST(CLEANER_IDLE(cleaner));
+ if (cleaner->overmem) {
+ /* Allow the iterators memory to be freed. */
+ if (cleaner->overmem_event != NULL) {
+ /* XXX remove */
+ fprintf(stderr, "overmem: restart\n");
+ isc_task_send(cleaner->task,
+ &cleaner->overmem_event);
+ }
+#if 0
+ result = dns_dbiterator_first(cleaner->iterator);
+ if (result == ISC_R_SUCCESS) {
+ fprintf(stderr, "overmem: resetting and pausing\n");
+ goto pause;
+ }
+ fprintf(stderr, "dns_dbiterator_first: %s\n",
+ dns_result_totext(result));
+#endif
+ }
return;
}
/*
- * Do immediate cleaning.
+ * Do immediate cleaning.
*/
isc_result_t
dns_cache_clean(dns_cache_t *cache, isc_stdtime_t now) {
isc_result_t result;
dns_dbiterator_t *iterator = NULL;
+ REQUIRE(VALID_CACHE(cache));
+
result = dns_db_createiterator(cache->db, ISC_FALSE, &iterator);
if (result != ISC_R_SUCCESS)
return result;
-
+
result = dns_dbiterator_first(iterator);
while (result == ISC_R_SUCCESS) {
@@ -577,17 +660,23 @@ dns_cache_clean(dns_cache_t *cache, isc_stdtime_t now) {
if (result != ISC_R_SUCCESS)
break;
- /* Check TTLs, mark expired rdatasets stale. */
+ /*
+ * Check TTLs, mark expired rdatasets stale.
+ */
result = dns_db_expirenode(cache->db, node, now);
if (result != ISC_R_SUCCESS) {
UNEXPECTED_ERROR(__FILE__, __LINE__,
- "cache cleaner: dns_db_expirenode() "
+ "cache cleaner: dns_db_expirenode() "
"failed: %s",
dns_result_totext(result));
- /* Continue anyway. */
+ /*
+ * Continue anyway.
+ */
}
- /* This is where the actual freeing takes place. */
+ /*
+ * This is where the actual freeing takes place.
+ */
dns_db_detachnode(cache->db, &node);
result = dns_dbiterator_next(iterator);
@@ -597,17 +686,56 @@ dns_cache_clean(dns_cache_t *cache, isc_stdtime_t now) {
if (result == ISC_R_NOMORE)
result = ISC_R_SUCCESS;
-
+
return result;
}
+static void
+water(void *arg, int mark) {
+ dns_cache_t *cache = arg;
+ isc_boolean_t overmem = ISC_TF(mark == ISC_MEM_HIWATER);
+
+ REQUIRE(VALID_CACHE(cache));
+ dns_db_overmem(cache->db, overmem);
+ cache->cleaner.overmem = overmem;
+
+ if (overmem && cache->cleaner.overmem_event != NULL) {
+ isc_task_send(cache->cleaner.task,
+ &cache->cleaner.overmem_event);
+ }
+}
+
+void
+dns_cache_setcachesize(dns_cache_t *cache, isc_uint32_t size) {
+ isc_uint32_t lowater;
+ isc_uint32_t hiwater;
+
+ REQUIRE(VALID_CACHE(cache));
+
+#if 0
+ /* Impose a minumum cache size. */
+ if (size != 0 && size < 100000)
+ size = 100000;
+#endif
+ hiwater = size - (size >> 3); /* ~(7/8) */
+ lowater = size - (size >> 2); /* ~(3/4) */
+
+ cache->cleaner.overmem = ISC_FALSE;
+ dns_db_overmem(cache->db, ISC_FALSE);
+ if (size == 0 || hiwater == 0 || lowater == 0) {
+ dns_db_overmem(cache->db, ISC_FALSE);
+ } else {
+ isc_mem_setwater(cache->mctx, water, cache, hiwater, lowater);
+ }
+}
+
/*
* The cleaner task is shutting down; do the necessary cleanup.
*/
static void
cleaner_shutdown_action(isc_task_t *task, isc_event_t *event) {
dns_cache_t *cache = event->ev_arg;
- isc_boolean_t should_free = ISC_FALSE;
+ isc_boolean_t should_free = ISC_FALSE;
UNUSED(task);
@@ -615,12 +743,12 @@ cleaner_shutdown_action(isc_task_t *task, isc_event_t *event) {
INSIST(event->ev_type == ISC_TASKEVENT_SHUTDOWN);
isc_event_free(&event);
-
+
cache->live_tasks--;
INSIST(cache->live_tasks == 0);
if (cache->references == 0)
- should_free = ISC_TRUE;
+ should_free = ISC_TRUE;
/*
* By detaching the timer in the context of its task,
@@ -632,7 +760,7 @@ cleaner_shutdown_action(isc_task_t *task, isc_event_t *event) {
/* Make sure we don't reschedule anymore. */
isc_task_purge(task, NULL, DNS_EVENT_CACHECLEAN, NULL);
-
+
UNLOCK(&cache->lock);
if (should_free)