BIND: update vendor tree to 9.5.2-P2
[dragonfly.git] / contrib / bind / lib / dns / acache.c
CommitLineData
bbbf71a3
JL
1/*
2 * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
3 *
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.
7 *
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.
15 */
16
17/* $Id: acache.c,v 1.20.128.2 2008/02/07 23:46:25 tbox Exp $ */
18
19#include <config.h>
20
21#include <isc/atomic.h>
22#include <isc/event.h>
23#include <isc/hash.h>
24#include <isc/magic.h>
25#include <isc/mem.h>
26#include <isc/mutex.h>
27#include <isc/random.h>
28#include <isc/refcount.h>
29#include <isc/rwlock.h>
30#include <isc/task.h>
31#include <isc/time.h>
32#include <isc/timer.h>
33
34#include <dns/acache.h>
35#include <dns/db.h>
36#include <dns/events.h>
37#include <dns/log.h>
38#include <dns/message.h>
39#include <dns/name.h>
40#include <dns/rdataset.h>
41#include <dns/result.h>
42#include <dns/zone.h>
43
44#define ACACHE_MAGIC ISC_MAGIC('A', 'C', 'H', 'E')
45#define DNS_ACACHE_VALID(acache) ISC_MAGIC_VALID(acache, ACACHE_MAGIC)
46
47#define ACACHEENTRY_MAGIC ISC_MAGIC('A', 'C', 'E', 'T')
48#define DNS_ACACHEENTRY_VALID(entry) ISC_MAGIC_VALID(entry, ACACHEENTRY_MAGIC)
49
50#define DBBUCKETS 67
51
52#if 0
53#define ATRACE(m) isc_log_write(dns_lctx, \
54 DNS_LOGCATEGORY_DATABASE, \
55 DNS_LOGMODULE_ACACHE, \
56 ISC_LOG_DEBUG(3), \
57 "acache %p: %s", acache, (m))
58#define AATRACE(a,m) isc_log_write(dns_lctx, \
59 DNS_LOGCATEGORY_DATABASE, \
60 DNS_LOGMODULE_ACACHE, \
61 ISC_LOG_DEBUG(3), \
62 "acache %p: %s", (a), (m))
63#else
64#define ATRACE(m)
65#define AATRACE(a, m)
66#endif
67
68/*
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
73 * constants here.)
74 */
75#define DNS_ACACHE_MINSIZE 2097152 /* Bytes. 2097152 = 2 MB */
76#define DNS_ACACHE_CLEANERINCREMENT 1000 /* Number of entries. */
77
78#define DEFAULT_ACACHE_ENTRY_LOCK_COUNT 1009 /*%< Should be prime. */
79
80#if defined(ISC_RWLOCK_USEATOMIC) && defined(ISC_PLATFORM_HAVEATOMICSTORE)
81#define ACACHE_USE_RWLOCK 1
82#endif
83
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))
89
90#define acache_storetime(entry, t) \
91 (isc_atomic_store((isc_int32_t *)&(entry)->lastused, (t)))
92#else
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)
97
98#define acache_storetime(entry, t) ((entry)->lastused = (t))
99#endif
100
101/* Locked by acache lock */
102typedef struct dbentry {
103 ISC_LINK(struct dbentry) link;
104
105 dns_db_t *db;
106 ISC_LIST(dns_acacheentry_t) originlist;
107 ISC_LIST(dns_acacheentry_t) referlist;
108} dbentry_t;
109
110typedef ISC_LIST(dbentry_t) dbentrylist_t;
111
112typedef struct acache_cleaner acache_cleaner_t;
113
114typedef enum {
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. */
118} cleaner_state_t;
119
120/*
121 * Convenience macros for comprehensive assertion checking.
122 */
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)
127
128struct acache_cleaner {
129 isc_mutex_t lock;
130 /*
131 * Locks overmem_event, overmem. (See cache.c)
132 */
133
134 dns_acache_t *acache;
135 unsigned int cleaning_interval; /* The cleaning-interval
136 from named.conf,
137 in seconds. */
138
139 isc_stdtime_t last_cleanup_time; /* The time when the last
140 cleanup task completed */
141
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;
146
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 */
152
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
157 state. */
158};
159
160struct dns_acachestats {
161 unsigned int hits;
162 unsigned int queries;
163 unsigned int misses;
164 unsigned int adds;
165 unsigned int deleted;
166 unsigned int cleaned;
167 unsigned int cleaner_runs;
168 unsigned int overmem;
169 unsigned int overmem_nocreates;
170 unsigned int nomem;
171};
172
173/*
174 * The actual acache object.
175 */
176
177struct dns_acache {
178 unsigned int magic;
179
180 isc_mem_t *mctx;
181 isc_refcount_t refs;
182
183#ifdef ACACHE_USE_RWLOCK
184 isc_rwlock_t *entrylocks;
185#else
186 isc_mutex_t *entrylocks;
187#endif
188
189 isc_mutex_t lock;
190
191 int live_cleaners;
192 acache_cleaner_t cleaner;
193 ISC_LIST(dns_acacheentry_t) entries;
194 unsigned int dbentries;
195 dbentrylist_t dbbucket[DBBUCKETS];
196
197 isc_boolean_t shutting_down;
198
199 isc_task_t *task;
200 isc_event_t cevent;
201 isc_boolean_t cevent_sent;
202
203 dns_acachestats_t stats;
204};
205
206struct dns_acacheentry {
207 unsigned int magic;
208
209 unsigned int locknum;
210 isc_refcount_t references;
211
212 dns_acache_t *acache;
213
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;
218
219 dns_db_t *origdb; /* reference to the DB
220 holding this entry */
221
222 /* Cache data */
223 dns_zone_t *zone; /* zone this entry
224 belongs to */
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
228 belongs to */
229 dns_name_t *foundname; /* corresponding DNS name
230 and rdataset */
231
232 /* Callback function and its argument */
233 void (*callback)(dns_acacheentry_t *, void **);
234 void *cbarg;
235
236 /* Timestamp of the last time this entry is referred to */
237 isc_stdtime32_t lastused;
238};
239
240/*
241 * Internal functions (and prototypes).
242 */
243static inline isc_boolean_t check_noentry(dns_acache_t *acache);
244static void destroy(dns_acache_t *acache);
245static void shutdown_entries(dns_acache_t *acache);
246static void shutdown_buckets(dns_acache_t *acache);
247static void destroy_entry(dns_acacheentry_t *ent);
248static inline void unlink_dbentries(dns_acache_t *acache,
249 dns_acacheentry_t *ent);
250static inline isc_result_t finddbent(dns_acache_t *acache,
251 dns_db_t *db, dbentry_t **dbentryp);
252static inline void clear_entry(dns_acache_t *acache, dns_acacheentry_t *entry);
253static isc_result_t acache_cleaner_init(dns_acache_t *acache,
254 isc_timermgr_t *timermgr,
255 acache_cleaner_t *cleaner);
256static void acache_cleaning_timer_action(isc_task_t *task, isc_event_t *event);
257static void acache_incremental_cleaning_action(isc_task_t *task,
258 isc_event_t *event);
259static void acache_overmem_cleaning_action(isc_task_t *task,
260 isc_event_t *event);
261static void acache_cleaner_shutdown_action(isc_task_t *task,
262 isc_event_t *event);
263
264/*
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
267 */
268static void
269reset_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;
279}
280
281/*
282 * The acache must be locked before calling.
283 */
284static inline isc_boolean_t
285check_noentry(dns_acache_t *acache) {
286 if (ISC_LIST_EMPTY(acache->entries) && acache->dbentries == 0) {
287 return (ISC_TRUE);
288 }
289
290 return (ISC_FALSE);
291}
292
293/*
294 * The acache must be locked before calling.
295 */
296static void
297shutdown_entries(dns_acache_t *acache) {
298 dns_acacheentry_t *entry, *entry_next;
299
300 REQUIRE(DNS_ACACHE_VALID(acache));
301 INSIST(acache->shutting_down);
302
303 /*
304 * Release the dependency of all entries, and detach them.
305 */
306 for (entry = ISC_LIST_HEAD(acache->entries);
307 entry != NULL;
308 entry = entry_next) {
309 entry_next = ISC_LIST_NEXT(entry, link);
310
311 ACACHE_LOCK(&acache->entrylocks[entry->locknum],
312 isc_rwlocktype_write);
313
314 /*
315 * If the cleaner holds this entry, it will be unlinked and
316 * freed in the cleaner later.
317 */
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;
324 }
325
326 ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
327 isc_rwlocktype_write);
328
329 if (acache->cleaner.current_entry != entry)
330 dns_acache_detachentry(&entry);
331 }
332}
333
334/*
335 * The acache must be locked before calling.
336 */
337static void
338shutdown_buckets(dns_acache_t *acache) {
339 int i;
340 dbentry_t *dbent;
341
342 REQUIRE(DNS_ACACHE_VALID(acache));
343 INSIST(acache->shutting_down);
344
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);
350
351 dns_db_detach(&dbent->db);
352
353 isc_mem_put(acache->mctx, dbent, sizeof(*dbent));
354
355 acache->dbentries--;
356 }
357 }
358
359 INSIST(acache->dbentries == 0);
360}
361
362static void
363shutdown_task(isc_task_t *task, isc_event_t *ev) {
364 dns_acache_t *acache;
365
366 UNUSED(task);
367
368 acache = ev->ev_arg;
369 INSIST(DNS_ACACHE_VALID(acache));
370
371 isc_event_free(&ev);
372
373 LOCK(&acache->lock);
374
375 shutdown_entries(acache);
376 shutdown_buckets(acache);
377
378 UNLOCK(&acache->lock);
379
380 dns_acache_detach(&acache);
381}
382
383/* The acache and the entry must be locked before calling. */
384static inline void
385unlink_dbentries(dns_acache_t *acache, dns_acacheentry_t *ent) {
386 isc_result_t result;
387 dbentry_t *dbent;
388
389 if (ISC_LINK_LINKED(ent, olink)) {
390 INSIST(ent->origdb != NULL);
391 dbent = NULL;
392 result = finddbent(acache, ent->origdb, &dbent);
393 INSIST(result == ISC_R_SUCCESS);
394
395 ISC_LIST_UNLINK(dbent->originlist, ent, olink);
396 }
397 if (ISC_LINK_LINKED(ent, rlink)) {
398 INSIST(ent->db != NULL);
399 dbent = NULL;
400 result = finddbent(acache, ent->db, &dbent);
401 INSIST(result == ISC_R_SUCCESS);
402
403 ISC_LIST_UNLINK(dbent->referlist, ent, rlink);
404 }
405}
406
407/* There must not be a reference to this entry. */
408static void
409destroy_entry(dns_acacheentry_t *entry) {
410 dns_acache_t *acache;
411
412 REQUIRE(DNS_ACACHEENTRY_VALID(entry));
413
414 acache = entry->acache;
415 REQUIRE(DNS_ACACHE_VALID(acache));
416
417 /*
418 * Since there is no reference to this entry, it is safe to call
419 * clear_entry() here.
420 */
421 clear_entry(acache, entry);
422
423 isc_mem_put(acache->mctx, entry, sizeof(*entry));
424
425 dns_acache_detach(&acache);
426}
427
428static void
429destroy(dns_acache_t *acache) {
430 int i;
431
432 REQUIRE(DNS_ACACHE_VALID(acache));
433
434 ATRACE("destroy");
435
436 isc_mem_setwater(acache->mctx, NULL, NULL, 0, 0);
437
438 if (acache->cleaner.overmem_event != NULL)
439 isc_event_free(&acache->cleaner.overmem_event);
440
441 if (acache->cleaner.resched_event != NULL)
442 isc_event_free(&acache->cleaner.resched_event);
443
444 if (acache->task != NULL)
445 isc_task_detach(&acache->task);
446
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);
452
453 DESTROYLOCK(&acache->cleaner.lock);
454
455 DESTROYLOCK(&acache->lock);
456 acache->magic = 0;
457
458 isc_mem_putanddetach(&acache->mctx, acache, sizeof(*acache));
459}
460
461static inline isc_result_t
462finddbent(dns_acache_t *acache, dns_db_t *db, dbentry_t **dbentryp) {
463 int bucket;
464 dbentry_t *dbentry;
465
466 REQUIRE(DNS_ACACHE_VALID(acache));
467 REQUIRE(db != NULL);
468 REQUIRE(dbentryp != NULL && *dbentryp == NULL);
469
470 /*
471 * The caller must be holding the acache lock.
472 */
473
474 bucket = isc_hash_calc((const unsigned char *)&db,
475 sizeof(db), ISC_TRUE) % DBBUCKETS;
476
477 for (dbentry = ISC_LIST_HEAD(acache->dbbucket[bucket]);
478 dbentry != NULL;
479 dbentry = ISC_LIST_NEXT(dbentry, link)) {
480 if (dbentry->db == db)
481 break;
482 }
483
484 *dbentryp = dbentry;
485
486 if (dbentry == NULL)
487 return (ISC_R_NOTFOUND);
488 else
489 return (ISC_R_SUCCESS);
490}
491
492static inline void
493clear_entry(dns_acache_t *acache, dns_acacheentry_t *entry) {
494 REQUIRE(DNS_ACACHE_VALID(acache));
495 REQUIRE(DNS_ACACHEENTRY_VALID(entry));
496
497 /*
498 * The caller must be holing the entry lock.
499 */
500
501 if (entry->foundname) {
502 dns_rdataset_t *rdataset, *rdataset_next;
503
504 for (rdataset = ISC_LIST_HEAD(entry->foundname->list);
505 rdataset != NULL;
506 rdataset = rdataset_next) {
507 rdataset_next = ISC_LIST_NEXT(rdataset, link);
508 ISC_LIST_UNLINK(entry->foundname->list,
509 rdataset, link);
510 dns_rdataset_disassociate(rdataset);
511 isc_mem_put(acache->mctx, rdataset, sizeof(*rdataset));
512 }
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;
518 }
519
520 if (entry->node != NULL) {
521 INSIST(entry->db != NULL);
522 dns_db_detachnode(entry->db, &entry->node);
523 }
524 if (entry->version != NULL) {
525 INSIST(entry->db != NULL);
526 dns_db_closeversion(entry->db, &entry->version, ISC_FALSE);
527 }
528 if (entry->db != NULL)
529 dns_db_detach(&entry->db);
530 if (entry->zone != NULL)
531 dns_zone_detach(&entry->zone);
532
533 if (entry->origdb != NULL)
534 dns_db_detach(&entry->origdb);
535}
536
537static isc_result_t
538acache_cleaner_init(dns_acache_t *acache, isc_timermgr_t *timermgr,
539 acache_cleaner_t *cleaner)
540{
541 int result;
542
543 ATRACE("acache cleaner init");
544
545 result = isc_mutex_init(&cleaner->lock);
546 if (result != ISC_R_SUCCESS)
547 goto fail;
548
549 cleaner->increment = DNS_ACACHE_CLEANERINCREMENT;
550 cleaner->state = cleaner_s_idle;
551 cleaner->acache = acache;
552 cleaner->overmem = ISC_FALSE;
553
554 cleaner->cleaning_timer = NULL;
555 cleaner->resched_event = NULL;
556 cleaner->overmem_event = NULL;
557 cleaner->current_entry = NULL;
558
559 if (timermgr != NULL) {
560 cleaner->acache->live_cleaners++;
561
562 result = isc_task_onshutdown(acache->task,
563 acache_cleaner_shutdown_action,
564 acache);
565 if (result != ISC_R_SUCCESS) {
566 UNEXPECTED_ERROR(__FILE__, __LINE__,
567 "acache cleaner: "
568 "isc_task_onshutdown() failed: %s",
569 dns_result_totext(result));
570 goto cleanup;
571 }
572
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,
576 NULL, NULL,
577 acache->task,
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;
585 goto cleanup;
586 }
587
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;
595 goto cleanup;
596 }
597
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;
605 goto cleanup;
606 }
607 }
608
609 return (ISC_R_SUCCESS);
610
611 cleanup:
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);
620 fail:
621 return (result);
622}
623
624static void
625begin_cleaning(acache_cleaner_t *cleaner) {
626 dns_acacheentry_t *head;
627 dns_acache_t *acache = cleaner->acache;
628
629 /*
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.
633 */
634
635 REQUIRE(CLEANER_IDLE(cleaner));
636 INSIST(DNS_ACACHE_VALID(acache));
637 INSIST(cleaner->current_entry == NULL);
638
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));
643
644 LOCK(&acache->lock);
645
646 head = ISC_LIST_HEAD(acache->entries);
647 if (head != NULL)
648 dns_acache_attachentry(head, &cleaner->current_entry);
649
650 UNLOCK(&acache->lock);
651
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);
656 }
657
658 return;
659}
660
661static void
662end_cleaning(acache_cleaner_t *cleaner, isc_event_t *event) {
663 dns_acache_t *acache = cleaner->acache;
664
665 REQUIRE(CLEANER_BUSY(cleaner));
666 REQUIRE(event != NULL);
667 REQUIRE(DNS_ACACHEENTRY_VALID(cleaner->current_entry));
668
669 /* No need to lock the cleaner (see begin_cleaning()). */
670
671 LOCK(&acache->lock);
672
673 /*
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.
677 */
678 if (isc_refcount_current(&cleaner->current_entry->references) == 1) {
679 INSIST(cleaner->current_entry->callback == NULL);
680
681 if (ISC_LINK_LINKED(cleaner->current_entry, link)) {
682 ISC_LIST_UNLINK(acache->entries,
683 cleaner->current_entry, link);
684 }
685 }
686 dns_acache_detachentry(&cleaner->current_entry);
687
688 if (cleaner->overmem)
689 acache->stats.overmem++;
690 acache->stats.cleaned += cleaner->ncleaned;
691 acache->stats.cleaner_runs++;
692
693 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE,
694 ISC_LOG_NOTICE,
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",
699 acache,
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);
706 reset_stats(acache);
707
708 isc_stdtime_get(&cleaner->last_cleanup_time);
709
710 UNLOCK(&acache->lock);
711
712 dns_acache_setcleaninginterval(cleaner->acache,
713 cleaner->cleaning_interval);
714
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",
718 cleaner->ncleaned,
719 (unsigned long)isc_mem_inuse(cleaner->acache->mctx));
720
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 "
725 "after cleaning");
726 }
727
728 cleaner->ncleaned = 0;
729 cleaner->state = cleaner_s_idle;
730 cleaner->resched_event = event;
731}
732
733/*
734 * This is run once for every acache-cleaning-interval as defined
735 * in named.conf.
736 */
737static void
738acache_cleaning_timer_action(isc_task_t *task, isc_event_t *event) {
739 acache_cleaner_t *cleaner = event->ev_arg;
740
741 UNUSED(task);
742
743 INSIST(event->ev_type == ISC_TIMEREVENT_TICK);
744
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);
748
749 if (cleaner->state == cleaner_s_idle)
750 begin_cleaning(cleaner);
751
752 isc_event_free(&event);
753}
754
755/* The caller must hold entry lock. */
756static inline isc_boolean_t
757entry_stale(acache_cleaner_t *cleaner, dns_acacheentry_t *entry,
758 isc_stdtime32_t now32, unsigned int interval)
759{
760 /*
761 * If the callback has been canceled, we definitely do not need the
762 * entry.
763 */
764 if (entry->callback == NULL)
765 return (ISC_TRUE);
766
767 if (interval > cleaner->cleaning_interval)
768 interval = cleaner->cleaning_interval;
769
770 if (entry->lastused + interval < now32)
771 return (ISC_TRUE);
772
773 /*
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.
777 */
778 if (cleaner->overmem) {
779 unsigned int passed =
780 now32 - entry->lastused; /* <= interval */
781 isc_uint32_t val;
782
783 if (passed > interval / 2)
784 return (ISC_TRUE);
785 isc_random_get(&val);
786 if (passed > interval / 4)
787 return (ISC_TF(val % 4 == 0));
788 return (ISC_TF(val % 8 == 0));
789 }
790
791 return (ISC_FALSE);
792}
793
794/*
795 * Do incremental cleaning.
796 */
797static void
798acache_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;
802 int n_entries;
803 isc_stdtime32_t now32, last32;
804 isc_stdtime_t now;
805 unsigned int interval;
806
807 INSIST(DNS_ACACHE_VALID(acache));
808 INSIST(task == acache->task);
809 INSIST(event->ev_type == DNS_EVENT_ACACHECLEAN);
810
811 if (cleaner->state == cleaner_s_done) {
812 cleaner->state = cleaner_s_busy;
813 end_cleaning(cleaner, event);
814 return;
815 }
816
817 INSIST(CLEANER_BUSY(cleaner));
818
819 n_entries = cleaner->increment;
820
821 isc_stdtime_get(&now);
822 isc_stdtime_convert32(now, &now32);
823
824 LOCK(&acache->lock);
825
826 entry = cleaner->current_entry;
827 isc_stdtime_convert32(cleaner->last_cleanup_time, &last32);
828 INSIST(now32 > last32);
829 interval = now32 - last32;
830
831 while (n_entries-- > 0) {
832 isc_boolean_t is_stale = ISC_FALSE;
833
834 INSIST(entry != NULL);
835
836 next = ISC_LIST_NEXT(entry, link);
837
838 ACACHE_LOCK(&acache->entrylocks[entry->locknum],
839 isc_rwlocktype_write);
840
841 is_stale = entry_stale(cleaner, entry, now32, interval);
842 if (is_stale) {
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;
848
849 cleaner->ncleaned++;
850 }
851
852 ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
853 isc_rwlocktype_write);
854
855 if (is_stale)
856 dns_acache_detachentry(&entry);
857
858 if (next == NULL) {
859 if (cleaner->overmem) {
860 entry = ISC_LIST_HEAD(acache->entries);
861 if (entry != NULL) {
862 /*
863 * If we are still in the overmem
864 * state, keep cleaning.
865 */
866 isc_log_write(dns_lctx,
867 DNS_LOGCATEGORY_DATABASE,
868 DNS_LOGMODULE_ACACHE,
869 ISC_LOG_DEBUG(1),
870 "acache cleaner: "
871 "still overmem, "
872 "reset and try again");
873 continue;
874 }
875 }
876
877 UNLOCK(&acache->lock);
878 end_cleaning(cleaner, event);
879 return;
880 }
881
882 entry = next;
883 }
884
885 /*
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.
890 */
891 INSIST(next != NULL && next != cleaner->current_entry);
892 dns_acache_detachentry(&cleaner->current_entry);
893 dns_acache_attachentry(next, &cleaner->current_entry);
894
895 UNLOCK(&acache->lock);
896
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));
901
902 isc_task_send(task, &event);
903 INSIST(CLEANER_BUSY(cleaner));
904
905 return;
906}
907
908/*
909 * This is called when the acache either surpasses its upper limit
910 * or shrinks beyond its lower limit.
911 */
912static void
913acache_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;
916
917 UNUSED(task);
918
919 INSIST(event->ev_type == DNS_EVENT_ACACHEOVERMEM);
920 INSIST(cleaner->overmem_event == NULL);
921
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,
925 cleaner->state);
926
927 LOCK(&cleaner->lock);
928
929 if (cleaner->overmem) {
930 if (cleaner->state == cleaner_s_idle)
931 want_cleaning = ISC_TRUE;
932 } else {
933 if (cleaner->state == cleaner_s_busy)
934 /*
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.
941 */
942 cleaner->state = cleaner_s_done;
943 }
944
945 cleaner->overmem_event = event;
946
947 UNLOCK(&cleaner->lock);
948
949 if (want_cleaning)
950 begin_cleaning(cleaner);
951}
952
953static void
954water(void *arg, int mark) {
955 dns_acache_t *acache = arg;
956 isc_boolean_t overmem = ISC_TF(mark == ISC_MEM_HIWATER);
957
958 REQUIRE(DNS_ACACHE_VALID(acache));
959
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));
965
966 LOCK(&acache->cleaner.lock);
967
968 if (acache->cleaner.overmem != overmem) {
969 acache->cleaner.overmem = overmem;
970
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);
975 }
976
977 UNLOCK(&acache->cleaner.lock);
978}
979
980/*
981 * The cleaner task is shutting down; do the necessary cleanup.
982 */
983static void
984acache_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;
987
988 INSIST(task == acache->task);
989 INSIST(event->ev_type == ISC_TASKEVENT_SHUTDOWN);
990 INSIST(DNS_ACACHE_VALID(acache));
991
992 ATRACE("acache cleaner shutdown");
993
994 if (CLEANER_BUSY(&acache->cleaner))
995 end_cleaning(&acache->cleaner, event);
996 else
997 isc_event_free(&event);
998
999 LOCK(&acache->lock);
1000
1001 acache->live_cleaners--;
1002 INSIST(acache->live_cleaners == 0);
1003
1004 if (isc_refcount_current(&acache->refs) == 0) {
1005 INSIST(check_noentry(acache) == ISC_TRUE);
1006 should_free = ISC_TRUE;
1007 }
1008
1009 /*
1010 * By detaching the timer in the context of its task,
1011 * we are guaranteed that there will be no further timer
1012 * events.
1013 */
1014 if (acache->cleaner.cleaning_timer != NULL)
1015 isc_timer_detach(&acache->cleaner.cleaning_timer);
1016
1017 /* Make sure we don't reschedule anymore. */
1018 (void)isc_task_purge(task, NULL, DNS_EVENT_ACACHECLEAN, NULL);
1019
1020 UNLOCK(&acache->lock);
1021
1022 if (should_free)
1023 destroy(acache);
1024}
1025
1026/*
1027 * Public functions.
1028 */
1029
1030isc_result_t
1031dns_acache_create(dns_acache_t **acachep, isc_mem_t *mctx,
1032 isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr)
1033{
1034 int i;
1035 isc_result_t result;
1036 dns_acache_t *acache;
1037
1038 REQUIRE(acachep != NULL && *acachep == NULL);
1039 REQUIRE(mctx != NULL);
1040 REQUIRE(taskmgr != NULL);
1041
1042 acache = isc_mem_get(mctx, sizeof(*acache));
1043 if (acache == NULL)
1044 return (ISC_R_NOMEMORY);
1045
1046 ATRACE("create");
1047
1048 result = isc_refcount_init(&acache->refs, 1);
1049 if (result != ISC_R_SUCCESS) {
1050 isc_mem_put(mctx, acache, sizeof(*acache));
1051 return (result);
1052 }
1053
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));
1059 return (result);
1060 }
1061
1062 acache->mctx = NULL;
1063 isc_mem_attach(mctx, &acache->mctx);
1064 ISC_LIST_INIT(acache->entries);
1065
1066 acache->shutting_down = ISC_FALSE;
1067
1068 acache->task = NULL;
1069 acache->entrylocks = NULL;
1070
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;
1077 goto cleanup;
1078 }
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,
1082 NULL, NULL, NULL);
1083 acache->cevent_sent = ISC_FALSE;
1084
1085 acache->dbentries = 0;
1086 for (i = 0; i < DBBUCKETS; i++)
1087 ISC_LIST_INIT(acache->dbbucket[i]);
1088
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;
1093 goto cleanup;
1094 }
1095 for (i = 0; i < DEFAULT_ACACHE_ENTRY_LOCK_COUNT; i++) {
1096 result = ACACHE_INITLOCK(&acache->entrylocks[i]);
1097 if (result != ISC_R_SUCCESS) {
1098 while (i-- > 0)
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;
1104 goto cleanup;
1105 }
1106 }
1107
1108 acache->live_cleaners = 0;
1109 result = acache_cleaner_init(acache, timermgr, &acache->cleaner);
1110 if (result != ISC_R_SUCCESS)
1111 goto cleanup;
1112
1113 acache->stats.cleaner_runs = 0;
1114 reset_stats(acache);
1115
1116 acache->magic = ACACHE_MAGIC;
1117
1118 *acachep = acache;
1119 return (ISC_R_SUCCESS);
1120
1121 cleanup:
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);
1133 }
1134 isc_mem_put(mctx, acache, sizeof(*acache));
1135 isc_mem_detach(&mctx);
1136
1137 return (result);
1138}
1139
1140void
1141dns_acache_attach(dns_acache_t *source, dns_acache_t **targetp) {
1142 REQUIRE(DNS_ACACHE_VALID(source));
1143 REQUIRE(targetp != NULL && *targetp == NULL);
1144
1145 AATRACE(source, "attach");
1146
1147 isc_refcount_increment(&source->refs, NULL);
1148
1149 *targetp = source;
1150}
1151
1152void
1153dns_acache_countquerymiss(dns_acache_t *acache) {
1154 acache->stats.misses++; /* XXXSK danger: unlocked! */
1155 acache->stats.queries++; /* XXXSK danger: unlocked! */
1156}
1157
1158void
1159dns_acache_detach(dns_acache_t **acachep) {
1160 dns_acache_t *acache;
1161 unsigned int refs;
1162 isc_boolean_t should_free = ISC_FALSE;
1163
1164 REQUIRE(acachep != NULL && DNS_ACACHE_VALID(*acachep));
1165 acache = *acachep;
1166
1167 ATRACE("detach");
1168
1169 isc_refcount_decrement(&acache->refs, &refs);
1170 if (refs == 0) {
1171 INSIST(check_noentry(acache) == ISC_TRUE);
1172 should_free = ISC_TRUE;
1173 }
1174
1175 *acachep = NULL;
1176
1177 /*
1178 * If we're exiting and the cleaner task exists, let it free the cache.
1179 */
1180 if (should_free && acache->live_cleaners > 0) {
1181 isc_task_shutdown(acache->task);
1182 should_free = ISC_FALSE;
1183 }
1184
1185 if (should_free)
1186 destroy(acache);
1187}
1188
1189void
1190dns_acache_shutdown(dns_acache_t *acache) {
1191 REQUIRE(DNS_ACACHE_VALID(acache));
1192
1193 LOCK(&acache->lock);
1194
1195 ATRACE("shutdown");
1196
1197 if (!acache->shutting_down) {
1198 isc_event_t *event;
1199 dns_acache_t *acache_evarg = NULL;
1200
1201 INSIST(!acache->cevent_sent);
1202
1203 acache->shutting_down = ISC_TRUE;
1204
1205 isc_mem_setwater(acache->mctx, NULL, NULL, 0, 0);
1206
1207 /*
1208 * Self attach the object in order to prevent it from being
1209 * destroyed while waiting for the event.
1210 */
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;
1216 }
1217
1218 UNLOCK(&acache->lock);
1219}
1220
1221isc_result_t
1222dns_acache_setdb(dns_acache_t *acache, dns_db_t *db) {
1223 int bucket;
1224 dbentry_t *dbentry;
1225 isc_result_t result = ISC_R_SUCCESS;
1226
1227 REQUIRE(DNS_ACACHE_VALID(acache));
1228 REQUIRE(db != NULL);
1229
1230 ATRACE("setdb");
1231
1232 LOCK(&acache->lock);
1233
1234 dbentry = NULL;
1235 result = finddbent(acache, db, &dbentry);
1236 if (result == ISC_R_SUCCESS) {
1237 result = ISC_R_EXISTS;
1238 goto end;
1239 }
1240 result = ISC_R_SUCCESS;
1241
1242 dbentry = isc_mem_get(acache->mctx, sizeof(*dbentry));
1243 if (dbentry == NULL) {
1244 result = ISC_R_NOMEMORY;
1245 goto end;
1246 }
1247
1248 ISC_LINK_INIT(dbentry, link);
1249 ISC_LIST_INIT(dbentry->originlist);
1250 ISC_LIST_INIT(dbentry->referlist);
1251
1252 dbentry->db = NULL;
1253 dns_db_attach(db, &dbentry->db);
1254
1255 bucket = isc_hash_calc((const unsigned char *)&db,
1256 sizeof(db), ISC_TRUE) % DBBUCKETS;
1257
1258 ISC_LIST_APPEND(acache->dbbucket[bucket], dbentry, link);
1259
1260 acache->dbentries++;
1261
1262 end:
1263 UNLOCK(&acache->lock);
1264
1265 return (result);
1266}
1267
1268isc_result_t
1269dns_acache_putdb(dns_acache_t *acache, dns_db_t *db) {
1270 int bucket;
1271 isc_result_t result;
1272 dbentry_t *dbentry;
1273 dns_acacheentry_t *entry;
1274
1275 REQUIRE(DNS_ACACHE_VALID(acache));
1276 REQUIRE(db != NULL);
1277
1278 ATRACE("putdb");
1279
1280 LOCK(&acache->lock);
1281
1282 dbentry = NULL;
1283 result = finddbent(acache, db, &dbentry);
1284 if (result != ISC_R_SUCCESS) {
1285 /*
1286 * The entry may have not been created due to memory shortage.
1287 */
1288 UNLOCK(&acache->lock);
1289 return (ISC_R_NOTFOUND);
1290 }
1291
1292 /*
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.
1297 */
1298 while ((entry = ISC_LIST_HEAD(dbentry->originlist)) != NULL) {
1299 ACACHE_LOCK(&acache->entrylocks[entry->locknum],
1300 isc_rwlocktype_write);
1301
1302 /*
1303 * Releasing olink first would avoid finddbent() in
1304 * unlink_dbentries().
1305 */
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);
1310
1311 if (entry->callback != NULL)
1312 (entry->callback)(entry, &entry->cbarg);
1313 entry->callback = NULL;
1314
1315 ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
1316 isc_rwlocktype_write);
1317
1318 if (acache->cleaner.current_entry != entry)
1319 dns_acache_detachentry(&entry);
1320 }
1321 while ((entry = ISC_LIST_HEAD(dbentry->referlist)) != NULL) {
1322 ACACHE_LOCK(&acache->entrylocks[entry->locknum],
1323 isc_rwlocktype_write);
1324
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);
1329
1330 if (entry->callback != NULL)
1331 (entry->callback)(entry, &entry->cbarg);
1332 entry->callback = NULL;
1333
1334 ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
1335 isc_rwlocktype_write);
1336
1337 if (acache->cleaner.current_entry != entry)
1338 dns_acache_detachentry(&entry);
1339 }
1340
1341 INSIST(ISC_LIST_EMPTY(dbentry->originlist) &&
1342 ISC_LIST_EMPTY(dbentry->referlist));
1343
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);
1348
1349 isc_mem_put(acache->mctx, dbentry, sizeof(*dbentry));
1350
1351 acache->dbentries--;
1352
1353 acache->stats.deleted++;
1354
1355 UNLOCK(&acache->lock);
1356
1357 return (ISC_R_SUCCESS);
1358}
1359
1360isc_result_t
1361dns_acache_createentry(dns_acache_t *acache, dns_db_t *origdb,
1362 void (*callback)(dns_acacheentry_t *, void **),
1363 void *cbarg, dns_acacheentry_t **entryp)
1364{
1365 dns_acacheentry_t *newentry;
1366 isc_result_t result;
1367 isc_uint32_t r;
1368
1369 REQUIRE(DNS_ACACHE_VALID(acache));
1370 REQUIRE(entryp != NULL && *entryp == NULL);
1371 REQUIRE(origdb != NULL);
1372
1373 /*
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.
1377 *
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.
1384 */
1385 if (acache->cleaner.overmem) {
1386 acache->stats.overmem_nocreates++; /* XXXSK danger: unlocked! */
1387 return (ISC_R_NORESOURCES);
1388 }
1389
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);
1394 }
1395
1396 isc_random_get(&r);
1397 newentry->locknum = r % DEFAULT_ACACHE_ENTRY_LOCK_COUNT;
1398
1399 result = isc_refcount_init(&newentry->references, 1);
1400 if (result != ISC_R_SUCCESS) {
1401 isc_mem_put(acache->mctx, newentry, sizeof(*newentry));
1402 return (result);
1403 };
1404
1405 ISC_LINK_INIT(newentry, link);
1406 ISC_LINK_INIT(newentry, olink);
1407 ISC_LINK_INIT(newentry, rlink);
1408
1409 newentry->acache = NULL;
1410 dns_acache_attach(acache, &newentry->acache);
1411
1412 newentry->zone = NULL;
1413 newentry->db = NULL;
1414 newentry->version = NULL;
1415 newentry->node = NULL;
1416 newentry->foundname = NULL;
1417
1418 newentry->callback = callback;
1419 newentry->cbarg = cbarg;
1420 newentry->origdb = NULL;
1421 dns_db_attach(origdb, &newentry->origdb);
1422
1423 isc_stdtime_get(&newentry->lastused);
1424
1425 newentry->magic = ACACHEENTRY_MAGIC;
1426
1427 *entryp = newentry;
1428
1429 return (ISC_R_SUCCESS);
1430}
1431
1432isc_result_t
1433dns_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)
1437{
1438 isc_result_t result = ISC_R_SUCCESS;
1439 dns_rdataset_t *erdataset;
1440 isc_stdtime32_t now32;
1441 dns_acache_t *acache;
1442 int locknum;
1443
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));
1453
1454 locknum = entry->locknum;
1455 ACACHE_LOCK(&acache->entrylocks[locknum], isc_rwlocktype_read);
1456
1457 isc_stdtime_convert32(now, &now32);
1458 acache_storetime(entry, now32);
1459
1460 if (entry->zone != NULL && zonep != NULL)
1461 dns_zone_attach(entry->zone, zonep);
1462
1463 if (entry->db == NULL) {
1464 *dbp = NULL;
1465 *versionp = NULL;
1466 } else {
1467 dns_db_attach(entry->db, dbp);
1468 dns_db_attachversion(entry->db, entry->version, versionp);
1469 }
1470 if (entry->node == NULL)
1471 *nodep = NULL;
1472 else {
1473 dns_db_attachnode(entry->db, entry->node, nodep);
1474
1475 INSIST(entry->foundname != NULL);
1476 dns_name_copy(entry->foundname, fname, NULL);
1477 for (erdataset = ISC_LIST_HEAD(entry->foundname->list);
1478 erdataset != NULL;
1479 erdataset = ISC_LIST_NEXT(erdataset, link)) {
1480 dns_rdataset_t *ardataset;
1481
1482 ardataset = NULL;
1483 result = dns_message_gettemprdataset(msg, &ardataset);
1484 if (result != ISC_R_SUCCESS) {
1485 ACACHE_UNLOCK(&acache->entrylocks[locknum],
1486 isc_rwlocktype_read);
1487 goto fail;
1488 }
1489
1490 /*
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
1494 * header.
1495 */
1496 dns_rdataset_init(ardataset);
1497 dns_rdataset_clone(erdataset, ardataset);
1498 ISC_LIST_APPEND(fname->list, ardataset, link);
1499 }
1500 }
1501
1502 entry->acache->stats.hits++; /* XXXMLG danger: unlocked! */
1503 entry->acache->stats.queries++;
1504
1505 ACACHE_UNLOCK(&acache->entrylocks[locknum], isc_rwlocktype_read);
1506
1507 return (result);
1508
1509 fail:
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);
1514 }
1515 if (*nodep != NULL)
1516 dns_db_detachnode(*dbp, nodep);
1517 if (*versionp != NULL)
1518 dns_db_closeversion(*dbp, versionp, ISC_FALSE);
1519 if (*dbp != NULL)
1520 dns_db_detach(dbp);
1521 if (zonep != NULL && *zonep != NULL)
1522 dns_zone_detach(zonep);
1523
1524 return (result);
1525}
1526
1527isc_result_t
1528dns_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)
1531{
1532 isc_result_t result;
1533 dbentry_t *odbent;
1534 dbentry_t *rdbent = NULL;
1535 isc_boolean_t close_version = ISC_FALSE;
1536 dns_acacheentry_t *dummy_entry = NULL;
1537
1538 REQUIRE(DNS_ACACHE_VALID(acache));
1539 REQUIRE(DNS_ACACHEENTRY_VALID(entry));
1540
1541 LOCK(&acache->lock); /* XXX: need to lock it here for ordering */
1542 ACACHE_LOCK(&acache->entrylocks[entry->locknum], isc_rwlocktype_write);
1543
1544 /* Set zone */
1545 if (zone != NULL)
1546 dns_zone_attach(zone, &entry->zone);
1547 /* Set DB */
1548 if (db != NULL)
1549 dns_db_attach(db, &entry->db);
1550 /*
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.
1553 */
1554 if (version == NULL) {
1555 if (db != NULL) {
1556 dns_db_currentversion(db, &version);
1557 close_version = ISC_TRUE;
1558 }
1559 }
1560 if (version != NULL) {
1561 INSIST(db != NULL);
1562 dns_db_attachversion(db, version, &entry->version);
1563 }
1564 if (close_version)
1565 dns_db_closeversion(db, &version, ISC_FALSE);
1566 /* Set DB node. */
1567 if (node != NULL) {
1568 INSIST(db != NULL);
1569 dns_db_attachnode(db, node, &entry->node);
1570 }
1571
1572 /*
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.
1578 */
1579 if (node != NULL) {
1580 dns_rdataset_t *ardataset, *crdataset;
1581
1582 entry->foundname = isc_mem_get(acache->mctx,
1583 sizeof(*entry->foundname));
1584
1585 if (entry->foundname == NULL) {
1586 result = ISC_R_NOMEMORY;
1587 goto fail;
1588 }
1589 dns_name_init(entry->foundname, NULL);
1590 result = dns_name_dup(fname, acache->mctx,
1591 entry->foundname);
1592 if (result != ISC_R_SUCCESS)
1593 goto fail;
1594
1595 for (ardataset = ISC_LIST_HEAD(fname->list);
1596 ardataset != NULL;
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;
1602 goto fail;
1603 }
1604
1605 dns_rdataset_init(crdataset);
1606 dns_rdataset_clone(ardataset, crdataset);
1607 ISC_LIST_APPEND(entry->foundname->list, crdataset,
1608 link);
1609 }
1610 }
1611
1612 odbent = NULL;
1613 result = finddbent(acache, entry->origdb, &odbent);
1614 if (result != ISC_R_SUCCESS)
1615 goto fail;
1616 if (db != NULL) {
1617 rdbent = NULL;
1618 result = finddbent(acache, db, &rdbent);
1619 if (result != ISC_R_SUCCESS)
1620 goto fail;
1621 }
1622
1623 ISC_LIST_APPEND(acache->entries, entry, link);
1624 ISC_LIST_APPEND(odbent->originlist, entry, olink);
1625 if (rdbent != NULL)
1626 ISC_LIST_APPEND(rdbent->referlist, entry, rlink);
1627
1628 /*
1629 * The additional cache needs an implicit reference to entries in its
1630 * link.
1631 */
1632 dns_acache_attachentry(entry, &dummy_entry);
1633
1634 ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
1635 isc_rwlocktype_write);
1636
1637 acache->stats.adds++;
1638 UNLOCK(&acache->lock);
1639
1640 return (ISC_R_SUCCESS);
1641
1642 fail:
1643 clear_entry(acache, entry);
1644
1645 ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
1646 isc_rwlocktype_write);
1647 UNLOCK(&acache->lock);
1648
1649 return (result);
1650}
1651
1652void
1653dns_acache_cancelentry(dns_acacheentry_t *entry) {
1654 dns_acache_t *acache = entry->acache;
1655
1656 REQUIRE(DNS_ACACHEENTRY_VALID(entry));
1657 INSIST(DNS_ACACHE_VALID(acache));
1658
1659 LOCK(&acache->lock);
1660 ACACHE_LOCK(&acache->entrylocks[entry->locknum], isc_rwlocktype_write);
1661
1662 /*
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.
1667 */
1668 unlink_dbentries(acache, entry);
1669 clear_entry(entry->acache, entry);
1670
1671 entry->callback = NULL;
1672 entry->cbarg = NULL;
1673
1674 ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
1675 isc_rwlocktype_write);
1676 UNLOCK(&acache->lock);
1677}
1678
1679void
1680dns_acache_attachentry(dns_acacheentry_t *source,
1681 dns_acacheentry_t **targetp)
1682{
1683 REQUIRE(DNS_ACACHEENTRY_VALID(source));
1684 REQUIRE(targetp != NULL && *targetp == NULL);
1685
1686 isc_refcount_increment(&source->references, NULL);
1687
1688 *targetp = source;
1689}
1690
1691void
1692dns_acache_detachentry(dns_acacheentry_t **entryp) {
1693 dns_acacheentry_t *entry;
1694 unsigned int refs;
1695
1696 REQUIRE(entryp != NULL && DNS_ACACHEENTRY_VALID(*entryp));
1697 entry = *entryp;
1698
1699 isc_refcount_decrement(&entry->references, &refs);
1700
1701 /*
1702 * If there are no references to the entry, the entry must have been
1703 * unlinked and can be destroyed safely.
1704 */
1705 if (refs == 0) {
1706 INSIST(!ISC_LINK_LINKED(entry, link));
1707 (*entryp)->acache->stats.deleted++;
1708 destroy_entry(entry);
1709 }
1710
1711 *entryp = NULL;
1712}
1713
1714void
1715dns_acache_setcleaninginterval(dns_acache_t *acache, unsigned int t) {
1716 isc_interval_t interval;
1717 isc_result_t result;
1718
1719 REQUIRE(DNS_ACACHE_VALID(acache));
1720
1721 ATRACE("dns_acache_setcleaninginterval");
1722
1723 LOCK(&acache->lock);
1724
1725 /*
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.)
1728 */
1729 if (acache->cleaner.cleaning_timer == NULL)
1730 goto unlock;
1731
1732 acache->cleaner.cleaning_interval = t;
1733
1734 if (t == 0) {
1735 result = isc_timer_reset(acache->cleaner.cleaning_timer,
1736 isc_timertype_inactive,
1737 NULL, NULL, ISC_TRUE);
1738 } else {
1739 isc_interval_set(&interval, acache->cleaner.cleaning_interval,
1740 0);
1741 result = isc_timer_reset(acache->cleaner.cleaning_timer,
1742 isc_timertype_ticker,
1743 NULL, &interval, ISC_FALSE);
1744 }
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));
1750 else
1751 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1752 DNS_LOGMODULE_ACACHE, ISC_LOG_NOTICE,
1753 "acache %p cleaning interval set to %d.",
1754 acache, t);
1755
1756 unlock:
1757 UNLOCK(&acache->lock);
1758}
1759
1760/*
1761 * This function was derived from cache.c:dns_cache_setcachesize(). See the
1762 * function for more details about the logic.
1763 */
1764void
1765dns_acache_setcachesize(dns_acache_t *acache, isc_uint32_t size) {
1766 isc_uint32_t lowater;
1767 isc_uint32_t hiwater;
1768
1769 REQUIRE(DNS_ACACHE_VALID(acache));
1770
1771 if (size != 0 && size < DNS_ACACHE_MINSIZE)
1772 size = DNS_ACACHE_MINSIZE;
1773
1774 hiwater = size - (size >> 3);
1775 lowater = size - (size >> 2);
1776
1777 if (size == 0 || hiwater == 0 || lowater == 0)
1778 isc_mem_setwater(acache->mctx, water, acache, 0, 0);
1779 else
1780 isc_mem_setwater(acache->mctx, water, acache,
1781 hiwater, lowater);
1782}