]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - contrib/bind9/lib/dns/cache.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / contrib / bind9 / lib / dns / cache.c
1 /*
2  * Copyright (C) 2004-2009, 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 1999-2003  Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15  * PERFORMANCE OF THIS SOFTWARE.
16  */
17
18 /* $Id$ */
19
20 /*! \file */
21
22 #include <config.h>
23
24 #include <isc/mem.h>
25 #include <isc/string.h>
26 #include <isc/task.h>
27 #include <isc/time.h>
28 #include <isc/timer.h>
29 #include <isc/util.h>
30
31 #include <dns/cache.h>
32 #include <dns/db.h>
33 #include <dns/dbiterator.h>
34 #include <dns/events.h>
35 #include <dns/lib.h>
36 #include <dns/log.h>
37 #include <dns/masterdump.h>
38 #include <dns/rdata.h>
39 #include <dns/rdataset.h>
40 #include <dns/rdatasetiter.h>
41 #include <dns/result.h>
42
43 #include "rbtdb.h"
44
45 #define CACHE_MAGIC             ISC_MAGIC('$', '$', '$', '$')
46 #define VALID_CACHE(cache)      ISC_MAGIC_VALID(cache, CACHE_MAGIC)
47
48 /*!
49  * Control incremental cleaning.
50  * DNS_CACHE_MINSIZE is how many bytes is the floor for dns_cache_setcachesize().
51  * See also DNS_CACHE_CLEANERINCREMENT
52  */
53 #define DNS_CACHE_MINSIZE       2097152 /*%< Bytes.  2097152 = 2 MB */
54 /*!
55  * Control incremental cleaning.
56  * CLEANERINCREMENT is how many nodes are examined in one pass.
57  * See also DNS_CACHE_MINSIZE
58  */
59 #define DNS_CACHE_CLEANERINCREMENT      1000U   /*%< Number of nodes. */
60
61 /***
62  ***    Types
63  ***/
64
65 /*
66  * A cache_cleaner_t encapsulates the state of the periodic
67  * cache cleaning.
68  */
69
70 typedef struct cache_cleaner cache_cleaner_t;
71
72 typedef enum {
73         cleaner_s_idle, /*%< Waiting for cleaning-interval to expire. */
74         cleaner_s_busy, /*%< Currently cleaning. */
75         cleaner_s_done  /*%< Freed enough memory after being overmem. */
76 } cleaner_state_t;
77
78 /*
79  * Convenience macros for comprehensive assertion checking.
80  */
81 #define CLEANER_IDLE(c) ((c)->state == cleaner_s_idle && \
82                          (c)->resched_event != NULL)
83 #define CLEANER_BUSY(c) ((c)->state == cleaner_s_busy && \
84                          (c)->iterator != NULL && \
85                          (c)->resched_event == NULL)
86
87 /*%
88  * Accesses to a cache cleaner object are synchronized through
89  * task/event serialization, or locked from the cache object.
90  */
91 struct cache_cleaner {
92         isc_mutex_t     lock;
93         /*%<
94          * Locks overmem_event, overmem.  Note: never allocate memory
95          * while holding this lock - that could lead to deadlock since
96          * the lock is take by water() which is called from the memory
97          * allocator.
98          */
99
100         dns_cache_t     *cache;
101         isc_task_t      *task;
102         unsigned int    cleaning_interval; /*% The cleaning-interval from
103                                               named.conf, in seconds. */
104         isc_timer_t     *cleaning_timer;
105         isc_event_t     *resched_event; /*% Sent by cleaner task to
106                                            itself to reschedule */
107         isc_event_t     *overmem_event;
108
109         dns_dbiterator_t *iterator;
110         unsigned int    increment;      /*% Number of names to
111                                            clean in one increment */
112         cleaner_state_t state;          /*% Idle/Busy. */
113         isc_boolean_t   overmem;        /*% The cache is in an overmem state. */
114         isc_boolean_t    replaceiterator;
115 };
116
117 /*%
118  * The actual cache object.
119  */
120
121 struct dns_cache {
122         /* Unlocked. */
123         unsigned int            magic;
124         isc_mutex_t             lock;
125         isc_mutex_t             filelock;
126         isc_mem_t               *mctx;          /* Main cache memory */
127         isc_mem_t               *hmctx;         /* Heap memory */
128         char                    *name;
129
130         /* Locked by 'lock'. */
131         int                     references;
132         int                     live_tasks;
133         dns_rdataclass_t        rdclass;
134         dns_db_t                *db;
135         cache_cleaner_t         cleaner;
136         char                    *db_type;
137         int                     db_argc;
138         char                    **db_argv;
139         isc_uint32_t            size;
140
141         /* Locked by 'filelock'. */
142         char                    *filename;
143         /* Access to the on-disk cache file is also locked by 'filelock'. */
144 };
145
146 /***
147  ***    Functions
148  ***/
149
150 static isc_result_t
151 cache_cleaner_init(dns_cache_t *cache, isc_taskmgr_t *taskmgr,
152                    isc_timermgr_t *timermgr, cache_cleaner_t *cleaner);
153
154 static void
155 cleaning_timer_action(isc_task_t *task, isc_event_t *event);
156
157 static void
158 incremental_cleaning_action(isc_task_t *task, isc_event_t *event);
159
160 static void
161 cleaner_shutdown_action(isc_task_t *task, isc_event_t *event);
162
163 static void
164 overmem_cleaning_action(isc_task_t *task, isc_event_t *event);
165
166 static inline isc_result_t
167 cache_create_db(dns_cache_t *cache, dns_db_t **db) {
168         return (dns_db_create(cache->mctx, cache->db_type, dns_rootname,
169                               dns_dbtype_cache, cache->rdclass,
170                               cache->db_argc, cache->db_argv, db));
171 }
172
173 isc_result_t
174 dns_cache_create(isc_mem_t *cmctx, isc_taskmgr_t *taskmgr,
175                  isc_timermgr_t *timermgr, dns_rdataclass_t rdclass,
176                  const char *db_type, unsigned int db_argc, char **db_argv,
177                  dns_cache_t **cachep)
178 {
179         return (dns_cache_create3(cmctx, cmctx, taskmgr, timermgr, rdclass, "",
180                                   db_type, db_argc, db_argv, cachep));
181 }
182
183 isc_result_t
184 dns_cache_create2(isc_mem_t *cmctx, isc_taskmgr_t *taskmgr,
185                   isc_timermgr_t *timermgr, dns_rdataclass_t rdclass,
186                   const char *cachename, const char *db_type,
187                   unsigned int db_argc, char **db_argv, dns_cache_t **cachep)
188 {
189         return (dns_cache_create3(cmctx, cmctx, taskmgr, timermgr, rdclass,
190                                   cachename, db_type, db_argc, db_argv,
191                                   cachep));
192 }
193
194 isc_result_t
195 dns_cache_create3(isc_mem_t *cmctx, isc_mem_t *hmctx, isc_taskmgr_t *taskmgr,
196                   isc_timermgr_t *timermgr, dns_rdataclass_t rdclass,
197                   const char *cachename, const char *db_type,
198                   unsigned int db_argc, char **db_argv, dns_cache_t **cachep)
199 {
200         isc_result_t result;
201         dns_cache_t *cache;
202         int i, extra = 0;
203         isc_task_t *dbtask;
204
205         REQUIRE(cachep != NULL);
206         REQUIRE(*cachep == NULL);
207         REQUIRE(cmctx != NULL);
208         REQUIRE(hmctx != NULL);
209         REQUIRE(cachename != NULL);
210
211         cache = isc_mem_get(cmctx, sizeof(*cache));
212         if (cache == NULL)
213                 return (ISC_R_NOMEMORY);
214
215         cache->mctx = cache->hmctx = NULL;
216         isc_mem_attach(cmctx, &cache->mctx);
217         isc_mem_attach(hmctx, &cache->hmctx);
218
219         cache->name = NULL;
220         if (cachename != NULL) {
221                 cache->name = isc_mem_strdup(cmctx, cachename);
222                 if (cache->name == NULL) {
223                         result = ISC_R_NOMEMORY;
224                         goto cleanup_mem;
225                 }
226         }
227
228         result = isc_mutex_init(&cache->lock);
229         if (result != ISC_R_SUCCESS)
230                 goto cleanup_mem;
231
232         result = isc_mutex_init(&cache->filelock);
233         if (result != ISC_R_SUCCESS)
234                 goto cleanup_lock;
235
236         cache->references = 1;
237         cache->live_tasks = 0;
238         cache->rdclass = rdclass;
239
240         cache->db_type = isc_mem_strdup(cmctx, db_type);
241         if (cache->db_type == NULL) {
242                 result = ISC_R_NOMEMORY;
243                 goto cleanup_filelock;
244         }
245
246         /*
247          * For databases of type "rbt" we pass hmctx to dns_db_create()
248          * via cache->db_argv, followed by the rest of the arguments in
249          * db_argv (of which there really shouldn't be any).
250          */
251         if (strcmp(cache->db_type, "rbt") == 0)
252                 extra = 1;
253
254         cache->db_argc = db_argc + extra;
255         cache->db_argv = NULL;
256
257         if (cache->db_argc != 0) {
258                 cache->db_argv = isc_mem_get(cmctx,
259                                              cache->db_argc * sizeof(char *));
260                 if (cache->db_argv == NULL) {
261                         result = ISC_R_NOMEMORY;
262                         goto cleanup_dbtype;
263                 }
264
265                 for (i = 0; i < cache->db_argc; i++)
266                         cache->db_argv[i] = NULL;
267
268                 cache->db_argv[0] = (char *) hmctx;
269                 for (i = extra; i < cache->db_argc; i++) {
270                         cache->db_argv[i] = isc_mem_strdup(cmctx,
271                                                            db_argv[i - extra]);
272                         if (cache->db_argv[i] == NULL) {
273                                 result = ISC_R_NOMEMORY;
274                                 goto cleanup_dbargv;
275                         }
276                 }
277         }
278
279         /*
280          * Create the database
281          */
282         cache->db = NULL;
283         result = cache_create_db(cache, &cache->db);
284         if (result != ISC_R_SUCCESS)
285                 goto cleanup_dbargv;
286         if (taskmgr != NULL) {
287                 dbtask = NULL;
288                 result = isc_task_create(taskmgr, 1, &dbtask);
289                 if (result != ISC_R_SUCCESS)
290                         goto cleanup_db;
291                 dns_db_settask(cache->db, dbtask);
292                 isc_task_detach(&dbtask);
293         }
294
295         cache->filename = NULL;
296
297         cache->magic = CACHE_MAGIC;
298
299         /*
300          * RBT-type cache DB has its own mechanism of cache cleaning and doesn't
301          * need the control of the generic cleaner.
302          */
303         if (strcmp(db_type, "rbt") == 0)
304                 result = cache_cleaner_init(cache, NULL, NULL, &cache->cleaner);
305         else {
306                 result = cache_cleaner_init(cache, taskmgr, timermgr,
307                                             &cache->cleaner);
308         }
309         if (result != ISC_R_SUCCESS)
310                 goto cleanup_db;
311
312         *cachep = cache;
313         return (ISC_R_SUCCESS);
314
315  cleanup_db:
316         dns_db_detach(&cache->db);
317  cleanup_dbargv:
318         for (i = extra; i < cache->db_argc; i++)
319                 if (cache->db_argv[i] != NULL)
320                         isc_mem_free(cmctx, cache->db_argv[i]);
321         if (cache->db_argv != NULL)
322                 isc_mem_put(cmctx, cache->db_argv,
323                             cache->db_argc * sizeof(char *));
324  cleanup_dbtype:
325         isc_mem_free(cmctx, cache->db_type);
326  cleanup_filelock:
327         DESTROYLOCK(&cache->filelock);
328  cleanup_lock:
329         DESTROYLOCK(&cache->lock);
330  cleanup_mem:
331         if (cache->name != NULL)
332                 isc_mem_free(cmctx, cache->name);
333         isc_mem_detach(&cache->hmctx);
334         isc_mem_putanddetach(&cache->mctx, cache, sizeof(*cache));
335         return (result);
336 }
337
338 static void
339 cache_free(dns_cache_t *cache) {
340         int i;
341
342         REQUIRE(VALID_CACHE(cache));
343         REQUIRE(cache->references == 0);
344
345         isc_mem_setwater(cache->mctx, NULL, NULL, 0, 0);
346
347         if (cache->cleaner.task != NULL)
348                 isc_task_detach(&cache->cleaner.task);
349
350         if (cache->cleaner.overmem_event != NULL)
351                 isc_event_free(&cache->cleaner.overmem_event);
352
353         if (cache->cleaner.resched_event != NULL)
354                 isc_event_free(&cache->cleaner.resched_event);
355
356         if (cache->cleaner.iterator != NULL)
357                 dns_dbiterator_destroy(&cache->cleaner.iterator);
358
359         DESTROYLOCK(&cache->cleaner.lock);
360
361         if (cache->filename) {
362                 isc_mem_free(cache->mctx, cache->filename);
363                 cache->filename = NULL;
364         }
365
366         if (cache->db != NULL)
367                 dns_db_detach(&cache->db);
368
369         if (cache->db_argv != NULL) {
370                 /*
371                  * We don't free db_argv[0] in "rbt" cache databases
372                  * as it's a pointer to hmctx
373                  */
374                 int extra = 0;
375                 if (strcmp(cache->db_type, "rbt") == 0)
376                         extra = 1;
377                 for (i = extra; i < cache->db_argc; i++)
378                         if (cache->db_argv[i] != NULL)
379                                 isc_mem_free(cache->mctx, cache->db_argv[i]);
380                 isc_mem_put(cache->mctx, cache->db_argv,
381                             cache->db_argc * sizeof(char *));
382         }
383
384         if (cache->db_type != NULL)
385                 isc_mem_free(cache->mctx, cache->db_type);
386
387         if (cache->name != NULL)
388                 isc_mem_free(cache->mctx, cache->name);
389
390         DESTROYLOCK(&cache->lock);
391         DESTROYLOCK(&cache->filelock);
392
393         cache->magic = 0;
394         isc_mem_detach(&cache->hmctx);
395         isc_mem_putanddetach(&cache->mctx, cache, sizeof(*cache));
396 }
397
398
399 void
400 dns_cache_attach(dns_cache_t *cache, dns_cache_t **targetp) {
401
402         REQUIRE(VALID_CACHE(cache));
403         REQUIRE(targetp != NULL && *targetp == NULL);
404
405         LOCK(&cache->lock);
406         cache->references++;
407         UNLOCK(&cache->lock);
408
409         *targetp = cache;
410 }
411
412 void
413 dns_cache_detach(dns_cache_t **cachep) {
414         dns_cache_t *cache;
415         isc_boolean_t free_cache = ISC_FALSE;
416
417         REQUIRE(cachep != NULL);
418         cache = *cachep;
419         REQUIRE(VALID_CACHE(cache));
420
421         LOCK(&cache->lock);
422         REQUIRE(cache->references > 0);
423         cache->references--;
424         if (cache->references == 0) {
425                 cache->cleaner.overmem = ISC_FALSE;
426                 free_cache = ISC_TRUE;
427         }
428
429         *cachep = NULL;
430
431         if (free_cache) {
432                 /*
433                  * When the cache is shut down, dump it to a file if one is
434                  * specified.
435                  */
436                 isc_result_t result = dns_cache_dump(cache);
437                 if (result != ISC_R_SUCCESS)
438                         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
439                                       DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
440                                       "error dumping cache: %s ",
441                                       isc_result_totext(result));
442
443                 /*
444                  * If the cleaner task exists, let it free the cache.
445                  */
446                 if (cache->live_tasks > 0) {
447                         isc_task_shutdown(cache->cleaner.task);
448                         free_cache = ISC_FALSE;
449                 }
450         }
451
452         UNLOCK(&cache->lock);
453
454         if (free_cache)
455                 cache_free(cache);
456 }
457
458 void
459 dns_cache_attachdb(dns_cache_t *cache, dns_db_t **dbp) {
460         REQUIRE(VALID_CACHE(cache));
461         REQUIRE(dbp != NULL && *dbp == NULL);
462         REQUIRE(cache->db != NULL);
463
464         LOCK(&cache->lock);
465         dns_db_attach(cache->db, dbp);
466         UNLOCK(&cache->lock);
467
468 }
469
470 isc_result_t
471 dns_cache_setfilename(dns_cache_t *cache, const char *filename) {
472         char *newname;
473
474         REQUIRE(VALID_CACHE(cache));
475         REQUIRE(filename != NULL);
476
477         newname = isc_mem_strdup(cache->mctx, filename);
478         if (newname == NULL)
479                 return (ISC_R_NOMEMORY);
480
481         LOCK(&cache->filelock);
482         if (cache->filename)
483                 isc_mem_free(cache->mctx, cache->filename);
484         cache->filename = newname;
485         UNLOCK(&cache->filelock);
486
487         return (ISC_R_SUCCESS);
488 }
489
490 #ifdef BIND9
491 isc_result_t
492 dns_cache_load(dns_cache_t *cache) {
493         isc_result_t result;
494
495         REQUIRE(VALID_CACHE(cache));
496
497         if (cache->filename == NULL)
498                 return (ISC_R_SUCCESS);
499
500         LOCK(&cache->filelock);
501         result = dns_db_load(cache->db, cache->filename);
502         UNLOCK(&cache->filelock);
503
504         return (result);
505 }
506 #endif /* BIND9 */
507
508 isc_result_t
509 dns_cache_dump(dns_cache_t *cache) {
510 #ifdef BIND9
511         isc_result_t result;
512 #endif
513
514         REQUIRE(VALID_CACHE(cache));
515
516         if (cache->filename == NULL)
517                 return (ISC_R_SUCCESS);
518
519 #ifdef BIND9
520         LOCK(&cache->filelock);
521         result = dns_master_dump(cache->mctx, cache->db, NULL,
522                                  &dns_master_style_cache, cache->filename);
523         UNLOCK(&cache->filelock);
524         return (result);
525 #else
526         return (ISC_R_NOTIMPLEMENTED);
527 #endif
528
529 }
530
531 void
532 dns_cache_setcleaninginterval(dns_cache_t *cache, unsigned int t) {
533         isc_interval_t interval;
534         isc_result_t result;
535
536         LOCK(&cache->lock);
537
538         /*
539          * It may be the case that the cache has already shut down.
540          * If so, it has no timer.
541          */
542         if (cache->cleaner.cleaning_timer == NULL)
543                 goto unlock;
544
545         cache->cleaner.cleaning_interval = t;
546
547         if (t == 0) {
548                 result = isc_timer_reset(cache->cleaner.cleaning_timer,
549                                          isc_timertype_inactive,
550                                          NULL, NULL, ISC_TRUE);
551         } else {
552                 isc_interval_set(&interval, cache->cleaner.cleaning_interval,
553                                  0);
554                 result = isc_timer_reset(cache->cleaner.cleaning_timer,
555                                          isc_timertype_ticker,
556                                          NULL, &interval, ISC_FALSE);
557         }
558         if (result != ISC_R_SUCCESS)
559                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
560                               DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
561                               "could not set cache cleaning interval: %s",
562                               isc_result_totext(result));
563
564  unlock:
565         UNLOCK(&cache->lock);
566 }
567
568 unsigned int
569 dns_cache_getcleaninginterval(dns_cache_t *cache) {
570         unsigned int t;
571
572         REQUIRE(VALID_CACHE(cache));
573
574         LOCK(&cache->lock);
575         t = cache->cleaner.cleaning_interval;
576         UNLOCK(&cache->lock);
577
578         return (t);
579 }
580
581 const char *
582 dns_cache_getname(dns_cache_t *cache) {
583         REQUIRE(VALID_CACHE(cache));
584
585         return (cache->name);
586 }
587
588 /*
589  * Initialize the cache cleaner object at *cleaner.
590  * Space for the object must be allocated by the caller.
591  */
592
593 static isc_result_t
594 cache_cleaner_init(dns_cache_t *cache, isc_taskmgr_t *taskmgr,
595                    isc_timermgr_t *timermgr, cache_cleaner_t *cleaner)
596 {
597         isc_result_t result;
598
599         result = isc_mutex_init(&cleaner->lock);
600         if (result != ISC_R_SUCCESS)
601                 goto fail;
602
603         cleaner->increment = DNS_CACHE_CLEANERINCREMENT;
604         cleaner->state = cleaner_s_idle;
605         cleaner->cache = cache;
606         cleaner->iterator = NULL;
607         cleaner->overmem = ISC_FALSE;
608         cleaner->replaceiterator = ISC_FALSE;
609
610         cleaner->task = NULL;
611         cleaner->cleaning_timer = NULL;
612         cleaner->resched_event = NULL;
613         cleaner->overmem_event = NULL;
614         cleaner->cleaning_interval = 0; /* Initially turned off. */
615
616         result = dns_db_createiterator(cleaner->cache->db, ISC_FALSE,
617                                        &cleaner->iterator);
618         if (result != ISC_R_SUCCESS)
619                 goto cleanup;
620
621         if (taskmgr != NULL && timermgr != NULL) {
622                 result = isc_task_create(taskmgr, 1, &cleaner->task);
623                 if (result != ISC_R_SUCCESS) {
624                         UNEXPECTED_ERROR(__FILE__, __LINE__,
625                                          "isc_task_create() failed: %s",
626                                          dns_result_totext(result));
627                         result = ISC_R_UNEXPECTED;
628                         goto cleanup;
629                 }
630                 cleaner->cache->live_tasks++;
631                 isc_task_setname(cleaner->task, "cachecleaner", cleaner);
632
633                 result = isc_task_onshutdown(cleaner->task,
634                                              cleaner_shutdown_action, cache);
635                 if (result != ISC_R_SUCCESS) {
636                         UNEXPECTED_ERROR(__FILE__, __LINE__,
637                                          "cache cleaner: "
638                                          "isc_task_onshutdown() failed: %s",
639                                          dns_result_totext(result));
640                         goto cleanup;
641                 }
642
643                 result = isc_timer_create(timermgr, isc_timertype_inactive,
644                                            NULL, NULL, cleaner->task,
645                                            cleaning_timer_action, cleaner,
646                                            &cleaner->cleaning_timer);
647                 if (result != ISC_R_SUCCESS) {
648                         UNEXPECTED_ERROR(__FILE__, __LINE__,
649                                          "isc_timer_create() failed: %s",
650                                          dns_result_totext(result));
651                         result = ISC_R_UNEXPECTED;
652                         goto cleanup;
653                 }
654
655                 cleaner->resched_event =
656                         isc_event_allocate(cache->mctx, cleaner,
657                                            DNS_EVENT_CACHECLEAN,
658                                            incremental_cleaning_action,
659                                            cleaner, sizeof(isc_event_t));
660                 if (cleaner->resched_event == NULL) {
661                         result = ISC_R_NOMEMORY;
662                         goto cleanup;
663                 }
664
665                 cleaner->overmem_event =
666                         isc_event_allocate(cache->mctx, cleaner,
667                                            DNS_EVENT_CACHEOVERMEM,
668                                            overmem_cleaning_action,
669                                            cleaner, sizeof(isc_event_t));
670                 if (cleaner->overmem_event == NULL) {
671                         result = ISC_R_NOMEMORY;
672                         goto cleanup;
673                 }
674         }
675
676         return (ISC_R_SUCCESS);
677
678  cleanup:
679         if (cleaner->overmem_event != NULL)
680                 isc_event_free(&cleaner->overmem_event);
681         if (cleaner->resched_event != NULL)
682                 isc_event_free(&cleaner->resched_event);
683         if (cleaner->cleaning_timer != NULL)
684                 isc_timer_detach(&cleaner->cleaning_timer);
685         if (cleaner->task != NULL)
686                 isc_task_detach(&cleaner->task);
687         if (cleaner->iterator != NULL)
688                 dns_dbiterator_destroy(&cleaner->iterator);
689         DESTROYLOCK(&cleaner->lock);
690  fail:
691         return (result);
692 }
693
694 static void
695 begin_cleaning(cache_cleaner_t *cleaner) {
696         isc_result_t result = ISC_R_SUCCESS;
697
698         REQUIRE(CLEANER_IDLE(cleaner));
699
700         /*
701          * Create an iterator, if it does not already exist, and
702          * position it at the beginning of the cache.
703          */
704         if (cleaner->iterator == NULL)
705                 result = dns_db_createiterator(cleaner->cache->db, ISC_FALSE,
706                                                &cleaner->iterator);
707         if (result != ISC_R_SUCCESS)
708                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
709                               DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
710                               "cache cleaner could not create "
711                               "iterator: %s", isc_result_totext(result));
712         else {
713                 dns_dbiterator_setcleanmode(cleaner->iterator, ISC_TRUE);
714                 result = dns_dbiterator_first(cleaner->iterator);
715         }
716         if (result != ISC_R_SUCCESS) {
717                 /*
718                  * If the result is ISC_R_NOMORE, the database is empty,
719                  * so there is nothing to be cleaned.
720                  */
721                 if (result != ISC_R_NOMORE && cleaner->iterator != NULL) {
722                         UNEXPECTED_ERROR(__FILE__, __LINE__,
723                                          "cache cleaner: "
724                                          "dns_dbiterator_first() failed: %s",
725                                          dns_result_totext(result));
726                         dns_dbiterator_destroy(&cleaner->iterator);
727                 } else if (cleaner->iterator != NULL) {
728                         result = dns_dbiterator_pause(cleaner->iterator);
729                         RUNTIME_CHECK(result == ISC_R_SUCCESS);
730                 }
731         } else {
732                 /*
733                  * Pause the iterator to free its lock.
734                  */
735                 result = dns_dbiterator_pause(cleaner->iterator);
736                 RUNTIME_CHECK(result == ISC_R_SUCCESS);
737
738                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
739                               DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1),
740                               "begin cache cleaning, mem inuse %lu",
741                             (unsigned long)isc_mem_inuse(cleaner->cache->mctx));
742                 cleaner->state = cleaner_s_busy;
743                 isc_task_send(cleaner->task, &cleaner->resched_event);
744         }
745
746         return;
747 }
748
749 static void
750 end_cleaning(cache_cleaner_t *cleaner, isc_event_t *event) {
751         isc_result_t result;
752
753         REQUIRE(CLEANER_BUSY(cleaner));
754         REQUIRE(event != NULL);
755
756         result = dns_dbiterator_pause(cleaner->iterator);
757         if (result != ISC_R_SUCCESS)
758                 dns_dbiterator_destroy(&cleaner->iterator);
759
760         dns_cache_setcleaninginterval(cleaner->cache,
761                                       cleaner->cleaning_interval);
762
763         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
764                       ISC_LOG_DEBUG(1), "end cache cleaning, mem inuse %lu",
765                       (unsigned long)isc_mem_inuse(cleaner->cache->mctx));
766
767         cleaner->state = cleaner_s_idle;
768         cleaner->resched_event = event;
769 }
770
771 /*
772  * This is run once for every cache-cleaning-interval as defined in named.conf.
773  */
774 static void
775 cleaning_timer_action(isc_task_t *task, isc_event_t *event) {
776         cache_cleaner_t *cleaner = event->ev_arg;
777
778         UNUSED(task);
779
780         INSIST(task == cleaner->task);
781         INSIST(event->ev_type == ISC_TIMEREVENT_TICK);
782
783         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
784                       ISC_LOG_DEBUG(1), "cache cleaning timer fired, "
785                       "cleaner state = %d", cleaner->state);
786
787         if (cleaner->state == cleaner_s_idle)
788                 begin_cleaning(cleaner);
789
790         isc_event_free(&event);
791 }
792
793 /*
794  * This is called when the cache either surpasses its upper limit
795  * or shrinks beyond its lower limit.
796  */
797 static void
798 overmem_cleaning_action(isc_task_t *task, isc_event_t *event) {
799         cache_cleaner_t *cleaner = event->ev_arg;
800         isc_boolean_t want_cleaning = ISC_FALSE;
801
802         UNUSED(task);
803
804         INSIST(task == cleaner->task);
805         INSIST(event->ev_type == DNS_EVENT_CACHEOVERMEM);
806         INSIST(cleaner->overmem_event == NULL);
807
808         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
809                       ISC_LOG_DEBUG(1), "overmem_cleaning_action called, "
810                       "overmem = %d, state = %d", cleaner->overmem,
811                       cleaner->state);
812
813         LOCK(&cleaner->lock);
814
815         if (cleaner->overmem) {
816                 if (cleaner->state == cleaner_s_idle)
817                         want_cleaning = ISC_TRUE;
818         } else {
819                 if (cleaner->state == cleaner_s_busy)
820                         /*
821                          * end_cleaning() can't be called here because
822                          * then both cleaner->overmem_event and
823                          * cleaner->resched_event will point to this
824                          * event.  Set the state to done, and then
825                          * when the incremental_cleaning_action() event
826                          * is posted, it will handle the end_cleaning.
827                          */
828                         cleaner->state = cleaner_s_done;
829         }
830
831         cleaner->overmem_event = event;
832
833         UNLOCK(&cleaner->lock);
834
835         if (want_cleaning)
836                 begin_cleaning(cleaner);
837 }
838
839 /*
840  * Do incremental cleaning.
841  */
842 static void
843 incremental_cleaning_action(isc_task_t *task, isc_event_t *event) {
844         cache_cleaner_t *cleaner = event->ev_arg;
845         isc_result_t result;
846         unsigned int n_names;
847         isc_time_t start;
848
849         UNUSED(task);
850
851         INSIST(task == cleaner->task);
852         INSIST(event->ev_type == DNS_EVENT_CACHECLEAN);
853
854         if (cleaner->state == cleaner_s_done) {
855                 cleaner->state = cleaner_s_busy;
856                 end_cleaning(cleaner, event);
857                 LOCK(&cleaner->cache->lock);
858                 LOCK(&cleaner->lock);
859                 if (cleaner->replaceiterator) {
860                         dns_dbiterator_destroy(&cleaner->iterator);
861                         (void) dns_db_createiterator(cleaner->cache->db,
862                                                      ISC_FALSE,
863                                                      &cleaner->iterator);
864                         cleaner->replaceiterator = ISC_FALSE;
865                 }
866                 UNLOCK(&cleaner->lock);
867                 UNLOCK(&cleaner->cache->lock);
868                 return;
869         }
870
871         INSIST(CLEANER_BUSY(cleaner));
872
873         n_names = cleaner->increment;
874
875         REQUIRE(DNS_DBITERATOR_VALID(cleaner->iterator));
876
877         isc_time_now(&start);
878         while (n_names-- > 0) {
879                 dns_dbnode_t *node = NULL;
880
881                 result = dns_dbiterator_current(cleaner->iterator, &node,
882                                                 NULL);
883                 if (result != ISC_R_SUCCESS) {
884                         UNEXPECTED_ERROR(__FILE__, __LINE__,
885                                  "cache cleaner: dns_dbiterator_current() "
886                                  "failed: %s", dns_result_totext(result));
887
888                         end_cleaning(cleaner, event);
889                         return;
890                 }
891
892                 /*
893                  * The node was not needed, but was required by
894                  * dns_dbiterator_current().  Give up its reference.
895                  */
896                 dns_db_detachnode(cleaner->cache->db, &node);
897
898                 /*
899                  * Step to the next node.
900                  */
901                 result = dns_dbiterator_next(cleaner->iterator);
902
903                 if (result != ISC_R_SUCCESS) {
904                         /*
905                          * Either the end was reached (ISC_R_NOMORE) or
906                          * some error was signaled.  If the cache is still
907                          * overmem and no error was encountered,
908                          * keep trying to clean it, otherwise stop cleaning.
909                          */
910                         if (result != ISC_R_NOMORE)
911                                 UNEXPECTED_ERROR(__FILE__, __LINE__,
912                                                  "cache cleaner: "
913                                                  "dns_dbiterator_next() "
914                                                  "failed: %s",
915                                                  dns_result_totext(result));
916                         else if (cleaner->overmem) {
917                                 result = dns_dbiterator_first(cleaner->
918                                                               iterator);
919                                 if (result == ISC_R_SUCCESS) {
920                                         isc_log_write(dns_lctx,
921                                                       DNS_LOGCATEGORY_DATABASE,
922                                                       DNS_LOGMODULE_CACHE,
923                                                       ISC_LOG_DEBUG(1),
924                                                       "cache cleaner: "
925                                                       "still overmem, "
926                                                       "reset and try again");
927                                         continue;
928                                 }
929                         }
930
931                         end_cleaning(cleaner, event);
932                         return;
933                 }
934         }
935
936         /*
937          * We have successfully performed a cleaning increment but have
938          * not gone through the entire cache.  Free the iterator locks
939          * and reschedule another batch.  If it fails, just try to continue
940          * anyway.
941          */
942         result = dns_dbiterator_pause(cleaner->iterator);
943         RUNTIME_CHECK(result == ISC_R_SUCCESS);
944
945         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
946                       ISC_LOG_DEBUG(1), "cache cleaner: checked %u nodes, "
947                       "mem inuse %lu, sleeping", cleaner->increment,
948                       (unsigned long)isc_mem_inuse(cleaner->cache->mctx));
949
950         isc_task_send(task, &event);
951         INSIST(CLEANER_BUSY(cleaner));
952         return;
953 }
954
955 /*
956  * Do immediate cleaning.
957  */
958 isc_result_t
959 dns_cache_clean(dns_cache_t *cache, isc_stdtime_t now) {
960         isc_result_t result;
961         dns_dbiterator_t *iterator = NULL;
962
963         REQUIRE(VALID_CACHE(cache));
964
965         result = dns_db_createiterator(cache->db, 0, &iterator);
966         if (result != ISC_R_SUCCESS)
967                 return result;
968
969         result = dns_dbiterator_first(iterator);
970
971         while (result == ISC_R_SUCCESS) {
972                 dns_dbnode_t *node = NULL;
973                 result = dns_dbiterator_current(iterator, &node,
974                                                 (dns_name_t *)NULL);
975                 if (result != ISC_R_SUCCESS)
976                         break;
977
978                 /*
979                  * Check TTLs, mark expired rdatasets stale.
980                  */
981                 result = dns_db_expirenode(cache->db, node, now);
982                 if (result != ISC_R_SUCCESS) {
983                         UNEXPECTED_ERROR(__FILE__, __LINE__,
984                                          "cache cleaner: dns_db_expirenode() "
985                                          "failed: %s",
986                                          dns_result_totext(result));
987                         /*
988                          * Continue anyway.
989                          */
990                 }
991
992                 /*
993                  * This is where the actual freeing takes place.
994                  */
995                 dns_db_detachnode(cache->db, &node);
996
997                 result = dns_dbiterator_next(iterator);
998         }
999
1000         dns_dbiterator_destroy(&iterator);
1001
1002         if (result == ISC_R_NOMORE)
1003                 result = ISC_R_SUCCESS;
1004
1005         return (result);
1006 }
1007
1008 static void
1009 water(void *arg, int mark) {
1010         dns_cache_t *cache = arg;
1011         isc_boolean_t overmem = ISC_TF(mark == ISC_MEM_HIWATER);
1012
1013         REQUIRE(VALID_CACHE(cache));
1014
1015         LOCK(&cache->cleaner.lock);
1016
1017         if (overmem != cache->cleaner.overmem) {
1018                 dns_db_overmem(cache->db, overmem);
1019                 cache->cleaner.overmem = overmem;
1020                 isc_mem_waterack(cache->mctx, mark);
1021         }
1022
1023         if (cache->cleaner.overmem_event != NULL)
1024                 isc_task_send(cache->cleaner.task,
1025                               &cache->cleaner.overmem_event);
1026
1027         UNLOCK(&cache->cleaner.lock);
1028 }
1029
1030 void
1031 dns_cache_setcachesize(dns_cache_t *cache, isc_uint32_t size) {
1032         isc_uint32_t lowater;
1033         isc_uint32_t hiwater;
1034
1035         REQUIRE(VALID_CACHE(cache));
1036
1037         /*
1038          * Impose a minimum cache size; pathological things happen if there
1039          * is too little room.
1040          */
1041         if (size != 0 && size < DNS_CACHE_MINSIZE)
1042                 size = DNS_CACHE_MINSIZE;
1043
1044         LOCK(&cache->lock);
1045         cache->size = size;
1046         UNLOCK(&cache->lock);
1047
1048         hiwater = size - (size >> 3);   /* Approximately 7/8ths. */
1049         lowater = size - (size >> 2);   /* Approximately 3/4ths. */
1050
1051         /*
1052          * If the cache was overmem and cleaning, but now with the new limits
1053          * it is no longer in an overmem condition, then the next
1054          * isc_mem_put for cache memory will do the right thing and trigger
1055          * water().
1056          */
1057
1058         if (size == 0 || hiwater == 0 || lowater == 0)
1059                 /*
1060                  * Disable cache memory limiting.
1061                  */
1062                 isc_mem_setwater(cache->mctx, water, cache, 0, 0);
1063         else
1064                 /*
1065                  * Establish new cache memory limits (either for the first
1066                  * time, or replacing other limits).
1067                  */
1068                 isc_mem_setwater(cache->mctx, water, cache, hiwater, lowater);
1069 }
1070
1071 isc_uint32_t
1072 dns_cache_getcachesize(dns_cache_t *cache) {
1073         isc_uint32_t size;
1074
1075         REQUIRE(VALID_CACHE(cache));
1076
1077         LOCK(&cache->lock);
1078         size = cache->size;
1079         UNLOCK(&cache->lock);
1080
1081         return (size);
1082 }
1083
1084 /*
1085  * The cleaner task is shutting down; do the necessary cleanup.
1086  */
1087 static void
1088 cleaner_shutdown_action(isc_task_t *task, isc_event_t *event) {
1089         dns_cache_t *cache = event->ev_arg;
1090         isc_boolean_t should_free = ISC_FALSE;
1091
1092         UNUSED(task);
1093
1094         INSIST(task == cache->cleaner.task);
1095         INSIST(event->ev_type == ISC_TASKEVENT_SHUTDOWN);
1096
1097         if (CLEANER_BUSY(&cache->cleaner))
1098                 end_cleaning(&cache->cleaner, event);
1099         else
1100                 isc_event_free(&event);
1101
1102         LOCK(&cache->lock);
1103
1104         cache->live_tasks--;
1105         INSIST(cache->live_tasks == 0);
1106
1107         if (cache->references == 0)
1108                 should_free = ISC_TRUE;
1109
1110         /*
1111          * By detaching the timer in the context of its task,
1112          * we are guaranteed that there will be no further timer
1113          * events.
1114          */
1115         if (cache->cleaner.cleaning_timer != NULL)
1116                 isc_timer_detach(&cache->cleaner.cleaning_timer);
1117
1118         /* Make sure we don't reschedule anymore. */
1119         (void)isc_task_purge(task, NULL, DNS_EVENT_CACHECLEAN, NULL);
1120
1121         UNLOCK(&cache->lock);
1122
1123         if (should_free)
1124                 cache_free(cache);
1125 }
1126
1127 isc_result_t
1128 dns_cache_flush(dns_cache_t *cache) {
1129         dns_db_t *db = NULL;
1130         isc_result_t result;
1131
1132         result = cache_create_db(cache, &db);
1133         if (result != ISC_R_SUCCESS)
1134                 return (result);
1135
1136         LOCK(&cache->lock);
1137         LOCK(&cache->cleaner.lock);
1138         if (cache->cleaner.state == cleaner_s_idle) {
1139                 if (cache->cleaner.iterator != NULL)
1140                         dns_dbiterator_destroy(&cache->cleaner.iterator);
1141                 (void) dns_db_createiterator(db, ISC_FALSE,
1142                                              &cache->cleaner.iterator);
1143         } else {
1144                 if (cache->cleaner.state == cleaner_s_busy)
1145                         cache->cleaner.state = cleaner_s_done;
1146                 cache->cleaner.replaceiterator = ISC_TRUE;
1147         }
1148         dns_db_detach(&cache->db);
1149         cache->db = db;
1150         UNLOCK(&cache->cleaner.lock);
1151         UNLOCK(&cache->lock);
1152
1153         return (ISC_R_SUCCESS);
1154 }
1155
1156 isc_result_t
1157 dns_cache_flushname(dns_cache_t *cache, dns_name_t *name) {
1158         isc_result_t result;
1159         dns_rdatasetiter_t *iter = NULL;
1160         dns_dbnode_t *node = NULL;
1161         dns_db_t *db = NULL;
1162
1163         LOCK(&cache->lock);
1164         if (cache->db != NULL)
1165                 dns_db_attach(cache->db, &db);
1166         UNLOCK(&cache->lock);
1167         if (db == NULL)
1168                 return (ISC_R_SUCCESS);
1169         result = dns_db_findnode(cache->db, name, ISC_FALSE, &node);
1170         if (result == ISC_R_NOTFOUND) {
1171                 result = ISC_R_SUCCESS;
1172                 goto cleanup_db;
1173         }
1174         if (result != ISC_R_SUCCESS)
1175                 goto cleanup_db;
1176
1177         result = dns_db_allrdatasets(cache->db, node, NULL,
1178                                      (isc_stdtime_t)0, &iter);
1179         if (result != ISC_R_SUCCESS)
1180                 goto cleanup_node;
1181
1182         for (result = dns_rdatasetiter_first(iter);
1183              result == ISC_R_SUCCESS;
1184              result = dns_rdatasetiter_next(iter))
1185         {
1186                 dns_rdataset_t rdataset;
1187                 dns_rdataset_init(&rdataset);
1188
1189                 dns_rdatasetiter_current(iter, &rdataset);
1190                 result = dns_db_deleterdataset(cache->db, node, NULL,
1191                                                rdataset.type, rdataset.covers);
1192                 dns_rdataset_disassociate(&rdataset);
1193                 if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED)
1194                         break;
1195         }
1196         if (result == ISC_R_NOMORE)
1197                 result = ISC_R_SUCCESS;
1198
1199         dns_rdatasetiter_destroy(&iter);
1200
1201  cleanup_node:
1202         dns_db_detachnode(cache->db, &node);
1203
1204  cleanup_db:
1205         dns_db_detach(&db);
1206         return (result);
1207 }