]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/bind9/lib/dns/acache.c
This commit was generated by cvs2svn to compensate for changes in r173619,
[FreeBSD/FreeBSD.git] / contrib / bind9 / lib / dns / acache.c
1 /*
2  * Copyright (C) 2004-2006  Internet Systems Consortium, Inc. ("ISC")
3  *
4  * Permission to use, copy, modify, and 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.3.2.16 2006/07/19 00:34:56 marka 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 */
102 typedef 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
110 typedef ISC_LIST(dbentry_t) dbentrylist_t;
111
112 typedef struct acache_cleaner acache_cleaner_t;
113
114 typedef 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
128 struct 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
160 struct 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
177 struct 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
206 struct 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  */
243 static inline isc_boolean_t check_noentry(dns_acache_t *acache);
244 static void destroy(dns_acache_t *acache);
245 static void shutdown_entries(dns_acache_t *acache);
246 static void shutdown_buckets(dns_acache_t *acache);
247 static void destroy_entry(dns_acacheentry_t *ent);
248 static inline void unlink_dbentries(dns_acache_t *acache,
249                                     dns_acacheentry_t *ent);
250 static inline isc_result_t finddbent(dns_acache_t *acache,
251                                      dns_db_t *db, dbentry_t **dbentryp);
252 static inline void clear_entry(dns_acache_t *acache, dns_acacheentry_t *entry);
253 static isc_result_t acache_cleaner_init(dns_acache_t *acache,
254                                         isc_timermgr_t *timermgr,
255                                         acache_cleaner_t *cleaner);
256 static void acache_cleaning_timer_action(isc_task_t *task, isc_event_t *event);
257 static void acache_incremental_cleaning_action(isc_task_t *task,
258                                                isc_event_t *event);
259 static void acache_overmem_cleaning_action(isc_task_t *task,
260                                            isc_event_t *event);
261 static 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  */
268 static void
269 reset_stats(dns_acache_t *acache) {
270         acache->stats.hits = 0;
271         acache->stats.queries = 0;
272         acache->stats.misses = 0;
273         acache->stats.adds = 0;
274         acache->stats.deleted = 0;
275         acache->stats.cleaned = 0;
276         acache->stats.overmem = 0;
277         acache->stats.overmem_nocreates = 0;
278         acache->stats.nomem = 0;
279 }
280
281 /*
282  * The acache must be locked before calling.
283  */
284 static inline isc_boolean_t
285 check_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  */
296 static void
297 shutdown_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  */
337 static void
338 shutdown_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
362 static void
363 shutdown_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. */
384 static inline void
385 unlink_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. */
408 static void
409 destroy_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
428 static void
429 destroy(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
461 static inline isc_result_t
462 finddbent(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
492 static inline void
493 clear_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
537 static isc_result_t
538 acache_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
624 static void
625 begin_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
661 static void
662 end_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  */
737 static void
738 acache_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. */
756 static inline isc_boolean_t
757 entry_stale(acache_cleaner_t *cleaner, dns_acacheentry_t *entry,
758             isc_stdtime32_t now32, unsigned int interval)
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  */
797 static void
798 acache_incremental_cleaning_action(isc_task_t *task, isc_event_t *event) {
799         acache_cleaner_t *cleaner = event->ev_arg;
800         dns_acache_t *acache = cleaner->acache;
801         dns_acacheentry_t *entry, *next = NULL;
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  */
912 static void
913 acache_overmem_cleaning_action(isc_task_t *task, isc_event_t *event) {
914         acache_cleaner_t *cleaner = event->ev_arg;
915         isc_boolean_t want_cleaning = ISC_FALSE;
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
953 static void
954 water(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         acache->cleaner.overmem = overmem;
969
970         if (acache->cleaner.overmem_event != NULL)
971                 isc_task_send(acache->task, &acache->cleaner.overmem_event);
972
973         UNLOCK(&acache->cleaner.lock);
974 }
975
976 /*
977  * The cleaner task is shutting down; do the necessary cleanup.
978  */
979 static void
980 acache_cleaner_shutdown_action(isc_task_t *task, isc_event_t *event) {
981         dns_acache_t *acache = event->ev_arg;
982         isc_boolean_t should_free = ISC_FALSE;
983
984         INSIST(task == acache->task);
985         INSIST(event->ev_type == ISC_TASKEVENT_SHUTDOWN);
986         INSIST(DNS_ACACHE_VALID(acache));
987
988         ATRACE("acache cleaner shutdown");
989
990         if (CLEANER_BUSY(&acache->cleaner))
991                 end_cleaning(&acache->cleaner, event);
992         else
993                 isc_event_free(&event);
994
995         LOCK(&acache->lock);
996
997         acache->live_cleaners--;
998         INSIST(acache->live_cleaners == 0);
999
1000         if (isc_refcount_current(&acache->refs) == 0) {
1001                 INSIST(check_noentry(acache) == ISC_TRUE);
1002                 should_free = ISC_TRUE;
1003         }
1004
1005         /*
1006          * By detaching the timer in the context of its task,
1007          * we are guaranteed that there will be no further timer
1008          * events.
1009          */
1010         if (acache->cleaner.cleaning_timer != NULL)
1011                 isc_timer_detach(&acache->cleaner.cleaning_timer);
1012
1013         /* Make sure we don't reschedule anymore. */
1014         (void)isc_task_purge(task, NULL, DNS_EVENT_ACACHECLEAN, NULL);
1015
1016         UNLOCK(&acache->lock);
1017
1018         if (should_free)
1019                 destroy(acache);
1020 }
1021
1022 /*
1023  *      Public functions.
1024  */
1025
1026 isc_result_t
1027 dns_acache_create(dns_acache_t **acachep, isc_mem_t *mctx,
1028                   isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr)
1029 {
1030         int i;
1031         isc_result_t result;
1032         dns_acache_t *acache;
1033
1034         REQUIRE(acachep != NULL && *acachep == NULL);
1035         REQUIRE(mctx != NULL);
1036         REQUIRE(taskmgr != NULL);
1037
1038         acache = isc_mem_get(mctx, sizeof(*acache));
1039         if (acache == NULL)
1040                 return (ISC_R_NOMEMORY);
1041
1042         ATRACE("create");
1043
1044         result = isc_refcount_init(&acache->refs, 1);
1045         if (result != ISC_R_SUCCESS) {
1046                 isc_mem_put(mctx, acache, sizeof(*acache));
1047                 return (result);
1048         }
1049
1050         result = isc_mutex_init(&acache->lock);
1051         if (result != ISC_R_SUCCESS) {
1052                 isc_refcount_decrement(&acache->refs, NULL);
1053                 isc_refcount_destroy(&acache->refs);
1054                 isc_mem_put(mctx, acache, sizeof(*acache));
1055                 return (result);
1056         }
1057
1058         acache->mctx = NULL;
1059         isc_mem_attach(mctx, &acache->mctx);
1060         ISC_LIST_INIT(acache->entries);
1061
1062         acache->shutting_down = ISC_FALSE;
1063
1064         acache->task = NULL;
1065         acache->entrylocks = NULL;
1066
1067         result = isc_task_create(taskmgr, 1, &acache->task);
1068         if (result != ISC_R_SUCCESS) {
1069                 UNEXPECTED_ERROR(__FILE__, __LINE__,
1070                                  "isc_task_create() failed(): %s",
1071                                  dns_result_totext(result));
1072                 result = ISC_R_UNEXPECTED;
1073                 goto cleanup;
1074         }
1075         isc_task_setname(acache->task, "acachetask", acache);
1076         ISC_EVENT_INIT(&acache->cevent, sizeof(acache->cevent), 0, NULL,
1077                        DNS_EVENT_ACACHECONTROL, shutdown_task, NULL,
1078                        NULL, NULL, NULL);
1079         acache->cevent_sent = ISC_FALSE;
1080
1081         acache->dbentries = 0;
1082         for (i = 0; i < DBBUCKETS; i++)
1083                 ISC_LIST_INIT(acache->dbbucket[i]);
1084
1085         acache->entrylocks = isc_mem_get(mctx, sizeof(*acache->entrylocks) *
1086                                          DEFAULT_ACACHE_ENTRY_LOCK_COUNT);
1087         if (acache->entrylocks == NULL) {
1088                 result = ISC_R_NOMEMORY;
1089                 goto cleanup;
1090         }
1091         for (i = 0; i < DEFAULT_ACACHE_ENTRY_LOCK_COUNT; i++) {
1092                 result = ACACHE_INITLOCK(&acache->entrylocks[i]);
1093                 if (result != ISC_R_SUCCESS) {
1094                         while (i-- > 0)
1095                                 ACACHE_DESTROYLOCK(&acache->entrylocks[i]);
1096                         isc_mem_put(mctx, acache->entrylocks,
1097                                     sizeof(*acache->entrylocks) *
1098                                     DEFAULT_ACACHE_ENTRY_LOCK_COUNT);
1099                         acache->entrylocks = NULL;
1100                         goto cleanup;
1101                 }
1102         }
1103
1104         acache->live_cleaners = 0;
1105         result = acache_cleaner_init(acache, timermgr, &acache->cleaner); 
1106         if (result != ISC_R_SUCCESS)
1107                 goto cleanup;
1108
1109         acache->stats.cleaner_runs = 0;
1110         reset_stats(acache);
1111
1112         acache->magic = ACACHE_MAGIC;
1113
1114         *acachep = acache;
1115         return (ISC_R_SUCCESS);
1116
1117  cleanup:
1118         if (acache->task != NULL)
1119                 isc_task_detach(&acache->task);
1120         DESTROYLOCK(&acache->lock);
1121         isc_refcount_decrement(&acache->refs, NULL);
1122         isc_refcount_destroy(&acache->refs);
1123         if (acache->entrylocks != NULL) {
1124                 for (i = 0; i < DEFAULT_ACACHE_ENTRY_LOCK_COUNT; i++)
1125                         ACACHE_DESTROYLOCK(&acache->entrylocks[i]);
1126                 isc_mem_put(mctx, acache->entrylocks,
1127                             sizeof(*acache->entrylocks) *
1128                             DEFAULT_ACACHE_ENTRY_LOCK_COUNT);
1129         }
1130         isc_mem_put(mctx, acache, sizeof(*acache));
1131         isc_mem_detach(&mctx);
1132
1133         return (result);
1134 }
1135
1136 void
1137 dns_acache_attach(dns_acache_t *source, dns_acache_t **targetp) {
1138         REQUIRE(DNS_ACACHE_VALID(source));
1139         REQUIRE(targetp != NULL && *targetp == NULL);
1140
1141         AATRACE(source, "attach");
1142
1143         isc_refcount_increment(&source->refs, NULL);
1144
1145         *targetp = source;
1146 }
1147
1148 void
1149 dns_acache_countquerymiss(dns_acache_t *acache) {
1150         acache->stats.misses++;         /* XXXSK danger: unlocked! */
1151         acache->stats.queries++;        /* XXXSK danger: unlocked! */
1152 }
1153
1154 void
1155 dns_acache_detach(dns_acache_t **acachep) {
1156         dns_acache_t *acache;
1157         unsigned int refs;
1158         isc_boolean_t should_free = ISC_FALSE;
1159
1160         REQUIRE(acachep != NULL && DNS_ACACHE_VALID(*acachep));
1161         acache = *acachep;
1162
1163         ATRACE("detach");
1164
1165         isc_refcount_decrement(&acache->refs, &refs);
1166         if (refs == 0) {
1167                 INSIST(check_noentry(acache) == ISC_TRUE);
1168                 should_free = ISC_TRUE;
1169         }
1170
1171         *acachep = NULL;
1172
1173         /*
1174          * If we're exiting and the cleaner task exists, let it free the cache.
1175          */
1176         if (should_free && acache->live_cleaners > 0) {
1177                 isc_task_shutdown(acache->task);
1178                 should_free = ISC_FALSE;
1179         }
1180         
1181         if (should_free)
1182                 destroy(acache);
1183 }
1184
1185 void
1186 dns_acache_shutdown(dns_acache_t *acache) {
1187         REQUIRE(DNS_ACACHE_VALID(acache));
1188
1189         LOCK(&acache->lock);
1190
1191         ATRACE("shutdown");
1192
1193         if (!acache->shutting_down) {
1194                 isc_event_t *event;
1195                 dns_acache_t *acache_evarg = NULL;
1196
1197                 INSIST(!acache->cevent_sent);
1198
1199                 acache->shutting_down = ISC_TRUE;
1200
1201                 isc_mem_setwater(acache->mctx, NULL, NULL, 0, 0);
1202
1203                 /*
1204                  * Self attach the object in order to prevent it from being
1205                  * destroyed while waiting for the event.
1206                  */
1207                 dns_acache_attach(acache, &acache_evarg);
1208                 event = &acache->cevent;
1209                 event->ev_arg = acache_evarg;
1210                 isc_task_send(acache->task, &event);
1211                 acache->cevent_sent = ISC_TRUE;
1212         }
1213
1214         UNLOCK(&acache->lock);
1215 }
1216
1217 isc_result_t
1218 dns_acache_setdb(dns_acache_t *acache, dns_db_t *db) {
1219         int bucket;
1220         dbentry_t *dbentry;
1221         isc_result_t result = ISC_R_SUCCESS;
1222
1223         REQUIRE(DNS_ACACHE_VALID(acache));
1224         REQUIRE(db != NULL);
1225
1226         ATRACE("setdb");
1227
1228         LOCK(&acache->lock);
1229
1230         dbentry = NULL;
1231         result = finddbent(acache, db, &dbentry);
1232         if (result == ISC_R_SUCCESS) {
1233                 result = ISC_R_EXISTS;
1234                 goto end;
1235         }
1236         result = ISC_R_SUCCESS;
1237
1238         dbentry = isc_mem_get(acache->mctx, sizeof(*dbentry));
1239         if (dbentry == NULL) {
1240                 result = ISC_R_NOMEMORY;
1241                 goto end;
1242         }
1243
1244         ISC_LINK_INIT(dbentry, link);
1245         ISC_LIST_INIT(dbentry->originlist);
1246         ISC_LIST_INIT(dbentry->referlist);
1247
1248         dbentry->db = NULL;
1249         dns_db_attach(db, &dbentry->db);
1250
1251         bucket = isc_hash_calc((const unsigned char *)&db,
1252                                sizeof(db), ISC_TRUE) % DBBUCKETS;
1253
1254         ISC_LIST_APPEND(acache->dbbucket[bucket], dbentry, link);
1255
1256         acache->dbentries++;
1257
1258  end:
1259         UNLOCK(&acache->lock);
1260
1261         return (result);
1262 }
1263
1264 isc_result_t
1265 dns_acache_putdb(dns_acache_t *acache, dns_db_t *db) {
1266         int bucket;
1267         isc_result_t result;
1268         dbentry_t *dbentry;
1269         dns_acacheentry_t *entry;
1270
1271         REQUIRE(DNS_ACACHE_VALID(acache));
1272         REQUIRE(db != NULL);
1273
1274         ATRACE("putdb");
1275
1276         LOCK(&acache->lock);
1277
1278         dbentry = NULL;
1279         result = finddbent(acache, db, &dbentry);
1280         if (result != ISC_R_SUCCESS) {
1281                 /*
1282                  * The entry may have not been created due to memory shortage.
1283                  */
1284                 UNLOCK(&acache->lock);
1285                 return (ISC_R_NOTFOUND);
1286         }
1287
1288         /*
1289          * Release corresponding cache entries: for each entry, release all
1290          * links the entry has, and then callback to the entry holder (if any).
1291          * If no other external references exist (this can happen if the
1292          * original holder has canceled callback,) destroy it here.
1293          */
1294         while ((entry = ISC_LIST_HEAD(dbentry->originlist)) != NULL) {
1295                 ACACHE_LOCK(&acache->entrylocks[entry->locknum],
1296                             isc_rwlocktype_write);
1297
1298                 /*
1299                  * Releasing olink first would avoid finddbent() in
1300                  * unlink_dbentries().
1301                  */
1302                 ISC_LIST_UNLINK(dbentry->originlist, entry, olink);
1303                 if (acache->cleaner.current_entry != entry)
1304                         ISC_LIST_UNLINK(acache->entries, entry, link);
1305                 unlink_dbentries(acache, entry);
1306
1307                 if (entry->callback != NULL)
1308                         (entry->callback)(entry, &entry->cbarg);
1309                 entry->callback = NULL;
1310
1311                 ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
1312                               isc_rwlocktype_write);
1313
1314                 if (acache->cleaner.current_entry != entry)
1315                         dns_acache_detachentry(&entry);
1316         }
1317         while ((entry = ISC_LIST_HEAD(dbentry->referlist)) != NULL) {
1318                 ACACHE_LOCK(&acache->entrylocks[entry->locknum],
1319                             isc_rwlocktype_write);
1320
1321                 ISC_LIST_UNLINK(dbentry->referlist, entry, rlink);
1322                 if (acache->cleaner.current_entry != entry)
1323                         ISC_LIST_UNLINK(acache->entries, entry, link);
1324                 unlink_dbentries(acache, entry);
1325
1326                 if (entry->callback != NULL)
1327                         (entry->callback)(entry, &entry->cbarg);
1328                 entry->callback = NULL;
1329
1330                 ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
1331                               isc_rwlocktype_write);
1332
1333                 if (acache->cleaner.current_entry != entry)
1334                         dns_acache_detachentry(&entry);
1335         }
1336
1337         INSIST(ISC_LIST_EMPTY(dbentry->originlist) &&
1338                ISC_LIST_EMPTY(dbentry->referlist));
1339
1340         bucket = isc_hash_calc((const unsigned char *)&db,
1341                                sizeof(db), ISC_TRUE) % DBBUCKETS;
1342         ISC_LIST_UNLINK(acache->dbbucket[bucket], dbentry, link);
1343         dns_db_detach(&dbentry->db);
1344
1345         isc_mem_put(acache->mctx, dbentry, sizeof(*dbentry));
1346
1347         acache->dbentries--;
1348
1349         acache->stats.deleted++;
1350
1351         UNLOCK(&acache->lock);
1352
1353         return (ISC_R_SUCCESS);
1354 }
1355
1356 isc_result_t
1357 dns_acache_createentry(dns_acache_t *acache, dns_db_t *origdb,
1358                        void (*callback)(dns_acacheentry_t *, void **),
1359                        void *cbarg, dns_acacheentry_t **entryp)
1360 {
1361         dns_acacheentry_t *newentry;
1362         isc_result_t result;
1363         isc_uint32_t r;
1364
1365         REQUIRE(DNS_ACACHE_VALID(acache));
1366         REQUIRE(entryp != NULL && *entryp == NULL);
1367         REQUIRE(origdb != NULL);
1368
1369         /* 
1370          * Should we exceed our memory limit for some reason (for 
1371          * example, if the cleaner does not run aggressively enough), 
1372          * then we will not create additional entries.
1373          *
1374          * XXXSK: It might be better to lock the acache->cleaner->lock,
1375          * but locking may be an expensive bottleneck. If we misread 
1376          * the value, we will occasionally refuse to create a few 
1377          * cache entries, or create a few that we should not. I do not
1378          * expect this to happen often, and it will not have very bad
1379          * effects when it does. So no lock for now.
1380          */
1381         if (acache->cleaner.overmem) {
1382                 acache->stats.overmem_nocreates++; /* XXXSK danger: unlocked! */
1383                 return (ISC_R_NORESOURCES);
1384         }
1385
1386         newentry = isc_mem_get(acache->mctx, sizeof(*newentry));
1387         if (newentry == NULL) {
1388                 acache->stats.nomem++;  /* XXXMLG danger: unlocked! */
1389                 return (ISC_R_NOMEMORY);
1390         }
1391
1392         isc_random_get(&r);
1393         newentry->locknum = r % DEFAULT_ACACHE_ENTRY_LOCK_COUNT;
1394         
1395         result = isc_refcount_init(&newentry->references, 1);
1396         if (result != ISC_R_SUCCESS) {
1397                 isc_mem_put(acache->mctx, newentry, sizeof(*newentry));
1398                 return (result);
1399         };
1400
1401         ISC_LINK_INIT(newentry, link);
1402         ISC_LINK_INIT(newentry, olink);
1403         ISC_LINK_INIT(newentry, rlink);
1404
1405         newentry->acache = NULL;
1406         dns_acache_attach(acache, &newentry->acache);
1407
1408         newentry->zone = NULL;
1409         newentry->db = NULL;
1410         newentry->version = NULL;
1411         newentry->node = NULL;
1412         newentry->foundname = NULL;
1413
1414         newentry->callback = callback;
1415         newentry->cbarg = cbarg;
1416         newentry->origdb = NULL;
1417         dns_db_attach(origdb, &newentry->origdb);
1418
1419         isc_stdtime_get(&newentry->lastused);
1420
1421         newentry->magic = ACACHEENTRY_MAGIC;
1422
1423         *entryp = newentry;
1424
1425         return (ISC_R_SUCCESS);
1426 }
1427
1428 isc_result_t
1429 dns_acache_getentry(dns_acacheentry_t *entry, dns_zone_t **zonep,
1430                     dns_db_t **dbp, dns_dbversion_t **versionp,
1431                     dns_dbnode_t **nodep, dns_name_t *fname,
1432                     dns_message_t *msg, isc_stdtime_t now)
1433 {
1434         isc_result_t result = ISC_R_SUCCESS;
1435         dns_rdataset_t *erdataset;
1436         isc_stdtime32_t now32;
1437         dns_acache_t *acache;
1438         int locknum;
1439
1440         REQUIRE(DNS_ACACHEENTRY_VALID(entry));
1441         REQUIRE(zonep == NULL || *zonep == NULL);
1442         REQUIRE(dbp != NULL && *dbp == NULL);
1443         REQUIRE(versionp != NULL && *versionp == NULL);
1444         REQUIRE(nodep != NULL && *nodep == NULL);
1445         REQUIRE(fname != NULL);
1446         REQUIRE(msg != NULL);
1447         acache = entry->acache;
1448         REQUIRE(DNS_ACACHE_VALID(acache));
1449
1450         locknum = entry->locknum;
1451         ACACHE_LOCK(&acache->entrylocks[locknum], isc_rwlocktype_read);
1452
1453         isc_stdtime_convert32(now, &now32);
1454         acache_storetime(entry, now32);
1455
1456         if (entry->zone != NULL && zonep != NULL)
1457                 dns_zone_attach(entry->zone, zonep);
1458
1459         if (entry->db == NULL) {
1460                 *dbp = NULL;
1461                 *versionp = NULL;
1462         } else {
1463                 dns_db_attach(entry->db, dbp);
1464                 dns_db_attachversion(entry->db, entry->version, versionp);
1465         }
1466         if (entry->node == NULL)
1467                 *nodep = NULL;
1468         else {
1469                 dns_db_attachnode(entry->db, entry->node, nodep);
1470
1471                 INSIST(entry->foundname != NULL);
1472                 dns_name_copy(entry->foundname, fname, NULL);
1473                 for (erdataset = ISC_LIST_HEAD(entry->foundname->list);
1474                      erdataset != NULL;
1475                      erdataset = ISC_LIST_NEXT(erdataset, link)) {
1476                         dns_rdataset_t *ardataset;
1477
1478                         ardataset = NULL;
1479                         result = dns_message_gettemprdataset(msg, &ardataset);
1480                         if (result != ISC_R_SUCCESS) {
1481                                 ACACHE_UNLOCK(&acache->entrylocks[locknum],
1482                                               isc_rwlocktype_read);
1483                                 goto fail;
1484                         }
1485
1486                         /*
1487                          * XXXJT: if we simply clone the rdataset, we'll get
1488                          * lost wrt cyclic ordering.  We'll need an additional
1489                          * trick to get the latest counter from the original
1490                          * header.
1491                          */
1492                         dns_rdataset_init(ardataset);
1493                         dns_rdataset_clone(erdataset, ardataset);
1494                         ISC_LIST_APPEND(fname->list, ardataset, link);
1495                 }
1496         }
1497
1498         entry->acache->stats.hits++; /* XXXMLG danger: unlocked! */
1499         entry->acache->stats.queries++;
1500
1501         ACACHE_UNLOCK(&acache->entrylocks[locknum], isc_rwlocktype_read);
1502
1503         return (result);
1504
1505   fail:
1506         while ((erdataset = ISC_LIST_HEAD(fname->list)) != NULL) {
1507                 ISC_LIST_UNLINK(fname->list, erdataset, link);
1508                 dns_rdataset_disassociate(erdataset);
1509                 dns_message_puttemprdataset(msg, &erdataset);
1510         }
1511         if (*nodep != NULL)
1512                 dns_db_detachnode(*dbp, nodep);
1513         if (*versionp != NULL)
1514                 dns_db_closeversion(*dbp, versionp, ISC_FALSE);
1515         if (*dbp != NULL)
1516                 dns_db_detach(dbp);
1517         if (zonep != NULL && *zonep != NULL)
1518                 dns_zone_detach(zonep);
1519
1520         return (result);
1521 }
1522
1523 isc_result_t
1524 dns_acache_setentry(dns_acache_t *acache, dns_acacheentry_t *entry,
1525                     dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version,
1526                     dns_dbnode_t *node, dns_name_t *fname)
1527 {
1528         isc_result_t result;
1529         dbentry_t *odbent;
1530         dbentry_t *rdbent = NULL;
1531         isc_boolean_t close_version = ISC_FALSE;
1532         dns_acacheentry_t *dummy_entry = NULL;
1533
1534         REQUIRE(DNS_ACACHE_VALID(acache));
1535         REQUIRE(DNS_ACACHEENTRY_VALID(entry));
1536
1537         LOCK(&acache->lock);    /* XXX: need to lock it here for ordering */
1538         ACACHE_LOCK(&acache->entrylocks[entry->locknum], isc_rwlocktype_write);
1539
1540         /* Set zone */
1541         if (zone != NULL)
1542                 dns_zone_attach(zone, &entry->zone);
1543         /* Set DB */
1544         if (db != NULL)
1545                 dns_db_attach(db, &entry->db);
1546         /*
1547          * Set DB version.  If the version is not given by the caller,
1548          * which is the case for glue or cache DBs, use the current version.
1549          */
1550         if (version == NULL) {
1551                 if (db != NULL) {
1552                         dns_db_currentversion(db, &version);
1553                         close_version = ISC_TRUE;
1554                 }
1555         }
1556         if (version != NULL) {
1557                 INSIST(db != NULL);
1558                 dns_db_attachversion(db, version, &entry->version);
1559         }
1560         if (close_version)
1561                 dns_db_closeversion(db, &version, ISC_FALSE);
1562         /* Set DB node. */
1563         if (node != NULL) {
1564                 INSIST(db != NULL);
1565                 dns_db_attachnode(db, node, &entry->node);
1566         }
1567
1568         /*
1569          * Set list of the corresponding rdatasets, if given.
1570          * To minimize the overhead and memory consumption, we'll do this for
1571          * positive cache only, in which case the DB node is non NULL.
1572          * We do not want to cache incomplete information, so give up the
1573          * entire entry when a memory shortage happen during the process.
1574          */
1575         if (node != NULL) {
1576                 dns_rdataset_t *ardataset, *crdataset;
1577
1578                 entry->foundname = isc_mem_get(acache->mctx,
1579                                                sizeof(*entry->foundname));
1580
1581                 if (entry->foundname == NULL) {
1582                         result = ISC_R_NOMEMORY;
1583                         goto fail;
1584                 }
1585                 dns_name_init(entry->foundname, NULL);
1586                 result = dns_name_dup(fname, acache->mctx,
1587                                       entry->foundname);
1588                 if (result != ISC_R_SUCCESS)
1589                         goto fail;
1590
1591                 for (ardataset = ISC_LIST_HEAD(fname->list);
1592                      ardataset != NULL;
1593                      ardataset = ISC_LIST_NEXT(ardataset, link)) {
1594                         crdataset = isc_mem_get(acache->mctx,
1595                                                 sizeof(*crdataset));
1596                         if (crdataset == NULL) {
1597                                 result = ISC_R_NOMEMORY;
1598                                 goto fail;
1599                         }
1600
1601                         dns_rdataset_init(crdataset);
1602                         dns_rdataset_clone(ardataset, crdataset);
1603                         ISC_LIST_APPEND(entry->foundname->list, crdataset,
1604                                         link);
1605                 }
1606         }
1607
1608         odbent = NULL;
1609         result = finddbent(acache, entry->origdb, &odbent);
1610         if (result != ISC_R_SUCCESS)
1611                 goto fail;
1612         if (db != NULL) {
1613                 rdbent = NULL;
1614                 result = finddbent(acache, db, &rdbent);
1615                 if (result != ISC_R_SUCCESS)
1616                         goto fail;
1617         }
1618
1619         ISC_LIST_APPEND(acache->entries, entry, link);
1620         ISC_LIST_APPEND(odbent->originlist, entry, olink);
1621         if (rdbent != NULL)
1622                 ISC_LIST_APPEND(rdbent->referlist, entry, rlink);
1623
1624         /*
1625          * The additional cache needs an implicit reference to entries in its
1626          * link.
1627          */
1628         dns_acache_attachentry(entry, &dummy_entry);
1629
1630         ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
1631                       isc_rwlocktype_write);
1632
1633         acache->stats.adds++;
1634         UNLOCK(&acache->lock);
1635
1636         return (ISC_R_SUCCESS);
1637
1638  fail:
1639         clear_entry(acache, entry);
1640
1641         ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
1642                       isc_rwlocktype_write);
1643         UNLOCK(&acache->lock);
1644
1645         return (result);
1646 }
1647
1648 void
1649 dns_acache_cancelentry(dns_acacheentry_t *entry) {
1650         dns_acache_t *acache = entry->acache;
1651
1652         REQUIRE(DNS_ACACHEENTRY_VALID(entry));
1653         INSIST(DNS_ACACHE_VALID(acache));
1654
1655         LOCK(&acache->lock);
1656         ACACHE_LOCK(&acache->entrylocks[entry->locknum], isc_rwlocktype_write);
1657
1658         /*
1659          * Release dependencies stored in this entry as much as possible.
1660          * The main link cannot be released, since the acache object has
1661          * a reference to this entry; the empty entry will be released in
1662          * the next cleaning action.
1663          */
1664         unlink_dbentries(acache, entry);
1665         clear_entry(entry->acache, entry);
1666
1667         entry->callback = NULL;
1668         entry->cbarg = NULL;
1669
1670         ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
1671                       isc_rwlocktype_write);
1672         UNLOCK(&acache->lock);
1673 }
1674
1675 void
1676 dns_acache_attachentry(dns_acacheentry_t *source,
1677                        dns_acacheentry_t **targetp)
1678 {
1679         REQUIRE(DNS_ACACHEENTRY_VALID(source));
1680         REQUIRE(targetp != NULL && *targetp == NULL);
1681
1682         isc_refcount_increment(&source->references, NULL);
1683
1684         *targetp = source;
1685 }
1686
1687 void
1688 dns_acache_detachentry(dns_acacheentry_t **entryp) {
1689         dns_acacheentry_t *entry;
1690         unsigned int refs;
1691
1692         REQUIRE(entryp != NULL && DNS_ACACHEENTRY_VALID(*entryp));
1693         entry = *entryp;
1694
1695         isc_refcount_decrement(&entry->references, &refs);
1696
1697         /*
1698          * If there are no references to the entry, the entry must have been
1699          * unlinked and can be destroyed safely.
1700          */
1701         if (refs == 0) {
1702                 INSIST(!ISC_LINK_LINKED(entry, link));
1703                 (*entryp)->acache->stats.deleted++;
1704                 destroy_entry(entry);
1705         }
1706
1707         *entryp = NULL;
1708 }
1709
1710 void
1711 dns_acache_setcleaninginterval(dns_acache_t *acache, unsigned int t) {
1712         isc_interval_t interval;
1713         isc_result_t result;
1714
1715         REQUIRE(DNS_ACACHE_VALID(acache));
1716
1717         ATRACE("dns_acache_setcleaninginterval");
1718
1719         LOCK(&acache->lock);
1720
1721         /*
1722          * It may be the case that the acache has already shut down.
1723          * If so, it has no timer.  (Not sure if this can really happen.)
1724          */
1725         if (acache->cleaner.cleaning_timer == NULL)
1726                 goto unlock;
1727
1728         acache->cleaner.cleaning_interval = t;
1729
1730         if (t == 0) {
1731                 result = isc_timer_reset(acache->cleaner.cleaning_timer,
1732                                          isc_timertype_inactive,
1733                                          NULL, NULL, ISC_TRUE);
1734         } else {
1735                 isc_interval_set(&interval, acache->cleaner.cleaning_interval,
1736                                  0);
1737                 result = isc_timer_reset(acache->cleaner.cleaning_timer,
1738                                          isc_timertype_ticker,
1739                                          NULL, &interval, ISC_FALSE);
1740         }
1741         if (result != ISC_R_SUCCESS)    
1742                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1743                               DNS_LOGMODULE_ACACHE, ISC_LOG_WARNING,
1744                               "could not set acache cleaning interval: %s",
1745                               isc_result_totext(result));
1746         else
1747                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1748                               DNS_LOGMODULE_ACACHE, ISC_LOG_NOTICE,
1749                               "acache %p cleaning interval set to %d.",
1750                               acache, t);
1751
1752  unlock:
1753         UNLOCK(&acache->lock);
1754 }
1755
1756 /*
1757  * This function was derived from cache.c:dns_cache_setcachesize().  See the
1758  * function for more details about the logic.
1759  */
1760 void
1761 dns_acache_setcachesize(dns_acache_t *acache, isc_uint32_t size) {
1762         isc_uint32_t lowater;
1763         isc_uint32_t hiwater;
1764
1765         REQUIRE(DNS_ACACHE_VALID(acache));
1766
1767         if (size != 0 && size < DNS_ACACHE_MINSIZE)
1768                 size = DNS_ACACHE_MINSIZE;
1769
1770         hiwater = size - (size >> 3);
1771         lowater = size - (size >> 2);
1772
1773         if (size == 0 || hiwater == 0 || lowater == 0)
1774                 isc_mem_setwater(acache->mctx, water, acache, 0, 0);
1775         else
1776                 isc_mem_setwater(acache->mctx, water, acache,
1777                                  hiwater, lowater);
1778 }