mirror of
https://github.com/isc-projects/bind9.git
synced 2026-02-21 17:01:23 -05:00
removes some unused code in view.c and uncomments some code in cache.c. This still isn't really usable, since the trust level of cached data is not persistent, so all data in the persistent cache will be promoted to "ultimate" trust on reload.
798 lines
19 KiB
C
798 lines
19 KiB
C
/*
|
|
* Copyright (C) 1999-2001 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.
|
|
*/
|
|
|
|
/* $Id: cache.c,v 1.33 2001/01/12 22:22:15 bwelling Exp $ */
|
|
|
|
#include <config.h>
|
|
|
|
#include <isc/mem.h>
|
|
#include <isc/task.h>
|
|
#include <isc/time.h>
|
|
#include <isc/timer.h>
|
|
#include <isc/util.h>
|
|
|
|
#include <dns/cache.h>
|
|
#include <dns/db.h>
|
|
#include <dns/dbiterator.h>
|
|
#include <dns/events.h>
|
|
#include <dns/log.h>
|
|
#include <dns/masterdump.h>
|
|
#include <dns/result.h>
|
|
|
|
#define CACHE_MAGIC 0x24242424U /* $$$$. */
|
|
#define VALID_CACHE(cache) ISC_MAGIC_VALID(cache, CACHE_MAGIC)
|
|
|
|
/***
|
|
*** Types
|
|
***/
|
|
|
|
/*
|
|
* A cache_cleaner_t encapsulsates the state of the periodic
|
|
* cache cleaning.
|
|
*/
|
|
|
|
typedef struct cache_cleaner cache_cleaner_t;
|
|
|
|
typedef enum {
|
|
cleaner_s_idle, /* Waiting for cleaning-interval to expire. */
|
|
cleaner_s_busy /* Currently cleaning. */
|
|
} cleaner_state_t;
|
|
|
|
/*
|
|
* Convenience macros for comprehensive assertion checking.
|
|
*/
|
|
#define CLEANER_IDLE(c) ((c)->state == cleaner_s_idle && \
|
|
(c)->iterator == NULL && \
|
|
(c)->resched_event != NULL)
|
|
#define CLEANER_BUSY(c) ((c)->state == cleaner_s_busy && \
|
|
(c)->iterator != NULL && \
|
|
(c)->resched_event == NULL)
|
|
|
|
struct cache_cleaner {
|
|
dns_cache_t *cache;
|
|
isc_task_t *task;
|
|
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
|
|
itself to reschedule */
|
|
isc_event_t *overmem_event;
|
|
|
|
dns_dbiterator_t *iterator;
|
|
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.
|
|
*/
|
|
|
|
struct dns_cache {
|
|
/* Unlocked */
|
|
unsigned int magic;
|
|
isc_mutex_t lock;
|
|
isc_mutex_t filelock;
|
|
isc_mem_t *mctx;
|
|
|
|
/* Locked by 'lock'. */
|
|
int references;
|
|
int live_tasks;
|
|
dns_rdataclass_t rdclass;
|
|
dns_db_t *db;
|
|
cache_cleaner_t cleaner;
|
|
|
|
/* Locked by 'filelock'. */
|
|
char * filename;
|
|
/* Access to the on-disk cache file is also locked by 'filelock'. */
|
|
};
|
|
|
|
/***
|
|
*** Functions
|
|
***/
|
|
|
|
static isc_result_t
|
|
cache_cleaner_init(dns_cache_t *cache,
|
|
isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr,
|
|
cache_cleaner_t *cleaner);
|
|
|
|
static void
|
|
cleaning_timer_action(isc_task_t *task, isc_event_t *event);
|
|
|
|
static void
|
|
incremental_cleaning_action(isc_task_t *task, isc_event_t *event);
|
|
|
|
static void
|
|
cleaner_shutdown_action(isc_task_t *task, isc_event_t *event);
|
|
|
|
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,
|
|
dns_cache_t **cachep)
|
|
{
|
|
isc_result_t result;
|
|
dns_cache_t *cache;
|
|
|
|
REQUIRE(cachep != NULL);
|
|
REQUIRE(*cachep == NULL);
|
|
REQUIRE(mctx != NULL);
|
|
|
|
cache = isc_mem_get(mctx, sizeof *cache);
|
|
if (cache == NULL)
|
|
return (ISC_R_NOMEMORY);
|
|
|
|
cache->mctx = NULL;
|
|
isc_mem_attach(mctx, &cache->mctx);
|
|
|
|
result = isc_mutex_init(&cache->lock);
|
|
if (result != ISC_R_SUCCESS) {
|
|
UNEXPECTED_ERROR(__FILE__, __LINE__,
|
|
"isc_mutex_init() failed: %s",
|
|
dns_result_totext(result));
|
|
result = ISC_R_UNEXPECTED;
|
|
goto cleanup_mem;
|
|
}
|
|
|
|
result = isc_mutex_init(&cache->filelock);
|
|
if (result != ISC_R_SUCCESS) {
|
|
UNEXPECTED_ERROR(__FILE__, __LINE__,
|
|
"isc_mutex_init() failed: %s",
|
|
dns_result_totext(result));
|
|
result = ISC_R_UNEXPECTED;
|
|
goto cleanup_lock;
|
|
}
|
|
|
|
cache->references = 1;
|
|
cache->live_tasks = 0;
|
|
cache->rdclass = rdclass;
|
|
|
|
cache->db = NULL;
|
|
result = dns_db_create(cache->mctx, db_type, dns_rootname,
|
|
dns_dbtype_cache, rdclass, db_argc, db_argv,
|
|
&cache->db);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto cleanup_filelock;
|
|
|
|
cache->filename = NULL;
|
|
|
|
cache->magic = CACHE_MAGIC;
|
|
|
|
result = cache_cleaner_init(cache, taskmgr, timermgr,
|
|
&cache->cleaner);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto cleanup_db;
|
|
|
|
*cachep = cache;
|
|
return (ISC_R_SUCCESS);
|
|
|
|
cleanup_db:
|
|
dns_db_detach(&cache->db);
|
|
cleanup_filelock:
|
|
DESTROYLOCK(&cache->filelock);
|
|
cleanup_lock:
|
|
DESTROYLOCK(&cache->lock);
|
|
cleanup_mem:
|
|
isc_mem_put(mctx, cache, sizeof *cache);
|
|
isc_mem_detach(&mctx);
|
|
return (result);
|
|
}
|
|
|
|
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);
|
|
|
|
if (cache->cleaner.iterator != NULL)
|
|
dns_dbiterator_destroy(&cache->cleaner.iterator);
|
|
|
|
if (cache->filename) {
|
|
isc_mem_free(cache->mctx, cache->filename);
|
|
cache->filename = NULL;
|
|
}
|
|
|
|
if (cache->db)
|
|
dns_db_detach(&cache->db);
|
|
|
|
DESTROYLOCK(&cache->lock);
|
|
DESTROYLOCK(&cache->filelock);
|
|
cache->magic = 0;
|
|
mctx = cache->mctx;
|
|
isc_mem_put(cache->mctx, cache, sizeof *cache);
|
|
isc_mem_detach(&mctx);
|
|
}
|
|
|
|
|
|
void
|
|
dns_cache_attach(dns_cache_t *cache, dns_cache_t **targetp) {
|
|
|
|
REQUIRE(VALID_CACHE(cache));
|
|
REQUIRE(targetp != NULL && *targetp == NULL);
|
|
|
|
LOCK(&cache->lock);
|
|
cache->references++;
|
|
UNLOCK(&cache->lock);
|
|
|
|
*targetp = cache;
|
|
}
|
|
|
|
void
|
|
dns_cache_detach(dns_cache_t **cachep) {
|
|
dns_cache_t *cache;
|
|
isc_boolean_t free_cache = ISC_FALSE;
|
|
|
|
REQUIRE(cachep != NULL);
|
|
cache = *cachep;
|
|
REQUIRE(VALID_CACHE(cache));
|
|
|
|
LOCK(&cache->lock);
|
|
REQUIRE(cache->references > 0);
|
|
cache->references--;
|
|
if (cache->references == 0) {
|
|
cache->cleaner.overmem = ISC_FALSE;
|
|
free_cache = ISC_TRUE;
|
|
}
|
|
UNLOCK(&cache->lock);
|
|
*cachep = NULL;
|
|
if (free_cache) {
|
|
/*
|
|
* When the cache is shut down, dump it to a file if one is
|
|
* specified.
|
|
*/
|
|
dns_cache_dump(cache);
|
|
|
|
/* XXXRTH This is not locked! */
|
|
if (cache->live_tasks > 0)
|
|
isc_task_shutdown(cache->cleaner.task);
|
|
else
|
|
cache_free(cache);
|
|
}
|
|
}
|
|
|
|
void
|
|
dns_cache_attachdb(dns_cache_t *cache, dns_db_t **dbp) {
|
|
REQUIRE(VALID_CACHE(cache));
|
|
REQUIRE(dbp != NULL && *dbp == NULL);
|
|
REQUIRE(cache->db != NULL);
|
|
LOCK(&cache->lock);
|
|
dns_db_attach(cache->db, dbp);
|
|
UNLOCK(&cache->lock);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_cache_setfilename(dns_cache_t *cache, char *filename) {
|
|
char *newname;
|
|
|
|
REQUIRE(VALID_CACHE(cache));
|
|
REQUIRE(filename != NULL);
|
|
|
|
newname = isc_mem_strdup(cache->mctx, filename);
|
|
if (newname == NULL)
|
|
return (ISC_R_NOMEMORY);
|
|
LOCK(&cache->filelock);
|
|
if (cache->filename)
|
|
isc_mem_free(cache->mctx, cache->filename);
|
|
cache->filename = newname;
|
|
UNLOCK(&cache->filelock);
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_cache_load(dns_cache_t *cache) {
|
|
isc_result_t result;
|
|
|
|
REQUIRE(VALID_CACHE(cache));
|
|
|
|
if (cache->filename == NULL)
|
|
return (ISC_R_SUCCESS);
|
|
LOCK(&cache->filelock);
|
|
result = dns_db_load(cache->db, cache->filename);
|
|
UNLOCK(&cache->filelock);
|
|
return (result);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_cache_dump(dns_cache_t *cache) {
|
|
isc_result_t result;
|
|
|
|
REQUIRE(VALID_CACHE(cache));
|
|
|
|
LOCK(&cache->filelock);
|
|
if (cache->filename == NULL)
|
|
return (ISC_R_SUCCESS);
|
|
result = dns_master_dump(cache->mctx, cache->db, NULL,
|
|
&dns_master_style_cache, cache->filename);
|
|
UNLOCK(&cache->filelock);
|
|
return (result);
|
|
}
|
|
|
|
void
|
|
dns_cache_setcleaninginterval(dns_cache_t *cache, unsigned int t) {
|
|
LOCK(&cache->lock);
|
|
|
|
/*
|
|
* It may be the case that the cache has already shut down.
|
|
* If so, it has no timer.
|
|
*/
|
|
if (cache->cleaner.cleaning_timer == NULL)
|
|
goto unlock;
|
|
|
|
cache->cleaner.cleaning_interval = t;
|
|
if (t == 0) {
|
|
isc_timer_reset(cache->cleaner.cleaning_timer,
|
|
isc_timertype_inactive, NULL, NULL, ISC_TRUE);
|
|
} else {
|
|
isc_interval_t interval;
|
|
isc_interval_set(&interval, cache->cleaner.cleaning_interval,
|
|
0);
|
|
isc_timer_reset(cache->cleaner.cleaning_timer,
|
|
isc_timertype_ticker,
|
|
NULL, &interval, ISC_FALSE);
|
|
}
|
|
unlock:
|
|
UNLOCK(&cache->lock);
|
|
}
|
|
|
|
/*
|
|
* 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,
|
|
isc_timermgr_t *timermgr, cache_cleaner_t *cleaner)
|
|
{
|
|
isc_result_t result;
|
|
|
|
cleaner->increment = 100;
|
|
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) {
|
|
UNEXPECTED_ERROR(__FILE__, __LINE__,
|
|
"isc_task_create() failed: %s",
|
|
dns_result_totext(result));
|
|
result = ISC_R_UNEXPECTED;
|
|
goto cleanup;
|
|
}
|
|
cleaner->cache->live_tasks++;
|
|
isc_task_setname(cleaner->task, "cachecleaner", cleaner);
|
|
|
|
result = isc_task_onshutdown(cleaner->task,
|
|
cleaner_shutdown_action, cache);
|
|
if (result != ISC_R_SUCCESS) {
|
|
UNEXPECTED_ERROR(__FILE__, __LINE__,
|
|
"cache cleaner: "
|
|
"isc_task_onshutdown() failed: %s",
|
|
dns_result_totext(result));
|
|
goto cleanup;
|
|
}
|
|
|
|
cleaner->cleaning_interval = 0; /* Initially turned off. */
|
|
result = isc_timer_create(timermgr, isc_timertype_inactive,
|
|
NULL, NULL,
|
|
cleaner->task,
|
|
cleaning_timer_action, cleaner,
|
|
&cleaner->cleaning_timer);
|
|
if (result != ISC_R_SUCCESS) {
|
|
UNEXPECTED_ERROR(__FILE__, __LINE__,
|
|
"isc_timer_create() failed: %s",
|
|
dns_result_totext(result));
|
|
result = ISC_R_UNEXPECTED;
|
|
goto cleanup;
|
|
}
|
|
|
|
cleaner->resched_event =
|
|
isc_event_allocate(cache->mctx, cleaner,
|
|
DNS_EVENT_CACHECLEAN,
|
|
incremental_cleaning_action,
|
|
cleaner, sizeof(isc_event_t));
|
|
if (cleaner->resched_event == NULL) {
|
|
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)
|
|
isc_task_detach(&cleaner->task);
|
|
return (result);
|
|
}
|
|
|
|
static void
|
|
begin_cleaning(cache_cleaner_t *cleaner) {
|
|
isc_result_t result;
|
|
|
|
REQUIRE(CLEANER_IDLE(cleaner));
|
|
/*
|
|
* Create an iterator and position it at the beginning of the cache.
|
|
*/
|
|
result = dns_db_createiterator(cleaner->cache->db, ISC_FALSE,
|
|
&cleaner->iterator);
|
|
if (result != ISC_R_SUCCESS) {
|
|
isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
|
|
DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
|
|
"cache cleaner could not create "
|
|
"iterator: %s", isc_result_totext(result));
|
|
goto idle;
|
|
}
|
|
result = dns_dbiterator_first(cleaner->iterator);
|
|
if (result == ISC_R_NOMORE) {
|
|
/*
|
|
* The database is empty. We are done.
|
|
*/
|
|
goto destroyiter;
|
|
}
|
|
if (result != ISC_R_SUCCESS) {
|
|
UNEXPECTED_ERROR(__FILE__, __LINE__,
|
|
"cache cleaner: dns_dbiterator_first() "
|
|
"failed: %s", dns_result_totext(result));
|
|
goto destroyiter;
|
|
}
|
|
|
|
isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
|
|
DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1),
|
|
"begin cache cleaning");
|
|
cleaner->state = cleaner_s_busy;
|
|
isc_task_send(cleaner->task, &cleaner->resched_event);
|
|
ENSURE(CLEANER_BUSY(cleaner));
|
|
return;
|
|
|
|
destroyiter:
|
|
dns_dbiterator_destroy(&cleaner->iterator);
|
|
idle:
|
|
ENSURE(CLEANER_IDLE(cleaner));
|
|
return;
|
|
}
|
|
|
|
static void
|
|
end_cleaning(cache_cleaner_t *cleaner, isc_event_t *event) {
|
|
REQUIRE(CLEANER_BUSY(cleaner));
|
|
isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
|
|
DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1),
|
|
"end cache cleaning");
|
|
dns_dbiterator_destroy(&cleaner->iterator);
|
|
cleaner->state = cleaner_s_idle;
|
|
cleaner->resched_event = event;
|
|
ENSURE(CLEANER_IDLE(cleaner));
|
|
}
|
|
|
|
/*
|
|
* This is run once for every cache-cleaning-interval as defined in named.conf.
|
|
*/
|
|
static void
|
|
cleaning_timer_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 == ISC_TIMEREVENT_TICK);
|
|
|
|
if (cleaner->state == cleaner_s_idle) {
|
|
begin_cleaning(cleaner);
|
|
} else {
|
|
INSIST(CLEANER_BUSY(cleaner));
|
|
isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
|
|
DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
|
|
"cache cleaner did not finish "
|
|
"in one cleaning-interval");
|
|
}
|
|
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.
|
|
*/
|
|
static void
|
|
incremental_cleaning_action(isc_task_t *task, isc_event_t *event) {
|
|
isc_result_t result;
|
|
cache_cleaner_t *cleaner = event->ev_arg;
|
|
isc_stdtime_t now;
|
|
int n_names;
|
|
|
|
INSIST(event->ev_type == DNS_EVENT_CACHECLEAN);
|
|
INSIST(CLEANER_BUSY(cleaner));
|
|
|
|
n_names = cleaner->increment;
|
|
isc_stdtime_get(&now);
|
|
|
|
REQUIRE(DNS_DBITERATOR_VALID(cleaner->iterator));
|
|
|
|
while (n_names-- > 0) {
|
|
dns_dbnode_t *node = NULL;
|
|
result = dns_dbiterator_current(cleaner->iterator, &node,
|
|
(dns_name_t *) NULL);
|
|
if (result != ISC_R_SUCCESS) {
|
|
UNEXPECTED_ERROR(__FILE__, __LINE__,
|
|
"cache cleaner: dns_dbiterator_current() "
|
|
"failed: %s", dns_result_totext(result));
|
|
goto idle;
|
|
}
|
|
INSIST(node != NULL);
|
|
|
|
/*
|
|
* 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() "
|
|
"failed: %s",
|
|
dns_result_totext(result));
|
|
/*
|
|
* Continue anyway.
|
|
*/
|
|
}
|
|
|
|
/*
|
|
* This is where the actual freeing takes place.
|
|
*/
|
|
dns_db_detachnode(cleaner->cache->db, &node);
|
|
|
|
/*
|
|
* Step to the next node.
|
|
*/
|
|
result = dns_dbiterator_next(cleaner->iterator);
|
|
if (result == ISC_R_NOMORE) {
|
|
/*
|
|
* 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));
|
|
goto idle;
|
|
}
|
|
}
|
|
|
|
#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.
|
|
*/
|
|
}
|
|
/*
|
|
* Still busy, reschedule.
|
|
*/
|
|
isc_task_send(task, &event);
|
|
INSIST(CLEANER_BUSY(cleaner));
|
|
return;
|
|
|
|
idle:
|
|
/*
|
|
* No longer busy; save the event for later use.
|
|
*/
|
|
end_cleaning(cleaner, event);
|
|
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.
|
|
*/
|
|
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) {
|
|
dns_dbnode_t *node = NULL;
|
|
result = dns_dbiterator_current(iterator, &node,
|
|
(dns_name_t *) NULL);
|
|
if (result != ISC_R_SUCCESS)
|
|
break;
|
|
|
|
/*
|
|
* 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() "
|
|
"failed: %s",
|
|
dns_result_totext(result));
|
|
/*
|
|
* Continue anyway.
|
|
*/
|
|
}
|
|
|
|
/*
|
|
* This is where the actual freeing takes place.
|
|
*/
|
|
dns_db_detachnode(cache->db, &node);
|
|
|
|
result = dns_dbiterator_next(iterator);
|
|
}
|
|
|
|
dns_dbiterator_destroy(&iterator);
|
|
|
|
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;
|
|
|
|
UNUSED(task);
|
|
|
|
LOCK(&cache->lock);
|
|
|
|
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;
|
|
|
|
/*
|
|
* By detaching the timer in the context of its task,
|
|
* we are guaranteed that there will be no further timer
|
|
* events.
|
|
*/
|
|
if (cache->cleaner.cleaning_timer != NULL)
|
|
isc_timer_detach(&cache->cleaner.cleaning_timer);
|
|
|
|
/* Make sure we don't reschedule anymore. */
|
|
isc_task_purge(task, NULL, DNS_EVENT_CACHECLEAN, NULL);
|
|
|
|
UNLOCK(&cache->lock);
|
|
|
|
if (should_free)
|
|
cache_free(cache);
|
|
}
|