PageRenderTime 86ms CodeModel.GetById 23ms app.highlight 54ms RepoModel.GetById 1ms app.codeStats 1ms

/contrib/bind9/lib/dns/cache.c

https://bitbucket.org/freebsd/freebsd-head/
C | 1207 lines | 815 code | 214 blank | 178 comment | 245 complexity | d1c43e0491de7ece6b1c62eade214957 MD5 | raw file
   1/*
   2 * Copyright (C) 2004-2009, 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
   3 * Copyright (C) 1999-2003  Internet Software Consortium.
   4 *
   5 * Permission to use, copy, modify, and/or distribute this software for any
   6 * purpose with or without fee is hereby granted, provided that the above
   7 * copyright notice and this permission notice appear in all copies.
   8 *
   9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  11 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  15 * PERFORMANCE OF THIS SOFTWARE.
  16 */
  17
  18/* $Id$ */
  19
  20/*! \file */
  21
  22#include <config.h>
  23
  24#include <isc/mem.h>
  25#include <isc/string.h>
  26#include <isc/task.h>
  27#include <isc/time.h>
  28#include <isc/timer.h>
  29#include <isc/util.h>
  30
  31#include <dns/cache.h>
  32#include <dns/db.h>
  33#include <dns/dbiterator.h>
  34#include <dns/events.h>
  35#include <dns/lib.h>
  36#include <dns/log.h>
  37#include <dns/masterdump.h>
  38#include <dns/rdata.h>
  39#include <dns/rdataset.h>
  40#include <dns/rdatasetiter.h>
  41#include <dns/result.h>
  42
  43#include "rbtdb.h"
  44
  45#define CACHE_MAGIC		ISC_MAGIC('$', '$', '$', '$')
  46#define VALID_CACHE(cache)	ISC_MAGIC_VALID(cache, CACHE_MAGIC)
  47
  48/*!
  49 * Control incremental cleaning.
  50 * DNS_CACHE_MINSIZE is how many bytes is the floor for dns_cache_setcachesize().
  51 * See also DNS_CACHE_CLEANERINCREMENT
  52 */
  53#define DNS_CACHE_MINSIZE	2097152 /*%< Bytes.  2097152 = 2 MB */
  54/*!
  55 * Control incremental cleaning.
  56 * CLEANERINCREMENT is how many nodes are examined in one pass.
  57 * See also DNS_CACHE_MINSIZE
  58 */
  59#define DNS_CACHE_CLEANERINCREMENT	1000U	/*%< Number of nodes. */
  60
  61/***
  62 ***	Types
  63 ***/
  64
  65/*
  66 * A cache_cleaner_t encapsulates the state of the periodic
  67 * cache cleaning.
  68 */
  69
  70typedef struct cache_cleaner cache_cleaner_t;
  71
  72typedef enum {
  73	cleaner_s_idle,	/*%< Waiting for cleaning-interval to expire. */
  74	cleaner_s_busy,	/*%< Currently cleaning. */
  75	cleaner_s_done	/*%< Freed enough memory after being overmem. */
  76} cleaner_state_t;
  77
  78/*
  79 * Convenience macros for comprehensive assertion checking.
  80 */
  81#define CLEANER_IDLE(c) ((c)->state == cleaner_s_idle && \
  82			 (c)->resched_event != NULL)
  83#define CLEANER_BUSY(c) ((c)->state == cleaner_s_busy && \
  84			 (c)->iterator != NULL && \
  85			 (c)->resched_event == NULL)
  86
  87/*%
  88 * Accesses to a cache cleaner object are synchronized through
  89 * task/event serialization, or locked from the cache object.
  90 */
  91struct cache_cleaner {
  92	isc_mutex_t	lock;
  93	/*%<
  94	 * Locks overmem_event, overmem.  Note: never allocate memory
  95	 * while holding this lock - that could lead to deadlock since
  96	 * the lock is take by water() which is called from the memory
  97	 * allocator.
  98	 */
  99
 100	dns_cache_t	*cache;
 101	isc_task_t	*task;
 102	unsigned int	cleaning_interval; /*% The cleaning-interval from
 103					      named.conf, in seconds. */
 104	isc_timer_t	*cleaning_timer;
 105	isc_event_t	*resched_event;	/*% Sent by cleaner task to
 106					   itself to reschedule */
 107	isc_event_t	*overmem_event;
 108
 109	dns_dbiterator_t *iterator;
 110	unsigned int	increment;	/*% Number of names to
 111					   clean in one increment */
 112	cleaner_state_t	state;		/*% Idle/Busy. */
 113	isc_boolean_t	overmem;	/*% The cache is in an overmem state. */
 114	isc_boolean_t	 replaceiterator;
 115};
 116
 117/*%
 118 * The actual cache object.
 119 */
 120
 121struct dns_cache {
 122	/* Unlocked. */
 123	unsigned int		magic;
 124	isc_mutex_t		lock;
 125	isc_mutex_t		filelock;
 126	isc_mem_t		*mctx;		/* Main cache memory */
 127	isc_mem_t		*hmctx;		/* Heap memory */
 128	char			*name;
 129
 130	/* Locked by 'lock'. */
 131	int			references;
 132	int			live_tasks;
 133	dns_rdataclass_t	rdclass;
 134	dns_db_t		*db;
 135	cache_cleaner_t		cleaner;
 136	char			*db_type;
 137	int			db_argc;
 138	char			**db_argv;
 139	isc_uint32_t		size;
 140
 141	/* Locked by 'filelock'. */
 142	char			*filename;
 143	/* Access to the on-disk cache file is also locked by 'filelock'. */
 144};
 145
 146/***
 147 ***	Functions
 148 ***/
 149
 150static isc_result_t
 151cache_cleaner_init(dns_cache_t *cache, isc_taskmgr_t *taskmgr,
 152		   isc_timermgr_t *timermgr, cache_cleaner_t *cleaner);
 153
 154static void
 155cleaning_timer_action(isc_task_t *task, isc_event_t *event);
 156
 157static void
 158incremental_cleaning_action(isc_task_t *task, isc_event_t *event);
 159
 160static void
 161cleaner_shutdown_action(isc_task_t *task, isc_event_t *event);
 162
 163static void
 164overmem_cleaning_action(isc_task_t *task, isc_event_t *event);
 165
 166static inline isc_result_t
 167cache_create_db(dns_cache_t *cache, dns_db_t **db) {
 168	return (dns_db_create(cache->mctx, cache->db_type, dns_rootname,
 169			      dns_dbtype_cache, cache->rdclass,
 170			      cache->db_argc, cache->db_argv, db));
 171}
 172
 173isc_result_t
 174dns_cache_create(isc_mem_t *cmctx, isc_taskmgr_t *taskmgr,
 175		 isc_timermgr_t *timermgr, dns_rdataclass_t rdclass,
 176		 const char *db_type, unsigned int db_argc, char **db_argv,
 177		 dns_cache_t **cachep)
 178{
 179	return (dns_cache_create3(cmctx, cmctx, taskmgr, timermgr, rdclass, "",
 180				  db_type, db_argc, db_argv, cachep));
 181}
 182
 183isc_result_t
 184dns_cache_create2(isc_mem_t *cmctx, isc_taskmgr_t *taskmgr,
 185		  isc_timermgr_t *timermgr, dns_rdataclass_t rdclass,
 186		  const char *cachename, const char *db_type,
 187		  unsigned int db_argc, char **db_argv, dns_cache_t **cachep)
 188{
 189	return (dns_cache_create3(cmctx, cmctx, taskmgr, timermgr, rdclass,
 190				  cachename, db_type, db_argc, db_argv,
 191				  cachep));
 192}
 193
 194isc_result_t
 195dns_cache_create3(isc_mem_t *cmctx, isc_mem_t *hmctx, isc_taskmgr_t *taskmgr,
 196		  isc_timermgr_t *timermgr, dns_rdataclass_t rdclass,
 197		  const char *cachename, const char *db_type,
 198		  unsigned int db_argc, char **db_argv, dns_cache_t **cachep)
 199{
 200	isc_result_t result;
 201	dns_cache_t *cache;
 202	int i, extra = 0;
 203	isc_task_t *dbtask;
 204
 205	REQUIRE(cachep != NULL);
 206	REQUIRE(*cachep == NULL);
 207	REQUIRE(cmctx != NULL);
 208	REQUIRE(hmctx != NULL);
 209	REQUIRE(cachename != NULL);
 210
 211	cache = isc_mem_get(cmctx, sizeof(*cache));
 212	if (cache == NULL)
 213		return (ISC_R_NOMEMORY);
 214
 215	cache->mctx = cache->hmctx = NULL;
 216	isc_mem_attach(cmctx, &cache->mctx);
 217	isc_mem_attach(hmctx, &cache->hmctx);
 218
 219	cache->name = NULL;
 220	if (cachename != NULL) {
 221		cache->name = isc_mem_strdup(cmctx, cachename);
 222		if (cache->name == NULL) {
 223			result = ISC_R_NOMEMORY;
 224			goto cleanup_mem;
 225		}
 226	}
 227
 228	result = isc_mutex_init(&cache->lock);
 229	if (result != ISC_R_SUCCESS)
 230		goto cleanup_mem;
 231
 232	result = isc_mutex_init(&cache->filelock);
 233	if (result != ISC_R_SUCCESS)
 234		goto cleanup_lock;
 235
 236	cache->references = 1;
 237	cache->live_tasks = 0;
 238	cache->rdclass = rdclass;
 239
 240	cache->db_type = isc_mem_strdup(cmctx, db_type);
 241	if (cache->db_type == NULL) {
 242		result = ISC_R_NOMEMORY;
 243		goto cleanup_filelock;
 244	}
 245
 246	/*
 247	 * For databases of type "rbt" we pass hmctx to dns_db_create()
 248	 * via cache->db_argv, followed by the rest of the arguments in
 249	 * db_argv (of which there really shouldn't be any).
 250	 */
 251	if (strcmp(cache->db_type, "rbt") == 0)
 252		extra = 1;
 253
 254	cache->db_argc = db_argc + extra;
 255	cache->db_argv = NULL;
 256
 257	if (cache->db_argc != 0) {
 258		cache->db_argv = isc_mem_get(cmctx,
 259					     cache->db_argc * sizeof(char *));
 260		if (cache->db_argv == NULL) {
 261			result = ISC_R_NOMEMORY;
 262			goto cleanup_dbtype;
 263		}
 264
 265		for (i = 0; i < cache->db_argc; i++)
 266			cache->db_argv[i] = NULL;
 267
 268		cache->db_argv[0] = (char *) hmctx;
 269		for (i = extra; i < cache->db_argc; i++) {
 270			cache->db_argv[i] = isc_mem_strdup(cmctx,
 271							   db_argv[i - extra]);
 272			if (cache->db_argv[i] == NULL) {
 273				result = ISC_R_NOMEMORY;
 274				goto cleanup_dbargv;
 275			}
 276		}
 277	}
 278
 279	/*
 280	 * Create the database
 281	 */
 282	cache->db = NULL;
 283	result = cache_create_db(cache, &cache->db);
 284	if (result != ISC_R_SUCCESS)
 285		goto cleanup_dbargv;
 286	if (taskmgr != NULL) {
 287		dbtask = NULL;
 288		result = isc_task_create(taskmgr, 1, &dbtask);
 289		if (result != ISC_R_SUCCESS)
 290			goto cleanup_db;
 291		dns_db_settask(cache->db, dbtask);
 292		isc_task_detach(&dbtask);
 293	}
 294
 295	cache->filename = NULL;
 296
 297	cache->magic = CACHE_MAGIC;
 298
 299	/*
 300	 * RBT-type cache DB has its own mechanism of cache cleaning and doesn't
 301	 * need the control of the generic cleaner.
 302	 */
 303	if (strcmp(db_type, "rbt") == 0)
 304		result = cache_cleaner_init(cache, NULL, NULL, &cache->cleaner);
 305	else {
 306		result = cache_cleaner_init(cache, taskmgr, timermgr,
 307					    &cache->cleaner);
 308	}
 309	if (result != ISC_R_SUCCESS)
 310		goto cleanup_db;
 311
 312	*cachep = cache;
 313	return (ISC_R_SUCCESS);
 314
 315 cleanup_db:
 316	dns_db_detach(&cache->db);
 317 cleanup_dbargv:
 318	for (i = extra; i < cache->db_argc; i++)
 319		if (cache->db_argv[i] != NULL)
 320			isc_mem_free(cmctx, cache->db_argv[i]);
 321	if (cache->db_argv != NULL)
 322		isc_mem_put(cmctx, cache->db_argv,
 323			    cache->db_argc * sizeof(char *));
 324 cleanup_dbtype:
 325	isc_mem_free(cmctx, cache->db_type);
 326 cleanup_filelock:
 327	DESTROYLOCK(&cache->filelock);
 328 cleanup_lock:
 329	DESTROYLOCK(&cache->lock);
 330 cleanup_mem:
 331	if (cache->name != NULL)
 332		isc_mem_free(cmctx, cache->name);
 333	isc_mem_detach(&cache->hmctx);
 334	isc_mem_putanddetach(&cache->mctx, cache, sizeof(*cache));
 335	return (result);
 336}
 337
 338static void
 339cache_free(dns_cache_t *cache) {
 340	int i;
 341
 342	REQUIRE(VALID_CACHE(cache));
 343	REQUIRE(cache->references == 0);
 344
 345	isc_mem_setwater(cache->mctx, NULL, NULL, 0, 0);
 346
 347	if (cache->cleaner.task != NULL)
 348		isc_task_detach(&cache->cleaner.task);
 349
 350	if (cache->cleaner.overmem_event != NULL)
 351		isc_event_free(&cache->cleaner.overmem_event);
 352
 353	if (cache->cleaner.resched_event != NULL)
 354		isc_event_free(&cache->cleaner.resched_event);
 355
 356	if (cache->cleaner.iterator != NULL)
 357		dns_dbiterator_destroy(&cache->cleaner.iterator);
 358
 359	DESTROYLOCK(&cache->cleaner.lock);
 360
 361	if (cache->filename) {
 362		isc_mem_free(cache->mctx, cache->filename);
 363		cache->filename = NULL;
 364	}
 365
 366	if (cache->db != NULL)
 367		dns_db_detach(&cache->db);
 368
 369	if (cache->db_argv != NULL) {
 370		/*
 371		 * We don't free db_argv[0] in "rbt" cache databases
 372		 * as it's a pointer to hmctx
 373		 */
 374		int extra = 0;
 375		if (strcmp(cache->db_type, "rbt") == 0)
 376			extra = 1;
 377		for (i = extra; i < cache->db_argc; i++)
 378			if (cache->db_argv[i] != NULL)
 379				isc_mem_free(cache->mctx, cache->db_argv[i]);
 380		isc_mem_put(cache->mctx, cache->db_argv,
 381			    cache->db_argc * sizeof(char *));
 382	}
 383
 384	if (cache->db_type != NULL)
 385		isc_mem_free(cache->mctx, cache->db_type);
 386
 387	if (cache->name != NULL)
 388		isc_mem_free(cache->mctx, cache->name);
 389
 390	DESTROYLOCK(&cache->lock);
 391	DESTROYLOCK(&cache->filelock);
 392
 393	cache->magic = 0;
 394	isc_mem_detach(&cache->hmctx);
 395	isc_mem_putanddetach(&cache->mctx, cache, sizeof(*cache));
 396}
 397
 398
 399void
 400dns_cache_attach(dns_cache_t *cache, dns_cache_t **targetp) {
 401
 402	REQUIRE(VALID_CACHE(cache));
 403	REQUIRE(targetp != NULL && *targetp == NULL);
 404
 405	LOCK(&cache->lock);
 406	cache->references++;
 407	UNLOCK(&cache->lock);
 408
 409	*targetp = cache;
 410}
 411
 412void
 413dns_cache_detach(dns_cache_t **cachep) {
 414	dns_cache_t *cache;
 415	isc_boolean_t free_cache = ISC_FALSE;
 416
 417	REQUIRE(cachep != NULL);
 418	cache = *cachep;
 419	REQUIRE(VALID_CACHE(cache));
 420
 421	LOCK(&cache->lock);
 422	REQUIRE(cache->references > 0);
 423	cache->references--;
 424	if (cache->references == 0) {
 425		cache->cleaner.overmem = ISC_FALSE;
 426		free_cache = ISC_TRUE;
 427	}
 428
 429	*cachep = NULL;
 430
 431	if (free_cache) {
 432		/*
 433		 * When the cache is shut down, dump it to a file if one is
 434		 * specified.
 435		 */
 436		isc_result_t result = dns_cache_dump(cache);
 437		if (result != ISC_R_SUCCESS)
 438			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
 439				      DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
 440				      "error dumping cache: %s ",
 441				      isc_result_totext(result));
 442
 443		/*
 444		 * If the cleaner task exists, let it free the cache.
 445		 */
 446		if (cache->live_tasks > 0) {
 447			isc_task_shutdown(cache->cleaner.task);
 448			free_cache = ISC_FALSE;
 449		}
 450	}
 451
 452	UNLOCK(&cache->lock);
 453
 454	if (free_cache)
 455		cache_free(cache);
 456}
 457
 458void
 459dns_cache_attachdb(dns_cache_t *cache, dns_db_t **dbp) {
 460	REQUIRE(VALID_CACHE(cache));
 461	REQUIRE(dbp != NULL && *dbp == NULL);
 462	REQUIRE(cache->db != NULL);
 463
 464	LOCK(&cache->lock);
 465	dns_db_attach(cache->db, dbp);
 466	UNLOCK(&cache->lock);
 467
 468}
 469
 470isc_result_t
 471dns_cache_setfilename(dns_cache_t *cache, const char *filename) {
 472	char *newname;
 473
 474	REQUIRE(VALID_CACHE(cache));
 475	REQUIRE(filename != NULL);
 476
 477	newname = isc_mem_strdup(cache->mctx, filename);
 478	if (newname == NULL)
 479		return (ISC_R_NOMEMORY);
 480
 481	LOCK(&cache->filelock);
 482	if (cache->filename)
 483		isc_mem_free(cache->mctx, cache->filename);
 484	cache->filename = newname;
 485	UNLOCK(&cache->filelock);
 486
 487	return (ISC_R_SUCCESS);
 488}
 489
 490#ifdef BIND9
 491isc_result_t
 492dns_cache_load(dns_cache_t *cache) {
 493	isc_result_t result;
 494
 495	REQUIRE(VALID_CACHE(cache));
 496
 497	if (cache->filename == NULL)
 498		return (ISC_R_SUCCESS);
 499
 500	LOCK(&cache->filelock);
 501	result = dns_db_load(cache->db, cache->filename);
 502	UNLOCK(&cache->filelock);
 503
 504	return (result);
 505}
 506#endif /* BIND9 */
 507
 508isc_result_t
 509dns_cache_dump(dns_cache_t *cache) {
 510#ifdef BIND9
 511	isc_result_t result;
 512#endif
 513
 514	REQUIRE(VALID_CACHE(cache));
 515
 516	if (cache->filename == NULL)
 517		return (ISC_R_SUCCESS);
 518
 519#ifdef BIND9
 520	LOCK(&cache->filelock);
 521	result = dns_master_dump(cache->mctx, cache->db, NULL,
 522				 &dns_master_style_cache, cache->filename);
 523	UNLOCK(&cache->filelock);
 524	return (result);
 525#else
 526	return (ISC_R_NOTIMPLEMENTED);
 527#endif
 528
 529}
 530
 531void
 532dns_cache_setcleaninginterval(dns_cache_t *cache, unsigned int t) {
 533	isc_interval_t interval;
 534	isc_result_t result;
 535
 536	LOCK(&cache->lock);
 537
 538	/*
 539	 * It may be the case that the cache has already shut down.
 540	 * If so, it has no timer.
 541	 */
 542	if (cache->cleaner.cleaning_timer == NULL)
 543		goto unlock;
 544
 545	cache->cleaner.cleaning_interval = t;
 546
 547	if (t == 0) {
 548		result = isc_timer_reset(cache->cleaner.cleaning_timer,
 549					 isc_timertype_inactive,
 550					 NULL, NULL, ISC_TRUE);
 551	} else {
 552		isc_interval_set(&interval, cache->cleaner.cleaning_interval,
 553				 0);
 554		result = isc_timer_reset(cache->cleaner.cleaning_timer,
 555					 isc_timertype_ticker,
 556					 NULL, &interval, ISC_FALSE);
 557	}
 558	if (result != ISC_R_SUCCESS)
 559		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
 560			      DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
 561			      "could not set cache cleaning interval: %s",
 562			      isc_result_totext(result));
 563
 564 unlock:
 565	UNLOCK(&cache->lock);
 566}
 567
 568unsigned int
 569dns_cache_getcleaninginterval(dns_cache_t *cache) {
 570	unsigned int t;
 571
 572	REQUIRE(VALID_CACHE(cache));
 573
 574	LOCK(&cache->lock);
 575	t = cache->cleaner.cleaning_interval;
 576	UNLOCK(&cache->lock);
 577
 578	return (t);
 579}
 580
 581const char *
 582dns_cache_getname(dns_cache_t *cache) {
 583	REQUIRE(VALID_CACHE(cache));
 584
 585	return (cache->name);
 586}
 587
 588/*
 589 * Initialize the cache cleaner object at *cleaner.
 590 * Space for the object must be allocated by the caller.
 591 */
 592
 593static isc_result_t
 594cache_cleaner_init(dns_cache_t *cache, isc_taskmgr_t *taskmgr,
 595		   isc_timermgr_t *timermgr, cache_cleaner_t *cleaner)
 596{
 597	isc_result_t result;
 598
 599	result = isc_mutex_init(&cleaner->lock);
 600	if (result != ISC_R_SUCCESS)
 601		goto fail;
 602
 603	cleaner->increment = DNS_CACHE_CLEANERINCREMENT;
 604	cleaner->state = cleaner_s_idle;
 605	cleaner->cache = cache;
 606	cleaner->iterator = NULL;
 607	cleaner->overmem = ISC_FALSE;
 608	cleaner->replaceiterator = ISC_FALSE;
 609
 610	cleaner->task = NULL;
 611	cleaner->cleaning_timer = NULL;
 612	cleaner->resched_event = NULL;
 613	cleaner->overmem_event = NULL;
 614	cleaner->cleaning_interval = 0; /* Initially turned off. */
 615
 616	result = dns_db_createiterator(cleaner->cache->db, ISC_FALSE,
 617				       &cleaner->iterator);
 618	if (result != ISC_R_SUCCESS)
 619		goto cleanup;
 620
 621	if (taskmgr != NULL && timermgr != NULL) {
 622		result = isc_task_create(taskmgr, 1, &cleaner->task);
 623		if (result != ISC_R_SUCCESS) {
 624			UNEXPECTED_ERROR(__FILE__, __LINE__,
 625					 "isc_task_create() failed: %s",
 626					 dns_result_totext(result));
 627			result = ISC_R_UNEXPECTED;
 628			goto cleanup;
 629		}
 630		cleaner->cache->live_tasks++;
 631		isc_task_setname(cleaner->task, "cachecleaner", cleaner);
 632
 633		result = isc_task_onshutdown(cleaner->task,
 634					     cleaner_shutdown_action, cache);
 635		if (result != ISC_R_SUCCESS) {
 636			UNEXPECTED_ERROR(__FILE__, __LINE__,
 637					 "cache cleaner: "
 638					 "isc_task_onshutdown() failed: %s",
 639					 dns_result_totext(result));
 640			goto cleanup;
 641		}
 642
 643		result = isc_timer_create(timermgr, isc_timertype_inactive,
 644					   NULL, NULL, cleaner->task,
 645					   cleaning_timer_action, cleaner,
 646					   &cleaner->cleaning_timer);
 647		if (result != ISC_R_SUCCESS) {
 648			UNEXPECTED_ERROR(__FILE__, __LINE__,
 649					 "isc_timer_create() failed: %s",
 650					 dns_result_totext(result));
 651			result = ISC_R_UNEXPECTED;
 652			goto cleanup;
 653		}
 654
 655		cleaner->resched_event =
 656			isc_event_allocate(cache->mctx, cleaner,
 657					   DNS_EVENT_CACHECLEAN,
 658					   incremental_cleaning_action,
 659					   cleaner, sizeof(isc_event_t));
 660		if (cleaner->resched_event == NULL) {
 661			result = ISC_R_NOMEMORY;
 662			goto cleanup;
 663		}
 664
 665		cleaner->overmem_event =
 666			isc_event_allocate(cache->mctx, cleaner,
 667					   DNS_EVENT_CACHEOVERMEM,
 668					   overmem_cleaning_action,
 669					   cleaner, sizeof(isc_event_t));
 670		if (cleaner->overmem_event == NULL) {
 671			result = ISC_R_NOMEMORY;
 672			goto cleanup;
 673		}
 674	}
 675
 676	return (ISC_R_SUCCESS);
 677
 678 cleanup:
 679	if (cleaner->overmem_event != NULL)
 680		isc_event_free(&cleaner->overmem_event);
 681	if (cleaner->resched_event != NULL)
 682		isc_event_free(&cleaner->resched_event);
 683	if (cleaner->cleaning_timer != NULL)
 684		isc_timer_detach(&cleaner->cleaning_timer);
 685	if (cleaner->task != NULL)
 686		isc_task_detach(&cleaner->task);
 687	if (cleaner->iterator != NULL)
 688		dns_dbiterator_destroy(&cleaner->iterator);
 689	DESTROYLOCK(&cleaner->lock);
 690 fail:
 691	return (result);
 692}
 693
 694static void
 695begin_cleaning(cache_cleaner_t *cleaner) {
 696	isc_result_t result = ISC_R_SUCCESS;
 697
 698	REQUIRE(CLEANER_IDLE(cleaner));
 699
 700	/*
 701	 * Create an iterator, if it does not already exist, and
 702	 * position it at the beginning of the cache.
 703	 */
 704	if (cleaner->iterator == NULL)
 705		result = dns_db_createiterator(cleaner->cache->db, ISC_FALSE,
 706					       &cleaner->iterator);
 707	if (result != ISC_R_SUCCESS)
 708		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
 709			      DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
 710			      "cache cleaner could not create "
 711			      "iterator: %s", isc_result_totext(result));
 712	else {
 713		dns_dbiterator_setcleanmode(cleaner->iterator, ISC_TRUE);
 714		result = dns_dbiterator_first(cleaner->iterator);
 715	}
 716	if (result != ISC_R_SUCCESS) {
 717		/*
 718		 * If the result is ISC_R_NOMORE, the database is empty,
 719		 * so there is nothing to be cleaned.
 720		 */
 721		if (result != ISC_R_NOMORE && cleaner->iterator != NULL) {
 722			UNEXPECTED_ERROR(__FILE__, __LINE__,
 723					 "cache cleaner: "
 724					 "dns_dbiterator_first() failed: %s",
 725					 dns_result_totext(result));
 726			dns_dbiterator_destroy(&cleaner->iterator);
 727		} else if (cleaner->iterator != NULL) {
 728			result = dns_dbiterator_pause(cleaner->iterator);
 729			RUNTIME_CHECK(result == ISC_R_SUCCESS);
 730		}
 731	} else {
 732		/*
 733		 * Pause the iterator to free its lock.
 734		 */
 735		result = dns_dbiterator_pause(cleaner->iterator);
 736		RUNTIME_CHECK(result == ISC_R_SUCCESS);
 737
 738		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
 739			      DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1),
 740			      "begin cache cleaning, mem inuse %lu",
 741			    (unsigned long)isc_mem_inuse(cleaner->cache->mctx));
 742		cleaner->state = cleaner_s_busy;
 743		isc_task_send(cleaner->task, &cleaner->resched_event);
 744	}
 745
 746	return;
 747}
 748
 749static void
 750end_cleaning(cache_cleaner_t *cleaner, isc_event_t *event) {
 751	isc_result_t result;
 752
 753	REQUIRE(CLEANER_BUSY(cleaner));
 754	REQUIRE(event != NULL);
 755
 756	result = dns_dbiterator_pause(cleaner->iterator);
 757	if (result != ISC_R_SUCCESS)
 758		dns_dbiterator_destroy(&cleaner->iterator);
 759
 760	dns_cache_setcleaninginterval(cleaner->cache,
 761				      cleaner->cleaning_interval);
 762
 763	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
 764		      ISC_LOG_DEBUG(1), "end cache cleaning, mem inuse %lu",
 765		      (unsigned long)isc_mem_inuse(cleaner->cache->mctx));
 766
 767	cleaner->state = cleaner_s_idle;
 768	cleaner->resched_event = event;
 769}
 770
 771/*
 772 * This is run once for every cache-cleaning-interval as defined in named.conf.
 773 */
 774static void
 775cleaning_timer_action(isc_task_t *task, isc_event_t *event) {
 776	cache_cleaner_t *cleaner = event->ev_arg;
 777
 778	UNUSED(task);
 779
 780	INSIST(task == cleaner->task);
 781	INSIST(event->ev_type == ISC_TIMEREVENT_TICK);
 782
 783	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
 784		      ISC_LOG_DEBUG(1), "cache cleaning timer fired, "
 785		      "cleaner state = %d", cleaner->state);
 786
 787	if (cleaner->state == cleaner_s_idle)
 788		begin_cleaning(cleaner);
 789
 790	isc_event_free(&event);
 791}
 792
 793/*
 794 * This is called when the cache either surpasses its upper limit
 795 * or shrinks beyond its lower limit.
 796 */
 797static void
 798overmem_cleaning_action(isc_task_t *task, isc_event_t *event) {
 799	cache_cleaner_t *cleaner = event->ev_arg;
 800	isc_boolean_t want_cleaning = ISC_FALSE;
 801
 802	UNUSED(task);
 803
 804	INSIST(task == cleaner->task);
 805	INSIST(event->ev_type == DNS_EVENT_CACHEOVERMEM);
 806	INSIST(cleaner->overmem_event == NULL);
 807
 808	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
 809		      ISC_LOG_DEBUG(1), "overmem_cleaning_action called, "
 810		      "overmem = %d, state = %d", cleaner->overmem,
 811		      cleaner->state);
 812
 813	LOCK(&cleaner->lock);
 814
 815	if (cleaner->overmem) {
 816		if (cleaner->state == cleaner_s_idle)
 817			want_cleaning = ISC_TRUE;
 818	} else {
 819		if (cleaner->state == cleaner_s_busy)
 820			/*
 821			 * end_cleaning() can't be called here because
 822			 * then both cleaner->overmem_event and
 823			 * cleaner->resched_event will point to this
 824			 * event.  Set the state to done, and then
 825			 * when the incremental_cleaning_action() event
 826			 * is posted, it will handle the end_cleaning.
 827			 */
 828			cleaner->state = cleaner_s_done;
 829	}
 830
 831	cleaner->overmem_event = event;
 832
 833	UNLOCK(&cleaner->lock);
 834
 835	if (want_cleaning)
 836		begin_cleaning(cleaner);
 837}
 838
 839/*
 840 * Do incremental cleaning.
 841 */
 842static void
 843incremental_cleaning_action(isc_task_t *task, isc_event_t *event) {
 844	cache_cleaner_t *cleaner = event->ev_arg;
 845	isc_result_t result;
 846	unsigned int n_names;
 847	isc_time_t start;
 848
 849	UNUSED(task);
 850
 851	INSIST(task == cleaner->task);
 852	INSIST(event->ev_type == DNS_EVENT_CACHECLEAN);
 853
 854	if (cleaner->state == cleaner_s_done) {
 855		cleaner->state = cleaner_s_busy;
 856		end_cleaning(cleaner, event);
 857		LOCK(&cleaner->cache->lock);
 858		LOCK(&cleaner->lock);
 859		if (cleaner->replaceiterator) {
 860			dns_dbiterator_destroy(&cleaner->iterator);
 861			(void) dns_db_createiterator(cleaner->cache->db,
 862						     ISC_FALSE,
 863						     &cleaner->iterator);
 864			cleaner->replaceiterator = ISC_FALSE;
 865		}
 866		UNLOCK(&cleaner->lock);
 867		UNLOCK(&cleaner->cache->lock);
 868		return;
 869	}
 870
 871	INSIST(CLEANER_BUSY(cleaner));
 872
 873	n_names = cleaner->increment;
 874
 875	REQUIRE(DNS_DBITERATOR_VALID(cleaner->iterator));
 876
 877	isc_time_now(&start);
 878	while (n_names-- > 0) {
 879		dns_dbnode_t *node = NULL;
 880
 881		result = dns_dbiterator_current(cleaner->iterator, &node,
 882						NULL);
 883		if (result != ISC_R_SUCCESS) {
 884			UNEXPECTED_ERROR(__FILE__, __LINE__,
 885				 "cache cleaner: dns_dbiterator_current() "
 886				 "failed: %s", dns_result_totext(result));
 887
 888			end_cleaning(cleaner, event);
 889			return;
 890		}
 891
 892		/*
 893		 * The node was not needed, but was required by
 894		 * dns_dbiterator_current().  Give up its reference.
 895		 */
 896		dns_db_detachnode(cleaner->cache->db, &node);
 897
 898		/*
 899		 * Step to the next node.
 900		 */
 901		result = dns_dbiterator_next(cleaner->iterator);
 902
 903		if (result != ISC_R_SUCCESS) {
 904			/*
 905			 * Either the end was reached (ISC_R_NOMORE) or
 906			 * some error was signaled.  If the cache is still
 907			 * overmem and no error was encountered,
 908			 * keep trying to clean it, otherwise stop cleaning.
 909			 */
 910			if (result != ISC_R_NOMORE)
 911				UNEXPECTED_ERROR(__FILE__, __LINE__,
 912						 "cache cleaner: "
 913						 "dns_dbiterator_next() "
 914						 "failed: %s",
 915						 dns_result_totext(result));
 916			else if (cleaner->overmem) {
 917				result = dns_dbiterator_first(cleaner->
 918							      iterator);
 919				if (result == ISC_R_SUCCESS) {
 920					isc_log_write(dns_lctx,
 921						      DNS_LOGCATEGORY_DATABASE,
 922						      DNS_LOGMODULE_CACHE,
 923						      ISC_LOG_DEBUG(1),
 924						      "cache cleaner: "
 925						      "still overmem, "
 926						      "reset and try again");
 927					continue;
 928				}
 929			}
 930
 931			end_cleaning(cleaner, event);
 932			return;
 933		}
 934	}
 935
 936	/*
 937	 * We have successfully performed a cleaning increment but have
 938	 * not gone through the entire cache.  Free the iterator locks
 939	 * and reschedule another batch.  If it fails, just try to continue
 940	 * anyway.
 941	 */
 942	result = dns_dbiterator_pause(cleaner->iterator);
 943	RUNTIME_CHECK(result == ISC_R_SUCCESS);
 944
 945	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
 946		      ISC_LOG_DEBUG(1), "cache cleaner: checked %u nodes, "
 947		      "mem inuse %lu, sleeping", cleaner->increment,
 948		      (unsigned long)isc_mem_inuse(cleaner->cache->mctx));
 949
 950	isc_task_send(task, &event);
 951	INSIST(CLEANER_BUSY(cleaner));
 952	return;
 953}
 954
 955/*
 956 * Do immediate cleaning.
 957 */
 958isc_result_t
 959dns_cache_clean(dns_cache_t *cache, isc_stdtime_t now) {
 960	isc_result_t result;
 961	dns_dbiterator_t *iterator = NULL;
 962
 963	REQUIRE(VALID_CACHE(cache));
 964
 965	result = dns_db_createiterator(cache->db, 0, &iterator);
 966	if (result != ISC_R_SUCCESS)
 967		return result;
 968
 969	result = dns_dbiterator_first(iterator);
 970
 971	while (result == ISC_R_SUCCESS) {
 972		dns_dbnode_t *node = NULL;
 973		result = dns_dbiterator_current(iterator, &node,
 974						(dns_name_t *)NULL);
 975		if (result != ISC_R_SUCCESS)
 976			break;
 977
 978		/*
 979		 * Check TTLs, mark expired rdatasets stale.
 980		 */
 981		result = dns_db_expirenode(cache->db, node, now);
 982		if (result != ISC_R_SUCCESS) {
 983			UNEXPECTED_ERROR(__FILE__, __LINE__,
 984					 "cache cleaner: dns_db_expirenode() "
 985					 "failed: %s",
 986					 dns_result_totext(result));
 987			/*
 988			 * Continue anyway.
 989			 */
 990		}
 991
 992		/*
 993		 * This is where the actual freeing takes place.
 994		 */
 995		dns_db_detachnode(cache->db, &node);
 996
 997		result = dns_dbiterator_next(iterator);
 998	}
 999
1000	dns_dbiterator_destroy(&iterator);
1001
1002	if (result == ISC_R_NOMORE)
1003		result = ISC_R_SUCCESS;
1004
1005	return (result);
1006}
1007
1008static void
1009water(void *arg, int mark) {
1010	dns_cache_t *cache = arg;
1011	isc_boolean_t overmem = ISC_TF(mark == ISC_MEM_HIWATER);
1012
1013	REQUIRE(VALID_CACHE(cache));
1014
1015	LOCK(&cache->cleaner.lock);
1016
1017	if (overmem != cache->cleaner.overmem) {
1018		dns_db_overmem(cache->db, overmem);
1019		cache->cleaner.overmem = overmem;
1020		isc_mem_waterack(cache->mctx, mark);
1021	}
1022
1023	if (cache->cleaner.overmem_event != NULL)
1024		isc_task_send(cache->cleaner.task,
1025			      &cache->cleaner.overmem_event);
1026
1027	UNLOCK(&cache->cleaner.lock);
1028}
1029
1030void
1031dns_cache_setcachesize(dns_cache_t *cache, isc_uint32_t size) {
1032	isc_uint32_t lowater;
1033	isc_uint32_t hiwater;
1034
1035	REQUIRE(VALID_CACHE(cache));
1036
1037	/*
1038	 * Impose a minimum cache size; pathological things happen if there
1039	 * is too little room.
1040	 */
1041	if (size != 0 && size < DNS_CACHE_MINSIZE)
1042		size = DNS_CACHE_MINSIZE;
1043
1044	LOCK(&cache->lock);
1045	cache->size = size;
1046	UNLOCK(&cache->lock);
1047
1048	hiwater = size - (size >> 3);	/* Approximately 7/8ths. */
1049	lowater = size - (size >> 2);	/* Approximately 3/4ths. */
1050
1051	/*
1052	 * If the cache was overmem and cleaning, but now with the new limits
1053	 * it is no longer in an overmem condition, then the next
1054	 * isc_mem_put for cache memory will do the right thing and trigger
1055	 * water().
1056	 */
1057
1058	if (size == 0 || hiwater == 0 || lowater == 0)
1059		/*
1060		 * Disable cache memory limiting.
1061		 */
1062		isc_mem_setwater(cache->mctx, water, cache, 0, 0);
1063	else
1064		/*
1065		 * Establish new cache memory limits (either for the first
1066		 * time, or replacing other limits).
1067		 */
1068		isc_mem_setwater(cache->mctx, water, cache, hiwater, lowater);
1069}
1070
1071isc_uint32_t
1072dns_cache_getcachesize(dns_cache_t *cache) {
1073	isc_uint32_t size;
1074
1075	REQUIRE(VALID_CACHE(cache));
1076
1077	LOCK(&cache->lock);
1078	size = cache->size;
1079	UNLOCK(&cache->lock);
1080
1081	return (size);
1082}
1083
1084/*
1085 * The cleaner task is shutting down; do the necessary cleanup.
1086 */
1087static void
1088cleaner_shutdown_action(isc_task_t *task, isc_event_t *event) {
1089	dns_cache_t *cache = event->ev_arg;
1090	isc_boolean_t should_free = ISC_FALSE;
1091
1092	UNUSED(task);
1093
1094	INSIST(task == cache->cleaner.task);
1095	INSIST(event->ev_type == ISC_TASKEVENT_SHUTDOWN);
1096
1097	if (CLEANER_BUSY(&cache->cleaner))
1098		end_cleaning(&cache->cleaner, event);
1099	else
1100		isc_event_free(&event);
1101
1102	LOCK(&cache->lock);
1103
1104	cache->live_tasks--;
1105	INSIST(cache->live_tasks == 0);
1106
1107	if (cache->references == 0)
1108		should_free = ISC_TRUE;
1109
1110	/*
1111	 * By detaching the timer in the context of its task,
1112	 * we are guaranteed that there will be no further timer
1113	 * events.
1114	 */
1115	if (cache->cleaner.cleaning_timer != NULL)
1116		isc_timer_detach(&cache->cleaner.cleaning_timer);
1117
1118	/* Make sure we don't reschedule anymore. */
1119	(void)isc_task_purge(task, NULL, DNS_EVENT_CACHECLEAN, NULL);
1120
1121	UNLOCK(&cache->lock);
1122
1123	if (should_free)
1124		cache_free(cache);
1125}
1126
1127isc_result_t
1128dns_cache_flush(dns_cache_t *cache) {
1129	dns_db_t *db = NULL;
1130	isc_result_t result;
1131
1132	result = cache_create_db(cache, &db);
1133	if (result != ISC_R_SUCCESS)
1134		return (result);
1135
1136	LOCK(&cache->lock);
1137	LOCK(&cache->cleaner.lock);
1138	if (cache->cleaner.state == cleaner_s_idle) {
1139		if (cache->cleaner.iterator != NULL)
1140			dns_dbiterator_destroy(&cache->cleaner.iterator);
1141		(void) dns_db_createiterator(db, ISC_FALSE,
1142					     &cache->cleaner.iterator);
1143	} else {
1144		if (cache->cleaner.state == cleaner_s_busy)
1145			cache->cleaner.state = cleaner_s_done;
1146		cache->cleaner.replaceiterator = ISC_TRUE;
1147	}
1148	dns_db_detach(&cache->db);
1149	cache->db = db;
1150	UNLOCK(&cache->cleaner.lock);
1151	UNLOCK(&cache->lock);
1152
1153	return (ISC_R_SUCCESS);
1154}
1155
1156isc_result_t
1157dns_cache_flushname(dns_cache_t *cache, dns_name_t *name) {
1158	isc_result_t result;
1159	dns_rdatasetiter_t *iter = NULL;
1160	dns_dbnode_t *node = NULL;
1161	dns_db_t *db = NULL;
1162
1163	LOCK(&cache->lock);
1164	if (cache->db != NULL)
1165		dns_db_attach(cache->db, &db);
1166	UNLOCK(&cache->lock);
1167	if (db == NULL)
1168		return (ISC_R_SUCCESS);
1169	result = dns_db_findnode(cache->db, name, ISC_FALSE, &node);
1170	if (result == ISC_R_NOTFOUND) {
1171		result = ISC_R_SUCCESS;
1172		goto cleanup_db;
1173	}
1174	if (result != ISC_R_SUCCESS)
1175		goto cleanup_db;
1176
1177	result = dns_db_allrdatasets(cache->db, node, NULL,
1178				     (isc_stdtime_t)0, &iter);
1179	if (result != ISC_R_SUCCESS)
1180		goto cleanup_node;
1181
1182	for (result = dns_rdatasetiter_first(iter);
1183	     result == ISC_R_SUCCESS;
1184	     result = dns_rdatasetiter_next(iter))
1185	{
1186		dns_rdataset_t rdataset;
1187		dns_rdataset_init(&rdataset);
1188
1189		dns_rdatasetiter_current(iter, &rdataset);
1190		result = dns_db_deleterdataset(cache->db, node, NULL,
1191					       rdataset.type, rdataset.covers);
1192		dns_rdataset_disassociate(&rdataset);
1193		if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED)
1194			break;
1195	}
1196	if (result == ISC_R_NOMORE)
1197		result = ISC_R_SUCCESS;
1198
1199	dns_rdatasetiter_destroy(&iter);
1200
1201 cleanup_node:
1202	dns_db_detachnode(cache->db, &node);
1203
1204 cleanup_db:
1205	dns_db_detach(&db);
1206	return (result);
1207}