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