]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
1526. [func] Implemented "additional section caching (or acache)",
authorTatuya JINMEI 神明達哉 <jinmei@isc.org>
Tue, 21 Dec 2004 10:45:20 +0000 (10:45 +0000)
committerTatuya JINMEI 神明達哉 <jinmei@isc.org>
Tue, 21 Dec 2004 10:45:20 +0000 (10:45 +0000)
an internal cache framework for additional section
content to improve response performance.  Several
configuration options were provided to control the
behavior.

27 files changed:
CHANGES
bin/named/config.c
bin/named/include/named/server.h
bin/named/query.c
bin/named/server.c
doc/arm/Bv9ARM-book.xml
lib/dns/Makefile.in
lib/dns/acache.c [new file with mode: 0644]
lib/dns/db.c
lib/dns/include/dns/acache.h [new file with mode: 0644]
lib/dns/include/dns/db.h
lib/dns/include/dns/events.h
lib/dns/include/dns/log.h
lib/dns/include/dns/rdataset.h
lib/dns/include/dns/types.h
lib/dns/include/dns/view.h
lib/dns/include/dns/zone.h
lib/dns/log.c
lib/dns/ncache.c
lib/dns/rbtdb.c
lib/dns/rdatalist.c
lib/dns/rdataset.c
lib/dns/rdataslab.c
lib/dns/sdb.c
lib/dns/view.c
lib/dns/zone.c
lib/isccfg/namedconf.c

diff --git a/CHANGES b/CHANGES
index 1d2f9e1ae0dc2b37c0654da39c08bb74212b17b7..446d4a87f831c656f3485f4fb8c08d60c4f0c7fc 100644 (file)
--- a/CHANGES
+++ b/CHANGES
 1527.  [cleanup]       Reduce the number of gettimeofday() calls without
                        losing necessary timer granularity.
 
-1526.  [placeholder]
+1526.  [func]          Implemented "additional section caching (or acache)",
+                       an internal cache framework for additional section
+                       content to improve response performance.  Several
+                       configuration options were provided to control the
+                       behavior.
 
 1525.  [bug]           dns_cache_create() could trigger a REQUIRE
                        failure in isc_mem_put() during error cleanup.
index 10739e777ba80e8b19a1f04d9a4ee29f16bdc8ab..1965758be000a743938dbebcf131d6fb142b6570 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: config.c,v 1.51 2004/10/05 02:47:50 marka Exp $ */
+/* $Id: config.c,v 1.52 2004/12/21 10:45:15 jinmei Exp $ */
 
 #include <config.h>
 
@@ -125,6 +125,9 @@ options {\n\
        check-names master fail;\n\
        check-names slave warn;\n\
        check-names response ignore;\n\
+       use-additional-cache true;\n\
+       acache-cleaning-interval 60;\n\
+       max-acache-size 0;\n\
        dnssec-enable no; /* Make yes for 9.4. */ \n\
 "
 
index 2aafc9cccce89bcc18f5694fc2133731253fe5bf..e50f9ab3541714debe6560042a0bae3cb133d5d4 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: server.h,v 1.75 2004/10/11 05:30:19 marka Exp $ */
+/* $Id: server.h,v 1.76 2004/12/21 10:45:15 jinmei Exp $ */
 
 #ifndef NAMED_SERVER_H
 #define NAMED_SERVER_H 1
@@ -94,7 +94,8 @@ struct ns_server {
        ns_controls_t *         controls;       /* Control channels */
        unsigned int            dispatchgen;
        ns_dispatchlist_t       dispatches;
-                                               
+
+       dns_acache_t            *acache;
 };
 
 #define NS_SERVER_MAGIC                        ISC_MAGIC('S','V','E','R')
index 907fac355640fbe7602df497aea3c2acb9e56f33..62f9ffacea2e3142e263fea2a1686a5ddf6b2521 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: query.c,v 1.261 2004/06/30 14:16:06 marka Exp $ */
+/* $Id: query.c,v 1.262 2004/12/21 10:45:15 jinmei Exp $ */
 
 #include <config.h>
 
 #define DNS_GETDB_NOLOG 0x02U
 #define DNS_GETDB_PARTIAL 0x04U
 
+typedef struct client_additionalctx {
+       ns_client_t *client;
+       dns_rdataset_t *rdataset;
+} client_additionalctx_t;
+
 static void
 query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype);
 
@@ -538,37 +543,18 @@ query_findversion(ns_client_t *client, dns_db_t *db,
 }
 
 static inline isc_result_t
-query_getzonedb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype,
-               unsigned int options, dns_zone_t **zonep, dns_db_t **dbp,
-               dns_dbversion_t **versionp)
+query_validatezonedb(ns_client_t *client, dns_name_t *name,
+                    dns_rdatatype_t qtype, unsigned int options,
+                    dns_zone_t *zone, dns_db_t *db,
+                    dns_dbversion_t **versionp)
 {
        isc_result_t result;
        isc_boolean_t check_acl, new_zone;
        dns_acl_t *queryacl;
        ns_dbversion_t *dbversion;
-       unsigned int ztoptions;
-       dns_zone_t *zone = NULL;
-       dns_db_t *db = NULL;
-       isc_boolean_t partial = ISC_FALSE;
-
-       REQUIRE(zonep != NULL && *zonep == NULL);
-       REQUIRE(dbp != NULL && *dbp == NULL);
-
-       /*
-        * Find a zone database to answer the query.
-        */
-       ztoptions = ((options & DNS_GETDB_NOEXACT) != 0) ?
-               DNS_ZTFIND_NOEXACT : 0;
-
-       result = dns_zt_find(client->view->zonetable, name, ztoptions, NULL,
-                            &zone);
-       if (result == DNS_R_PARTIALMATCH)
-               partial = ISC_TRUE;
-       if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH)
-               result = dns_zone_getdb(zone, &db);
 
-       if (result != ISC_R_SUCCESS)            
-               goto fail;
+       REQUIRE(zone != NULL);
+       REQUIRE(db != NULL);
 
        /*
         * This limits our searching to the zone where the first name
@@ -689,17 +675,63 @@ query_getzonedb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype,
         */
        dbversion->queryok = ISC_TRUE;
 
+       /* Transfer ownership, if necessary. */
+       if (versionp != NULL)
+               *versionp = dbversion->version;
+
+       return (ISC_R_SUCCESS);
+
+ refuse:
+       return (DNS_R_REFUSED);
+
+ fail:
+       return (result);
+}
+
+static inline isc_result_t
+query_getzonedb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype,
+               unsigned int options, dns_zone_t **zonep, dns_db_t **dbp,
+               dns_dbversion_t **versionp)
+{
+       isc_result_t result;
+       unsigned int ztoptions;
+       dns_zone_t *zone = NULL;
+       dns_db_t *db = NULL;
+       isc_boolean_t partial = ISC_FALSE;
+
+       REQUIRE(zonep != NULL && *zonep == NULL);
+       REQUIRE(dbp != NULL && *dbp == NULL);
+
+       /*
+        * Find a zone database to answer the query.
+        */
+       ztoptions = ((options & DNS_GETDB_NOEXACT) != 0) ?
+               DNS_ZTFIND_NOEXACT : 0;
+
+       result = dns_zt_find(client->view->zonetable, name, ztoptions, NULL,
+                            &zone);
+       if (result == DNS_R_PARTIALMATCH)
+               partial = ISC_TRUE;
+       if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH)
+               result = dns_zone_getdb(zone, &db);
+
+       if (result != ISC_R_SUCCESS)            
+               goto fail;
+
+       result = query_validatezonedb(client, name, qtype, options, zone, db,
+                                     versionp);
+
+       if (result != ISC_R_SUCCESS)
+               goto fail;
+
        /* Transfer ownership. */
        *zonep = zone;
        *dbp = db;
-       *versionp = dbversion->version;
 
        if (partial && (options & DNS_GETDB_PARTIAL) != 0)
                return (DNS_R_PARTIALMATCH);
        return (ISC_R_SUCCESS);
 
- refuse:
-       result = DNS_R_REFUSED;
  fail:
        if (zone != NULL)
                dns_zone_detach(&zone);
@@ -1237,11 +1269,516 @@ query_addadditional(void *arg, dns_name_t *name, dns_rdatatype_t qtype) {
        return (eresult);
 }
 
+static inline void
+query_discardcache(ns_client_t *client, dns_rdataset_t *rdataset_base,
+                  dns_rdatasetadditional_t additionaltype,
+                  dns_rdatatype_t type, dns_zone_t **zonep, dns_db_t **dbp,
+                  dns_dbversion_t **versionp, dns_dbnode_t **nodep,
+                  dns_name_t *fname)
+{
+       dns_rdataset_t *rdataset;
+
+       while  ((rdataset = ISC_LIST_HEAD(fname->list)) != NULL) {
+               ISC_LIST_UNLINK(fname->list, rdataset, link);
+               query_putrdataset(client, &rdataset);
+       }
+       if (*versionp != NULL)
+               dns_db_closeversion(*dbp, versionp, ISC_FALSE);
+       if (*nodep != NULL)
+               dns_db_detachnode(*dbp, nodep);
+       if (*dbp != NULL)
+               dns_db_detach(dbp);
+       if (*zonep != NULL)
+               dns_zone_detach(zonep);
+       (void)dns_rdataset_putadditional(client->view->acache, rdataset_base,
+                                        additionaltype, type);
+}
+
+static inline isc_result_t
+query_iscachevalid(dns_zone_t *zone, dns_db_t *db, dns_db_t *db0,
+                  dns_dbversion_t *version)
+{
+       isc_result_t result = ISC_R_SUCCESS;
+       dns_dbversion_t *version_current = NULL;
+       dns_db_t *db_current = db0;
+
+       if (db_current == NULL) {
+               result = dns_zone_getdb(zone, &db_current);
+               if (result != ISC_R_SUCCESS)
+                       return (result);
+       }
+       dns_db_currentversion(db_current, &version_current);
+       if (db_current != db || version_current != version) {
+               result = ISC_R_FAILURE;
+               goto cleanup;
+       }
+
+ cleanup:
+       dns_db_closeversion(db_current, &version_current, ISC_FALSE);
+       if (db0 == NULL && db_current != NULL)
+               dns_db_detach(&db_current);
+       
+       return (result);
+}
+
+static isc_result_t
+query_addadditional2(void *arg, dns_name_t *name, dns_rdatatype_t qtype) {
+       client_additionalctx_t *additionalctx = arg;
+       dns_rdataset_t *rdataset_base;
+       ns_client_t *client;
+       isc_result_t result, eresult;
+       dns_dbnode_t *node, *cnode;
+       dns_db_t *db, *cdb;
+       dns_name_t *fname, *mname0, cfname;
+       dns_rdataset_t *rdataset, *sigrdataset;
+       dns_rdataset_t *crdataset, *crdataset_next;
+       isc_buffer_t *dbuf;
+       isc_buffer_t b;
+       dns_dbversion_t *version, *cversion;
+       isc_boolean_t added_something, need_addname, needadditionalcache;
+       isc_boolean_t need_sigrrset;
+       dns_zone_t *zone;
+       dns_rdatatype_t type;
+       dns_rdatasetadditional_t additionaltype;
+
+       if (qtype != dns_rdatatype_a) {
+               /*
+                * This function is optimized for "address" types.  For other
+                * types, use a generic routine.
+                * XXX: ideally, this function should be generic enough.
+                */
+               return (query_addadditional(additionalctx->client,
+                                           name, qtype));
+       }
+
+       /*
+        * Initialization.
+        */
+       rdataset_base = additionalctx->rdataset;
+       client = additionalctx->client;
+       REQUIRE(NS_CLIENT_VALID(client));
+       eresult = ISC_R_SUCCESS;
+       fname = NULL;
+       rdataset = NULL;
+       sigrdataset = NULL;
+       db = NULL;
+       cdb = NULL;
+       version = NULL;
+       cversion = NULL;
+       node = NULL;
+       cnode = NULL;
+       added_something = ISC_FALSE;
+       need_addname = ISC_FALSE;
+       zone = NULL;
+       needadditionalcache = ISC_FALSE;
+       additionaltype = dns_rdatasetadditional_fromauth;
+       dns_name_init(&cfname, NULL);
+
+       CTRACE("query_addadditional2");
+
+       /*
+        * We treat type A additional section processing as if it
+        * were "any address type" additional section processing.
+        * To avoid multiple lookups, we do an 'any' database
+        * lookup and iterate over the node.
+        * XXXJT: this approach can cause a suboptimal result when the cache
+        * DB only has partial address types and the glue DB has remaining
+        * ones. 
+        */
+       type = dns_rdatatype_any;
+
+       /*
+        * Get some resources.
+        */
+       dbuf = query_getnamebuf(client);
+       if (dbuf == NULL)
+               goto cleanup;
+       fname = query_newname(client, dbuf, &b);
+       if (fname == NULL)
+               goto cleanup;
+       dns_name_setbuffer(&cfname, &b); /* share the buffer */
+
+       /* Check additional cache */
+       result = dns_rdataset_getadditional(rdataset_base, additionaltype,
+                                           type, client->view->acache, &zone,
+                                           &cdb, &cversion, &cnode, &cfname,
+                                           client->message, client->now);
+       if (result != ISC_R_SUCCESS)
+               goto findauthdb;
+       if (zone == NULL) {
+               CTRACE("query_addadditional2: auth zone not found");
+               goto try_cache;
+       }
+
+       /* Is the cached DB up-to-date? */
+       result = query_iscachevalid(zone, cdb, NULL, cversion);
+       if (result != ISC_R_SUCCESS) {
+               CTRACE("query_addadditional2: old auth additional cache");
+               query_discardcache(client, rdataset_base, additionaltype,
+                                  type, &zone, &cdb, &cversion, &cnode,
+                                  &cfname);
+               goto findauthdb;
+       }
+
+       if (cnode == NULL) {
+               /*
+                * We have a negative cache.  We don't have to check the zone
+                * ACL, since the result (not using this zone) would be same
+                * regardless of the result.
+                */
+               CTRACE("query_addadditional2: negative auth additional cache");
+               dns_db_closeversion(cdb, &cversion, ISC_FALSE);
+               dns_db_detach(&cdb);
+               dns_zone_detach(&zone);
+               goto try_cache;
+       }
+
+       result = query_validatezonedb(client, name, qtype, DNS_GETDB_NOLOG,
+                                     zone, cdb, NULL);
+       if (result != ISC_R_SUCCESS) {
+               query_discardcache(client, rdataset_base, additionaltype,
+                                  type, &zone, &cdb, &cversion, &cnode,
+                                  &cfname);
+               goto try_cache;
+       }
+
+       /* We've got an active cache. */
+       CTRACE("query_addadditional2: auth additional cache");
+       dns_db_closeversion(cdb, &cversion, ISC_FALSE);
+       db = cdb;
+       node = cnode;
+       dns_name_clone(&cfname, fname);
+       query_keepname(client, fname, dbuf);
+       goto foundcache;
+
+       /*
+        * Look for a zone database that might contain authoritative
+        * additional data.
+        */
+ findauthdb:
+       result = query_getzonedb(client, name, qtype, DNS_GETDB_NOLOG,
+                                &zone, &db, &version);
+       if (result != ISC_R_SUCCESS) {
+               /* Cache the negative result */
+               (void)dns_rdataset_setadditional(rdataset_base, additionaltype,
+                                                type, client->view->acache,
+                                                NULL, NULL, NULL, NULL,
+                                                NULL);
+               goto try_cache;
+       }
+
+       CTRACE("query_addadditional2: db_find");
+
+       /*
+        * Since we are looking for authoritative data, we do not set
+        * the GLUEOK flag.  Glue will be looked for later, but not
+        * necessarily in the same database.
+        */
+       node = NULL;
+       result = dns_db_find(db, name, version, type, client->query.dboptions,
+                            client->now, &node, fname, NULL, NULL);
+       if (result == ISC_R_SUCCESS)
+               goto found;
+
+       /* Cache the negative result */
+       (void)dns_rdataset_setadditional(rdataset_base, additionaltype,
+                                        type, client->view->acache, zone, db,
+                                        version, NULL, fname);
+
+       if (node != NULL)
+               dns_db_detachnode(db, &node);
+       version = NULL;
+       dns_db_detach(&db);
+
+       /*
+        * No authoritative data was found.  The cache is our next best bet.
+        */
+
+ try_cache:
+       additionaltype = dns_rdatasetadditional_fromcache;
+       result = query_getcachedb(client, name, qtype, &db, DNS_GETDB_NOLOG);
+       if (result != ISC_R_SUCCESS)
+               /*
+                * Most likely the client isn't allowed to query the cache.
+                */
+               goto try_glue;
+
+       result = dns_db_find(db, name, version, type,  client->query.dboptions,
+                            client->now, &node, fname, NULL, NULL);
+       if (result == ISC_R_SUCCESS)
+               goto found;
+
+       if (node != NULL)
+               dns_db_detachnode(db, &node);
+       dns_db_detach(&db);
+
+ try_glue:
+       /*
+        * No cached data was found.  Glue is our last chance.
+        * RFC1035 sayeth:
+        *
+        *      NS records cause both the usual additional section
+        *      processing to locate a type A record, and, when used
+        *      in a referral, a special search of the zone in which
+        *      they reside for glue information.
+        *
+        * This is the "special search".  Note that we must search
+        * the zone where the NS record resides, not the zone it
+        * points to, and that we only do the search in the delegation
+        * case (identified by client->query.gluedb being set).
+        */
+       if (client->query.gluedb == NULL)
+               goto cleanup;
+
+       /*
+        * Don't poision caches using the bailiwick protection model.
+        */
+       if (!dns_name_issubdomain(name, dns_db_origin(client->query.gluedb)))
+               goto cleanup;
+
+       /* Check additional cache */
+       additionaltype = dns_rdatasetadditional_fromglue;
+       result = dns_rdataset_getadditional(rdataset_base, additionaltype,
+                                           type, client->view->acache, NULL,
+                                           &cdb, &cversion, &cnode, &cfname,
+                                           client->message, client->now);
+       if (result != ISC_R_SUCCESS)
+               goto findglue;
+
+       result = query_iscachevalid(zone, cdb, client->query.gluedb, cversion);
+       if (result != ISC_R_SUCCESS) {
+               CTRACE("query_addadditional2: old glue additional cache");
+               query_discardcache(client, rdataset_base, additionaltype,
+                                  type, &zone, &cdb, &cversion, &cnode,
+                                  &cfname);
+               goto findglue;
+       }
+
+       if (cnode == NULL) {
+               /* We have a negative cache. */
+               CTRACE("query_addadditional2: negative glue additional cache");
+               dns_db_closeversion(cdb, &cversion, ISC_FALSE);
+               dns_db_detach(&cdb);
+               goto cleanup;
+       }
+
+       /* Cache hit. */
+       CTRACE("query_addadditional2: glue additional cache");
+       dns_db_closeversion(cdb, &cversion, ISC_FALSE);
+       db = cdb;
+       node = cnode;
+       dns_name_clone(&cfname, fname);
+       query_keepname(client, fname, dbuf);
+       goto foundcache;
+
+ findglue:
+       dns_db_attach(client->query.gluedb, &db);
+       result = dns_db_find(db, name, version, type,
+                            client->query.dboptions | DNS_DBFIND_GLUEOK,
+                            client->now, &node, fname, NULL, NULL);
+       if (!(result == ISC_R_SUCCESS ||
+             result == DNS_R_ZONECUT ||
+             result == DNS_R_GLUE)) {
+               /* cache the negative result */
+               (void)dns_rdataset_setadditional(rdataset_base, additionaltype,
+                                                type, client->view->acache,
+                                                NULL, db, version, NULL,
+                                                fname);
+               goto cleanup;
+       }
+
+ found:
+       /*
+        * We have found a DB node to iterate over from a DB.
+        * We are going to look for address RRsets (i.e., A and AAAA) in the DB
+        * node we've just found.  We'll then store the complete information
+        * in the additional data cache.
+        */
+       dns_name_clone(fname, &cfname);
+       query_keepname(client, fname, dbuf);
+       needadditionalcache = ISC_TRUE;
+
+       rdataset = query_newrdataset(client);
+       if (rdataset == NULL)
+               goto cleanup;
+       if (WANTDNSSEC(client)) {
+               sigrdataset = query_newrdataset(client);
+               if (sigrdataset == NULL)
+                       goto cleanup;
+       }
+
+       /*
+        * Find A RRset with sig RRset.  Even if we don't find a sig RRset
+        * for a client using DNSSEC, we'll continue the process to make a
+        * complete list to be cached.  However, we need to cancel the
+        * caching when something unexpected happens, in order to avoid
+        * caching incomplete information.
+        */
+       result = dns_db_findrdataset(db, node, version, dns_rdatatype_a, 0,
+                                    client->now, rdataset, sigrdataset);
+       if (result == DNS_R_NCACHENXDOMAIN)
+               goto setcache;
+       if (result == DNS_R_NCACHENXRRSET) {
+               dns_rdataset_disassociate(rdataset);
+               /*
+                * Negative cache entries don't have sigrdatasets.
+                */
+               INSIST(sigrdataset == NULL ||
+                      ! dns_rdataset_isassociated(sigrdataset));
+       }
+       if (result == ISC_R_SUCCESS) {
+               /* Remember the result as a cache */
+               ISC_LIST_APPEND(cfname.list, rdataset, link);
+               if (sigrdataset != NULL &&
+                   dns_rdataset_isassociated(sigrdataset)) {
+                       ISC_LIST_APPEND(cfname.list, sigrdataset,
+                                       link);
+                       sigrdataset =
+                               query_newrdataset(client);
+                       if (sigrdataset == NULL)
+                               needadditionalcache = ISC_FALSE;
+               }
+               rdataset = query_newrdataset(client);
+               if (rdataset == NULL) {
+                       /* do not cache incomplete information */
+                       goto foundcache;
+               }
+       }
+
+       /* Find AAAA RRset with sig RRset */
+       result = dns_db_findrdataset(db, node, version,
+                                    dns_rdatatype_aaaa, 0,
+                                    client->now, rdataset,
+                                    sigrdataset);
+       /* The NXDOMAIN case should be covered above */
+       INSIST(result != DNS_R_NCACHENXDOMAIN);
+       if (result == DNS_R_NCACHENXRRSET) {
+               dns_rdataset_disassociate(rdataset);
+               /*
+                * Negative cache entries don't have sigrdatasets.
+                */
+               INSIST(sigrdataset == NULL ||
+                      ! dns_rdataset_isassociated(sigrdataset));
+       }
+       if (result == ISC_R_SUCCESS) {
+               ISC_LIST_APPEND(cfname.list, rdataset, link);
+               rdataset = NULL;
+               if (sigrdataset != NULL &&
+                   dns_rdataset_isassociated(sigrdataset)) {
+                       ISC_LIST_APPEND(cfname.list, sigrdataset,
+                                       link);
+                       sigrdataset = NULL;
+               }
+       }
+
+ setcache:
+       /*
+        * Set the new result in the cache if required.  We do not support
+        * caching additional data from a cache DB. 
+        */
+       if (needadditionalcache == ISC_TRUE &&
+           (additionaltype == dns_rdatasetadditional_fromauth ||
+            additionaltype == dns_rdatasetadditional_fromglue)) {
+               (void)dns_rdataset_setadditional(rdataset_base, additionaltype,
+                                                type, client->view->acache,
+                                                zone, db, version, node,
+                                                &cfname);
+       }
+
+ foundcache:
+       need_sigrrset = ISC_FALSE;
+       mname0 = NULL;
+       for (crdataset = ISC_LIST_HEAD(cfname.list);
+            crdataset != NULL;
+            crdataset = crdataset_next) {
+               dns_name_t *mname;
+
+               crdataset_next = ISC_LIST_NEXT(crdataset, link);
+
+               mname = NULL;
+               if (crdataset->type == dns_rdatatype_a ||
+                   crdataset->type == dns_rdatatype_aaaa) {
+                       if (!query_isduplicate(client, fname, crdataset->type,
+                                              &mname)) {
+                               if (mname != NULL) {
+                                       /*
+                                        * A different type of this name is
+                                        * already stored in the additional
+                                        * section.  We'll reuse the name.
+                                        * Note that this should happen at most
+                                        * once.  Otherwise, fname->link could
+                                        * leak below.
+                                        */
+                                       INSIST(mname0 == NULL);
+
+                                       query_releasename(client, &fname);
+                                       fname = mname;
+                                       mname0 = mname;
+                               } else
+                                       need_addname = ISC_TRUE;
+                               ISC_LIST_UNLINK(cfname.list, crdataset, link);
+                               ISC_LIST_APPEND(fname->list, crdataset, link);
+                               added_something = ISC_TRUE;
+                               need_sigrrset = ISC_TRUE;
+                       } else
+                               need_sigrrset = ISC_FALSE;
+               } else if (crdataset->type == dns_rdatatype_sig &&
+                          need_sigrrset && WANTDNSSEC(client)) {
+                       ISC_LIST_UNLINK(cfname.list, crdataset, link);
+                       ISC_LIST_APPEND(fname->list, crdataset, link);
+                       added_something = ISC_TRUE; /* just in case */
+                       need_sigrrset = ISC_FALSE;
+               }
+       }
+
+       CTRACE("query_addadditional2: addname");
+
+       /*
+        * If we haven't added anything, then we're done.
+        */
+       if (!added_something)
+               goto cleanup;
+
+       /*
+        * We may have added our rdatasets to an existing name, if so, then
+        * need_addname will be ISC_FALSE.  Whether we used an existing name
+        * or a new one, we must set fname to NULL to prevent cleanup.
+        */
+       if (need_addname)
+               dns_message_addname(client->message, fname,
+                                   DNS_SECTION_ADDITIONAL);
+       fname = NULL;
+
+ cleanup:
+       CTRACE("query_addadditional2: cleanup");
+
+       if (rdataset != NULL)
+               query_putrdataset(client, &rdataset);
+       if (sigrdataset != NULL)
+               query_putrdataset(client, &sigrdataset);
+       while  ((crdataset = ISC_LIST_HEAD(cfname.list)) != NULL) {
+               ISC_LIST_UNLINK(cfname.list, crdataset, link);
+               query_putrdataset(client, &crdataset);
+       }
+       if (fname != NULL)
+               query_releasename(client, &fname);
+       if (node != NULL)
+               dns_db_detachnode(db, &node);
+       if (db != NULL)
+               dns_db_detach(&db);
+       if (zone != NULL)
+               dns_zone_detach(&zone);
+
+       CTRACE("query_addadditional2: done");
+       return (eresult);
+}
+
 static inline void
 query_addrdataset(ns_client_t *client, dns_name_t *fname,
                  dns_rdataset_t *rdataset)
 {
        dns_rdatatype_t type = rdataset->type;
+       client_additionalctx_t additionalctx;
 
        /*
         * Add 'rdataset' and any pertinent additional data to
@@ -1264,8 +1801,10 @@ query_addrdataset(ns_client_t *client, dns_name_t *fname,
         *
         * We don't care if dns_rdataset_additionaldata() fails.
         */
-       (void)dns_rdataset_additionaldata(rdataset,
-                                         query_addadditional, client);
+       additionalctx.client = client;
+       additionalctx.rdataset = rdataset;
+       (void)dns_rdataset_additionaldata(rdataset, query_addadditional2,
+                                         &additionalctx);
        /*
         * RFC 2535 section 3.5 says that when NS, SOA, A, or AAAA records
         * are retrieved, any KEY RRs for the owner name should be added
@@ -1362,11 +1901,12 @@ query_addrrset(ns_client_t *client, dns_name_t **namep,
 }
 
 static inline isc_result_t
-query_addsoa(ns_client_t *client, dns_db_t *db, isc_boolean_t zero_ttl) {
-       dns_name_t *name, *fname;
+query_addsoa(ns_client_t *client, dns_db_t *db, dns_dbversion_t *version,
+            isc_boolean_t zero_ttl)
+{
+       dns_name_t *name;
        dns_dbnode_t *node;
        isc_result_t result, eresult;
-       dns_fixedname_t foundname;
        dns_rdataset_t *rdataset = NULL, *sigrdataset = NULL;
        dns_rdataset_t **sigrdatasetp = NULL;
 
@@ -1378,8 +1918,6 @@ query_addsoa(ns_client_t *client, dns_db_t *db, isc_boolean_t zero_ttl) {
        name = NULL;
        rdataset = NULL;
        node = NULL;
-       dns_fixedname_init(&foundname);
-       fname = dns_fixedname_name(&foundname);
 
        /*
         * Get resources and make 'name' be the database origin.
@@ -1405,9 +1943,26 @@ query_addsoa(ns_client_t *client, dns_db_t *db, isc_boolean_t zero_ttl) {
        /*
         * Find the SOA.
         */
-       result = dns_db_find(db, name, NULL, dns_rdatatype_soa,
-                            client->query.dboptions, 0, &node,
-                            fname, rdataset, sigrdataset);
+       result = dns_db_getsoanode(db, &node);
+       if (result == ISC_R_SUCCESS) {
+               result = dns_db_findrdataset(db, node, version,
+                                            dns_rdatatype_soa,
+                                            0, client->now, rdataset,
+                                            sigrdataset);
+       } else {
+               dns_fixedname_t foundname;
+               dns_name_t *fname;
+
+               dns_fixedname_init(&foundname);
+               fname = dns_fixedname_name(&foundname);
+
+               result = dns_db_find(db, name, version, dns_rdatatype_soa,
+                                    client->query.dboptions, 0, &node,
+                                    fname, rdataset, sigrdataset);
+
+               if (result == ISC_R_SUCCESS)
+                       (void)dns_db_setsoanode(db, node);
+       }
        if (result != ISC_R_SUCCESS) {
                /*
                 * This is bad.  We tried to get the SOA RR at the zone top
@@ -1463,7 +2018,7 @@ query_addsoa(ns_client_t *client, dns_db_t *db, isc_boolean_t zero_ttl) {
 }
 
 static inline isc_result_t
-query_addns(ns_client_t *client, dns_db_t *db) {
+query_addns(ns_client_t *client, dns_db_t *db, dns_dbversion_t *version) {
        dns_name_t *name, *fname;
        dns_dbnode_t *node;
        isc_result_t result, eresult;
@@ -1510,13 +2065,25 @@ query_addns(ns_client_t *client, dns_db_t *db) {
        /*
         * Find the NS rdataset.
         */
-       CTRACE("query_addns: calling dns_db_find");
-       result = dns_db_find(db, name, NULL, dns_rdatatype_ns,
-                            client->query.dboptions, 0, &node,
-                            fname, rdataset, sigrdataset);
-       CTRACE("query_addns: dns_db_find complete");
+       result = dns_db_getnsnode(db, &node);
+       if (result == ISC_R_SUCCESS) {
+               result = dns_db_findrdataset(db, node, version,
+                                            dns_rdatatype_ns,
+                                            0, client->now, rdataset,
+                                            sigrdataset);
+       } else {
+               CTRACE("query_addns: calling dns_db_find");
+               result = dns_db_find(db, name, NULL, dns_rdatatype_ns,
+                                    client->query.dboptions, 0, &node,
+                                    fname, rdataset, sigrdataset);
+               CTRACE("query_addns: dns_db_find complete");
+
+               if (result == ISC_R_SUCCESS)
+                       (void)dns_db_setnsnode(db, node);
+       }
        if (result != ISC_R_SUCCESS) {
-               CTRACE("query_addns: dns_db_find failed");
+               CTRACE("query_addns: "
+                      "dns_db_findrdataset or dns_db_find failed");
                /*
                 * This is bad.  We tried to get the NS rdataset at the zone
                 * top and it didn't work!
@@ -2514,7 +3081,7 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
 
        if (is_zone)
                authoritative = ISC_TRUE;
-       
+
        if (event == NULL && client->query.restarts == 0) {
                if (is_zone) {
                        dns_zone_attach(zone, &client->query.authzone);
@@ -2838,7 +3405,7 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                /*
                 * Add SOA.
                 */
-               result = query_addsoa(client, db, ISC_FALSE);
+               result = query_addsoa(client, db, version, ISC_FALSE);
                if (result != ISC_R_SUCCESS) {
                        QUERY_ERROR(result);
                        goto cleanup;
@@ -2879,9 +3446,9 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                 * resolver and not have it cached.
                 */
                if (qtype == dns_rdatatype_soa)
-                       result = query_addsoa(client, db, ISC_TRUE);
+                       result = query_addsoa(client, db, version, ISC_TRUE);
                else
-                       result = query_addsoa(client, db, ISC_FALSE);
+                       result = query_addsoa(client, db, version, ISC_FALSE);
                if (result != ISC_R_SUCCESS) {
                        QUERY_ERROR(result);
                        goto cleanup;
@@ -3205,7 +3772,8 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                                /*
                                 * Add SOA.
                                 */
-                               result = query_addsoa(client, db, ISC_FALSE);
+                               result = query_addsoa(client, db, version,
+                                                     ISC_FALSE);
                                if (result == ISC_R_SUCCESS)
                                        result = ISC_R_NOMORE;
                        } else {
@@ -3257,7 +3825,7 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                               qtype == dns_rdatatype_any) &&
                              dns_name_equal(client->query.qname,
                                             dns_db_origin(db))))
-                               (void)query_addns(client, db);
+                               (void)query_addns(client, db, version);
                } else if (qtype != dns_rdatatype_ns) {
                        if (fname != NULL)
                                query_releasename(client, &fname);
index 513b2e446f2949f3bfbc2993fd2f2345e558ea1d..4b220133937bb46870b068415b1be76723ae0ef1 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: server.c,v 1.433 2004/11/10 22:14:28 marka Exp $ */
+/* $Id: server.c,v 1.434 2004/12/21 10:45:15 jinmei Exp $ */
 
 #include <config.h>
 
@@ -41,6 +41,7 @@
 
 #include <bind9/check.h>
 
+#include <dns/acache.h>
 #include <dns/adb.h>
 #include <dns/cache.h>
 #include <dns/db.h>
@@ -733,6 +734,7 @@ configure_view(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
        isc_result_t result;
        isc_uint32_t max_adb_size;
        isc_uint32_t max_cache_size;
+       isc_uint32_t max_acache_size;
        isc_uint32_t lame_ttl;
        dns_tsig_keyring_t *ring;
        dns_view_t *pview = NULL;       /* Production view */
@@ -776,6 +778,51 @@ configure_view(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
        CHECKM(ns_config_getport(config, &port), "port");
        dns_view_setdstport(view, port);
 
+       /*
+        * Create additional cache for this view and zones under the view
+        * unless explicitly disabled.
+        */
+       obj = NULL;
+       ns_config_get(maps, "use-additional-cache", &obj);
+       if (obj == NULL || cfg_obj_asboolean(obj)) {
+               cmctx = NULL;
+               CHECK(isc_mem_create(0, 0, &cmctx));
+               CHECK(dns_acache_create(&view->acache, cmctx, ns_g_taskmgr,
+                                       ns_g_timermgr));
+               isc_mem_detach(&cmctx);
+       }
+       if (view->acache != NULL) {
+               obj = NULL;
+               result = ns_config_get(maps, "acache-cleaning-interval", &obj);
+               INSIST(result == ISC_R_SUCCESS);
+               dns_acache_setcleaninginterval(view->acache,
+                                              cfg_obj_asuint32(obj) * 60);
+
+               obj = NULL;
+               result = ns_config_get(maps, "max-acache-size", &obj);
+               INSIST(result == ISC_R_SUCCESS);
+               if (cfg_obj_isstring(obj)) {
+                       str = cfg_obj_asstring(obj);
+                       INSIST(strcasecmp(str, "unlimited") == 0);
+                       max_acache_size = ISC_UINT32_MAX;
+               } else {
+                       isc_resourcevalue_t value;
+
+                       value = cfg_obj_asuint64(obj);
+                       if (value > ISC_UINT32_MAX) {
+                               cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,
+                                           "'max-acache-size "
+                                           "%" ISC_PRINT_QUADFORMAT
+                                           "d' is too large",
+                                           value);
+                               result = ISC_R_RANGE;
+                               goto cleanup;
+                       }
+                       max_acache_size = (isc_uint32_t)value;
+               }
+               dns_acache_setcachesize(view->acache, max_acache_size);
+       }
+
        /*
         * Configure the zones.
         */
@@ -1737,6 +1784,8 @@ configure_zone(cfg_obj_t *config, cfg_obj_t *zconfig, cfg_obj_t *vconfig,
                 * new view.
                 */
                dns_zone_setview(zone, view);
+               if (view->acache != NULL)
+                       dns_zone_setacache(zone, view->acache);
        } else {
                /*
                 * We cannot reuse an existing zone, we have
@@ -1745,6 +1794,8 @@ configure_zone(cfg_obj_t *config, cfg_obj_t *zconfig, cfg_obj_t *vconfig,
                CHECK(dns_zone_create(&zone, mctx));
                CHECK(dns_zone_setorigin(zone, origin));
                dns_zone_setview(zone, view);
+               if (view->acache != NULL)
+                       dns_zone_setacache(zone, view->acache);
                CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr, zone));
        }
 
index 8ecb23228725050bb7ae01ca021ff7527dce0ad3..04882f40bf353449777990e94faf3921512bd8bb 100644 (file)
@@ -2,7 +2,7 @@
 <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.0//EN"
                "http://www.oasis-open.org/docbook/xml/4.0/docbookx.dtd">
 
-<!-- File: $Id: Bv9ARM-book.xml,v 1.259 2004/11/11 01:08:24 marka Exp $ -->
+<!-- File: $Id: Bv9ARM-book.xml,v 1.260 2004/12/21 10:45:16 jinmei Exp $ -->
 
 <book>
 <title>BIND 9 Administrator Reference Manual</title>
@@ -392,7 +392,12 @@ signed zones, serving many thousands of queries per second.</para></sect1>
 cache and zones loaded off disk.  The <command>max-cache-size</command>
 option can be used to limit the amount of memory used by the cache,
 at the expense of reducing cache hit rates and causing more <acronym>DNS</acronym>
-traffic. It is still good practice to have enough memory to load
+traffic.
+Additionally, if additional section caching
+(<xref linkend="acache"/>) is enabled, 
+the <command>max-acache-size</command> can be used to limit the amount
+of memory used by the mechanism.
+It is still good practice to have enough memory to load
 all zone and cache data into memory &mdash; unfortunately, the best way
 to determine this for a given installation is to watch the name server
 in operation. After a few weeks the server process should reach
@@ -2800,8 +2805,11 @@ statement in the <filename>named.conf</filename> file:</para>
     <optional> edns-udp-size <replaceable>number</replaceable>; </optional>
     <optional> root-delegation-only <optional> exclude { <replaceable>namelist</replaceable> } </optional> ; </optional>
     <optional> querylog <replaceable>yes_or_no</replaceable> ; </optional>
-};
     <optional> disable-algorithms <replaceable>domain</replaceable> { <replaceable>algorithm</replaceable>; <optional> <replaceable>algorithm</replaceable>; </optional> }; </optional>
+    <optional> use-additional-cache <replaceable>yes_or_no</replaceable> ; </optional>
+    <optional> acache-cleaning-interval <replaceable>number</replaceable>; </optional>
+    <optional> max-acache-size <replaceable>size_spec</replaceable> ; </optional>
+};
 </programlisting>
 </sect2>
 
@@ -4346,6 +4354,99 @@ to be incremented, and may additionally cause the
 
 </sect3>
 
+<sect3 id="acache">
+<title>Additional Section Caching</title>
+
+<para>
+The additional section cache, also called <command>acache,</command>
+is an internal cache to improve the response performance of BIND 9.
+When the additional section caching is enabled, BIND 9 will
+cache internal short-cut to the additional section content for each
+answer RR.
+Note that acache is an internal caching mechanism of BIND 9, and is
+not relevant to the DNS caching server function.
+</para>
+
+<para>
+The additional section caching does not make any difference on the
+response content (except the RRsets ordering of the additional
+section, see below), but can improve the response performance significantly.
+It is particularly effective when BIND 9 acts as an authoritative server
+for a zone that has many delegations with many glue RRs.
+</para>
+
+<para>
+In order to achieve the maximum performance improvement by acache,
+it is recommended to set <command>additional-from-cache</command>
+to <command>no</command>, since the current implementation of acache
+does not make a short-cut of additional section information from a DNS
+cache data.
+</para>
+
+<para>
+One obvious disadvantage of acache is that it requires much more
+memory for the internal cached data.
+Thus, if the response performance does not matter and memory
+consumption is much more severe, the acache mechanism can be
+disabled by setting <command>use-additional-cache</command> to
+<command>no</command>.
+It is also possible to specify the upper limit of memory consumption
+for acache by <command>max-acache-size</command>.
+</para>
+
+<para>
+The additional section caching also has a minor effect on the RRset
+ordering in the additional section.
+Without acache, the "cyclic" order is effective for the additional
+section as well as the answer and authority sections.
+However, the additional section caching fixes the ordering when it
+first caches an RRset for the additional section, and the same
+ordering will be kept in succeeding responses, regardless of the
+configuration for <command>rrset-order</command>.
+This should be minor, though, since an RRset in the additional section
+typically only contains a small number of RRs (and in many cases it
+only contains a single RR), in which case the
+ordering does not matter much.
+</para>
+
+<para>
+The following is a summary of options related to acache.
+</para>
+
+<variablelist>
+
+<varlistentry><term><command>use-additional-cache</command></term>
+<listitem><para>
+If yes, the additional section caching is enabled.
+The default value is yes.
+</para> 
+</listitem></varlistentry>
+
+<varlistentry><term><command>acache-cleaning-interval</command></term>
+<listitem><para>
+The server will remove stale cache entries, based on an LRU based
+algorithm, every <command>acache-cleaning-interval</command> minutes.
+The default is 60 minutes.
+If set to 0, no  periodic cleaning will occur.
+</para> 
+</listitem></varlistentry>
+
+<varlistentry><term><command>max-acache-size</command></term>
+<listitem><para>
+The maximum amount of memory to use for the server's acache, in bytes.
+When the amount of data in the acache reaches this limit, the server
+will cause more aggressive cleaning so that the limit is not exceeded.
+In a server with multiple views, the limit applies separately to the
+acache of each view.
+The default is <literal>unlimited</literal>, meaning that
+entries are purged from acache only at the periodic cleaning time.
+</para> 
+</listitem></varlistentry>
+
+</variablelist>
+
+</sect3>
+
 </sect2>
 
 <sect2 id="server_statement_grammar">
index b4304d80ecc2464de0316586f2df40a0ca507ae4..248f8746493b50b50889143b3bbe6648b4e21bef 100644 (file)
@@ -13,7 +13,7 @@
 # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 # PERFORMANCE OF THIS SOFTWARE.
 
-# $Id: Makefile.in,v 1.148 2004/12/09 01:40:59 marka Exp $
+# $Id: Makefile.in,v 1.149 2004/12/21 10:45:16 jinmei Exp $
 
 srcdir =       @srcdir@
 VPATH =                @srcdir@
@@ -49,7 +49,7 @@ DSTOBJS =     dst_api.@O@  dst_lib.@O@ dst_parse.@O@  dst_result.@O@ \
                opensslrsa_link.@O@
 
 # Alphabetically
-DNSOBJS =      acl.@O@ adb.@O@ byaddr.@O@ \
+DNSOBJS =      acache.@O@ acl.@O@ adb.@O@ byaddr.@O@ \
                cache.@O@ callbacks.@O@ compress.@O@ \
                db.@O@ dbiterator.@O@ dbtable.@O@ diff.@O@ dispatch.@O@ \
                dnssec.@O@ ds.@O@ forward.@O@ journal.@O@ keytable.@O@ \
@@ -73,7 +73,7 @@ DSTSRCS =     dst_api.c dst_lib.c dst_parse.c \
                openssl_link.c openssldh_link.c \
                openssldsa_link.c opensslrsa_link.c
 
-DNSSRCS =      acl.c adb.c byaddr.c \
+DNSSRCS =      acache.c acl.c adb.c byaddr.c \
                cache.c callbacks.c compress.c \
                db.c dbiterator.c dbtable.c diff.c dispatch.c \
                dnssec.c ds.c forward.c journal.c keytable.c \
diff --git a/lib/dns/acache.c b/lib/dns/acache.c
new file mode 100644 (file)
index 0000000..a59d326
--- /dev/null
@@ -0,0 +1,1575 @@
+/*
+ * Copyright (C) 2003  Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+ * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: acache.c,v 1.2 2004/12/21 10:45:16 jinmei Exp $ */
+
+#include <config.h>
+
+#include <isc/event.h>
+#include <isc/hash.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/mutex.h>
+#include <isc/random.h>
+#include <isc/refcount.h>
+#include <isc/task.h>
+#include <isc/time.h>
+#include <isc/timer.h>
+
+#include <dns/acache.h>
+#include <dns/db.h>
+#include <dns/events.h>
+#include <dns/log.h>
+#include <dns/message.h>
+#include <dns/name.h>
+#include <dns/rdataset.h>
+#include <dns/result.h>
+#include <dns/zone.h>
+
+#define ACACHE_MAGIC                   ISC_MAGIC('A', 'C', 'H', 'E')
+#define DNS_ACACHE_VALID(acache)       ISC_MAGIC_VALID(acache, ACACHE_MAGIC)
+
+#define ACACHEENTRY_MAGIC              ISC_MAGIC('A', 'C', 'E', 'T')
+#define DNS_ACACHEENTRY_VALID(entry)   ISC_MAGIC_VALID(entry, ACACHEENTRY_MAGIC)
+
+#define DBBUCKETS      67
+
+#if 0
+#define ATRACE(m)       isc_log_write(dns_lctx, \
+                                     DNS_LOGCATEGORY_DATABASE, \
+                                     DNS_LOGMODULE_ACACHE, \
+                                     ISC_LOG_DEBUG(3), \
+                                     "acache %p: %s", acache, (m))
+#define AATRACE(a,m)    isc_log_write(dns_lctx, \
+                                     DNS_LOGCATEGORY_DATABASE, \
+                                     DNS_LOGMODULE_ACACHE, \
+                                     ISC_LOG_DEBUG(3), \
+                                     "acache %p: %s", (a), (m))
+#else
+#define ATRACE(m)
+#define AATRACE(a, m)
+#endif
+
+/*
+ * The following variables control incremental cleaning.
+ * MINSIZE is how many bytes is the floor for dns_acache_setcachesize().
+ * CLEANERINCREMENT is how many entries are examined in one pass.
+ * (XXX simply derived from definitions in cache.c  There may be better
+ *  constants here.)
+ */
+#define DNS_ACACHE_MINSIZE             2097152 /* Bytes.  2097152 = 2 MB */
+#define DNS_ACACHE_CLEANERINCREMENT    1000    /* Number of entries. */
+
+/* Locked by acache lock */
+typedef struct dbentry {
+       ISC_LINK(struct dbentry)        link;
+
+       dns_db_t                        *db;
+       ISC_LIST(dns_acacheentry_t)     originlist;
+       ISC_LIST(dns_acacheentry_t)     referlist;
+} dbentry_t;
+
+typedef ISC_LIST(dbentry_t) dbentrylist_t;
+
+typedef struct acache_cleaner acache_cleaner_t;
+
+typedef enum {
+       cleaner_s_idle, /* Waiting for cleaning-interval to expire. */
+       cleaner_s_busy, /* Currently cleaning. */
+       cleaner_s_done  /* Freed enough memory after being overmem. */
+} cleaner_state_t;
+
+/*
+ * Convenience macros for comprehensive assertion checking.
+ */
+#define CLEANER_IDLE(c) ((c)->state == cleaner_s_idle && \
+                        (c)->resched_event != NULL)
+#define CLEANER_BUSY(c) ((c)->state == cleaner_s_busy && \
+                        (c)->resched_event == NULL)
+
+struct acache_cleaner {
+       isc_mutex_t             lock;
+       /*
+        * Locks overmem_event, overmem.  (See cache.c)
+        */
+
+       dns_acache_t            *acache;
+       unsigned int            cleaning_interval; /* The cleaning-interval
+                                                     from named.conf,
+                                                     in seconds. */
+
+       isc_timer_t             *cleaning_timer;
+       isc_event_t             *resched_event; /* Sent by cleaner task to
+                                                  itself to reschedule */
+       isc_event_t             *overmem_event;
+
+       dns_acacheentry_t       *current_entry; /* The bookmark entry to
+                                                  restart the cleaning.
+                                                  Locked by acache lock. */
+       int                     increment;      /* Number of entries to
+                                                  clean in one increment */
+
+       unsigned long           ncleaned;       /* Number of entries cleaned
+                                                  up (for logging purposes) */
+       cleaner_state_t         state;          /* Idle/Busy/Done. */
+       isc_boolean_t           overmem;        /* The acache is in an overmem
+                                                  state. */
+};
+
+/*
+ * The actual acache object.
+ */
+
+struct dns_acache {
+       unsigned int                    magic;
+
+       isc_mem_t                       *mctx;
+       isc_refcount_t                  refs;
+
+       isc_mutex_t                     lock;
+
+       int                             live_cleaners;
+       acache_cleaner_t                cleaner;
+       ISC_LIST(dns_acacheentry_t)     entries;
+       unsigned int                    dbentries;
+       dbentrylist_t                   dbbucket[DBBUCKETS];
+
+       isc_boolean_t                   shutting_down;
+
+       isc_task_t                      *task;
+       isc_event_t                     cevent;
+       isc_boolean_t                   cevent_sent;
+};
+
+struct dns_acacheentry {
+       unsigned int            magic;
+
+       isc_mutex_t             lock;
+       isc_refcount_t          references;
+
+       dns_acache_t            *acache;
+
+       /* Data for Management of cache entries */
+       ISC_LINK(dns_acacheentry_t) link;
+       ISC_LINK(dns_acacheentry_t) olink;
+       ISC_LINK(dns_acacheentry_t) rlink;
+
+       dns_db_t                *origdb; /* reference to the DB
+                                           holding this entry */
+
+       /* Cache data */
+       dns_zone_t              *zone;          /* zone this entry
+                                                  belongs to */
+       dns_db_t                *db;            /* DB this entry belongs to */
+       dns_dbversion_t         *version;       /* the version of the DB */
+       dns_dbnode_t            *node;          /* node this entry
+                                                  belongs to */
+       dns_name_t              *foundname;     /* corresponding DNS name
+                                                  and rdataset */
+
+       /* Callback function and its argument */
+       void                    (*callback)(dns_acacheentry_t *, void **);
+       void                    *cbarg;
+
+       /* Timestamp of the last time this entry is referred to */
+       isc_stdtime_t           lastused;
+};
+
+/*
+ *     Internal functions (and prototypes).
+ */
+static inline isc_boolean_t check_noentry(dns_acache_t *acache);
+static void destroy(dns_acache_t *acache);
+static void shutdown_entries(dns_acache_t *acache);
+static void shutdown_buckets(dns_acache_t *acache);
+static void destroy_entry(dns_acacheentry_t *ent);
+static inline void unlink_dbentries(dns_acache_t *acache,
+                                   dns_acacheentry_t *ent);
+static inline isc_result_t finddbent(dns_acache_t *acache,
+                                    dns_db_t *db, dbentry_t **dbentryp);
+static inline void clear_entry(dns_acache_t *acache, dns_acacheentry_t *entry);
+static isc_result_t acache_cleaner_init(dns_acache_t *acache,
+                                       isc_timermgr_t *timermgr,
+                                       acache_cleaner_t *cleaner);
+static void acache_cleaning_timer_action(isc_task_t *task, isc_event_t *event);
+static void acache_incremental_cleaning_action(isc_task_t *task,
+                                              isc_event_t *event);
+static void acache_overmem_cleaning_action(isc_task_t *task,
+                                          isc_event_t *event);
+static void acache_cleaner_shutdown_action(isc_task_t *task,
+                                          isc_event_t *event);
+
+/*
+ * The acache must be locked before calling.
+ */
+static inline isc_boolean_t
+check_noentry(dns_acache_t *acache) {
+       if (ISC_LIST_EMPTY(acache->entries) && acache->dbentries == 0) {
+               return (ISC_TRUE);
+       }
+
+       return (ISC_FALSE);
+}
+
+/*
+ * The acache must be locked before calling.
+ */
+static void
+shutdown_entries(dns_acache_t *acache) {
+       dns_acacheentry_t *entry, *entry_next;
+
+       REQUIRE(DNS_ACACHE_VALID(acache));
+       INSIST(acache->shutting_down);
+
+       /*
+        * Release the dependency of all entries, and detach them.
+        */
+       for (entry = ISC_LIST_HEAD(acache->entries);
+            entry != NULL;
+            entry = entry_next) {
+               entry_next = ISC_LIST_NEXT(entry, link);
+
+               LOCK(&entry->lock);
+
+               /*
+                * If the cleaner holds this entry, it will be unlinked and
+                * freed in the cleaner later.
+                */
+               if (acache->cleaner.current_entry != entry)
+                       ISC_LIST_UNLINK(acache->entries, entry, link);
+               unlink_dbentries(acache, entry);
+               if (entry->callback != NULL) {
+                       (entry->callback)(entry, &entry->cbarg);
+                       entry->callback = NULL;
+               }
+
+               UNLOCK(&entry->lock);
+
+               if (acache->cleaner.current_entry != entry)
+                       dns_acache_detachentry(&entry);
+       }
+}
+
+/*
+ * The acache must be locked before calling.
+ */
+static void
+shutdown_buckets(dns_acache_t *acache) {
+       int i;
+       dbentry_t *dbent;
+
+       REQUIRE(DNS_ACACHE_VALID(acache));
+       INSIST(acache->shutting_down);
+
+       for (i = 0; i < DBBUCKETS; i++) {
+               while ((dbent = ISC_LIST_HEAD(acache->dbbucket[i])) != NULL) {
+                       INSIST(ISC_LIST_EMPTY(dbent->originlist) &&
+                              ISC_LIST_EMPTY(dbent->referlist));
+                       ISC_LIST_UNLINK(acache->dbbucket[i], dbent, link);
+                                               
+                       dns_db_detach(&dbent->db);
+
+                       isc_mem_put(acache->mctx, dbent, sizeof(*dbent));
+                                   
+                       acache->dbentries--;
+               }
+       }
+
+       INSIST(acache->dbentries == 0);
+}
+
+static void
+shutdown_task(isc_task_t *task, isc_event_t *ev) {
+       dns_acache_t *acache;
+
+       UNUSED(task);
+
+       acache = ev->ev_arg;
+       INSIST(DNS_ACACHE_VALID(acache));
+
+       isc_event_free(&ev);
+
+       LOCK(&acache->lock);
+
+       shutdown_entries(acache);
+       shutdown_buckets(acache);
+
+       UNLOCK(&acache->lock);
+
+       dns_acache_detach(&acache);
+}
+
+/* The acache and the entry must be locked before calling. */
+static inline void
+unlink_dbentries(dns_acache_t *acache, dns_acacheentry_t *ent) {
+       isc_result_t result;
+       dbentry_t *dbent;
+
+       if (ISC_LINK_LINKED(ent, olink)) {
+               INSIST(ent->origdb != NULL);
+               dbent = NULL;
+               result = finddbent(acache, ent->origdb, &dbent);
+               INSIST(result == ISC_R_SUCCESS);
+
+               ISC_LIST_UNLINK(dbent->originlist, ent, olink);
+       }
+       if (ISC_LINK_LINKED(ent, rlink)) {
+               INSIST(ent->db != NULL);
+               dbent = NULL;
+               result = finddbent(acache, ent->db, &dbent);
+               INSIST(result == ISC_R_SUCCESS);
+
+               ISC_LIST_UNLINK(dbent->referlist, ent, rlink);
+       }
+}
+
+/* There must not be a reference to this entry. */
+static void
+destroy_entry(dns_acacheentry_t *entry) {
+       dns_acache_t *acache;
+
+       REQUIRE(DNS_ACACHEENTRY_VALID(entry));
+
+       acache = entry->acache;
+       REQUIRE(DNS_ACACHE_VALID(acache));
+
+       /*
+        * Since there is no reference to this entry, it is safe to call
+        * clear_entry() here.
+        */
+       clear_entry(acache, entry);
+
+       isc_mem_put(acache->mctx, entry, sizeof(*entry));
+
+       dns_acache_detach(&acache);
+}
+
+static void
+destroy(dns_acache_t *acache) {
+       isc_mem_t *mctx;
+
+       REQUIRE(DNS_ACACHE_VALID(acache));
+
+       ATRACE("destroy");
+
+       isc_mem_setwater(acache->mctx, NULL, NULL, 0, 0);
+
+       if (acache->cleaner.overmem_event != NULL)
+               isc_event_free(&acache->cleaner.overmem_event);
+
+       if (acache->cleaner.resched_event != NULL)
+               isc_event_free(&acache->cleaner.resched_event);
+
+       if (acache->task != NULL)
+               isc_task_detach(&acache->task);
+
+       DESTROYLOCK(&acache->cleaner.lock);
+
+       DESTROYLOCK(&acache->lock);
+       acache->magic = 0;
+       mctx = acache->mctx;
+
+       isc_mem_putanddetach(&acache->mctx, acache, sizeof(*acache));
+}
+
+static inline isc_result_t
+finddbent(dns_acache_t *acache, dns_db_t *db, dbentry_t **dbentryp) {
+       int bucket;
+       dbentry_t *dbentry;
+
+       REQUIRE(DNS_ACACHE_VALID(acache));
+       REQUIRE(db != NULL);
+       REQUIRE(dbentryp != NULL && *dbentryp == NULL);
+
+       /*
+        * The caller must be holding the acache lock.
+        */
+
+       bucket = isc_hash_calc((const unsigned char *)&db,
+                              sizeof(db), ISC_TRUE) % DBBUCKETS;
+
+       for (dbentry = ISC_LIST_HEAD(acache->dbbucket[bucket]);
+            dbentry != NULL;
+            dbentry = ISC_LIST_NEXT(dbentry, link)) {
+               if (dbentry->db == db)
+                       break;
+       }
+
+       *dbentryp = dbentry;
+
+       if (dbentry == NULL)
+               return (ISC_R_NOTFOUND);
+       else
+               return (ISC_R_SUCCESS);
+}
+
+static inline void
+clear_entry(dns_acache_t *acache, dns_acacheentry_t *entry) {
+       REQUIRE(DNS_ACACHE_VALID(acache));
+       REQUIRE(DNS_ACACHEENTRY_VALID(entry));
+
+       /*
+        * The caller must be holing the entry lock.
+        */
+
+       if (entry->foundname) {
+               dns_rdataset_t *rdataset, *rdataset_next;
+
+               for (rdataset = ISC_LIST_HEAD(entry->foundname->list);
+                    rdataset != NULL;
+                    rdataset = rdataset_next) {
+                       rdataset_next = ISC_LIST_NEXT(rdataset, link);
+                       ISC_LIST_UNLINK(entry->foundname->list,
+                                       rdataset, link);
+                       dns_rdataset_disassociate(rdataset);
+                       isc_mem_put(acache->mctx, rdataset, sizeof(*rdataset));
+               }
+               if (dns_name_dynamic(entry->foundname))
+                       dns_name_free(entry->foundname, acache->mctx);
+               isc_mem_put(acache->mctx, entry->foundname,
+                           sizeof(*entry->foundname)); 
+               entry->foundname = NULL;
+       }
+
+       if (entry->node != NULL) {
+               INSIST(entry->db != NULL);
+               dns_db_detachnode(entry->db, &entry->node);
+       }
+       if (entry->version != NULL) {
+               INSIST(entry->db != NULL);
+               dns_db_closeversion(entry->db, &entry->version, ISC_FALSE);
+       }
+       if (entry->db != NULL)
+               dns_db_detach(&entry->db);
+       if (entry->zone != NULL)
+               dns_zone_detach(&entry->zone);
+
+       if (entry->origdb != NULL)
+               dns_db_detach(&entry->origdb);
+}
+
+static isc_result_t
+acache_cleaner_init(dns_acache_t *acache, isc_timermgr_t *timermgr,
+                   acache_cleaner_t *cleaner)
+{
+       int result;
+
+       ATRACE("acache cleaner init");
+
+       result = isc_mutex_init(&cleaner->lock);
+       if (result != ISC_R_SUCCESS) {
+               UNEXPECTED_ERROR(__FILE__, __LINE__,
+                                "isc_mutex_init() failed: %s",
+                                dns_result_totext(result));
+               result = ISC_R_UNEXPECTED;
+               goto fail;
+       }
+
+       cleaner->increment = DNS_ACACHE_CLEANERINCREMENT;
+       cleaner->state = cleaner_s_idle;
+       cleaner->acache = acache;
+       cleaner->overmem = ISC_FALSE;
+
+       cleaner->cleaning_timer = NULL;
+       cleaner->resched_event = NULL;
+       cleaner->overmem_event = NULL;
+       cleaner->current_entry = NULL;
+
+       if (timermgr != NULL) {
+               cleaner->acache->live_cleaners++;
+               
+               result = isc_task_onshutdown(acache->task,
+                                            acache_cleaner_shutdown_action,
+                                            acache);
+               if (result != ISC_R_SUCCESS) {
+                       UNEXPECTED_ERROR(__FILE__, __LINE__,
+                                        "acache cleaner: "
+                                        "isc_task_onshutdown() failed: %s",
+                                        dns_result_totext(result));
+                       goto cleanup;
+               }
+
+               cleaner->cleaning_interval = 0; /* Initially turned off. */
+               result = isc_timer_create(timermgr, isc_timertype_inactive,
+                                         NULL, NULL,
+                                         acache->task,
+                                         acache_cleaning_timer_action,
+                                         cleaner, &cleaner->cleaning_timer);
+               if (result != ISC_R_SUCCESS) {
+                       UNEXPECTED_ERROR(__FILE__, __LINE__,
+                                        "isc_timer_create() failed: %s",
+                                        dns_result_totext(result));
+                       result = ISC_R_UNEXPECTED;
+                       goto cleanup;
+               }
+
+               cleaner->resched_event =
+                       isc_event_allocate(acache->mctx, cleaner,
+                                          DNS_EVENT_ACACHECLEAN,
+                                          acache_incremental_cleaning_action,
+                                          cleaner, sizeof(isc_event_t));
+               if (cleaner->resched_event == NULL) {
+                       result = ISC_R_NOMEMORY;
+                       goto cleanup;
+               }
+
+               cleaner->overmem_event =
+                       isc_event_allocate(acache->mctx, cleaner,
+                                          DNS_EVENT_ACACHEOVERMEM,
+                                          acache_overmem_cleaning_action,
+                                          cleaner, sizeof(isc_event_t));
+               if (cleaner->overmem_event == NULL) {
+                       result = ISC_R_NOMEMORY;
+                       goto cleanup;
+               }
+       }
+
+       return (ISC_R_SUCCESS);
+
+ cleanup:
+       if (cleaner->overmem_event != NULL)
+               isc_event_free(&cleaner->overmem_event);
+       if (cleaner->resched_event != NULL)
+               isc_event_free(&cleaner->resched_event);
+       if (cleaner->cleaning_timer != NULL)
+               isc_timer_detach(&cleaner->cleaning_timer);
+       cleaner->acache->live_cleaners--;
+       DESTROYLOCK(&cleaner->lock);
+ fail:
+       return (result);
+}
+
+static void
+begin_cleaning(acache_cleaner_t *cleaner) {
+       dns_acacheentry_t *head;
+       dns_acache_t *acache = cleaner->acache;
+
+       /*
+        * This function does not have to lock the cleaner, since critical
+        * parameters (except current_entry, which is locked by acache lock,)
+        * are only used in a single task context.
+        */
+
+       REQUIRE(CLEANER_IDLE(cleaner));
+       INSIST(DNS_ACACHE_VALID(acache));
+       INSIST(cleaner->current_entry == NULL);
+
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_ACACHE, ISC_LOG_DEBUG(1),
+                     "begin acache cleaning, mem inuse %lu",
+                     (unsigned long)isc_mem_inuse(cleaner->acache->mctx));
+
+       LOCK(&acache->lock);
+
+       head = ISC_LIST_HEAD(acache->entries);
+       if (head != NULL)
+               dns_acache_attachentry(head, &cleaner->current_entry);
+
+       UNLOCK(&acache->lock);
+
+       if (cleaner->current_entry != NULL) {
+               cleaner->ncleaned = 0;
+               cleaner->state = cleaner_s_busy;
+               isc_task_send(acache->task, &cleaner->resched_event);
+       }
+
+       return;
+}
+
+static void
+end_cleaning(acache_cleaner_t *cleaner, isc_event_t *event) {
+       dns_acache_t *acache = cleaner->acache;
+
+       REQUIRE(CLEANER_BUSY(cleaner));
+       REQUIRE(event != NULL);
+       REQUIRE(DNS_ACACHEENTRY_VALID(cleaner->current_entry));
+
+       /* No need to lock the cleaner (see begin_cleaning()). */
+
+       LOCK(&acache->lock);
+
+       /*
+        * Even if the cleaner has the last reference to the entry, which means
+        * the entry has been unused, it may still be linked if unlinking the
+        * entry has been delayed due to the reference.
+        */
+       if (isc_refcount_current(&cleaner->current_entry->references) == 1) {
+               INSIST(cleaner->current_entry->callback == NULL);
+               
+               if (ISC_LINK_LINKED(cleaner->current_entry, link)) {
+                       ISC_LIST_UNLINK(acache->entries,
+                                       cleaner->current_entry, link);
+               }
+       }
+       dns_acache_detachentry(&cleaner->current_entry);
+
+       UNLOCK(&acache->lock);
+
+       dns_acache_setcleaninginterval(cleaner->acache,
+                                      cleaner->cleaning_interval);
+
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE,
+                     ISC_LOG_DEBUG(1), "end acache cleaning, "
+                     "%lu entries cleaned, mem inuse %lu",
+                     cleaner->ncleaned,
+                     (unsigned long)isc_mem_inuse(cleaner->acache->mctx));
+
+       if (cleaner->overmem) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_ACACHE, ISC_LOG_NOTICE,
+                             "acache is still in overmem state "
+                             "after cleaning");
+       }
+
+       cleaner->ncleaned = 0;
+       cleaner->state = cleaner_s_idle;
+       cleaner->resched_event = event;
+}
+
+/*
+ * This is run once for every acache-cleaning-interval as defined
+ * in named.conf.
+ */
+static void
+acache_cleaning_timer_action(isc_task_t *task, isc_event_t *event) {
+       acache_cleaner_t *cleaner = event->ev_arg;
+
+       UNUSED(task);
+
+       INSIST(event->ev_type == ISC_TIMEREVENT_TICK);
+
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE,
+                     ISC_LOG_DEBUG(1), "acache cleaning timer fired, "
+                     "cleaner state = %d", cleaner->state);
+
+       if (cleaner->state == cleaner_s_idle)
+               begin_cleaning(cleaner);
+
+       isc_event_free(&event);
+}
+
+/* The caller must hold entry lock. */
+static inline isc_boolean_t
+entry_stale(acache_cleaner_t *cleaner, dns_acacheentry_t *entry,
+           isc_stdtime_t now)
+{
+       unsigned int interval = cleaner->cleaning_interval;
+
+       /*
+        * If the callback has been canceled, we definitely do not need the
+        * entry.
+        */
+       if (entry->callback == NULL)
+               return (ISC_TRUE);
+
+       if (entry->lastused + interval < now)
+               return (ISC_TRUE);
+
+       /*
+        * If the acache is in an overmem state, probabilistically decide if
+        * the entry should be purged, based on the time passed from its last
+        * use and the cleaning interval.
+        */
+       if (cleaner->overmem) {
+               unsigned int passed = now - entry->lastused; /* <= interval */
+               isc_uint32_t val, r;
+
+               isc_random_get(&val);
+               r = val % interval;
+
+               if (r < passed)
+                       return (ISC_TRUE);
+       }
+
+       return (ISC_FALSE);
+}
+
+/*
+ * Do incremental cleaning.
+ */
+static void
+acache_incremental_cleaning_action(isc_task_t *task, isc_event_t *event) {
+       acache_cleaner_t *cleaner = event->ev_arg;
+       dns_acache_t *acache = cleaner->acache;
+       dns_acacheentry_t *entry, *next = NULL;
+       int n_entries;
+       isc_stdtime_t now;
+
+       INSIST(DNS_ACACHE_VALID(acache));
+       INSIST(task == acache->task);
+       INSIST(event->ev_type == DNS_EVENT_ACACHECLEAN);
+
+       if (cleaner->state == cleaner_s_done) {
+               cleaner->state = cleaner_s_busy;
+               end_cleaning(cleaner, event);
+               return;
+       }
+
+       INSIST(CLEANER_BUSY(cleaner));
+
+       n_entries = cleaner->increment;
+
+       isc_stdtime_get(&now);
+
+       LOCK(&acache->lock);
+
+       entry = cleaner->current_entry;
+
+       while (n_entries-- > 0) {
+               isc_boolean_t is_stale = ISC_FALSE;
+               
+               INSIST(entry != NULL);
+
+               next = ISC_LIST_NEXT(entry, link);
+
+               LOCK(&entry->lock);
+
+               is_stale = entry_stale(cleaner, entry, now);
+               if (is_stale) {
+                       ISC_LIST_UNLINK(acache->entries, entry, link);
+                       unlink_dbentries(acache, entry);
+                       if (entry->callback != NULL)
+                               (entry->callback)(entry, &entry->cbarg);
+                       entry->callback = NULL;
+
+                       cleaner->ncleaned++;
+               }
+
+               UNLOCK(&entry->lock);
+
+               if (is_stale)
+                       dns_acache_detachentry(&entry);
+
+               if (next == NULL) {
+                       UNLOCK(&acache->lock);
+                       end_cleaning(cleaner, event);
+                       return;
+               }
+
+               entry = next;
+       }
+
+       /*
+        * We have successfully performed a cleaning increment but have
+        * not gone through the entire cache.  Remember the entry that will
+        * be the starting point in the next clean-up, and reschedule another
+        * batch.  If it fails, just try to continue anyway.
+        */
+       INSIST(next != NULL && next != cleaner->current_entry);
+       dns_acache_detachentry(&cleaner->current_entry);
+       dns_acache_attachentry(next, &cleaner->current_entry);
+
+       UNLOCK(&acache->lock);
+
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE,
+                     ISC_LOG_DEBUG(1), "acache cleaner: checked %d entries, "
+                     "mem inuse %lu, sleeping", cleaner->increment,
+                     (unsigned long)isc_mem_inuse(cleaner->acache->mctx));
+
+       isc_task_send(task, &event);
+       INSIST(CLEANER_BUSY(cleaner));
+
+       return;
+}
+
+/*
+ * This is called when the acache either surpasses its upper limit
+ * or shrinks beyond its lower limit.
+ */
+static void
+acache_overmem_cleaning_action(isc_task_t *task, isc_event_t *event) {
+       acache_cleaner_t *cleaner = event->ev_arg;
+       isc_boolean_t want_cleaning = ISC_FALSE;
+       
+       UNUSED(task);
+
+       INSIST(event->ev_type == DNS_EVENT_ACACHEOVERMEM);
+       INSIST(cleaner->overmem_event == NULL);
+
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE,
+                     ISC_LOG_DEBUG(1), "overmem_cleaning_action called, "
+                     "overmem = %d, state = %d", cleaner->overmem,
+                     cleaner->state);
+
+       LOCK(&cleaner->lock);
+
+       if (cleaner->overmem) {
+               if (cleaner->state == cleaner_s_idle)
+                       want_cleaning = ISC_TRUE;
+       } else {
+               if (cleaner->state == cleaner_s_busy)
+                       /*
+                        * end_cleaning() can't be called here because
+                        * then both cleaner->overmem_event and
+                        * cleaner->resched_event will point to this
+                        * event.  Set the state to done, and then
+                        * when the acache_incremental_cleaning_action() event
+                        * is posted, it will handle the end_cleaning.
+                        */
+                       cleaner->state = cleaner_s_done;
+       }
+
+       cleaner->overmem_event = event;
+
+       UNLOCK(&cleaner->lock);
+
+       if (want_cleaning)
+               begin_cleaning(cleaner);
+}
+
+static void
+water(void *arg, int mark) {
+       dns_acache_t *acache = arg;
+       isc_boolean_t overmem = ISC_TF(mark == ISC_MEM_HIWATER);
+
+       REQUIRE(DNS_ACACHE_VALID(acache));
+
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_ACACHE, ISC_LOG_DEBUG(1),
+                     "acache memory reaches %s watermark, mem inuse %lu",
+                     overmem ? "high" : "low",
+                     (unsigned long)isc_mem_inuse(acache->mctx));
+
+       LOCK(&acache->cleaner.lock);
+
+       acache->cleaner.overmem = overmem;
+
+       if (acache->cleaner.overmem_event != NULL)
+               isc_task_send(acache->task, &acache->cleaner.overmem_event);
+
+       UNLOCK(&acache->cleaner.lock);
+}
+
+/*
+ * The cleaner task is shutting down; do the necessary cleanup.
+ */
+static void
+acache_cleaner_shutdown_action(isc_task_t *task, isc_event_t *event) {
+       dns_acache_t *acache = event->ev_arg;
+       isc_boolean_t should_free = ISC_FALSE;
+
+       INSIST(task == acache->task);
+       INSIST(event->ev_type == ISC_TASKEVENT_SHUTDOWN);
+       INSIST(DNS_ACACHE_VALID(acache));
+
+       ATRACE("acache cleaner shutdown");
+
+       if (CLEANER_BUSY(&acache->cleaner))
+               end_cleaning(&acache->cleaner, event);
+       else
+               isc_event_free(&event);
+
+       LOCK(&acache->lock);
+
+       acache->live_cleaners--;
+       INSIST(acache->live_cleaners == 0);
+
+       if (isc_refcount_current(&acache->refs) == 0) {
+               INSIST(check_noentry(acache) == ISC_TRUE);
+               should_free = ISC_TRUE;
+       }
+
+       /*
+        * By detaching the timer in the context of its task,
+        * we are guaranteed that there will be no further timer
+        * events.
+        */
+       if (acache->cleaner.cleaning_timer != NULL)
+               isc_timer_detach(&acache->cleaner.cleaning_timer);
+
+       /* Make sure we don't reschedule anymore. */
+       (void)isc_task_purge(task, NULL, DNS_EVENT_ACACHECLEAN, NULL);
+
+       UNLOCK(&acache->lock);
+
+       if (should_free)
+               destroy(acache);
+}
+
+/*
+ *     Public functions.
+ */
+
+isc_result_t
+dns_acache_create(dns_acache_t **acachep, isc_mem_t *mctx,
+                 isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr)
+{
+       int i;
+       isc_result_t result;
+       dns_acache_t *acache;
+
+       REQUIRE(acachep != NULL && *acachep == NULL);
+       REQUIRE(mctx != NULL);
+       REQUIRE(taskmgr != NULL);
+
+       acache = isc_mem_get(mctx, sizeof(*acache));
+       if (acache == NULL)
+               return (ISC_R_NOMEMORY);
+
+       ATRACE("create");
+
+       isc_refcount_init(&acache->refs, 1);
+
+       result = isc_mutex_init(&acache->lock);
+       if (result != ISC_R_SUCCESS) {
+               isc_mem_put(mctx, acache, sizeof(*acache));
+               UNEXPECTED_ERROR(__FILE__, __LINE__,
+                                "isc_mutex_init() failed: %s",
+                                isc_result_totext(result));
+               return (ISC_R_UNEXPECTED);
+       }
+
+       acache->mctx = NULL;
+       isc_mem_attach(mctx, &acache->mctx);
+       ISC_LIST_INIT(acache->entries);
+
+       acache->shutting_down = ISC_FALSE;
+
+       acache->task = NULL;
+       result = isc_task_create(taskmgr, 1, &acache->task);
+       if (result != ISC_R_SUCCESS) {
+               UNEXPECTED_ERROR(__FILE__, __LINE__,
+                                "isc_task_create() failed(): %s",
+                                dns_result_totext(result));
+               result = ISC_R_UNEXPECTED;
+               goto cleanup;
+       }
+       isc_task_setname(acache->task, "acachetask", acache);
+       ISC_EVENT_INIT(&acache->cevent, sizeof(acache->cevent), 0, NULL,
+                      DNS_EVENT_ACACHECONTROL, shutdown_task, NULL,
+                      NULL, NULL, NULL);
+       acache->cevent_sent = ISC_FALSE;
+
+       acache->dbentries = 0;
+       for (i = 0; i < DBBUCKETS; i++)
+               ISC_LIST_INIT(acache->dbbucket[i]);
+
+       acache->live_cleaners = 0;
+       result = acache_cleaner_init(acache, timermgr, &acache->cleaner); 
+       if (result != ISC_R_SUCCESS)
+               goto cleanup;
+
+       acache->magic = ACACHE_MAGIC;
+
+       *acachep = acache;
+       return (ISC_R_SUCCESS);
+
+ cleanup:
+       if (acache->task != NULL)
+               isc_task_detach(&acache->task);
+       DESTROYLOCK(&acache->lock);
+       isc_mem_put(mctx, acache, sizeof(*acache));
+       isc_mem_detach(&mctx);
+
+       return (result);
+}
+
+void
+dns_acache_attach(dns_acache_t *source, dns_acache_t **targetp) {
+       REQUIRE(DNS_ACACHE_VALID(source));
+       REQUIRE(targetp != NULL && *targetp == NULL);
+
+       AATRACE(source, "attach");
+
+       isc_refcount_increment(&source->refs, NULL);
+
+       *targetp = source;
+}
+
+void
+dns_acache_detach(dns_acache_t **acachep) {
+       dns_acache_t *acache;
+       unsigned int refs;
+       isc_boolean_t should_free = ISC_FALSE;
+
+       REQUIRE(acachep != NULL && DNS_ACACHE_VALID(*acachep));
+       acache = *acachep;
+
+       ATRACE("detach");
+
+       isc_refcount_decrement(&acache->refs, &refs);
+       if (refs == 0) {
+               INSIST(check_noentry(acache) == ISC_TRUE);
+               should_free = ISC_TRUE;
+       }
+
+       *acachep = NULL;
+
+       /*
+        * If we're exiting and the cleaner task exists, let it free the cache.
+        */
+       if (should_free && acache->live_cleaners > 0) {
+               isc_task_shutdown(acache->task);
+               should_free = ISC_FALSE;
+       }
+       
+       if (should_free)
+               destroy(acache);
+}
+
+void
+dns_acache_shutdown(dns_acache_t *acache) {
+       REQUIRE(DNS_ACACHE_VALID(acache));
+
+       LOCK(&acache->lock);
+
+       ATRACE("shutdown");
+
+       if (!acache->shutting_down) {
+               isc_event_t *event;
+               dns_acache_t *acache_evarg = NULL;
+
+               INSIST(!acache->cevent_sent);
+
+               acache->shutting_down = ISC_TRUE;
+
+               isc_mem_setwater(acache->mctx, NULL, NULL, 0, 0);
+
+               /*
+                * Self attach the object in order to prevent it from being
+                * destroyed while waiting for the event.
+                */
+               dns_acache_attach(acache, &acache_evarg);
+               event = &acache->cevent;
+               event->ev_arg = acache_evarg;
+               isc_task_send(acache->task, &event);
+               acache->cevent_sent = ISC_TRUE;
+       }
+
+       UNLOCK(&acache->lock);
+}
+
+isc_result_t
+dns_acache_setdb(dns_acache_t *acache, dns_db_t *db) {
+       int bucket;
+       dbentry_t *dbentry;
+       isc_result_t result = ISC_R_SUCCESS;
+
+       REQUIRE(DNS_ACACHE_VALID(acache));
+       REQUIRE(db != NULL);
+
+       ATRACE("setdb");
+
+       LOCK(&acache->lock);
+
+       dbentry = NULL;
+       result = finddbent(acache, db, &dbentry);
+       if (result == ISC_R_SUCCESS) {
+               result = ISC_R_EXISTS;
+               goto end;
+       }
+       result = ISC_R_SUCCESS;
+
+       dbentry = isc_mem_get(acache->mctx, sizeof(*dbentry));
+       if (dbentry == NULL) {
+               result = ISC_R_NOMEMORY;
+               goto end;
+       }
+
+       ISC_LINK_INIT(dbentry, link);
+       ISC_LIST_INIT(dbentry->originlist);
+       ISC_LIST_INIT(dbentry->referlist);
+
+       dbentry->db = NULL;
+       dns_db_attach(db, &dbentry->db);
+
+       bucket = isc_hash_calc((const unsigned char *)&db,
+                              sizeof(db), ISC_TRUE) % DBBUCKETS;
+
+       ISC_LIST_APPEND(acache->dbbucket[bucket], dbentry, link);
+
+       acache->dbentries++;
+
+ end:
+       UNLOCK(&acache->lock);
+
+       return (result);
+}
+
+isc_result_t
+dns_acache_putdb(dns_acache_t *acache, dns_db_t *db) {
+       int bucket;
+       isc_result_t result;
+       dbentry_t *dbentry;
+       dns_acacheentry_t *entry;
+
+       REQUIRE(DNS_ACACHE_VALID(acache));
+       REQUIRE(db != NULL);
+
+       ATRACE("putdb");
+
+       LOCK(&acache->lock);
+
+       dbentry = NULL;
+       result = finddbent(acache, db, &dbentry);
+       if (result != ISC_R_SUCCESS) {
+               /*
+                * The entry may have not been created due to memory shortage.
+                */
+               UNLOCK(&acache->lock);
+               return (ISC_R_NOTFOUND);
+       }
+
+       /*
+        * Release corresponding cache entries: for each entry, release all
+        * links the entry has, and then callback to the entry holder (if any).
+        * If no other external references exist (this can happen if the
+        * original holder has canceled callback,) destroy it here.
+        */
+       while ((entry = ISC_LIST_HEAD(dbentry->originlist)) != NULL) {
+               LOCK(&entry->lock);
+
+               /*
+                * Releasing olink first would avoid finddbent() in
+                * unlink_dbentries().
+                */
+               ISC_LIST_UNLINK(dbentry->originlist, entry, olink);
+               if (acache->cleaner.current_entry == NULL ||
+                   acache->cleaner.current_entry != entry) {
+                       ISC_LIST_UNLINK(acache->entries, entry, link);
+               }
+               unlink_dbentries(acache, entry);
+
+               if (entry->callback != NULL)
+                       (entry->callback)(entry, &entry->cbarg);
+               entry->callback = NULL;
+
+               UNLOCK(&entry->lock);
+
+               if (acache->cleaner.current_entry == NULL ||
+                   acache->cleaner.current_entry != entry) {
+                       dns_acache_detachentry(&entry);
+               }
+       }
+       while ((entry = ISC_LIST_HEAD(dbentry->referlist)) != NULL) {
+               LOCK(&entry->lock);
+
+               ISC_LIST_UNLINK(dbentry->referlist, entry, rlink);
+               if (acache->cleaner.current_entry == NULL ||
+                   acache->cleaner.current_entry != entry) {
+                       ISC_LIST_UNLINK(acache->entries, entry, link);
+               }
+               unlink_dbentries(acache, entry);
+
+               if (entry->callback != NULL)
+                       (entry->callback)(entry, &entry->cbarg);
+               entry->callback = NULL;
+
+               UNLOCK(&entry->lock);
+
+               if (acache->cleaner.current_entry == NULL ||
+                   acache->cleaner.current_entry != entry) {
+                       dns_acache_detachentry(&entry);
+               }
+       }
+
+       INSIST(ISC_LIST_EMPTY(dbentry->originlist) &&
+              ISC_LIST_EMPTY(dbentry->referlist));
+
+       bucket = isc_hash_calc((const unsigned char *)&db,
+                              sizeof(db), ISC_TRUE) % DBBUCKETS;
+       ISC_LIST_UNLINK(acache->dbbucket[bucket], dbentry, link);
+       dns_db_detach(&dbentry->db);
+
+       isc_mem_put(acache->mctx, dbentry, sizeof(*dbentry));
+
+       acache->dbentries--;
+
+       UNLOCK(&acache->lock);
+
+       return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_acache_createentry(dns_acache_t *acache, dns_db_t *origdb,
+                      void (*callback)(dns_acacheentry_t *, void **),
+                      void *cbarg, dns_acacheentry_t **entryp)
+{
+       dns_acacheentry_t *newentry;
+       isc_result_t result;
+
+       REQUIRE(DNS_ACACHE_VALID(acache));
+       REQUIRE(entryp != NULL && *entryp == NULL);
+       REQUIRE(origdb != NULL);
+
+       newentry = isc_mem_get(acache->mctx, sizeof(*newentry));
+       if (newentry == NULL)
+               return (ISC_R_NOMEMORY);
+
+       result = isc_mutex_init(&newentry->lock);
+       if (result != ISC_R_SUCCESS) {
+               isc_mem_put(acache->mctx, newentry, sizeof(*newentry));
+               UNEXPECTED_ERROR(__FILE__, __LINE__,
+                                "isc_mutex_init() failed: %s",
+                                isc_result_totext(result));
+               return (ISC_R_UNEXPECTED);
+       };
+       /*
+        * We need two counters on creation: one for the caller, and one for
+        * the cache object.
+        */
+       isc_refcount_init(&newentry->references, 2);
+
+       ISC_LINK_INIT(newentry, link);
+       ISC_LINK_INIT(newentry, olink);
+       ISC_LINK_INIT(newentry, rlink);
+
+       newentry->acache = NULL;
+       dns_acache_attach(acache, &newentry->acache);
+
+       newentry->zone = NULL;
+       newentry->db = NULL;
+       newentry->version = NULL;
+       newentry->node = NULL;
+       newentry->foundname = NULL;
+
+       newentry->callback = callback;
+       newentry->cbarg = cbarg;
+       newentry->origdb = NULL;
+       dns_db_attach(origdb, &newentry->origdb);
+
+       isc_stdtime_get(&newentry->lastused);
+
+       newentry->magic = ACACHEENTRY_MAGIC;
+
+       *entryp = newentry;
+
+       return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_acache_getentry(dns_acacheentry_t *entry, dns_zone_t **zonep,
+                   dns_db_t **dbp, dns_dbversion_t **versionp,
+                   dns_dbnode_t **nodep, dns_name_t *fname,
+                   dns_message_t *msg, isc_stdtime_t now)
+{
+       isc_result_t result = ISC_R_SUCCESS;
+       dns_rdataset_t *erdataset;
+
+       REQUIRE(DNS_ACACHEENTRY_VALID(entry));
+       REQUIRE(zonep == NULL || *zonep == NULL);
+       REQUIRE(dbp != NULL && *dbp == NULL);
+       REQUIRE(versionp != NULL && *versionp == NULL);
+       REQUIRE(nodep != NULL && *nodep == NULL);
+       REQUIRE(fname != NULL);
+       REQUIRE(msg != NULL);
+
+       LOCK(&entry->lock);
+
+       entry->lastused = now;
+
+       if (entry->zone != NULL && zonep != NULL)
+               dns_zone_attach(entry->zone, zonep);
+
+       if (entry->db == NULL) {
+               *dbp = NULL;
+               *versionp = NULL;
+       } else {
+               dns_db_attach(entry->db, dbp);
+               dns_db_attachversion(entry->db, entry->version, versionp);
+       }
+       if (entry->node == NULL)
+               *nodep = NULL;
+       else {
+               dns_db_attachnode(entry->db, entry->node, nodep);
+
+               INSIST(entry->foundname != NULL);
+               dns_name_copy(entry->foundname, fname, NULL);
+               for (erdataset = ISC_LIST_HEAD(entry->foundname->list);
+                    erdataset != NULL;
+                    erdataset = ISC_LIST_NEXT(erdataset, link)) {
+                       dns_rdataset_t *ardataset;
+
+                       ardataset = NULL;
+                       result = dns_message_gettemprdataset(msg, &ardataset);
+                       if (result != ISC_R_SUCCESS) {
+                               UNLOCK(&entry->lock);
+                               goto fail;
+                       }
+
+                       /*
+                        * XXXJT: if we simply clone the rdataset, we'll get
+                        * lost wrt cyclic ordering.  We'll need an additional
+                        * trick to get the latest counter from the original
+                        * header.
+                        */
+                       dns_rdataset_init(ardataset);
+                       dns_rdataset_clone(erdataset, ardataset);
+                       ISC_LIST_APPEND(fname->list, ardataset, link);
+               }
+       }
+
+       UNLOCK(&entry->lock);
+
+       return (result);
+
+  fail:
+       while ((erdataset = ISC_LIST_HEAD(fname->list)) != NULL) {
+               ISC_LIST_UNLINK(fname->list, erdataset, link);
+               dns_rdataset_disassociate(erdataset);
+               dns_message_puttemprdataset(msg, &erdataset);
+       }
+       if (*nodep != NULL)
+               dns_db_detachnode(*dbp, nodep);
+       if (*versionp != NULL)
+               dns_db_closeversion(*dbp, versionp, ISC_FALSE);
+       if (*dbp != NULL)
+               dns_db_detach(dbp);
+       if (zonep != NULL && *zonep != NULL)
+               dns_zone_detach(zonep);
+
+       return (result);
+}
+
+isc_result_t
+dns_acache_setentry(dns_acache_t *acache, dns_acacheentry_t *entry,
+                   dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version,
+                   dns_dbnode_t *node, dns_name_t *fname)
+{
+       isc_result_t result;
+       dbentry_t *odbent;
+       dbentry_t *rdbent = NULL;
+       isc_boolean_t close_version = ISC_FALSE;
+
+       REQUIRE(DNS_ACACHE_VALID(acache));
+       REQUIRE(DNS_ACACHEENTRY_VALID(entry));
+
+       LOCK(&acache->lock);    /* XXX: need to lock it here for ordering */
+       LOCK(&entry->lock);
+
+       /* Set zone */
+       if (zone != NULL)
+               dns_zone_attach(zone, &entry->zone);
+       /* Set DB */
+       if (db != NULL)
+               dns_db_attach(db, &entry->db);
+       /*
+        * Set DB version.  If the version is not given by the caller,
+        * which is the case for glue or cache DBs, use the current version.
+        */
+       if (version == NULL) {
+               if (db != NULL) {
+                       dns_db_currentversion(db, &version);
+                       close_version = ISC_TRUE;
+               }
+       }
+       if (version != NULL) {
+               INSIST(db != NULL);
+               dns_db_attachversion(db, version, &entry->version);
+       }
+       if (close_version)
+               dns_db_closeversion(db, &version, ISC_FALSE);
+       /* Set DB node. */
+       if (node != NULL) {
+               INSIST(db != NULL);
+               dns_db_attachnode(db, node, &entry->node);
+       }
+
+       /*
+        * Set list of the corresponding rdatasets, if given.
+        * To minimize the overhead and memory consumption, we'll do this for
+        * positive cache only, in which case the DB node is non NULL.
+        * We do not want to cache incomplete information, so give up the
+        * entire entry when a memory shortage happen during the process.
+        */
+       if (node != NULL) {
+               dns_rdataset_t *ardataset, *crdataset;
+
+               entry->foundname = isc_mem_get(acache->mctx,
+                                              sizeof(*entry->foundname));
+
+               if (entry->foundname == NULL) {
+                       result = ISC_R_NOMEMORY;
+                       goto fail;
+               }
+               dns_name_init(entry->foundname, NULL);
+               result = dns_name_dup(fname, acache->mctx,
+                                     entry->foundname);
+               if (result != ISC_R_SUCCESS)
+                       goto fail;
+
+               for (ardataset = ISC_LIST_HEAD(fname->list);
+                    ardataset != NULL;
+                    ardataset = ISC_LIST_NEXT(ardataset, link)) {
+                       crdataset = isc_mem_get(acache->mctx,
+                                               sizeof(*crdataset));
+                       if (crdataset == NULL) {
+                               result = ISC_R_NOMEMORY;
+                               goto fail;
+                       }
+
+                       dns_rdataset_init(crdataset);
+                       dns_rdataset_clone(ardataset, crdataset);
+                       ISC_LIST_APPEND(entry->foundname->list, crdataset,
+                                       link);
+               }
+       }
+
+       odbent = NULL;
+       result = finddbent(acache, entry->origdb, &odbent);
+       if (result != ISC_R_SUCCESS)
+               goto fail;
+       if (db != NULL) {
+               rdbent = NULL;
+               result = finddbent(acache, db, &rdbent);
+               if (result != ISC_R_SUCCESS)
+                       goto fail;
+       }
+
+       ISC_LIST_APPEND(acache->entries, entry, link);
+       ISC_LIST_APPEND(odbent->originlist, entry, olink);
+       if (rdbent != NULL)
+               ISC_LIST_APPEND(rdbent->referlist, entry, rlink);
+
+       UNLOCK(&entry->lock);
+       UNLOCK(&acache->lock);
+
+       return (ISC_R_SUCCESS);
+
+ fail:
+       clear_entry(acache, entry);
+
+       UNLOCK(&entry->lock);
+       UNLOCK(&acache->lock);
+
+       return (result);
+}
+
+void
+dns_acache_cancelentry(dns_acacheentry_t *entry) {
+       dns_acache_t *acache = entry->acache;
+
+       REQUIRE(DNS_ACACHEENTRY_VALID(entry));
+       INSIST(DNS_ACACHE_VALID(acache));
+
+       LOCK(&acache->lock);
+       LOCK(&entry->lock);
+
+       /*
+        * Release dependencies stored in this entry as much as possible.
+        * The main link cannot be released, since the acache object has
+        * a reference to this entry; the empty entry will be released in
+        * the next cleaning action.
+        */
+       unlink_dbentries(acache, entry);
+       clear_entry(entry->acache, entry);
+
+       entry->callback = NULL;
+       entry->cbarg = NULL;
+
+       UNLOCK(&entry->lock);
+       UNLOCK(&acache->lock);
+}
+
+void
+dns_acache_attachentry(dns_acacheentry_t *source,
+                      dns_acacheentry_t **targetp)
+{
+       REQUIRE(DNS_ACACHEENTRY_VALID(source));
+       REQUIRE(targetp != NULL && *targetp == NULL);
+
+       isc_refcount_increment(&source->references, NULL);
+
+       *targetp = source;
+}
+
+void
+dns_acache_detachentry(dns_acacheentry_t **entryp) {
+       dns_acacheentry_t *entry;
+       unsigned int refs;
+
+       REQUIRE(entryp != NULL && DNS_ACACHEENTRY_VALID(*entryp));
+       entry = *entryp;
+
+       isc_refcount_decrement(&entry->references, &refs);
+
+       /*
+        * If there are no references to the entry, the entry must have been
+        * unlinked and can be destroyed safely.
+        */
+       if (refs == 0) {
+               INSIST(!ISC_LINK_LINKED(entry, link));
+               destroy_entry(entry);
+       }
+
+       *entryp = NULL;
+}
+
+void
+dns_acache_setcleaninginterval(dns_acache_t *acache, unsigned int t) {
+       isc_interval_t interval;
+       isc_result_t result;
+
+       REQUIRE(DNS_ACACHE_VALID(acache));
+
+       ATRACE("dns_acache_setcleaninginterval");
+
+       LOCK(&acache->lock);
+
+       /*
+        * It may be the case that the acache has already shut down.
+        * If so, it has no timer.  (Not sure if this can really happen.)
+        */
+       if (acache->cleaner.cleaning_timer == NULL)
+               goto unlock;
+
+       acache->cleaner.cleaning_interval = t;
+
+       if (t == 0) {
+               result = isc_timer_reset(acache->cleaner.cleaning_timer,
+                                        isc_timertype_inactive,
+                                        NULL, NULL, ISC_TRUE);
+       } else {
+               isc_interval_set(&interval, acache->cleaner.cleaning_interval,
+                                0);
+               result = isc_timer_reset(acache->cleaner.cleaning_timer,
+                                        isc_timertype_ticker,
+                                        NULL, &interval, ISC_FALSE);
+       }
+       if (result != ISC_R_SUCCESS)    
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_ACACHE, ISC_LOG_WARNING,
+                             "could not set acache cleaning interval: %s",
+                             isc_result_totext(result));
+
+ unlock:
+       UNLOCK(&acache->lock);
+}
+
+/*
+ * This function was derived from cache.c:dns_cache_setcachesize().  See the
+ * function for more details about the logic.
+ */
+void
+dns_acache_setcachesize(dns_acache_t *acache, isc_uint32_t size) {
+       isc_uint32_t lowater;
+       isc_uint32_t hiwater;
+
+       REQUIRE(DNS_ACACHE_VALID(acache));
+
+       if (size != 0 && size < DNS_ACACHE_MINSIZE)
+               size = DNS_ACACHE_MINSIZE;
+
+       hiwater = size - (size >> 3);
+       lowater = size - (size >> 2);
+
+       if (size == 0 || hiwater == 0 || lowater == 0)
+               isc_mem_setwater(acache->mctx, water, acache, 0, 0);
+       else
+               isc_mem_setwater(acache->mctx, water, acache,
+                                hiwater, lowater);
+}
index c8033469afca48d4c4f8fa47eba492c4c8428b42..a539ebdee43185a2ffcc797cd8d4abcfba15e54e 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: db.c,v 1.74 2004/03/05 05:09:18 marka Exp $ */
+/* $Id: db.c,v 1.75 2004/12/21 10:45:16 jinmei Exp $ */
 
 /***
  *** Imports
@@ -791,3 +791,51 @@ dns_db_unregister(dns_dbimplementation_t **dbimp) {
        isc_mem_detach(&mctx);
        RWUNLOCK(&implock, isc_rwlocktype_write);
 }
+
+isc_result_t
+dns_db_getsoanode(dns_db_t *db, dns_dbnode_t **nodep) {
+       REQUIRE(DNS_DB_VALID(db));
+       REQUIRE(dns_db_iszone(db) == ISC_TRUE);
+       REQUIRE(nodep != NULL && *nodep == NULL);
+
+       if (db->methods->getsoanode != NULL)
+               return ((db->methods->getsoanode)(db, nodep));
+
+       return (ISC_R_NOTFOUND);
+}
+
+isc_result_t
+dns_db_setsoanode(dns_db_t *db, dns_dbnode_t *node) {
+       REQUIRE(DNS_DB_VALID(db));
+       REQUIRE(dns_db_iszone(db) == ISC_TRUE);
+       REQUIRE(node != NULL);
+
+       if (db->methods->setsoanode != NULL)
+               return ((db->methods->setsoanode)(db, node));
+
+       return (ISC_R_FAILURE);
+}
+
+isc_result_t
+dns_db_getnsnode(dns_db_t *db, dns_dbnode_t **nodep) {
+       REQUIRE(DNS_DB_VALID(db));
+       REQUIRE(dns_db_iszone(db) == ISC_TRUE);
+       REQUIRE(nodep != NULL && *nodep == NULL);
+
+       if (db->methods->getnsnode != NULL)
+               return ((db->methods->getnsnode)(db, nodep));
+
+       return (ISC_R_NOTFOUND);
+}
+
+isc_result_t
+dns_db_setnsnode(dns_db_t *db, dns_dbnode_t *node) {
+       REQUIRE(DNS_DB_VALID(db));
+       REQUIRE(dns_db_iszone(db) == ISC_TRUE);
+       REQUIRE(node != NULL);
+
+       if (db->methods->setnsnode != NULL)
+               return ((db->methods->setnsnode)(db, node));
+
+       return (ISC_R_FAILURE);
+}
diff --git a/lib/dns/include/dns/acache.h b/lib/dns/include/dns/acache.h
new file mode 100644 (file)
index 0000000..2969f77
--- /dev/null
@@ -0,0 +1,440 @@
+/*
+ * Copyright (C) 2003  Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+ * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: acache.h,v 1.2 2004/12/21 10:45:18 jinmei Exp $ */
+
+#ifndef DNS_ACACHE_H
+#define DNS_ACACHE_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*
+ * Acache
+ * 
+ * The Additional Cache Object
+ *
+ *     This module manages internal caching entries that correspond to
+ *     the additional section data of a DNS DB node (an RRset header, more
+ *     accurately).  An additional cache entry is expected to be (somehow)
+ *     attached to a particular RR in a particular DB node, and contains a set
+ *     of information of an additional data for the DB node.
+ *
+ *     An additional cache object is intended to be created as a per-view
+ *     object, and manages all cache entries within the view.
+ *
+ *     The intended usage of the additional caching is to provide a short cut
+ *     to additional glue RRs of an NS RR.  For each NS RR, it is often
+ *     necessary to look for glue RRs to make a proper response.  Once the
+ *     glue RRs are known, the additional caching allows the client to
+ *     associate the information to the original NS RR so that further
+ *     expensive lookups can be avoided for the NS RR.
+ *
+ *     Each additional cache entry contains information to identify a
+ *     particular DB node and (optionally) an associated RRset.  The
+ *     information consists of its zone, database, the version of the
+ *     database, database node, and RRset.
+ *
+ *     A "negative" information can also be cached.  For example, if a glue
+ *     RR does not exist as an authoritative data in the same zone as that
+ *     of the NS RR, this fact can be cached by specifying a NULL pointer
+ *     for the database, version, and node.  (See the description for
+ *     dns_acache_getentry() below for more details.)
+ *
+ *     Since each member stored in an additional cache entry holds a reference
+ *     to a corresponding object, a stale cache entry may cause unnecessary
+ *     memory consumption.  For instance, when a zone is reloaded, additional
+ *     cache entries that have a reference to the zone (and its DB and/or
+ *     DB nodes) can delay the cleanup of the referred objects.  In order to
+ *     minimize such a bad effect, this module provides several cleanup
+ *     mechanisms.
+ *
+ *     The first one is a shutdown procedure called when the associated view
+ *     is shut down.  In this case, dns_acache_shutdown() will be called and
+ *     all cache entries will be purged.  This mechanism will help the
+ *     situation when the configuration is reloaded or the main server is
+ *     stopped.
+ *
+ *     Per-DB cleanup mechanism is also provided.  Each additional cache entry
+ *     is associated with related DB, which is expected to have been
+ *     registered when the DB was created by dns_acache_setdb().  If a
+ *     particular DB is going to be destroyed, the primary holder of the DB,
+ *     a typical example of which is a zone, will call dns_acache_putdb().
+ *     Then this module will clean-up all cache entries associated with the
+ *     DB.  This mechanism is effective when a secondary zone DB is going to
+ *     be stale after a zone transfer.
+ *
+ *     Finally, this module supports for periodic clean-up of stale entries.
+ *     Each cache entry has a timestamp field, which is updated every time
+ *     the entry is referred.  A periodically invoked cleaner checks the
+ *     timestamp of each entry, and purge entries that have not been referred
+ *     for a certain period.  The cleaner interval can be specified by
+ *     dns_acache_setcleaninginterval().  If the periodic clean-up is not
+ *     enough, it is also possible to specify the upper limit of entries
+ *     in terms of the memory consumption.  If the maximum value is
+ *     specified, the cleaner is invoked when the memory consumption reaches
+ *     the high watermark inferred from the maximum value.  In this case,
+ *     the cleaner will use more aggressive algorithm to decide the "victim"
+ *     entries.  The maximum value can be specified by
+ *     dns_acache_setcachesize().
+ *
+ *     When a cache entry is going to be purged within this module, the
+ *     callback function specified at the creation time will be called.
+ *     The callback function is expected to release all internal resources
+ *     related to the entry, which will typically be specific to DB
+ *     implementation, and to call dns_acache_detachentry().  The callback
+ *     mechanism is very important, since the holder of an additional cache
+ *     entry may not be able to initiate the clean-up of the entry, due to
+ *     the reference ordering.  For example, as long as an additional cache
+ *     entry has a reference to a DB object, the DB cannot be freed, in which
+ *     a DB node may have a reference to the cache entry.
+ *
+ *     Credits:
+ *     The basic idea of this kind of short-cut for frequently used
+ *     information is similar to the "pre-compiled answer" approach adopted
+ *     in nsd by NLnet LABS with RIPE NCC.  Our work here is an independent
+ *     effort, but the success of nsd encouraged us to pursue this path.
+ *
+ *     The design and implementation of the periodic memory management and
+ *     the upper limitation of memory consumption was derived from the cache
+ *     DB implementation of BIND9.
+ *
+ * MP:
+ *     There are two main locks in this module.  One is for each entry, and
+ *     the other is for the additional cache object.
+ *
+ * Reliability:
+ *     The callback function for a cache entry is called with holding the
+ *     entry lock.  Thus, it implicitly assumes the callback function does not
+ *     call a function that can require the lock.  Typically, the only
+ *     function that can be called from the callback function safely is
+ *     dns_acache_detachentry().  The breakage of this implicit assumption
+ *     may cause a deadlock.
+ *
+ * Resources:
+ *     In a 32-bit architecture (such as i386), the following additional
+ *     memory is required comparing to the case that disables this module.
+ *     - 76 bytes for each additional cache entry
+ *     - if the entry has a DNS name and associated RRset,
+ *       * 44 bytes + size of the name (1-255 bytes)
+ *       * 52 bytes x number_of_RRs 
+ *     - 28 bytes for each DB related to this module
+ *
+ *     Using the additional cache also requires extra memory consumption in
+ *     the DB implementation.  In the current implementation for rbtdb, we
+ *     need:
+ *     - two additional pointers for each DB node (8 bytes for a 32-bit
+ *       architecture
+ *     - for each RR associated to an RR in a DB node, we also need
+ *       a pointer and management objects to support the additional cache
+ *       function.  These are allocated on-demand.  The total size is
+ *       32 bytes for a 32-bit architecture.
+ *
+ * Security:
+ *     Since this module does not handle any low-level data directly,
+ *     no security issue specific to this module is anticipated.
+ *
+ * Standards:
+ *     None.
+ */
+
+/***
+ *** Imports
+ ***/
+
+#include <isc/mutex.h>
+#include <isc/lang.h>
+#include <isc/refcount.h>
+#include <isc/stdtime.h>
+
+#include <dns/types.h>
+
+/***
+ *** Functions
+ ***/
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+dns_acache_create(dns_acache_t **acachep, isc_mem_t *mctx,
+                 isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr);
+/*
+ * Create a new DNS additional cache object.
+ *
+ * Requires:
+ *
+ *     'mctx' is a valid memory context
+ *
+ *     'taskmgr' is a valid task manager
+ *
+ *     'timermgr' is a valid timer or NULL.  If NULL, no periodic cleaning of
+ *     the cache will take place.
+ *
+ *     'acachep' is a valid pointer, and *acachep == NULL
+ *
+ * Ensures:
+ *
+ *     '*acachep' is attached to the newly created cache
+ *
+ * Returns:
+ *
+ *     ISC_R_SUCCESS
+ *     ISC_R_NOMEMORY
+ *     ISC_R_UNEXPECTED
+ */
+
+void
+dns_acache_attach(dns_acache_t *source, dns_acache_t **targetp);
+/*
+ * Attach *targetp to cache.
+ *
+ * Requires:
+ *
+ *     'acache' is a valid additional cache.
+ *
+ *     'targetp' points to a NULL dns_acache_t *.
+ *
+ * Ensures:
+ *
+ *     *targetp is attached to the 'source' additional cache.
+ */
+
+void
+dns_acache_detach(dns_acache_t **acachep);
+/*
+ * Detach *acachep from its cache.
+ *
+ * Requires:
+ *
+ *     '*acachep' points to a valid additional cache.
+ *
+ * Ensures:
+ *
+ *     *acachep is NULL.
+ *
+ *     If '*acachep' is the last reference to the cache and the additional
+ *     cache does not have an outstanding task, all resources used by the
+ *     cache will be freed.
+ */
+
+void
+dns_acache_setcleaninginterval(dns_acache_t *acache, unsigned int t);
+/*
+ * Set the periodic cleaning interval of an additional cache to 'interval'
+ * seconds.
+ */
+
+void
+dns_acache_setcachesize(dns_acache_t *acache, isc_uint32_t size);
+/*
+ * Set the maximum additional cache size.  0 means unlimited.
+ */
+
+isc_result_t
+dns_acache_setdb(dns_acache_t *acache, dns_db_t *db);
+/*
+ * Set 'db' in 'acache' when the db can be referred from acache, in order
+ * to provide a hint for resolving the back reference.
+ *
+ * Requires:
+ *     'acache' is a valid acache pointer.
+ *     'db' is a valid DNS DB pointer.
+ *
+ * Ensures:
+ *     'acache' will have a reference to 'db'.
+ *
+ * Returns:
+ *     ISC_R_SUCCESS
+ *     ISC_R_EXISTS    (which means the specified 'db' is already set)
+ *     ISC_R_NOMEMORY
+ */
+
+isc_result_t
+dns_acache_putdb(dns_acache_t *acache, dns_db_t *db);
+/*
+ * Release 'db' from 'acache' if it has been set by dns_acache_setdb().
+ *
+ * Requires:
+ *     'acache' is a valid acache pointer.
+ *     'db' is a valid DNS DB pointer.
+ *
+ * Ensures:
+ *     'acache' will release the reference to 'db'.  Additionally, the content
+ *     of each cache entry that is related to the 'db' will be released via
+ *     the callback function.
+ *
+ * Returns:
+ *     ISC_R_SUCCESS
+ *     ISC_R_NOTFOUND  (which means the specified 'db' is not set in 'acache')
+ *     ISC_R_NOMEMORY
+ */
+
+void
+dns_acache_shutdown(dns_acache_t *acache);
+/*
+ * Shutdown 'acache'.
+ *
+ * Requires:
+ *
+ *     '*acache' is a valid additional cache.
+ */
+
+isc_result_t
+dns_acache_createentry(dns_acache_t *acache, dns_db_t *origdb,
+                      void (*callback)(dns_acacheentry_t *, void **),
+                      void *cbarg, dns_acacheentry_t **entryp);
+/*
+ * Create an additional cache entry.  A new entry is created and attached to
+ * the given additional cache object.  A callback function is also associated
+ * with the created entry, which will be called when the cache entry is purged
+ * for some reason.
+ *
+ * Requires:
+ *
+ *     'acache' is a valid additional cache.
+ *     'entryp' is a valid pointer, and *entryp == NULL
+ *     'origdb' is a valid DNS DB pointer.
+ *     'callback' and 'cbarg' can be NULL.  In this case, however, the entry
+ *     is meaningless (and will be cleaned-up in the next periodical
+ *     cleaning).
+ *
+ * Ensures:
+ *     '*entryp' will point to a new additional cache entry.
+ *
+ * Returns:
+ *     ISC_R_SUCCESS
+ *     ISC_R_NOMEMORY
+ */
+
+isc_result_t
+dns_acache_getentry(dns_acacheentry_t *entry, dns_zone_t **zonep,
+                   dns_db_t **dbp, dns_dbversion_t **versionp,
+                   dns_dbnode_t **nodep, dns_name_t *fname,
+                   dns_message_t *msg, isc_stdtime_t now);
+/*
+ * Get content from a particular additional cache entry.
+ *
+ * Requires:
+ *
+ *     'entry' is a valid additional cache entry.
+ *     'zonep' is a NULL pointer or '*zonep' == NULL (this is the only
+ *     optional parameter.)
+ *     'dbp' is a valid pointer, and '*dbp' == NULL
+ *     'versionp' is a valid pointer, and '*versionp' == NULL
+ *     'nodep' is a valid pointer, and '*nodep' == NULL
+ *     'fname' is a valid DNS name.
+ *     'msg' is a valid DNS message.
+ *
+ * Ensures:
+ *     Several possible cases can happen according to the content.
+ *     1. For a positive cache entry,
+ *     '*zonep' will point to the corresponding zone (if zonep is a valid
+ *     pointer),
+ *     '*dbp' will point to a DB for the zone,
+ *     '*versionp' will point to its version, and
+ *     '*nodep' will point to the corresponding DB node.
+ *     'fname' will have the DNS name of the DB node and contain a list of
+ *     rdataset for the node (which can be an empty list).
+ *
+ *     2. For a negative cache entry that means no corresponding zone exists,
+ *     '*zonep' == NULL (if zonep is a valid pointer)
+ *     '*dbp', '*versionp', and '*nodep' will be NULL.
+ *
+ *     3. For a negative cache entry that means no corresponding DB node
+ *     exists, '*zonep' will point to the corresponding zone (if zonep is a
+ *     valid pointer),
+ *     '*dbp' will point to a corresponding DB for zone,
+ *     '*versionp' will point to its version.
+ *     '*nodep' will be kept as NULL.
+ *     'fname' will not change.
+ *
+ *     On failure, no new references will be created.
+ *
+ * Returns:
+ *     ISC_R_SUCCESS
+ *     ISC_R_NOMEMORY
+ */
+
+isc_result_t
+dns_acache_setentry(dns_acache_t *acache, dns_acacheentry_t *entry,
+                   dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version,
+                   dns_dbnode_t *node, dns_name_t *fname);
+/*
+ * Set content to a particular additional cache entry.
+ *
+ * Requires:
+ *     'acache' is a valid additional cache.
+ *     'entry' is a valid additional cache entry.
+ *     All the others pointers are NULL or a valid pointer of the
+ *     corresponding type.
+ *
+ * Returns:
+ *     ISC_R_SUCCESS
+ *     ISC_R_NOMEMORY
+ *     ISC_R_NOTFOUND
+ */
+
+void
+dns_acache_cancelentry(dns_acacheentry_t *entry);
+/*
+ * Cancel the use of the cache entry 'entry'.  This function is supposed to
+ * be called when the node that holds the entry finds the content is not
+ * correct any more.  This function will try to release as much dependency as
+ * possible, and will be ready to be cleaned-up.  The registered callback
+ * function will be canceled and will never called.
+ *
+ * Requires:
+ *     'entry' is a valid additional cache entry.
+ */
+
+void
+dns_acache_attachentry(dns_acacheentry_t *source, dns_acacheentry_t **targetp);
+/*
+ * Attach *targetp to the cache entry 'source'.
+ *
+ * Requires:
+ *
+ *     'source' is a valid additional cache entry.
+ *
+ *     'targetp' points to a NULL dns_acacheentry_t *.
+ *
+ * Ensures:
+ *
+ *     *targetp is attached to 'source'.
+ */
+                      
+void
+dns_acache_detachentry(dns_acacheentry_t **entryp);
+/*
+ * Detach *entryp from its cache.
+ *
+ * Requires:
+ *
+ *     '*entryp' points to a valid additional cache entry.
+ *
+ * Ensures:
+ *
+ *     *entryp is NULL.
+ *
+ *     If '*entryp' is the last reference to the entry, 
+ *     cache does not have an outstanding task, all resources used by the
+ *     entry (including the entry object itself) will be freed.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_ACACHE_H */
index b4c7261da58fbfe6b0c46e0a78a948112dac112d..c94dc4f930650a8d9ab319a5864aa8d99eda40c5 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: db.h,v 1.77 2004/05/14 04:45:57 marka Exp $ */
+/* $Id: db.h,v 1.78 2004/12/21 10:45:18 jinmei Exp $ */
 
 #ifndef DNS_DB_H
 #define DNS_DB_H 1
@@ -145,6 +145,10 @@ typedef struct dns_dbmethods {
        isc_boolean_t   (*ispersistent)(dns_db_t *db);
        void            (*overmem)(dns_db_t *db, isc_boolean_t overmem);
        void            (*settask)(dns_db_t *db, isc_task_t *);
+       isc_result_t    (*getsoanode)(dns_db_t *db, dns_dbnode_t **nodep);
+       isc_result_t    (*setsoanode)(dns_db_t *db, dns_dbnode_t *nodep);
+       isc_result_t    (*getnsnode)(dns_db_t *db, dns_dbnode_t **nodep);
+       isc_result_t    (*setnsnode)(dns_db_t *db, dns_dbnode_t *nodep);
 } dns_dbmethods_t;
 
 typedef isc_result_t
@@ -1266,6 +1270,84 @@ dns_db_unregister(dns_dbimplementation_t **dbimp);
  *     Any memory allocated in *dbimp will be freed.
  */
 
+isc_result_t
+dns_db_getsoanode(dns_db_t *db, dns_dbnode_t **nodep);
+/*
+ * Get a cached SOA DB node corresponding to the DB's zone.
+ *
+ * Requires:
+ *
+ *     'db' is a valid zone database.
+ *     'nodep' != NULL && '*nodep' == NULL
+ *
+ * Ensures:
+ *     On sucess, '*nodep' will point to a DB node for the SOA RR of 'db.'
+ *
+ * Returns:
+ *     ISC_R_SUCCESS
+ *     ISC_R_NOTFOUND - an SOA RR node has not been cached in 'db' or SOA RR
+ *                      caching is not supported for 'db'
+ */
+
+isc_result_t
+dns_db_setsoanode(dns_db_t *db, dns_dbnode_t *node);
+/*
+ * Set an SOA DB node as cache corresponding to the DB's zone.  If there is
+ * already a node set in the DB, it will be detached and replaced with the
+ * new one.
+ *
+ * Requires:
+ *
+ *     'db' is a valid zone database.
+ *     'node' is a valid DB node.
+ *
+ * Ensures:
+ *     On sucess, '*nodep' will point to a DB node for the SOA RR of 'db.'
+ *
+ * Returns:
+ *     ISC_R_SUCCESS
+ *     ISC_R_FAILURE - SOA RR caching is not supported for 'db'
+ */
+
+isc_result_t
+dns_db_getnsnode(dns_db_t *db, dns_dbnode_t **nodep);
+/*
+ * Get a cached NS DB node corresponding to the DB's zone.
+ *
+ * Requires:
+ *
+ *     'db' is a valid zone database.
+ *     'nodep' != NULL && '*nodep' == NULL
+ *
+ * Ensures:
+ *     On sucess, '*nodep' will point to a DB node for the NS RR of 'db.'
+ *
+ * Returns:
+ *     ISC_R_SUCCESS
+ *     ISC_R_NOTFOUND - an NS RR node has not been cached in 'db' or NS RR
+ *                      caching is not supported for 'db'
+ */
+
+isc_result_t
+dns_db_setnsnode(dns_db_t *db, dns_dbnode_t *node);
+/*
+ * Set an NS DB node as cache corresponding to the DB's zone.  If there is
+ * already a node set in the DB, it will be detached and replaced with the
+ * new one.
+ *
+ * Requires:
+ *
+ *     'db' is a valid zone database.
+ *     'node' is a valid DB node.
+ *
+ * Ensures:
+ *     On sucess, '*nodep' will point to a DB node for the NS RR of 'db.'
+ *
+ * Returns:
+ *     ISC_R_SUCCESS
+ *     ISC_R_FAILURE - NS RR caching is not supported for 'db'
+ */
+
 ISC_LANG_ENDDECLS
 
 #endif /* DNS_DB_H */
index 0c562914855b613cab78d394735783bb396e8d28..9f8ffd13ccf3247b592d2a010aa0f454203ee138 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: events.h,v 1.42 2004/03/05 05:09:42 marka Exp $ */
+/* $Id: events.h,v 1.43 2004/12/21 10:45:19 jinmei Exp $ */
 
 #ifndef DNS_EVENTS_H
 #define DNS_EVENTS_H 1
 #define DNS_EVENT_DUMPQUANTUM                  (ISC_EVENTCLASS_DNS + 34)
 #define DNS_EVENT_IMPORTRECVDONE               (ISC_EVENTCLASS_DNS + 35)
 #define DNS_EVENT_FREESTORAGE                  (ISC_EVENTCLASS_DNS + 36)
+#define DNS_EVENT_VIEWACACHESHUTDOWN           (ISC_EVENTCLASS_DNS + 37)
+#define DNS_EVENT_ACACHECONTROL                        (ISC_EVENTCLASS_DNS + 38)
+#define DNS_EVENT_ACACHECLEAN                  (ISC_EVENTCLASS_DNS + 39)
+#define DNS_EVENT_ACACHEOVERMEM                        (ISC_EVENTCLASS_DNS + 40)
 
 #define DNS_EVENT_FIRSTEVENT                   (ISC_EVENTCLASS_DNS + 0)
 #define DNS_EVENT_LASTEVENT                    (ISC_EVENTCLASS_DNS + 65535)
index dc1fa8c88f08504f4176f9379d946e2317a89360..514d31aa1e8d0894ae4d10774866dffd2023ace0 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: log.h,v 1.33 2004/03/05 05:09:43 marka Exp $ */
+/* $Id: log.h,v 1.34 2004/12/21 10:45:19 jinmei Exp $ */
 
 /* Principal Authors: DCL */
 
@@ -69,6 +69,7 @@ LIBDNS_EXTERNAL_DATA extern isc_logmodule_t dns_modules[];
 #define DNS_LOGMODULE_SDB              (&dns_modules[22])
 #define DNS_LOGMODULE_DIFF             (&dns_modules[23])
 #define DNS_LOGMODULE_HINTS            (&dns_modules[24])
+#define DNS_LOGMODULE_ACACHE           (&dns_modules[25])
 
 ISC_LANG_BEGINDECLS
 
index ee61b9b549846da64bbea49d770a934a7c37e992..a286600bad54b09fd76ef2de3877d538408acba6 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: rdataset.h,v 1.51 2004/03/05 05:09:45 marka Exp $ */
+/* $Id: rdataset.h,v 1.52 2004/12/21 10:45:19 jinmei Exp $ */
 
 #ifndef DNS_RDATASET_H
 #define DNS_RDATASET_H 1
 
 #include <isc/lang.h>
 #include <isc/magic.h>
+#include <isc/stdtime.h>
 
 #include <dns/types.h>
 
 ISC_LANG_BEGINDECLS
 
+typedef enum {
+       dns_rdatasetadditional_fromauth,
+       dns_rdatasetadditional_fromcache,
+       dns_rdatasetadditional_fromglue
+} dns_rdatasetadditional_t;
+
 typedef struct dns_rdatasetmethods {
        void                    (*disassociate)(dns_rdataset_t *rdataset);
        isc_result_t            (*first)(dns_rdataset_t *rdataset);
@@ -74,6 +81,30 @@ typedef struct dns_rdatasetmethods {
                                              dns_name_t *name,
                                              dns_rdataset_t *nsec,
                                              dns_rdataset_t *nsecsig);
+       isc_result_t            (*getadditional)(dns_rdataset_t *rdataset,
+                                                dns_rdatasetadditional_t type,
+                                                dns_rdatatype_t qtype,
+                                                dns_acache_t *acache,
+                                                dns_zone_t **zonep,
+                                                dns_db_t **dbp,
+                                                dns_dbversion_t **versionp,
+                                                dns_dbnode_t **nodep,
+                                                dns_name_t *fname,
+                                                dns_message_t *msg,
+                                                isc_stdtime_t now);
+       isc_result_t            (*setadditional)(dns_rdataset_t *rdataset,
+                                                dns_rdatasetadditional_t type,
+                                                dns_rdatatype_t qtype,
+                                                dns_acache_t *acache,
+                                                dns_zone_t *zone,
+                                                dns_db_t *db,
+                                                dns_dbversion_t *version,
+                                                dns_dbnode_t *node,
+                                                dns_name_t *fname);
+       isc_result_t            (*putadditional)(dns_acache_t *acache,
+                                                dns_rdataset_t *rdataset,
+                                                dns_rdatasetadditional_t type,
+                                                dns_rdatatype_t qtype);
 } dns_rdatasetmethods_t;
 
 #define DNS_RDATASET_MAGIC            ISC_MAGIC('D','N','S','R')
@@ -409,7 +440,6 @@ dns_rdataset_towirepartial(dns_rdataset_t *rdataset,
  *                   written.
  */
 
-
 isc_result_t
 dns_rdataset_additionaldata(dns_rdataset_t *rdataset,
                            dns_additionaldatafunc_t add, void *arg);
@@ -463,6 +493,100 @@ dns_rdataset_addnoqname(dns_rdataset_t *rdataset, dns_name_t *name);
  *     'name' to be valid and have NSEC and RRSIG(NSEC) rdatasets.
  */
 
+isc_result_t
+dns_rdataset_getadditional(dns_rdataset_t *rdataset,
+                          dns_rdatasetadditional_t type,
+                          dns_rdatatype_t qtype,
+                          dns_acache_t *acache,
+                          dns_zone_t **zonep,
+                          dns_db_t **dbp,
+                          dns_dbversion_t **versionp,
+                          dns_dbnode_t **nodep,
+                          dns_name_t *fname,
+                          dns_message_t *msg,
+                          isc_stdtime_t now);
+/*
+ * Get cached additional information from the DB node for a particular
+ * 'rdataset.'  'type' is one of dns_rdatasetadditional_fromauth,
+ * dns_rdatasetadditional_fromcache, and dns_rdatasetadditional_fromglue,
+ * which specifies the origin of the information.  'qtype' is intended to
+ * be used for specifying a particular rdata type in the cached information.
+ *
+ * Requires:
+ *     'rdataset' is a valid rdataset.
+ *     'acache' can be NULL, in which case this function will simply return
+ *     ISC_R_FAILURE.
+ *     For the other pointers, see dns_acache_getentry().
+ *
+ * Ensures:
+ *     See dns_acache_getentry().
+ *
+ * Returns:
+ *     ISC_R_SUCCESS
+ *     ISC_R_FAILURE   - additional information caching is not supported.
+ *     ISC_R_NOTFOUND  - the corresponding DB node has not cached additional
+ *                       information for 'rdataset.'
+ *
+ *     Any error that dns_acache_getentry() can return.
+ */
+
+isc_result_t
+dns_rdataset_setadditional(dns_rdataset_t *rdataset,
+                          dns_rdatasetadditional_t type,
+                          dns_rdatatype_t qtype,
+                          dns_acache_t *acache,
+                          dns_zone_t *zone,
+                          dns_db_t *db,
+                          dns_dbversion_t *version,
+                          dns_dbnode_t *node,
+                          dns_name_t *fname);
+/*
+ * Set cached additional information to the DB node for a particular
+ * 'rdataset.'  See dns_rdataset_getadditional for the semantics of 'type'
+ * and 'qtype'.
+ *
+ * Requires:
+ *     'rdataset' is a valid rdataset.
+ *     'acache' can be NULL, in which case this function will simply return
+ *     ISC_R_FAILURE.
+ *     For the other pointers, see dns_acache_setentry().
+ *
+ * Ensures:
+ *     See dns_acache_setentry().
+ *
+ * Returns:
+ *     ISC_R_SUCCESS
+ *     ISC_R_FAILURE   - additional information caching is not supported.
+ *     ISC_R_NOMEMORY
+ *
+ *     Any error that dns_acache_setentry() can return.
+ */
+
+isc_result_t
+dns_rdataset_putadditional(dns_acache_t *acache,
+                          dns_rdataset_t *rdataset,
+                          dns_rdatasetadditional_t type,
+                          dns_rdatatype_t qtype);
+/*
+ * Discard cached additional information stored in the DB node for a particular
+ * 'rdataset.'  See dns_rdataset_getadditional for the semantics of 'type'
+ * and 'qtype'.
+ *
+ * Requires:
+ *     'rdataset' is a valid rdataset.
+ *     'acache' can be NULL, in which case this function will simply return
+ *     ISC_R_FAILURE.
+ *
+ * Ensures:
+ *     See dns_acache_cancelentry().
+ *
+ * Returns:
+ *     ISC_R_SUCCESS
+ *     ISC_R_FAILURE   - additional information caching is not supported.
+ *     ISC_R_NOTFOUND  - the corresponding DB node has not cached additional
+ *                       information for 'rdataset.'
+ */
+
 ISC_LANG_ENDDECLS
 
 #endif /* DNS_RDATASET_H */
index 42e83142529b0402e7ac196392bb3ac04b519023..625380b41722bb932abfba6673049d3b76360ac6 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: types.h,v 1.110 2004/03/30 02:13:44 marka Exp $ */
+/* $Id: types.h,v 1.111 2004/12/21 10:45:19 jinmei Exp $ */
 
 #ifndef DNS_TYPES_H
 #define DNS_TYPES_H 1
@@ -30,6 +30,8 @@
 
 #include <isc/types.h>
 
+typedef struct dns_acache                      dns_acache_t;
+typedef struct dns_acacheentry                 dns_acacheentry_t;
 typedef struct dns_acl                                 dns_acl_t;
 typedef struct dns_aclelement                  dns_aclelement_t;
 typedef struct dns_aclenv                      dns_aclenv_t;
index e46085b6174d07023d82cb2b6fa4f5a4e0738f8d..4570cfc70b434b52d88ad9a57f1de4cea194decc 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: view.h,v 1.91 2004/03/10 02:19:56 marka Exp $ */
+/* $Id: view.h,v 1.92 2004/12/21 10:45:19 jinmei Exp $ */
 
 #ifndef DNS_VIEW_H
 #define DNS_VIEW_H 1
@@ -86,6 +86,7 @@ struct dns_view {
        dns_resolver_t *                resolver;
        dns_adb_t *                     adb;
        dns_requestmgr_t *              requestmgr;
+       dns_acache_t *                  acache;
        dns_cache_t *                   cache;
        dns_db_t *                      cachedb;
        dns_db_t *                      hints;
index 2316c259b171f36c51570703ae69f0ece32977d3..28337a68f8131db708484c99aac1818f115c2eeb 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: zone.h,v 1.127 2004/10/26 02:01:19 marka Exp $ */
+/* $Id: zone.h,v 1.128 2004/12/21 10:45:19 jinmei Exp $ */
 
 #ifndef DNS_ZONE_H
 #define DNS_ZONE_H 1
@@ -1432,6 +1432,19 @@ dns_zone_checknames(dns_zone_t *zone, dns_name_t *name, dns_rdata_t *rdata);
  *     DNS_R_BADNAME           failed rdata checks.
  */
 
+void
+dns_zone_setacache(dns_zone_t *zone, dns_acache_t *acache);
+/*
+ *     Associate the zone with an additional cache.
+ *
+ * Require:
+ *     'zone' to be a valid zone.
+ *     'acache' to be a non NULL pointer.
+ *
+ * Ensures:
+ *     'zone' will have a reference to 'acache'
+ */
+
 ISC_LANG_ENDDECLS
 
 #endif /* DNS_ZONE_H */
index a37d05aef68c5542d754cabfdf983646b3bc0275..0ce419f18cfac107e3a14a54593d50d5e571325c 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: log.c,v 1.36 2004/03/05 05:09:20 marka Exp $ */
+/* $Id: log.c,v 1.37 2004/12/21 10:45:16 jinmei Exp $ */
 
 /* Principal Authors: DCL */
 
@@ -74,6 +74,7 @@ LIBDNS_EXTERNAL_DATA isc_logmodule_t dns_modules[] = {
        { "dns/sdb",            0 },
        { "dns/diff",           0 },
        { "dns/hints",          0 },
+       { "dns/acache",         0 },
        { NULL,                 0 }
 };
 
index 5e1c87fe2eca1a0d7abdb17a00dd9f96085d2c97..5bec301b95b034f66c680984051e6ce892cc7d8f 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: ncache.c,v 1.36 2004/03/05 05:09:21 marka Exp $ */
+/* $Id: ncache.c,v 1.37 2004/12/21 10:45:17 jinmei Exp $ */
 
 #include <config.h>
 
@@ -473,6 +473,9 @@ static dns_rdatasetmethods_t rdataset_methods = {
        rdataset_clone,
        rdataset_count,
        NULL,
+       NULL,
+       NULL,
+       NULL,
        NULL
 };
 
index 93aaa593e9919d13d64b6d175acabbf23aa36579..30959ddaa5560bd8f7f247627876f9c757c02c2d 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: rbtdb.c,v 1.200 2004/05/23 06:59:20 marka Exp $ */
+/* $Id: rbtdb.c,v 1.201 2004/12/21 10:45:17 jinmei Exp $ */
 
 /*
  * Principal Author: Bob Halley
@@ -34,6 +34,7 @@
 #include <isc/task.h>
 #include <isc/util.h>
 
+#include <dns/acache.h>
 #include <dns/db.h>
 #include <dns/dbiterator.h>
 #include <dns/events.h>
@@ -46,6 +47,8 @@
 #include <dns/rdatasetiter.h>
 #include <dns/rdataslab.h>
 #include <dns/result.h>
+#include <dns/view.h>
+#include <dns/zone.h>
 #include <dns/zonekey.h>
 
 #ifdef DNS_RBTDB_VERSION64
@@ -103,6 +106,8 @@ struct noqname {
        void *     nsecsig;
 };
 
+typedef struct acachectl acachectl_t;  
+
 typedef struct rdatasetheader {
        /*
         * Locked by the owning node's lock.
@@ -140,6 +145,9 @@ typedef struct rdatasetheader {
         * should not be so crucial, no lock is set for the counter for
         * performance reasons.
         */
+
+       acachectl_t                     *additional_auth;
+       acachectl_t                     *additional_glue;
 } rdatasetheader_t;
 
 #define RDATASET_ATTR_NONEXISTENT      0x0001
@@ -148,6 +156,19 @@ typedef struct rdatasetheader {
 #define RDATASET_ATTR_RETAIN           0x0008
 #define RDATASET_ATTR_NXDOMAIN         0x0010
 
+typedef struct acache_cbarg {
+       dns_rdatasetadditional_t        type;
+       unsigned int                    count;
+       dns_db_t                        *db;
+       dns_dbnode_t                    *node;
+       rdatasetheader_t                *header;
+} acache_cbarg_t;
+
+struct acachectl {
+       dns_acacheentry_t               *entry;
+       acache_cbarg_t                  *cbarg;
+};
+
 /*
  * XXX
  * When the cache will pre-expire data (due to memory low or other
@@ -219,6 +240,8 @@ typedef struct {
        rbtdb_versionlist_t             open_versions;
        isc_boolean_t                   overmem;
        isc_task_t *                    task;
+       dns_dbnode_t                    *soanode;
+       dns_dbnode_t                    *nsnode;
        /* Locked by tree_lock. */
        dns_rbt_t *                     tree;
        isc_boolean_t                   secure;
@@ -264,6 +287,30 @@ static isc_result_t rdataset_getnoqname(dns_rdataset_t *rdataset,
                                        dns_name_t *name,
                                        dns_rdataset_t *nsec,
                                        dns_rdataset_t *nsecsig);
+static isc_result_t rdataset_getadditional(dns_rdataset_t *rdataset,
+                                          dns_rdatasetadditional_t type,
+                                          dns_rdatatype_t qtype,
+                                          dns_acache_t *acache,
+                                          dns_zone_t **zonep,
+                                          dns_db_t **dbp,
+                                          dns_dbversion_t **versionp,
+                                          dns_dbnode_t **nodep,
+                                          dns_name_t *fname,
+                                          dns_message_t *msg,
+                                          isc_stdtime_t now);
+static isc_result_t rdataset_setadditional(dns_rdataset_t *rdataset,
+                                          dns_rdatasetadditional_t type,
+                                          dns_rdatatype_t qtype,
+                                          dns_acache_t *acache,
+                                          dns_zone_t *zone,
+                                          dns_db_t *db,
+                                          dns_dbversion_t *version,
+                                          dns_dbnode_t *node,
+                                          dns_name_t *fname);
+static isc_result_t rdataset_putadditional(dns_acache_t *acache,
+                                          dns_rdataset_t *rdataset,
+                                          dns_rdatasetadditional_t type,
+                                          dns_rdatatype_t qtype);
 
 static dns_rdatasetmethods_t rdataset_methods = {
        rdataset_disassociate,
@@ -273,7 +320,10 @@ static dns_rdatasetmethods_t rdataset_methods = {
        rdataset_clone,
        rdataset_count,
        NULL,
-       rdataset_getnoqname
+       rdataset_getnoqname,
+       rdataset_getadditional,
+       rdataset_setadditional,
+       rdataset_putadditional
 };
 
 static void rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp);
@@ -468,6 +518,11 @@ maybe_free_rbtdb(dns_rbtdb_t *rbtdb) {
 
        /* XXX check for open versions here */
 
+       if (rbtdb->soanode != NULL)
+               dns_db_detachnode((dns_db_t *)rbtdb, &rbtdb->soanode);
+       if (rbtdb->nsnode != NULL)
+               dns_db_detachnode((dns_db_t *)rbtdb, &rbtdb->nsnode);
+
        /*
         * Even though there are no external direct references, there still
         * may be nodes in use.
@@ -631,6 +686,35 @@ add_changed(dns_rbtdb_t *rbtdb, rbtdb_version_t *version,
        return (changed);
 }
 
+static void
+free_acachearray(isc_mem_t *mctx, rdatasetheader_t *header,
+                acachectl_t *array)
+{
+       unsigned int count;
+       unsigned int i;
+       unsigned char *raw;
+
+       /*
+        * The caller must be holding the corresponding node lock.
+        */
+
+       if (array == NULL)
+               return;
+
+       raw = (unsigned char *)header + sizeof(*header);
+       count = raw[0] * 256 + raw[1];
+
+       /*
+        * Sanity check: since an additional cache entry has a reference to
+        * the original DB node (in the callback arg), there should be no
+        * acache entries when the node can be freed. 
+        */
+       for (i = 0; i < count; i++)
+               INSIST(array[i].entry == NULL && array[i].cbarg == NULL);
+
+       isc_mem_put(mctx, array, count * sizeof(acachectl_t));
+}
+
 static inline void
 free_noqname(isc_mem_t *mctx, struct noqname **noqname) {
 
@@ -652,7 +736,10 @@ free_rdataset(isc_mem_t *mctx, rdatasetheader_t *rdataset) {
 
        if (rdataset->noqname != NULL)
                free_noqname(mctx, &rdataset->noqname);
-       
+
+       free_acachearray(mctx, rdataset, rdataset->additional_auth);
+       free_acachearray(mctx, rdataset, rdataset->additional_glue);
+
        if ((rdataset->attributes & RDATASET_ATTR_NONEXISTENT) != 0)
                size = sizeof(*rdataset);
        else
@@ -4265,6 +4352,8 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
        newheader->noqname = NULL;
        newheader->count = 0;
        newheader->trust = rdataset->trust;
+       newheader->additional_auth = NULL;
+       newheader->additional_glue = NULL;
        if (rbtversion != NULL) {
                newheader->serial = rbtversion->serial;
                now = 0;
@@ -4338,6 +4427,8 @@ subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
        newheader->trust = 0;
        newheader->noqname = NULL;
        newheader->count = 0;
+       newheader->additional_auth = NULL;
+       newheader->additional_glue = NULL;
 
        LOCK(&rbtdb->node_locks[rbtnode->locknum].lock);
 
@@ -4390,6 +4481,13 @@ subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
                         * header, not newheader.
                         */
                        newheader->serial = rbtversion->serial;
+                       /*
+                        * XXXJT: dns_rdataslab_subtract() copied the pointers
+                        * to additional info.  We need to clear these fields
+                        * to avoid having duplicated references.
+                        */
+                       newheader->additional_auth = NULL;
+                       newheader->additional_glue = NULL;
                } else if (result == DNS_R_NXRRSET) {
                        /*
                         * This subtraction would remove all of the rdata;
@@ -4409,6 +4507,8 @@ subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
                        newheader->serial = rbtversion->serial;
                        newheader->noqname = NULL;
                        newheader->count = 0;
+                       newheader->additional_auth = NULL;
+                       newheader->additional_glue = NULL;
                } else {
                        free_rdataset(rbtdb->common.mctx, newheader);
                        goto unlock;
@@ -4474,6 +4574,8 @@ deleterdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
        newheader->attributes = RDATASET_ATTR_NONEXISTENT;
        newheader->trust = 0;
        newheader->noqname = NULL;
+       newheader->additional_auth = NULL;
+       newheader->additional_glue = NULL;
        if (rbtversion != NULL)
                newheader->serial = rbtversion->serial;
        else
@@ -4556,6 +4658,8 @@ loading_addrdataset(void *arg, dns_name_t *name, dns_rdataset_t *rdataset) {
        newheader->serial = 1;
        newheader->noqname = NULL;
        newheader->count = 0;
+       newheader->additional_auth = NULL;
+       newheader->additional_glue = NULL;
 
        result = add(rbtdb, node, rbtdb->current_version, newheader,
                     DNS_DBADD_MERGE, ISC_TRUE, NULL, 0);
@@ -4755,6 +4859,74 @@ ispersistent(dns_db_t *db) {
        return (ISC_FALSE);
 }
 
+static isc_result_t
+getsoanode(dns_db_t *db, dns_dbnode_t **nodep) {
+       dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+       isc_result_t result = ISC_R_SUCCESS;
+
+       REQUIRE(VALID_RBTDB(rbtdb));
+       REQUIRE(nodep != NULL && *nodep == NULL);
+
+       LOCK(&rbtdb->lock);
+       if (rbtdb->soanode != NULL) {
+               attachnode(db, rbtdb->soanode, nodep);
+       } else
+               result = ISC_R_NOTFOUND;
+       UNLOCK(&rbtdb->lock);
+
+       return (result);
+}
+
+static isc_result_t
+setsoanode(dns_db_t *db, dns_dbnode_t *node) {
+       dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+
+       REQUIRE(VALID_RBTDB(rbtdb));
+       REQUIRE(node != NULL);
+
+       LOCK(&rbtdb->lock);
+       if (rbtdb->soanode != NULL)
+               detachnode(db, &rbtdb->soanode);
+       attachnode(db, node, &rbtdb->soanode);
+       UNLOCK(&rbtdb->lock);
+
+       return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+getnsnode(dns_db_t *db, dns_dbnode_t **nodep) {
+       dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+       isc_result_t result = ISC_R_SUCCESS;
+
+       REQUIRE(VALID_RBTDB(rbtdb));
+       REQUIRE(nodep != NULL && *nodep == NULL);
+
+       LOCK(&rbtdb->lock);
+       if (rbtdb->nsnode != NULL) {
+               attachnode(db, rbtdb->nsnode, nodep);
+       } else
+               result = ISC_R_NOTFOUND;
+       UNLOCK(&rbtdb->lock);
+
+       return (result);
+}
+
+static isc_result_t
+setnsnode(dns_db_t *db, dns_dbnode_t *node) {
+       dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+
+       REQUIRE(VALID_RBTDB(rbtdb));
+       REQUIRE(node != NULL);
+
+       LOCK(&rbtdb->lock);
+       if (rbtdb->nsnode != NULL)
+               detachnode(db, &rbtdb->nsnode);
+       attachnode(db, node, &rbtdb->nsnode);
+       UNLOCK(&rbtdb->lock);
+
+       return (ISC_R_SUCCESS);
+}
+
 static dns_dbmethods_t zone_methods = {
        attach,
        detach,
@@ -4782,7 +4954,11 @@ static dns_dbmethods_t zone_methods = {
        nodecount,
        ispersistent,
        overmem,
-       settask
+       settask,
+       getsoanode,
+       setsoanode,
+       getnsnode,
+       setnsnode
 };
 
 static dns_dbmethods_t cache_methods = {
@@ -4812,7 +4988,11 @@ static dns_dbmethods_t cache_methods = {
        nodecount,
        ispersistent,
        overmem,
-       settask
+       settask,
+       getsoanode,
+       setsoanode,
+       getnsnode,
+       setnsnode
 };
 
 isc_result_t
@@ -5704,3 +5884,346 @@ dbiterator_origin(dns_dbiterator_t *iterator, dns_name_t *name) {
 
        return (dns_name_copy(origin, name, NULL));
 }
+
+/*
+ * Additional cache routines.
+ */
+static isc_result_t
+rdataset_getadditional(dns_rdataset_t *rdataset, dns_rdatasetadditional_t type,
+                      dns_rdatatype_t qtype, dns_acache_t *acache,
+                      dns_zone_t **zonep, dns_db_t **dbp,
+                      dns_dbversion_t **versionp, dns_dbnode_t **nodep,
+                      dns_name_t *fname, dns_message_t *msg,
+                      isc_stdtime_t now)
+{
+       dns_rbtdb_t *rbtdb = rdataset->private1;
+       dns_rbtnode_t *rbtnode = rdataset->private2;
+       unsigned char *raw = rdataset->private3;
+       unsigned int current_count = rdataset->privateuint4;
+       unsigned int count;
+       rdatasetheader_t *header;
+       isc_mutex_t *nodelock;
+       unsigned int total_count;
+       acachectl_t *acarray;
+       dns_acacheentry_t *entry;
+       isc_result_t result;
+
+       UNUSED(qtype); /* we do not use this value at least for now */
+       UNUSED(acache);
+
+       header = (struct rdatasetheader *)(raw - sizeof(*header));
+
+       total_count = rdataset_count(rdataset);
+       INSIST(total_count > current_count);
+       count = total_count - current_count - 1;
+
+       acarray = NULL;
+
+       nodelock = &rbtdb->node_locks[rbtnode->locknum].lock;
+       LOCK(nodelock);
+
+       switch (type) {
+       case dns_rdatasetadditional_fromauth:
+               acarray = header->additional_auth;
+               break;
+       case dns_rdatasetadditional_fromcache:
+               acarray = NULL;
+               break;
+       case dns_rdatasetadditional_fromglue:
+               acarray = header->additional_glue;
+               break;
+       default:
+               INSIST(0);
+       }
+
+       if (acarray == NULL) {
+               UNLOCK(nodelock);
+               return (ISC_R_NOTFOUND);
+       }
+
+       if (acarray[count].entry == NULL) {
+               UNLOCK(nodelock);
+               return (ISC_R_NOTFOUND);
+       }
+
+       entry = NULL;
+       dns_acache_attachentry(acarray[count].entry, &entry);
+
+       UNLOCK(nodelock);
+
+       result = dns_acache_getentry(entry, zonep, dbp, versionp,
+                                    nodep, fname, msg, now);
+
+       dns_acache_detachentry(&entry);
+
+       return (result);
+}
+
+static void
+acache_callback(dns_acacheentry_t *entry, void **arg) {
+       dns_rbtdb_t *rbtdb;
+       dns_rbtnode_t *rbtnode;
+       isc_mutex_t *nodelock;
+       acachectl_t *acarray = NULL;
+       acache_cbarg_t *cbarg;
+       unsigned int count;
+
+       REQUIRE(arg != NULL);
+       cbarg = *arg;
+
+       /*
+        * The caller must hold the entry lock.
+        */
+
+       rbtdb = (dns_rbtdb_t *)cbarg->db;
+       rbtnode = (dns_rbtnode_t *)cbarg->node;
+
+       nodelock = &rbtdb->node_locks[rbtnode->locknum].lock;
+       LOCK(nodelock);
+
+       switch (cbarg->type) {
+       case dns_rdatasetadditional_fromauth:
+               acarray = cbarg->header->additional_auth;
+               break;
+       case dns_rdatasetadditional_fromglue:
+               acarray = cbarg->header->additional_glue;
+               break;
+       default:
+               INSIST(0);
+       }
+
+       count = cbarg->count;
+       if (acarray[count].entry == entry)
+               acarray[count].entry = NULL;
+       INSIST(acarray[count].cbarg != NULL);
+       isc_mem_put(rbtdb->common.mctx, acarray[count].cbarg,
+                   sizeof(acache_cbarg_t));
+       acarray[count].cbarg = NULL;
+
+       dns_acache_detachentry(&entry);
+
+       UNLOCK(nodelock);
+
+       dns_db_detachnode((dns_db_t *)rbtdb, (dns_dbnode_t **)&rbtnode);
+       dns_db_detach((dns_db_t **)&rbtdb);
+
+       *arg = NULL;
+}
+
+static void
+acache_cancelentry(isc_mem_t *mctx, dns_acacheentry_t *entry,
+                     acache_cbarg_t **cbargp)
+{
+       acache_cbarg_t *cbarg;
+
+       REQUIRE(mctx != NULL);
+       REQUIRE(entry != NULL);
+       REQUIRE(cbargp != NULL && *cbargp != NULL);
+
+       cbarg = *cbargp;
+
+       dns_acache_cancelentry(entry);
+       dns_db_detachnode(cbarg->db, &cbarg->node);
+       dns_db_detach(&cbarg->db);
+
+       isc_mem_put(mctx, cbarg, sizeof(acache_cbarg_t));
+
+       *cbargp = NULL;
+}
+
+static isc_result_t
+rdataset_setadditional(dns_rdataset_t *rdataset, dns_rdatasetadditional_t type,
+                      dns_rdatatype_t qtype, dns_acache_t *acache,
+                      dns_zone_t *zone, dns_db_t *db,
+                      dns_dbversion_t *version, dns_dbnode_t *node,
+                      dns_name_t *fname)
+{
+       dns_rbtdb_t *rbtdb = rdataset->private1;
+       dns_rbtnode_t *rbtnode = rdataset->private2;
+       unsigned char *raw = rdataset->private3;
+       unsigned int current_count = rdataset->privateuint4;
+       rdatasetheader_t *header;
+       unsigned int total_count, count;
+       isc_mutex_t *nodelock;
+       isc_mem_t *mctx = rbtdb->common.mctx;
+       isc_result_t result;
+       acachectl_t *acarray;
+       dns_acacheentry_t *newentry, *oldentry = NULL;
+       acache_cbarg_t *newcbarg, *oldcbarg = NULL;
+
+       UNUSED(qtype);
+
+       if (type == dns_rdatasetadditional_fromcache)
+               return (ISC_R_SUCCESS);
+
+       header = (struct rdatasetheader *)(raw - sizeof(*header));
+
+       total_count = rdataset_count(rdataset);
+       INSIST(total_count > current_count);
+       count = total_count - current_count - 1; /* should be private data */
+
+       newcbarg = isc_mem_get(mctx, sizeof(*newcbarg));
+       if (newcbarg == NULL)
+               return (ISC_R_NOMEMORY);
+       newcbarg->type = type;
+       newcbarg->count = count;
+       newcbarg->header = header;
+       newcbarg->db = NULL;
+       dns_db_attach((dns_db_t *)rbtdb, &newcbarg->db);
+       newcbarg->node = NULL;
+       dns_db_attachnode((dns_db_t *)rbtdb, (dns_dbnode_t *)rbtnode,
+                         &newcbarg->node);
+       newentry = NULL;
+       result = dns_acache_createentry(acache, (dns_db_t *)rbtdb,
+                                       acache_callback, newcbarg, &newentry);
+       if (result != ISC_R_SUCCESS)
+               goto fail;
+       /* Set cache data in the new entry. */
+       result = dns_acache_setentry(acache, newentry, zone, db,
+                                    version, node, fname);
+       if (result != ISC_R_SUCCESS)
+               goto fail;
+
+       nodelock = &rbtdb->node_locks[rbtnode->locknum].lock;
+       LOCK(nodelock);
+
+       acarray = NULL;
+       switch (type) {
+       case dns_rdatasetadditional_fromauth:
+               acarray = header->additional_auth;
+               break;
+       case dns_rdatasetadditional_fromglue:
+               acarray = header->additional_glue;
+               break;
+       default:
+               INSIST(0);
+       }
+
+       if (acarray == NULL) {
+               unsigned int i;
+
+               acarray = isc_mem_get(mctx, total_count *
+                                     sizeof(acachectl_t));
+
+               if (acarray == NULL) {
+                       UNLOCK(nodelock);
+                       goto fail;
+               }
+
+               for (i = 0; i < total_count; i++) {
+                       acarray[i].entry = NULL;
+                       acarray[i].cbarg = NULL;
+               }
+       }
+       switch (type) {
+       case dns_rdatasetadditional_fromauth:
+               header->additional_auth = acarray;
+               break;
+       case dns_rdatasetadditional_fromglue:
+               header->additional_glue = acarray;
+               break;
+       default:
+               INSIST(0);
+       }
+
+       if (acarray[count].entry != NULL) {
+               /*
+                * Swap the entry.  Delay cleaning-up the old entry since
+                * it would require a node lock.
+                */
+               oldentry = acarray[count].entry;
+               INSIST(acarray[count].cbarg != NULL);
+               oldcbarg = acarray[count].cbarg;
+       }
+       acarray[count].entry = newentry;
+       acarray[count].cbarg = newcbarg;
+
+       UNLOCK(nodelock);
+
+       if (oldentry != NULL) {
+               if (oldcbarg != NULL)
+                       acache_cancelentry(mctx, oldentry, &oldcbarg); 
+               dns_acache_detachentry(&oldentry);
+       }
+
+       return (ISC_R_SUCCESS);
+
+  fail:
+       if (newentry != NULL) {
+               if (newcbarg != NULL)
+                       acache_cancelentry(mctx, newentry, &newcbarg);
+               dns_acache_detachentry(&newentry);
+       }
+
+       return (result);
+}
+
+static isc_result_t
+rdataset_putadditional(dns_acache_t *acache, dns_rdataset_t *rdataset,
+                      dns_rdatasetadditional_t type, dns_rdatatype_t qtype)
+{ 
+       dns_rbtdb_t *rbtdb = rdataset->private1;
+       dns_rbtnode_t *rbtnode = rdataset->private2;
+       unsigned char *raw = rdataset->private3;
+       unsigned int current_count = rdataset->privateuint4;
+       rdatasetheader_t *header;
+       isc_mutex_t *nodelock;
+       unsigned int total_count, count;
+       acachectl_t *acarray;
+       dns_acacheentry_t *entry;
+       acache_cbarg_t *cbarg;
+
+       UNUSED(qtype);          /* we do not use this value at least for now */
+       UNUSED(acache);
+
+       if (type == dns_rdatasetadditional_fromcache)
+               return (ISC_R_SUCCESS);
+
+       header = (struct rdatasetheader *)(raw - sizeof(*header));
+
+       total_count = rdataset_count(rdataset);
+       INSIST(total_count > current_count);
+       count = total_count - current_count - 1;
+
+       acarray = NULL;
+       entry = NULL;
+
+       nodelock = &rbtdb->node_locks[rbtnode->locknum].lock;
+       LOCK(nodelock);
+
+       switch (type) {
+       case dns_rdatasetadditional_fromauth:
+               acarray = header->additional_auth;
+               break;
+       case dns_rdatasetadditional_fromglue:
+               acarray = header->additional_glue;
+               break;
+       default:
+               INSIST(0);
+       }
+
+       if (acarray == NULL) {
+               UNLOCK(nodelock);
+               return (ISC_R_NOTFOUND);
+       }
+
+       entry = acarray[count].entry;
+       if (entry == NULL) {
+               UNLOCK(nodelock);
+               return (ISC_R_NOTFOUND);
+       }
+
+       acarray[count].entry = NULL;
+       cbarg = acarray[count].cbarg;
+       acarray[count].cbarg = NULL;
+
+       UNLOCK(nodelock);
+
+       if (entry != NULL) {
+               if(cbarg != NULL)
+                       acache_cancelentry(rbtdb->common.mctx, entry, &cbarg);
+               dns_acache_detachentry(&entry);
+       }
+
+       return (ISC_R_SUCCESS);
+}
index 2ceda74fcae316f734d533a481a6e59d4c158864..a181f5ccbf36cd133b75d010814c5db818582471 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: rdatalist.c,v 1.28 2004/03/05 05:09:23 marka Exp $ */
+/* $Id: rdatalist.c,v 1.29 2004/12/21 10:45:17 jinmei Exp $ */
 
 #include <config.h>
 
@@ -38,7 +38,10 @@ static dns_rdatasetmethods_t methods = {
        isc__rdatalist_clone,
        isc__rdatalist_count,
        isc__rdatalist_addnoqname,
-       isc__rdatalist_getnoqname
+       isc__rdatalist_getnoqname,
+       NULL,
+       NULL,
+       NULL
 };
 
 void
index 8f3f1f0dcf28a42af0f56af308dc00122853f196..f65b4bd825e6a9b7e001252e62430f7a862c4e65 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: rdataset.c,v 1.72 2004/03/05 05:09:23 marka Exp $ */
+/* $Id: rdataset.c,v 1.73 2004/12/21 10:45:17 jinmei Exp $ */
 
 #include <config.h>
 
@@ -174,6 +174,9 @@ static dns_rdatasetmethods_t question_methods = {
        question_clone,
        question_count,
        NULL,
+       NULL,
+       NULL,
+       NULL,
        NULL
 };
 
@@ -624,3 +627,81 @@ dns_rdataset_getnoqname(dns_rdataset_t *rdataset, dns_name_t *name,
                return (ISC_R_NOTIMPLEMENTED);
        return((rdataset->methods->getnoqname)(rdataset, name, nsec, nsecsig));
 }
+
+/*
+ * Additional cache stuff
+ */
+isc_result_t
+dns_rdataset_getadditional(dns_rdataset_t *rdataset,
+                          dns_rdatasetadditional_t type,
+                          dns_rdatatype_t qtype,
+                          dns_acache_t *acache,
+                          dns_zone_t **zonep,
+                          dns_db_t **dbp,
+                          dns_dbversion_t **versionp,
+                          dns_dbnode_t **nodep,
+                          dns_name_t *fname,
+                          dns_message_t *msg,
+                          isc_stdtime_t now)
+{
+       REQUIRE(DNS_RDATASET_VALID(rdataset));
+       REQUIRE(rdataset->methods != NULL);
+       REQUIRE(zonep == NULL || *zonep == NULL);
+       REQUIRE(dbp != NULL && *dbp == NULL);
+       REQUIRE(versionp != NULL && *versionp == NULL);
+       REQUIRE(nodep != NULL && *nodep == NULL);
+       REQUIRE(fname != NULL);
+       REQUIRE(msg != NULL);
+
+       if (acache != NULL && rdataset->methods->getadditional != NULL) {
+               return ((rdataset->methods->getadditional)(rdataset, type,
+                                                          qtype, acache,
+                                                          zonep, dbp,
+                                                          versionp, nodep,
+                                                          fname, msg, now));
+       }
+
+       return (ISC_R_FAILURE);
+}
+
+isc_result_t
+dns_rdataset_setadditional(dns_rdataset_t *rdataset,
+                          dns_rdatasetadditional_t type,
+                          dns_rdatatype_t qtype,
+                          dns_acache_t *acache,
+                          dns_zone_t *zone,
+                          dns_db_t *db,
+                          dns_dbversion_t *version,
+                          dns_dbnode_t *node,
+                          dns_name_t *fname)
+{
+       REQUIRE(DNS_RDATASET_VALID(rdataset));
+       REQUIRE(rdataset->methods != NULL);
+
+       if (acache != NULL && rdataset->methods->setadditional != NULL) {
+               return ((rdataset->methods->setadditional)(rdataset, type,
+                                                          qtype, acache, zone,
+                                                          db, version,
+                                                          node, fname));
+       }
+
+       return (ISC_R_FAILURE);
+}
+
+isc_result_t
+dns_rdataset_putadditional(dns_acache_t *acache,
+                          dns_rdataset_t *rdataset,
+                          dns_rdatasetadditional_t type,
+                          dns_rdatatype_t qtype)
+{
+       REQUIRE(DNS_RDATASET_VALID(rdataset));
+       REQUIRE(rdataset->methods != NULL);
+
+       if (acache != NULL && rdataset->methods->putadditional != NULL) {
+               return ((rdataset->methods->putadditional)(acache, rdataset,
+                                                          type, qtype));
+       }
+
+       return (ISC_R_FAILURE);
+}
+
index 2e1b5f823e9b134ff47bb82b04d6d802ef9ece15..228cf1fe72b3c73a0d085d03d3a21b8d20ae14f6 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: rdataslab.c,v 1.35 2004/03/05 05:09:23 marka Exp $ */
+/* $Id: rdataslab.c,v 1.36 2004/12/21 10:45:17 jinmei Exp $ */
 
 #include <config.h>
 
@@ -241,6 +241,9 @@ static dns_rdatasetmethods_t rdataset_methods = {
        rdataset_clone,
        rdataset_count,
        NULL,
+       NULL,
+       NULL,
+       NULL,
        NULL
 };
 
index 486874788f4297c464583f3c28f36eff7377b84d..ebf113ae19256cd4ac014deefa134f00a0ef216a 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: sdb.c,v 1.46 2004/07/22 03:58:07 marka Exp $ */
+/* $Id: sdb.c,v 1.47 2004/12/21 10:45:18 jinmei Exp $ */
 
 #include <config.h>
 
@@ -1234,7 +1234,11 @@ static dns_dbmethods_t sdb_methods = {
        nodecount,
        ispersistent,
        overmem,
-       settask
+       settask,
+       NULL,
+       NULL,
+       NULL,
+       NULL
 };
 
 static isc_result_t
@@ -1361,7 +1365,10 @@ static dns_rdatasetmethods_t methods = {
        rdataset_clone,
        isc__rdatalist_count,
        isc__rdatalist_addnoqname,
-       isc__rdatalist_getnoqname
+       isc__rdatalist_getnoqname,
+       NULL,
+       NULL,
+       NULL
 };
 
 static void
index b3848c1cc36f91efc4aeba8fb8c6f379eb01e7ab..ba90d117c8a3e2108eceb47ca67c28209cc313da 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: view.c,v 1.126 2004/03/10 02:19:56 marka Exp $ */
+/* $Id: view.c,v 1.127 2004/12/21 10:45:18 jinmei Exp $ */
 
 #include <config.h>
 
@@ -24,6 +24,7 @@
 #include <isc/string.h>                /* Required for HP/UX (and others?) */
 #include <isc/util.h>
 
+#include <dns/acache.h>
 #include <dns/acl.h>
 #include <dns/adb.h>
 #include <dns/cache.h>
@@ -120,6 +121,7 @@ dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass,
                goto cleanup_trustedkeys;
        }
 
+       view->acache = NULL;
        view->cache = NULL;
        view->cachedb = NULL;
        view->hints = NULL;
@@ -253,6 +255,8 @@ destroy(dns_view_t *view) {
                dns_adb_detach(&view->adb);
        if (view->resolver != NULL)
                dns_resolver_detach(&view->resolver);
+       if (view->acache != NULL)
+               dns_acache_detach(&view->acache);
        if (view->requestmgr != NULL)
                dns_requestmgr_detach(&view->requestmgr);
        if (view->task != NULL)
@@ -365,6 +369,8 @@ view_flushanddetach(dns_view_t **viewp, isc_boolean_t flush) {
                        dns_adb_shutdown(view->adb);
                if (!REQSHUTDOWN(view))
                        dns_requestmgr_shutdown(view->requestmgr);
+               if (view->acache != NULL)
+                       dns_acache_shutdown(view->acache);
                if (view->flush)
                        dns_zt_flushanddetach(&view->zonetable);
                else
index dac17f6fb1d11846bba8412a163da8a1e17ba3f3..063c1a64cd96ec13ad9257fe5c3e4a83c47d6c0b 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: zone.c,v 1.425 2004/11/23 05:23:46 marka Exp $ */
+/* $Id: zone.c,v 1.426 2004/12/21 10:45:18 jinmei Exp $ */
 
 #include <config.h>
 
@@ -31,6 +31,7 @@
 #include <isc/timer.h>
 #include <isc/util.h>
 
+#include <dns/acache.h>
 #include <dns/acl.h>
 #include <dns/adb.h>
 #include <dns/callbacks.h>
@@ -206,6 +207,7 @@ struct dns_zone {
        dns_ssutable_t          *ssutable;
        isc_uint32_t            sigvalidityinterval;
        dns_view_t              *view;
+       dns_acache_t            *acache;
        /*
         * Zones in certain states such as "waiting for zone transfer"
         * or "zone transfer in progress" are kept on per-state linked lists
@@ -384,6 +386,8 @@ static void zone_iattach(dns_zone_t *source, dns_zone_t **target);
 static void zone_idetach(dns_zone_t **zonep);
 static isc_result_t zone_replacedb(dns_zone_t *zone, dns_db_t *db,
                                   isc_boolean_t dump);
+static inline void zone_attachdb(dns_zone_t *zone, dns_db_t *db);
+static inline void zone_detachdb(dns_zone_t *zone);
 static isc_result_t default_journal(dns_zone_t *zone);
 static void zone_xfrdone(dns_zone_t *zone, isc_result_t result);
 static isc_result_t zone_postload(dns_zone_t *zone, dns_db_t *db,
@@ -572,6 +576,7 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) {
        zone->ssutable = NULL;
        zone->sigvalidityinterval = 30 * 24 * 3600;
        zone->view = NULL;
+       zone->acache = NULL;
        ISC_LINK_INIT(zone, statelink);
        zone->statelist = NULL;
        zone->counters = NULL;
@@ -636,7 +641,9 @@ zone_free(dns_zone_t *zone) {
        if (zone->counters != NULL)
                dns_stats_freecounters(zone->mctx, &zone->counters);
        if (zone->db != NULL)
-               dns_db_detach(&zone->db);
+               zone_detachdb(zone);
+       if (zone->acache != NULL)
+               dns_acache_detach(&zone->acache);
        zone_freedbargs(zone);
        RUNTIME_CHECK(dns_zone_setmasterswithkeys(zone, NULL, NULL, 0)
                      == ISC_R_SUCCESS);
@@ -821,6 +828,33 @@ dns_zone_setorigin(dns_zone_t *zone, dns_name_t *origin) {
        return (result);
 }
 
+void
+dns_zone_setacache(dns_zone_t *zone, dns_acache_t *acache) {
+       REQUIRE(DNS_ZONE_VALID(zone));
+       REQUIRE(acache != NULL);
+       
+       LOCK_ZONE(zone);
+       if (zone->acache != NULL)
+               dns_acache_detach(&zone->acache);
+       dns_acache_attach(acache, &zone->acache);
+       if (zone->db != NULL) {
+               isc_result_t result;
+
+               /*
+                * If the zone reuses an existing DB, the DB needs to be
+                * set in the acache explicitly.  We can safely ignore the
+                * case where the DB is already set.  If other error happens,
+                * the acache will not work effectively.
+                */
+               result = dns_acache_setdb(acache, zone->db);
+               if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS) {
+                       UNEXPECTED_ERROR(__FILE__, __LINE__,
+                                        "dns_acache_setdb() failed: %s",
+                                        isc_result_totext(result));
+               }
+       }
+       UNLOCK_ZONE(zone);
+}
 
 static isc_result_t
 dns_zone_setstring(dns_zone_t *zone, char **field, const char *value) {
@@ -1423,7 +1457,7 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime,
                if (result != ISC_R_SUCCESS)
                        goto cleanup;
        } else {
-               dns_db_attach(db, &zone->db);
+               zone_attachdb(zone, db);
                DNS_ZONE_SETFLAG(zone,
                                 DNS_ZONEFLG_LOADED|DNS_ZONEFLG_NEEDNOTIFY);
        }
@@ -2641,7 +2675,7 @@ zone_unload(dns_zone_t *zone) {
 
        REQUIRE(LOCKED_ZONE(zone));
 
-       dns_db_detach(&zone->db);
+       zone_detachdb(zone);
        DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_LOADED);
        DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NEEDDUMP);
 }
@@ -3400,7 +3434,7 @@ stub_callback(isc_task_t *task, isc_event_t *event) {
        dns_db_closeversion(stub->db, &stub->version, ISC_TRUE);
        LOCK_ZONE(zone);
        if (zone->db == NULL)
-               dns_db_attach(stub->db, &zone->db);
+               zone_attachdb(zone, stub->db);
        UNLOCK_ZONE(zone);
        dns_db_detach(&stub->db);
 
@@ -5427,8 +5461,8 @@ zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump) {
                      "replacing zone database");
 
        if (zone->db != NULL)
-               dns_db_detach(&zone->db);
-       dns_db_attach(db, &zone->db);
+               zone_detachdb(zone);
+       zone_attachdb(zone, db);
        dns_db_settask(zone->db, zone->task);
        DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED|DNS_ZONEFLG_NEEDNOTIFY);
        return (ISC_R_SUCCESS);
@@ -5438,6 +5472,31 @@ zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump) {
        return (result);
 }
 
+static inline void
+zone_attachdb(dns_zone_t *zone, dns_db_t *db) {
+       REQUIRE(zone->db == NULL && db != NULL);
+
+       dns_db_attach(db, &zone->db);
+       if (zone->acache != NULL) {
+               isc_result_t result;
+               result = dns_acache_setdb(zone->acache, db);
+               if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS) {
+                       UNEXPECTED_ERROR(__FILE__, __LINE__,
+                                        "dns_acache_setdb() failed: %s",
+                                        isc_result_totext(result));
+               }
+       }
+}
+
+static inline void
+zone_detachdb(dns_zone_t *zone) {
+       REQUIRE(zone->db != NULL);
+
+       if (zone->acache != NULL)
+               (void)dns_acache_putdb(zone->acache, zone->db);
+       dns_db_detach(&zone->db);
+}
+
 static void
 zone_xfrdone(dns_zone_t *zone, isc_result_t result) {
        isc_time_t now;
index a93179a2d9961d8d117de2bcc50a0f0d08172bb1..90176929dda5f091b78c46fb79431faf53917b66 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: namedconf.c,v 1.41 2004/11/11 01:08:24 marka Exp $ */
+/* $Id: namedconf.c,v 1.42 2004/12/21 10:45:20 jinmei Exp $ */
 
 #include <config.h>
 
@@ -733,6 +733,9 @@ view_clauses[] = {
        { "dnssec-must-be-secure",  &cfg_type_mustbesecure,
           CFG_CLAUSEFLAG_MULTI },
        { "ixfr-from-differences", &cfg_type_ixfrdifftype, 0 },
+       { "use-additional-cache", &cfg_type_boolean, 0 },
+       { "acache-cleaning-interval", &cfg_type_uint32, 0 },
+       { "max-acache-size", &cfg_type_sizenodefault, 0 },
        { NULL, NULL, 0 }
 };