diff --git a/CHANGES b/CHANGES index 6abc197d92..0f9b43de08 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ +3336. [func] Maintain statistics for RRsets tagged as "stale". + [RT #29514] + 3335. [func] nslookup: return a nonzero exit code when unable to get an answer. [RT #29492] diff --git a/bin/named/statschannel.c b/bin/named/statschannel.c index 42c793ee9e..fe5d3c6751 100644 --- a/bin/named/statschannel.c +++ b/bin/named/statschannel.c @@ -656,6 +656,7 @@ rdatasetstats_dump(dns_rdatastatstype_t type, isc_uint64_t val, void *arg) { char typebuf[64]; const char *typestr; isc_boolean_t nxrrset = ISC_FALSE; + isc_boolean_t stale = ISC_FALSE; #ifdef HAVE_LIBXML2 xmlTextWriterPtr writer; int xmlrc; @@ -677,11 +678,15 @@ rdatasetstats_dump(dns_rdatastatstype_t type, isc_uint64_t val, void *arg) { != 0) nxrrset = ISC_TRUE; + if ((DNS_RDATASTATSTYPE_ATTR(type) & DNS_RDATASTATSTYPE_ATTR_STALE) + != 0) + stale = ISC_TRUE; + switch (dumparg->type) { case isc_statsformat_file: fp = dumparg->arg; - fprintf(fp, "%20" ISC_PRINT_QUADFORMAT "u %s%s\n", val, - nxrrset ? "!" : "", typestr); + fprintf(fp, "%20" ISC_PRINT_QUADFORMAT "u %s%s%s\n", val, + stale ? "#" : "", nxrrset ? "!" : "", typestr); break; case isc_statsformat_xml: #ifdef HAVE_LIBXML2 @@ -689,7 +694,8 @@ rdatasetstats_dump(dns_rdatastatstype_t type, isc_uint64_t val, void *arg) { TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "rrset")); TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "name")); - TRY0(xmlTextWriterWriteFormatString(writer, "%s%s", + TRY0(xmlTextWriterWriteFormatString(writer, "%s%s%s", + stale ? "#" : "", nxrrset ? "!" : "", typestr)); TRY0(xmlTextWriterEndElement(writer)); /* name */ diff --git a/doc/arm/Bv9ARM-book.xml b/doc/arm/Bv9ARM-book.xml index 48119cb5d2..9233cd9f97 100644 --- a/doc/arm/Bv9ARM-book.xml +++ b/doc/arm/Bv9ARM-book.xml @@ -13731,7 +13731,8 @@ HOST-127.EXAMPLE. MX 0 . If the exclamation mark (!) is printed for a RR type, it means that particular type of RRset is known to be nonexistent (this is also known as - "NXRRSET"). + "NXRRSET"). If a hash mark (#) is present then + the RRset is marked for garbage collection. Maintained per view. diff --git a/lib/dns/include/dns/stats.h b/lib/dns/include/dns/stats.h index ac3e06eba8..d1c9c700e0 100644 --- a/lib/dns/include/dns/stats.h +++ b/lib/dns/include/dns/stats.h @@ -162,10 +162,18 @@ LIBDNS_EXTERNAL_DATA extern const char *dns_statscounter_names[]; * _NXDOMAIN * RRset type counters only. Indicates a non existent name. When this * attribute is set, the base type is of no use. + * + * _STALE + * RRset type counters only. This indicates a record that marked for + * removal. + * + * Note: incrementing _STALE will decrement the corresponding non-stale + * counter. */ #define DNS_RDATASTATSTYPE_ATTR_OTHERTYPE 0x0001 #define DNS_RDATASTATSTYPE_ATTR_NXRRSET 0x0002 #define DNS_RDATASTATSTYPE_ATTR_NXDOMAIN 0x0004 +#define DNS_RDATASTATSTYPE_ATTR_STALE 0x0008 /*%< * Conversion macros among dns_rdatatype_t, attributes and isc_statscounter_t. @@ -299,6 +307,9 @@ dns_rdatasetstats_increment(dns_stats_t *stats, dns_rdatastatstype_t rrsettype); /*%< * Increment the statistics counter for 'rrsettype'. * + * Note: if 'rrsettype' has the _STALE attribute set the corresponding + * non-stale counter will be decremented. + * * Requires: *\li 'stats' is a valid dns_stats_t created by dns_rdatasetstats_create(). */ diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c index 0eaff14e2d..11f2134fd2 100644 --- a/lib/dns/rbtdb.c +++ b/lib/dns/rbtdb.c @@ -1368,6 +1368,28 @@ rollback_node(dns_rbtnode_t *node, rbtdb_serial_t serial) { node->dirty = 1; } +static inline void +mark_stale_header(dns_rbtdb_t *rbtdb, rdatasetheader_t *header) { + + /* + * If we are already stale there is nothing to do. + */ + if ((header->attributes & RDATASET_ATTR_STALE) != 0) + return; + + header->attributes |= RDATASET_ATTR_STALE; + header->node->dirty = 1; + + /* + * If we have not been counted then there is nothing to do. + */ + if ((header->attributes & RDATASET_ATTR_STATCOUNT) == 0) + return; + + if (EXISTS(header)) + update_rrsetstats(rbtdb, header, ISC_TRUE); +} + static inline void clean_stale_headers(dns_rbtdb_t *rbtdb, isc_mem_t *mctx, rdatasetheader_t *top) { @@ -4245,9 +4267,8 @@ cache_zonecut_callback(dns_rbtnode_t *node, dns_name_t *name, void *arg) { free_rdataset(search->rbtdb, mctx, header); } else { - header->attributes |= - RDATASET_ATTR_STALE; - node->dirty = 1; + mark_stale_header(search->rbtdb, + header); header_prev = header; } } else @@ -4359,9 +4380,8 @@ find_deepest_zonecut(rbtdb_search_t *search, dns_rbtnode_t *node, free_rdataset(rbtdb, m, header); } else { - header->attributes |= - RDATASET_ATTR_STALE; - node->dirty = 1; + mark_stale_header(rbtdb, + header); header_prev = header; } } else @@ -4534,9 +4554,8 @@ find_coveringnsec(rbtdb_search_t *search, dns_dbnode_t **nodep, free_rdataset(search->rbtdb, m, header); } else { - header->attributes |= - RDATASET_ATTR_STALE; - node->dirty = 1; + mark_stale_header(search->rbtdb, + header); header_prev = header; } } else @@ -4928,9 +4947,7 @@ cache_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, free_rdataset(search.rbtdb, mctx, header); } else { - header->attributes |= - RDATASET_ATTR_STALE; - node->dirty = 1; + mark_stale_header(search.rbtdb, header); header_prev = header; } } else @@ -5236,9 +5253,7 @@ cache_findzonecut(dns_db_t *db, dns_name_t *name, unsigned int options, free_rdataset(search.rbtdb, mctx, header); } else { - header->attributes |= - RDATASET_ATTR_STALE; - node->dirty = 1; + mark_stale_header(search.rbtdb, header); header_prev = header; } } else @@ -5446,8 +5461,7 @@ expirenode(dns_db_t *db, dns_dbnode_t *node, isc_stdtime_t now) { * refcurrent(rbtnode) must be non-zero. This is so * because 'node' is an argument to the function. */ - header->attributes |= RDATASET_ATTR_STALE; - rbtnode->dirty = 1; + mark_stale_header(rbtdb, header); if (log) isc_log_write(dns_lctx, category, module, level, "overmem cache: stale %s", @@ -5455,8 +5469,7 @@ expirenode(dns_db_t *db, dns_dbnode_t *node, isc_stdtime_t now) { } else if (force_expire) { if (! RETAIN(header)) { set_ttl(rbtdb, header, 0); - header->attributes |= RDATASET_ATTR_STALE; - rbtnode->dirty = 1; + mark_stale_header(rbtdb, header); } else if (log) { isc_log_write(dns_lctx, category, module, level, "overmem cache: " @@ -5712,8 +5725,7 @@ cache_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, * non-zero. This is so because 'node' is an * argument to the function. */ - header->attributes |= RDATASET_ATTR_STALE; - rbtnode->dirty = 1; + mark_stale_header(rbtdb, header); } } else if (EXISTS(header)) { if (header->type == matchtype) @@ -5977,9 +5989,7 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion, */ if (covers == dns_rdatatype_any) { set_ttl(rbtdb, topheader, 0); - topheader->attributes |= - RDATASET_ATTR_STALE; - rbtnode->dirty = 1; + mark_stale_header(rbtdb, topheader); } else if (topheader->type == sigtype) sigheader = topheader; } @@ -6024,8 +6034,7 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion, * NXDOMAIN/NODATA(QTYPE=ANY). */ set_ttl(rbtdb, topheader, 0); - topheader->attributes |= RDATASET_ATTR_STALE; - rbtnode->dirty = 1; + mark_stale_header(rbtdb, topheader); topheader = NULL; goto find_header; } @@ -6237,11 +6246,10 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion, changed->dirty = ISC_TRUE; if (rbtversion == NULL) { set_ttl(rbtdb, header, 0); - header->attributes |= RDATASET_ATTR_STALE; + mark_stale_header(rbtdb, header); if (sigheader != NULL) { set_ttl(rbtdb, sigheader, 0); - sigheader->attributes |= - RDATASET_ATTR_STALE; + mark_stale_header(rbtdb, sigheader); } } idx = newheader->node->locknum; @@ -9390,8 +9398,7 @@ expire_header(dns_rbtdb_t *rbtdb, rdatasetheader_t *header, isc_boolean_t tree_locked, expire_t reason) { set_ttl(rbtdb, header, 0); - header->attributes |= RDATASET_ATTR_STALE; - header->node->dirty = 1; + mark_stale_header(rbtdb, header); /* * Caller must hold the node (write) lock. diff --git a/lib/dns/stats.c b/lib/dns/stats.c index a59dde6332..2641e9bdf5 100644 --- a/lib/dns/stats.c +++ b/lib/dns/stats.c @@ -50,6 +50,9 @@ typedef enum { * XXXJT: this introduces tight coupling with the rdata implementation. * Ideally, we should have rdata handle this type of details. */ +/* + * types, !types, nxdomain, stale types, stale !types, stale nxdomain + */ enum { /* For 0-255, we use the rdtype value as counter indices */ rdtypecounter_dlv = 256, /* for dns_rdatatype_dlv */ @@ -58,7 +61,9 @@ enum { /* The following are used for rdataset */ rdtypenxcounter_max = rdtypecounter_max * 2, rdtypecounter_nxdomain = rdtypenxcounter_max, - rdatasettypecounter_max = rdtypecounter_nxdomain + 1 + /* stale counters offset */ + rdtypecounter_stale = rdtypecounter_nxdomain + 1, + rdatasettypecounter_max = rdtypecounter_stale * 2 }; struct dns_stats { @@ -119,7 +124,7 @@ dns_stats_detach(dns_stats_t **statsp) { * Create methods */ static isc_result_t -create_stats(isc_mem_t *mctx, dns_statstype_t type, int ncounters, +create_stats(isc_mem_t *mctx, dns_statstype_t type, int ncounters, dns_stats_t **statsp) { dns_stats_t *stats; @@ -176,7 +181,7 @@ dns_rdatasetstats_create(isc_mem_t *mctx, dns_stats_t **statsp) { REQUIRE(statsp != NULL && *statsp == NULL); return (create_stats(mctx, dns_statstype_rdataset, - (rdtypecounter_max * 2) + 1, statsp)); + rdatasettypecounter_max, statsp)); } isc_result_t @@ -236,10 +241,19 @@ update_rdatasetstats(dns_stats_t *stats, dns_rdatastatstype_t rrsettype, counter += rdtypecounter_max; } - if (increment) + if (increment) { + if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) & + DNS_RDATASTATSTYPE_ATTR_STALE) != 0) { + isc_stats_decrement(stats->counters, counter); + counter += rdtypecounter_stale; + } isc_stats_increment(stats->counters, counter); - else + } else { + if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) & + DNS_RDATASTATSTYPE_ATTR_STALE) != 0) + counter += rdtypecounter_stale; isc_stats_decrement(stats->counters, counter); + } } void @@ -259,6 +273,7 @@ dns_rdatasetstats_decrement(dns_stats_t *stats, dns_rdatastatstype_t rrsettype) update_rdatasetstats(stats, rrsettype, ISC_FALSE); } + void dns_opcodestats_increment(dns_stats_t *stats, dns_opcode_t code) { REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_opcode); @@ -321,17 +336,35 @@ dns_rdatatypestats_dump(dns_stats_t *stats, dns_rdatatypestats_dumper_t dump_fn, static void rdataset_dumpcb(isc_statscounter_t counter, isc_uint64_t value, void *arg) { rdatadumparg_t *rdatadumparg = arg; + unsigned int attributes; if (counter < rdtypecounter_max) { dump_rdentry(counter, value, 0, rdatadumparg->fn, rdatadumparg->arg); - } else if (counter < rdtypenxcounter_max) { - dump_rdentry(counter - rdtypecounter_max, value, - DNS_RDATASTATSTYPE_ATTR_NXRRSET, - rdatadumparg->fn, rdatadumparg->arg); - } else { + } else if (counter < rdtypecounter_nxdomain) { + counter -= rdtypecounter_max; + attributes = DNS_RDATASTATSTYPE_ATTR_NXRRSET; + dump_rdentry(counter, value, attributes, rdatadumparg->fn, + rdatadumparg->arg); + } else if (counter == rdtypecounter_nxdomain) { dump_rdentry(0, value, DNS_RDATASTATSTYPE_ATTR_NXDOMAIN, rdatadumparg->fn, rdatadumparg->arg); + } else if (counter < rdtypecounter_stale + rdtypecounter_max) { + counter -= rdtypecounter_stale; + attributes = DNS_RDATASTATSTYPE_ATTR_STALE; + dump_rdentry(counter, value, attributes, rdatadumparg->fn, + rdatadumparg->arg); + } else if (counter < rdtypecounter_stale + rdtypecounter_nxdomain) { + counter -= rdtypecounter_stale + rdtypecounter_max; + attributes = DNS_RDATASTATSTYPE_ATTR_NXRRSET | + DNS_RDATASTATSTYPE_ATTR_STALE; + dump_rdentry(counter, value, attributes, rdatadumparg->fn, + rdatadumparg->arg); + } else { + attributes = DNS_RDATASTATSTYPE_ATTR_NXDOMAIN | + DNS_RDATASTATSTYPE_ATTR_STALE; + dump_rdentry(0, value, attributes, rdatadumparg->fn, + rdatadumparg->arg); } } diff --git a/lib/dns/tests/Makefile.in b/lib/dns/tests/Makefile.in index eb75411174..821f791743 100644 --- a/lib/dns/tests/Makefile.in +++ b/lib/dns/tests/Makefile.in @@ -39,13 +39,13 @@ LIBS = @LIBS@ @ATFLIBS@ OBJS = dnstest.@O@ SRCS = dnstest.c master_test.c dbiterator_test.c time_test.c \ private_test.c update_test.c zonemgr_test.c zt_test.c \ - dbdiff_test.c nsec3_test.c dispatch_test.c + dbdiff_test.c nsec3_test.c dispatch_test.c rdatasetstats_test.c SUBDIRS = TARGETS = master_test@EXEEXT@ dbiterator_test@EXEEXT@ time_test@EXEEXT@ \ private_test@EXEEXT@ update_test@EXEEXT@ zonemgr_test@EXEEXT@ \ zt_test@EXEEXT@ dbversion_test@EXEEXT@ dbdiff_test@EXEEXT@ \ - nsec3_test@EXEEXT@ dispatch_test@EXEEXT@ + nsec3_test@EXEEXT@ dispatch_test@EXEEXT@ rdatasetstats_test@EXEEXT@ @BIND9_MAKE_RULES@ @@ -110,6 +110,11 @@ dispatch_test@EXEEXT@: dispatch_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS} dispatch_test.@O@ dnstest.@O@ ${DNSLIBS} \ ${ISCLIBS} ${LIBS} +rdatasetstats_test@EXEEXT@: rdatasetstats_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ + rdatasetstats_test.@O@ dnstest.@O@ ${DNSLIBS} \ + ${ISCLIBS} ${LIBS} + unit:: sh ${top_srcdir}/unit/unittest.sh diff --git a/lib/dns/tests/rdatasetstats_test.c b/lib/dns/tests/rdatasetstats_test.c new file mode 100644 index 0000000000..2de7ec3650 --- /dev/null +++ b/lib/dns/tests/rdatasetstats_test.c @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2011, 2012 Internet Systems Consortium, Inc. ("ISC") + * + * Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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$ */ + +/*! \file */ + +#include + +#include + +#include + +#include + +#include "dnstest.h" + +/* + * Helper functions + */ +static void +set_typestats(dns_stats_t *stats, dns_rdatatype_t type, + isc_boolean_t stale) +{ + dns_rdatastatstype_t which; + unsigned int attributes; + + attributes = 0; + if (stale) attributes |= DNS_RDATASTATSTYPE_ATTR_STALE; + which = DNS_RDATASTATSTYPE_VALUE(type, attributes); + dns_rdatasetstats_increment(stats, which); + + attributes = DNS_RDATASTATSTYPE_ATTR_NXRRSET; + if (stale) attributes |= DNS_RDATASTATSTYPE_ATTR_STALE; + which = DNS_RDATASTATSTYPE_VALUE(type, attributes); + dns_rdatasetstats_increment(stats, which); +} + +static void +set_nxdomainstats(dns_stats_t *stats, isc_boolean_t stale) { + dns_rdatastatstype_t which; + unsigned int attributes; + + attributes = DNS_RDATASTATSTYPE_ATTR_NXDOMAIN; + if (stale) attributes |= DNS_RDATASTATSTYPE_ATTR_STALE; + which = DNS_RDATASTATSTYPE_VALUE(0, attributes); + dns_rdatasetstats_increment(stats, which); +} + +static void +checkit1(dns_rdatastatstype_t which, isc_uint64_t value, void *arg) { + unsigned int attributes; +#if debug + unsigned int type; +#endif + + UNUSED(which); + UNUSED(arg); + + attributes = DNS_RDATASTATSTYPE_ATTR(which); +#if debug + type = DNS_RDATASTATSTYPE_BASE(which); + + fprintf(stderr, "%s%s%s%s/%u, %u\n", + attributes & DNS_RDATASTATSTYPE_ATTR_OTHERTYPE ? "O" : " ", + attributes & DNS_RDATASTATSTYPE_ATTR_NXRRSET ? "!" : " ", + attributes & DNS_RDATASTATSTYPE_ATTR_STALE ? "#" : " ", + attributes & DNS_RDATASTATSTYPE_ATTR_NXDOMAIN ? "X" : " ", + type, (unsigned)value); +#endif + if ((attributes & DNS_RDATASTATSTYPE_ATTR_STALE) == 0) + ATF_REQUIRE_EQ(value, 1); + else + ATF_REQUIRE_EQ(value, 0); +} + +static void +checkit2(dns_rdatastatstype_t which, isc_uint64_t value, void *arg) { + unsigned int attributes; +#if debug + unsigned int type; +#endif + + UNUSED(which); + UNUSED(arg); + + attributes = DNS_RDATASTATSTYPE_ATTR(which); +#if debug + type = DNS_RDATASTATSTYPE_BASE(which); + + fprintf(stderr, "%s%s%s%s/%u, %u\n", + attributes & DNS_RDATASTATSTYPE_ATTR_OTHERTYPE ? "O" : " ", + attributes & DNS_RDATASTATSTYPE_ATTR_NXRRSET ? "!" : " ", + attributes & DNS_RDATASTATSTYPE_ATTR_STALE ? "#" : " ", + attributes & DNS_RDATASTATSTYPE_ATTR_NXDOMAIN ? "X" : " ", + type, (unsigned)value); +#endif + if ((attributes & DNS_RDATASTATSTYPE_ATTR_STALE) == 0) + ATF_REQUIRE_EQ(value, 0); + else + ATF_REQUIRE_EQ(value, 1); +} +/* + * Individual unit tests + */ + +ATF_TC(rdatasetstats); +ATF_TC_HEAD(rdatasetstats, tc) { + atf_tc_set_md_var(tc, "descr", "test that rdatasetstats counters are properly set"); +} +ATF_TC_BODY(rdatasetstats, tc) { + unsigned int i; + dns_stats_t *stats = NULL; + isc_result_t result; + + UNUSED(tc); + + result = dns_test_begin(NULL, ISC_TRUE); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = dns_rdatasetstats_create(mctx, &stats); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + /* First 256 types. */ + for (i = 0; i <= 255; i++) + set_typestats(stats, (dns_rdatatype_t)i, ISC_FALSE); + /* Specials */ + set_typestats(stats, dns_rdatatype_dlv, ISC_FALSE); + set_typestats(stats, (dns_rdatatype_t)1000, ISC_FALSE); + set_nxdomainstats(stats, ISC_FALSE); + + /* + * Check that all counters are set to appropriately. + */ + dns_rdatasetstats_dump(stats, checkit1, NULL, 1); + + /* First 256 types. */ + for (i = 0; i <= 255; i++) + set_typestats(stats, (dns_rdatatype_t)i, ISC_TRUE); + /* Specials */ + set_typestats(stats, dns_rdatatype_dlv, ISC_TRUE); + set_typestats(stats, (dns_rdatatype_t)1000, ISC_TRUE); + set_nxdomainstats(stats, ISC_TRUE); + + /* + * Check that all counters are set to appropriately. + */ + dns_rdatasetstats_dump(stats, checkit2, NULL, 1); + + dns_stats_detach(&stats); + dns_test_end(); +} + +/* + * Main + */ +ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, rdatasetstats); + return (atf_no_error()); +} +