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 */
23 #include <isc/atomic.h>
24 #include <isc/event.h>
26 #include <isc/magic.h>
28 #include <isc/mutex.h>
29 #include <isc/random.h>
30 #include <isc/refcount.h>
31 #include <isc/rwlock.h>
34 #include <isc/timer.h>
36 #include <dns/acache.h>
38 #include <dns/events.h>
40 #include <dns/message.h>
42 #include <dns/rdataset.h>
43 #include <dns/result.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)
55 #define ATRACE(m) isc_log_write(dns_lctx, \
56 DNS_LOGCATEGORY_DATABASE, \
57 DNS_LOGMODULE_ACACHE, \
59 "acache %p: %s", acache, (m))
60 #define AATRACE(a,m) isc_log_write(dns_lctx, \
61 DNS_LOGCATEGORY_DATABASE, \
62 DNS_LOGMODULE_ACACHE, \
64 "acache %p: %s", (a), (m))
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
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
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)))
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))
103 /* Locked by acache lock */
104 typedef struct dbentry
{
105 ISC_LINK(struct dbentry
) link
;
108 ISC_LIST(dns_acacheentry_t
) originlist
;
109 ISC_LIST(dns_acacheentry_t
) referlist
;
112 typedef ISC_LIST(dbentry_t
) dbentrylist_t
;
114 typedef struct acache_cleaner acache_cleaner_t
;
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. */
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
{
133 * Locks overmem_event, overmem. (See cache.c)
136 dns_acache_t
*acache
;
137 unsigned int cleaning_interval
; /* The cleaning-interval
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
162 struct dns_acachestats
{
164 unsigned int queries
;
167 unsigned int deleted
;
168 unsigned int cleaned
;
169 unsigned int cleaner_runs
;
170 unsigned int overmem
;
171 unsigned int overmem_nocreates
;
176 * The actual acache object.
185 #ifdef ACACHE_USE_RWLOCK
186 isc_rwlock_t
*entrylocks
;
188 isc_mutex_t
*entrylocks
;
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
;
203 isc_boolean_t cevent_sent
;
205 dns_acachestats_t stats
;
208 struct dns_acacheentry
{
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 */
225 dns_zone_t
*zone
; /* zone this entry
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
231 dns_name_t
*foundname
; /* corresponding DNS name
234 /* Callback function and its argument */
235 void (*callback
)(dns_acacheentry_t
*, void **);
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
,
261 static void acache_overmem_cleaning_action(isc_task_t
*task
,
263 static void acache_cleaner_shutdown_action(isc_task_t
*task
,
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
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) {
296 * The acache must be locked before calling.
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
);
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.
340 shutdown_buckets(dns_acache_t
*acache
) {
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
));
361 INSIST(acache
->dbentries
== 0);
365 shutdown_task(isc_task_t
*task
, isc_event_t
*ev
) {
366 dns_acache_t
*acache
;
371 INSIST(DNS_ACACHE_VALID(acache
));
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. */
387 unlink_dbentries(dns_acache_t
*acache
, dns_acacheentry_t
*ent
) {
391 if (ISC_LINK_LINKED(ent
, olink
)) {
392 INSIST(ent
->origdb
!= 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
);
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. */
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
);
431 destroy(dns_acache_t
*acache
) {
434 REQUIRE(DNS_ACACHE_VALID(acache
));
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
);
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
) {
468 REQUIRE(DNS_ACACHE_VALID(acache
));
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
]);
481 dbentry
= ISC_LIST_NEXT(dbentry
, link
)) {
482 if (dbentry
->db
== db
)
489 return (ISC_R_NOTFOUND
);
491 return (ISC_R_SUCCESS
);
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
);
508 rdataset
= rdataset_next
) {
509 rdataset_next
= ISC_LIST_NEXT(rdataset
, link
);
510 ISC_LIST_UNLINK(entry
->foundname
->list
,
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
);
540 acache_cleaner_init(dns_acache_t
*acache
, isc_timermgr_t
*timermgr
,
541 acache_cleaner_t
*cleaner
)
545 ATRACE("acache cleaner init");
547 result
= isc_mutex_init(&cleaner
->lock
);
548 if (result
!= ISC_R_SUCCESS
)
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
,
567 if (result
!= ISC_R_SUCCESS
) {
568 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
570 "isc_task_onshutdown() failed: %s",
571 dns_result_totext(result
));
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
,
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
;
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
;
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
;
611 return (ISC_R_SUCCESS
);
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
);
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
));
648 head
= ISC_LIST_HEAD(acache
->entries
);
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
);
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()). */
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
,
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",
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
);
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",
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 "
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
740 acache_cleaning_timer_action(isc_task_t
*task
, isc_event_t
*event
) {
741 acache_cleaner_t
*cleaner
= event
->ev_arg
;
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
766 if (entry
->callback
== NULL
)
769 if (interval
> cleaner
->cleaning_interval
)
770 interval
= cleaner
->cleaning_interval
;
772 if (entry
->lastused
+ interval
< now32
)
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 */
785 if (passed
> interval
/ 2)
787 isc_random_get(&val
);
788 if (passed
> interval
/ 4)
789 return (ISC_TF(val
% 4 == 0));
790 return (ISC_TF(val
% 8 == 0));
797 * Do incremental cleaning.
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
;
805 isc_stdtime32_t now32
, last32
;
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
);
819 INSIST(CLEANER_BUSY(cleaner
));
821 n_entries
= cleaner
->increment
;
823 isc_stdtime_get(&now
);
824 isc_stdtime_convert32(now
, &now32
);
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
);
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
;
854 ACACHE_UNLOCK(&acache
->entrylocks
[entry
->locknum
],
855 isc_rwlocktype_write
);
858 dns_acache_detachentry(&entry
);
861 if (cleaner
->overmem
) {
862 entry
= ISC_LIST_HEAD(acache
->entries
);
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
,
874 "reset and try again");
879 UNLOCK(&acache
->lock
);
880 end_cleaning(cleaner
, event
);
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
));
911 * This is called when the acache either surpasses its upper limit
912 * or shrinks beyond its lower limit.
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
;
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
,
929 LOCK(&cleaner
->lock
);
931 if (cleaner
->overmem
) {
932 if (cleaner
->state
== cleaner_s_idle
)
933 want_cleaning
= ISC_TRUE
;
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
);
952 begin_cleaning(cleaner
);
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.
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
);
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
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
);
1033 dns_acache_create(dns_acache_t
**acachep
, isc_mem_t
*mctx
,
1034 isc_taskmgr_t
*taskmgr
, isc_timermgr_t
*timermgr
)
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
));
1046 return (ISC_R_NOMEMORY
);
1050 result
= isc_refcount_init(&acache
->refs
, 1);
1051 if (result
!= ISC_R_SUCCESS
) {
1052 isc_mem_put(mctx
, acache
, sizeof(*acache
));
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
));
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
;
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
,
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
;
1097 for (i
= 0; i
< DEFAULT_ACACHE_ENTRY_LOCK_COUNT
; i
++) {
1098 result
= ACACHE_INITLOCK(&acache
->entrylocks
[i
]);
1099 if (result
!= ISC_R_SUCCESS
) {
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
;
1110 acache
->live_cleaners
= 0;
1111 result
= acache_cleaner_init(acache
, timermgr
, &acache
->cleaner
);
1112 if (result
!= ISC_R_SUCCESS
)
1115 acache
->stats
.cleaner_runs
= 0;
1116 reset_stats(acache
);
1118 acache
->magic
= ACACHE_MAGIC
;
1121 return (ISC_R_SUCCESS
);
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
);
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
);
1155 dns_acache_countquerymiss(dns_acache_t
*acache
) {
1156 acache
->stats
.misses
++; /* XXXSK danger: unlocked! */
1157 acache
->stats
.queries
++; /* XXXSK danger: unlocked! */
1161 dns_acache_detach(dns_acache_t
**acachep
) {
1162 dns_acache_t
*acache
;
1164 isc_boolean_t should_free
= ISC_FALSE
;
1166 REQUIRE(acachep
!= NULL
&& DNS_ACACHE_VALID(*acachep
));
1171 isc_refcount_decrement(&acache
->refs
, &refs
);
1173 INSIST(check_noentry(acache
) == ISC_TRUE
);
1174 should_free
= ISC_TRUE
;
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
;
1192 dns_acache_shutdown(dns_acache_t
*acache
) {
1193 REQUIRE(DNS_ACACHE_VALID(acache
));
1195 LOCK(&acache
->lock
);
1199 if (!acache
->shutting_down
) {
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
);
1224 dns_acache_setdb(dns_acache_t
*acache
, dns_db_t
*db
) {
1227 isc_result_t result
= ISC_R_SUCCESS
;
1229 REQUIRE(DNS_ACACHE_VALID(acache
));
1230 REQUIRE(db
!= NULL
);
1234 LOCK(&acache
->lock
);
1237 result
= finddbent(acache
, db
, &dbentry
);
1238 if (result
== ISC_R_SUCCESS
) {
1239 result
= ISC_R_EXISTS
;
1242 result
= ISC_R_SUCCESS
;
1244 dbentry
= isc_mem_get(acache
->mctx
, sizeof(*dbentry
));
1245 if (dbentry
== NULL
) {
1246 result
= ISC_R_NOMEMORY
;
1250 ISC_LINK_INIT(dbentry
, link
);
1251 ISC_LIST_INIT(dbentry
->originlist
);
1252 ISC_LIST_INIT(dbentry
->referlist
);
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
++;
1265 UNLOCK(&acache
->lock
);
1271 dns_acache_putdb(dns_acache_t
*acache
, dns_db_t
*db
) {
1273 isc_result_t result
;
1275 dns_acacheentry_t
*entry
;
1277 REQUIRE(DNS_ACACHE_VALID(acache
));
1278 REQUIRE(db
!= NULL
);
1282 LOCK(&acache
->lock
);
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
);
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
;
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
);
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
));
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
;
1431 return (ISC_R_SUCCESS
);
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
;
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
) {
1469 dns_db_attach(entry
->db
, dbp
);
1470 dns_db_attachversion(entry
->db
, entry
->version
, versionp
);
1472 if (entry
->node
== NULL
)
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
);
1481 erdataset
= ISC_LIST_NEXT(erdataset
, link
)) {
1482 dns_rdataset_t
*ardataset
;
1485 result
= dns_message_gettemprdataset(msg
, &ardataset
);
1486 if (result
!= ISC_R_SUCCESS
) {
1487 ACACHE_UNLOCK(&acache
->entrylocks
[locknum
],
1488 isc_rwlocktype_read
);
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
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
);
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
);
1518 dns_db_detachnode(*dbp
, nodep
);
1519 if (*versionp
!= NULL
)
1520 dns_db_closeversion(*dbp
, versionp
, ISC_FALSE
);
1523 if (zonep
!= NULL
&& *zonep
!= NULL
)
1524 dns_zone_detach(zonep
);
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
;
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
);
1548 dns_zone_attach(zone
, &entry
->zone
);
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
) {
1558 dns_db_currentversion(db
, &version
);
1559 close_version
= ISC_TRUE
;
1562 if (version
!= NULL
) {
1564 dns_db_attachversion(db
, version
, &entry
->version
);
1567 dns_db_closeversion(db
, &version
, ISC_FALSE
);
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.
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
;
1591 dns_name_init(entry
->foundname
, NULL
);
1592 result
= dns_name_dup(fname
, acache
->mctx
,
1594 if (result
!= ISC_R_SUCCESS
)
1597 for (ardataset
= ISC_LIST_HEAD(fname
->list
);
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
;
1607 dns_rdataset_init(crdataset
);
1608 dns_rdataset_clone(ardataset
, crdataset
);
1609 ISC_LIST_APPEND(entry
->foundname
->list
, crdataset
,
1615 result
= finddbent(acache
, entry
->origdb
, &odbent
);
1616 if (result
!= ISC_R_SUCCESS
)
1620 result
= finddbent(acache
, db
, &rdbent
);
1621 if (result
!= ISC_R_SUCCESS
)
1625 ISC_LIST_APPEND(acache
->entries
, entry
, link
);
1626 ISC_LIST_APPEND(odbent
->originlist
, entry
, olink
);
1628 ISC_LIST_APPEND(rdbent
->referlist
, entry
, rlink
);
1631 * The additional cache needs an implicit reference to entries in its
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
);
1645 clear_entry(acache
, entry
);
1647 ACACHE_UNLOCK(&acache
->entrylocks
[entry
->locknum
],
1648 isc_rwlocktype_write
);
1649 UNLOCK(&acache
->lock
);
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
);
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
);
1694 dns_acache_detachentry(dns_acacheentry_t
**entryp
) {
1695 dns_acacheentry_t
*entry
;
1698 REQUIRE(entryp
!= NULL
&& DNS_ACACHEENTRY_VALID(*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.
1708 INSIST(!ISC_LINK_LINKED(entry
, link
));
1709 (*entryp
)->acache
->stats
.deleted
++;
1710 destroy_entry(entry
);
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
)
1734 acache
->cleaner
.cleaning_interval
= t
;
1737 result
= isc_timer_reset(acache
->cleaner
.cleaning_timer
,
1738 isc_timertype_inactive
,
1739 NULL
, NULL
, ISC_TRUE
);
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
));
1753 isc_log_write(dns_lctx
, DNS_LOGCATEGORY_DATABASE
,
1754 DNS_LOGMODULE_ACACHE
, ISC_LOG_NOTICE
,
1755 "acache %p cleaning interval set to %d.",
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.
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);
1782 isc_mem_setwater(acache
->mctx
, water
, acache
,