acache.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2004-2008, 2012, 2013  Internet Systems Consortium, Inc. ("ISC")
00003  *
00004  * Permission to use, copy, modify, and/or distribute this software for any
00005  * purpose with or without fee is hereby granted, provided that the above
00006  * copyright notice and this permission notice appear in all copies.
00007  *
00008  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
00009  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
00010  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
00011  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
00012  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
00013  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
00014  * PERFORMANCE OF THIS SOFTWARE.
00015  */
00016 
00017 /* $Id: acache.c,v 1.22 2008/02/07 23:46:54 tbox Exp $ */
00018 
00019 #include <config.h>
00020 
00021 #include <isc/atomic.h>
00022 #include <isc/event.h>
00023 #include <isc/hash.h>
00024 #include <isc/magic.h>
00025 #include <isc/mem.h>
00026 #include <isc/mutex.h>
00027 #include <isc/random.h>
00028 #include <isc/refcount.h>
00029 #include <isc/rwlock.h>
00030 #include <isc/serial.h>
00031 #include <isc/task.h>
00032 #include <isc/time.h>
00033 #include <isc/timer.h>
00034 
00035 #include <dns/acache.h>
00036 #include <dns/db.h>
00037 #include <dns/events.h>
00038 #include <dns/log.h>
00039 #include <dns/message.h>
00040 #include <dns/name.h>
00041 #include <dns/rdataset.h>
00042 #include <dns/result.h>
00043 #include <dns/zone.h>
00044 
00045 #define ACACHE_MAGIC                    ISC_MAGIC('A', 'C', 'H', 'E')
00046 #define DNS_ACACHE_VALID(acache)        ISC_MAGIC_VALID(acache, ACACHE_MAGIC)
00047 
00048 #define ACACHEENTRY_MAGIC               ISC_MAGIC('A', 'C', 'E', 'T')
00049 #define DNS_ACACHEENTRY_VALID(entry)    ISC_MAGIC_VALID(entry, ACACHEENTRY_MAGIC)
00050 
00051 #define DBBUCKETS       67
00052 
00053 #if 0
00054 #define ATRACE(m)       isc_log_write(dns_lctx, \
00055                                       DNS_LOGCATEGORY_DATABASE, \
00056                                       DNS_LOGMODULE_ACACHE, \
00057                                       ISC_LOG_DEBUG(3), \
00058                                       "acache %p: %s", acache, (m))
00059 #define AATRACE(a,m)    isc_log_write(dns_lctx, \
00060                                       DNS_LOGCATEGORY_DATABASE, \
00061                                       DNS_LOGMODULE_ACACHE, \
00062                                       ISC_LOG_DEBUG(3), \
00063                                       "acache %p: %s", (a), (m))
00064 #else
00065 #define ATRACE(m)
00066 #define AATRACE(a, m)
00067 #endif
00068 
00069 /*
00070  * The following variables control incremental cleaning.
00071  * MINSIZE is how many bytes is the floor for dns_acache_setcachesize().
00072  * CLEANERINCREMENT is how many entries are examined in one pass.
00073  * (XXX simply derived from definitions in cache.c  There may be better
00074  *  constants here.)
00075  */
00076 #define DNS_ACACHE_MINSIZE              2097152U /* Bytes.  2097152 = 2 MB */
00077 #define DNS_ACACHE_CLEANERINCREMENT     1000     /* Number of entries. */
00078 
00079 #define DEFAULT_ACACHE_ENTRY_LOCK_COUNT 1009     /*%< Should be prime. */
00080 
00081 #if defined(ISC_RWLOCK_USEATOMIC) && defined(ISC_PLATFORM_HAVEATOMICSTORE)
00082 #define ACACHE_USE_RWLOCK 1
00083 #endif
00084 
00085 #ifdef ACACHE_USE_RWLOCK
00086 #define ACACHE_INITLOCK(l)      isc_rwlock_init((l), 0, 0)
00087 #define ACACHE_DESTROYLOCK(l)   isc_rwlock_destroy(l)
00088 #define ACACHE_LOCK(l, t)       RWLOCK((l), (t))
00089 #define ACACHE_UNLOCK(l, t)     RWUNLOCK((l), (t))
00090 
00091 #define acache_storetime(entry, t) \
00092         (isc_atomic_store((isc_int32_t *)&(entry)->lastused, (t)))
00093 #else
00094 #define ACACHE_INITLOCK(l)      isc_mutex_init(l)
00095 #define ACACHE_DESTROYLOCK(l)   DESTROYLOCK(l)
00096 #define ACACHE_LOCK(l, t)       LOCK(l)
00097 #define ACACHE_UNLOCK(l, t)     UNLOCK(l)
00098 
00099 #define acache_storetime(entry, t) ((entry)->lastused = (t))
00100 #endif
00101 
00102 /* Locked by acache lock */
00103 typedef struct dbentry {
00104         ISC_LINK(struct dbentry)        link;
00105 
00106         dns_db_t                        *db;
00107         ISC_LIST(dns_acacheentry_t)     originlist;
00108         ISC_LIST(dns_acacheentry_t)     referlist;
00109 } dbentry_t;
00110 
00111 typedef ISC_LIST(dbentry_t) dbentrylist_t;
00112 
00113 typedef struct acache_cleaner acache_cleaner_t;
00114 
00115 typedef enum {
00116         cleaner_s_idle, /* Waiting for cleaning-interval to expire. */
00117         cleaner_s_busy, /* Currently cleaning. */
00118         cleaner_s_done  /* Freed enough memory after being overmem. */
00119 } cleaner_state_t;
00120 
00121 /*
00122  * Convenience macros for comprehensive assertion checking.
00123  */
00124 #define CLEANER_IDLE(c) ((c)->state == cleaner_s_idle && \
00125                          (c)->resched_event != NULL)
00126 #define CLEANER_BUSY(c) ((c)->state == cleaner_s_busy && \
00127                          (c)->resched_event == NULL)
00128 
00129 struct acache_cleaner {
00130         isc_mutex_t             lock;
00131         /*
00132          * Locks overmem_event, overmem.  (See cache.c)
00133          */
00134 
00135         dns_acache_t            *acache;
00136         unsigned int            cleaning_interval; /* The cleaning-interval
00137                                                       from named.conf,
00138                                                       in seconds. */
00139 
00140         isc_stdtime_t           last_cleanup_time; /* The time when the last
00141                                                       cleanup task completed */
00142 
00143         isc_timer_t             *cleaning_timer;
00144         isc_event_t             *resched_event; /* Sent by cleaner task to
00145                                                    itself to reschedule */
00146         isc_event_t             *overmem_event;
00147 
00148         dns_acacheentry_t       *current_entry; /* The bookmark entry to
00149                                                    restart the cleaning.
00150                                                    Locked by acache lock. */
00151         int                     increment;      /* Number of entries to
00152                                                    clean in one increment */
00153 
00154         unsigned long           ncleaned;       /* Number of entries cleaned
00155                                                    up (for logging purposes) */
00156         cleaner_state_t         state;          /* Idle/Busy/Done. */
00157         isc_boolean_t           overmem;        /* The acache is in an overmem
00158                                                    state. */
00159 };
00160 
00161 struct dns_acachestats {
00162         unsigned int                    hits;
00163         unsigned int                    queries;
00164         unsigned int                    misses;
00165         unsigned int                    adds;
00166         unsigned int                    deleted;
00167         unsigned int                    cleaned;
00168         unsigned int                    cleaner_runs;
00169         unsigned int                    overmem;
00170         unsigned int                    overmem_nocreates;
00171         unsigned int                    nomem;
00172 };
00173 
00174 /*
00175  * The actual acache object.
00176  */
00177 
00178 struct dns_acache {
00179         unsigned int                    magic;
00180 
00181         isc_mem_t                       *mctx;
00182         isc_refcount_t                  refs;
00183 
00184 #ifdef ACACHE_USE_RWLOCK
00185         isc_rwlock_t                    *entrylocks;
00186 #else
00187         isc_mutex_t                     *entrylocks;
00188 #endif
00189 
00190         isc_mutex_t                     lock;
00191 
00192         int                             live_cleaners;
00193         acache_cleaner_t                cleaner;
00194         ISC_LIST(dns_acacheentry_t)     entries;
00195         unsigned int                    dbentries;
00196         dbentrylist_t                   dbbucket[DBBUCKETS];
00197 
00198         isc_boolean_t                   shutting_down;
00199 
00200         isc_task_t                      *task;
00201         isc_event_t                     cevent;
00202         isc_boolean_t                   cevent_sent;
00203 
00204         dns_acachestats_t               stats;
00205 };
00206 
00207 struct dns_acacheentry {
00208         unsigned int            magic;
00209 
00210         unsigned int            locknum;
00211         isc_refcount_t          references;
00212 
00213         dns_acache_t            *acache;
00214 
00215         /* Data for Management of cache entries */
00216         ISC_LINK(dns_acacheentry_t) link;
00217         ISC_LINK(dns_acacheentry_t) olink;
00218         ISC_LINK(dns_acacheentry_t) rlink;
00219 
00220         dns_db_t                *origdb; /* reference to the DB
00221                                             holding this entry */
00222 
00223         /* Cache data */
00224         dns_zone_t              *zone;          /* zone this entry
00225                                                    belongs to */
00226         dns_db_t                *db;            /* DB this entry belongs to */
00227         dns_dbversion_t         *version;       /* the version of the DB */
00228         dns_dbnode_t            *node;          /* node this entry
00229                                                    belongs to */
00230         dns_name_t              *foundname;     /* corresponding DNS name
00231                                                    and rdataset */
00232 
00233         /* Callback function and its argument */
00234         void                    (*callback)(dns_acacheentry_t *, void **);
00235         void                    *cbarg;
00236 
00237         /* Timestamp of the last time this entry is referred to */
00238         isc_stdtime32_t         lastused;
00239 };
00240 
00241 /*
00242  *      Internal functions (and prototypes).
00243  */
00244 static inline isc_boolean_t check_noentry(dns_acache_t *acache);
00245 static void destroy(dns_acache_t *acache);
00246 static void shutdown_entries(dns_acache_t *acache);
00247 static void shutdown_buckets(dns_acache_t *acache);
00248 static void destroy_entry(dns_acacheentry_t *ent);
00249 static inline void unlink_dbentries(dns_acache_t *acache,
00250                                     dns_acacheentry_t *ent);
00251 static inline isc_result_t finddbent(dns_acache_t *acache,
00252                                      dns_db_t *db, dbentry_t **dbentryp);
00253 static inline void clear_entry(dns_acache_t *acache, dns_acacheentry_t *entry);
00254 static isc_result_t acache_cleaner_init(dns_acache_t *acache,
00255                                         isc_timermgr_t *timermgr,
00256                                         acache_cleaner_t *cleaner);
00257 static void acache_cleaning_timer_action(isc_task_t *task, isc_event_t *event);
00258 static void acache_incremental_cleaning_action(isc_task_t *task,
00259                                                isc_event_t *event);
00260 static void acache_overmem_cleaning_action(isc_task_t *task,
00261                                            isc_event_t *event);
00262 static void acache_cleaner_shutdown_action(isc_task_t *task,
00263                                            isc_event_t *event);
00264 
00265 /*
00266  * acache should be locked.  If it is not, the stats can get out of whack,
00267  * which is not a big deal for us since this is for debugging / stats
00268  */
00269 static void
00270 reset_stats(dns_acache_t *acache) {
00271         acache->stats.hits = 0;
00272         acache->stats.queries = 0;
00273         acache->stats.misses = 0;
00274         acache->stats.adds = 0;
00275         acache->stats.deleted = 0;
00276         acache->stats.cleaned = 0;
00277         acache->stats.overmem = 0;
00278         acache->stats.overmem_nocreates = 0;
00279         acache->stats.nomem = 0;
00280 }
00281 
00282 /*
00283  * The acache must be locked before calling.
00284  */
00285 static inline isc_boolean_t
00286 check_noentry(dns_acache_t *acache) {
00287         if (ISC_LIST_EMPTY(acache->entries) && acache->dbentries == 0) {
00288                 return (ISC_TRUE);
00289         }
00290 
00291         return (ISC_FALSE);
00292 }
00293 
00294 /*
00295  * The acache must be locked before calling.
00296  */
00297 static void
00298 shutdown_entries(dns_acache_t *acache) {
00299         dns_acacheentry_t *entry, *entry_next;
00300 
00301         REQUIRE(DNS_ACACHE_VALID(acache));
00302         INSIST(acache->shutting_down);
00303 
00304         /*
00305          * Release the dependency of all entries, and detach them.
00306          */
00307         for (entry = ISC_LIST_HEAD(acache->entries);
00308              entry != NULL;
00309              entry = entry_next) {
00310                 entry_next = ISC_LIST_NEXT(entry, link);
00311 
00312                 ACACHE_LOCK(&acache->entrylocks[entry->locknum],
00313                             isc_rwlocktype_write);
00314 
00315                 /*
00316                  * If the cleaner holds this entry, it will be unlinked and
00317                  * freed in the cleaner later.
00318                  */
00319                 if (acache->cleaner.current_entry != entry)
00320                         ISC_LIST_UNLINK(acache->entries, entry, link);
00321                 unlink_dbentries(acache, entry);
00322                 if (entry->callback != NULL) {
00323                         (entry->callback)(entry, &entry->cbarg);
00324                         entry->callback = NULL;
00325                 }
00326 
00327                 ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
00328                               isc_rwlocktype_write);
00329 
00330                 if (acache->cleaner.current_entry != entry)
00331                         dns_acache_detachentry(&entry);
00332         }
00333 }
00334 
00335 /*
00336  * The acache must be locked before calling.
00337  */
00338 static void
00339 shutdown_buckets(dns_acache_t *acache) {
00340         int i;
00341         dbentry_t *dbent;
00342 
00343         REQUIRE(DNS_ACACHE_VALID(acache));
00344         INSIST(acache->shutting_down);
00345 
00346         for (i = 0; i < DBBUCKETS; i++) {
00347                 while ((dbent = ISC_LIST_HEAD(acache->dbbucket[i])) != NULL) {
00348                         INSIST(ISC_LIST_EMPTY(dbent->originlist) &&
00349                                ISC_LIST_EMPTY(dbent->referlist));
00350                         ISC_LIST_UNLINK(acache->dbbucket[i], dbent, link);
00351 
00352                         dns_db_detach(&dbent->db);
00353 
00354                         isc_mem_put(acache->mctx, dbent, sizeof(*dbent));
00355 
00356                         acache->dbentries--;
00357                 }
00358         }
00359 
00360         INSIST(acache->dbentries == 0);
00361 }
00362 
00363 static void
00364 shutdown_task(isc_task_t *task, isc_event_t *ev) {
00365         dns_acache_t *acache;
00366 
00367         UNUSED(task);
00368 
00369         acache = ev->ev_arg;
00370         INSIST(DNS_ACACHE_VALID(acache));
00371 
00372         isc_event_free(&ev);
00373 
00374         LOCK(&acache->lock);
00375 
00376         shutdown_entries(acache);
00377         shutdown_buckets(acache);
00378 
00379         UNLOCK(&acache->lock);
00380 
00381         dns_acache_detach(&acache);
00382 }
00383 
00384 /* The acache and the entry must be locked before calling. */
00385 static inline void
00386 unlink_dbentries(dns_acache_t *acache, dns_acacheentry_t *ent) {
00387         isc_result_t result;
00388         dbentry_t *dbent;
00389 
00390         if (ISC_LINK_LINKED(ent, olink)) {
00391                 INSIST(ent->origdb != NULL);
00392                 dbent = NULL;
00393                 result = finddbent(acache, ent->origdb, &dbent);
00394                 INSIST(result == ISC_R_SUCCESS);
00395 
00396                 ISC_LIST_UNLINK(dbent->originlist, ent, olink);
00397         }
00398         if (ISC_LINK_LINKED(ent, rlink)) {
00399                 INSIST(ent->db != NULL);
00400                 dbent = NULL;
00401                 result = finddbent(acache, ent->db, &dbent);
00402                 INSIST(result == ISC_R_SUCCESS);
00403 
00404                 ISC_LIST_UNLINK(dbent->referlist, ent, rlink);
00405         }
00406 }
00407 
00408 /* There must not be a reference to this entry. */
00409 static void
00410 destroy_entry(dns_acacheentry_t *entry) {
00411         dns_acache_t *acache;
00412 
00413         REQUIRE(DNS_ACACHEENTRY_VALID(entry));
00414 
00415         acache = entry->acache;
00416         REQUIRE(DNS_ACACHE_VALID(acache));
00417 
00418         /*
00419          * Since there is no reference to this entry, it is safe to call
00420          * clear_entry() here.
00421          */
00422         clear_entry(acache, entry);
00423 
00424         isc_mem_put(acache->mctx, entry, sizeof(*entry));
00425 
00426         dns_acache_detach(&acache);
00427 }
00428 
00429 static void
00430 destroy(dns_acache_t *acache) {
00431         int i;
00432 
00433         REQUIRE(DNS_ACACHE_VALID(acache));
00434 
00435         ATRACE("destroy");
00436 
00437         isc_mem_setwater(acache->mctx, NULL, NULL, 0, 0);
00438 
00439         if (acache->cleaner.overmem_event != NULL)
00440                 isc_event_free(&acache->cleaner.overmem_event);
00441 
00442         if (acache->cleaner.resched_event != NULL)
00443                 isc_event_free(&acache->cleaner.resched_event);
00444 
00445         if (acache->task != NULL)
00446                 isc_task_detach(&acache->task);
00447 
00448         for (i = 0; i < DEFAULT_ACACHE_ENTRY_LOCK_COUNT; i++)
00449                 ACACHE_DESTROYLOCK(&acache->entrylocks[i]);
00450         isc_mem_put(acache->mctx, acache->entrylocks,
00451                     sizeof(*acache->entrylocks) *
00452                     DEFAULT_ACACHE_ENTRY_LOCK_COUNT);
00453 
00454         DESTROYLOCK(&acache->cleaner.lock);
00455 
00456         DESTROYLOCK(&acache->lock);
00457         acache->magic = 0;
00458 
00459         isc_mem_putanddetach(&acache->mctx, acache, sizeof(*acache));
00460 }
00461 
00462 static inline isc_result_t
00463 finddbent(dns_acache_t *acache, dns_db_t *db, dbentry_t **dbentryp) {
00464         int bucket;
00465         dbentry_t *dbentry;
00466 
00467         REQUIRE(DNS_ACACHE_VALID(acache));
00468         REQUIRE(db != NULL);
00469         REQUIRE(dbentryp != NULL && *dbentryp == NULL);
00470 
00471         /*
00472          * The caller must be holding the acache lock.
00473          */
00474 
00475         bucket = isc_hash_calc((const unsigned char *)&db,
00476                                sizeof(db), ISC_TRUE) % DBBUCKETS;
00477 
00478         for (dbentry = ISC_LIST_HEAD(acache->dbbucket[bucket]);
00479              dbentry != NULL;
00480              dbentry = ISC_LIST_NEXT(dbentry, link)) {
00481                 if (dbentry->db == db)
00482                         break;
00483         }
00484 
00485         *dbentryp = dbentry;
00486 
00487         if (dbentry == NULL)
00488                 return (ISC_R_NOTFOUND);
00489         else
00490                 return (ISC_R_SUCCESS);
00491 }
00492 
00493 static inline void
00494 clear_entry(dns_acache_t *acache, dns_acacheentry_t *entry) {
00495         REQUIRE(DNS_ACACHE_VALID(acache));
00496         REQUIRE(DNS_ACACHEENTRY_VALID(entry));
00497 
00498         /*
00499          * The caller must be holing the entry lock.
00500          */
00501 
00502         if (entry->foundname) {
00503                 dns_rdataset_t *rdataset, *rdataset_next;
00504 
00505                 for (rdataset = ISC_LIST_HEAD(entry->foundname->list);
00506                      rdataset != NULL;
00507                      rdataset = rdataset_next) {
00508                         rdataset_next = ISC_LIST_NEXT(rdataset, link);
00509                         ISC_LIST_UNLINK(entry->foundname->list,
00510                                         rdataset, link);
00511                         dns_rdataset_disassociate(rdataset);
00512                         isc_mem_put(acache->mctx, rdataset, sizeof(*rdataset));
00513                 }
00514                 if (dns_name_dynamic(entry->foundname))
00515                         dns_name_free(entry->foundname, acache->mctx);
00516                 isc_mem_put(acache->mctx, entry->foundname,
00517                             sizeof(*entry->foundname));
00518                 entry->foundname = NULL;
00519         }
00520 
00521         if (entry->node != NULL) {
00522                 INSIST(entry->db != NULL);
00523                 dns_db_detachnode(entry->db, &entry->node);
00524         }
00525         if (entry->version != NULL) {
00526                 INSIST(entry->db != NULL);
00527                 dns_db_closeversion(entry->db, &entry->version, ISC_FALSE);
00528         }
00529         if (entry->db != NULL)
00530                 dns_db_detach(&entry->db);
00531         if (entry->zone != NULL)
00532                 dns_zone_detach(&entry->zone);
00533 
00534         if (entry->origdb != NULL)
00535                 dns_db_detach(&entry->origdb);
00536 }
00537 
00538 static isc_result_t
00539 acache_cleaner_init(dns_acache_t *acache, isc_timermgr_t *timermgr,
00540                     acache_cleaner_t *cleaner)
00541 {
00542         int result;
00543 
00544         ATRACE("acache cleaner init");
00545 
00546         result = isc_mutex_init(&cleaner->lock);
00547         if (result != ISC_R_SUCCESS)
00548                 goto fail;
00549 
00550         cleaner->increment = DNS_ACACHE_CLEANERINCREMENT;
00551         cleaner->state = cleaner_s_idle;
00552         cleaner->acache = acache;
00553         cleaner->overmem = ISC_FALSE;
00554 
00555         cleaner->cleaning_timer = NULL;
00556         cleaner->resched_event = NULL;
00557         cleaner->overmem_event = NULL;
00558         cleaner->current_entry = NULL;
00559 
00560         if (timermgr != NULL) {
00561                 cleaner->acache->live_cleaners++;
00562 
00563                 result = isc_task_onshutdown(acache->task,
00564                                              acache_cleaner_shutdown_action,
00565                                              acache);
00566                 if (result != ISC_R_SUCCESS) {
00567                         UNEXPECTED_ERROR(__FILE__, __LINE__,
00568                                          "acache cleaner: "
00569                                          "isc_task_onshutdown() failed: %s",
00570                                          dns_result_totext(result));
00571                         goto cleanup;
00572                 }
00573 
00574                 cleaner->cleaning_interval = 0; /* Initially turned off. */
00575                 isc_stdtime_get(&cleaner->last_cleanup_time);
00576                 result = isc_timer_create(timermgr, isc_timertype_inactive,
00577                                           NULL, NULL,
00578                                           acache->task,
00579                                           acache_cleaning_timer_action,
00580                                           cleaner, &cleaner->cleaning_timer);
00581                 if (result != ISC_R_SUCCESS) {
00582                         UNEXPECTED_ERROR(__FILE__, __LINE__,
00583                                          "isc_timer_create() failed: %s",
00584                                          dns_result_totext(result));
00585                         result = ISC_R_UNEXPECTED;
00586                         goto cleanup;
00587                 }
00588 
00589                 cleaner->resched_event =
00590                         isc_event_allocate(acache->mctx, cleaner,
00591                                            DNS_EVENT_ACACHECLEAN,
00592                                            acache_incremental_cleaning_action,
00593                                            cleaner, sizeof(isc_event_t));
00594                 if (cleaner->resched_event == NULL) {
00595                         result = ISC_R_NOMEMORY;
00596                         goto cleanup;
00597                 }
00598 
00599                 cleaner->overmem_event =
00600                         isc_event_allocate(acache->mctx, cleaner,
00601                                            DNS_EVENT_ACACHEOVERMEM,
00602                                            acache_overmem_cleaning_action,
00603                                            cleaner, sizeof(isc_event_t));
00604                 if (cleaner->overmem_event == NULL) {
00605                         result = ISC_R_NOMEMORY;
00606                         goto cleanup;
00607                 }
00608         }
00609 
00610         return (ISC_R_SUCCESS);
00611 
00612  cleanup:
00613         if (cleaner->overmem_event != NULL)
00614                 isc_event_free(&cleaner->overmem_event);
00615         if (cleaner->resched_event != NULL)
00616                 isc_event_free(&cleaner->resched_event);
00617         if (cleaner->cleaning_timer != NULL)
00618                 isc_timer_detach(&cleaner->cleaning_timer);
00619         cleaner->acache->live_cleaners--;
00620         DESTROYLOCK(&cleaner->lock);
00621  fail:
00622         return (result);
00623 }
00624 
00625 static void
00626 begin_cleaning(acache_cleaner_t *cleaner) {
00627         dns_acacheentry_t *head;
00628         dns_acache_t *acache = cleaner->acache;
00629 
00630         /*
00631          * This function does not have to lock the cleaner, since critical
00632          * parameters (except current_entry, which is locked by acache lock,)
00633          * are only used in a single task context.
00634          */
00635 
00636         REQUIRE(CLEANER_IDLE(cleaner));
00637         INSIST(DNS_ACACHE_VALID(acache));
00638         INSIST(cleaner->current_entry == NULL);
00639 
00640         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
00641                       DNS_LOGMODULE_ACACHE, ISC_LOG_DEBUG(1),
00642                       "begin acache cleaning, mem inuse %lu",
00643                       (unsigned long)isc_mem_inuse(cleaner->acache->mctx));
00644 
00645         LOCK(&acache->lock);
00646 
00647         head = ISC_LIST_HEAD(acache->entries);
00648         if (head != NULL)
00649                 dns_acache_attachentry(head, &cleaner->current_entry);
00650 
00651         UNLOCK(&acache->lock);
00652 
00653         if (cleaner->current_entry != NULL) {
00654                 cleaner->ncleaned = 0;
00655                 cleaner->state = cleaner_s_busy;
00656                 isc_task_send(acache->task, &cleaner->resched_event);
00657         }
00658 
00659         return;
00660 }
00661 
00662 static void
00663 end_cleaning(acache_cleaner_t *cleaner, isc_event_t *event) {
00664         dns_acache_t *acache = cleaner->acache;
00665 
00666         REQUIRE(CLEANER_BUSY(cleaner));
00667         REQUIRE(event != NULL);
00668         REQUIRE(DNS_ACACHEENTRY_VALID(cleaner->current_entry));
00669 
00670         /* No need to lock the cleaner (see begin_cleaning()). */
00671 
00672         LOCK(&acache->lock);
00673 
00674         /*
00675          * Even if the cleaner has the last reference to the entry, which means
00676          * the entry has been unused, it may still be linked if unlinking the
00677          * entry has been delayed due to the reference.
00678          */
00679         if (isc_refcount_current(&cleaner->current_entry->references) == 1) {
00680                 INSIST(cleaner->current_entry->callback == NULL);
00681 
00682                 if (ISC_LINK_LINKED(cleaner->current_entry, link)) {
00683                         ISC_LIST_UNLINK(acache->entries,
00684                                         cleaner->current_entry, link);
00685                 }
00686         }
00687         dns_acache_detachentry(&cleaner->current_entry);
00688 
00689         if (cleaner->overmem)
00690                 acache->stats.overmem++;
00691         acache->stats.cleaned += cleaner->ncleaned;
00692         acache->stats.cleaner_runs++;
00693 
00694         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE,
00695                       ISC_LOG_NOTICE,
00696                       "acache %p stats: hits=%d misses=%d queries=%d "
00697                       "adds=%d deleted=%d "
00698                       "cleaned=%d cleaner_runs=%d overmem=%d "
00699                       "overmem_nocreates=%d nomem=%d",
00700                       acache,
00701                       acache->stats.hits, acache->stats.misses,
00702                       acache->stats.queries,
00703                       acache->stats.adds, acache->stats.deleted,
00704                       acache->stats.cleaned, acache->stats.cleaner_runs,
00705                       acache->stats.overmem, acache->stats.overmem_nocreates,
00706                       acache->stats.nomem);
00707         reset_stats(acache);
00708 
00709         isc_stdtime_get(&cleaner->last_cleanup_time);
00710 
00711         UNLOCK(&acache->lock);
00712 
00713         dns_acache_setcleaninginterval(cleaner->acache,
00714                                        cleaner->cleaning_interval);
00715 
00716         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE,
00717                       ISC_LOG_DEBUG(1), "end acache cleaning, "
00718                       "%lu entries cleaned, mem inuse %lu",
00719                       cleaner->ncleaned,
00720                       (unsigned long)isc_mem_inuse(cleaner->acache->mctx));
00721 
00722         if (cleaner->overmem) {
00723                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
00724                               DNS_LOGMODULE_ACACHE, ISC_LOG_NOTICE,
00725                               "acache is still in overmem state "
00726                               "after cleaning");
00727         }
00728 
00729         cleaner->ncleaned = 0;
00730         cleaner->state = cleaner_s_idle;
00731         cleaner->resched_event = event;
00732 }
00733 
00734 /*
00735  * This is run once for every acache-cleaning-interval as defined
00736  * in named.conf.
00737  */
00738 static void
00739 acache_cleaning_timer_action(isc_task_t *task, isc_event_t *event) {
00740         acache_cleaner_t *cleaner = event->ev_arg;
00741 
00742         UNUSED(task);
00743 
00744         INSIST(event->ev_type == ISC_TIMEREVENT_TICK);
00745 
00746         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE,
00747                       ISC_LOG_DEBUG(1), "acache cleaning timer fired, "
00748                       "cleaner state = %d", cleaner->state);
00749 
00750         if (cleaner->state == cleaner_s_idle)
00751                 begin_cleaning(cleaner);
00752 
00753         isc_event_free(&event);
00754 }
00755 
00756 /* The caller must hold entry lock. */
00757 static inline isc_boolean_t
00758 entry_stale(acache_cleaner_t *cleaner, dns_acacheentry_t *entry,
00759             isc_stdtime32_t now32, unsigned int interval)
00760 {
00761         /*
00762          * If the callback has been canceled, we definitely do not need the
00763          * entry.
00764          */
00765         if (entry->callback == NULL)
00766                 return (ISC_TRUE);
00767 
00768         if (interval > cleaner->cleaning_interval)
00769                 interval = cleaner->cleaning_interval;
00770 
00771         if (entry->lastused + interval < now32)
00772                 return (ISC_TRUE);
00773 
00774         /*
00775          * If the acache is in the overmem state, probabilistically decide if
00776          * the entry should be purged, based on the time passed from its last
00777          * use and the cleaning interval.
00778          */
00779         if (cleaner->overmem) {
00780                 unsigned int passed;
00781                 isc_uint32_t val;
00782 
00783                 if (isc_serial_ge(now32, entry->lastused))
00784                         passed = now32 - entry->lastused; /* <= interval */
00785                 else
00786                         passed = 0;
00787 
00788                 if (passed > interval / 2)
00789                         return (ISC_TRUE);
00790                 isc_random_get(&val);
00791                 if (passed > interval / 4)
00792                         return (ISC_TF(val % 4 == 0));
00793                 return (ISC_TF(val % 8 == 0));
00794         }
00795 
00796         return (ISC_FALSE);
00797 }
00798 
00799 /*
00800  * Do incremental cleaning.
00801  */
00802 static void
00803 acache_incremental_cleaning_action(isc_task_t *task, isc_event_t *event) {
00804         acache_cleaner_t *cleaner = event->ev_arg;
00805         dns_acache_t *acache = cleaner->acache;
00806         dns_acacheentry_t *entry, *next = NULL;
00807         int n_entries;
00808         isc_stdtime32_t now32, last32;
00809         isc_stdtime_t now;
00810         unsigned int interval;
00811 
00812         INSIST(DNS_ACACHE_VALID(acache));
00813         INSIST(task == acache->task);
00814         INSIST(event->ev_type == DNS_EVENT_ACACHECLEAN);
00815 
00816         if (cleaner->state == cleaner_s_done) {
00817                 cleaner->state = cleaner_s_busy;
00818                 end_cleaning(cleaner, event);
00819                 return;
00820         }
00821 
00822         INSIST(CLEANER_BUSY(cleaner));
00823 
00824         n_entries = cleaner->increment;
00825 
00826         isc_stdtime_get(&now);
00827         isc_stdtime_convert32(now, &now32);
00828 
00829         LOCK(&acache->lock);
00830 
00831         entry = cleaner->current_entry;
00832         isc_stdtime_convert32(cleaner->last_cleanup_time, &last32);
00833         if (isc_serial_ge(now32, last32))
00834                 interval = now32 - last32;
00835         else
00836                 interval = 0;
00837 
00838         while (n_entries-- > 0) {
00839                 isc_boolean_t is_stale = ISC_FALSE;
00840 
00841                 INSIST(entry != NULL);
00842 
00843                 next = ISC_LIST_NEXT(entry, link);
00844 
00845                 ACACHE_LOCK(&acache->entrylocks[entry->locknum],
00846                             isc_rwlocktype_write);
00847 
00848                 is_stale = entry_stale(cleaner, entry, now32, interval);
00849                 if (is_stale) {
00850                         ISC_LIST_UNLINK(acache->entries, entry, link);
00851                         unlink_dbentries(acache, entry);
00852                         if (entry->callback != NULL)
00853                                 (entry->callback)(entry, &entry->cbarg);
00854                         entry->callback = NULL;
00855 
00856                         cleaner->ncleaned++;
00857                 }
00858 
00859                 ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
00860                               isc_rwlocktype_write);
00861 
00862                 if (is_stale)
00863                         dns_acache_detachentry(&entry);
00864 
00865                 if (next == NULL) {
00866                         if (cleaner->overmem) {
00867                                 entry = ISC_LIST_HEAD(acache->entries);
00868                                 if (entry != NULL) {
00869                                         /*
00870                                          * If we are still in the overmem
00871                                          * state, keep cleaning.  In case we
00872                                          * exit from the loop immediately after
00873                                          * this, reset next to the head entry
00874                                          * as we'll expect it will be never
00875                                          * NULL.
00876                                          */
00877                                         isc_log_write(dns_lctx,
00878                                                       DNS_LOGCATEGORY_DATABASE,
00879                                                       DNS_LOGMODULE_ACACHE,
00880                                                       ISC_LOG_DEBUG(1),
00881                                                       "acache cleaner: "
00882                                                       "still overmem, "
00883                                                       "reset and try again");
00884                                         next = entry;
00885                                         continue;
00886                                 }
00887                         }
00888 
00889                         UNLOCK(&acache->lock);
00890                         end_cleaning(cleaner, event);
00891                         return;
00892                 }
00893 
00894                 entry = next;
00895         }
00896 
00897         /*
00898          * We have successfully performed a cleaning increment but have
00899          * not gone through the entire cache.  Remember the entry that will
00900          * be the starting point in the next clean-up, and reschedule another
00901          * batch.  If it fails, just try to continue anyway.
00902          */
00903         INSIST(next != NULL);
00904         dns_acache_detachentry(&cleaner->current_entry);
00905         dns_acache_attachentry(next, &cleaner->current_entry);
00906 
00907         UNLOCK(&acache->lock);
00908 
00909         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE,
00910                       ISC_LOG_DEBUG(1), "acache cleaner: checked %d entries, "
00911                       "mem inuse %lu, sleeping", cleaner->increment,
00912                       (unsigned long)isc_mem_inuse(cleaner->acache->mctx));
00913 
00914         isc_task_send(task, &event);
00915         INSIST(CLEANER_BUSY(cleaner));
00916 
00917         return;
00918 }
00919 
00920 /*
00921  * This is called when the acache either surpasses its upper limit
00922  * or shrinks beyond its lower limit.
00923  */
00924 static void
00925 acache_overmem_cleaning_action(isc_task_t *task, isc_event_t *event) {
00926         acache_cleaner_t *cleaner = event->ev_arg;
00927         isc_boolean_t want_cleaning = ISC_FALSE;
00928 
00929         UNUSED(task);
00930 
00931         INSIST(event->ev_type == DNS_EVENT_ACACHEOVERMEM);
00932         INSIST(cleaner->overmem_event == NULL);
00933 
00934         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE,
00935                       ISC_LOG_DEBUG(1), "overmem_cleaning_action called, "
00936                       "overmem = %d, state = %d", cleaner->overmem,
00937                       cleaner->state);
00938 
00939         LOCK(&cleaner->lock);
00940 
00941         if (cleaner->overmem) {
00942                 if (cleaner->state == cleaner_s_idle)
00943                         want_cleaning = ISC_TRUE;
00944         } else {
00945                 if (cleaner->state == cleaner_s_busy)
00946                         /*
00947                          * end_cleaning() can't be called here because
00948                          * then both cleaner->overmem_event and
00949                          * cleaner->resched_event will point to this
00950                          * event.  Set the state to done, and then
00951                          * when the acache_incremental_cleaning_action() event
00952                          * is posted, it will handle the end_cleaning.
00953                          */
00954                         cleaner->state = cleaner_s_done;
00955         }
00956 
00957         cleaner->overmem_event = event;
00958 
00959         UNLOCK(&cleaner->lock);
00960 
00961         if (want_cleaning)
00962                 begin_cleaning(cleaner);
00963 }
00964 
00965 static void
00966 water(void *arg, int mark) {
00967         dns_acache_t *acache = arg;
00968         isc_boolean_t overmem = ISC_TF(mark == ISC_MEM_HIWATER);
00969 
00970         REQUIRE(DNS_ACACHE_VALID(acache));
00971 
00972         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
00973                       DNS_LOGMODULE_ACACHE, ISC_LOG_DEBUG(1),
00974                       "acache memory reaches %s watermark, mem inuse %lu",
00975                       overmem ? "high" : "low",
00976                       (unsigned long)isc_mem_inuse(acache->mctx));
00977 
00978         LOCK(&acache->cleaner.lock);
00979 
00980         if (acache->cleaner.overmem != overmem) {
00981                 acache->cleaner.overmem = overmem;
00982 
00983                 if (acache->cleaner.overmem_event != NULL)
00984                         isc_task_send(acache->task,
00985                                       &acache->cleaner.overmem_event);
00986                 isc_mem_waterack(acache->mctx, mark);
00987         }
00988 
00989         UNLOCK(&acache->cleaner.lock);
00990 }
00991 
00992 /*
00993  * The cleaner task is shutting down; do the necessary cleanup.
00994  */
00995 static void
00996 acache_cleaner_shutdown_action(isc_task_t *task, isc_event_t *event) {
00997         dns_acache_t *acache = event->ev_arg;
00998         isc_boolean_t should_free = ISC_FALSE;
00999 
01000         INSIST(task == acache->task);
01001         INSIST(event->ev_type == ISC_TASKEVENT_SHUTDOWN);
01002         INSIST(DNS_ACACHE_VALID(acache));
01003 
01004         ATRACE("acache cleaner shutdown");
01005 
01006         if (CLEANER_BUSY(&acache->cleaner))
01007                 end_cleaning(&acache->cleaner, event);
01008         else
01009                 isc_event_free(&event);
01010 
01011         LOCK(&acache->lock);
01012 
01013         acache->live_cleaners--;
01014         INSIST(acache->live_cleaners == 0);
01015 
01016         if (isc_refcount_current(&acache->refs) == 0) {
01017                 INSIST(check_noentry(acache) == ISC_TRUE);
01018                 should_free = ISC_TRUE;
01019         }
01020 
01021         /*
01022          * By detaching the timer in the context of its task,
01023          * we are guaranteed that there will be no further timer
01024          * events.
01025          */
01026         if (acache->cleaner.cleaning_timer != NULL)
01027                 isc_timer_detach(&acache->cleaner.cleaning_timer);
01028 
01029         /* Make sure we don't reschedule anymore. */
01030         (void)isc_task_purge(task, NULL, DNS_EVENT_ACACHECLEAN, NULL);
01031 
01032         UNLOCK(&acache->lock);
01033 
01034         if (should_free)
01035                 destroy(acache);
01036 }
01037 
01038 /*
01039  *      Public functions.
01040  */
01041 
01042 isc_result_t
01043 dns_acache_create(dns_acache_t **acachep, isc_mem_t *mctx,
01044                   isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr)
01045 {
01046         int i;
01047         isc_result_t result;
01048         dns_acache_t *acache;
01049 
01050         REQUIRE(acachep != NULL && *acachep == NULL);
01051         REQUIRE(mctx != NULL);
01052         REQUIRE(taskmgr != NULL);
01053 
01054         acache = isc_mem_get(mctx, sizeof(*acache));
01055         if (acache == NULL)
01056                 return (ISC_R_NOMEMORY);
01057 
01058         ATRACE("create");
01059 
01060         result = isc_refcount_init(&acache->refs, 1);
01061         if (result != ISC_R_SUCCESS) {
01062                 isc_mem_put(mctx, acache, sizeof(*acache));
01063                 return (result);
01064         }
01065 
01066         result = isc_mutex_init(&acache->lock);
01067         if (result != ISC_R_SUCCESS) {
01068                 isc_refcount_decrement(&acache->refs, NULL);
01069                 isc_refcount_destroy(&acache->refs);
01070                 isc_mem_put(mctx, acache, sizeof(*acache));
01071                 return (result);
01072         }
01073 
01074         acache->mctx = NULL;
01075         isc_mem_attach(mctx, &acache->mctx);
01076         ISC_LIST_INIT(acache->entries);
01077 
01078         acache->shutting_down = ISC_FALSE;
01079 
01080         acache->task = NULL;
01081         acache->entrylocks = NULL;
01082 
01083         result = isc_task_create(taskmgr, 1, &acache->task);
01084         if (result != ISC_R_SUCCESS) {
01085                 UNEXPECTED_ERROR(__FILE__, __LINE__,
01086                                  "isc_task_create() failed(): %s",
01087                                  dns_result_totext(result));
01088                 result = ISC_R_UNEXPECTED;
01089                 goto cleanup;
01090         }
01091         isc_task_setname(acache->task, "acachetask", acache);
01092         ISC_EVENT_INIT(&acache->cevent, sizeof(acache->cevent), 0, NULL,
01093                        DNS_EVENT_ACACHECONTROL, shutdown_task, NULL,
01094                        NULL, NULL, NULL);
01095         acache->cevent_sent = ISC_FALSE;
01096 
01097         acache->dbentries = 0;
01098         for (i = 0; i < DBBUCKETS; i++)
01099                 ISC_LIST_INIT(acache->dbbucket[i]);
01100 
01101         acache->entrylocks = isc_mem_get(mctx, sizeof(*acache->entrylocks) *
01102                                          DEFAULT_ACACHE_ENTRY_LOCK_COUNT);
01103         if (acache->entrylocks == NULL) {
01104                 result = ISC_R_NOMEMORY;
01105                 goto cleanup;
01106         }
01107         for (i = 0; i < DEFAULT_ACACHE_ENTRY_LOCK_COUNT; i++) {
01108                 result = ACACHE_INITLOCK(&acache->entrylocks[i]);
01109                 if (result != ISC_R_SUCCESS) {
01110                         while (i-- > 0)
01111                                 ACACHE_DESTROYLOCK(&acache->entrylocks[i]);
01112                         isc_mem_put(mctx, acache->entrylocks,
01113                                     sizeof(*acache->entrylocks) *
01114                                     DEFAULT_ACACHE_ENTRY_LOCK_COUNT);
01115                         acache->entrylocks = NULL;
01116                         goto cleanup;
01117                 }
01118         }
01119 
01120         acache->live_cleaners = 0;
01121         result = acache_cleaner_init(acache, timermgr, &acache->cleaner);
01122         if (result != ISC_R_SUCCESS)
01123                 goto cleanup;
01124 
01125         acache->stats.cleaner_runs = 0;
01126         reset_stats(acache);
01127 
01128         acache->magic = ACACHE_MAGIC;
01129 
01130         *acachep = acache;
01131         return (ISC_R_SUCCESS);
01132 
01133  cleanup:
01134         if (acache->task != NULL)
01135                 isc_task_detach(&acache->task);
01136         DESTROYLOCK(&acache->lock);
01137         isc_refcount_decrement(&acache->refs, NULL);
01138         isc_refcount_destroy(&acache->refs);
01139         if (acache->entrylocks != NULL) {
01140                 for (i = 0; i < DEFAULT_ACACHE_ENTRY_LOCK_COUNT; i++)
01141                         ACACHE_DESTROYLOCK(&acache->entrylocks[i]);
01142                 isc_mem_put(mctx, acache->entrylocks,
01143                             sizeof(*acache->entrylocks) *
01144                             DEFAULT_ACACHE_ENTRY_LOCK_COUNT);
01145         }
01146         isc_mem_put(mctx, acache, sizeof(*acache));
01147         isc_mem_detach(&mctx);
01148 
01149         return (result);
01150 }
01151 
01152 void
01153 dns_acache_attach(dns_acache_t *source, dns_acache_t **targetp) {
01154         REQUIRE(DNS_ACACHE_VALID(source));
01155         REQUIRE(targetp != NULL && *targetp == NULL);
01156 
01157         AATRACE(source, "attach");
01158 
01159         isc_refcount_increment(&source->refs, NULL);
01160 
01161         *targetp = source;
01162 }
01163 
01164 void
01165 dns_acache_countquerymiss(dns_acache_t *acache) {
01166         acache->stats.misses++;         /* XXXSK danger: unlocked! */
01167         acache->stats.queries++;        /* XXXSK danger: unlocked! */
01168 }
01169 
01170 void
01171 dns_acache_detach(dns_acache_t **acachep) {
01172         dns_acache_t *acache;
01173         unsigned int refs;
01174         isc_boolean_t should_free = ISC_FALSE;
01175 
01176         REQUIRE(acachep != NULL && DNS_ACACHE_VALID(*acachep));
01177         acache = *acachep;
01178 
01179         ATRACE("detach");
01180 
01181         isc_refcount_decrement(&acache->refs, &refs);
01182         if (refs == 0) {
01183                 INSIST(check_noentry(acache) == ISC_TRUE);
01184                 should_free = ISC_TRUE;
01185         }
01186 
01187         *acachep = NULL;
01188 
01189         /*
01190          * If we're exiting and the cleaner task exists, let it free the cache.
01191          */
01192         if (should_free && acache->live_cleaners > 0) {
01193                 isc_task_shutdown(acache->task);
01194                 should_free = ISC_FALSE;
01195         }
01196 
01197         if (should_free)
01198                 destroy(acache);
01199 }
01200 
01201 void
01202 dns_acache_shutdown(dns_acache_t *acache) {
01203         REQUIRE(DNS_ACACHE_VALID(acache));
01204 
01205         LOCK(&acache->lock);
01206 
01207         ATRACE("shutdown");
01208 
01209         if (!acache->shutting_down) {
01210                 isc_event_t *event;
01211                 dns_acache_t *acache_evarg = NULL;
01212 
01213                 INSIST(!acache->cevent_sent);
01214 
01215                 acache->shutting_down = ISC_TRUE;
01216 
01217                 isc_mem_setwater(acache->mctx, NULL, NULL, 0, 0);
01218 
01219                 /*
01220                  * Self attach the object in order to prevent it from being
01221                  * destroyed while waiting for the event.
01222                  */
01223                 dns_acache_attach(acache, &acache_evarg);
01224                 event = &acache->cevent;
01225                 event->ev_arg = acache_evarg;
01226                 isc_task_send(acache->task, &event);
01227                 acache->cevent_sent = ISC_TRUE;
01228         }
01229 
01230         UNLOCK(&acache->lock);
01231 }
01232 
01233 isc_result_t
01234 dns_acache_setdb(dns_acache_t *acache, dns_db_t *db) {
01235         int bucket;
01236         dbentry_t *dbentry;
01237         isc_result_t result = ISC_R_SUCCESS;
01238 
01239         REQUIRE(DNS_ACACHE_VALID(acache));
01240         REQUIRE(db != NULL);
01241 
01242         ATRACE("setdb");
01243 
01244         LOCK(&acache->lock);
01245 
01246         dbentry = NULL;
01247         result = finddbent(acache, db, &dbentry);
01248         if (result == ISC_R_SUCCESS) {
01249                 result = ISC_R_EXISTS;
01250                 goto end;
01251         }
01252         result = ISC_R_SUCCESS;
01253 
01254         dbentry = isc_mem_get(acache->mctx, sizeof(*dbentry));
01255         if (dbentry == NULL) {
01256                 result = ISC_R_NOMEMORY;
01257                 goto end;
01258         }
01259 
01260         ISC_LINK_INIT(dbentry, link);
01261         ISC_LIST_INIT(dbentry->originlist);
01262         ISC_LIST_INIT(dbentry->referlist);
01263 
01264         dbentry->db = NULL;
01265         dns_db_attach(db, &dbentry->db);
01266 
01267         bucket = isc_hash_calc((const unsigned char *)&db,
01268                                sizeof(db), ISC_TRUE) % DBBUCKETS;
01269 
01270         ISC_LIST_APPEND(acache->dbbucket[bucket], dbentry, link);
01271 
01272         acache->dbentries++;
01273 
01274  end:
01275         UNLOCK(&acache->lock);
01276 
01277         return (result);
01278 }
01279 
01280 isc_result_t
01281 dns_acache_putdb(dns_acache_t *acache, dns_db_t *db) {
01282         int bucket;
01283         isc_result_t result;
01284         dbentry_t *dbentry;
01285         dns_acacheentry_t *entry;
01286 
01287         REQUIRE(DNS_ACACHE_VALID(acache));
01288         REQUIRE(db != NULL);
01289 
01290         ATRACE("putdb");
01291 
01292         LOCK(&acache->lock);
01293 
01294         dbentry = NULL;
01295         result = finddbent(acache, db, &dbentry);
01296         if (result != ISC_R_SUCCESS) {
01297                 /*
01298                  * The entry may have not been created due to memory shortage.
01299                  */
01300                 UNLOCK(&acache->lock);
01301                 return (ISC_R_NOTFOUND);
01302         }
01303 
01304         /*
01305          * Release corresponding cache entries: for each entry, release all
01306          * links the entry has, and then callback to the entry holder (if any).
01307          * If no other external references exist (this can happen if the
01308          * original holder has canceled callback,) destroy it here.
01309          */
01310         while ((entry = ISC_LIST_HEAD(dbentry->originlist)) != NULL) {
01311                 ACACHE_LOCK(&acache->entrylocks[entry->locknum],
01312                             isc_rwlocktype_write);
01313 
01314                 /*
01315                  * Releasing olink first would avoid finddbent() in
01316                  * unlink_dbentries().
01317                  */
01318                 ISC_LIST_UNLINK(dbentry->originlist, entry, olink);
01319                 if (acache->cleaner.current_entry != entry)
01320                         ISC_LIST_UNLINK(acache->entries, entry, link);
01321                 unlink_dbentries(acache, entry);
01322 
01323                 if (entry->callback != NULL)
01324                         (entry->callback)(entry, &entry->cbarg);
01325                 entry->callback = NULL;
01326 
01327                 ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
01328                               isc_rwlocktype_write);
01329 
01330                 if (acache->cleaner.current_entry != entry)
01331                         dns_acache_detachentry(&entry);
01332         }
01333         while ((entry = ISC_LIST_HEAD(dbentry->referlist)) != NULL) {
01334                 ACACHE_LOCK(&acache->entrylocks[entry->locknum],
01335                             isc_rwlocktype_write);
01336 
01337                 ISC_LIST_UNLINK(dbentry->referlist, entry, rlink);
01338                 if (acache->cleaner.current_entry != entry)
01339                         ISC_LIST_UNLINK(acache->entries, entry, link);
01340                 unlink_dbentries(acache, entry);
01341 
01342                 if (entry->callback != NULL)
01343                         (entry->callback)(entry, &entry->cbarg);
01344                 entry->callback = NULL;
01345 
01346                 ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
01347                               isc_rwlocktype_write);
01348 
01349                 if (acache->cleaner.current_entry != entry)
01350                         dns_acache_detachentry(&entry);
01351         }
01352 
01353         INSIST(ISC_LIST_EMPTY(dbentry->originlist) &&
01354                ISC_LIST_EMPTY(dbentry->referlist));
01355 
01356         bucket = isc_hash_calc((const unsigned char *)&db,
01357                                sizeof(db), ISC_TRUE) % DBBUCKETS;
01358         ISC_LIST_UNLINK(acache->dbbucket[bucket], dbentry, link);
01359         dns_db_detach(&dbentry->db);
01360 
01361         isc_mem_put(acache->mctx, dbentry, sizeof(*dbentry));
01362 
01363         acache->dbentries--;
01364 
01365         acache->stats.deleted++;
01366 
01367         UNLOCK(&acache->lock);
01368 
01369         return (ISC_R_SUCCESS);
01370 }
01371 
01372 isc_result_t
01373 dns_acache_createentry(dns_acache_t *acache, dns_db_t *origdb,
01374                        void (*callback)(dns_acacheentry_t *, void **),
01375                        void *cbarg, dns_acacheentry_t **entryp)
01376 {
01377         dns_acacheentry_t *newentry;
01378         isc_result_t result;
01379         isc_uint32_t r;
01380 
01381         REQUIRE(DNS_ACACHE_VALID(acache));
01382         REQUIRE(entryp != NULL && *entryp == NULL);
01383         REQUIRE(origdb != NULL);
01384 
01385         /*
01386          * Should we exceed our memory limit for some reason (for
01387          * example, if the cleaner does not run aggressively enough),
01388          * then we will not create additional entries.
01389          *
01390          * XXXSK: It might be better to lock the acache->cleaner->lock,
01391          * but locking may be an expensive bottleneck. If we misread
01392          * the value, we will occasionally refuse to create a few
01393          * cache entries, or create a few that we should not. I do not
01394          * expect this to happen often, and it will not have very bad
01395          * effects when it does. So no lock for now.
01396          */
01397         if (acache->cleaner.overmem) {
01398                 acache->stats.overmem_nocreates++; /* XXXSK danger: unlocked! */
01399                 return (ISC_R_NORESOURCES);
01400         }
01401 
01402         newentry = isc_mem_get(acache->mctx, sizeof(*newentry));
01403         if (newentry == NULL) {
01404                 acache->stats.nomem++;  /* XXXMLG danger: unlocked! */
01405                 return (ISC_R_NOMEMORY);
01406         }
01407 
01408         isc_random_get(&r);
01409         newentry->locknum = r % DEFAULT_ACACHE_ENTRY_LOCK_COUNT;
01410 
01411         result = isc_refcount_init(&newentry->references, 1);
01412         if (result != ISC_R_SUCCESS) {
01413                 isc_mem_put(acache->mctx, newentry, sizeof(*newentry));
01414                 return (result);
01415         };
01416 
01417         ISC_LINK_INIT(newentry, link);
01418         ISC_LINK_INIT(newentry, olink);
01419         ISC_LINK_INIT(newentry, rlink);
01420 
01421         newentry->acache = NULL;
01422         dns_acache_attach(acache, &newentry->acache);
01423 
01424         newentry->zone = NULL;
01425         newentry->db = NULL;
01426         newentry->version = NULL;
01427         newentry->node = NULL;
01428         newentry->foundname = NULL;
01429 
01430         newentry->callback = callback;
01431         newentry->cbarg = cbarg;
01432         newentry->origdb = NULL;
01433         dns_db_attach(origdb, &newentry->origdb);
01434 
01435         isc_stdtime_get(&newentry->lastused);
01436 
01437         newentry->magic = ACACHEENTRY_MAGIC;
01438 
01439         *entryp = newentry;
01440 
01441         return (ISC_R_SUCCESS);
01442 }
01443 
01444 isc_result_t
01445 dns_acache_getentry(dns_acacheentry_t *entry, dns_zone_t **zonep,
01446                     dns_db_t **dbp, dns_dbversion_t **versionp,
01447                     dns_dbnode_t **nodep, dns_name_t *fname,
01448                     dns_message_t *msg, isc_stdtime_t now)
01449 {
01450         isc_result_t result = ISC_R_SUCCESS;
01451         dns_rdataset_t *erdataset;
01452         isc_stdtime32_t now32;
01453         dns_acache_t *acache;
01454         int locknum;
01455 
01456         REQUIRE(DNS_ACACHEENTRY_VALID(entry));
01457         REQUIRE(zonep == NULL || *zonep == NULL);
01458         REQUIRE(dbp != NULL && *dbp == NULL);
01459         REQUIRE(versionp != NULL && *versionp == NULL);
01460         REQUIRE(nodep != NULL && *nodep == NULL);
01461         REQUIRE(fname != NULL);
01462         REQUIRE(msg != NULL);
01463         acache = entry->acache;
01464         REQUIRE(DNS_ACACHE_VALID(acache));
01465 
01466         locknum = entry->locknum;
01467         ACACHE_LOCK(&acache->entrylocks[locknum], isc_rwlocktype_read);
01468 
01469         isc_stdtime_convert32(now, &now32);
01470         acache_storetime(entry, now32);
01471 
01472         if (entry->zone != NULL && zonep != NULL)
01473                 dns_zone_attach(entry->zone, zonep);
01474 
01475         if (entry->db == NULL) {
01476                 *dbp = NULL;
01477                 *versionp = NULL;
01478         } else {
01479                 dns_db_attach(entry->db, dbp);
01480                 dns_db_attachversion(entry->db, entry->version, versionp);
01481         }
01482         if (entry->node == NULL)
01483                 *nodep = NULL;
01484         else {
01485                 dns_db_attachnode(entry->db, entry->node, nodep);
01486 
01487                 INSIST(entry->foundname != NULL);
01488                 dns_name_copy(entry->foundname, fname, NULL);
01489                 for (erdataset = ISC_LIST_HEAD(entry->foundname->list);
01490                      erdataset != NULL;
01491                      erdataset = ISC_LIST_NEXT(erdataset, link)) {
01492                         dns_rdataset_t *ardataset;
01493 
01494                         ardataset = NULL;
01495                         result = dns_message_gettemprdataset(msg, &ardataset);
01496                         if (result != ISC_R_SUCCESS) {
01497                                 ACACHE_UNLOCK(&acache->entrylocks[locknum],
01498                                               isc_rwlocktype_read);
01499                                 goto fail;
01500                         }
01501 
01502                         /*
01503                          * XXXJT: if we simply clone the rdataset, we'll get
01504                          * lost wrt cyclic ordering.  We'll need an additional
01505                          * trick to get the latest counter from the original
01506                          * header.
01507                          */
01508                         dns_rdataset_init(ardataset);
01509                         dns_rdataset_clone(erdataset, ardataset);
01510                         ISC_LIST_APPEND(fname->list, ardataset, link);
01511                 }
01512         }
01513 
01514         entry->acache->stats.hits++; /* XXXMLG danger: unlocked! */
01515         entry->acache->stats.queries++;
01516 
01517         ACACHE_UNLOCK(&acache->entrylocks[locknum], isc_rwlocktype_read);
01518 
01519         return (result);
01520 
01521   fail:
01522         while ((erdataset = ISC_LIST_HEAD(fname->list)) != NULL) {
01523                 ISC_LIST_UNLINK(fname->list, erdataset, link);
01524                 dns_rdataset_disassociate(erdataset);
01525                 dns_message_puttemprdataset(msg, &erdataset);
01526         }
01527         if (*nodep != NULL)
01528                 dns_db_detachnode(*dbp, nodep);
01529         if (*versionp != NULL)
01530                 dns_db_closeversion(*dbp, versionp, ISC_FALSE);
01531         if (*dbp != NULL)
01532                 dns_db_detach(dbp);
01533         if (zonep != NULL && *zonep != NULL)
01534                 dns_zone_detach(zonep);
01535 
01536         return (result);
01537 }
01538 
01539 isc_result_t
01540 dns_acache_setentry(dns_acache_t *acache, dns_acacheentry_t *entry,
01541                     dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version,
01542                     dns_dbnode_t *node, dns_name_t *fname)
01543 {
01544         isc_result_t result;
01545         dbentry_t *odbent;
01546         dbentry_t *rdbent = NULL;
01547         isc_boolean_t close_version = ISC_FALSE;
01548         dns_acacheentry_t *dummy_entry = NULL;
01549 
01550         REQUIRE(DNS_ACACHE_VALID(acache));
01551         REQUIRE(DNS_ACACHEENTRY_VALID(entry));
01552 
01553         LOCK(&acache->lock);    /* XXX: need to lock it here for ordering */
01554         ACACHE_LOCK(&acache->entrylocks[entry->locknum], isc_rwlocktype_write);
01555 
01556         /* Set zone */
01557         if (zone != NULL)
01558                 dns_zone_attach(zone, &entry->zone);
01559         /* Set DB */
01560         if (db != NULL)
01561                 dns_db_attach(db, &entry->db);
01562         /*
01563          * Set DB version.  If the version is not given by the caller,
01564          * which is the case for glue or cache DBs, use the current version.
01565          */
01566         if (version == NULL) {
01567                 if (db != NULL) {
01568                         dns_db_currentversion(db, &version);
01569                         close_version = ISC_TRUE;
01570                 }
01571         }
01572         if (version != NULL) {
01573                 INSIST(db != NULL);
01574                 dns_db_attachversion(db, version, &entry->version);
01575         }
01576         if (close_version)
01577                 dns_db_closeversion(db, &version, ISC_FALSE);
01578         /* Set DB node. */
01579         if (node != NULL) {
01580                 INSIST(db != NULL);
01581                 dns_db_attachnode(db, node, &entry->node);
01582         }
01583 
01584         /*
01585          * Set list of the corresponding rdatasets, if given.
01586          * To minimize the overhead and memory consumption, we'll do this for
01587          * positive cache only, in which case the DB node is non NULL.
01588          * We do not want to cache incomplete information, so give up the
01589          * entire entry when a memory shortage happen during the process.
01590          */
01591         if (node != NULL) {
01592                 dns_rdataset_t *ardataset, *crdataset;
01593 
01594                 entry->foundname = isc_mem_get(acache->mctx,
01595                                                sizeof(*entry->foundname));
01596 
01597                 if (entry->foundname == NULL) {
01598                         result = ISC_R_NOMEMORY;
01599                         goto fail;
01600                 }
01601                 dns_name_init(entry->foundname, NULL);
01602                 result = dns_name_dup(fname, acache->mctx,
01603                                       entry->foundname);
01604                 if (result != ISC_R_SUCCESS)
01605                         goto fail;
01606 
01607                 for (ardataset = ISC_LIST_HEAD(fname->list);
01608                      ardataset != NULL;
01609                      ardataset = ISC_LIST_NEXT(ardataset, link)) {
01610                         crdataset = isc_mem_get(acache->mctx,
01611                                                 sizeof(*crdataset));
01612                         if (crdataset == NULL) {
01613                                 result = ISC_R_NOMEMORY;
01614                                 goto fail;
01615                         }
01616 
01617                         dns_rdataset_init(crdataset);
01618                         dns_rdataset_clone(ardataset, crdataset);
01619                         ISC_LIST_APPEND(entry->foundname->list, crdataset,
01620                                         link);
01621                 }
01622         }
01623 
01624         odbent = NULL;
01625         result = finddbent(acache, entry->origdb, &odbent);
01626         if (result != ISC_R_SUCCESS)
01627                 goto fail;
01628         if (db != NULL) {
01629                 rdbent = NULL;
01630                 result = finddbent(acache, db, &rdbent);
01631                 if (result != ISC_R_SUCCESS)
01632                         goto fail;
01633         }
01634 
01635         ISC_LIST_APPEND(acache->entries, entry, link);
01636         ISC_LIST_APPEND(odbent->originlist, entry, olink);
01637         if (rdbent != NULL)
01638                 ISC_LIST_APPEND(rdbent->referlist, entry, rlink);
01639 
01640         /*
01641          * The additional cache needs an implicit reference to entries in its
01642          * link.
01643          */
01644         dns_acache_attachentry(entry, &dummy_entry);
01645 
01646         ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
01647                       isc_rwlocktype_write);
01648 
01649         acache->stats.adds++;
01650         UNLOCK(&acache->lock);
01651 
01652         return (ISC_R_SUCCESS);
01653 
01654  fail:
01655         clear_entry(acache, entry);
01656 
01657         ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
01658                       isc_rwlocktype_write);
01659         UNLOCK(&acache->lock);
01660 
01661         return (result);
01662 }
01663 
01664 isc_boolean_t
01665 dns_acache_cancelentry(dns_acacheentry_t *entry) {
01666         dns_acache_t *acache;
01667         isc_boolean_t callback_active;
01668 
01669         REQUIRE(DNS_ACACHEENTRY_VALID(entry));
01670 
01671         acache = entry->acache;
01672 
01673         INSIST(DNS_ACACHE_VALID(entry->acache));
01674 
01675         LOCK(&acache->lock);
01676         ACACHE_LOCK(&acache->entrylocks[entry->locknum], isc_rwlocktype_write);
01677 
01678         callback_active = ISC_TF(entry->cbarg != NULL);
01679 
01680         /*
01681          * Release dependencies stored in this entry as much as possible.
01682          * The main link cannot be released, since the acache object has
01683          * a reference to this entry; the empty entry will be released in
01684          * the next cleaning action.
01685          */
01686         unlink_dbentries(acache, entry);
01687         clear_entry(entry->acache, entry);
01688 
01689         entry->callback = NULL;
01690         entry->cbarg = NULL;
01691 
01692         ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
01693                       isc_rwlocktype_write);
01694         UNLOCK(&acache->lock);
01695 
01696         return (callback_active);
01697 }
01698 
01699 void
01700 dns_acache_attachentry(dns_acacheentry_t *source,
01701                        dns_acacheentry_t **targetp)
01702 {
01703         REQUIRE(DNS_ACACHEENTRY_VALID(source));
01704         REQUIRE(targetp != NULL && *targetp == NULL);
01705 
01706         isc_refcount_increment(&source->references, NULL);
01707 
01708         *targetp = source;
01709 }
01710 
01711 void
01712 dns_acache_detachentry(dns_acacheentry_t **entryp) {
01713         dns_acacheentry_t *entry;
01714         unsigned int refs;
01715 
01716         REQUIRE(entryp != NULL && DNS_ACACHEENTRY_VALID(*entryp));
01717         entry = *entryp;
01718 
01719         isc_refcount_decrement(&entry->references, &refs);
01720 
01721         /*
01722          * If there are no references to the entry, the entry must have been
01723          * unlinked and can be destroyed safely.
01724          */
01725         if (refs == 0) {
01726                 INSIST(!ISC_LINK_LINKED(entry, link));
01727                 (*entryp)->acache->stats.deleted++;
01728                 destroy_entry(entry);
01729         }
01730 
01731         *entryp = NULL;
01732 }
01733 
01734 void
01735 dns_acache_setcleaninginterval(dns_acache_t *acache, unsigned int t) {
01736         isc_interval_t interval;
01737         isc_result_t result;
01738 
01739         REQUIRE(DNS_ACACHE_VALID(acache));
01740 
01741         ATRACE("dns_acache_setcleaninginterval");
01742 
01743         LOCK(&acache->lock);
01744 
01745         /*
01746          * It may be the case that the acache has already shut down.
01747          * If so, it has no timer.  (Not sure if this can really happen.)
01748          */
01749         if (acache->cleaner.cleaning_timer == NULL)
01750                 goto unlock;
01751 
01752         acache->cleaner.cleaning_interval = t;
01753 
01754         if (t == 0) {
01755                 result = isc_timer_reset(acache->cleaner.cleaning_timer,
01756                                          isc_timertype_inactive,
01757                                          NULL, NULL, ISC_TRUE);
01758         } else {
01759                 isc_interval_set(&interval, acache->cleaner.cleaning_interval,
01760                                  0);
01761                 result = isc_timer_reset(acache->cleaner.cleaning_timer,
01762                                          isc_timertype_ticker,
01763                                          NULL, &interval, ISC_FALSE);
01764         }
01765         if (result != ISC_R_SUCCESS)
01766                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
01767                               DNS_LOGMODULE_ACACHE, ISC_LOG_WARNING,
01768                               "could not set acache cleaning interval: %s",
01769                               isc_result_totext(result));
01770         else
01771                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
01772                               DNS_LOGMODULE_ACACHE, ISC_LOG_NOTICE,
01773                               "acache %p cleaning interval set to %d.",
01774                               acache, t);
01775 
01776  unlock:
01777         UNLOCK(&acache->lock);
01778 }
01779 
01780 /*
01781  * This function was derived from cache.c:dns_cache_setcachesize().  See the
01782  * function for more details about the logic.
01783  */
01784 void
01785 dns_acache_setcachesize(dns_acache_t *acache, size_t size) {
01786         size_t hiwater, lowater;
01787 
01788         REQUIRE(DNS_ACACHE_VALID(acache));
01789 
01790         if (size != 0U && size < DNS_ACACHE_MINSIZE)
01791                 size = DNS_ACACHE_MINSIZE;
01792 
01793         hiwater = size - (size >> 3);
01794         lowater = size - (size >> 2);
01795 
01796         if (size == 0U || hiwater == 0U || lowater == 0U)
01797                 isc_mem_setwater(acache->mctx, water, acache, 0, 0);
01798         else
01799                 isc_mem_setwater(acache->mctx, water, acache,
01800                                  hiwater, lowater);
01801 }

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