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