Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / external / bsd / bind / dist / lib / dns / acache.c
blobacd80aa95ed8f43b2d7d19b3d7c7e109eaa6dc1b
1 /* $NetBSD$ */
3 /*
4 * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
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.
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.
19 /* Id: acache.c,v 1.22 2008/02/07 23:46:54 tbox Exp */
21 #include <config.h>
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/task.h>
33 #include <isc/time.h>
34 #include <isc/timer.h>
36 #include <dns/acache.h>
37 #include <dns/db.h>
38 #include <dns/events.h>
39 #include <dns/log.h>
40 #include <dns/message.h>
41 #include <dns/name.h>
42 #include <dns/rdataset.h>
43 #include <dns/result.h>
44 #include <dns/zone.h>
46 #define ACACHE_MAGIC ISC_MAGIC('A', 'C', 'H', 'E')
47 #define DNS_ACACHE_VALID(acache) ISC_MAGIC_VALID(acache, ACACHE_MAGIC)
49 #define ACACHEENTRY_MAGIC ISC_MAGIC('A', 'C', 'E', 'T')
50 #define DNS_ACACHEENTRY_VALID(entry) ISC_MAGIC_VALID(entry, ACACHEENTRY_MAGIC)
52 #define DBBUCKETS 67
54 #if 0
55 #define ATRACE(m) isc_log_write(dns_lctx, \
56 DNS_LOGCATEGORY_DATABASE, \
57 DNS_LOGMODULE_ACACHE, \
58 ISC_LOG_DEBUG(3), \
59 "acache %p: %s", acache, (m))
60 #define AATRACE(a,m) isc_log_write(dns_lctx, \
61 DNS_LOGCATEGORY_DATABASE, \
62 DNS_LOGMODULE_ACACHE, \
63 ISC_LOG_DEBUG(3), \
64 "acache %p: %s", (a), (m))
65 #else
66 #define ATRACE(m)
67 #define AATRACE(a, m)
68 #endif
71 * The following variables control incremental cleaning.
72 * MINSIZE is how many bytes is the floor for dns_acache_setcachesize().
73 * CLEANERINCREMENT is how many entries are examined in one pass.
74 * (XXX simply derived from definitions in cache.c There may be better
75 * constants here.)
77 #define DNS_ACACHE_MINSIZE 2097152 /* Bytes. 2097152 = 2 MB */
78 #define DNS_ACACHE_CLEANERINCREMENT 1000 /* Number of entries. */
80 #define DEFAULT_ACACHE_ENTRY_LOCK_COUNT 1009 /*%< Should be prime. */
82 #if defined(ISC_RWLOCK_USEATOMIC) && defined(ISC_PLATFORM_HAVEATOMICSTORE)
83 #define ACACHE_USE_RWLOCK 1
84 #endif
86 #ifdef ACACHE_USE_RWLOCK
87 #define ACACHE_INITLOCK(l) isc_rwlock_init((l), 0, 0)
88 #define ACACHE_DESTROYLOCK(l) isc_rwlock_destroy(l)
89 #define ACACHE_LOCK(l, t) RWLOCK((l), (t))
90 #define ACACHE_UNLOCK(l, t) RWUNLOCK((l), (t))
92 #define acache_storetime(entry, t) \
93 (isc_atomic_store((isc_int32_t *)&(entry)->lastused, (t)))
94 #else
95 #define ACACHE_INITLOCK(l) isc_mutex_init(l)
96 #define ACACHE_DESTROYLOCK(l) DESTROYLOCK(l)
97 #define ACACHE_LOCK(l, t) LOCK(l)
98 #define ACACHE_UNLOCK(l, t) UNLOCK(l)
100 #define acache_storetime(entry, t) ((entry)->lastused = (t))
101 #endif
103 /* Locked by acache lock */
104 typedef struct dbentry {
105 ISC_LINK(struct dbentry) link;
107 dns_db_t *db;
108 ISC_LIST(dns_acacheentry_t) originlist;
109 ISC_LIST(dns_acacheentry_t) referlist;
110 } dbentry_t;
112 typedef ISC_LIST(dbentry_t) dbentrylist_t;
114 typedef struct acache_cleaner acache_cleaner_t;
116 typedef enum {
117 cleaner_s_idle, /* Waiting for cleaning-interval to expire. */
118 cleaner_s_busy, /* Currently cleaning. */
119 cleaner_s_done /* Freed enough memory after being overmem. */
120 } cleaner_state_t;
123 * Convenience macros for comprehensive assertion checking.
125 #define CLEANER_IDLE(c) ((c)->state == cleaner_s_idle && \
126 (c)->resched_event != NULL)
127 #define CLEANER_BUSY(c) ((c)->state == cleaner_s_busy && \
128 (c)->resched_event == NULL)
130 struct acache_cleaner {
131 isc_mutex_t lock;
133 * Locks overmem_event, overmem. (See cache.c)
136 dns_acache_t *acache;
137 unsigned int cleaning_interval; /* The cleaning-interval
138 from named.conf,
139 in seconds. */
141 isc_stdtime_t last_cleanup_time; /* The time when the last
142 cleanup task completed */
144 isc_timer_t *cleaning_timer;
145 isc_event_t *resched_event; /* Sent by cleaner task to
146 itself to reschedule */
147 isc_event_t *overmem_event;
149 dns_acacheentry_t *current_entry; /* The bookmark entry to
150 restart the cleaning.
151 Locked by acache lock. */
152 int increment; /* Number of entries to
153 clean in one increment */
155 unsigned long ncleaned; /* Number of entries cleaned
156 up (for logging purposes) */
157 cleaner_state_t state; /* Idle/Busy/Done. */
158 isc_boolean_t overmem; /* The acache is in an overmem
159 state. */
162 struct dns_acachestats {
163 unsigned int hits;
164 unsigned int queries;
165 unsigned int misses;
166 unsigned int adds;
167 unsigned int deleted;
168 unsigned int cleaned;
169 unsigned int cleaner_runs;
170 unsigned int overmem;
171 unsigned int overmem_nocreates;
172 unsigned int nomem;
176 * The actual acache object.
179 struct dns_acache {
180 unsigned int magic;
182 isc_mem_t *mctx;
183 isc_refcount_t refs;
185 #ifdef ACACHE_USE_RWLOCK
186 isc_rwlock_t *entrylocks;
187 #else
188 isc_mutex_t *entrylocks;
189 #endif
191 isc_mutex_t lock;
193 int live_cleaners;
194 acache_cleaner_t cleaner;
195 ISC_LIST(dns_acacheentry_t) entries;
196 unsigned int dbentries;
197 dbentrylist_t dbbucket[DBBUCKETS];
199 isc_boolean_t shutting_down;
201 isc_task_t *task;
202 isc_event_t cevent;
203 isc_boolean_t cevent_sent;
205 dns_acachestats_t stats;
208 struct dns_acacheentry {
209 unsigned int magic;
211 unsigned int locknum;
212 isc_refcount_t references;
214 dns_acache_t *acache;
216 /* Data for Management of cache entries */
217 ISC_LINK(dns_acacheentry_t) link;
218 ISC_LINK(dns_acacheentry_t) olink;
219 ISC_LINK(dns_acacheentry_t) rlink;
221 dns_db_t *origdb; /* reference to the DB
222 holding this entry */
224 /* Cache data */
225 dns_zone_t *zone; /* zone this entry
226 belongs to */
227 dns_db_t *db; /* DB this entry belongs to */
228 dns_dbversion_t *version; /* the version of the DB */
229 dns_dbnode_t *node; /* node this entry
230 belongs to */
231 dns_name_t *foundname; /* corresponding DNS name
232 and rdataset */
234 /* Callback function and its argument */
235 void (*callback)(dns_acacheentry_t *, void **);
236 void *cbarg;
238 /* Timestamp of the last time this entry is referred to */
239 isc_stdtime32_t lastused;
243 * Internal functions (and prototypes).
245 static inline isc_boolean_t check_noentry(dns_acache_t *acache);
246 static void destroy(dns_acache_t *acache);
247 static void shutdown_entries(dns_acache_t *acache);
248 static void shutdown_buckets(dns_acache_t *acache);
249 static void destroy_entry(dns_acacheentry_t *ent);
250 static inline void unlink_dbentries(dns_acache_t *acache,
251 dns_acacheentry_t *ent);
252 static inline isc_result_t finddbent(dns_acache_t *acache,
253 dns_db_t *db, dbentry_t **dbentryp);
254 static inline void clear_entry(dns_acache_t *acache, dns_acacheentry_t *entry);
255 static isc_result_t acache_cleaner_init(dns_acache_t *acache,
256 isc_timermgr_t *timermgr,
257 acache_cleaner_t *cleaner);
258 static void acache_cleaning_timer_action(isc_task_t *task, isc_event_t *event);
259 static void acache_incremental_cleaning_action(isc_task_t *task,
260 isc_event_t *event);
261 static void acache_overmem_cleaning_action(isc_task_t *task,
262 isc_event_t *event);
263 static void acache_cleaner_shutdown_action(isc_task_t *task,
264 isc_event_t *event);
267 * acache should be locked. If it is not, the stats can get out of whack,
268 * which is not a big deal for us since this is for debugging / stats
270 static void
271 reset_stats(dns_acache_t *acache) {
272 acache->stats.hits = 0;
273 acache->stats.queries = 0;
274 acache->stats.misses = 0;
275 acache->stats.adds = 0;
276 acache->stats.deleted = 0;
277 acache->stats.cleaned = 0;
278 acache->stats.overmem = 0;
279 acache->stats.overmem_nocreates = 0;
280 acache->stats.nomem = 0;
284 * The acache must be locked before calling.
286 static inline isc_boolean_t
287 check_noentry(dns_acache_t *acache) {
288 if (ISC_LIST_EMPTY(acache->entries) && acache->dbentries == 0) {
289 return (ISC_TRUE);
292 return (ISC_FALSE);
296 * The acache must be locked before calling.
298 static void
299 shutdown_entries(dns_acache_t *acache) {
300 dns_acacheentry_t *entry, *entry_next;
302 REQUIRE(DNS_ACACHE_VALID(acache));
303 INSIST(acache->shutting_down);
306 * Release the dependency of all entries, and detach them.
308 for (entry = ISC_LIST_HEAD(acache->entries);
309 entry != NULL;
310 entry = entry_next) {
311 entry_next = ISC_LIST_NEXT(entry, link);
313 ACACHE_LOCK(&acache->entrylocks[entry->locknum],
314 isc_rwlocktype_write);
317 * If the cleaner holds this entry, it will be unlinked and
318 * freed in the cleaner later.
320 if (acache->cleaner.current_entry != entry)
321 ISC_LIST_UNLINK(acache->entries, entry, link);
322 unlink_dbentries(acache, entry);
323 if (entry->callback != NULL) {
324 (entry->callback)(entry, &entry->cbarg);
325 entry->callback = NULL;
328 ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
329 isc_rwlocktype_write);
331 if (acache->cleaner.current_entry != entry)
332 dns_acache_detachentry(&entry);
337 * The acache must be locked before calling.
339 static void
340 shutdown_buckets(dns_acache_t *acache) {
341 int i;
342 dbentry_t *dbent;
344 REQUIRE(DNS_ACACHE_VALID(acache));
345 INSIST(acache->shutting_down);
347 for (i = 0; i < DBBUCKETS; i++) {
348 while ((dbent = ISC_LIST_HEAD(acache->dbbucket[i])) != NULL) {
349 INSIST(ISC_LIST_EMPTY(dbent->originlist) &&
350 ISC_LIST_EMPTY(dbent->referlist));
351 ISC_LIST_UNLINK(acache->dbbucket[i], dbent, link);
353 dns_db_detach(&dbent->db);
355 isc_mem_put(acache->mctx, dbent, sizeof(*dbent));
357 acache->dbentries--;
361 INSIST(acache->dbentries == 0);
364 static void
365 shutdown_task(isc_task_t *task, isc_event_t *ev) {
366 dns_acache_t *acache;
368 UNUSED(task);
370 acache = ev->ev_arg;
371 INSIST(DNS_ACACHE_VALID(acache));
373 isc_event_free(&ev);
375 LOCK(&acache->lock);
377 shutdown_entries(acache);
378 shutdown_buckets(acache);
380 UNLOCK(&acache->lock);
382 dns_acache_detach(&acache);
385 /* The acache and the entry must be locked before calling. */
386 static inline void
387 unlink_dbentries(dns_acache_t *acache, dns_acacheentry_t *ent) {
388 isc_result_t result;
389 dbentry_t *dbent;
391 if (ISC_LINK_LINKED(ent, olink)) {
392 INSIST(ent->origdb != NULL);
393 dbent = NULL;
394 result = finddbent(acache, ent->origdb, &dbent);
395 INSIST(result == ISC_R_SUCCESS);
397 ISC_LIST_UNLINK(dbent->originlist, ent, olink);
399 if (ISC_LINK_LINKED(ent, rlink)) {
400 INSIST(ent->db != NULL);
401 dbent = NULL;
402 result = finddbent(acache, ent->db, &dbent);
403 INSIST(result == ISC_R_SUCCESS);
405 ISC_LIST_UNLINK(dbent->referlist, ent, rlink);
409 /* There must not be a reference to this entry. */
410 static void
411 destroy_entry(dns_acacheentry_t *entry) {
412 dns_acache_t *acache;
414 REQUIRE(DNS_ACACHEENTRY_VALID(entry));
416 acache = entry->acache;
417 REQUIRE(DNS_ACACHE_VALID(acache));
420 * Since there is no reference to this entry, it is safe to call
421 * clear_entry() here.
423 clear_entry(acache, entry);
425 isc_mem_put(acache->mctx, entry, sizeof(*entry));
427 dns_acache_detach(&acache);
430 static void
431 destroy(dns_acache_t *acache) {
432 int i;
434 REQUIRE(DNS_ACACHE_VALID(acache));
436 ATRACE("destroy");
438 isc_mem_setwater(acache->mctx, NULL, NULL, 0, 0);
440 if (acache->cleaner.overmem_event != NULL)
441 isc_event_free(&acache->cleaner.overmem_event);
443 if (acache->cleaner.resched_event != NULL)
444 isc_event_free(&acache->cleaner.resched_event);
446 if (acache->task != NULL)
447 isc_task_detach(&acache->task);
449 for (i = 0; i < DEFAULT_ACACHE_ENTRY_LOCK_COUNT; i++)
450 ACACHE_DESTROYLOCK(&acache->entrylocks[i]);
451 isc_mem_put(acache->mctx, acache->entrylocks,
452 sizeof(*acache->entrylocks) *
453 DEFAULT_ACACHE_ENTRY_LOCK_COUNT);
455 DESTROYLOCK(&acache->cleaner.lock);
457 DESTROYLOCK(&acache->lock);
458 acache->magic = 0;
460 isc_mem_putanddetach(&acache->mctx, acache, sizeof(*acache));
463 static inline isc_result_t
464 finddbent(dns_acache_t *acache, dns_db_t *db, dbentry_t **dbentryp) {
465 int bucket;
466 dbentry_t *dbentry;
468 REQUIRE(DNS_ACACHE_VALID(acache));
469 REQUIRE(db != NULL);
470 REQUIRE(dbentryp != NULL && *dbentryp == NULL);
473 * The caller must be holding the acache lock.
476 bucket = isc_hash_calc((const unsigned char *)&db,
477 sizeof(db), ISC_TRUE) % DBBUCKETS;
479 for (dbentry = ISC_LIST_HEAD(acache->dbbucket[bucket]);
480 dbentry != NULL;
481 dbentry = ISC_LIST_NEXT(dbentry, link)) {
482 if (dbentry->db == db)
483 break;
486 *dbentryp = dbentry;
488 if (dbentry == NULL)
489 return (ISC_R_NOTFOUND);
490 else
491 return (ISC_R_SUCCESS);
494 static inline void
495 clear_entry(dns_acache_t *acache, dns_acacheentry_t *entry) {
496 REQUIRE(DNS_ACACHE_VALID(acache));
497 REQUIRE(DNS_ACACHEENTRY_VALID(entry));
500 * The caller must be holing the entry lock.
503 if (entry->foundname) {
504 dns_rdataset_t *rdataset, *rdataset_next;
506 for (rdataset = ISC_LIST_HEAD(entry->foundname->list);
507 rdataset != NULL;
508 rdataset = rdataset_next) {
509 rdataset_next = ISC_LIST_NEXT(rdataset, link);
510 ISC_LIST_UNLINK(entry->foundname->list,
511 rdataset, link);
512 dns_rdataset_disassociate(rdataset);
513 isc_mem_put(acache->mctx, rdataset, sizeof(*rdataset));
515 if (dns_name_dynamic(entry->foundname))
516 dns_name_free(entry->foundname, acache->mctx);
517 isc_mem_put(acache->mctx, entry->foundname,
518 sizeof(*entry->foundname));
519 entry->foundname = NULL;
522 if (entry->node != NULL) {
523 INSIST(entry->db != NULL);
524 dns_db_detachnode(entry->db, &entry->node);
526 if (entry->version != NULL) {
527 INSIST(entry->db != NULL);
528 dns_db_closeversion(entry->db, &entry->version, ISC_FALSE);
530 if (entry->db != NULL)
531 dns_db_detach(&entry->db);
532 if (entry->zone != NULL)
533 dns_zone_detach(&entry->zone);
535 if (entry->origdb != NULL)
536 dns_db_detach(&entry->origdb);
539 static isc_result_t
540 acache_cleaner_init(dns_acache_t *acache, isc_timermgr_t *timermgr,
541 acache_cleaner_t *cleaner)
543 int result;
545 ATRACE("acache cleaner init");
547 result = isc_mutex_init(&cleaner->lock);
548 if (result != ISC_R_SUCCESS)
549 goto fail;
551 cleaner->increment = DNS_ACACHE_CLEANERINCREMENT;
552 cleaner->state = cleaner_s_idle;
553 cleaner->acache = acache;
554 cleaner->overmem = ISC_FALSE;
556 cleaner->cleaning_timer = NULL;
557 cleaner->resched_event = NULL;
558 cleaner->overmem_event = NULL;
559 cleaner->current_entry = NULL;
561 if (timermgr != NULL) {
562 cleaner->acache->live_cleaners++;
564 result = isc_task_onshutdown(acache->task,
565 acache_cleaner_shutdown_action,
566 acache);
567 if (result != ISC_R_SUCCESS) {
568 UNEXPECTED_ERROR(__FILE__, __LINE__,
569 "acache cleaner: "
570 "isc_task_onshutdown() failed: %s",
571 dns_result_totext(result));
572 goto cleanup;
575 cleaner->cleaning_interval = 0; /* Initially turned off. */
576 isc_stdtime_get(&cleaner->last_cleanup_time);
577 result = isc_timer_create(timermgr, isc_timertype_inactive,
578 NULL, NULL,
579 acache->task,
580 acache_cleaning_timer_action,
581 cleaner, &cleaner->cleaning_timer);
582 if (result != ISC_R_SUCCESS) {
583 UNEXPECTED_ERROR(__FILE__, __LINE__,
584 "isc_timer_create() failed: %s",
585 dns_result_totext(result));
586 result = ISC_R_UNEXPECTED;
587 goto cleanup;
590 cleaner->resched_event =
591 isc_event_allocate(acache->mctx, cleaner,
592 DNS_EVENT_ACACHECLEAN,
593 acache_incremental_cleaning_action,
594 cleaner, sizeof(isc_event_t));
595 if (cleaner->resched_event == NULL) {
596 result = ISC_R_NOMEMORY;
597 goto cleanup;
600 cleaner->overmem_event =
601 isc_event_allocate(acache->mctx, cleaner,
602 DNS_EVENT_ACACHEOVERMEM,
603 acache_overmem_cleaning_action,
604 cleaner, sizeof(isc_event_t));
605 if (cleaner->overmem_event == NULL) {
606 result = ISC_R_NOMEMORY;
607 goto cleanup;
611 return (ISC_R_SUCCESS);
613 cleanup:
614 if (cleaner->overmem_event != NULL)
615 isc_event_free(&cleaner->overmem_event);
616 if (cleaner->resched_event != NULL)
617 isc_event_free(&cleaner->resched_event);
618 if (cleaner->cleaning_timer != NULL)
619 isc_timer_detach(&cleaner->cleaning_timer);
620 cleaner->acache->live_cleaners--;
621 DESTROYLOCK(&cleaner->lock);
622 fail:
623 return (result);
626 static void
627 begin_cleaning(acache_cleaner_t *cleaner) {
628 dns_acacheentry_t *head;
629 dns_acache_t *acache = cleaner->acache;
632 * This function does not have to lock the cleaner, since critical
633 * parameters (except current_entry, which is locked by acache lock,)
634 * are only used in a single task context.
637 REQUIRE(CLEANER_IDLE(cleaner));
638 INSIST(DNS_ACACHE_VALID(acache));
639 INSIST(cleaner->current_entry == NULL);
641 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
642 DNS_LOGMODULE_ACACHE, ISC_LOG_DEBUG(1),
643 "begin acache cleaning, mem inuse %lu",
644 (unsigned long)isc_mem_inuse(cleaner->acache->mctx));
646 LOCK(&acache->lock);
648 head = ISC_LIST_HEAD(acache->entries);
649 if (head != NULL)
650 dns_acache_attachentry(head, &cleaner->current_entry);
652 UNLOCK(&acache->lock);
654 if (cleaner->current_entry != NULL) {
655 cleaner->ncleaned = 0;
656 cleaner->state = cleaner_s_busy;
657 isc_task_send(acache->task, &cleaner->resched_event);
660 return;
663 static void
664 end_cleaning(acache_cleaner_t *cleaner, isc_event_t *event) {
665 dns_acache_t *acache = cleaner->acache;
667 REQUIRE(CLEANER_BUSY(cleaner));
668 REQUIRE(event != NULL);
669 REQUIRE(DNS_ACACHEENTRY_VALID(cleaner->current_entry));
671 /* No need to lock the cleaner (see begin_cleaning()). */
673 LOCK(&acache->lock);
676 * Even if the cleaner has the last reference to the entry, which means
677 * the entry has been unused, it may still be linked if unlinking the
678 * entry has been delayed due to the reference.
680 if (isc_refcount_current(&cleaner->current_entry->references) == 1) {
681 INSIST(cleaner->current_entry->callback == NULL);
683 if (ISC_LINK_LINKED(cleaner->current_entry, link)) {
684 ISC_LIST_UNLINK(acache->entries,
685 cleaner->current_entry, link);
688 dns_acache_detachentry(&cleaner->current_entry);
690 if (cleaner->overmem)
691 acache->stats.overmem++;
692 acache->stats.cleaned += cleaner->ncleaned;
693 acache->stats.cleaner_runs++;
695 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE,
696 ISC_LOG_NOTICE,
697 "acache %p stats: hits=%d misses=%d queries=%d "
698 "adds=%d deleted=%d "
699 "cleaned=%d cleaner_runs=%d overmem=%d "
700 "overmem_nocreates=%d nomem=%d",
701 acache,
702 acache->stats.hits, acache->stats.misses,
703 acache->stats.queries,
704 acache->stats.adds, acache->stats.deleted,
705 acache->stats.cleaned, acache->stats.cleaner_runs,
706 acache->stats.overmem, acache->stats.overmem_nocreates,
707 acache->stats.nomem);
708 reset_stats(acache);
710 isc_stdtime_get(&cleaner->last_cleanup_time);
712 UNLOCK(&acache->lock);
714 dns_acache_setcleaninginterval(cleaner->acache,
715 cleaner->cleaning_interval);
717 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE,
718 ISC_LOG_DEBUG(1), "end acache cleaning, "
719 "%lu entries cleaned, mem inuse %lu",
720 cleaner->ncleaned,
721 (unsigned long)isc_mem_inuse(cleaner->acache->mctx));
723 if (cleaner->overmem) {
724 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
725 DNS_LOGMODULE_ACACHE, ISC_LOG_NOTICE,
726 "acache is still in overmem state "
727 "after cleaning");
730 cleaner->ncleaned = 0;
731 cleaner->state = cleaner_s_idle;
732 cleaner->resched_event = event;
736 * This is run once for every acache-cleaning-interval as defined
737 * in named.conf.
739 static void
740 acache_cleaning_timer_action(isc_task_t *task, isc_event_t *event) {
741 acache_cleaner_t *cleaner = event->ev_arg;
743 UNUSED(task);
745 INSIST(event->ev_type == ISC_TIMEREVENT_TICK);
747 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE,
748 ISC_LOG_DEBUG(1), "acache cleaning timer fired, "
749 "cleaner state = %d", cleaner->state);
751 if (cleaner->state == cleaner_s_idle)
752 begin_cleaning(cleaner);
754 isc_event_free(&event);
757 /* The caller must hold entry lock. */
758 static inline isc_boolean_t
759 entry_stale(acache_cleaner_t *cleaner, dns_acacheentry_t *entry,
760 isc_stdtime32_t now32, unsigned int interval)
763 * If the callback has been canceled, we definitely do not need the
764 * entry.
766 if (entry->callback == NULL)
767 return (ISC_TRUE);
769 if (interval > cleaner->cleaning_interval)
770 interval = cleaner->cleaning_interval;
772 if (entry->lastused + interval < now32)
773 return (ISC_TRUE);
776 * If the acache is in the overmem state, probabilistically decide if
777 * the entry should be purged, based on the time passed from its last
778 * use and the cleaning interval.
780 if (cleaner->overmem) {
781 unsigned int passed =
782 now32 - entry->lastused; /* <= interval */
783 isc_uint32_t val;
785 if (passed > interval / 2)
786 return (ISC_TRUE);
787 isc_random_get(&val);
788 if (passed > interval / 4)
789 return (ISC_TF(val % 4 == 0));
790 return (ISC_TF(val % 8 == 0));
793 return (ISC_FALSE);
797 * Do incremental cleaning.
799 static void
800 acache_incremental_cleaning_action(isc_task_t *task, isc_event_t *event) {
801 acache_cleaner_t *cleaner = event->ev_arg;
802 dns_acache_t *acache = cleaner->acache;
803 dns_acacheentry_t *entry, *next = NULL;
804 int n_entries;
805 isc_stdtime32_t now32, last32;
806 isc_stdtime_t now;
807 unsigned int interval;
809 INSIST(DNS_ACACHE_VALID(acache));
810 INSIST(task == acache->task);
811 INSIST(event->ev_type == DNS_EVENT_ACACHECLEAN);
813 if (cleaner->state == cleaner_s_done) {
814 cleaner->state = cleaner_s_busy;
815 end_cleaning(cleaner, event);
816 return;
819 INSIST(CLEANER_BUSY(cleaner));
821 n_entries = cleaner->increment;
823 isc_stdtime_get(&now);
824 isc_stdtime_convert32(now, &now32);
826 LOCK(&acache->lock);
828 entry = cleaner->current_entry;
829 isc_stdtime_convert32(cleaner->last_cleanup_time, &last32);
830 INSIST(now32 > last32);
831 interval = now32 - last32;
833 while (n_entries-- > 0) {
834 isc_boolean_t is_stale = ISC_FALSE;
836 INSIST(entry != NULL);
838 next = ISC_LIST_NEXT(entry, link);
840 ACACHE_LOCK(&acache->entrylocks[entry->locknum],
841 isc_rwlocktype_write);
843 is_stale = entry_stale(cleaner, entry, now32, interval);
844 if (is_stale) {
845 ISC_LIST_UNLINK(acache->entries, entry, link);
846 unlink_dbentries(acache, entry);
847 if (entry->callback != NULL)
848 (entry->callback)(entry, &entry->cbarg);
849 entry->callback = NULL;
851 cleaner->ncleaned++;
854 ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
855 isc_rwlocktype_write);
857 if (is_stale)
858 dns_acache_detachentry(&entry);
860 if (next == NULL) {
861 if (cleaner->overmem) {
862 entry = ISC_LIST_HEAD(acache->entries);
863 if (entry != NULL) {
865 * If we are still in the overmem
866 * state, keep cleaning.
868 isc_log_write(dns_lctx,
869 DNS_LOGCATEGORY_DATABASE,
870 DNS_LOGMODULE_ACACHE,
871 ISC_LOG_DEBUG(1),
872 "acache cleaner: "
873 "still overmem, "
874 "reset and try again");
875 continue;
879 UNLOCK(&acache->lock);
880 end_cleaning(cleaner, event);
881 return;
884 entry = next;
888 * We have successfully performed a cleaning increment but have
889 * not gone through the entire cache. Remember the entry that will
890 * be the starting point in the next clean-up, and reschedule another
891 * batch. If it fails, just try to continue anyway.
893 INSIST(next != NULL && next != cleaner->current_entry);
894 dns_acache_detachentry(&cleaner->current_entry);
895 dns_acache_attachentry(next, &cleaner->current_entry);
897 UNLOCK(&acache->lock);
899 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE,
900 ISC_LOG_DEBUG(1), "acache cleaner: checked %d entries, "
901 "mem inuse %lu, sleeping", cleaner->increment,
902 (unsigned long)isc_mem_inuse(cleaner->acache->mctx));
904 isc_task_send(task, &event);
905 INSIST(CLEANER_BUSY(cleaner));
907 return;
911 * This is called when the acache either surpasses its upper limit
912 * or shrinks beyond its lower limit.
914 static void
915 acache_overmem_cleaning_action(isc_task_t *task, isc_event_t *event) {
916 acache_cleaner_t *cleaner = event->ev_arg;
917 isc_boolean_t want_cleaning = ISC_FALSE;
919 UNUSED(task);
921 INSIST(event->ev_type == DNS_EVENT_ACACHEOVERMEM);
922 INSIST(cleaner->overmem_event == NULL);
924 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE,
925 ISC_LOG_DEBUG(1), "overmem_cleaning_action called, "
926 "overmem = %d, state = %d", cleaner->overmem,
927 cleaner->state);
929 LOCK(&cleaner->lock);
931 if (cleaner->overmem) {
932 if (cleaner->state == cleaner_s_idle)
933 want_cleaning = ISC_TRUE;
934 } else {
935 if (cleaner->state == cleaner_s_busy)
937 * end_cleaning() can't be called here because
938 * then both cleaner->overmem_event and
939 * cleaner->resched_event will point to this
940 * event. Set the state to done, and then
941 * when the acache_incremental_cleaning_action() event
942 * is posted, it will handle the end_cleaning.
944 cleaner->state = cleaner_s_done;
947 cleaner->overmem_event = event;
949 UNLOCK(&cleaner->lock);
951 if (want_cleaning)
952 begin_cleaning(cleaner);
955 static void
956 water(void *arg, int mark) {
957 dns_acache_t *acache = arg;
958 isc_boolean_t overmem = ISC_TF(mark == ISC_MEM_HIWATER);
960 REQUIRE(DNS_ACACHE_VALID(acache));
962 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
963 DNS_LOGMODULE_ACACHE, ISC_LOG_DEBUG(1),
964 "acache memory reaches %s watermark, mem inuse %lu",
965 overmem ? "high" : "low",
966 (unsigned long)isc_mem_inuse(acache->mctx));
968 LOCK(&acache->cleaner.lock);
970 if (acache->cleaner.overmem != overmem) {
971 acache->cleaner.overmem = overmem;
973 if (acache->cleaner.overmem_event != NULL)
974 isc_task_send(acache->task,
975 &acache->cleaner.overmem_event);
976 isc_mem_waterack(acache->mctx, mark);
979 UNLOCK(&acache->cleaner.lock);
983 * The cleaner task is shutting down; do the necessary cleanup.
985 static void
986 acache_cleaner_shutdown_action(isc_task_t *task, isc_event_t *event) {
987 dns_acache_t *acache = event->ev_arg;
988 isc_boolean_t should_free = ISC_FALSE;
990 INSIST(task == acache->task);
991 INSIST(event->ev_type == ISC_TASKEVENT_SHUTDOWN);
992 INSIST(DNS_ACACHE_VALID(acache));
994 ATRACE("acache cleaner shutdown");
996 if (CLEANER_BUSY(&acache->cleaner))
997 end_cleaning(&acache->cleaner, event);
998 else
999 isc_event_free(&event);
1001 LOCK(&acache->lock);
1003 acache->live_cleaners--;
1004 INSIST(acache->live_cleaners == 0);
1006 if (isc_refcount_current(&acache->refs) == 0) {
1007 INSIST(check_noentry(acache) == ISC_TRUE);
1008 should_free = ISC_TRUE;
1012 * By detaching the timer in the context of its task,
1013 * we are guaranteed that there will be no further timer
1014 * events.
1016 if (acache->cleaner.cleaning_timer != NULL)
1017 isc_timer_detach(&acache->cleaner.cleaning_timer);
1019 /* Make sure we don't reschedule anymore. */
1020 (void)isc_task_purge(task, NULL, DNS_EVENT_ACACHECLEAN, NULL);
1022 UNLOCK(&acache->lock);
1024 if (should_free)
1025 destroy(acache);
1029 * Public functions.
1032 isc_result_t
1033 dns_acache_create(dns_acache_t **acachep, isc_mem_t *mctx,
1034 isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr)
1036 int i;
1037 isc_result_t result;
1038 dns_acache_t *acache;
1040 REQUIRE(acachep != NULL && *acachep == NULL);
1041 REQUIRE(mctx != NULL);
1042 REQUIRE(taskmgr != NULL);
1044 acache = isc_mem_get(mctx, sizeof(*acache));
1045 if (acache == NULL)
1046 return (ISC_R_NOMEMORY);
1048 ATRACE("create");
1050 result = isc_refcount_init(&acache->refs, 1);
1051 if (result != ISC_R_SUCCESS) {
1052 isc_mem_put(mctx, acache, sizeof(*acache));
1053 return (result);
1056 result = isc_mutex_init(&acache->lock);
1057 if (result != ISC_R_SUCCESS) {
1058 isc_refcount_decrement(&acache->refs, NULL);
1059 isc_refcount_destroy(&acache->refs);
1060 isc_mem_put(mctx, acache, sizeof(*acache));
1061 return (result);
1064 acache->mctx = NULL;
1065 isc_mem_attach(mctx, &acache->mctx);
1066 ISC_LIST_INIT(acache->entries);
1068 acache->shutting_down = ISC_FALSE;
1070 acache->task = NULL;
1071 acache->entrylocks = NULL;
1073 result = isc_task_create(taskmgr, 1, &acache->task);
1074 if (result != ISC_R_SUCCESS) {
1075 UNEXPECTED_ERROR(__FILE__, __LINE__,
1076 "isc_task_create() failed(): %s",
1077 dns_result_totext(result));
1078 result = ISC_R_UNEXPECTED;
1079 goto cleanup;
1081 isc_task_setname(acache->task, "acachetask", acache);
1082 ISC_EVENT_INIT(&acache->cevent, sizeof(acache->cevent), 0, NULL,
1083 DNS_EVENT_ACACHECONTROL, shutdown_task, NULL,
1084 NULL, NULL, NULL);
1085 acache->cevent_sent = ISC_FALSE;
1087 acache->dbentries = 0;
1088 for (i = 0; i < DBBUCKETS; i++)
1089 ISC_LIST_INIT(acache->dbbucket[i]);
1091 acache->entrylocks = isc_mem_get(mctx, sizeof(*acache->entrylocks) *
1092 DEFAULT_ACACHE_ENTRY_LOCK_COUNT);
1093 if (acache->entrylocks == NULL) {
1094 result = ISC_R_NOMEMORY;
1095 goto cleanup;
1097 for (i = 0; i < DEFAULT_ACACHE_ENTRY_LOCK_COUNT; i++) {
1098 result = ACACHE_INITLOCK(&acache->entrylocks[i]);
1099 if (result != ISC_R_SUCCESS) {
1100 while (i-- > 0)
1101 ACACHE_DESTROYLOCK(&acache->entrylocks[i]);
1102 isc_mem_put(mctx, acache->entrylocks,
1103 sizeof(*acache->entrylocks) *
1104 DEFAULT_ACACHE_ENTRY_LOCK_COUNT);
1105 acache->entrylocks = NULL;
1106 goto cleanup;
1110 acache->live_cleaners = 0;
1111 result = acache_cleaner_init(acache, timermgr, &acache->cleaner);
1112 if (result != ISC_R_SUCCESS)
1113 goto cleanup;
1115 acache->stats.cleaner_runs = 0;
1116 reset_stats(acache);
1118 acache->magic = ACACHE_MAGIC;
1120 *acachep = acache;
1121 return (ISC_R_SUCCESS);
1123 cleanup:
1124 if (acache->task != NULL)
1125 isc_task_detach(&acache->task);
1126 DESTROYLOCK(&acache->lock);
1127 isc_refcount_decrement(&acache->refs, NULL);
1128 isc_refcount_destroy(&acache->refs);
1129 if (acache->entrylocks != NULL) {
1130 for (i = 0; i < DEFAULT_ACACHE_ENTRY_LOCK_COUNT; i++)
1131 ACACHE_DESTROYLOCK(&acache->entrylocks[i]);
1132 isc_mem_put(mctx, acache->entrylocks,
1133 sizeof(*acache->entrylocks) *
1134 DEFAULT_ACACHE_ENTRY_LOCK_COUNT);
1136 isc_mem_put(mctx, acache, sizeof(*acache));
1137 isc_mem_detach(&mctx);
1139 return (result);
1142 void
1143 dns_acache_attach(dns_acache_t *source, dns_acache_t **targetp) {
1144 REQUIRE(DNS_ACACHE_VALID(source));
1145 REQUIRE(targetp != NULL && *targetp == NULL);
1147 AATRACE(source, "attach");
1149 isc_refcount_increment(&source->refs, NULL);
1151 *targetp = source;
1154 void
1155 dns_acache_countquerymiss(dns_acache_t *acache) {
1156 acache->stats.misses++; /* XXXSK danger: unlocked! */
1157 acache->stats.queries++; /* XXXSK danger: unlocked! */
1160 void
1161 dns_acache_detach(dns_acache_t **acachep) {
1162 dns_acache_t *acache;
1163 unsigned int refs;
1164 isc_boolean_t should_free = ISC_FALSE;
1166 REQUIRE(acachep != NULL && DNS_ACACHE_VALID(*acachep));
1167 acache = *acachep;
1169 ATRACE("detach");
1171 isc_refcount_decrement(&acache->refs, &refs);
1172 if (refs == 0) {
1173 INSIST(check_noentry(acache) == ISC_TRUE);
1174 should_free = ISC_TRUE;
1177 *acachep = NULL;
1180 * If we're exiting and the cleaner task exists, let it free the cache.
1182 if (should_free && acache->live_cleaners > 0) {
1183 isc_task_shutdown(acache->task);
1184 should_free = ISC_FALSE;
1187 if (should_free)
1188 destroy(acache);
1191 void
1192 dns_acache_shutdown(dns_acache_t *acache) {
1193 REQUIRE(DNS_ACACHE_VALID(acache));
1195 LOCK(&acache->lock);
1197 ATRACE("shutdown");
1199 if (!acache->shutting_down) {
1200 isc_event_t *event;
1201 dns_acache_t *acache_evarg = NULL;
1203 INSIST(!acache->cevent_sent);
1205 acache->shutting_down = ISC_TRUE;
1207 isc_mem_setwater(acache->mctx, NULL, NULL, 0, 0);
1210 * Self attach the object in order to prevent it from being
1211 * destroyed while waiting for the event.
1213 dns_acache_attach(acache, &acache_evarg);
1214 event = &acache->cevent;
1215 event->ev_arg = acache_evarg;
1216 isc_task_send(acache->task, &event);
1217 acache->cevent_sent = ISC_TRUE;
1220 UNLOCK(&acache->lock);
1223 isc_result_t
1224 dns_acache_setdb(dns_acache_t *acache, dns_db_t *db) {
1225 int bucket;
1226 dbentry_t *dbentry;
1227 isc_result_t result = ISC_R_SUCCESS;
1229 REQUIRE(DNS_ACACHE_VALID(acache));
1230 REQUIRE(db != NULL);
1232 ATRACE("setdb");
1234 LOCK(&acache->lock);
1236 dbentry = NULL;
1237 result = finddbent(acache, db, &dbentry);
1238 if (result == ISC_R_SUCCESS) {
1239 result = ISC_R_EXISTS;
1240 goto end;
1242 result = ISC_R_SUCCESS;
1244 dbentry = isc_mem_get(acache->mctx, sizeof(*dbentry));
1245 if (dbentry == NULL) {
1246 result = ISC_R_NOMEMORY;
1247 goto end;
1250 ISC_LINK_INIT(dbentry, link);
1251 ISC_LIST_INIT(dbentry->originlist);
1252 ISC_LIST_INIT(dbentry->referlist);
1254 dbentry->db = NULL;
1255 dns_db_attach(db, &dbentry->db);
1257 bucket = isc_hash_calc((const unsigned char *)&db,
1258 sizeof(db), ISC_TRUE) % DBBUCKETS;
1260 ISC_LIST_APPEND(acache->dbbucket[bucket], dbentry, link);
1262 acache->dbentries++;
1264 end:
1265 UNLOCK(&acache->lock);
1267 return (result);
1270 isc_result_t
1271 dns_acache_putdb(dns_acache_t *acache, dns_db_t *db) {
1272 int bucket;
1273 isc_result_t result;
1274 dbentry_t *dbentry;
1275 dns_acacheentry_t *entry;
1277 REQUIRE(DNS_ACACHE_VALID(acache));
1278 REQUIRE(db != NULL);
1280 ATRACE("putdb");
1282 LOCK(&acache->lock);
1284 dbentry = NULL;
1285 result = finddbent(acache, db, &dbentry);
1286 if (result != ISC_R_SUCCESS) {
1288 * The entry may have not been created due to memory shortage.
1290 UNLOCK(&acache->lock);
1291 return (ISC_R_NOTFOUND);
1295 * Release corresponding cache entries: for each entry, release all
1296 * links the entry has, and then callback to the entry holder (if any).
1297 * If no other external references exist (this can happen if the
1298 * original holder has canceled callback,) destroy it here.
1300 while ((entry = ISC_LIST_HEAD(dbentry->originlist)) != NULL) {
1301 ACACHE_LOCK(&acache->entrylocks[entry->locknum],
1302 isc_rwlocktype_write);
1305 * Releasing olink first would avoid finddbent() in
1306 * unlink_dbentries().
1308 ISC_LIST_UNLINK(dbentry->originlist, entry, olink);
1309 if (acache->cleaner.current_entry != entry)
1310 ISC_LIST_UNLINK(acache->entries, entry, link);
1311 unlink_dbentries(acache, entry);
1313 if (entry->callback != NULL)
1314 (entry->callback)(entry, &entry->cbarg);
1315 entry->callback = NULL;
1317 ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
1318 isc_rwlocktype_write);
1320 if (acache->cleaner.current_entry != entry)
1321 dns_acache_detachentry(&entry);
1323 while ((entry = ISC_LIST_HEAD(dbentry->referlist)) != NULL) {
1324 ACACHE_LOCK(&acache->entrylocks[entry->locknum],
1325 isc_rwlocktype_write);
1327 ISC_LIST_UNLINK(dbentry->referlist, entry, rlink);
1328 if (acache->cleaner.current_entry != entry)
1329 ISC_LIST_UNLINK(acache->entries, entry, link);
1330 unlink_dbentries(acache, entry);
1332 if (entry->callback != NULL)
1333 (entry->callback)(entry, &entry->cbarg);
1334 entry->callback = NULL;
1336 ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
1337 isc_rwlocktype_write);
1339 if (acache->cleaner.current_entry != entry)
1340 dns_acache_detachentry(&entry);
1343 INSIST(ISC_LIST_EMPTY(dbentry->originlist) &&
1344 ISC_LIST_EMPTY(dbentry->referlist));
1346 bucket = isc_hash_calc((const unsigned char *)&db,
1347 sizeof(db), ISC_TRUE) % DBBUCKETS;
1348 ISC_LIST_UNLINK(acache->dbbucket[bucket], dbentry, link);
1349 dns_db_detach(&dbentry->db);
1351 isc_mem_put(acache->mctx, dbentry, sizeof(*dbentry));
1353 acache->dbentries--;
1355 acache->stats.deleted++;
1357 UNLOCK(&acache->lock);
1359 return (ISC_R_SUCCESS);
1362 isc_result_t
1363 dns_acache_createentry(dns_acache_t *acache, dns_db_t *origdb,
1364 void (*callback)(dns_acacheentry_t *, void **),
1365 void *cbarg, dns_acacheentry_t **entryp)
1367 dns_acacheentry_t *newentry;
1368 isc_result_t result;
1369 isc_uint32_t r;
1371 REQUIRE(DNS_ACACHE_VALID(acache));
1372 REQUIRE(entryp != NULL && *entryp == NULL);
1373 REQUIRE(origdb != NULL);
1376 * Should we exceed our memory limit for some reason (for
1377 * example, if the cleaner does not run aggressively enough),
1378 * then we will not create additional entries.
1380 * XXXSK: It might be better to lock the acache->cleaner->lock,
1381 * but locking may be an expensive bottleneck. If we misread
1382 * the value, we will occasionally refuse to create a few
1383 * cache entries, or create a few that we should not. I do not
1384 * expect this to happen often, and it will not have very bad
1385 * effects when it does. So no lock for now.
1387 if (acache->cleaner.overmem) {
1388 acache->stats.overmem_nocreates++; /* XXXSK danger: unlocked! */
1389 return (ISC_R_NORESOURCES);
1392 newentry = isc_mem_get(acache->mctx, sizeof(*newentry));
1393 if (newentry == NULL) {
1394 acache->stats.nomem++; /* XXXMLG danger: unlocked! */
1395 return (ISC_R_NOMEMORY);
1398 isc_random_get(&r);
1399 newentry->locknum = r % DEFAULT_ACACHE_ENTRY_LOCK_COUNT;
1401 result = isc_refcount_init(&newentry->references, 1);
1402 if (result != ISC_R_SUCCESS) {
1403 isc_mem_put(acache->mctx, newentry, sizeof(*newentry));
1404 return (result);
1407 ISC_LINK_INIT(newentry, link);
1408 ISC_LINK_INIT(newentry, olink);
1409 ISC_LINK_INIT(newentry, rlink);
1411 newentry->acache = NULL;
1412 dns_acache_attach(acache, &newentry->acache);
1414 newentry->zone = NULL;
1415 newentry->db = NULL;
1416 newentry->version = NULL;
1417 newentry->node = NULL;
1418 newentry->foundname = NULL;
1420 newentry->callback = callback;
1421 newentry->cbarg = cbarg;
1422 newentry->origdb = NULL;
1423 dns_db_attach(origdb, &newentry->origdb);
1425 isc_stdtime_get(&newentry->lastused);
1427 newentry->magic = ACACHEENTRY_MAGIC;
1429 *entryp = newentry;
1431 return (ISC_R_SUCCESS);
1434 isc_result_t
1435 dns_acache_getentry(dns_acacheentry_t *entry, dns_zone_t **zonep,
1436 dns_db_t **dbp, dns_dbversion_t **versionp,
1437 dns_dbnode_t **nodep, dns_name_t *fname,
1438 dns_message_t *msg, isc_stdtime_t now)
1440 isc_result_t result = ISC_R_SUCCESS;
1441 dns_rdataset_t *erdataset;
1442 isc_stdtime32_t now32;
1443 dns_acache_t *acache;
1444 int locknum;
1446 REQUIRE(DNS_ACACHEENTRY_VALID(entry));
1447 REQUIRE(zonep == NULL || *zonep == NULL);
1448 REQUIRE(dbp != NULL && *dbp == NULL);
1449 REQUIRE(versionp != NULL && *versionp == NULL);
1450 REQUIRE(nodep != NULL && *nodep == NULL);
1451 REQUIRE(fname != NULL);
1452 REQUIRE(msg != NULL);
1453 acache = entry->acache;
1454 REQUIRE(DNS_ACACHE_VALID(acache));
1456 locknum = entry->locknum;
1457 ACACHE_LOCK(&acache->entrylocks[locknum], isc_rwlocktype_read);
1459 isc_stdtime_convert32(now, &now32);
1460 acache_storetime(entry, now32);
1462 if (entry->zone != NULL && zonep != NULL)
1463 dns_zone_attach(entry->zone, zonep);
1465 if (entry->db == NULL) {
1466 *dbp = NULL;
1467 *versionp = NULL;
1468 } else {
1469 dns_db_attach(entry->db, dbp);
1470 dns_db_attachversion(entry->db, entry->version, versionp);
1472 if (entry->node == NULL)
1473 *nodep = NULL;
1474 else {
1475 dns_db_attachnode(entry->db, entry->node, nodep);
1477 INSIST(entry->foundname != NULL);
1478 dns_name_copy(entry->foundname, fname, NULL);
1479 for (erdataset = ISC_LIST_HEAD(entry->foundname->list);
1480 erdataset != NULL;
1481 erdataset = ISC_LIST_NEXT(erdataset, link)) {
1482 dns_rdataset_t *ardataset;
1484 ardataset = NULL;
1485 result = dns_message_gettemprdataset(msg, &ardataset);
1486 if (result != ISC_R_SUCCESS) {
1487 ACACHE_UNLOCK(&acache->entrylocks[locknum],
1488 isc_rwlocktype_read);
1489 goto fail;
1493 * XXXJT: if we simply clone the rdataset, we'll get
1494 * lost wrt cyclic ordering. We'll need an additional
1495 * trick to get the latest counter from the original
1496 * header.
1498 dns_rdataset_init(ardataset);
1499 dns_rdataset_clone(erdataset, ardataset);
1500 ISC_LIST_APPEND(fname->list, ardataset, link);
1504 entry->acache->stats.hits++; /* XXXMLG danger: unlocked! */
1505 entry->acache->stats.queries++;
1507 ACACHE_UNLOCK(&acache->entrylocks[locknum], isc_rwlocktype_read);
1509 return (result);
1511 fail:
1512 while ((erdataset = ISC_LIST_HEAD(fname->list)) != NULL) {
1513 ISC_LIST_UNLINK(fname->list, erdataset, link);
1514 dns_rdataset_disassociate(erdataset);
1515 dns_message_puttemprdataset(msg, &erdataset);
1517 if (*nodep != NULL)
1518 dns_db_detachnode(*dbp, nodep);
1519 if (*versionp != NULL)
1520 dns_db_closeversion(*dbp, versionp, ISC_FALSE);
1521 if (*dbp != NULL)
1522 dns_db_detach(dbp);
1523 if (zonep != NULL && *zonep != NULL)
1524 dns_zone_detach(zonep);
1526 return (result);
1529 isc_result_t
1530 dns_acache_setentry(dns_acache_t *acache, dns_acacheentry_t *entry,
1531 dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version,
1532 dns_dbnode_t *node, dns_name_t *fname)
1534 isc_result_t result;
1535 dbentry_t *odbent;
1536 dbentry_t *rdbent = NULL;
1537 isc_boolean_t close_version = ISC_FALSE;
1538 dns_acacheentry_t *dummy_entry = NULL;
1540 REQUIRE(DNS_ACACHE_VALID(acache));
1541 REQUIRE(DNS_ACACHEENTRY_VALID(entry));
1543 LOCK(&acache->lock); /* XXX: need to lock it here for ordering */
1544 ACACHE_LOCK(&acache->entrylocks[entry->locknum], isc_rwlocktype_write);
1546 /* Set zone */
1547 if (zone != NULL)
1548 dns_zone_attach(zone, &entry->zone);
1549 /* Set DB */
1550 if (db != NULL)
1551 dns_db_attach(db, &entry->db);
1553 * Set DB version. If the version is not given by the caller,
1554 * which is the case for glue or cache DBs, use the current version.
1556 if (version == NULL) {
1557 if (db != NULL) {
1558 dns_db_currentversion(db, &version);
1559 close_version = ISC_TRUE;
1562 if (version != NULL) {
1563 INSIST(db != NULL);
1564 dns_db_attachversion(db, version, &entry->version);
1566 if (close_version)
1567 dns_db_closeversion(db, &version, ISC_FALSE);
1568 /* Set DB node. */
1569 if (node != NULL) {
1570 INSIST(db != NULL);
1571 dns_db_attachnode(db, node, &entry->node);
1575 * Set list of the corresponding rdatasets, if given.
1576 * To minimize the overhead and memory consumption, we'll do this for
1577 * positive cache only, in which case the DB node is non NULL.
1578 * We do not want to cache incomplete information, so give up the
1579 * entire entry when a memory shortage happen during the process.
1581 if (node != NULL) {
1582 dns_rdataset_t *ardataset, *crdataset;
1584 entry->foundname = isc_mem_get(acache->mctx,
1585 sizeof(*entry->foundname));
1587 if (entry->foundname == NULL) {
1588 result = ISC_R_NOMEMORY;
1589 goto fail;
1591 dns_name_init(entry->foundname, NULL);
1592 result = dns_name_dup(fname, acache->mctx,
1593 entry->foundname);
1594 if (result != ISC_R_SUCCESS)
1595 goto fail;
1597 for (ardataset = ISC_LIST_HEAD(fname->list);
1598 ardataset != NULL;
1599 ardataset = ISC_LIST_NEXT(ardataset, link)) {
1600 crdataset = isc_mem_get(acache->mctx,
1601 sizeof(*crdataset));
1602 if (crdataset == NULL) {
1603 result = ISC_R_NOMEMORY;
1604 goto fail;
1607 dns_rdataset_init(crdataset);
1608 dns_rdataset_clone(ardataset, crdataset);
1609 ISC_LIST_APPEND(entry->foundname->list, crdataset,
1610 link);
1614 odbent = NULL;
1615 result = finddbent(acache, entry->origdb, &odbent);
1616 if (result != ISC_R_SUCCESS)
1617 goto fail;
1618 if (db != NULL) {
1619 rdbent = NULL;
1620 result = finddbent(acache, db, &rdbent);
1621 if (result != ISC_R_SUCCESS)
1622 goto fail;
1625 ISC_LIST_APPEND(acache->entries, entry, link);
1626 ISC_LIST_APPEND(odbent->originlist, entry, olink);
1627 if (rdbent != NULL)
1628 ISC_LIST_APPEND(rdbent->referlist, entry, rlink);
1631 * The additional cache needs an implicit reference to entries in its
1632 * link.
1634 dns_acache_attachentry(entry, &dummy_entry);
1636 ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
1637 isc_rwlocktype_write);
1639 acache->stats.adds++;
1640 UNLOCK(&acache->lock);
1642 return (ISC_R_SUCCESS);
1644 fail:
1645 clear_entry(acache, entry);
1647 ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
1648 isc_rwlocktype_write);
1649 UNLOCK(&acache->lock);
1651 return (result);
1654 void
1655 dns_acache_cancelentry(dns_acacheentry_t *entry) {
1656 dns_acache_t *acache = entry->acache;
1658 REQUIRE(DNS_ACACHEENTRY_VALID(entry));
1659 INSIST(DNS_ACACHE_VALID(acache));
1661 LOCK(&acache->lock);
1662 ACACHE_LOCK(&acache->entrylocks[entry->locknum], isc_rwlocktype_write);
1665 * Release dependencies stored in this entry as much as possible.
1666 * The main link cannot be released, since the acache object has
1667 * a reference to this entry; the empty entry will be released in
1668 * the next cleaning action.
1670 unlink_dbentries(acache, entry);
1671 clear_entry(entry->acache, entry);
1673 entry->callback = NULL;
1674 entry->cbarg = NULL;
1676 ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
1677 isc_rwlocktype_write);
1678 UNLOCK(&acache->lock);
1681 void
1682 dns_acache_attachentry(dns_acacheentry_t *source,
1683 dns_acacheentry_t **targetp)
1685 REQUIRE(DNS_ACACHEENTRY_VALID(source));
1686 REQUIRE(targetp != NULL && *targetp == NULL);
1688 isc_refcount_increment(&source->references, NULL);
1690 *targetp = source;
1693 void
1694 dns_acache_detachentry(dns_acacheentry_t **entryp) {
1695 dns_acacheentry_t *entry;
1696 unsigned int refs;
1698 REQUIRE(entryp != NULL && DNS_ACACHEENTRY_VALID(*entryp));
1699 entry = *entryp;
1701 isc_refcount_decrement(&entry->references, &refs);
1704 * If there are no references to the entry, the entry must have been
1705 * unlinked and can be destroyed safely.
1707 if (refs == 0) {
1708 INSIST(!ISC_LINK_LINKED(entry, link));
1709 (*entryp)->acache->stats.deleted++;
1710 destroy_entry(entry);
1713 *entryp = NULL;
1716 void
1717 dns_acache_setcleaninginterval(dns_acache_t *acache, unsigned int t) {
1718 isc_interval_t interval;
1719 isc_result_t result;
1721 REQUIRE(DNS_ACACHE_VALID(acache));
1723 ATRACE("dns_acache_setcleaninginterval");
1725 LOCK(&acache->lock);
1728 * It may be the case that the acache has already shut down.
1729 * If so, it has no timer. (Not sure if this can really happen.)
1731 if (acache->cleaner.cleaning_timer == NULL)
1732 goto unlock;
1734 acache->cleaner.cleaning_interval = t;
1736 if (t == 0) {
1737 result = isc_timer_reset(acache->cleaner.cleaning_timer,
1738 isc_timertype_inactive,
1739 NULL, NULL, ISC_TRUE);
1740 } else {
1741 isc_interval_set(&interval, acache->cleaner.cleaning_interval,
1743 result = isc_timer_reset(acache->cleaner.cleaning_timer,
1744 isc_timertype_ticker,
1745 NULL, &interval, ISC_FALSE);
1747 if (result != ISC_R_SUCCESS)
1748 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1749 DNS_LOGMODULE_ACACHE, ISC_LOG_WARNING,
1750 "could not set acache cleaning interval: %s",
1751 isc_result_totext(result));
1752 else
1753 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1754 DNS_LOGMODULE_ACACHE, ISC_LOG_NOTICE,
1755 "acache %p cleaning interval set to %d.",
1756 acache, t);
1758 unlock:
1759 UNLOCK(&acache->lock);
1763 * This function was derived from cache.c:dns_cache_setcachesize(). See the
1764 * function for more details about the logic.
1766 void
1767 dns_acache_setcachesize(dns_acache_t *acache, isc_uint32_t size) {
1768 isc_uint32_t lowater;
1769 isc_uint32_t hiwater;
1771 REQUIRE(DNS_ACACHE_VALID(acache));
1773 if (size != 0 && size < DNS_ACACHE_MINSIZE)
1774 size = DNS_ACACHE_MINSIZE;
1776 hiwater = size - (size >> 3);
1777 lowater = size - (size >> 2);
1779 if (size == 0 || hiwater == 0 || lowater == 0)
1780 isc_mem_setwater(acache->mctx, water, acache, 0, 0);
1781 else
1782 isc_mem_setwater(acache->mctx, water, acache,
1783 hiwater, lowater);