1 /*
2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3  *
4  * This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
7  *
8  * See the COPYRIGHT file distributed with this work for additional
9  * information regarding copyright ownership.
10  */
11 
12 #include <config.h>
13 
14 #include <isc/atomic.h>
15 #include <isc/event.h>
16 #include <isc/hash.h>
17 #include <isc/magic.h>
18 #include <isc/mem.h>
19 #include <isc/mutex.h>
20 #include <isc/platform.h>
21 #include <isc/random.h>
22 #include <isc/refcount.h>
23 #include <isc/rwlock.h>
24 #include <isc/serial.h>
25 #include <isc/task.h>
26 #include <isc/time.h>
27 #include <isc/timer.h>
28 #include <isc/util.h>
29 
30 #include <dns/acache.h>
31 #include <dns/db.h>
32 #include <dns/events.h>
33 #include <dns/log.h>
34 #include <dns/message.h>
35 #include <dns/name.h>
36 #include <dns/rdataset.h>
37 #include <dns/result.h>
38 #include <dns/zone.h>
39 
40 #if defined(ISC_PLATFORM_HAVESTDATOMIC)
41 #if defined(__cplusplus)
42 #include <isc/stdatomic.h>
43 #else
44 #include <stdatomic.h>
45 #endif
46 #endif
47 
48 #define ACACHE_MAGIC			ISC_MAGIC('A', 'C', 'H', 'E')
49 #define DNS_ACACHE_VALID(acache)	ISC_MAGIC_VALID(acache, ACACHE_MAGIC)
50 
51 #define ACACHEENTRY_MAGIC		ISC_MAGIC('A', 'C', 'E', 'T')
52 #define DNS_ACACHEENTRY_VALID(entry)	ISC_MAGIC_VALID(entry, ACACHEENTRY_MAGIC)
53 
54 #define DBBUCKETS	67
55 
56 #if 0
57 #define ATRACE(m)       isc_log_write(dns_lctx, \
58 				      DNS_LOGCATEGORY_DATABASE, \
59 				      DNS_LOGMODULE_ACACHE, \
60 				      ISC_LOG_DEBUG(3), \
61 				      "acache %p: %s", acache, (m))
62 #define AATRACE(a,m)    isc_log_write(dns_lctx, \
63 				      DNS_LOGCATEGORY_DATABASE, \
64 				      DNS_LOGMODULE_ACACHE, \
65 				      ISC_LOG_DEBUG(3), \
66 				      "acache %p: %s", (a), (m))
67 #else
68 #define ATRACE(m)
69 #define AATRACE(a, m)
70 #endif
71 
72 /*
73  * The following variables control incremental cleaning.
74  * MINSIZE is how many bytes is the floor for dns_acache_setcachesize().
75  * CLEANERINCREMENT is how many entries are examined in one pass.
76  * (XXX simply derived from definitions in cache.c  There may be better
77  *  constants here.)
78  */
79 #define DNS_ACACHE_MINSIZE 		2097152U /* Bytes.  2097152 = 2 MB */
80 #define DNS_ACACHE_CLEANERINCREMENT	1000	 /* Number of entries. */
81 
82 #define DEFAULT_ACACHE_ENTRY_LOCK_COUNT	1009	 /*%< Should be prime. */
83 
84 #if defined(ISC_RWLOCK_USEATOMIC) &&					\
85 	((defined(ISC_PLATFORM_HAVESTDATOMIC) && defined(ATOMIC_LONG_LOCK_FREE)) || \
86 	 defined(ISC_PLATFORM_HAVEATOMICSTORE))
87 #define ACACHE_USE_RWLOCK 1
88 #if (defined(ISC_PLATFORM_HAVESTDATOMIC) && defined(ATOMIC_LONG_LOCK_FREE))
89 #define ACACHE_HAVESTDATOMIC 1
90 #endif
91 #endif
92 
93 #ifdef ACACHE_USE_RWLOCK
94 #define ACACHE_INITLOCK(l)	isc_rwlock_init((l), 0, 0)
95 #define ACACHE_DESTROYLOCK(l)	isc_rwlock_destroy(l)
96 #define ACACHE_LOCK(l, t)	RWLOCK((l), (t))
97 #define ACACHE_UNLOCK(l, t)	RWUNLOCK((l), (t))
98 
99 #ifdef ACACHE_HAVESTDATOMIC
100 #define acache_storetime(entry, t) \
101 	atomic_store_explicit(&(entry)->lastused, (t), \
102 			      memory_order_relaxed);
103 #else
104 #define acache_storetime(entry, t) \
105 	(isc_atomic_store((int32_t *)&(entry)->lastused, (t)))
106 #endif
107 
108 #else
109 #define ACACHE_INITLOCK(l)	isc_mutex_init(l)
110 #define ACACHE_DESTROYLOCK(l)	DESTROYLOCK(l)
111 #define ACACHE_LOCK(l, t)	LOCK(l)
112 #define ACACHE_UNLOCK(l, t)	UNLOCK(l)
113 
114 #define acache_storetime(entry, t) ((entry)->lastused = (t))
115 #endif
116 
117 /* Locked by acache lock */
118 typedef struct dbentry {
119 	ISC_LINK(struct dbentry)	link;
120 
121 	dns_db_t			*db;
122 	ISC_LIST(dns_acacheentry_t)	originlist;
123 	ISC_LIST(dns_acacheentry_t)	referlist;
124 } dbentry_t;
125 
126 typedef ISC_LIST(dbentry_t) dbentrylist_t;
127 
128 typedef struct acache_cleaner acache_cleaner_t;
129 
130 typedef enum {
131 	cleaner_s_idle,	/* Waiting for cleaning-interval to expire. */
132 	cleaner_s_busy,	/* Currently cleaning. */
133 	cleaner_s_done  /* Freed enough memory after being overmem. */
134 } cleaner_state_t;
135 
136 /*
137  * Convenience macros for comprehensive assertion checking.
138  */
139 #define CLEANER_IDLE(c) ((c)->state == cleaner_s_idle && \
140 			 (c)->resched_event != NULL)
141 #define CLEANER_BUSY(c) ((c)->state == cleaner_s_busy && \
142 			 (c)->resched_event == NULL)
143 
144 struct acache_cleaner {
145 	isc_mutex_t		lock;
146 	/*
147 	 * Locks overmem_event, overmem.  (See cache.c)
148 	 */
149 
150 	dns_acache_t		*acache;
151 	unsigned int		cleaning_interval; /* The cleaning-interval
152 						      from named.conf,
153 						      in seconds. */
154 
155 	isc_stdtime_t		last_cleanup_time; /* The time when the last
156 						      cleanup task completed */
157 
158 	isc_timer_t 		*cleaning_timer;
159 	isc_event_t		*resched_event;	/* Sent by cleaner task to
160 						   itself to reschedule */
161 	isc_event_t		*overmem_event;
162 
163 	dns_acacheentry_t	*current_entry;	/* The bookmark entry to
164 						   restart the cleaning.
165 						   Locked by acache lock. */
166 	int 		 	increment;	/* Number of entries to
167 						   clean in one increment */
168 
169 	unsigned long		ncleaned;	/* Number of entries cleaned
170 						   up (for logging purposes) */
171 	cleaner_state_t  	state;		/* Idle/Busy/Done. */
172 	bool	 	overmem;	/* The acache is in an overmem
173 						   state. */
174 };
175 
176 struct dns_acachestats {
177 #ifdef ACACHE_HAVESTDATOMIC
178 	_Atomic(unsigned int)		hits;
179 	_Atomic(unsigned int)		queries;
180 	_Atomic(unsigned int)		misses;
181 #define ACACHE_STATSLOCK(x) (void)0
182 #define ACACHE_STATSUNLOCK(x) (void)0
183 #define ACACHE_INC(x)	atomic_fetch_add(&(x), 1)
184 #define ACACHE_LOAD(x)  atomic_load(&(x))
185 #else
186 	unsigned int			hits;
187 	unsigned int			queries;
188 	unsigned int			misses;
189 #if defined(ISC_PLATFORM_HAVEXADD)
190 #define ACACHE_STATSLOCK(x) (void)0
191 #define ACACHE_STATSUNLOCK(x) (void)0
192 #define ACACHE_INC(x) isc_atomic_xadd((int32_t*)&(x), 1)
193 #define ACACHE_LOAD(x) isc_atomic_xadd((int32_t*)&(x), 0)
194 #else
195 	isc_mutex_t			lock;
196 #define ISC_HAVE_STATSLOCK
197 #define ACACHE_STATSLOCK(l)	LOCK(l)
198 #define ACACHE_STATSUNLOCK(l)	UNLOCK(l)
199 #define ACACHE_INC(x) ((x)++)
200 #define ACACHE_LOAD(x) (x)
201 #endif
202 #endif
203 	unsigned int			adds;
204 	unsigned int			deleted;
205 	unsigned int			cleaned;
206 	unsigned int			cleaner_runs;
207 	unsigned int			overmem;
208 	unsigned int			overmem_nocreates;
209 	unsigned int			nomem;
210 };
211 
212 /*
213  * The actual acache object.
214  */
215 
216 struct dns_acache {
217 	unsigned int			magic;
218 
219 	isc_mem_t			*mctx;
220 	isc_refcount_t			refs;
221 
222 #ifdef ACACHE_USE_RWLOCK
223 	isc_rwlock_t 			*entrylocks;
224 #else
225 	isc_mutex_t 			*entrylocks;
226 #endif
227 
228 	isc_mutex_t			lock;
229 
230 	int				live_cleaners;
231 	acache_cleaner_t		cleaner;
232 	ISC_LIST(dns_acacheentry_t)	entries;
233 	unsigned int			dbentries;
234 	dbentrylist_t			dbbucket[DBBUCKETS];
235 
236 	bool			shutting_down;
237 
238 	isc_task_t 			*task;
239 	isc_event_t			cevent;
240 	bool			cevent_sent;
241 
242 	dns_acachestats_t		stats;
243 };
244 
245 struct dns_acacheentry {
246 	unsigned int 		magic;
247 
248 	unsigned int		locknum;
249 	isc_refcount_t 		references;
250 
251 	dns_acache_t 		*acache;
252 
253 	/* Data for Management of cache entries */
254 	ISC_LINK(dns_acacheentry_t) link;
255 	ISC_LINK(dns_acacheentry_t) olink;
256 	ISC_LINK(dns_acacheentry_t) rlink;
257 
258 	dns_db_t 		*origdb; /* reference to the DB
259 					    holding this entry */
260 
261 	/* Cache data */
262 	dns_zone_t 		*zone;		/* zone this entry
263 						   belongs to */
264 	dns_db_t		*db;   		/* DB this entry belongs to */
265 	dns_dbversion_t		*version;	/* the version of the DB */
266 	dns_dbnode_t 		*node;		/* node this entry
267 						   belongs to */
268 	dns_name_t 		*foundname;	/* corresponding DNS name
269 						   and rdataset */
270 
271 	/* Callback function and its argument */
272 	void 			(*callback)(dns_acacheentry_t *, void **);
273 	void 			*cbarg;
274 
275 	/* Timestamp of the last time this entry is referred to */
276 #ifdef ACACHE_HAVESTDATOMIC
277 	atomic_uint_fast32_t	lastused;
278 #else
279 	isc_stdtime_t		lastused;
280 #endif
281 };
282 
283 /*
284  *	Internal functions (and prototypes).
285  */
286 static inline bool check_noentry(dns_acache_t *acache);
287 static void destroy(dns_acache_t *acache);
288 static void shutdown_entries(dns_acache_t *acache);
289 static void shutdown_buckets(dns_acache_t *acache);
290 static void destroy_entry(dns_acacheentry_t *ent);
291 static inline void unlink_dbentries(dns_acache_t *acache,
292 				    dns_acacheentry_t *ent);
293 static inline isc_result_t finddbent(dns_acache_t *acache,
294 				     dns_db_t *db, dbentry_t **dbentryp);
295 static inline void clear_entry(dns_acache_t *acache, dns_acacheentry_t *entry);
296 static isc_result_t acache_cleaner_init(dns_acache_t *acache,
297 					isc_timermgr_t *timermgr,
298 					acache_cleaner_t *cleaner);
299 static void acache_cleaning_timer_action(isc_task_t *task, isc_event_t *event);
300 static void acache_incremental_cleaning_action(isc_task_t *task,
301 					       isc_event_t *event);
302 static void acache_overmem_cleaning_action(isc_task_t *task,
303 					   isc_event_t *event);
304 static void acache_cleaner_shutdown_action(isc_task_t *task,
305 					   isc_event_t *event);
306 
307 /*
308  * acache should be locked.  If it is not, the stats can get out of whack,
309  * which is not a big deal for us since this is for debugging / stats
310  */
311 static void
reset_stats(dns_acache_t * acache)312 reset_stats(dns_acache_t *acache) {
313 	acache->stats.hits = 0;
314 	acache->stats.queries = 0;
315 	acache->stats.misses = 0;
316 	acache->stats.adds = 0;
317 	acache->stats.deleted = 0;
318 	acache->stats.cleaned = 0;
319 	acache->stats.overmem = 0;
320 	acache->stats.overmem_nocreates = 0;
321 	acache->stats.nomem = 0;
322 }
323 
324 /*
325  * The acache must be locked before calling.
326  */
327 static inline bool
check_noentry(dns_acache_t * acache)328 check_noentry(dns_acache_t *acache) {
329 	if (ISC_LIST_EMPTY(acache->entries) && acache->dbentries == 0) {
330 		return (true);
331 	}
332 
333 	return (false);
334 }
335 
336 /*
337  * The acache must be locked before calling.
338  */
339 static void
shutdown_entries(dns_acache_t * acache)340 shutdown_entries(dns_acache_t *acache) {
341 	dns_acacheentry_t *entry, *entry_next;
342 
343 	REQUIRE(DNS_ACACHE_VALID(acache));
344 	INSIST(acache->shutting_down);
345 
346 	/*
347 	 * Release the dependency of all entries, and detach them.
348 	 */
349 	for (entry = ISC_LIST_HEAD(acache->entries);
350 	     entry != NULL;
351 	     entry = entry_next) {
352 		entry_next = ISC_LIST_NEXT(entry, link);
353 
354 		ACACHE_LOCK(&acache->entrylocks[entry->locknum],
355 			    isc_rwlocktype_write);
356 
357 		/*
358 		 * If the cleaner holds this entry, it will be unlinked and
359 		 * freed in the cleaner later.
360 		 */
361 		if (acache->cleaner.current_entry != entry)
362 			ISC_LIST_UNLINK(acache->entries, entry, link);
363 		unlink_dbentries(acache, entry);
364 		if (entry->callback != NULL) {
365 			(entry->callback)(entry, &entry->cbarg);
366 			entry->callback = NULL;
367 		}
368 
369 		ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
370 			      isc_rwlocktype_write);
371 
372 		if (acache->cleaner.current_entry != entry)
373 			dns_acache_detachentry(&entry);
374 	}
375 }
376 
377 /*
378  * The acache must be locked before calling.
379  */
380 static void
shutdown_buckets(dns_acache_t * acache)381 shutdown_buckets(dns_acache_t *acache) {
382 	int i;
383 	dbentry_t *dbent;
384 
385 	REQUIRE(DNS_ACACHE_VALID(acache));
386 	INSIST(acache->shutting_down);
387 
388 	for (i = 0; i < DBBUCKETS; i++) {
389 		while ((dbent = ISC_LIST_HEAD(acache->dbbucket[i])) != NULL) {
390 			INSIST(ISC_LIST_EMPTY(dbent->originlist) &&
391 			       ISC_LIST_EMPTY(dbent->referlist));
392 			ISC_LIST_UNLINK(acache->dbbucket[i], dbent, link);
393 
394 			dns_db_detach(&dbent->db);
395 
396 			isc_mem_put(acache->mctx, dbent, sizeof(*dbent));
397 
398 			acache->dbentries--;
399 		}
400 	}
401 
402 	INSIST(acache->dbentries == 0);
403 }
404 
405 static void
shutdown_task(isc_task_t * task,isc_event_t * ev)406 shutdown_task(isc_task_t *task, isc_event_t *ev) {
407 	dns_acache_t *acache;
408 
409 	UNUSED(task);
410 
411 	acache = ev->ev_arg;
412 	INSIST(DNS_ACACHE_VALID(acache));
413 
414 	isc_event_free(&ev);
415 
416 	LOCK(&acache->lock);
417 
418 	shutdown_entries(acache);
419 	shutdown_buckets(acache);
420 
421 	UNLOCK(&acache->lock);
422 
423 	dns_acache_detach(&acache);
424 }
425 
426 /* The acache and the entry must be locked before calling. */
427 static inline void
unlink_dbentries(dns_acache_t * acache,dns_acacheentry_t * ent)428 unlink_dbentries(dns_acache_t *acache, dns_acacheentry_t *ent) {
429 	isc_result_t result;
430 	dbentry_t *dbent;
431 
432 	if (ISC_LINK_LINKED(ent, olink)) {
433 		INSIST(ent->origdb != NULL);
434 		dbent = NULL;
435 		result = finddbent(acache, ent->origdb, &dbent);
436 		INSIST(result == ISC_R_SUCCESS);
437 
438 		ISC_LIST_UNLINK(dbent->originlist, ent, olink);
439 	}
440 	if (ISC_LINK_LINKED(ent, rlink)) {
441 		INSIST(ent->db != NULL);
442 		dbent = NULL;
443 		result = finddbent(acache, ent->db, &dbent);
444 		INSIST(result == ISC_R_SUCCESS);
445 
446 		ISC_LIST_UNLINK(dbent->referlist, ent, rlink);
447 	}
448 }
449 
450 /* There must not be a reference to this entry. */
451 static void
destroy_entry(dns_acacheentry_t * entry)452 destroy_entry(dns_acacheentry_t *entry) {
453 	dns_acache_t *acache;
454 
455 	REQUIRE(DNS_ACACHEENTRY_VALID(entry));
456 
457 	acache = entry->acache;
458 	REQUIRE(DNS_ACACHE_VALID(acache));
459 
460 	/*
461 	 * Since there is no reference to this entry, it is safe to call
462 	 * clear_entry() here.
463 	 */
464 	clear_entry(acache, entry);
465 
466 	isc_mem_put(acache->mctx, entry, sizeof(*entry));
467 
468 	dns_acache_detach(&acache);
469 }
470 
471 static void
destroy(dns_acache_t * acache)472 destroy(dns_acache_t *acache) {
473 	int i;
474 
475 	REQUIRE(DNS_ACACHE_VALID(acache));
476 
477 	ATRACE("destroy");
478 
479 	isc_mem_setwater(acache->mctx, NULL, NULL, 0, 0);
480 
481 	if (acache->cleaner.overmem_event != NULL)
482 		isc_event_free(&acache->cleaner.overmem_event);
483 
484 	if (acache->cleaner.resched_event != NULL)
485 		isc_event_free(&acache->cleaner.resched_event);
486 
487 	if (acache->task != NULL)
488 		isc_task_detach(&acache->task);
489 
490 	for (i = 0; i < DEFAULT_ACACHE_ENTRY_LOCK_COUNT; i++)
491 		ACACHE_DESTROYLOCK(&acache->entrylocks[i]);
492 	isc_mem_put(acache->mctx, acache->entrylocks,
493 		    sizeof(*acache->entrylocks) *
494 		    DEFAULT_ACACHE_ENTRY_LOCK_COUNT);
495 
496 	DESTROYLOCK(&acache->cleaner.lock);
497 
498 #ifdef ISC_HAVE_STATSLOCK
499 	DESTROYLOCK(&acache->stats.lock);
500 #endif
501 
502 	DESTROYLOCK(&acache->lock);
503 	acache->magic = 0;
504 
505 	isc_mem_putanddetach(&acache->mctx, acache, sizeof(*acache));
506 }
507 
508 static inline isc_result_t
finddbent(dns_acache_t * acache,dns_db_t * db,dbentry_t ** dbentryp)509 finddbent(dns_acache_t *acache, dns_db_t *db, dbentry_t **dbentryp) {
510 	int bucket;
511 	dbentry_t *dbentry;
512 
513 	REQUIRE(DNS_ACACHE_VALID(acache));
514 	REQUIRE(db != NULL);
515 	REQUIRE(dbentryp != NULL && *dbentryp == NULL);
516 
517 	/*
518 	 * The caller must be holding the acache lock.
519 	 */
520 
521 	bucket = isc_hash_function(&db, sizeof(db), true, NULL) % DBBUCKETS;
522 
523 	for (dbentry = ISC_LIST_HEAD(acache->dbbucket[bucket]);
524 	     dbentry != NULL;
525 	     dbentry = ISC_LIST_NEXT(dbentry, link)) {
526 		if (dbentry->db == db)
527 			break;
528 	}
529 
530 	*dbentryp = dbentry;
531 
532 	if (dbentry == NULL)
533 		return (ISC_R_NOTFOUND);
534 	else
535 		return (ISC_R_SUCCESS);
536 }
537 
538 static inline void
clear_entry(dns_acache_t * acache,dns_acacheentry_t * entry)539 clear_entry(dns_acache_t *acache, dns_acacheentry_t *entry) {
540 	REQUIRE(DNS_ACACHE_VALID(acache));
541 	REQUIRE(DNS_ACACHEENTRY_VALID(entry));
542 
543 	/*
544 	 * The caller must be holing the entry lock.
545 	 */
546 
547 	if (entry->foundname) {
548 		dns_rdataset_t *rdataset, *rdataset_next;
549 
550 		for (rdataset = ISC_LIST_HEAD(entry->foundname->list);
551 		     rdataset != NULL;
552 		     rdataset = rdataset_next) {
553 			rdataset_next = ISC_LIST_NEXT(rdataset, link);
554 			ISC_LIST_UNLINK(entry->foundname->list,
555 					rdataset, link);
556 			dns_rdataset_disassociate(rdataset);
557 			isc_mem_put(acache->mctx, rdataset, sizeof(*rdataset));
558 		}
559 		if (dns_name_dynamic(entry->foundname))
560 			dns_name_free(entry->foundname, acache->mctx);
561 		isc_mem_put(acache->mctx, entry->foundname,
562 			    sizeof(*entry->foundname));
563 		entry->foundname = NULL;
564 	}
565 
566 	if (entry->node != NULL) {
567 		INSIST(entry->db != NULL);
568 		dns_db_detachnode(entry->db, &entry->node);
569 	}
570 	if (entry->version != NULL) {
571 		INSIST(entry->db != NULL);
572 		dns_db_closeversion(entry->db, &entry->version, false);
573 	}
574 	if (entry->db != NULL)
575 		dns_db_detach(&entry->db);
576 	if (entry->zone != NULL)
577 		dns_zone_detach(&entry->zone);
578 
579 	if (entry->origdb != NULL)
580 		dns_db_detach(&entry->origdb);
581 }
582 
583 static isc_result_t
acache_cleaner_init(dns_acache_t * acache,isc_timermgr_t * timermgr,acache_cleaner_t * cleaner)584 acache_cleaner_init(dns_acache_t *acache, isc_timermgr_t *timermgr,
585 		    acache_cleaner_t *cleaner)
586 {
587 	int result;
588 
589 	ATRACE("acache cleaner init");
590 
591 	result = isc_mutex_init(&cleaner->lock);
592 	if (result != ISC_R_SUCCESS)
593 		goto fail;
594 
595 	cleaner->increment = DNS_ACACHE_CLEANERINCREMENT;
596 	cleaner->state = cleaner_s_idle;
597 	cleaner->acache = acache;
598 	cleaner->overmem = false;
599 
600 	cleaner->cleaning_timer = NULL;
601 	cleaner->resched_event = NULL;
602 	cleaner->overmem_event = NULL;
603 	cleaner->current_entry = NULL;
604 
605 	if (timermgr != NULL) {
606 		cleaner->acache->live_cleaners++;
607 
608 		result = isc_task_onshutdown(acache->task,
609 					     acache_cleaner_shutdown_action,
610 					     acache);
611 		if (result != ISC_R_SUCCESS) {
612 			UNEXPECTED_ERROR(__FILE__, __LINE__,
613 					 "acache cleaner: "
614 					 "isc_task_onshutdown() failed: %s",
615 					 dns_result_totext(result));
616 			goto cleanup;
617 		}
618 
619 		cleaner->cleaning_interval = 0; /* Initially turned off. */
620 		isc_stdtime_get(&cleaner->last_cleanup_time);
621 		result = isc_timer_create(timermgr, isc_timertype_inactive,
622 					  NULL, NULL,
623 					  acache->task,
624 					  acache_cleaning_timer_action,
625 					  cleaner, &cleaner->cleaning_timer);
626 		if (result != ISC_R_SUCCESS) {
627 			UNEXPECTED_ERROR(__FILE__, __LINE__,
628 					 "isc_timer_create() failed: %s",
629 					 dns_result_totext(result));
630 			result = ISC_R_UNEXPECTED;
631 			goto cleanup;
632 		}
633 
634 		cleaner->resched_event =
635 			isc_event_allocate(acache->mctx, cleaner,
636 					   DNS_EVENT_ACACHECLEAN,
637 					   acache_incremental_cleaning_action,
638 					   cleaner, sizeof(isc_event_t));
639 		if (cleaner->resched_event == NULL) {
640 			result = ISC_R_NOMEMORY;
641 			goto cleanup;
642 		}
643 
644 		cleaner->overmem_event =
645 			isc_event_allocate(acache->mctx, cleaner,
646 					   DNS_EVENT_ACACHEOVERMEM,
647 					   acache_overmem_cleaning_action,
648 					   cleaner, sizeof(isc_event_t));
649 		if (cleaner->overmem_event == NULL) {
650 			result = ISC_R_NOMEMORY;
651 			goto cleanup;
652 		}
653 	}
654 
655 	return (ISC_R_SUCCESS);
656 
657  cleanup:
658 	if (cleaner->overmem_event != NULL)
659 		isc_event_free(&cleaner->overmem_event);
660 	if (cleaner->resched_event != NULL)
661 		isc_event_free(&cleaner->resched_event);
662 	if (cleaner->cleaning_timer != NULL)
663 		isc_timer_detach(&cleaner->cleaning_timer);
664 	cleaner->acache->live_cleaners--;
665 	DESTROYLOCK(&cleaner->lock);
666  fail:
667 	return (result);
668 }
669 
670 static void
begin_cleaning(acache_cleaner_t * cleaner)671 begin_cleaning(acache_cleaner_t *cleaner) {
672 	dns_acacheentry_t *head;
673 	dns_acache_t *acache = cleaner->acache;
674 
675 	/*
676 	 * This function does not have to lock the cleaner, since critical
677 	 * parameters (except current_entry, which is locked by acache lock,)
678 	 * are only used in a single task context.
679 	 */
680 
681 	REQUIRE(CLEANER_IDLE(cleaner));
682 	INSIST(DNS_ACACHE_VALID(acache));
683 	INSIST(cleaner->current_entry == NULL);
684 
685 	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
686 		      DNS_LOGMODULE_ACACHE, ISC_LOG_DEBUG(1),
687 		      "begin acache cleaning, mem inuse %lu",
688 		      (unsigned long)isc_mem_inuse(cleaner->acache->mctx));
689 
690 	LOCK(&acache->lock);
691 
692 	head = ISC_LIST_HEAD(acache->entries);
693 	if (head != NULL)
694 		dns_acache_attachentry(head, &cleaner->current_entry);
695 
696 	UNLOCK(&acache->lock);
697 
698 	if (cleaner->current_entry != NULL) {
699 		cleaner->ncleaned = 0;
700 		cleaner->state = cleaner_s_busy;
701 		isc_task_send(acache->task, &cleaner->resched_event);
702 	}
703 
704 	return;
705 }
706 
707 static void
end_cleaning(acache_cleaner_t * cleaner,isc_event_t * event)708 end_cleaning(acache_cleaner_t *cleaner, isc_event_t *event) {
709 	dns_acache_t *acache = cleaner->acache;
710 
711 	REQUIRE(CLEANER_BUSY(cleaner));
712 	REQUIRE(event != NULL);
713 	REQUIRE(DNS_ACACHEENTRY_VALID(cleaner->current_entry));
714 
715 	/* No need to lock the cleaner (see begin_cleaning()). */
716 
717 	LOCK(&acache->lock);
718 
719 	/*
720 	 * Even if the cleaner has the last reference to the entry, which means
721 	 * the entry has been unused, it may still be linked if unlinking the
722 	 * entry has been delayed due to the reference.
723 	 */
724 	if (isc_refcount_current(&cleaner->current_entry->references) == 1) {
725 		INSIST(cleaner->current_entry->callback == NULL);
726 
727 		if (ISC_LINK_LINKED(cleaner->current_entry, link)) {
728 			ISC_LIST_UNLINK(acache->entries,
729 					cleaner->current_entry, link);
730 		}
731 	}
732 	dns_acache_detachentry(&cleaner->current_entry);
733 
734 	if (cleaner->overmem)
735 		acache->stats.overmem++;
736 	acache->stats.cleaned += cleaner->ncleaned;
737 	acache->stats.cleaner_runs++;
738 
739 	ACACHE_STATSLOCK(&acache->stats.lock);
740 	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE,
741 		      ISC_LOG_NOTICE,
742 		      "acache %p stats: hits=%d misses=%d queries=%d "
743 		      "adds=%d deleted=%d "
744 		      "cleaned=%d cleaner_runs=%d overmem=%d "
745 		      "overmem_nocreates=%d nomem=%d",
746 		      acache,
747 		      ACACHE_LOAD(acache->stats.hits),
748 		      ACACHE_LOAD(acache->stats.misses),
749 		      ACACHE_LOAD(acache->stats.queries),
750 		      acache->stats.adds, acache->stats.deleted,
751 		      acache->stats.cleaned, acache->stats.cleaner_runs,
752 		      acache->stats.overmem, acache->stats.overmem_nocreates,
753 		      acache->stats.nomem);
754 	reset_stats(acache);
755 	ACACHE_STATSUNLOCK(&acache->stats.lock);
756 
757 	isc_stdtime_get(&cleaner->last_cleanup_time);
758 
759 	UNLOCK(&acache->lock);
760 
761 	dns_acache_setcleaninginterval(cleaner->acache,
762 				       cleaner->cleaning_interval);
763 
764 	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE,
765 		      ISC_LOG_DEBUG(1), "end acache cleaning, "
766 		      "%lu entries cleaned, mem inuse %lu",
767 		      cleaner->ncleaned,
768 		      (unsigned long)isc_mem_inuse(cleaner->acache->mctx));
769 
770 	if (cleaner->overmem) {
771 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
772 			      DNS_LOGMODULE_ACACHE, ISC_LOG_NOTICE,
773 			      "acache is still in overmem state "
774 			      "after cleaning");
775 	}
776 
777 	cleaner->ncleaned = 0;
778 	cleaner->state = cleaner_s_idle;
779 	cleaner->resched_event = event;
780 }
781 
782 /*
783  * This is run once for every acache-cleaning-interval as defined
784  * in named.conf.
785  */
786 static void
acache_cleaning_timer_action(isc_task_t * task,isc_event_t * event)787 acache_cleaning_timer_action(isc_task_t *task, isc_event_t *event) {
788 	acache_cleaner_t *cleaner = event->ev_arg;
789 
790 	UNUSED(task);
791 
792 	INSIST(event->ev_type == ISC_TIMEREVENT_TICK);
793 
794 	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE,
795 		      ISC_LOG_DEBUG(1), "acache cleaning timer fired, "
796 		      "cleaner state = %d", cleaner->state);
797 
798 	if (cleaner->state == cleaner_s_idle)
799 		begin_cleaning(cleaner);
800 
801 	isc_event_free(&event);
802 }
803 
804 /* The caller must hold entry lock. */
805 static inline bool
entry_stale(acache_cleaner_t * cleaner,dns_acacheentry_t * entry,isc_stdtime_t now32,unsigned int interval)806 entry_stale(acache_cleaner_t *cleaner, dns_acacheentry_t *entry,
807 	    isc_stdtime_t now32, unsigned int interval)
808 {
809 	/*
810 	 * If the callback has been canceled, we definitely do not need the
811 	 * entry.
812 	 */
813 	if (entry->callback == NULL)
814 		return (true);
815 
816 	if (interval > cleaner->cleaning_interval)
817 		interval = cleaner->cleaning_interval;
818 
819 	if (entry->lastused + interval < now32)
820 		return (true);
821 
822 	/*
823 	 * If the acache is in the overmem state, probabilistically decide if
824 	 * the entry should be purged, based on the time passed from its last
825 	 * use and the cleaning interval.
826 	 */
827 	if (cleaner->overmem) {
828 		unsigned int passed;
829 		uint32_t val;
830 
831 		if (isc_serial_ge(now32, entry->lastused))
832 			passed = now32 - entry->lastused; /* <= interval */
833 		else
834 			passed = 0;
835 
836 		if (passed > interval / 2)
837 			return (true);
838 		isc_random_get(&val);
839 		if (passed > interval / 4)
840 			return (val % 4 == 0);
841 		return (val % 8 == 0);
842 	}
843 
844 	return (false);
845 }
846 
847 /*
848  * Do incremental cleaning.
849  */
850 static void
acache_incremental_cleaning_action(isc_task_t * task,isc_event_t * event)851 acache_incremental_cleaning_action(isc_task_t *task, isc_event_t *event) {
852 	acache_cleaner_t *cleaner = event->ev_arg;
853 	dns_acache_t *acache = cleaner->acache;
854 	dns_acacheentry_t *entry, *next = NULL;
855 	int n_entries;
856 	isc_stdtime_t now32, last32;
857 	isc_stdtime_t now;
858 	unsigned int interval;
859 
860 	INSIST(DNS_ACACHE_VALID(acache));
861 	INSIST(task == acache->task);
862 	INSIST(event->ev_type == DNS_EVENT_ACACHECLEAN);
863 
864 	if (cleaner->state == cleaner_s_done) {
865 		cleaner->state = cleaner_s_busy;
866 		end_cleaning(cleaner, event);
867 		return;
868 	}
869 
870 	INSIST(CLEANER_BUSY(cleaner));
871 
872 	n_entries = cleaner->increment;
873 
874 	isc_stdtime_get(&now);
875 	isc_stdtime_convert32(now, &now32);
876 
877 	LOCK(&acache->lock);
878 
879 	entry = cleaner->current_entry;
880 	isc_stdtime_convert32(cleaner->last_cleanup_time, &last32);
881 	if (isc_serial_ge(now32, last32))
882 		interval = now32 - last32;
883 	else
884 		interval = 0;
885 
886 	while (n_entries-- > 0) {
887 		bool is_stale = false;
888 
889 		INSIST(entry != NULL);
890 
891 		next = ISC_LIST_NEXT(entry, link);
892 
893 		ACACHE_LOCK(&acache->entrylocks[entry->locknum],
894 			    isc_rwlocktype_write);
895 
896 		is_stale = entry_stale(cleaner, entry, now32, interval);
897 		if (is_stale) {
898 			ISC_LIST_UNLINK(acache->entries, entry, link);
899 			unlink_dbentries(acache, entry);
900 			if (entry->callback != NULL)
901 				(entry->callback)(entry, &entry->cbarg);
902 			entry->callback = NULL;
903 
904 			cleaner->ncleaned++;
905 		}
906 
907 		ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
908 			      isc_rwlocktype_write);
909 
910 		if (is_stale)
911 			dns_acache_detachentry(&entry);
912 
913 		if (next == NULL) {
914 			if (cleaner->overmem) {
915 				entry = ISC_LIST_HEAD(acache->entries);
916 				if (entry != NULL) {
917 					/*
918 					 * If we are still in the overmem
919 					 * state, keep cleaning.  In case we
920 					 * exit from the loop immediately after
921 					 * this, reset next to the head entry
922 					 * as we'll expect it will be never
923 					 * NULL.
924 					 */
925 					isc_log_write(dns_lctx,
926 						      DNS_LOGCATEGORY_DATABASE,
927 						      DNS_LOGMODULE_ACACHE,
928 						      ISC_LOG_DEBUG(1),
929 						      "acache cleaner: "
930 						      "still overmem, "
931 						      "reset and try again");
932 					next = entry;
933 					continue;
934 				}
935 			}
936 
937 			UNLOCK(&acache->lock);
938 			end_cleaning(cleaner, event);
939 			return;
940 		}
941 
942 		entry = next;
943 	}
944 
945 	/*
946 	 * We have successfully performed a cleaning increment but have
947 	 * not gone through the entire cache.  Remember the entry that will
948 	 * be the starting point in the next clean-up, and reschedule another
949 	 * batch.  If it fails, just try to continue anyway.
950 	 */
951 	INSIST(next != NULL);
952 	dns_acache_detachentry(&cleaner->current_entry);
953 	dns_acache_attachentry(next, &cleaner->current_entry);
954 
955 	UNLOCK(&acache->lock);
956 
957 	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE,
958 		      ISC_LOG_DEBUG(1), "acache cleaner: checked %d entries, "
959 		      "mem inuse %lu, sleeping", cleaner->increment,
960 		      (unsigned long)isc_mem_inuse(cleaner->acache->mctx));
961 
962 	isc_task_send(task, &event);
963 	INSIST(CLEANER_BUSY(cleaner));
964 
965 	return;
966 }
967 
968 /*
969  * This is called when the acache either surpasses its upper limit
970  * or shrinks beyond its lower limit.
971  */
972 static void
acache_overmem_cleaning_action(isc_task_t * task,isc_event_t * event)973 acache_overmem_cleaning_action(isc_task_t *task, isc_event_t *event) {
974 	acache_cleaner_t *cleaner = event->ev_arg;
975 	bool want_cleaning = false;
976 
977 	UNUSED(task);
978 
979 	INSIST(event->ev_type == DNS_EVENT_ACACHEOVERMEM);
980 	INSIST(cleaner->overmem_event == NULL);
981 
982 	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE,
983 		      ISC_LOG_DEBUG(1), "overmem_cleaning_action called, "
984 		      "overmem = %d, state = %d", cleaner->overmem,
985 		      cleaner->state);
986 
987 	LOCK(&cleaner->lock);
988 
989 	if (cleaner->overmem) {
990 		if (cleaner->state == cleaner_s_idle)
991 			want_cleaning = true;
992 	} else {
993 		if (cleaner->state == cleaner_s_busy)
994 			/*
995 			 * end_cleaning() can't be called here because
996 			 * then both cleaner->overmem_event and
997 			 * cleaner->resched_event will point to this
998 			 * event.  Set the state to done, and then
999 			 * when the acache_incremental_cleaning_action() event
1000 			 * is posted, it will handle the end_cleaning.
1001 			 */
1002 			cleaner->state = cleaner_s_done;
1003 	}
1004 
1005 	cleaner->overmem_event = event;
1006 
1007 	UNLOCK(&cleaner->lock);
1008 
1009 	if (want_cleaning)
1010 		begin_cleaning(cleaner);
1011 }
1012 
1013 static void
water(void * arg,int mark)1014 water(void *arg, int mark) {
1015 	dns_acache_t *acache = arg;
1016 	bool overmem = (mark == ISC_MEM_HIWATER);
1017 
1018 	REQUIRE(DNS_ACACHE_VALID(acache));
1019 
1020 	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1021 		      DNS_LOGMODULE_ACACHE, ISC_LOG_DEBUG(1),
1022 		      "acache memory reaches %s watermark, mem inuse %lu",
1023 		      overmem ? "high" : "low",
1024 		      (unsigned long)isc_mem_inuse(acache->mctx));
1025 
1026 	LOCK(&acache->cleaner.lock);
1027 
1028 	if (acache->cleaner.overmem != overmem) {
1029 		acache->cleaner.overmem = overmem;
1030 
1031 		if (acache->cleaner.overmem_event != NULL)
1032 			isc_task_send(acache->task,
1033 				      &acache->cleaner.overmem_event);
1034 		isc_mem_waterack(acache->mctx, mark);
1035 	}
1036 
1037 	UNLOCK(&acache->cleaner.lock);
1038 }
1039 
1040 /*
1041  * The cleaner task is shutting down; do the necessary cleanup.
1042  */
1043 static void
acache_cleaner_shutdown_action(isc_task_t * task,isc_event_t * event)1044 acache_cleaner_shutdown_action(isc_task_t *task, isc_event_t *event) {
1045 	dns_acache_t *acache = event->ev_arg;
1046 	bool should_free = false;
1047 
1048 	INSIST(task == acache->task);
1049 	INSIST(event->ev_type == ISC_TASKEVENT_SHUTDOWN);
1050 	INSIST(DNS_ACACHE_VALID(acache));
1051 
1052 	ATRACE("acache cleaner shutdown");
1053 
1054 	if (CLEANER_BUSY(&acache->cleaner))
1055 		end_cleaning(&acache->cleaner, event);
1056 	else
1057 		isc_event_free(&event);
1058 
1059 	LOCK(&acache->lock);
1060 
1061 	acache->live_cleaners--;
1062 	INSIST(acache->live_cleaners == 0);
1063 
1064 	if (isc_refcount_current(&acache->refs) == 0) {
1065 		INSIST(check_noentry(acache) == true);
1066 		should_free = true;
1067 	}
1068 
1069 	/*
1070 	 * By detaching the timer in the context of its task,
1071 	 * we are guaranteed that there will be no further timer
1072 	 * events.
1073 	 */
1074 	if (acache->cleaner.cleaning_timer != NULL)
1075 		isc_timer_detach(&acache->cleaner.cleaning_timer);
1076 
1077 	/* Make sure we don't reschedule anymore. */
1078 	(void)isc_task_purge(task, NULL, DNS_EVENT_ACACHECLEAN, NULL);
1079 
1080 	UNLOCK(&acache->lock);
1081 
1082 	if (should_free)
1083 		destroy(acache);
1084 }
1085 
1086 /*
1087  *	Public functions.
1088  */
1089 
1090 isc_result_t
dns_acache_create(dns_acache_t ** acachep,isc_mem_t * mctx,isc_taskmgr_t * taskmgr,isc_timermgr_t * timermgr)1091 dns_acache_create(dns_acache_t **acachep, isc_mem_t *mctx,
1092 		  isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr)
1093 {
1094 	int i;
1095 	isc_result_t result;
1096 	dns_acache_t *acache;
1097 
1098 	REQUIRE(acachep != NULL && *acachep == NULL);
1099 	REQUIRE(mctx != NULL);
1100 	REQUIRE(taskmgr != NULL);
1101 
1102 	acache = isc_mem_get(mctx, sizeof(*acache));
1103 	if (acache == NULL)
1104 		return (ISC_R_NOMEMORY);
1105 
1106 	ATRACE("create");
1107 
1108 	result = isc_refcount_init(&acache->refs, 1);
1109 	if (result != ISC_R_SUCCESS) {
1110 		isc_mem_put(mctx, acache, sizeof(*acache));
1111 		return (result);
1112 	}
1113 
1114 	result = isc_mutex_init(&acache->lock);
1115 	if (result != ISC_R_SUCCESS) {
1116 		isc_refcount_decrement(&acache->refs, NULL);
1117 		isc_refcount_destroy(&acache->refs);
1118 		isc_mem_put(mctx, acache, sizeof(*acache));
1119 		return (result);
1120 	}
1121 
1122 #ifdef ISC_HAVE_STATSLOCK
1123 	result = isc_mutex_init(&acache->stats.lock);
1124 	if (result != ISC_R_SUCCESS) {
1125 		DESTROYLOCK(&acache->lock);
1126 		isc_refcount_decrement(&acache->refs, NULL);
1127 		isc_refcount_destroy(&acache->refs);
1128 		isc_mem_put(mctx, acache, sizeof(*acache));
1129 		return (result);
1130 	}
1131 #endif
1132 
1133 	acache->mctx = NULL;
1134 	isc_mem_attach(mctx, &acache->mctx);
1135 	ISC_LIST_INIT(acache->entries);
1136 
1137 	acache->shutting_down = false;
1138 
1139 	acache->task = NULL;
1140 	acache->entrylocks = NULL;
1141 
1142 	result = isc_task_create(taskmgr, 1, &acache->task);
1143 	if (result != ISC_R_SUCCESS) {
1144 		UNEXPECTED_ERROR(__FILE__, __LINE__,
1145 				 "isc_task_create() failed(): %s",
1146 				 dns_result_totext(result));
1147 		result = ISC_R_UNEXPECTED;
1148 		goto cleanup;
1149 	}
1150 	isc_task_setname(acache->task, "acachetask", acache);
1151 	ISC_EVENT_INIT(&acache->cevent, sizeof(acache->cevent), 0, NULL,
1152 		       DNS_EVENT_ACACHECONTROL, shutdown_task, NULL,
1153 		       NULL, NULL, NULL);
1154 	acache->cevent_sent = false;
1155 
1156 	acache->dbentries = 0;
1157 	for (i = 0; i < DBBUCKETS; i++)
1158 		ISC_LIST_INIT(acache->dbbucket[i]);
1159 
1160 	acache->entrylocks = isc_mem_get(mctx, sizeof(*acache->entrylocks) *
1161 					 DEFAULT_ACACHE_ENTRY_LOCK_COUNT);
1162 	if (acache->entrylocks == NULL) {
1163 		result = ISC_R_NOMEMORY;
1164 		goto cleanup;
1165 	}
1166 	for (i = 0; i < DEFAULT_ACACHE_ENTRY_LOCK_COUNT; i++) {
1167 		result = ACACHE_INITLOCK(&acache->entrylocks[i]);
1168 		if (result != ISC_R_SUCCESS) {
1169 			while (i-- > 0)
1170 				ACACHE_DESTROYLOCK(&acache->entrylocks[i]);
1171 			isc_mem_put(mctx, acache->entrylocks,
1172 				    sizeof(*acache->entrylocks) *
1173 				    DEFAULT_ACACHE_ENTRY_LOCK_COUNT);
1174 			acache->entrylocks = NULL;
1175 			goto cleanup;
1176 		}
1177 	}
1178 
1179 	acache->live_cleaners = 0;
1180 	result = acache_cleaner_init(acache, timermgr, &acache->cleaner);
1181 	if (result != ISC_R_SUCCESS)
1182 		goto cleanup;
1183 
1184 	acache->stats.cleaner_runs = 0;
1185 	reset_stats(acache);
1186 
1187 	acache->magic = ACACHE_MAGIC;
1188 
1189 	*acachep = acache;
1190 	return (ISC_R_SUCCESS);
1191 
1192  cleanup:
1193 	if (acache->task != NULL)
1194 		isc_task_detach(&acache->task);
1195 #ifdef ISC_HAVE_STATSLOCK
1196 	DESTROYLOCK(&acache->stats.lock);
1197 #endif
1198 	DESTROYLOCK(&acache->lock);
1199 	isc_refcount_decrement(&acache->refs, NULL);
1200 	isc_refcount_destroy(&acache->refs);
1201 	if (acache->entrylocks != NULL) {
1202 		for (i = 0; i < DEFAULT_ACACHE_ENTRY_LOCK_COUNT; i++)
1203 			ACACHE_DESTROYLOCK(&acache->entrylocks[i]);
1204 		isc_mem_put(mctx, acache->entrylocks,
1205 			    sizeof(*acache->entrylocks) *
1206 			    DEFAULT_ACACHE_ENTRY_LOCK_COUNT);
1207 	}
1208 	isc_mem_put(mctx, acache, sizeof(*acache));
1209 	isc_mem_detach(&mctx);
1210 
1211 	return (result);
1212 }
1213 
1214 void
dns_acache_attach(dns_acache_t * source,dns_acache_t ** targetp)1215 dns_acache_attach(dns_acache_t *source, dns_acache_t **targetp) {
1216 	REQUIRE(DNS_ACACHE_VALID(source));
1217 	REQUIRE(targetp != NULL && *targetp == NULL);
1218 
1219 	AATRACE(source, "attach");
1220 
1221 	isc_refcount_increment(&source->refs, NULL);
1222 
1223 	*targetp = source;
1224 }
1225 
1226 void
dns_acache_countquerymiss(dns_acache_t * acache)1227 dns_acache_countquerymiss(dns_acache_t *acache) {
1228 	ACACHE_STATSLOCK(&acache->stats.lock);
1229 	ACACHE_INC(acache->stats.misses);
1230 	ACACHE_INC(acache->stats.queries);
1231 	ACACHE_STATSUNLOCK(&acache->stats.lock);
1232 }
1233 
1234 void
dns_acache_detach(dns_acache_t ** acachep)1235 dns_acache_detach(dns_acache_t **acachep) {
1236 	dns_acache_t *acache;
1237 	unsigned int refs;
1238 	bool should_free = false;
1239 
1240 	REQUIRE(acachep != NULL && DNS_ACACHE_VALID(*acachep));
1241 	acache = *acachep;
1242 
1243 	ATRACE("detach");
1244 
1245 	isc_refcount_decrement(&acache->refs, &refs);
1246 	if (refs == 0) {
1247 		INSIST(check_noentry(acache) == true);
1248 		should_free = true;
1249 	}
1250 
1251 	*acachep = NULL;
1252 
1253 	/*
1254 	 * If we're exiting and the cleaner task exists, let it free the cache.
1255 	 */
1256 	if (should_free && acache->live_cleaners > 0) {
1257 		isc_task_shutdown(acache->task);
1258 		should_free = false;
1259 	}
1260 
1261 	if (should_free)
1262 		destroy(acache);
1263 }
1264 
1265 void
dns_acache_shutdown(dns_acache_t * acache)1266 dns_acache_shutdown(dns_acache_t *acache) {
1267 	REQUIRE(DNS_ACACHE_VALID(acache));
1268 
1269 	LOCK(&acache->lock);
1270 
1271 	ATRACE("shutdown");
1272 
1273 	if (!acache->shutting_down) {
1274 		isc_event_t *event;
1275 		dns_acache_t *acache_evarg = NULL;
1276 
1277 		INSIST(!acache->cevent_sent);
1278 
1279 		acache->shutting_down = true;
1280 
1281 		isc_mem_setwater(acache->mctx, NULL, NULL, 0, 0);
1282 
1283 		/*
1284 		 * Self attach the object in order to prevent it from being
1285 		 * destroyed while waiting for the event.
1286 		 */
1287 		dns_acache_attach(acache, &acache_evarg);
1288 		event = &acache->cevent;
1289 		event->ev_arg = acache_evarg;
1290 		isc_task_send(acache->task, &event);
1291 		acache->cevent_sent = true;
1292 	}
1293 
1294 	UNLOCK(&acache->lock);
1295 }
1296 
1297 isc_result_t
dns_acache_setdb(dns_acache_t * acache,dns_db_t * db)1298 dns_acache_setdb(dns_acache_t *acache, dns_db_t *db) {
1299 	int bucket;
1300 	dbentry_t *dbentry;
1301 	isc_result_t result = ISC_R_SUCCESS;
1302 
1303 	REQUIRE(DNS_ACACHE_VALID(acache));
1304 	REQUIRE(db != NULL);
1305 
1306 	ATRACE("setdb");
1307 
1308 	LOCK(&acache->lock);
1309 
1310 	dbentry = NULL;
1311 	result = finddbent(acache, db, &dbentry);
1312 	if (result == ISC_R_SUCCESS) {
1313 		result = ISC_R_EXISTS;
1314 		goto end;
1315 	}
1316 	result = ISC_R_SUCCESS;
1317 
1318 	dbentry = isc_mem_get(acache->mctx, sizeof(*dbentry));
1319 	if (dbentry == NULL) {
1320 		result = ISC_R_NOMEMORY;
1321 		goto end;
1322 	}
1323 
1324 	ISC_LINK_INIT(dbentry, link);
1325 	ISC_LIST_INIT(dbentry->originlist);
1326 	ISC_LIST_INIT(dbentry->referlist);
1327 
1328 	dbentry->db = NULL;
1329 	dns_db_attach(db, &dbentry->db);
1330 
1331 	bucket = isc_hash_function(&db, sizeof(db), true, NULL) % DBBUCKETS;
1332 
1333 	ISC_LIST_APPEND(acache->dbbucket[bucket], dbentry, link);
1334 
1335 	acache->dbentries++;
1336 
1337  end:
1338 	UNLOCK(&acache->lock);
1339 
1340 	return (result);
1341 }
1342 
1343 isc_result_t
dns_acache_putdb(dns_acache_t * acache,dns_db_t * db)1344 dns_acache_putdb(dns_acache_t *acache, dns_db_t *db) {
1345 	int bucket;
1346 	isc_result_t result;
1347 	dbentry_t *dbentry;
1348 	dns_acacheentry_t *entry;
1349 
1350 	REQUIRE(DNS_ACACHE_VALID(acache));
1351 	REQUIRE(db != NULL);
1352 
1353 	ATRACE("putdb");
1354 
1355 	LOCK(&acache->lock);
1356 
1357 	dbentry = NULL;
1358 	result = finddbent(acache, db, &dbentry);
1359 	if (result != ISC_R_SUCCESS) {
1360 		/*
1361 		 * The entry may have not been created due to memory shortage.
1362 		 */
1363 		UNLOCK(&acache->lock);
1364 		return (ISC_R_NOTFOUND);
1365 	}
1366 
1367 	/*
1368 	 * Release corresponding cache entries: for each entry, release all
1369 	 * links the entry has, and then callback to the entry holder (if any).
1370 	 * If no other external references exist (this can happen if the
1371 	 * original holder has canceled callback,) destroy it here.
1372 	 */
1373 	while ((entry = ISC_LIST_HEAD(dbentry->originlist)) != NULL) {
1374 		ACACHE_LOCK(&acache->entrylocks[entry->locknum],
1375 			    isc_rwlocktype_write);
1376 
1377 		/*
1378 		 * Releasing olink first would avoid finddbent() in
1379 		 * unlink_dbentries().
1380 		 */
1381 		ISC_LIST_UNLINK(dbentry->originlist, entry, olink);
1382 		if (acache->cleaner.current_entry != entry)
1383 			ISC_LIST_UNLINK(acache->entries, entry, link);
1384 		unlink_dbentries(acache, entry);
1385 
1386 		if (entry->callback != NULL)
1387 			(entry->callback)(entry, &entry->cbarg);
1388 		entry->callback = NULL;
1389 
1390 		ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
1391 			      isc_rwlocktype_write);
1392 
1393 		if (acache->cleaner.current_entry != entry)
1394 			dns_acache_detachentry(&entry);
1395 	}
1396 	while ((entry = ISC_LIST_HEAD(dbentry->referlist)) != NULL) {
1397 		ACACHE_LOCK(&acache->entrylocks[entry->locknum],
1398 			    isc_rwlocktype_write);
1399 
1400 		ISC_LIST_UNLINK(dbentry->referlist, entry, rlink);
1401 		if (acache->cleaner.current_entry != entry)
1402 			ISC_LIST_UNLINK(acache->entries, entry, link);
1403 		unlink_dbentries(acache, entry);
1404 
1405 		if (entry->callback != NULL)
1406 			(entry->callback)(entry, &entry->cbarg);
1407 		entry->callback = NULL;
1408 
1409 		ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
1410 			      isc_rwlocktype_write);
1411 
1412 		if (acache->cleaner.current_entry != entry)
1413 			dns_acache_detachentry(&entry);
1414 	}
1415 
1416 	INSIST(ISC_LIST_EMPTY(dbentry->originlist) &&
1417 	       ISC_LIST_EMPTY(dbentry->referlist));
1418 
1419 	bucket = isc_hash_function(&db, sizeof(db), true, NULL) % DBBUCKETS;
1420 
1421 	ISC_LIST_UNLINK(acache->dbbucket[bucket], dbentry, link);
1422 	dns_db_detach(&dbentry->db);
1423 
1424 	isc_mem_put(acache->mctx, dbentry, sizeof(*dbentry));
1425 
1426 	acache->dbentries--;
1427 
1428 	acache->stats.deleted++;
1429 
1430 	UNLOCK(&acache->lock);
1431 
1432 	return (ISC_R_SUCCESS);
1433 }
1434 
1435 isc_result_t
dns_acache_createentry(dns_acache_t * acache,dns_db_t * origdb,void (* callback)(dns_acacheentry_t *,void **),void * cbarg,dns_acacheentry_t ** entryp)1436 dns_acache_createentry(dns_acache_t *acache, dns_db_t *origdb,
1437 		       void (*callback)(dns_acacheentry_t *, void **),
1438 		       void *cbarg, dns_acacheentry_t **entryp)
1439 {
1440 	dns_acacheentry_t *newentry;
1441 	isc_result_t result;
1442 	uint32_t r;
1443 	isc_stdtime_t tmptime;
1444 
1445 	REQUIRE(DNS_ACACHE_VALID(acache));
1446 	REQUIRE(entryp != NULL && *entryp == NULL);
1447 	REQUIRE(origdb != NULL);
1448 
1449 	/*
1450 	 * Should we exceed our memory limit for some reason (for
1451 	 * example, if the cleaner does not run aggressively enough),
1452 	 * then we will not create additional entries.
1453 	 *
1454 	 * XXXSK: It might be better to lock the acache->cleaner->lock,
1455 	 * but locking may be an expensive bottleneck. If we misread
1456 	 * the value, we will occasionally refuse to create a few
1457 	 * cache entries, or create a few that we should not. I do not
1458 	 * expect this to happen often, and it will not have very bad
1459 	 * effects when it does. So no lock for now.
1460 	 */
1461 	if (acache->cleaner.overmem) {
1462 		acache->stats.overmem_nocreates++; /* XXXSK danger: unlocked! */
1463 		return (ISC_R_NORESOURCES);
1464 	}
1465 
1466 	newentry = isc_mem_get(acache->mctx, sizeof(*newentry));
1467 	if (newentry == NULL) {
1468 		acache->stats.nomem++;  /* XXXMLG danger: unlocked! */
1469 		return (ISC_R_NOMEMORY);
1470 	}
1471 
1472 	isc_random_get(&r);
1473 	newentry->locknum = r % DEFAULT_ACACHE_ENTRY_LOCK_COUNT;
1474 
1475 	result = isc_refcount_init(&newentry->references, 1);
1476 	if (result != ISC_R_SUCCESS) {
1477 		isc_mem_put(acache->mctx, newentry, sizeof(*newentry));
1478 		return (result);
1479 	};
1480 
1481 	ISC_LINK_INIT(newentry, link);
1482 	ISC_LINK_INIT(newentry, olink);
1483 	ISC_LINK_INIT(newentry, rlink);
1484 
1485 	newentry->acache = NULL;
1486 	dns_acache_attach(acache, &newentry->acache);
1487 
1488 	newentry->zone = NULL;
1489 	newentry->db = NULL;
1490 	newentry->version = NULL;
1491 	newentry->node = NULL;
1492 	newentry->foundname = NULL;
1493 
1494 	newentry->callback = callback;
1495 	newentry->cbarg = cbarg;
1496 	newentry->origdb = NULL;
1497 	dns_db_attach(origdb, &newentry->origdb);
1498 
1499 	isc_stdtime_get(&tmptime);
1500 	acache_storetime(newentry, tmptime);
1501 
1502 	newentry->magic = ACACHEENTRY_MAGIC;
1503 
1504 	*entryp = newentry;
1505 
1506 	return (ISC_R_SUCCESS);
1507 }
1508 
1509 isc_result_t
dns_acache_getentry(dns_acacheentry_t * entry,dns_zone_t ** zonep,dns_db_t ** dbp,dns_dbversion_t ** versionp,dns_dbnode_t ** nodep,dns_name_t * fname,dns_message_t * msg,isc_stdtime_t now)1510 dns_acache_getentry(dns_acacheentry_t *entry, dns_zone_t **zonep,
1511 		    dns_db_t **dbp, dns_dbversion_t **versionp,
1512 		    dns_dbnode_t **nodep, dns_name_t *fname,
1513 		    dns_message_t *msg, isc_stdtime_t now)
1514 {
1515 	isc_result_t result = ISC_R_SUCCESS;
1516 	dns_rdataset_t *erdataset;
1517 	isc_stdtime_t	now32;
1518 	dns_acache_t *acache;
1519 	int locknum;
1520 
1521 	REQUIRE(DNS_ACACHEENTRY_VALID(entry));
1522 	REQUIRE(zonep == NULL || *zonep == NULL);
1523 	REQUIRE(dbp != NULL && *dbp == NULL);
1524 	REQUIRE(versionp != NULL && *versionp == NULL);
1525 	REQUIRE(nodep != NULL && *nodep == NULL);
1526 	REQUIRE(fname != NULL);
1527 	REQUIRE(msg != NULL);
1528 	acache = entry->acache;
1529 	REQUIRE(DNS_ACACHE_VALID(acache));
1530 
1531 	locknum = entry->locknum;
1532 	ACACHE_LOCK(&acache->entrylocks[locknum], isc_rwlocktype_read);
1533 
1534 	isc_stdtime_convert32(now, &now32);
1535 	acache_storetime(entry, now32);
1536 
1537 	if (entry->zone != NULL && zonep != NULL)
1538 		dns_zone_attach(entry->zone, zonep);
1539 
1540 	if (entry->db == NULL) {
1541 		*dbp = NULL;
1542 		*versionp = NULL;
1543 	} else {
1544 		dns_db_attach(entry->db, dbp);
1545 		dns_db_attachversion(entry->db, entry->version, versionp);
1546 	}
1547 	if (entry->node == NULL)
1548 		*nodep = NULL;
1549 	else {
1550 		dns_db_attachnode(entry->db, entry->node, nodep);
1551 
1552 		INSIST(entry->foundname != NULL);
1553 		dns_name_copy(entry->foundname, fname, NULL);
1554 		for (erdataset = ISC_LIST_HEAD(entry->foundname->list);
1555 		     erdataset != NULL;
1556 		     erdataset = ISC_LIST_NEXT(erdataset, link)) {
1557 			dns_rdataset_t *ardataset;
1558 
1559 			ardataset = NULL;
1560 			result = dns_message_gettemprdataset(msg, &ardataset);
1561 			if (result != ISC_R_SUCCESS) {
1562 				ACACHE_UNLOCK(&acache->entrylocks[locknum],
1563 					      isc_rwlocktype_read);
1564 				goto fail;
1565 			}
1566 
1567 			/*
1568 			 * XXXJT: if we simply clone the rdataset, we'll get
1569 			 * lost wrt cyclic ordering.  We'll need an additional
1570 			 * trick to get the latest counter from the original
1571 			 * header.
1572 			 */
1573 			dns_rdataset_clone(erdataset, ardataset);
1574 			ISC_LIST_APPEND(fname->list, ardataset, link);
1575 		}
1576 	}
1577 
1578 	ACACHE_STATSLOCK(&entry->acache->stats.lock);
1579 	ACACHE_INC(entry->acache->stats.hits);
1580 	ACACHE_INC(entry->acache->stats.queries);
1581 	ACACHE_STATSUNLOCK(&entry->acache->stats.lock);
1582 
1583 	ACACHE_UNLOCK(&acache->entrylocks[locknum], isc_rwlocktype_read);
1584 
1585 	return (result);
1586 
1587   fail:
1588 	while ((erdataset = ISC_LIST_HEAD(fname->list)) != NULL) {
1589 		ISC_LIST_UNLINK(fname->list, erdataset, link);
1590 		dns_rdataset_disassociate(erdataset);
1591 		dns_message_puttemprdataset(msg, &erdataset);
1592 	}
1593 	if (*nodep != NULL)
1594 		dns_db_detachnode(*dbp, nodep);
1595 	if (*versionp != NULL)
1596 		dns_db_closeversion(*dbp, versionp, false);
1597 	if (*dbp != NULL)
1598 		dns_db_detach(dbp);
1599 	if (zonep != NULL && *zonep != NULL)
1600 		dns_zone_detach(zonep);
1601 
1602 	return (result);
1603 }
1604 
1605 isc_result_t
dns_acache_setentry(dns_acache_t * acache,dns_acacheentry_t * entry,dns_zone_t * zone,dns_db_t * db,dns_dbversion_t * version,dns_dbnode_t * node,dns_name_t * fname)1606 dns_acache_setentry(dns_acache_t *acache, dns_acacheentry_t *entry,
1607 		    dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version,
1608 		    dns_dbnode_t *node, dns_name_t *fname)
1609 {
1610 	isc_result_t result;
1611 	dbentry_t *odbent;
1612 	dbentry_t *rdbent = NULL;
1613 	bool close_version = false;
1614 	dns_acacheentry_t *dummy_entry = NULL;
1615 
1616 	REQUIRE(DNS_ACACHE_VALID(acache));
1617 	REQUIRE(DNS_ACACHEENTRY_VALID(entry));
1618 
1619 	LOCK(&acache->lock);	/* XXX: need to lock it here for ordering */
1620 	ACACHE_LOCK(&acache->entrylocks[entry->locknum], isc_rwlocktype_write);
1621 
1622 	/* Set zone */
1623 	if (zone != NULL)
1624 		dns_zone_attach(zone, &entry->zone);
1625 	/* Set DB */
1626 	if (db != NULL)
1627 		dns_db_attach(db, &entry->db);
1628 	/*
1629 	 * Set DB version.  If the version is not given by the caller,
1630 	 * which is the case for glue or cache DBs, use the current version.
1631 	 */
1632 	if (version == NULL) {
1633 		if (db != NULL) {
1634 			dns_db_currentversion(db, &version);
1635 			close_version = true;
1636 		}
1637 	}
1638 	if (version != NULL) {
1639 		INSIST(db != NULL);
1640 		dns_db_attachversion(db, version, &entry->version);
1641 	}
1642 	if (close_version)
1643 		dns_db_closeversion(db, &version, false);
1644 	/* Set DB node. */
1645 	if (node != NULL) {
1646 		dns_rdataset_t *ardataset, *crdataset;
1647 
1648 		INSIST(db != NULL);
1649 		dns_db_attachnode(db, node, &entry->node);
1650 
1651 		/*
1652 		 * Set list of the corresponding rdatasets, if
1653 		 * given. To minimize the overhead and memory
1654 		 * consumption, we'll do this for positive cache
1655 		 * only, in which case the DB node is non NULL.  We
1656 		 * do not want to cache incomplete information, so
1657 		 * give up the entire entry when a memory shortage
1658 		 * happen during the process.
1659 		 */
1660 		entry->foundname = isc_mem_get(acache->mctx,
1661 					       sizeof(*entry->foundname));
1662 
1663 		if (entry->foundname == NULL) {
1664 			result = ISC_R_NOMEMORY;
1665 			goto fail;
1666 		}
1667 		dns_name_init(entry->foundname, NULL);
1668 		result = dns_name_dup(fname, acache->mctx,
1669 				      entry->foundname);
1670 		if (result != ISC_R_SUCCESS)
1671 			goto fail;
1672 
1673 		for (ardataset = ISC_LIST_HEAD(fname->list);
1674 		     ardataset != NULL;
1675 		     ardataset = ISC_LIST_NEXT(ardataset, link)) {
1676 			crdataset = isc_mem_get(acache->mctx,
1677 						sizeof(*crdataset));
1678 			if (crdataset == NULL) {
1679 				result = ISC_R_NOMEMORY;
1680 				goto fail;
1681 			}
1682 
1683 			dns_rdataset_init(crdataset);
1684 			dns_rdataset_clone(ardataset, crdataset);
1685 			ISC_LIST_APPEND(entry->foundname->list, crdataset,
1686 					link);
1687 		}
1688 	}
1689 
1690 	odbent = NULL;
1691 	result = finddbent(acache, entry->origdb, &odbent);
1692 	if (result != ISC_R_SUCCESS)
1693 		goto fail;
1694 	if (db != NULL) {
1695 		rdbent = NULL;
1696 		result = finddbent(acache, db, &rdbent);
1697 		if (result != ISC_R_SUCCESS)
1698 			goto fail;
1699 	}
1700 
1701 	ISC_LIST_APPEND(acache->entries, entry, link);
1702 	ISC_LIST_APPEND(odbent->originlist, entry, olink);
1703 	if (rdbent != NULL)
1704 		ISC_LIST_APPEND(rdbent->referlist, entry, rlink);
1705 
1706 	/*
1707 	 * The additional cache needs an implicit reference to entries in its
1708 	 * link.
1709 	 */
1710 	dns_acache_attachentry(entry, &dummy_entry);
1711 
1712 	ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
1713 		      isc_rwlocktype_write);
1714 
1715 	acache->stats.adds++;
1716 	UNLOCK(&acache->lock);
1717 
1718 	return (ISC_R_SUCCESS);
1719 
1720  fail:
1721 	clear_entry(acache, entry);
1722 
1723 	ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
1724 		      isc_rwlocktype_write);
1725 	UNLOCK(&acache->lock);
1726 
1727 	return (result);
1728 }
1729 
1730 bool
dns_acache_cancelentry(dns_acacheentry_t * entry)1731 dns_acache_cancelentry(dns_acacheentry_t *entry) {
1732 	dns_acache_t *acache;
1733 	bool callback_active;
1734 
1735 	REQUIRE(DNS_ACACHEENTRY_VALID(entry));
1736 
1737 	acache = entry->acache;
1738 
1739 	INSIST(DNS_ACACHE_VALID(entry->acache));
1740 
1741 	LOCK(&acache->lock);
1742 	ACACHE_LOCK(&acache->entrylocks[entry->locknum], isc_rwlocktype_write);
1743 
1744 	callback_active = (entry->cbarg != NULL);
1745 
1746 	/*
1747 	 * Release dependencies stored in this entry as much as possible.
1748 	 * The main link cannot be released, since the acache object has
1749 	 * a reference to this entry; the empty entry will be released in
1750 	 * the next cleaning action.
1751 	 */
1752 	unlink_dbentries(acache, entry);
1753 	clear_entry(entry->acache, entry);
1754 
1755 	entry->callback = NULL;
1756 	entry->cbarg = NULL;
1757 
1758 	ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
1759 		      isc_rwlocktype_write);
1760 	UNLOCK(&acache->lock);
1761 
1762 	return (callback_active);
1763 }
1764 
1765 void
dns_acache_attachentry(dns_acacheentry_t * source,dns_acacheentry_t ** targetp)1766 dns_acache_attachentry(dns_acacheentry_t *source,
1767 		       dns_acacheentry_t **targetp)
1768 {
1769 	REQUIRE(DNS_ACACHEENTRY_VALID(source));
1770 	REQUIRE(targetp != NULL && *targetp == NULL);
1771 
1772 	isc_refcount_increment(&source->references, NULL);
1773 
1774 	*targetp = source;
1775 }
1776 
1777 void
dns_acache_detachentry(dns_acacheentry_t ** entryp)1778 dns_acache_detachentry(dns_acacheentry_t **entryp) {
1779 	dns_acacheentry_t *entry;
1780 	unsigned int refs;
1781 
1782 	REQUIRE(entryp != NULL && DNS_ACACHEENTRY_VALID(*entryp));
1783 	entry = *entryp;
1784 
1785 	isc_refcount_decrement(&entry->references, &refs);
1786 
1787 	/*
1788 	 * If there are no references to the entry, the entry must have been
1789 	 * unlinked and can be destroyed safely.
1790 	 */
1791 	if (refs == 0) {
1792 		INSIST(!ISC_LINK_LINKED(entry, link));
1793 		(*entryp)->acache->stats.deleted++;
1794 		destroy_entry(entry);
1795 	}
1796 
1797 	*entryp = NULL;
1798 }
1799 
1800 void
dns_acache_setcleaninginterval(dns_acache_t * acache,unsigned int t)1801 dns_acache_setcleaninginterval(dns_acache_t *acache, unsigned int t) {
1802 	isc_interval_t interval;
1803 	isc_result_t result;
1804 
1805 	REQUIRE(DNS_ACACHE_VALID(acache));
1806 
1807 	ATRACE("dns_acache_setcleaninginterval");
1808 
1809 	LOCK(&acache->lock);
1810 
1811 	/*
1812 	 * It may be the case that the acache has already shut down.
1813 	 * If so, it has no timer.  (Not sure if this can really happen.)
1814 	 */
1815 	if (acache->cleaner.cleaning_timer == NULL)
1816 		goto unlock;
1817 
1818 	acache->cleaner.cleaning_interval = t;
1819 
1820 	if (t == 0) {
1821 		result = isc_timer_reset(acache->cleaner.cleaning_timer,
1822 					 isc_timertype_inactive,
1823 					 NULL, NULL, true);
1824 	} else {
1825 		isc_interval_set(&interval, acache->cleaner.cleaning_interval,
1826 				 0);
1827 		result = isc_timer_reset(acache->cleaner.cleaning_timer,
1828 					 isc_timertype_ticker,
1829 					 NULL, &interval, false);
1830 	}
1831 	if (result != ISC_R_SUCCESS)
1832 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1833 			      DNS_LOGMODULE_ACACHE, ISC_LOG_WARNING,
1834 			      "could not set acache cleaning interval: %s",
1835 			      isc_result_totext(result));
1836 	else
1837 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1838 			      DNS_LOGMODULE_ACACHE, ISC_LOG_NOTICE,
1839 			      "acache %p cleaning interval set to %d.",
1840 			      acache, t);
1841 
1842  unlock:
1843 	UNLOCK(&acache->lock);
1844 }
1845 
1846 /*
1847  * This function was derived from cache.c:dns_cache_setcachesize().  See the
1848  * function for more details about the logic.
1849  */
1850 void
dns_acache_setcachesize(dns_acache_t * acache,size_t size)1851 dns_acache_setcachesize(dns_acache_t *acache, size_t size) {
1852 	size_t hiwater, lowater;
1853 
1854 	REQUIRE(DNS_ACACHE_VALID(acache));
1855 
1856 	if (size != 0U && size < DNS_ACACHE_MINSIZE)
1857 		size = DNS_ACACHE_MINSIZE;
1858 
1859 	hiwater = size - (size >> 3);
1860 	lowater = size - (size >> 2);
1861 
1862 	if (size == 0U || hiwater == 0U || lowater == 0U)
1863 		isc_mem_setwater(acache->mctx, water, acache, 0, 0);
1864 	else
1865 		isc_mem_setwater(acache->mctx, water, acache,
1866 				 hiwater, lowater);
1867 }
1868