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.
* 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>
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\
"
* 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
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')
* 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);
}
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
*/
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);
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
*
* 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
}
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;
name = NULL;
rdataset = NULL;
node = NULL;
- dns_fixedname_init(&foundname);
- fname = dns_fixedname_name(&foundname);
/*
* Get resources and make 'name' be the database origin.
/*
* 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
}
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;
/*
* 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!
if (is_zone)
authoritative = ISC_TRUE;
-
+
if (event == NULL && client->query.restarts == 0) {
if (is_zone) {
dns_zone_attach(zone, &client->query.authzone);
/*
* 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;
* 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;
/*
* 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 {
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);
* 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>
#include <bind9/check.h>
+#include <dns/acache.h>
#include <dns/adb.h>
#include <dns/cache.h>
#include <dns/db.h>
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 */
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.
*/
* 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
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));
}
<!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>
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 — 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
<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>
</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">
# 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@
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@ \
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 \
--- /dev/null
+/*
+ * 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);
+}
* 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
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);
+}
--- /dev/null
+/*
+ * 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 */
* 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
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
* 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 */
* 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)
* 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 */
#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
* 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);
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')
* written.
*/
-
isc_result_t
dns_rdataset_additionaldata(dns_rdataset_t *rdataset,
dns_additionaldatafunc_t add, void *arg);
* '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 */
* 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
#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;
* 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
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;
* 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
* 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 */
* 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 */
{ "dns/sdb", 0 },
{ "dns/diff", 0 },
{ "dns/hints", 0 },
+ { "dns/acache", 0 },
{ NULL, 0 }
};
* 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>
rdataset_clone,
rdataset_count,
NULL,
+ NULL,
+ NULL,
+ NULL,
NULL
};
* 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
#include <isc/task.h>
#include <isc/util.h>
+#include <dns/acache.h>
#include <dns/db.h>
#include <dns/dbiterator.h>
#include <dns/events.h>
#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
void * nsecsig;
};
+typedef struct acachectl acachectl_t;
+
typedef struct rdatasetheader {
/*
* Locked by the owning node's lock.
* 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
#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
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;
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,
rdataset_clone,
rdataset_count,
NULL,
- rdataset_getnoqname
+ rdataset_getnoqname,
+ rdataset_getadditional,
+ rdataset_setadditional,
+ rdataset_putadditional
};
static void rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp);
/* 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.
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) {
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
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;
newheader->trust = 0;
newheader->noqname = NULL;
newheader->count = 0;
+ newheader->additional_auth = NULL;
+ newheader->additional_glue = NULL;
LOCK(&rbtdb->node_locks[rbtnode->locknum].lock);
* 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;
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;
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
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);
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,
nodecount,
ispersistent,
overmem,
- settask
+ settask,
+ getsoanode,
+ setsoanode,
+ getnsnode,
+ setnsnode
};
static dns_dbmethods_t cache_methods = {
nodecount,
ispersistent,
overmem,
- settask
+ settask,
+ getsoanode,
+ setsoanode,
+ getnsnode,
+ setnsnode
};
isc_result_t
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);
+}
* 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>
isc__rdatalist_clone,
isc__rdatalist_count,
isc__rdatalist_addnoqname,
- isc__rdatalist_getnoqname
+ isc__rdatalist_getnoqname,
+ NULL,
+ NULL,
+ NULL
};
void
* 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>
question_clone,
question_count,
NULL,
+ NULL,
+ NULL,
+ NULL,
NULL
};
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);
+}
+
* 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>
rdataset_clone,
rdataset_count,
NULL,
+ NULL,
+ NULL,
+ NULL,
NULL
};
* 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>
nodecount,
ispersistent,
overmem,
- settask
+ settask,
+ NULL,
+ NULL,
+ NULL,
+ NULL
};
static isc_result_t
rdataset_clone,
isc__rdatalist_count,
isc__rdatalist_addnoqname,
- isc__rdatalist_getnoqname
+ isc__rdatalist_getnoqname,
+ NULL,
+ NULL,
+ NULL
};
static void
* 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>
#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>
goto cleanup_trustedkeys;
}
+ view->acache = NULL;
view->cache = NULL;
view->cachedb = NULL;
view->hints = NULL;
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)
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
* 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>
#include <isc/timer.h>
#include <isc/util.h>
+#include <dns/acache.h>
#include <dns/acl.h>
#include <dns/adb.h>
#include <dns/callbacks.h>
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
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,
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;
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);
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) {
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);
}
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);
}
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);
"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);
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;
* 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>
{ "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 }
};