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