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