2 * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14 * PERFORMANCE OF THIS SOFTWARE.
17 /* $Id: acache.c,v 1.20.128.2 2008/02/07 23:46:25 tbox Exp $ */
21 #include <isc/atomic.h>
22 #include <isc/event.h>
24 #include <isc/magic.h>
26 #include <isc/mutex.h>
27 #include <isc/random.h>
28 #include <isc/refcount.h>
29 #include <isc/rwlock.h>
32 #include <isc/timer.h>
34 #include <dns/acache.h>
36 #include <dns/events.h>
38 #include <dns/message.h>
40 #include <dns/rdataset.h>
41 #include <dns/result.h>
44 #define ACACHE_MAGIC ISC_MAGIC('A', 'C', 'H', 'E')
45 #define DNS_ACACHE_VALID(acache) ISC_MAGIC_VALID(acache, ACACHE_MAGIC)
47 #define ACACHEENTRY_MAGIC ISC_MAGIC('A', 'C', 'E', 'T')
48 #define DNS_ACACHEENTRY_VALID(entry) ISC_MAGIC_VALID(entry, ACACHEENTRY_MAGIC)
53 #define ATRACE(m) isc_log_write(dns_lctx, \
54 DNS_LOGCATEGORY_DATABASE, \
55 DNS_LOGMODULE_ACACHE, \
57 "acache %p: %s", acache, (m))
58 #define AATRACE(a,m) isc_log_write(dns_lctx, \
59 DNS_LOGCATEGORY_DATABASE, \
60 DNS_LOGMODULE_ACACHE, \
62 "acache %p: %s", (a), (m))
69 * The following variables control incremental cleaning.
70 * MINSIZE is how many bytes is the floor for dns_acache_setcachesize().
71 * CLEANERINCREMENT is how many entries are examined in one pass.
72 * (XXX simply derived from definitions in cache.c There may be better
75 #define DNS_ACACHE_MINSIZE 2097152 /* Bytes. 2097152 = 2 MB */
76 #define DNS_ACACHE_CLEANERINCREMENT 1000 /* Number of entries. */
78 #define DEFAULT_ACACHE_ENTRY_LOCK_COUNT 1009 /*%< Should be prime. */
80 #if defined(ISC_RWLOCK_USEATOMIC) && defined(ISC_PLATFORM_HAVEATOMICSTORE)
81 #define ACACHE_USE_RWLOCK 1
84 #ifdef ACACHE_USE_RWLOCK
85 #define ACACHE_INITLOCK(l) isc_rwlock_init((l), 0, 0)
86 #define ACACHE_DESTROYLOCK(l) isc_rwlock_destroy(l)
87 #define ACACHE_LOCK(l, t) RWLOCK((l), (t))
88 #define ACACHE_UNLOCK(l, t) RWUNLOCK((l), (t))
90 #define acache_storetime(entry, t) \
91 (isc_atomic_store((isc_int32_t *)&(entry)->lastused, (t)))
93 #define ACACHE_INITLOCK(l) isc_mutex_init(l)
94 #define ACACHE_DESTROYLOCK(l) DESTROYLOCK(l)
95 #define ACACHE_LOCK(l, t) LOCK(l)
96 #define ACACHE_UNLOCK(l, t) UNLOCK(l)
98 #define acache_storetime(entry, t) ((entry)->lastused = (t))
101 /* Locked by acache lock */
102 typedef struct dbentry
{
103 ISC_LINK(struct dbentry
) link
;
106 ISC_LIST(dns_acacheentry_t
) originlist
;
107 ISC_LIST(dns_acacheentry_t
) referlist
;
110 typedef ISC_LIST(dbentry_t
) dbentrylist_t
;
112 typedef struct acache_cleaner acache_cleaner_t
;
115 cleaner_s_idle
, /* Waiting for cleaning-interval to expire. */
116 cleaner_s_busy
, /* Currently cleaning. */
117 cleaner_s_done
/* Freed enough memory after being overmem. */
121 * Convenience macros for comprehensive assertion checking.
123 #define CLEANER_IDLE(c) ((c)->state == cleaner_s_idle && \
124 (c)->resched_event != NULL)
125 #define CLEANER_BUSY(c) ((c)->state == cleaner_s_busy && \
126 (c)->resched_event == NULL)
128 struct acache_cleaner
{
131 * Locks overmem_event, overmem. (See cache.c)
134 dns_acache_t
*acache
;
135 unsigned int cleaning_interval
; /* The cleaning-interval
139 isc_stdtime_t last_cleanup_time
; /* The time when the last
140 cleanup task completed */
142 isc_timer_t
*cleaning_timer
;
143 isc_event_t
*resched_event
; /* Sent by cleaner task to
144 itself to reschedule */
145 isc_event_t
*overmem_event
;
147 dns_acacheentry_t
*current_entry
; /* The bookmark entry to
148 restart the cleaning.
149 Locked by acache lock. */
150 int increment
; /* Number of entries to
151 clean in one increment */
153 unsigned long ncleaned
; /* Number of entries cleaned
154 up (for logging purposes) */
155 cleaner_state_t state
; /* Idle/Busy/Done. */
156 isc_boolean_t overmem
; /* The acache is in an overmem
160 struct dns_acachestats
{
162 unsigned int queries
;
165 unsigned int deleted
;
166 unsigned int cleaned
;
167 unsigned int cleaner_runs
;
168 unsigned int overmem
;
169 unsigned int overmem_nocreates
;
174 * The actual acache object.
183 #ifdef ACACHE_USE_RWLOCK
184 isc_rwlock_t
*entrylocks
;
186 isc_mutex_t
*entrylocks
;
192 acache_cleaner_t cleaner
;
193 ISC_LIST(dns_acacheentry_t
) entries
;
194 unsigned int dbentries
;
195 dbentrylist_t dbbucket
[DBBUCKETS
];
197 isc_boolean_t shutting_down
;
201 isc_boolean_t cevent_sent
;
203 dns_acachestats_t stats
;
206 struct dns_acacheentry
{
209 unsigned int locknum
;
210 isc_refcount_t references
;
212 dns_acache_t
*acache
;
214 /* Data for Management of cache entries */
215 ISC_LINK(dns_acacheentry_t
) link
;
216 ISC_LINK(dns_acacheentry_t
) olink
;
217 ISC_LINK(dns_acacheentry_t
) rlink
;
219 dns_db_t
*origdb
; /* reference to the DB
220 holding this entry */
223 dns_zone_t
*zone
; /* zone this entry
225 dns_db_t
*db
; /* DB this entry belongs to */
226 dns_dbversion_t
*version
; /* the version of the DB */
227 dns_dbnode_t
*node
; /* node this entry
229 dns_name_t
*foundname
; /* corresponding DNS name
232 /* Callback function and its argument */
233 void (*callback
)(dns_acacheentry_t
*, void **);
236 /* Timestamp of the last time this entry is referred to */
237 isc_stdtime32_t lastused
;
241 * Internal functions (and prototypes).
243 static inline isc_boolean_t
check_noentry(dns_acache_t
*acache
);
244 static void destroy(dns_acache_t
*acache
);
245 static void shutdown_entries(dns_acache_t
*acache
);
246 static void shutdown_buckets(dns_acache_t
*acache
);
247 static void destroy_entry(dns_acacheentry_t
*ent
);
248 static inline void unlink_dbentries(dns_acache_t
*acache
,
249 dns_acacheentry_t
*ent
);
250 static inline isc_result_t
finddbent(dns_acache_t
*acache
,
251 dns_db_t
*db
, dbentry_t
**dbentryp
);
252 static inline void clear_entry(dns_acache_t
*acache
, dns_acacheentry_t
*entry
);
253 static isc_result_t
acache_cleaner_init(dns_acache_t
*acache
,
254 isc_timermgr_t
*timermgr
,
255 acache_cleaner_t
*cleaner
);
256 static void acache_cleaning_timer_action(isc_task_t
*task
, isc_event_t
*event
);
257 static void acache_incremental_cleaning_action(isc_task_t
*task
,
259 static void acache_overmem_cleaning_action(isc_task_t
*task
,
261 static void acache_cleaner_shutdown_action(isc_task_t
*task
,
265 * acache should be locked. If it is not, the stats can get out of whack,
266 * which is not a big deal for us since this is for debugging / stats
269 reset_stats(dns_acache_t
*acache
) {
270 acache
->stats
.hits
= 0;
271 acache
->stats
.queries
= 0;
272 acache
->stats
.misses
= 0;
273 acache
->stats
.adds
= 0;
274 acache
->stats
.deleted
= 0;
275 acache
->stats
.cleaned
= 0;
276 acache
->stats
.overmem
= 0;
277 acache
->stats
.overmem_nocreates
= 0;
278 acache
->stats
.nomem
= 0;
282 * The acache must be locked before calling.
284 static inline isc_boolean_t
285 check_noentry(dns_acache_t
*acache
) {
286 if (ISC_LIST_EMPTY(acache
->entries
) && acache
->dbentries
== 0) {
294 * The acache must be locked before calling.
297 shutdown_entries(dns_acache_t
*acache
) {
298 dns_acacheentry_t
*entry
, *entry_next
;
300 REQUIRE(DNS_ACACHE_VALID(acache
));
301 INSIST(acache
->shutting_down
);
304 * Release the dependency of all entries, and detach them.
306 for (entry
= ISC_LIST_HEAD(acache
->entries
);
308 entry
= entry_next
) {
309 entry_next
= ISC_LIST_NEXT(entry
, link
);
311 ACACHE_LOCK(&acache
->entrylocks
[entry
->locknum
],
312 isc_rwlocktype_write
);
315 * If the cleaner holds this entry, it will be unlinked and
316 * freed in the cleaner later.
318 if (acache
->cleaner
.current_entry
!= entry
)
319 ISC_LIST_UNLINK(acache
->entries
, entry
, link
);
320 unlink_dbentries(acache
, entry
);
321 if (entry
->callback
!= NULL
) {
322 (entry
->callback
)(entry
, &entry
->cbarg
);
323 entry
->callback
= NULL
;
326 ACACHE_UNLOCK(&acache
->entrylocks
[entry
->locknum
],
327 isc_rwlocktype_write
);
329 if (acache
->cleaner
.current_entry
!= entry
)
330 dns_acache_detachentry(&entry
);
335 * The acache must be locked before calling.
338 shutdown_buckets(dns_acache_t
*acache
) {
342 REQUIRE(DNS_ACACHE_VALID(acache
));
343 INSIST(acache
->shutting_down
);
345 for (i
= 0; i
< DBBUCKETS
; i
++) {
346 while ((dbent
= ISC_LIST_HEAD(acache
->dbbucket
[i
])) != NULL
) {
347 INSIST(ISC_LIST_EMPTY(dbent
->originlist
) &&
348 ISC_LIST_EMPTY(dbent
->referlist
));
349 ISC_LIST_UNLINK(acache
->dbbucket
[i
], dbent
, link
);
351 dns_db_detach(&dbent
->db
);
353 isc_mem_put(acache
->mctx
, dbent
, sizeof(*dbent
));
359 INSIST(acache
->dbentries
== 0);
363 shutdown_task(isc_task_t
*task
, isc_event_t
*ev
) {
364 dns_acache_t
*acache
;
369 INSIST(DNS_ACACHE_VALID(acache
));
375 shutdown_entries(acache
);
376 shutdown_buckets(acache
);
378 UNLOCK(&acache
->lock
);
380 dns_acache_detach(&acache
);
383 /* The acache and the entry must be locked before calling. */
385 unlink_dbentries(dns_acache_t
*acache
, dns_acacheentry_t
*ent
) {
389 if (ISC_LINK_LINKED(ent
, olink
)) {
390 INSIST(ent
->origdb
!= NULL
);
392 result
= finddbent(acache
, ent
->origdb
, &dbent
);
393 INSIST(result
== ISC_R_SUCCESS
);
395 ISC_LIST_UNLINK(dbent
->originlist
, ent
, olink
);
397 if (ISC_LINK_LINKED(ent
, rlink
)) {
398 INSIST(ent
->db
!= NULL
);
400 result
= finddbent(acache
, ent
->db
, &dbent
);
401 INSIST(result
== ISC_R_SUCCESS
);
403 ISC_LIST_UNLINK(dbent
->referlist
, ent
, rlink
);
407 /* There must not be a reference to this entry. */
409 destroy_entry(dns_acacheentry_t
*entry
) {
410 dns_acache_t
*acache
;
412 REQUIRE(DNS_ACACHEENTRY_VALID(entry
));
414 acache
= entry
->acache
;
415 REQUIRE(DNS_ACACHE_VALID(acache
));
418 * Since there is no reference to this entry, it is safe to call
419 * clear_entry() here.
421 clear_entry(acache
, entry
);
423 isc_mem_put(acache
->mctx
, entry
, sizeof(*entry
));
425 dns_acache_detach(&acache
);
429 destroy(dns_acache_t
*acache
) {
432 REQUIRE(DNS_ACACHE_VALID(acache
));
436 isc_mem_setwater(acache
->mctx
, NULL
, NULL
, 0, 0);
438 if (acache
->cleaner
.overmem_event
!= NULL
)
439 isc_event_free(&acache
->cleaner
.overmem_event
);
441 if (acache
->cleaner
.resched_event
!= NULL
)
442 isc_event_free(&acache
->cleaner
.resched_event
);
444 if (acache
->task
!= NULL
)
445 isc_task_detach(&acache
->task
);
447 for (i
= 0; i
< DEFAULT_ACACHE_ENTRY_LOCK_COUNT
; i
++)
448 ACACHE_DESTROYLOCK(&acache
->entrylocks
[i
]);
449 isc_mem_put(acache
->mctx
, acache
->entrylocks
,
450 sizeof(*acache
->entrylocks
) *
451 DEFAULT_ACACHE_ENTRY_LOCK_COUNT
);
453 DESTROYLOCK(&acache
->cleaner
.lock
);
455 DESTROYLOCK(&acache
->lock
);
458 isc_mem_putanddetach(&acache
->mctx
, acache
, sizeof(*acache
));
461 static inline isc_result_t
462 finddbent(dns_acache_t
*acache
, dns_db_t
*db
, dbentry_t
**dbentryp
) {
466 REQUIRE(DNS_ACACHE_VALID(acache
));
468 REQUIRE(dbentryp
!= NULL
&& *dbentryp
== NULL
);
471 * The caller must be holding the acache lock.
474 bucket
= isc_hash_calc((const unsigned char *)&db
,
475 sizeof(db
), ISC_TRUE
) % DBBUCKETS
;
477 for (dbentry
= ISC_LIST_HEAD(acache
->dbbucket
[bucket
]);
479 dbentry
= ISC_LIST_NEXT(dbentry
, link
)) {
480 if (dbentry
->db
== db
)
487 return (ISC_R_NOTFOUND
);
489 return (ISC_R_SUCCESS
);
493 clear_entry(dns_acache_t
*acache
, dns_acacheentry_t
*entry
) {
494 REQUIRE(DNS_ACACHE_VALID(acache
));
495 REQUIRE(DNS_ACACHEENTRY_VALID(entry
));
498 * The caller must be holing the entry lock.
501 if (entry
->foundname
) {
502 dns_rdataset_t
*rdataset
, *rdataset_next
;
504 for (rdataset
= ISC_LIST_HEAD(entry
->foundname
->list
);
506 rdataset
= rdataset_next
) {
507 rdataset_next
= ISC_LIST_NEXT(rdataset
, link
);
508 ISC_LIST_UNLINK(entry
->foundname
->list
,
510 dns_rdataset_disassociate(rdataset
);
511 isc_mem_put(acache
->mctx
, rdataset
, sizeof(*rdataset
));
513 if (dns_name_dynamic(entry
->foundname
))
514 dns_name_free(entry
->foundname
, acache
->mctx
);
515 isc_mem_put(acache
->mctx
, entry
->foundname
,
516 sizeof(*entry
->foundname
));
517 entry
->foundname
= NULL
;
520 if (entry
->node
!= NULL
) {
521 INSIST(entry
->db
!= NULL
);
522 dns_db_detachnode(entry
->db
, &entry
->node
);
524 if (entry
->version
!= NULL
) {
525 INSIST(entry
->db
!= NULL
);
526 dns_db_closeversion(entry
->db
, &entry
->version
, ISC_FALSE
);
528 if (entry
->db
!= NULL
)
529 dns_db_detach(&entry
->db
);
530 if (entry
->zone
!= NULL
)
531 dns_zone_detach(&entry
->zone
);
533 if (entry
->origdb
!= NULL
)
534 dns_db_detach(&entry
->origdb
);
538 acache_cleaner_init(dns_acache_t
*acache
, isc_timermgr_t
*timermgr
,
539 acache_cleaner_t
*cleaner
)
543 ATRACE("acache cleaner init");
545 result
= isc_mutex_init(&cleaner
->lock
);
546 if (result
!= ISC_R_SUCCESS
)
549 cleaner
->increment
= DNS_ACACHE_CLEANERINCREMENT
;
550 cleaner
->state
= cleaner_s_idle
;
551 cleaner
->acache
= acache
;
552 cleaner
->overmem
= ISC_FALSE
;
554 cleaner
->cleaning_timer
= NULL
;
555 cleaner
->resched_event
= NULL
;
556 cleaner
->overmem_event
= NULL
;
557 cleaner
->current_entry
= NULL
;
559 if (timermgr
!= NULL
) {
560 cleaner
->acache
->live_cleaners
++;
562 result
= isc_task_onshutdown(acache
->task
,
563 acache_cleaner_shutdown_action
,
565 if (result
!= ISC_R_SUCCESS
) {
566 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
568 "isc_task_onshutdown() failed: %s",
569 dns_result_totext(result
));
573 cleaner
->cleaning_interval
= 0; /* Initially turned off. */
574 isc_stdtime_get(&cleaner
->last_cleanup_time
);
575 result
= isc_timer_create(timermgr
, isc_timertype_inactive
,
578 acache_cleaning_timer_action
,
579 cleaner
, &cleaner
->cleaning_timer
);
580 if (result
!= ISC_R_SUCCESS
) {
581 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
582 "isc_timer_create() failed: %s",
583 dns_result_totext(result
));
584 result
= ISC_R_UNEXPECTED
;
588 cleaner
->resched_event
=
589 isc_event_allocate(acache
->mctx
, cleaner
,
590 DNS_EVENT_ACACHECLEAN
,
591 acache_incremental_cleaning_action
,
592 cleaner
, sizeof(isc_event_t
));
593 if (cleaner
->resched_event
== NULL
) {
594 result
= ISC_R_NOMEMORY
;
598 cleaner
->overmem_event
=
599 isc_event_allocate(acache
->mctx
, cleaner
,
600 DNS_EVENT_ACACHEOVERMEM
,
601 acache_overmem_cleaning_action
,
602 cleaner
, sizeof(isc_event_t
));
603 if (cleaner
->overmem_event
== NULL
) {
604 result
= ISC_R_NOMEMORY
;
609 return (ISC_R_SUCCESS
);
612 if (cleaner
->overmem_event
!= NULL
)
613 isc_event_free(&cleaner
->overmem_event
);
614 if (cleaner
->resched_event
!= NULL
)
615 isc_event_free(&cleaner
->resched_event
);
616 if (cleaner
->cleaning_timer
!= NULL
)
617 isc_timer_detach(&cleaner
->cleaning_timer
);
618 cleaner
->acache
->live_cleaners
--;
619 DESTROYLOCK(&cleaner
->lock
);
625 begin_cleaning(acache_cleaner_t
*cleaner
) {
626 dns_acacheentry_t
*head
;
627 dns_acache_t
*acache
= cleaner
->acache
;
630 * This function does not have to lock the cleaner, since critical
631 * parameters (except current_entry, which is locked by acache lock,)
632 * are only used in a single task context.
635 REQUIRE(CLEANER_IDLE(cleaner
));
636 INSIST(DNS_ACACHE_VALID(acache
));
637 INSIST(cleaner
->current_entry
== NULL
);
639 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
640 DNS_LOGMODULE_ACACHE
, ISC_LOG_DEBUG(1),
641 "begin acache cleaning, mem inuse %lu",
642 (unsigned long)isc_mem_inuse(cleaner
->acache
->mctx
));
646 head
= ISC_LIST_HEAD(acache
->entries
);
648 dns_acache_attachentry(head
, &cleaner
->current_entry
);
650 UNLOCK(&acache
->lock
);
652 if (cleaner
->current_entry
!= NULL
) {
653 cleaner
->ncleaned
= 0;
654 cleaner
->state
= cleaner_s_busy
;
655 isc_task_send(acache
->task
, &cleaner
->resched_event
);
662 end_cleaning(acache_cleaner_t
*cleaner
, isc_event_t
*event
) {
663 dns_acache_t
*acache
= cleaner
->acache
;
665 REQUIRE(CLEANER_BUSY(cleaner
));
666 REQUIRE(event
!= NULL
);
667 REQUIRE(DNS_ACACHEENTRY_VALID(cleaner
->current_entry
));
669 /* No need to lock the cleaner (see begin_cleaning()). */
674 * Even if the cleaner has the last reference to the entry, which means
675 * the entry has been unused, it may still be linked if unlinking the
676 * entry has been delayed due to the reference.
678 if (isc_refcount_current(&cleaner
->current_entry
->references
) == 1) {
679 INSIST(cleaner
->current_entry
->callback
== NULL
);
681 if (ISC_LINK_LINKED(cleaner
->current_entry
, link
)) {
682 ISC_LIST_UNLINK(acache
->entries
,
683 cleaner
->current_entry
, link
);
686 dns_acache_detachentry(&cleaner
->current_entry
);
688 if (cleaner
->overmem
)
689 acache
->stats
.overmem
++;
690 acache
->stats
.cleaned
+= cleaner
->ncleaned
;
691 acache
->stats
.cleaner_runs
++;
693 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
, DNS_LOGMODULE_ACACHE
,
695 "acache %p stats: hits=%d misses=%d queries=%d "
696 "adds=%d deleted=%d "
697 "cleaned=%d cleaner_runs=%d overmem=%d "
698 "overmem_nocreates=%d nomem=%d",
700 acache
->stats
.hits
, acache
->stats
.misses
,
701 acache
->stats
.queries
,
702 acache
->stats
.adds
, acache
->stats
.deleted
,
703 acache
->stats
.cleaned
, acache
->stats
.cleaner_runs
,
704 acache
->stats
.overmem
, acache
->stats
.overmem_nocreates
,
705 acache
->stats
.nomem
);
708 isc_stdtime_get(&cleaner
->last_cleanup_time
);
710 UNLOCK(&acache
->lock
);
712 dns_acache_setcleaninginterval(cleaner
->acache
,
713 cleaner
->cleaning_interval
);
715 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
, DNS_LOGMODULE_ACACHE
,
716 ISC_LOG_DEBUG(1), "end acache cleaning, "
717 "%lu entries cleaned, mem inuse %lu",
719 (unsigned long)isc_mem_inuse(cleaner
->acache
->mctx
));
721 if (cleaner
->overmem
) {
722 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
723 DNS_LOGMODULE_ACACHE
, ISC_LOG_NOTICE
,
724 "acache is still in overmem state "
728 cleaner
->ncleaned
= 0;
729 cleaner
->state
= cleaner_s_idle
;
730 cleaner
->resched_event
= event
;
734 * This is run once for every acache-cleaning-interval as defined
738 acache_cleaning_timer_action(isc_task_t
*task
, isc_event_t
*event
) {
739 acache_cleaner_t
*cleaner
= event
->ev_arg
;
743 INSIST(event
->ev_type
== ISC_TIMEREVENT_TICK
);
745 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
, DNS_LOGMODULE_ACACHE
,
746 ISC_LOG_DEBUG(1), "acache cleaning timer fired, "
747 "cleaner state = %d", cleaner
->state
);
749 if (cleaner
->state
== cleaner_s_idle
)
750 begin_cleaning(cleaner
);
752 isc_event_free(&event
);
755 /* The caller must hold entry lock. */
756 static inline isc_boolean_t
757 entry_stale(acache_cleaner_t
*cleaner
, dns_acacheentry_t
*entry
,
758 isc_stdtime32_t now32
, unsigned int interval
)
761 * If the callback has been canceled, we definitely do not need the
764 if (entry
->callback
== NULL
)
767 if (interval
> cleaner
->cleaning_interval
)
768 interval
= cleaner
->cleaning_interval
;
770 if (entry
->lastused
+ interval
< now32
)
774 * If the acache is in the overmem state, probabilistically decide if
775 * the entry should be purged, based on the time passed from its last
776 * use and the cleaning interval.
778 if (cleaner
->overmem
) {
779 unsigned int passed
=
780 now32
- entry
->lastused
; /* <= interval */
783 if (passed
> interval
/ 2)
785 isc_random_get(&val
);
786 if (passed
> interval
/ 4)
787 return (ISC_TF(val
% 4 == 0));
788 return (ISC_TF(val
% 8 == 0));
795 * Do incremental cleaning.
798 acache_incremental_cleaning_action(isc_task_t
*task
, isc_event_t
*event
) {
799 acache_cleaner_t
*cleaner
= event
->ev_arg
;
800 dns_acache_t
*acache
= cleaner
->acache
;
801 dns_acacheentry_t
*entry
, *next
= NULL
;
803 isc_stdtime32_t now32
, last32
;
805 unsigned int interval
;
807 INSIST(DNS_ACACHE_VALID(acache
));
808 INSIST(task
== acache
->task
);
809 INSIST(event
->ev_type
== DNS_EVENT_ACACHECLEAN
);
811 if (cleaner
->state
== cleaner_s_done
) {
812 cleaner
->state
= cleaner_s_busy
;
813 end_cleaning(cleaner
, event
);
817 INSIST(CLEANER_BUSY(cleaner
));
819 n_entries
= cleaner
->increment
;
821 isc_stdtime_get(&now
);
822 isc_stdtime_convert32(now
, &now32
);
826 entry
= cleaner
->current_entry
;
827 isc_stdtime_convert32(cleaner
->last_cleanup_time
, &last32
);
828 INSIST(now32
> last32
);
829 interval
= now32
- last32
;
831 while (n_entries
-- > 0) {
832 isc_boolean_t is_stale
= ISC_FALSE
;
834 INSIST(entry
!= NULL
);
836 next
= ISC_LIST_NEXT(entry
, link
);
838 ACACHE_LOCK(&acache
->entrylocks
[entry
->locknum
],
839 isc_rwlocktype_write
);
841 is_stale
= entry_stale(cleaner
, entry
, now32
, interval
);
843 ISC_LIST_UNLINK(acache
->entries
, entry
, link
);
844 unlink_dbentries(acache
, entry
);
845 if (entry
->callback
!= NULL
)
846 (entry
->callback
)(entry
, &entry
->cbarg
);
847 entry
->callback
= NULL
;
852 ACACHE_UNLOCK(&acache
->entrylocks
[entry
->locknum
],
853 isc_rwlocktype_write
);
856 dns_acache_detachentry(&entry
);
859 if (cleaner
->overmem
) {
860 entry
= ISC_LIST_HEAD(acache
->entries
);
863 * If we are still in the overmem
864 * state, keep cleaning.
866 isc_log_write(dns_lctx
,
867 DNS_LOGCATEGORY_DATABASE
,
868 DNS_LOGMODULE_ACACHE
,
872 "reset and try again");
877 UNLOCK(&acache
->lock
);
878 end_cleaning(cleaner
, event
);
886 * We have successfully performed a cleaning increment but have
887 * not gone through the entire cache. Remember the entry that will
888 * be the starting point in the next clean-up, and reschedule another
889 * batch. If it fails, just try to continue anyway.
891 INSIST(next
!= NULL
&& next
!= cleaner
->current_entry
);
892 dns_acache_detachentry(&cleaner
->current_entry
);
893 dns_acache_attachentry(next
, &cleaner
->current_entry
);
895 UNLOCK(&acache
->lock
);
897 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
, DNS_LOGMODULE_ACACHE
,
898 ISC_LOG_DEBUG(1), "acache cleaner: checked %d entries, "
899 "mem inuse %lu, sleeping", cleaner
->increment
,
900 (unsigned long)isc_mem_inuse(cleaner
->acache
->mctx
));
902 isc_task_send(task
, &event
);
903 INSIST(CLEANER_BUSY(cleaner
));
909 * This is called when the acache either surpasses its upper limit
910 * or shrinks beyond its lower limit.
913 acache_overmem_cleaning_action(isc_task_t
*task
, isc_event_t
*event
) {
914 acache_cleaner_t
*cleaner
= event
->ev_arg
;
915 isc_boolean_t want_cleaning
= ISC_FALSE
;
919 INSIST(event
->ev_type
== DNS_EVENT_ACACHEOVERMEM
);
920 INSIST(cleaner
->overmem_event
== NULL
);
922 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
, DNS_LOGMODULE_ACACHE
,
923 ISC_LOG_DEBUG(1), "overmem_cleaning_action called, "
924 "overmem = %d, state = %d", cleaner
->overmem
,
927 LOCK(&cleaner
->lock
);
929 if (cleaner
->overmem
) {
930 if (cleaner
->state
== cleaner_s_idle
)
931 want_cleaning
= ISC_TRUE
;
933 if (cleaner
->state
== cleaner_s_busy
)
935 * end_cleaning() can't be called here because
936 * then both cleaner->overmem_event and
937 * cleaner->resched_event will point to this
938 * event. Set the state to done, and then
939 * when the acache_incremental_cleaning_action() event
940 * is posted, it will handle the end_cleaning.
942 cleaner
->state
= cleaner_s_done
;
945 cleaner
->overmem_event
= event
;
947 UNLOCK(&cleaner
->lock
);
950 begin_cleaning(cleaner
);
954 water(void *arg
, int mark
) {
955 dns_acache_t
*acache
= arg
;
956 isc_boolean_t overmem
= ISC_TF(mark
== ISC_MEM_HIWATER
);
958 REQUIRE(DNS_ACACHE_VALID(acache
));
960 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
961 DNS_LOGMODULE_ACACHE
, ISC_LOG_DEBUG(1),
962 "acache memory reaches %s watermark, mem inuse %lu",
963 overmem
? "high" : "low",
964 (unsigned long)isc_mem_inuse(acache
->mctx
));
966 LOCK(&acache
->cleaner
.lock
);
968 if (acache
->cleaner
.overmem
!= overmem
) {
969 acache
->cleaner
.overmem
= overmem
;
971 if (acache
->cleaner
.overmem_event
!= NULL
)
972 isc_task_send(acache
->task
,
973 &acache
->cleaner
.overmem_event
);
974 isc_mem_waterack(acache
->mctx
, mark
);
977 UNLOCK(&acache
->cleaner
.lock
);
981 * The cleaner task is shutting down; do the necessary cleanup.
984 acache_cleaner_shutdown_action(isc_task_t
*task
, isc_event_t
*event
) {
985 dns_acache_t
*acache
= event
->ev_arg
;
986 isc_boolean_t should_free
= ISC_FALSE
;
988 INSIST(task
== acache
->task
);
989 INSIST(event
->ev_type
== ISC_TASKEVENT_SHUTDOWN
);
990 INSIST(DNS_ACACHE_VALID(acache
));
992 ATRACE("acache cleaner shutdown");
994 if (CLEANER_BUSY(&acache
->cleaner
))
995 end_cleaning(&acache
->cleaner
, event
);
997 isc_event_free(&event
);
1001 acache
->live_cleaners
--;
1002 INSIST(acache
->live_cleaners
== 0);
1004 if (isc_refcount_current(&acache
->refs
) == 0) {
1005 INSIST(check_noentry(acache
) == ISC_TRUE
);
1006 should_free
= ISC_TRUE
;
1010 * By detaching the timer in the context of its task,
1011 * we are guaranteed that there will be no further timer
1014 if (acache
->cleaner
.cleaning_timer
!= NULL
)
1015 isc_timer_detach(&acache
->cleaner
.cleaning_timer
);
1017 /* Make sure we don't reschedule anymore. */
1018 (void)isc_task_purge(task
, NULL
, DNS_EVENT_ACACHECLEAN
, NULL
);
1020 UNLOCK(&acache
->lock
);
1031 dns_acache_create(dns_acache_t
**acachep
, isc_mem_t
*mctx
,
1032 isc_taskmgr_t
*taskmgr
, isc_timermgr_t
*timermgr
)
1035 isc_result_t result
;
1036 dns_acache_t
*acache
;
1038 REQUIRE(acachep
!= NULL
&& *acachep
== NULL
);
1039 REQUIRE(mctx
!= NULL
);
1040 REQUIRE(taskmgr
!= NULL
);
1042 acache
= isc_mem_get(mctx
, sizeof(*acache
));
1044 return (ISC_R_NOMEMORY
);
1048 result
= isc_refcount_init(&acache
->refs
, 1);
1049 if (result
!= ISC_R_SUCCESS
) {
1050 isc_mem_put(mctx
, acache
, sizeof(*acache
));
1054 result
= isc_mutex_init(&acache
->lock
);
1055 if (result
!= ISC_R_SUCCESS
) {
1056 isc_refcount_decrement(&acache
->refs
, NULL
);
1057 isc_refcount_destroy(&acache
->refs
);
1058 isc_mem_put(mctx
, acache
, sizeof(*acache
));
1062 acache
->mctx
= NULL
;
1063 isc_mem_attach(mctx
, &acache
->mctx
);
1064 ISC_LIST_INIT(acache
->entries
);
1066 acache
->shutting_down
= ISC_FALSE
;
1068 acache
->task
= NULL
;
1069 acache
->entrylocks
= NULL
;
1071 result
= isc_task_create(taskmgr
, 1, &acache
->task
);
1072 if (result
!= ISC_R_SUCCESS
) {
1073 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
1074 "isc_task_create() failed(): %s",
1075 dns_result_totext(result
));
1076 result
= ISC_R_UNEXPECTED
;
1079 isc_task_setname(acache
->task
, "acachetask", acache
);
1080 ISC_EVENT_INIT(&acache
->cevent
, sizeof(acache
->cevent
), 0, NULL
,
1081 DNS_EVENT_ACACHECONTROL
, shutdown_task
, NULL
,
1083 acache
->cevent_sent
= ISC_FALSE
;
1085 acache
->dbentries
= 0;
1086 for (i
= 0; i
< DBBUCKETS
; i
++)
1087 ISC_LIST_INIT(acache
->dbbucket
[i
]);
1089 acache
->entrylocks
= isc_mem_get(mctx
, sizeof(*acache
->entrylocks
) *
1090 DEFAULT_ACACHE_ENTRY_LOCK_COUNT
);
1091 if (acache
->entrylocks
== NULL
) {
1092 result
= ISC_R_NOMEMORY
;
1095 for (i
= 0; i
< DEFAULT_ACACHE_ENTRY_LOCK_COUNT
; i
++) {
1096 result
= ACACHE_INITLOCK(&acache
->entrylocks
[i
]);
1097 if (result
!= ISC_R_SUCCESS
) {
1099 ACACHE_DESTROYLOCK(&acache
->entrylocks
[i
]);
1100 isc_mem_put(mctx
, acache
->entrylocks
,
1101 sizeof(*acache
->entrylocks
) *
1102 DEFAULT_ACACHE_ENTRY_LOCK_COUNT
);
1103 acache
->entrylocks
= NULL
;
1108 acache
->live_cleaners
= 0;
1109 result
= acache_cleaner_init(acache
, timermgr
, &acache
->cleaner
);
1110 if (result
!= ISC_R_SUCCESS
)
1113 acache
->stats
.cleaner_runs
= 0;
1114 reset_stats(acache
);
1116 acache
->magic
= ACACHE_MAGIC
;
1119 return (ISC_R_SUCCESS
);
1122 if (acache
->task
!= NULL
)
1123 isc_task_detach(&acache
->task
);
1124 DESTROYLOCK(&acache
->lock
);
1125 isc_refcount_decrement(&acache
->refs
, NULL
);
1126 isc_refcount_destroy(&acache
->refs
);
1127 if (acache
->entrylocks
!= NULL
) {
1128 for (i
= 0; i
< DEFAULT_ACACHE_ENTRY_LOCK_COUNT
; i
++)
1129 ACACHE_DESTROYLOCK(&acache
->entrylocks
[i
]);
1130 isc_mem_put(mctx
, acache
->entrylocks
,
1131 sizeof(*acache
->entrylocks
) *
1132 DEFAULT_ACACHE_ENTRY_LOCK_COUNT
);
1134 isc_mem_put(mctx
, acache
, sizeof(*acache
));
1135 isc_mem_detach(&mctx
);
1141 dns_acache_attach(dns_acache_t
*source
, dns_acache_t
**targetp
) {
1142 REQUIRE(DNS_ACACHE_VALID(source
));
1143 REQUIRE(targetp
!= NULL
&& *targetp
== NULL
);
1145 AATRACE(source
, "attach");
1147 isc_refcount_increment(&source
->refs
, NULL
);
1153 dns_acache_countquerymiss(dns_acache_t
*acache
) {
1154 acache
->stats
.misses
++; /* XXXSK danger: unlocked! */
1155 acache
->stats
.queries
++; /* XXXSK danger: unlocked! */
1159 dns_acache_detach(dns_acache_t
**acachep
) {
1160 dns_acache_t
*acache
;
1162 isc_boolean_t should_free
= ISC_FALSE
;
1164 REQUIRE(acachep
!= NULL
&& DNS_ACACHE_VALID(*acachep
));
1169 isc_refcount_decrement(&acache
->refs
, &refs
);
1171 INSIST(check_noentry(acache
) == ISC_TRUE
);
1172 should_free
= ISC_TRUE
;
1178 * If we're exiting and the cleaner task exists, let it free the cache.
1180 if (should_free
&& acache
->live_cleaners
> 0) {
1181 isc_task_shutdown(acache
->task
);
1182 should_free
= ISC_FALSE
;
1190 dns_acache_shutdown(dns_acache_t
*acache
) {
1191 REQUIRE(DNS_ACACHE_VALID(acache
));
1193 LOCK(&acache
->lock
);
1197 if (!acache
->shutting_down
) {
1199 dns_acache_t
*acache_evarg
= NULL
;
1201 INSIST(!acache
->cevent_sent
);
1203 acache
->shutting_down
= ISC_TRUE
;
1205 isc_mem_setwater(acache
->mctx
, NULL
, NULL
, 0, 0);
1208 * Self attach the object in order to prevent it from being
1209 * destroyed while waiting for the event.
1211 dns_acache_attach(acache
, &acache_evarg
);
1212 event
= &acache
->cevent
;
1213 event
->ev_arg
= acache_evarg
;
1214 isc_task_send(acache
->task
, &event
);
1215 acache
->cevent_sent
= ISC_TRUE
;
1218 UNLOCK(&acache
->lock
);
1222 dns_acache_setdb(dns_acache_t
*acache
, dns_db_t
*db
) {
1225 isc_result_t result
= ISC_R_SUCCESS
;
1227 REQUIRE(DNS_ACACHE_VALID(acache
));
1228 REQUIRE(db
!= NULL
);
1232 LOCK(&acache
->lock
);
1235 result
= finddbent(acache
, db
, &dbentry
);
1236 if (result
== ISC_R_SUCCESS
) {
1237 result
= ISC_R_EXISTS
;
1240 result
= ISC_R_SUCCESS
;
1242 dbentry
= isc_mem_get(acache
->mctx
, sizeof(*dbentry
));
1243 if (dbentry
== NULL
) {
1244 result
= ISC_R_NOMEMORY
;
1248 ISC_LINK_INIT(dbentry
, link
);
1249 ISC_LIST_INIT(dbentry
->originlist
);
1250 ISC_LIST_INIT(dbentry
->referlist
);
1253 dns_db_attach(db
, &dbentry
->db
);
1255 bucket
= isc_hash_calc((const unsigned char *)&db
,
1256 sizeof(db
), ISC_TRUE
) % DBBUCKETS
;
1258 ISC_LIST_APPEND(acache
->dbbucket
[bucket
], dbentry
, link
);
1260 acache
->dbentries
++;
1263 UNLOCK(&acache
->lock
);
1269 dns_acache_putdb(dns_acache_t
*acache
, dns_db_t
*db
) {
1271 isc_result_t result
;
1273 dns_acacheentry_t
*entry
;
1275 REQUIRE(DNS_ACACHE_VALID(acache
));
1276 REQUIRE(db
!= NULL
);
1280 LOCK(&acache
->lock
);
1283 result
= finddbent(acache
, db
, &dbentry
);
1284 if (result
!= ISC_R_SUCCESS
) {
1286 * The entry may have not been created due to memory shortage.
1288 UNLOCK(&acache
->lock
);
1289 return (ISC_R_NOTFOUND
);
1293 * Release corresponding cache entries: for each entry, release all
1294 * links the entry has, and then callback to the entry holder (if any).
1295 * If no other external references exist (this can happen if the
1296 * original holder has canceled callback,) destroy it here.
1298 while ((entry
= ISC_LIST_HEAD(dbentry
->originlist
)) != NULL
) {
1299 ACACHE_LOCK(&acache
->entrylocks
[entry
->locknum
],
1300 isc_rwlocktype_write
);
1303 * Releasing olink first would avoid finddbent() in
1304 * unlink_dbentries().
1306 ISC_LIST_UNLINK(dbentry
->originlist
, entry
, olink
);
1307 if (acache
->cleaner
.current_entry
!= entry
)
1308 ISC_LIST_UNLINK(acache
->entries
, entry
, link
);
1309 unlink_dbentries(acache
, entry
);
1311 if (entry
->callback
!= NULL
)
1312 (entry
->callback
)(entry
, &entry
->cbarg
);
1313 entry
->callback
= NULL
;
1315 ACACHE_UNLOCK(&acache
->entrylocks
[entry
->locknum
],
1316 isc_rwlocktype_write
);
1318 if (acache
->cleaner
.current_entry
!= entry
)
1319 dns_acache_detachentry(&entry
);
1321 while ((entry
= ISC_LIST_HEAD(dbentry
->referlist
)) != NULL
) {
1322 ACACHE_LOCK(&acache
->entrylocks
[entry
->locknum
],
1323 isc_rwlocktype_write
);
1325 ISC_LIST_UNLINK(dbentry
->referlist
, entry
, rlink
);
1326 if (acache
->cleaner
.current_entry
!= entry
)
1327 ISC_LIST_UNLINK(acache
->entries
, entry
, link
);
1328 unlink_dbentries(acache
, entry
);
1330 if (entry
->callback
!= NULL
)
1331 (entry
->callback
)(entry
, &entry
->cbarg
);
1332 entry
->callback
= NULL
;
1334 ACACHE_UNLOCK(&acache
->entrylocks
[entry
->locknum
],
1335 isc_rwlocktype_write
);
1337 if (acache
->cleaner
.current_entry
!= entry
)
1338 dns_acache_detachentry(&entry
);
1341 INSIST(ISC_LIST_EMPTY(dbentry
->originlist
) &&
1342 ISC_LIST_EMPTY(dbentry
->referlist
));
1344 bucket
= isc_hash_calc((const unsigned char *)&db
,
1345 sizeof(db
), ISC_TRUE
) % DBBUCKETS
;
1346 ISC_LIST_UNLINK(acache
->dbbucket
[bucket
], dbentry
, link
);
1347 dns_db_detach(&dbentry
->db
);
1349 isc_mem_put(acache
->mctx
, dbentry
, sizeof(*dbentry
));
1351 acache
->dbentries
--;
1353 acache
->stats
.deleted
++;
1355 UNLOCK(&acache
->lock
);
1357 return (ISC_R_SUCCESS
);
1361 dns_acache_createentry(dns_acache_t
*acache
, dns_db_t
*origdb
,
1362 void (*callback
)(dns_acacheentry_t
*, void **),
1363 void *cbarg
, dns_acacheentry_t
**entryp
)
1365 dns_acacheentry_t
*newentry
;
1366 isc_result_t result
;
1369 REQUIRE(DNS_ACACHE_VALID(acache
));
1370 REQUIRE(entryp
!= NULL
&& *entryp
== NULL
);
1371 REQUIRE(origdb
!= NULL
);
1374 * Should we exceed our memory limit for some reason (for
1375 * example, if the cleaner does not run aggressively enough),
1376 * then we will not create additional entries.
1378 * XXXSK: It might be better to lock the acache->cleaner->lock,
1379 * but locking may be an expensive bottleneck. If we misread
1380 * the value, we will occasionally refuse to create a few
1381 * cache entries, or create a few that we should not. I do not
1382 * expect this to happen often, and it will not have very bad
1383 * effects when it does. So no lock for now.
1385 if (acache
->cleaner
.overmem
) {
1386 acache
->stats
.overmem_nocreates
++; /* XXXSK danger: unlocked! */
1387 return (ISC_R_NORESOURCES
);
1390 newentry
= isc_mem_get(acache
->mctx
, sizeof(*newentry
));
1391 if (newentry
== NULL
) {
1392 acache
->stats
.nomem
++; /* XXXMLG danger: unlocked! */
1393 return (ISC_R_NOMEMORY
);
1397 newentry
->locknum
= r
% DEFAULT_ACACHE_ENTRY_LOCK_COUNT
;
1399 result
= isc_refcount_init(&newentry
->references
, 1);
1400 if (result
!= ISC_R_SUCCESS
) {
1401 isc_mem_put(acache
->mctx
, newentry
, sizeof(*newentry
));
1405 ISC_LINK_INIT(newentry
, link
);
1406 ISC_LINK_INIT(newentry
, olink
);
1407 ISC_LINK_INIT(newentry
, rlink
);
1409 newentry
->acache
= NULL
;
1410 dns_acache_attach(acache
, &newentry
->acache
);
1412 newentry
->zone
= NULL
;
1413 newentry
->db
= NULL
;
1414 newentry
->version
= NULL
;
1415 newentry
->node
= NULL
;
1416 newentry
->foundname
= NULL
;
1418 newentry
->callback
= callback
;
1419 newentry
->cbarg
= cbarg
;
1420 newentry
->origdb
= NULL
;
1421 dns_db_attach(origdb
, &newentry
->origdb
);
1423 isc_stdtime_get(&newentry
->lastused
);
1425 newentry
->magic
= ACACHEENTRY_MAGIC
;
1429 return (ISC_R_SUCCESS
);
1433 dns_acache_getentry(dns_acacheentry_t
*entry
, dns_zone_t
**zonep
,
1434 dns_db_t
**dbp
, dns_dbversion_t
**versionp
,
1435 dns_dbnode_t
**nodep
, dns_name_t
*fname
,
1436 dns_message_t
*msg
, isc_stdtime_t now
)
1438 isc_result_t result
= ISC_R_SUCCESS
;
1439 dns_rdataset_t
*erdataset
;
1440 isc_stdtime32_t now32
;
1441 dns_acache_t
*acache
;
1444 REQUIRE(DNS_ACACHEENTRY_VALID(entry
));
1445 REQUIRE(zonep
== NULL
|| *zonep
== NULL
);
1446 REQUIRE(dbp
!= NULL
&& *dbp
== NULL
);
1447 REQUIRE(versionp
!= NULL
&& *versionp
== NULL
);
1448 REQUIRE(nodep
!= NULL
&& *nodep
== NULL
);
1449 REQUIRE(fname
!= NULL
);
1450 REQUIRE(msg
!= NULL
);
1451 acache
= entry
->acache
;
1452 REQUIRE(DNS_ACACHE_VALID(acache
));
1454 locknum
= entry
->locknum
;
1455 ACACHE_LOCK(&acache
->entrylocks
[locknum
], isc_rwlocktype_read
);
1457 isc_stdtime_convert32(now
, &now32
);
1458 acache_storetime(entry
, now32
);
1460 if (entry
->zone
!= NULL
&& zonep
!= NULL
)
1461 dns_zone_attach(entry
->zone
, zonep
);
1463 if (entry
->db
== NULL
) {
1467 dns_db_attach(entry
->db
, dbp
);
1468 dns_db_attachversion(entry
->db
, entry
->version
, versionp
);
1470 if (entry
->node
== NULL
)
1473 dns_db_attachnode(entry
->db
, entry
->node
, nodep
);
1475 INSIST(entry
->foundname
!= NULL
);
1476 dns_name_copy(entry
->foundname
, fname
, NULL
);
1477 for (erdataset
= ISC_LIST_HEAD(entry
->foundname
->list
);
1479 erdataset
= ISC_LIST_NEXT(erdataset
, link
)) {
1480 dns_rdataset_t
*ardataset
;
1483 result
= dns_message_gettemprdataset(msg
, &ardataset
);
1484 if (result
!= ISC_R_SUCCESS
) {
1485 ACACHE_UNLOCK(&acache
->entrylocks
[locknum
],
1486 isc_rwlocktype_read
);
1491 * XXXJT: if we simply clone the rdataset, we'll get
1492 * lost wrt cyclic ordering. We'll need an additional
1493 * trick to get the latest counter from the original
1496 dns_rdataset_init(ardataset
);
1497 dns_rdataset_clone(erdataset
, ardataset
);
1498 ISC_LIST_APPEND(fname
->list
, ardataset
, link
);
1502 entry
->acache
->stats
.hits
++; /* XXXMLG danger: unlocked! */
1503 entry
->acache
->stats
.queries
++;
1505 ACACHE_UNLOCK(&acache
->entrylocks
[locknum
], isc_rwlocktype_read
);
1510 while ((erdataset
= ISC_LIST_HEAD(fname
->list
)) != NULL
) {
1511 ISC_LIST_UNLINK(fname
->list
, erdataset
, link
);
1512 dns_rdataset_disassociate(erdataset
);
1513 dns_message_puttemprdataset(msg
, &erdataset
);
1516 dns_db_detachnode(*dbp
, nodep
);
1517 if (*versionp
!= NULL
)
1518 dns_db_closeversion(*dbp
, versionp
, ISC_FALSE
);
1521 if (zonep
!= NULL
&& *zonep
!= NULL
)
1522 dns_zone_detach(zonep
);
1528 dns_acache_setentry(dns_acache_t
*acache
, dns_acacheentry_t
*entry
,
1529 dns_zone_t
*zone
, dns_db_t
*db
, dns_dbversion_t
*version
,
1530 dns_dbnode_t
*node
, dns_name_t
*fname
)
1532 isc_result_t result
;
1534 dbentry_t
*rdbent
= NULL
;
1535 isc_boolean_t close_version
= ISC_FALSE
;
1536 dns_acacheentry_t
*dummy_entry
= NULL
;
1538 REQUIRE(DNS_ACACHE_VALID(acache
));
1539 REQUIRE(DNS_ACACHEENTRY_VALID(entry
));
1541 LOCK(&acache
->lock
); /* XXX: need to lock it here for ordering */
1542 ACACHE_LOCK(&acache
->entrylocks
[entry
->locknum
], isc_rwlocktype_write
);
1546 dns_zone_attach(zone
, &entry
->zone
);
1549 dns_db_attach(db
, &entry
->db
);
1551 * Set DB version. If the version is not given by the caller,
1552 * which is the case for glue or cache DBs, use the current version.
1554 if (version
== NULL
) {
1556 dns_db_currentversion(db
, &version
);
1557 close_version
= ISC_TRUE
;
1560 if (version
!= NULL
) {
1562 dns_db_attachversion(db
, version
, &entry
->version
);
1565 dns_db_closeversion(db
, &version
, ISC_FALSE
);
1569 dns_db_attachnode(db
, node
, &entry
->node
);
1573 * Set list of the corresponding rdatasets, if given.
1574 * To minimize the overhead and memory consumption, we'll do this for
1575 * positive cache only, in which case the DB node is non NULL.
1576 * We do not want to cache incomplete information, so give up the
1577 * entire entry when a memory shortage happen during the process.
1580 dns_rdataset_t
*ardataset
, *crdataset
;
1582 entry
->foundname
= isc_mem_get(acache
->mctx
,
1583 sizeof(*entry
->foundname
));
1585 if (entry
->foundname
== NULL
) {
1586 result
= ISC_R_NOMEMORY
;
1589 dns_name_init(entry
->foundname
, NULL
);
1590 result
= dns_name_dup(fname
, acache
->mctx
,
1592 if (result
!= ISC_R_SUCCESS
)
1595 for (ardataset
= ISC_LIST_HEAD(fname
->list
);
1597 ardataset
= ISC_LIST_NEXT(ardataset
, link
)) {
1598 crdataset
= isc_mem_get(acache
->mctx
,
1599 sizeof(*crdataset
));
1600 if (crdataset
== NULL
) {
1601 result
= ISC_R_NOMEMORY
;
1605 dns_rdataset_init(crdataset
);
1606 dns_rdataset_clone(ardataset
, crdataset
);
1607 ISC_LIST_APPEND(entry
->foundname
->list
, crdataset
,
1613 result
= finddbent(acache
, entry
->origdb
, &odbent
);
1614 if (result
!= ISC_R_SUCCESS
)
1618 result
= finddbent(acache
, db
, &rdbent
);
1619 if (result
!= ISC_R_SUCCESS
)
1623 ISC_LIST_APPEND(acache
->entries
, entry
, link
);
1624 ISC_LIST_APPEND(odbent
->originlist
, entry
, olink
);
1626 ISC_LIST_APPEND(rdbent
->referlist
, entry
, rlink
);
1629 * The additional cache needs an implicit reference to entries in its
1632 dns_acache_attachentry(entry
, &dummy_entry
);
1634 ACACHE_UNLOCK(&acache
->entrylocks
[entry
->locknum
],
1635 isc_rwlocktype_write
);
1637 acache
->stats
.adds
++;
1638 UNLOCK(&acache
->lock
);
1640 return (ISC_R_SUCCESS
);
1643 clear_entry(acache
, entry
);
1645 ACACHE_UNLOCK(&acache
->entrylocks
[entry
->locknum
],
1646 isc_rwlocktype_write
);
1647 UNLOCK(&acache
->lock
);
1653 dns_acache_cancelentry(dns_acacheentry_t
*entry
) {
1654 dns_acache_t
*acache
= entry
->acache
;
1656 REQUIRE(DNS_ACACHEENTRY_VALID(entry
));
1657 INSIST(DNS_ACACHE_VALID(acache
));
1659 LOCK(&acache
->lock
);
1660 ACACHE_LOCK(&acache
->entrylocks
[entry
->locknum
], isc_rwlocktype_write
);
1663 * Release dependencies stored in this entry as much as possible.
1664 * The main link cannot be released, since the acache object has
1665 * a reference to this entry; the empty entry will be released in
1666 * the next cleaning action.
1668 unlink_dbentries(acache
, entry
);
1669 clear_entry(entry
->acache
, entry
);
1671 entry
->callback
= NULL
;
1672 entry
->cbarg
= NULL
;
1674 ACACHE_UNLOCK(&acache
->entrylocks
[entry
->locknum
],
1675 isc_rwlocktype_write
);
1676 UNLOCK(&acache
->lock
);
1680 dns_acache_attachentry(dns_acacheentry_t
*source
,
1681 dns_acacheentry_t
**targetp
)
1683 REQUIRE(DNS_ACACHEENTRY_VALID(source
));
1684 REQUIRE(targetp
!= NULL
&& *targetp
== NULL
);
1686 isc_refcount_increment(&source
->references
, NULL
);
1692 dns_acache_detachentry(dns_acacheentry_t
**entryp
) {
1693 dns_acacheentry_t
*entry
;
1696 REQUIRE(entryp
!= NULL
&& DNS_ACACHEENTRY_VALID(*entryp
));
1699 isc_refcount_decrement(&entry
->references
, &refs
);
1702 * If there are no references to the entry, the entry must have been
1703 * unlinked and can be destroyed safely.
1706 INSIST(!ISC_LINK_LINKED(entry
, link
));
1707 (*entryp
)->acache
->stats
.deleted
++;
1708 destroy_entry(entry
);
1715 dns_acache_setcleaninginterval(dns_acache_t
*acache
, unsigned int t
) {
1716 isc_interval_t interval
;
1717 isc_result_t result
;
1719 REQUIRE(DNS_ACACHE_VALID(acache
));
1721 ATRACE("dns_acache_setcleaninginterval");
1723 LOCK(&acache
->lock
);
1726 * It may be the case that the acache has already shut down.
1727 * If so, it has no timer. (Not sure if this can really happen.)
1729 if (acache
->cleaner
.cleaning_timer
== NULL
)
1732 acache
->cleaner
.cleaning_interval
= t
;
1735 result
= isc_timer_reset(acache
->cleaner
.cleaning_timer
,
1736 isc_timertype_inactive
,
1737 NULL
, NULL
, ISC_TRUE
);
1739 isc_interval_set(&interval
, acache
->cleaner
.cleaning_interval
,
1741 result
= isc_timer_reset(acache
->cleaner
.cleaning_timer
,
1742 isc_timertype_ticker
,
1743 NULL
, &interval
, ISC_FALSE
);
1745 if (result
!= ISC_R_SUCCESS
)
1746 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
1747 DNS_LOGMODULE_ACACHE
, ISC_LOG_WARNING
,
1748 "could not set acache cleaning interval: %s",
1749 isc_result_totext(result
));
1751 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
1752 DNS_LOGMODULE_ACACHE
, ISC_LOG_NOTICE
,
1753 "acache %p cleaning interval set to %d.",
1757 UNLOCK(&acache
->lock
);
1761 * This function was derived from cache.c:dns_cache_setcachesize(). See the
1762 * function for more details about the logic.
1765 dns_acache_setcachesize(dns_acache_t
*acache
, isc_uint32_t size
) {
1766 isc_uint32_t lowater
;
1767 isc_uint32_t hiwater
;
1769 REQUIRE(DNS_ACACHE_VALID(acache
));
1771 if (size
!= 0 && size
< DNS_ACACHE_MINSIZE
)
1772 size
= DNS_ACACHE_MINSIZE
;
1774 hiwater
= size
- (size
>> 3);
1775 lowater
= size
- (size
>> 2);
1777 if (size
== 0 || hiwater
== 0 || lowater
== 0)
1778 isc_mem_setwater(acache
->mctx
, water
, acache
, 0, 0);
1780 isc_mem_setwater(acache
->mctx
, water
, acache
,