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