cache.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2004-2009, 2011-2014  Internet Systems Consortium, Inc. ("ISC")
00003  * Copyright (C) 1999-2003  Internet Software Consortium.
00004  *
00005  * Permission to use, copy, modify, and/or distribute this software for any
00006  * purpose with or without fee is hereby granted, provided that the above
00007  * copyright notice and this permission notice appear in all copies.
00008  *
00009  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
00010  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
00011  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
00012  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
00013  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
00014  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
00015  * PERFORMANCE OF THIS SOFTWARE.
00016  */
00017 
00018 /* $Id: cache.c,v 1.91 2011/08/26 05:12:56 marka Exp $ */
00019 
00020 /*! \file */
00021 
00022 #include <config.h>
00023 
00024 #include <isc/json.h>
00025 #include <isc/mem.h>
00026 #include <isc/print.h>
00027 #include <isc/string.h>
00028 #include <isc/stats.h>
00029 #include <isc/task.h>
00030 #include <isc/time.h>
00031 #include <isc/timer.h>
00032 #include <isc/util.h>
00033 #include <isc/xml.h>
00034 
00035 #include <dns/cache.h>
00036 #include <dns/db.h>
00037 #include <dns/dbiterator.h>
00038 #include <dns/events.h>
00039 #include <dns/lib.h>
00040 #include <dns/log.h>
00041 #include <dns/masterdump.h>
00042 #include <dns/rdata.h>
00043 #include <dns/rdataset.h>
00044 #include <dns/rdatasetiter.h>
00045 #include <dns/result.h>
00046 #include <dns/stats.h>
00047 
00048 #include "rbtdb.h"
00049 
00050 #define CACHE_MAGIC             ISC_MAGIC('$', '$', '$', '$')
00051 #define VALID_CACHE(cache)      ISC_MAGIC_VALID(cache, CACHE_MAGIC)
00052 
00053 /*!
00054  * Control incremental cleaning.
00055  * DNS_CACHE_MINSIZE is how many bytes is the floor for dns_cache_setcachesize().
00056  * See also DNS_CACHE_CLEANERINCREMENT
00057  */
00058 #define DNS_CACHE_MINSIZE       2097152U /*%< Bytes.  2097152 = 2 MB */
00059 /*!
00060  * Control incremental cleaning.
00061  * CLEANERINCREMENT is how many nodes are examined in one pass.
00062  * See also DNS_CACHE_MINSIZE
00063  */
00064 #define DNS_CACHE_CLEANERINCREMENT      1000U   /*%< Number of nodes. */
00065 
00066 /***
00067  ***    Types
00068  ***/
00069 
00070 /*
00071  * A cache_cleaner_t encapsulates the state of the periodic
00072  * cache cleaning.
00073  */
00074 
00075 typedef struct cache_cleaner cache_cleaner_t;
00076 
00077 typedef enum {
00078         cleaner_s_idle, /*%< Waiting for cleaning-interval to expire. */
00079         cleaner_s_busy, /*%< Currently cleaning. */
00080         cleaner_s_done  /*%< Freed enough memory after being overmem. */
00081 } cleaner_state_t;
00082 
00083 /*
00084  * Convenience macros for comprehensive assertion checking.
00085  */
00086 #define CLEANER_IDLE(c) ((c)->state == cleaner_s_idle && \
00087                          (c)->resched_event != NULL)
00088 #define CLEANER_BUSY(c) ((c)->state == cleaner_s_busy && \
00089                          (c)->iterator != NULL && \
00090                          (c)->resched_event == NULL)
00091 
00092 /*%
00093  * Accesses to a cache cleaner object are synchronized through
00094  * task/event serialization, or locked from the cache object.
00095  */
00096 struct cache_cleaner {
00097         isc_mutex_t     lock;
00098         /*%<
00099          * Locks overmem_event, overmem.  Note: never allocate memory
00100          * while holding this lock - that could lead to deadlock since
00101          * the lock is take by water() which is called from the memory
00102          * allocator.
00103          */
00104 
00105         dns_cache_t     *cache;
00106         isc_task_t      *task;
00107         unsigned int    cleaning_interval; /*% The cleaning-interval from
00108                                               named.conf, in seconds. */
00109         isc_timer_t     *cleaning_timer;
00110         isc_event_t     *resched_event; /*% Sent by cleaner task to
00111                                            itself to reschedule */
00112         isc_event_t     *overmem_event;
00113 
00114         dns_dbiterator_t *iterator;
00115         unsigned int    increment;      /*% Number of names to
00116                                            clean in one increment */
00117         cleaner_state_t state;          /*% Idle/Busy. */
00118         isc_boolean_t   overmem;        /*% The cache is in an overmem state. */
00119         isc_boolean_t    replaceiterator;
00120 };
00121 
00122 /*%
00123  * The actual cache object.
00124  */
00125 
00126 struct dns_cache {
00127         /* Unlocked. */
00128         unsigned int            magic;
00129         isc_mutex_t             lock;
00130         isc_mutex_t             filelock;
00131         isc_mem_t               *mctx;          /* Main cache memory */
00132         isc_mem_t               *hmctx;         /* Heap memory */
00133         char                    *name;
00134 
00135         /* Locked by 'lock'. */
00136         int                     references;
00137         int                     live_tasks;
00138         dns_rdataclass_t        rdclass;
00139         dns_db_t                *db;
00140         cache_cleaner_t         cleaner;
00141         char                    *db_type;
00142         int                     db_argc;
00143         char                    **db_argv;
00144         size_t                  size;
00145         isc_stats_t             *stats;
00146 
00147         /* Locked by 'filelock'. */
00148         char                    *filename;
00149         /* Access to the on-disk cache file is also locked by 'filelock'. */
00150 };
00151 
00152 /***
00153  ***    Functions
00154  ***/
00155 
00156 static isc_result_t
00157 cache_cleaner_init(dns_cache_t *cache, isc_taskmgr_t *taskmgr,
00158                    isc_timermgr_t *timermgr, cache_cleaner_t *cleaner);
00159 
00160 static void
00161 cleaning_timer_action(isc_task_t *task, isc_event_t *event);
00162 
00163 static void
00164 incremental_cleaning_action(isc_task_t *task, isc_event_t *event);
00165 
00166 static void
00167 cleaner_shutdown_action(isc_task_t *task, isc_event_t *event);
00168 
00169 static void
00170 overmem_cleaning_action(isc_task_t *task, isc_event_t *event);
00171 
00172 static inline isc_result_t
00173 cache_create_db(dns_cache_t *cache, dns_db_t **db) {
00174         return (dns_db_create(cache->mctx, cache->db_type, dns_rootname,
00175                               dns_dbtype_cache, cache->rdclass,
00176                               cache->db_argc, cache->db_argv, db));
00177 }
00178 
00179 isc_result_t
00180 dns_cache_create(isc_mem_t *cmctx, isc_taskmgr_t *taskmgr,
00181                  isc_timermgr_t *timermgr, dns_rdataclass_t rdclass,
00182                  const char *db_type, unsigned int db_argc, char **db_argv,
00183                  dns_cache_t **cachep)
00184 {
00185         return (dns_cache_create3(cmctx, cmctx, taskmgr, timermgr, rdclass, "",
00186                                   db_type, db_argc, db_argv, cachep));
00187 }
00188 
00189 isc_result_t
00190 dns_cache_create2(isc_mem_t *cmctx, isc_taskmgr_t *taskmgr,
00191                   isc_timermgr_t *timermgr, dns_rdataclass_t rdclass,
00192                   const char *cachename, const char *db_type,
00193                   unsigned int db_argc, char **db_argv, dns_cache_t **cachep)
00194 {
00195         return (dns_cache_create3(cmctx, cmctx, taskmgr, timermgr, rdclass,
00196                                   cachename, db_type, db_argc, db_argv,
00197                                   cachep));
00198 }
00199 
00200 isc_result_t
00201 dns_cache_create3(isc_mem_t *cmctx, isc_mem_t *hmctx, isc_taskmgr_t *taskmgr,
00202                   isc_timermgr_t *timermgr, dns_rdataclass_t rdclass,
00203                   const char *cachename, const char *db_type,
00204                   unsigned int db_argc, char **db_argv, dns_cache_t **cachep)
00205 {
00206         isc_result_t result;
00207         dns_cache_t *cache;
00208         int i, extra = 0;
00209         isc_task_t *dbtask;
00210 
00211         REQUIRE(cachep != NULL);
00212         REQUIRE(*cachep == NULL);
00213         REQUIRE(cmctx != NULL);
00214         REQUIRE(hmctx != NULL);
00215         REQUIRE(cachename != NULL);
00216 
00217         cache = isc_mem_get(cmctx, sizeof(*cache));
00218         if (cache == NULL)
00219                 return (ISC_R_NOMEMORY);
00220 
00221         cache->mctx = cache->hmctx = NULL;
00222         isc_mem_attach(cmctx, &cache->mctx);
00223         isc_mem_attach(hmctx, &cache->hmctx);
00224 
00225         cache->name = NULL;
00226         if (cachename != NULL) {
00227                 cache->name = isc_mem_strdup(cmctx, cachename);
00228                 if (cache->name == NULL) {
00229                         result = ISC_R_NOMEMORY;
00230                         goto cleanup_mem;
00231                 }
00232         }
00233 
00234         result = isc_mutex_init(&cache->lock);
00235         if (result != ISC_R_SUCCESS)
00236                 goto cleanup_mem;
00237 
00238         result = isc_mutex_init(&cache->filelock);
00239         if (result != ISC_R_SUCCESS)
00240                 goto cleanup_lock;
00241 
00242         cache->references = 1;
00243         cache->live_tasks = 0;
00244         cache->rdclass = rdclass;
00245 
00246         cache->stats = NULL;
00247         result = isc_stats_create(cmctx, &cache->stats,
00248                                   dns_cachestatscounter_max);
00249         if (result != ISC_R_SUCCESS)
00250                 goto cleanup_filelock;
00251 
00252         cache->db_type = isc_mem_strdup(cmctx, db_type);
00253         if (cache->db_type == NULL) {
00254                 result = ISC_R_NOMEMORY;
00255                 goto cleanup_stats;
00256         }
00257 
00258         /*
00259          * For databases of type "rbt" we pass hmctx to dns_db_create()
00260          * via cache->db_argv, followed by the rest of the arguments in
00261          * db_argv (of which there really shouldn't be any).
00262          */
00263         if (strcmp(cache->db_type, "rbt") == 0)
00264                 extra = 1;
00265 
00266         cache->db_argc = db_argc + extra;
00267         cache->db_argv = NULL;
00268 
00269         if (cache->db_argc != 0) {
00270                 cache->db_argv = isc_mem_get(cmctx,
00271                                              cache->db_argc * sizeof(char *));
00272                 if (cache->db_argv == NULL) {
00273                         result = ISC_R_NOMEMORY;
00274                         goto cleanup_dbtype;
00275                 }
00276 
00277                 for (i = 0; i < cache->db_argc; i++)
00278                         cache->db_argv[i] = NULL;
00279 
00280                 cache->db_argv[0] = (char *) hmctx;
00281                 for (i = extra; i < cache->db_argc; i++) {
00282                         cache->db_argv[i] = isc_mem_strdup(cmctx,
00283                                                            db_argv[i - extra]);
00284                         if (cache->db_argv[i] == NULL) {
00285                                 result = ISC_R_NOMEMORY;
00286                                 goto cleanup_dbargv;
00287                         }
00288                 }
00289         }
00290 
00291         /*
00292          * Create the database
00293          */
00294         cache->db = NULL;
00295         result = cache_create_db(cache, &cache->db);
00296         if (result != ISC_R_SUCCESS)
00297                 goto cleanup_dbargv;
00298         if (taskmgr != NULL) {
00299                 dbtask = NULL;
00300                 result = isc_task_create(taskmgr, 1, &dbtask);
00301                 if (result != ISC_R_SUCCESS)
00302                         goto cleanup_db;
00303 
00304                 isc_task_setname(dbtask, "cache_dbtask", NULL);
00305                 dns_db_settask(cache->db, dbtask);
00306                 isc_task_detach(&dbtask);
00307         }
00308 
00309         cache->filename = NULL;
00310 
00311         cache->magic = CACHE_MAGIC;
00312 
00313         /*
00314          * RBT-type cache DB has its own mechanism of cache cleaning and doesn't
00315          * need the control of the generic cleaner.
00316          */
00317         if (strcmp(db_type, "rbt") == 0)
00318                 result = cache_cleaner_init(cache, NULL, NULL, &cache->cleaner);
00319         else {
00320                 result = cache_cleaner_init(cache, taskmgr, timermgr,
00321                                             &cache->cleaner);
00322         }
00323         if (result != ISC_R_SUCCESS)
00324                 goto cleanup_db;
00325 
00326         result = dns_db_setcachestats(cache->db, cache->stats);
00327         if (result != ISC_R_SUCCESS)
00328                 goto cleanup_db;
00329 
00330 
00331         *cachep = cache;
00332         return (ISC_R_SUCCESS);
00333 
00334  cleanup_db:
00335         dns_db_detach(&cache->db);
00336  cleanup_dbargv:
00337         for (i = extra; i < cache->db_argc; i++)
00338                 if (cache->db_argv[i] != NULL)
00339                         isc_mem_free(cmctx, cache->db_argv[i]);
00340         if (cache->db_argv != NULL)
00341                 isc_mem_put(cmctx, cache->db_argv,
00342                             cache->db_argc * sizeof(char *));
00343  cleanup_dbtype:
00344         isc_mem_free(cmctx, cache->db_type);
00345  cleanup_filelock:
00346         DESTROYLOCK(&cache->filelock);
00347  cleanup_stats:
00348         isc_stats_detach(&cache->stats);
00349  cleanup_lock:
00350         DESTROYLOCK(&cache->lock);
00351  cleanup_mem:
00352         if (cache->name != NULL)
00353                 isc_mem_free(cmctx, cache->name);
00354         isc_mem_detach(&cache->hmctx);
00355         isc_mem_putanddetach(&cache->mctx, cache, sizeof(*cache));
00356         return (result);
00357 }
00358 
00359 static void
00360 cache_free(dns_cache_t *cache) {
00361         int i;
00362 
00363         REQUIRE(VALID_CACHE(cache));
00364         REQUIRE(cache->references == 0);
00365 
00366         isc_mem_setwater(cache->mctx, NULL, NULL, 0, 0);
00367 
00368         if (cache->cleaner.task != NULL)
00369                 isc_task_detach(&cache->cleaner.task);
00370 
00371         if (cache->cleaner.overmem_event != NULL)
00372                 isc_event_free(&cache->cleaner.overmem_event);
00373 
00374         if (cache->cleaner.resched_event != NULL)
00375                 isc_event_free(&cache->cleaner.resched_event);
00376 
00377         if (cache->cleaner.iterator != NULL)
00378                 dns_dbiterator_destroy(&cache->cleaner.iterator);
00379 
00380         DESTROYLOCK(&cache->cleaner.lock);
00381 
00382         if (cache->filename) {
00383                 isc_mem_free(cache->mctx, cache->filename);
00384                 cache->filename = NULL;
00385         }
00386 
00387         if (cache->db != NULL)
00388                 dns_db_detach(&cache->db);
00389 
00390         if (cache->db_argv != NULL) {
00391                 /*
00392                  * We don't free db_argv[0] in "rbt" cache databases
00393                  * as it's a pointer to hmctx
00394                  */
00395                 int extra = 0;
00396                 if (strcmp(cache->db_type, "rbt") == 0)
00397                         extra = 1;
00398                 for (i = extra; i < cache->db_argc; i++)
00399                         if (cache->db_argv[i] != NULL)
00400                                 isc_mem_free(cache->mctx, cache->db_argv[i]);
00401                 isc_mem_put(cache->mctx, cache->db_argv,
00402                             cache->db_argc * sizeof(char *));
00403         }
00404 
00405         if (cache->db_type != NULL)
00406                 isc_mem_free(cache->mctx, cache->db_type);
00407 
00408         if (cache->name != NULL)
00409                 isc_mem_free(cache->mctx, cache->name);
00410 
00411         if (cache->stats != NULL)
00412                 isc_stats_detach(&cache->stats);
00413 
00414         DESTROYLOCK(&cache->lock);
00415         DESTROYLOCK(&cache->filelock);
00416 
00417         cache->magic = 0;
00418         isc_mem_detach(&cache->hmctx);
00419         isc_mem_putanddetach(&cache->mctx, cache, sizeof(*cache));
00420 }
00421 
00422 
00423 void
00424 dns_cache_attach(dns_cache_t *cache, dns_cache_t **targetp) {
00425 
00426         REQUIRE(VALID_CACHE(cache));
00427         REQUIRE(targetp != NULL && *targetp == NULL);
00428 
00429         LOCK(&cache->lock);
00430         cache->references++;
00431         UNLOCK(&cache->lock);
00432 
00433         *targetp = cache;
00434 }
00435 
00436 void
00437 dns_cache_detach(dns_cache_t **cachep) {
00438         dns_cache_t *cache;
00439         isc_boolean_t free_cache = ISC_FALSE;
00440 
00441         REQUIRE(cachep != NULL);
00442         cache = *cachep;
00443         REQUIRE(VALID_CACHE(cache));
00444 
00445         LOCK(&cache->lock);
00446         REQUIRE(cache->references > 0);
00447         cache->references--;
00448         if (cache->references == 0) {
00449                 cache->cleaner.overmem = ISC_FALSE;
00450                 free_cache = ISC_TRUE;
00451         }
00452 
00453         *cachep = NULL;
00454 
00455         if (free_cache) {
00456                 /*
00457                  * When the cache is shut down, dump it to a file if one is
00458                  * specified.
00459                  */
00460                 isc_result_t result = dns_cache_dump(cache);
00461                 if (result != ISC_R_SUCCESS)
00462                         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
00463                                       DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
00464                                       "error dumping cache: %s ",
00465                                       isc_result_totext(result));
00466 
00467                 /*
00468                  * If the cleaner task exists, let it free the cache.
00469                  */
00470                 if (cache->live_tasks > 0) {
00471                         isc_task_shutdown(cache->cleaner.task);
00472                         free_cache = ISC_FALSE;
00473                 }
00474         }
00475 
00476         UNLOCK(&cache->lock);
00477 
00478         if (free_cache)
00479                 cache_free(cache);
00480 }
00481 
00482 void
00483 dns_cache_attachdb(dns_cache_t *cache, dns_db_t **dbp) {
00484         REQUIRE(VALID_CACHE(cache));
00485         REQUIRE(dbp != NULL && *dbp == NULL);
00486         REQUIRE(cache->db != NULL);
00487 
00488         LOCK(&cache->lock);
00489         dns_db_attach(cache->db, dbp);
00490         UNLOCK(&cache->lock);
00491 
00492 }
00493 
00494 isc_result_t
00495 dns_cache_setfilename(dns_cache_t *cache, const char *filename) {
00496         char *newname;
00497 
00498         REQUIRE(VALID_CACHE(cache));
00499         REQUIRE(filename != NULL);
00500 
00501         newname = isc_mem_strdup(cache->mctx, filename);
00502         if (newname == NULL)
00503                 return (ISC_R_NOMEMORY);
00504 
00505         LOCK(&cache->filelock);
00506         if (cache->filename)
00507                 isc_mem_free(cache->mctx, cache->filename);
00508         cache->filename = newname;
00509         UNLOCK(&cache->filelock);
00510 
00511         return (ISC_R_SUCCESS);
00512 }
00513 
00514 isc_result_t
00515 dns_cache_load(dns_cache_t *cache) {
00516         isc_result_t result;
00517 
00518         REQUIRE(VALID_CACHE(cache));
00519 
00520         if (cache->filename == NULL)
00521                 return (ISC_R_SUCCESS);
00522 
00523         LOCK(&cache->filelock);
00524         result = dns_db_load(cache->db, cache->filename);
00525         UNLOCK(&cache->filelock);
00526 
00527         return (result);
00528 }
00529 
00530 isc_result_t
00531 dns_cache_dump(dns_cache_t *cache) {
00532         isc_result_t result;
00533 
00534         REQUIRE(VALID_CACHE(cache));
00535 
00536         if (cache->filename == NULL)
00537                 return (ISC_R_SUCCESS);
00538 
00539         LOCK(&cache->filelock);
00540         result = dns_master_dump(cache->mctx, cache->db, NULL,
00541                                  &dns_master_style_cache, cache->filename);
00542         UNLOCK(&cache->filelock);
00543         return (result);
00544 
00545 }
00546 
00547 void
00548 dns_cache_setcleaninginterval(dns_cache_t *cache, unsigned int t) {
00549         isc_interval_t interval;
00550         isc_result_t result;
00551 
00552         LOCK(&cache->lock);
00553 
00554         /*
00555          * It may be the case that the cache has already shut down.
00556          * If so, it has no timer.
00557          */
00558         if (cache->cleaner.cleaning_timer == NULL)
00559                 goto unlock;
00560 
00561         cache->cleaner.cleaning_interval = t;
00562 
00563         if (t == 0) {
00564                 result = isc_timer_reset(cache->cleaner.cleaning_timer,
00565                                          isc_timertype_inactive,
00566                                          NULL, NULL, ISC_TRUE);
00567         } else {
00568                 isc_interval_set(&interval, cache->cleaner.cleaning_interval,
00569                                  0);
00570                 result = isc_timer_reset(cache->cleaner.cleaning_timer,
00571                                          isc_timertype_ticker,
00572                                          NULL, &interval, ISC_FALSE);
00573         }
00574         if (result != ISC_R_SUCCESS)
00575                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
00576                               DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
00577                               "could not set cache cleaning interval: %s",
00578                               isc_result_totext(result));
00579 
00580  unlock:
00581         UNLOCK(&cache->lock);
00582 }
00583 
00584 unsigned int
00585 dns_cache_getcleaninginterval(dns_cache_t *cache) {
00586         unsigned int t;
00587 
00588         REQUIRE(VALID_CACHE(cache));
00589 
00590         LOCK(&cache->lock);
00591         t = cache->cleaner.cleaning_interval;
00592         UNLOCK(&cache->lock);
00593 
00594         return (t);
00595 }
00596 
00597 const char *
00598 dns_cache_getname(dns_cache_t *cache) {
00599         REQUIRE(VALID_CACHE(cache));
00600 
00601         return (cache->name);
00602 }
00603 
00604 /*
00605  * Initialize the cache cleaner object at *cleaner.
00606  * Space for the object must be allocated by the caller.
00607  */
00608 
00609 static isc_result_t
00610 cache_cleaner_init(dns_cache_t *cache, isc_taskmgr_t *taskmgr,
00611                    isc_timermgr_t *timermgr, cache_cleaner_t *cleaner)
00612 {
00613         isc_result_t result;
00614 
00615         result = isc_mutex_init(&cleaner->lock);
00616         if (result != ISC_R_SUCCESS)
00617                 goto fail;
00618 
00619         cleaner->increment = DNS_CACHE_CLEANERINCREMENT;
00620         cleaner->state = cleaner_s_idle;
00621         cleaner->cache = cache;
00622         cleaner->iterator = NULL;
00623         cleaner->overmem = ISC_FALSE;
00624         cleaner->replaceiterator = ISC_FALSE;
00625 
00626         cleaner->task = NULL;
00627         cleaner->cleaning_timer = NULL;
00628         cleaner->resched_event = NULL;
00629         cleaner->overmem_event = NULL;
00630         cleaner->cleaning_interval = 0; /* Initially turned off. */
00631 
00632         result = dns_db_createiterator(cleaner->cache->db, ISC_FALSE,
00633                                        &cleaner->iterator);
00634         if (result != ISC_R_SUCCESS)
00635                 goto cleanup;
00636 
00637         if (taskmgr != NULL && timermgr != NULL) {
00638                 result = isc_task_create(taskmgr, 1, &cleaner->task);
00639                 if (result != ISC_R_SUCCESS) {
00640                         UNEXPECTED_ERROR(__FILE__, __LINE__,
00641                                          "isc_task_create() failed: %s",
00642                                          dns_result_totext(result));
00643                         result = ISC_R_UNEXPECTED;
00644                         goto cleanup;
00645                 }
00646                 cleaner->cache->live_tasks++;
00647                 isc_task_setname(cleaner->task, "cachecleaner", cleaner);
00648 
00649                 result = isc_task_onshutdown(cleaner->task,
00650                                              cleaner_shutdown_action, cache);
00651                 if (result != ISC_R_SUCCESS) {
00652                         UNEXPECTED_ERROR(__FILE__, __LINE__,
00653                                          "cache cleaner: "
00654                                          "isc_task_onshutdown() failed: %s",
00655                                          dns_result_totext(result));
00656                         goto cleanup;
00657                 }
00658 
00659                 result = isc_timer_create(timermgr, isc_timertype_inactive,
00660                                            NULL, NULL, cleaner->task,
00661                                            cleaning_timer_action, cleaner,
00662                                            &cleaner->cleaning_timer);
00663                 if (result != ISC_R_SUCCESS) {
00664                         UNEXPECTED_ERROR(__FILE__, __LINE__,
00665                                          "isc_timer_create() failed: %s",
00666                                          dns_result_totext(result));
00667                         result = ISC_R_UNEXPECTED;
00668                         goto cleanup;
00669                 }
00670 
00671                 cleaner->resched_event =
00672                         isc_event_allocate(cache->mctx, cleaner,
00673                                            DNS_EVENT_CACHECLEAN,
00674                                            incremental_cleaning_action,
00675                                            cleaner, sizeof(isc_event_t));
00676                 if (cleaner->resched_event == NULL) {
00677                         result = ISC_R_NOMEMORY;
00678                         goto cleanup;
00679                 }
00680 
00681                 cleaner->overmem_event =
00682                         isc_event_allocate(cache->mctx, cleaner,
00683                                            DNS_EVENT_CACHEOVERMEM,
00684                                            overmem_cleaning_action,
00685                                            cleaner, sizeof(isc_event_t));
00686                 if (cleaner->overmem_event == NULL) {
00687                         result = ISC_R_NOMEMORY;
00688                         goto cleanup;
00689                 }
00690         }
00691 
00692         return (ISC_R_SUCCESS);
00693 
00694  cleanup:
00695         if (cleaner->overmem_event != NULL)
00696                 isc_event_free(&cleaner->overmem_event);
00697         if (cleaner->resched_event != NULL)
00698                 isc_event_free(&cleaner->resched_event);
00699         if (cleaner->cleaning_timer != NULL)
00700                 isc_timer_detach(&cleaner->cleaning_timer);
00701         if (cleaner->task != NULL)
00702                 isc_task_detach(&cleaner->task);
00703         if (cleaner->iterator != NULL)
00704                 dns_dbiterator_destroy(&cleaner->iterator);
00705         DESTROYLOCK(&cleaner->lock);
00706  fail:
00707         return (result);
00708 }
00709 
00710 static void
00711 begin_cleaning(cache_cleaner_t *cleaner) {
00712         isc_result_t result = ISC_R_SUCCESS;
00713 
00714         REQUIRE(CLEANER_IDLE(cleaner));
00715 
00716         /*
00717          * Create an iterator, if it does not already exist, and
00718          * position it at the beginning of the cache.
00719          */
00720         if (cleaner->iterator == NULL)
00721                 result = dns_db_createiterator(cleaner->cache->db, ISC_FALSE,
00722                                                &cleaner->iterator);
00723         if (result != ISC_R_SUCCESS)
00724                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
00725                               DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
00726                               "cache cleaner could not create "
00727                               "iterator: %s", isc_result_totext(result));
00728         else {
00729                 dns_dbiterator_setcleanmode(cleaner->iterator, ISC_TRUE);
00730                 result = dns_dbiterator_first(cleaner->iterator);
00731         }
00732         if (result != ISC_R_SUCCESS) {
00733                 /*
00734                  * If the result is ISC_R_NOMORE, the database is empty,
00735                  * so there is nothing to be cleaned.
00736                  */
00737                 if (result != ISC_R_NOMORE && cleaner->iterator != NULL) {
00738                         UNEXPECTED_ERROR(__FILE__, __LINE__,
00739                                          "cache cleaner: "
00740                                          "dns_dbiterator_first() failed: %s",
00741                                          dns_result_totext(result));
00742                         dns_dbiterator_destroy(&cleaner->iterator);
00743                 } else if (cleaner->iterator != NULL) {
00744                         result = dns_dbiterator_pause(cleaner->iterator);
00745                         RUNTIME_CHECK(result == ISC_R_SUCCESS);
00746                 }
00747         } else {
00748                 /*
00749                  * Pause the iterator to free its lock.
00750                  */
00751                 result = dns_dbiterator_pause(cleaner->iterator);
00752                 RUNTIME_CHECK(result == ISC_R_SUCCESS);
00753 
00754                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
00755                               DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1),
00756                               "begin cache cleaning, mem inuse %lu",
00757                             (unsigned long)isc_mem_inuse(cleaner->cache->mctx));
00758                 cleaner->state = cleaner_s_busy;
00759                 isc_task_send(cleaner->task, &cleaner->resched_event);
00760         }
00761 
00762         return;
00763 }
00764 
00765 static void
00766 end_cleaning(cache_cleaner_t *cleaner, isc_event_t *event) {
00767         isc_result_t result;
00768 
00769         REQUIRE(CLEANER_BUSY(cleaner));
00770         REQUIRE(event != NULL);
00771 
00772         result = dns_dbiterator_pause(cleaner->iterator);
00773         if (result != ISC_R_SUCCESS)
00774                 dns_dbiterator_destroy(&cleaner->iterator);
00775 
00776         dns_cache_setcleaninginterval(cleaner->cache,
00777                                       cleaner->cleaning_interval);
00778 
00779         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
00780                       ISC_LOG_DEBUG(1), "end cache cleaning, mem inuse %lu",
00781                       (unsigned long)isc_mem_inuse(cleaner->cache->mctx));
00782 
00783         cleaner->state = cleaner_s_idle;
00784         cleaner->resched_event = event;
00785 }
00786 
00787 /*
00788  * This is run once for every cache-cleaning-interval as defined in named.conf.
00789  */
00790 static void
00791 cleaning_timer_action(isc_task_t *task, isc_event_t *event) {
00792         cache_cleaner_t *cleaner = event->ev_arg;
00793 
00794         UNUSED(task);
00795 
00796         INSIST(task == cleaner->task);
00797         INSIST(event->ev_type == ISC_TIMEREVENT_TICK);
00798 
00799         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
00800                       ISC_LOG_DEBUG(1), "cache cleaning timer fired, "
00801                       "cleaner state = %d", cleaner->state);
00802 
00803         if (cleaner->state == cleaner_s_idle)
00804                 begin_cleaning(cleaner);
00805 
00806         isc_event_free(&event);
00807 }
00808 
00809 /*
00810  * This is called when the cache either surpasses its upper limit
00811  * or shrinks beyond its lower limit.
00812  */
00813 static void
00814 overmem_cleaning_action(isc_task_t *task, isc_event_t *event) {
00815         cache_cleaner_t *cleaner = event->ev_arg;
00816         isc_boolean_t want_cleaning = ISC_FALSE;
00817 
00818         UNUSED(task);
00819 
00820         INSIST(task == cleaner->task);
00821         INSIST(event->ev_type == DNS_EVENT_CACHEOVERMEM);
00822         INSIST(cleaner->overmem_event == NULL);
00823 
00824         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
00825                       ISC_LOG_DEBUG(1), "overmem_cleaning_action called, "
00826                       "overmem = %d, state = %d", cleaner->overmem,
00827                       cleaner->state);
00828 
00829         LOCK(&cleaner->lock);
00830 
00831         if (cleaner->overmem) {
00832                 if (cleaner->state == cleaner_s_idle)
00833                         want_cleaning = ISC_TRUE;
00834         } else {
00835                 if (cleaner->state == cleaner_s_busy)
00836                         /*
00837                          * end_cleaning() can't be called here because
00838                          * then both cleaner->overmem_event and
00839                          * cleaner->resched_event will point to this
00840                          * event.  Set the state to done, and then
00841                          * when the incremental_cleaning_action() event
00842                          * is posted, it will handle the end_cleaning.
00843                          */
00844                         cleaner->state = cleaner_s_done;
00845         }
00846 
00847         cleaner->overmem_event = event;
00848 
00849         UNLOCK(&cleaner->lock);
00850 
00851         if (want_cleaning)
00852                 begin_cleaning(cleaner);
00853 }
00854 
00855 /*
00856  * Do incremental cleaning.
00857  */
00858 static void
00859 incremental_cleaning_action(isc_task_t *task, isc_event_t *event) {
00860         cache_cleaner_t *cleaner = event->ev_arg;
00861         isc_result_t result;
00862         unsigned int n_names;
00863         isc_time_t start;
00864 
00865         UNUSED(task);
00866 
00867         INSIST(task == cleaner->task);
00868         INSIST(event->ev_type == DNS_EVENT_CACHECLEAN);
00869 
00870         if (cleaner->state == cleaner_s_done) {
00871                 cleaner->state = cleaner_s_busy;
00872                 end_cleaning(cleaner, event);
00873                 LOCK(&cleaner->cache->lock);
00874                 LOCK(&cleaner->lock);
00875                 if (cleaner->replaceiterator) {
00876                         dns_dbiterator_destroy(&cleaner->iterator);
00877                         (void) dns_db_createiterator(cleaner->cache->db,
00878                                                      ISC_FALSE,
00879                                                      &cleaner->iterator);
00880                         cleaner->replaceiterator = ISC_FALSE;
00881                 }
00882                 UNLOCK(&cleaner->lock);
00883                 UNLOCK(&cleaner->cache->lock);
00884                 return;
00885         }
00886 
00887         INSIST(CLEANER_BUSY(cleaner));
00888 
00889         n_names = cleaner->increment;
00890 
00891         REQUIRE(DNS_DBITERATOR_VALID(cleaner->iterator));
00892 
00893         isc_time_now(&start);
00894         while (n_names-- > 0) {
00895                 dns_dbnode_t *node = NULL;
00896 
00897                 result = dns_dbiterator_current(cleaner->iterator, &node,
00898                                                 NULL);
00899                 if (result != ISC_R_SUCCESS) {
00900                         UNEXPECTED_ERROR(__FILE__, __LINE__,
00901                                  "cache cleaner: dns_dbiterator_current() "
00902                                  "failed: %s", dns_result_totext(result));
00903 
00904                         end_cleaning(cleaner, event);
00905                         return;
00906                 }
00907 
00908                 /*
00909                  * The node was not needed, but was required by
00910                  * dns_dbiterator_current().  Give up its reference.
00911                  */
00912                 dns_db_detachnode(cleaner->cache->db, &node);
00913 
00914                 /*
00915                  * Step to the next node.
00916                  */
00917                 result = dns_dbiterator_next(cleaner->iterator);
00918 
00919                 if (result != ISC_R_SUCCESS) {
00920                         /*
00921                          * Either the end was reached (ISC_R_NOMORE) or
00922                          * some error was signaled.  If the cache is still
00923                          * overmem and no error was encountered,
00924                          * keep trying to clean it, otherwise stop cleaning.
00925                          */
00926                         if (result != ISC_R_NOMORE)
00927                                 UNEXPECTED_ERROR(__FILE__, __LINE__,
00928                                                  "cache cleaner: "
00929                                                  "dns_dbiterator_next() "
00930                                                  "failed: %s",
00931                                                  dns_result_totext(result));
00932                         else if (cleaner->overmem) {
00933                                 result = dns_dbiterator_first(cleaner->
00934                                                               iterator);
00935                                 if (result == ISC_R_SUCCESS) {
00936                                         isc_log_write(dns_lctx,
00937                                                       DNS_LOGCATEGORY_DATABASE,
00938                                                       DNS_LOGMODULE_CACHE,
00939                                                       ISC_LOG_DEBUG(1),
00940                                                       "cache cleaner: "
00941                                                       "still overmem, "
00942                                                       "reset and try again");
00943                                         continue;
00944                                 }
00945                         }
00946 
00947                         end_cleaning(cleaner, event);
00948                         return;
00949                 }
00950         }
00951 
00952         /*
00953          * We have successfully performed a cleaning increment but have
00954          * not gone through the entire cache.  Free the iterator locks
00955          * and reschedule another batch.  If it fails, just try to continue
00956          * anyway.
00957          */
00958         result = dns_dbiterator_pause(cleaner->iterator);
00959         RUNTIME_CHECK(result == ISC_R_SUCCESS);
00960 
00961         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
00962                       ISC_LOG_DEBUG(1), "cache cleaner: checked %u nodes, "
00963                       "mem inuse %lu, sleeping", cleaner->increment,
00964                       (unsigned long)isc_mem_inuse(cleaner->cache->mctx));
00965 
00966         isc_task_send(task, &event);
00967         INSIST(CLEANER_BUSY(cleaner));
00968         return;
00969 }
00970 
00971 /*
00972  * Do immediate cleaning.
00973  */
00974 isc_result_t
00975 dns_cache_clean(dns_cache_t *cache, isc_stdtime_t now) {
00976         isc_result_t result;
00977         dns_dbiterator_t *iterator = NULL;
00978 
00979         REQUIRE(VALID_CACHE(cache));
00980 
00981         result = dns_db_createiterator(cache->db, 0, &iterator);
00982         if (result != ISC_R_SUCCESS)
00983                 return result;
00984 
00985         result = dns_dbiterator_first(iterator);
00986 
00987         while (result == ISC_R_SUCCESS) {
00988                 dns_dbnode_t *node = NULL;
00989                 result = dns_dbiterator_current(iterator, &node,
00990                                                 (dns_name_t *)NULL);
00991                 if (result != ISC_R_SUCCESS)
00992                         break;
00993 
00994                 /*
00995                  * Check TTLs, mark expired rdatasets stale.
00996                  */
00997                 result = dns_db_expirenode(cache->db, node, now);
00998                 if (result != ISC_R_SUCCESS) {
00999                         UNEXPECTED_ERROR(__FILE__, __LINE__,
01000                                          "cache cleaner: dns_db_expirenode() "
01001                                          "failed: %s",
01002                                          dns_result_totext(result));
01003                         /*
01004                          * Continue anyway.
01005                          */
01006                 }
01007 
01008                 /*
01009                  * This is where the actual freeing takes place.
01010                  */
01011                 dns_db_detachnode(cache->db, &node);
01012 
01013                 result = dns_dbiterator_next(iterator);
01014         }
01015 
01016         dns_dbiterator_destroy(&iterator);
01017 
01018         if (result == ISC_R_NOMORE)
01019                 result = ISC_R_SUCCESS;
01020 
01021         return (result);
01022 }
01023 
01024 static void
01025 water(void *arg, int mark) {
01026         dns_cache_t *cache = arg;
01027         isc_boolean_t overmem = ISC_TF(mark == ISC_MEM_HIWATER);
01028 
01029         REQUIRE(VALID_CACHE(cache));
01030 
01031         LOCK(&cache->cleaner.lock);
01032 
01033         if (overmem != cache->cleaner.overmem) {
01034                 dns_db_overmem(cache->db, overmem);
01035                 cache->cleaner.overmem = overmem;
01036                 isc_mem_waterack(cache->mctx, mark);
01037         }
01038 
01039         if (cache->cleaner.overmem_event != NULL)
01040                 isc_task_send(cache->cleaner.task,
01041                               &cache->cleaner.overmem_event);
01042 
01043         UNLOCK(&cache->cleaner.lock);
01044 }
01045 
01046 void
01047 dns_cache_setcachesize(dns_cache_t *cache, size_t size) {
01048         size_t hiwater, lowater;
01049 
01050         REQUIRE(VALID_CACHE(cache));
01051 
01052         /*
01053          * Impose a minimum cache size; pathological things happen if there
01054          * is too little room.
01055          */
01056         if (size != 0U && size < DNS_CACHE_MINSIZE)
01057                 size = DNS_CACHE_MINSIZE;
01058 
01059         LOCK(&cache->lock);
01060         cache->size = size;
01061         UNLOCK(&cache->lock);
01062 
01063         hiwater = size - (size >> 3);   /* Approximately 7/8ths. */
01064         lowater = size - (size >> 2);   /* Approximately 3/4ths. */
01065 
01066         /*
01067          * If the cache was overmem and cleaning, but now with the new limits
01068          * it is no longer in an overmem condition, then the next
01069          * isc_mem_put for cache memory will do the right thing and trigger
01070          * water().
01071          */
01072 
01073         if (size == 0U || hiwater == 0U || lowater == 0U)
01074                 /*
01075                  * Disable cache memory limiting.
01076                  */
01077                 isc_mem_setwater(cache->mctx, water, cache, 0, 0);
01078         else
01079                 /*
01080                  * Establish new cache memory limits (either for the first
01081                  * time, or replacing other limits).
01082                  */
01083                 isc_mem_setwater(cache->mctx, water, cache, hiwater, lowater);
01084 }
01085 
01086 size_t
01087 dns_cache_getcachesize(dns_cache_t *cache) {
01088         size_t size;
01089 
01090         REQUIRE(VALID_CACHE(cache));
01091 
01092         LOCK(&cache->lock);
01093         size = cache->size;
01094         UNLOCK(&cache->lock);
01095 
01096         return (size);
01097 }
01098 
01099 /*
01100  * The cleaner task is shutting down; do the necessary cleanup.
01101  */
01102 static void
01103 cleaner_shutdown_action(isc_task_t *task, isc_event_t *event) {
01104         dns_cache_t *cache = event->ev_arg;
01105         isc_boolean_t should_free = ISC_FALSE;
01106 
01107         UNUSED(task);
01108 
01109         INSIST(task == cache->cleaner.task);
01110         INSIST(event->ev_type == ISC_TASKEVENT_SHUTDOWN);
01111 
01112         if (CLEANER_BUSY(&cache->cleaner))
01113                 end_cleaning(&cache->cleaner, event);
01114         else
01115                 isc_event_free(&event);
01116 
01117         LOCK(&cache->lock);
01118 
01119         cache->live_tasks--;
01120         INSIST(cache->live_tasks == 0);
01121 
01122         if (cache->references == 0)
01123                 should_free = ISC_TRUE;
01124 
01125         /*
01126          * By detaching the timer in the context of its task,
01127          * we are guaranteed that there will be no further timer
01128          * events.
01129          */
01130         if (cache->cleaner.cleaning_timer != NULL)
01131                 isc_timer_detach(&cache->cleaner.cleaning_timer);
01132 
01133         /* Make sure we don't reschedule anymore. */
01134         (void)isc_task_purge(task, NULL, DNS_EVENT_CACHECLEAN, NULL);
01135 
01136         UNLOCK(&cache->lock);
01137 
01138         if (should_free)
01139                 cache_free(cache);
01140 }
01141 
01142 isc_result_t
01143 dns_cache_flush(dns_cache_t *cache) {
01144         dns_db_t *db = NULL;
01145         isc_result_t result;
01146 
01147         result = cache_create_db(cache, &db);
01148         if (result != ISC_R_SUCCESS)
01149                 return (result);
01150 
01151         LOCK(&cache->lock);
01152         LOCK(&cache->cleaner.lock);
01153         if (cache->cleaner.state == cleaner_s_idle) {
01154                 if (cache->cleaner.iterator != NULL)
01155                         dns_dbiterator_destroy(&cache->cleaner.iterator);
01156                 (void) dns_db_createiterator(db, ISC_FALSE,
01157                                              &cache->cleaner.iterator);
01158         } else {
01159                 if (cache->cleaner.state == cleaner_s_busy)
01160                         cache->cleaner.state = cleaner_s_done;
01161                 cache->cleaner.replaceiterator = ISC_TRUE;
01162         }
01163         dns_db_detach(&cache->db);
01164         cache->db = db;
01165         dns_db_setcachestats(cache->db, cache->stats);
01166         UNLOCK(&cache->cleaner.lock);
01167         UNLOCK(&cache->lock);
01168 
01169         return (ISC_R_SUCCESS);
01170 }
01171 
01172 static isc_result_t
01173 clearnode(dns_db_t *db, dns_dbnode_t *node) {
01174         isc_result_t result;
01175         dns_rdatasetiter_t *iter = NULL;
01176 
01177         result = dns_db_allrdatasets(db, node, NULL, (isc_stdtime_t)0, &iter);
01178         if (result != ISC_R_SUCCESS)
01179                 return (result);
01180 
01181         for (result = dns_rdatasetiter_first(iter);
01182              result == ISC_R_SUCCESS;
01183              result = dns_rdatasetiter_next(iter))
01184         {
01185                 dns_rdataset_t rdataset;
01186                 dns_rdataset_init(&rdataset);
01187 
01188                 dns_rdatasetiter_current(iter, &rdataset);
01189                 result = dns_db_deleterdataset(db, node, NULL,
01190                                                rdataset.type, rdataset.covers);
01191                 dns_rdataset_disassociate(&rdataset);
01192                 if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED)
01193                         break;
01194         }
01195 
01196         if (result == ISC_R_NOMORE)
01197                 result = ISC_R_SUCCESS;
01198 
01199         dns_rdatasetiter_destroy(&iter);
01200         return (result);
01201 }
01202 
01203 static isc_result_t
01204 cleartree(dns_db_t *db, dns_name_t *name) {
01205         isc_result_t result, answer = ISC_R_SUCCESS;
01206         dns_dbiterator_t *iter = NULL;
01207         dns_dbnode_t *node = NULL;
01208         dns_fixedname_t fnodename;
01209         dns_name_t *nodename;
01210 
01211         dns_fixedname_init(&fnodename);
01212         nodename = dns_fixedname_name(&fnodename);
01213 
01214         result = dns_db_createiterator(db, 0, &iter);
01215         if (result != ISC_R_SUCCESS)
01216                 goto cleanup;
01217 
01218         result = dns_dbiterator_seek(iter, name);
01219         if (result != ISC_R_SUCCESS)
01220                 goto cleanup;
01221 
01222         while (result == ISC_R_SUCCESS) {
01223                 result = dns_dbiterator_current(iter, &node, nodename);
01224                 if (result == DNS_R_NEWORIGIN)
01225                         result = ISC_R_SUCCESS;
01226                 if (result != ISC_R_SUCCESS)
01227                         goto cleanup;
01228                 /*
01229                  * Are we done?
01230                  */
01231                 if (! dns_name_issubdomain(nodename, name))
01232                         goto cleanup;
01233 
01234                 /*
01235                  * If clearnode fails record and move onto the next node.
01236                  */
01237                 result = clearnode(db, node);
01238                 if (result != ISC_R_SUCCESS && answer == ISC_R_SUCCESS)
01239                         answer = result;
01240                 dns_db_detachnode(db, &node);
01241                 result = dns_dbiterator_next(iter);
01242         }
01243 
01244  cleanup:
01245         if (result == ISC_R_NOMORE || result == ISC_R_NOTFOUND)
01246                 result = ISC_R_SUCCESS;
01247         if (result != ISC_R_SUCCESS && answer == ISC_R_SUCCESS)
01248                 answer = result;
01249         if (node != NULL)
01250                 dns_db_detachnode(db, &node);
01251         if (iter != NULL)
01252                 dns_dbiterator_destroy(&iter);
01253 
01254         return (answer);
01255 }
01256 
01257 isc_result_t
01258 dns_cache_flushname(dns_cache_t *cache, dns_name_t *name) {
01259         return (dns_cache_flushnode(cache, name, ISC_FALSE));
01260 }
01261 
01262 isc_result_t
01263 dns_cache_flushnode(dns_cache_t *cache, dns_name_t *name,
01264                     isc_boolean_t tree)
01265 {
01266         isc_result_t result;
01267         dns_dbnode_t *node = NULL;
01268         dns_db_t *db = NULL;
01269 
01270         if (dns_name_equal(name, dns_rootname))
01271                 return (dns_cache_flush(cache));
01272 
01273         LOCK(&cache->lock);
01274         if (cache->db != NULL)
01275                 dns_db_attach(cache->db, &db);
01276         UNLOCK(&cache->lock);
01277         if (db == NULL)
01278                 return (ISC_R_SUCCESS);
01279 
01280         if (tree) {
01281                 result = cleartree(cache->db, name);
01282         } else {
01283                 result = dns_db_findnode(cache->db, name, ISC_FALSE, &node);
01284                 if (result == ISC_R_NOTFOUND) {
01285                         result = ISC_R_SUCCESS;
01286                         goto cleanup_db;
01287                 }
01288                 if (result != ISC_R_SUCCESS)
01289                         goto cleanup_db;
01290                 result = clearnode(cache->db, node);
01291                 dns_db_detachnode(cache->db, &node);
01292         }
01293 
01294  cleanup_db:
01295         dns_db_detach(&db);
01296         return (result);
01297 }
01298 
01299 isc_stats_t *
01300 dns_cache_getstats(dns_cache_t *cache) {
01301         REQUIRE(VALID_CACHE(cache));
01302         return (cache->stats);
01303 }
01304 
01305 void
01306 dns_cache_updatestats(dns_cache_t *cache, isc_result_t result) {
01307         REQUIRE(VALID_CACHE(cache));
01308         if (cache->stats == NULL)
01309                 return;
01310 
01311         switch (result) {
01312         case ISC_R_SUCCESS:
01313         case DNS_R_NCACHENXDOMAIN:
01314         case DNS_R_NCACHENXRRSET:
01315         case DNS_R_CNAME:
01316         case DNS_R_DNAME:
01317         case DNS_R_GLUE:
01318         case DNS_R_ZONECUT:
01319                 isc_stats_increment(cache->stats,
01320                                     dns_cachestatscounter_queryhits);
01321                 break;
01322         default:
01323                 isc_stats_increment(cache->stats,
01324                                     dns_cachestatscounter_querymisses);
01325         }
01326 }
01327 
01328 /*
01329  * XXX: Much of the following code has been copied in from statschannel.c.
01330  * We should refactor this into a generic function in stats.c that can be
01331  * called from both places.
01332  */
01333 typedef struct
01334 cache_dumparg {
01335         isc_statsformat_t       type;
01336         void                    *arg;           /* type dependent argument */
01337         int                     ncounters;      /* for general statistics */
01338         int                     *counterindices; /* for general statistics */
01339         isc_uint64_t            *countervalues;  /* for general statistics */
01340         isc_result_t            result;
01341 } cache_dumparg_t;
01342 
01343 static void
01344 getcounter(isc_statscounter_t counter, isc_uint64_t val, void *arg) {
01345         cache_dumparg_t *dumparg = arg;
01346 
01347         REQUIRE(counter < dumparg->ncounters);
01348         dumparg->countervalues[counter] = val;
01349 }
01350 
01351 static void
01352 getcounters(isc_stats_t *stats, isc_statsformat_t type, int ncounters,
01353             int *indices, isc_uint64_t *values)
01354 {
01355         cache_dumparg_t dumparg;
01356 
01357         memset(values, 0, sizeof(values[0]) * ncounters);
01358 
01359         dumparg.type = type;
01360         dumparg.ncounters = ncounters;
01361         dumparg.counterindices = indices;
01362         dumparg.countervalues = values;
01363 
01364         isc_stats_dump(stats, getcounter, &dumparg, ISC_STATSDUMP_VERBOSE);
01365 }
01366 
01367 void
01368 dns_cache_dumpstats(dns_cache_t *cache, FILE *fp) {
01369         int indices[dns_cachestatscounter_max];
01370         isc_uint64_t values[dns_cachestatscounter_max];
01371 
01372         REQUIRE(VALID_CACHE(cache));
01373 
01374         getcounters(cache->stats, isc_statsformat_file,
01375                     dns_cachestatscounter_max, indices, values);
01376 
01377         fprintf(fp, "%20" ISC_PRINT_QUADFORMAT "u %s\n",
01378                 values[dns_cachestatscounter_hits],
01379                 "cache hits");
01380         fprintf(fp, "%20" ISC_PRINT_QUADFORMAT "u %s\n",
01381                 values[dns_cachestatscounter_misses],
01382                 "cache misses");
01383         fprintf(fp, "%20" ISC_PRINT_QUADFORMAT "u %s\n",
01384                 values[dns_cachestatscounter_queryhits],
01385                 "cache hits (from query)");
01386         fprintf(fp, "%20" ISC_PRINT_QUADFORMAT "u %s\n",
01387                 values[dns_cachestatscounter_querymisses],
01388                 "cache misses (from query)");
01389         fprintf(fp, "%20" ISC_PRINT_QUADFORMAT "u %s\n",
01390                 values[dns_cachestatscounter_deletelru],
01391                 "cache records deleted due to memory exhaustion");
01392         fprintf(fp, "%20" ISC_PRINT_QUADFORMAT "u %s\n",
01393                 values[dns_cachestatscounter_deletettl],
01394                 "cache records deleted due to TTL expiration");
01395         fprintf(fp, "%20u %s\n", dns_db_nodecount(cache->db),
01396                 "cache database nodes");
01397         fprintf(fp, "%20u %s\n", dns_db_hashsize(cache->db),
01398                 "cache database hash buckets");
01399 
01400         fprintf(fp, "%20u %s\n", (unsigned int) isc_mem_total(cache->mctx),
01401                 "cache tree memory total");
01402         fprintf(fp, "%20u %s\n", (unsigned int) isc_mem_inuse(cache->mctx),
01403                 "cache tree memory in use");
01404         fprintf(fp, "%20u %s\n", (unsigned int) isc_mem_maxinuse(cache->mctx),
01405                 "cache tree highest memory in use");
01406 
01407         fprintf(fp, "%20u %s\n", (unsigned int) isc_mem_total(cache->hmctx),
01408                 "cache heap memory total");
01409         fprintf(fp, "%20u %s\n", (unsigned int) isc_mem_inuse(cache->hmctx),
01410                 "cache heap memory in use");
01411         fprintf(fp, "%20u %s\n", (unsigned int) isc_mem_maxinuse(cache->hmctx),
01412                 "cache heap highest memory in use");
01413 }
01414 
01415 #ifdef HAVE_LIBXML2
01416 #define TRY0(a) do { xmlrc = (a); if (xmlrc < 0) goto error; } while(0)
01417 static int
01418 renderstat(const char *name, isc_uint64_t value, xmlTextWriterPtr writer) {
01419         int xmlrc;
01420 
01421         TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter"));
01422         TRY0(xmlTextWriterWriteAttribute(writer,
01423                                          ISC_XMLCHAR "name", ISC_XMLCHAR name));
01424         TRY0(xmlTextWriterWriteFormatString(writer,
01425                                             "%" ISC_PRINT_QUADFORMAT "u",
01426                                             value));
01427         TRY0(xmlTextWriterEndElement(writer)); /* counter */
01428 
01429 error:
01430         return (xmlrc);
01431 }
01432 
01433 int
01434 dns_cache_renderxml(dns_cache_t *cache, xmlTextWriterPtr writer) {
01435         int indices[dns_cachestatscounter_max];
01436         isc_uint64_t values[dns_cachestatscounter_max];
01437         int xmlrc;
01438 
01439         REQUIRE(VALID_CACHE(cache));
01440 
01441         getcounters(cache->stats, isc_statsformat_file,
01442                     dns_cachestatscounter_max, indices, values);
01443         TRY0(renderstat("CacheHits",
01444                    values[dns_cachestatscounter_hits], writer));
01445         TRY0(renderstat("CacheMisses",
01446                    values[dns_cachestatscounter_misses], writer));
01447         TRY0(renderstat("QueryHits",
01448                    values[dns_cachestatscounter_queryhits], writer));
01449         TRY0(renderstat("QueryMisses",
01450                    values[dns_cachestatscounter_querymisses], writer));
01451         TRY0(renderstat("DeleteLRU",
01452                    values[dns_cachestatscounter_deletelru], writer));
01453         TRY0(renderstat("DeleteTTL",
01454                    values[dns_cachestatscounter_deletettl], writer));
01455 
01456         TRY0(renderstat("CacheNodes", dns_db_nodecount(cache->db), writer));
01457         TRY0(renderstat("CacheBuckets", dns_db_hashsize(cache->db), writer));
01458 
01459         TRY0(renderstat("TreeMemTotal", isc_mem_total(cache->mctx), writer));
01460         TRY0(renderstat("TreeMemInUse", isc_mem_inuse(cache->mctx), writer));
01461         TRY0(renderstat("TreeMemMax", isc_mem_maxinuse(cache->mctx), writer));
01462 
01463         TRY0(renderstat("HeapMemTotal", isc_mem_total(cache->hmctx), writer));
01464         TRY0(renderstat("HeapMemInUse", isc_mem_inuse(cache->hmctx), writer));
01465         TRY0(renderstat("HeapMemMax", isc_mem_maxinuse(cache->hmctx), writer));
01466 error:
01467         return (xmlrc);
01468 }
01469 #endif
01470 
01471 #ifdef HAVE_JSON
01472 #define CHECKMEM(m) do { \
01473         if (m == NULL) { \
01474                 result = ISC_R_NOMEMORY;\
01475                 goto error;\
01476         } \
01477 } while(0)
01478 
01479 isc_result_t
01480 dns_cache_renderjson(dns_cache_t *cache, json_object *cstats) {
01481         isc_result_t result = ISC_R_SUCCESS;
01482         int indices[dns_cachestatscounter_max];
01483         isc_uint64_t values[dns_cachestatscounter_max];
01484         json_object *obj;
01485 
01486         REQUIRE(VALID_CACHE(cache));
01487 
01488         getcounters(cache->stats, isc_statsformat_file,
01489                     dns_cachestatscounter_max, indices, values);
01490 
01491         obj = json_object_new_int64(values[dns_cachestatscounter_hits]);
01492         CHECKMEM(obj);
01493         json_object_object_add(cstats, "CacheHits", obj);
01494 
01495         obj = json_object_new_int64(values[dns_cachestatscounter_misses]);
01496         CHECKMEM(obj);
01497         json_object_object_add(cstats, "CacheMisses", obj);
01498 
01499         obj = json_object_new_int64(values[dns_cachestatscounter_queryhits]);
01500         CHECKMEM(obj);
01501         json_object_object_add(cstats, "QueryHits", obj);
01502 
01503         obj = json_object_new_int64(values[dns_cachestatscounter_querymisses]);
01504         CHECKMEM(obj);
01505         json_object_object_add(cstats, "QueryMisses", obj);
01506 
01507         obj = json_object_new_int64(values[dns_cachestatscounter_deletelru]);
01508         CHECKMEM(obj);
01509         json_object_object_add(cstats, "DeleteLRU", obj);
01510 
01511         obj = json_object_new_int64(values[dns_cachestatscounter_deletettl]);
01512         CHECKMEM(obj);
01513         json_object_object_add(cstats, "DeleteTTL", obj);
01514 
01515         obj = json_object_new_int64(dns_db_nodecount(cache->db));
01516         CHECKMEM(obj);
01517         json_object_object_add(cstats, "CacheNodes", obj);
01518 
01519         obj = json_object_new_int64(dns_db_hashsize(cache->db));
01520         CHECKMEM(obj);
01521         json_object_object_add(cstats, "CacheBuckets", obj);
01522 
01523         obj = json_object_new_int64(isc_mem_total(cache->mctx));
01524         CHECKMEM(obj);
01525         json_object_object_add(cstats, "TreeMemTotal", obj);
01526 
01527         obj = json_object_new_int64(isc_mem_inuse(cache->mctx));
01528         CHECKMEM(obj);
01529         json_object_object_add(cstats, "TreeMemInUse", obj);
01530 
01531         obj = json_object_new_int64(isc_mem_maxinuse(cache->mctx));
01532         CHECKMEM(obj);
01533         json_object_object_add(cstats, "HeapMemMax", obj);
01534 
01535         obj = json_object_new_int64(isc_mem_total(cache->hmctx));
01536         CHECKMEM(obj);
01537         json_object_object_add(cstats, "HeapMemTotal", obj);
01538 
01539         obj = json_object_new_int64(isc_mem_inuse(cache->hmctx));
01540         CHECKMEM(obj);
01541         json_object_object_add(cstats, "HeapMemInUse", obj);
01542 
01543         obj = json_object_new_int64(isc_mem_maxinuse(cache->hmctx));
01544         CHECKMEM(obj);
01545         json_object_object_add(cstats, "HeapMemMax", obj);
01546 
01547         result = ISC_R_SUCCESS;
01548 error:
01549         return (result);
01550 }
01551 #endif

Generated on Tue Apr 28 17:40:55 2015 by Doxygen 1.5.4 for BIND9 Internals 9.11.0pre-alpha