From: Alessio Podda Date: Fri, 5 Sep 2025 13:39:48 +0000 (+0200) Subject: Add low contention stats counter X-Git-Tag: v9.21.21~9^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ed0ecb62e4ed262e8a9575f2381f2a709297a1f2;p=thirdparty%2Fbind9.git Add low contention stats counter In the current statistics counter implementation, the statistics are backed by an array of counters, which are updated via atomic operations. This leads to contention, especially on high core count machines. This commit introduces a new isc_statsmulti_t counter that keeps a separate array per thread. These counters are then aggregated only when statistics are queried, shifting work off the critical path. These changes lead to a ~2% improvement in perflab. --- diff --git a/bin/delv/delv.c b/bin/delv/delv.c index 6ddef282d00..6e2370e0602 100644 --- a/bin/delv/delv.c +++ b/bin/delv/delv.c @@ -98,7 +98,7 @@ static dns_dispatch_t *dispatch4 = NULL; static dns_dispatch_t *dispatch6 = NULL; static dns_db_t *roothints = NULL; static isc_stats_t *resstats = NULL; -static dns_stats_t *resquerystats = NULL; +static isc_statsmulti_t *resquerystats = NULL; static FILE *logfp = NULL; /* Managers */ @@ -2177,7 +2177,7 @@ run_server(void *arg) { dns_rdatatypestats_create(isc_g_mctx, &resquerystats); dns_resolver_setquerystats(view->resolver, resquerystats); - dns_stats_detach(&resquerystats); + isc_statsmulti_detach(&resquerystats); dns_view_freeze(view); diff --git a/bin/named/server.c b/bin/named/server.c index 70efe66b08d..4b9138453d9 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -3635,7 +3635,7 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config, bool empty_zones_enable; const cfg_obj_t *disablelist = NULL; isc_stats_t *resstats = NULL; - dns_stats_t *resquerystats = NULL; + isc_statsmulti_t *resquerystats = NULL; isc_histomulti_t *resqueryinrttstats = NULL; isc_histomulti_t *resqueryoutrttstats = NULL; bool auto_root = false; @@ -5465,7 +5465,7 @@ cleanup: isc_stats_detach(&resstats); } if (resquerystats != NULL) { - dns_stats_detach(&resquerystats); + isc_statsmulti_detach(&resquerystats); } if (resqueryinrttstats != NULL) { isc_histomulti_detach(&resqueryinrttstats); @@ -10054,12 +10054,12 @@ named_server_resetstatscommand(named_server_t *server, isc_lex_t *lex, } if (recursive_high_water) { - isc_stats_set(ns_stats_get(server->sctx->nsstats), 0, - ns_statscounter_recurshighwater); + ns_stats_reset_highwater(server->sctx->nshighwaterstats, + ns_highwater_recursive); } if (tcp_high_water) { - isc_stats_set(ns_stats_get(server->sctx->nsstats), 0, - ns_statscounter_tcphighwater); + ns_stats_reset_highwater(server->sctx->nshighwaterstats, + ns_highwater_tcp); } return ISC_R_SUCCESS; @@ -11539,9 +11539,9 @@ named_server_status(named_server_t *server, isc_buffer_t *text) { CHECK(putstr(text, line)); snprintf(line, sizeof(line), "recursive high-water: %u\n", - (unsigned int)ns_stats_get_counter( - server->sctx->nsstats, - ns_statscounter_recurshighwater)); + (unsigned int)ns_stats_get_highwater( + server->sctx->nshighwaterstats, + ns_highwater_recursive)); CHECK(putstr(text, line)); snprintf(line, sizeof(line), "tcp clients: %u/%u\n", @@ -11550,8 +11550,8 @@ named_server_status(named_server_t *server, isc_buffer_t *text) { CHECK(putstr(text, line)); snprintf(line, sizeof(line), "TCP high-water: %u\n", - (unsigned int)ns_stats_get_counter( - server->sctx->nsstats, ns_statscounter_tcphighwater)); + (unsigned int)ns_stats_get_highwater( + server->sctx->nshighwaterstats, ns_highwater_tcp)); CHECK(putstr(text, line)); reload_status = atomic_load(&server->reload_status); diff --git a/bin/named/statschannel.c b/bin/named/statschannel.c index 56b473cf3dd..17b33d7cf81 100644 --- a/bin/named/statschannel.c +++ b/bin/named/statschannel.c @@ -140,6 +140,7 @@ user_zonetype(dns_zone_t *zone) { * below so that they'll be less susceptible to counter name changes. */ static const char *nsstats_desc[ns_statscounter_max]; +static const char *nshighwaterstats_desc[ns_highwater_max]; static const char *resstats_desc[dns_resstatscounter_max]; static const char *adbstats_desc[dns_adbstats_max]; static const char *zonestats_desc[dns_zonestatscounter_max]; @@ -153,6 +154,7 @@ static const char *dnstapstats_desc[dns_dnstapcounter_max]; static const char *gluecachestats_desc[dns_gluecachestatscounter_max]; #if defined(EXTENDED_STATS) static const char *nsstats_xmldesc[ns_statscounter_max]; +static const char *nshighwaterstats_xmldesc[ns_highwater_max]; static const char *resstats_xmldesc[dns_resstatscounter_max]; static const char *adbstats_xmldesc[dns_adbstats_max]; static const char *zonestats_xmldesc[dns_zonestatscounter_max]; @@ -167,18 +169,19 @@ static const char *gluecachestats_xmldesc[dns_gluecachestatscounter_max]; static const char *queryrttinstats_xmldesc[dns_queryrttcounter_in_max]; static const char *queryrttoutstats_xmldesc[dns_queryrttcounter_in_max]; #else /* if defined(EXTENDED_STATS) */ -#define nsstats_xmldesc NULL -#define resstats_xmldesc NULL -#define adbstats_xmldesc NULL -#define zonestats_xmldesc NULL -#define sockstats_xmldesc NULL -#define dnssecstats_xmldesc NULL -#define udpinsizestats_xmldesc NULL -#define udpoutsizestats_xmldesc NULL -#define tcpinsizestats_xmldesc NULL -#define tcpoutsizestats_xmldesc NULL -#define dnstapstats_xmldesc NULL -#define gluecachestats_xmldesc NULL +#define nsstats_xmldesc NULL +#define nshighwaterstats_xmldesc NULL +#define resstats_xmldesc NULL +#define adbstats_xmldesc NULL +#define zonestats_xmldesc NULL +#define sockstats_xmldesc NULL +#define dnssecstats_xmldesc NULL +#define udpinsizestats_xmldesc NULL +#define udpoutsizestats_xmldesc NULL +#define tcpinsizestats_xmldesc NULL +#define tcpoutsizestats_xmldesc NULL +#define dnstapstats_xmldesc NULL +#define gluecachestats_xmldesc NULL #endif /* EXTENDED_STATS */ #define TRY0(a) \ @@ -194,6 +197,7 @@ static const char *queryrttoutstats_xmldesc[dns_queryrttcounter_in_max]; * nsstats_desc[nsstats_index[0]] will be the description that is shown first. */ static int nsstats_index[ns_statscounter_max]; +static int nshighwaterstats_index[ns_highwater_max]; static int resstats_index[dns_resstatscounter_max]; static int adbstats_index[dns_adbstats_max]; static int zonestats_index[dns_zonestatscounter_max]; @@ -328,8 +332,6 @@ init_desc(void) { SET_NSSTATDESC(invalidsig, "requests with invalid signature", "ReqBadSIG"); SET_NSSTATDESC(requesttcp, "TCP requests received", "ReqTCP"); - SET_NSSTATDESC(tcphighwater, "TCP connection high-water", - "TCPConnHighWater"); SET_NSSTATDESC(authrej, "auth queries rejected", "AuthQryRej"); SET_NSSTATDESC(recurserej, "recursive queries rejected", "RecQryRej"); SET_NSSTATDESC(xfrrej, "transfer requests rejected", "XfrRej"); @@ -368,8 +370,6 @@ init_desc(void) { SET_NSSTATDESC(updatebadprereq, "updates rejected due to prerequisite failure", "UpdateBadPrereq"); - SET_NSSTATDESC(recurshighwater, "Recursive clients high-water", - "RecursHighwater"); SET_NSSTATDESC(recursclients, "recursing clients", "RecursClients"); SET_NSSTATDESC(dns64, "queries answered by DNS64", "DNS64"); SET_NSSTATDESC(ratedropped, "responses dropped for rate limits", @@ -440,6 +440,32 @@ init_desc(void) { INSIST(i == ns_statscounter_max); + /* Initialize highwater statistics */ + for (i = 0; i < ns_highwater_max; i++) { + nshighwaterstats_desc[i] = NULL; + } +#if defined(EXTENDED_STATS) + for (i = 0; i < ns_highwater_max; i++) { + nshighwaterstats_xmldesc[i] = NULL; + } +#endif /* if defined(EXTENDED_STATS) */ + +#define SET_NSHIGHWATERSTATDESC(counterid, desc, xmldesc) \ + do { \ + set_desc(ns_highwater_##counterid, ns_highwater_max, desc, \ + nshighwaterstats_desc, xmldesc, \ + nshighwaterstats_xmldesc); \ + nshighwaterstats_index[i++] = ns_highwater_##counterid; \ + } while (0) + + i = 0; + SET_NSHIGHWATERSTATDESC(tcp, "TCP connection high-water", + "TCPConnHighWater"); + SET_NSHIGHWATERSTATDESC(recursive, "Recursive clients high-water", + "RecursHighwater"); + + INSIST(i == ns_highwater_max); + /* Initialize resolver statistics */ for (i = 0; i < dns_resstatscounter_max; i++) { resstats_desc[i] = NULL; @@ -738,6 +764,9 @@ init_desc(void) { for (i = 0; i < ns_statscounter_max; i++) { INSIST(nsstats_desc[i] != NULL); } + for (i = 0; i < ns_highwater_max; i++) { + INSIST(nshighwaterstats_desc[i] != NULL); + } for (i = 0; i < dns_resstatscounter_max; i++) { INSIST(resstats_desc[i] != NULL); } @@ -871,6 +900,25 @@ dump_stats(isc_stats_t *stats, isc_statsformat_t type, void *arg, values, options); } +static isc_result_t +dump_statsmulti(isc_statsmulti_t *stats, isc_statsformat_t type, void *arg, + const char *category, const char **desc, int ncounters, + int *indices, uint64_t *values, int options) { + stats_dumparg_t dumparg; + unsigned int multioptions = 0; + + dumparg.type = type; + dumparg.ncounters = ncounters; + dumparg.counterindices = indices; + dumparg.countervalues = values; + + memset(values, 0, sizeof(values[0]) * ncounters); + isc_statsmulti_dump(stats, generalstat_dump, &dumparg, multioptions); + + return dump_counters(type, arg, category, desc, ncounters, indices, + values, options); +} + #if defined(EXTENDED_STATS) static isc_result_t dump_histo(isc_histomulti_t *hm, isc_statsformat_t type, void *arg, @@ -1416,7 +1464,7 @@ zone_xmlrender(dns_zone_t *zone, void *arg) { if (statlevel == dns_zonestat_full) { isc_stats_t *zonestats; isc_stats_t *gluecachestats; - dns_stats_t *rcvquerystats; + isc_statsmulti_t *rcvquerystats; dns_stats_t *dnssecsignstats; uint64_t nsstat_values[ns_statscounter_max]; uint64_t gluecachestats_values[dns_gluecachestatscounter_max]; @@ -1901,10 +1949,18 @@ generatexml(named_server_t *server, uint32_t flags, int *buflen, TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type", ISC_XMLCHAR "nsstat")); - CHECK(dump_stats(ns_stats_get(server->sctx->nsstats), + CHECK(dump_statsmulti(server->sctx->nsstats, + isc_statsformat_xml, writer, NULL, + nsstats_xmldesc, ns_statscounter_max, + nsstats_index, nsstat_values, + ISC_STATSMULTIDUMP_VERBOSE)); + + /* Add highwater stats to the same nsstat section */ + uint64_t nshighwaterstat_values[ns_highwater_max]; + CHECK(dump_stats(server->sctx->nshighwaterstats, isc_statsformat_xml, writer, NULL, - nsstats_xmldesc, ns_statscounter_max, - nsstats_index, nsstat_values, + nshighwaterstats_xmldesc, ns_highwater_max, + nshighwaterstats_index, nshighwaterstat_values, ISC_STATSDUMP_VERBOSE)); TRY0(xmlTextWriterEndElement(writer)); /* /nsstat */ @@ -2086,7 +2142,7 @@ generatexml(named_server_t *server, uint32_t flags, int *buflen, STATS_XML_XFRINS)) != 0)) { isc_stats_t *istats = NULL; - dns_stats_t *dstats = NULL; + isc_statsmulti_t *dstats = NULL; dns_adb_t *adb = NULL; isc_histomulti_t *hmpin = NULL, *hmpout = NULL; @@ -2127,7 +2183,7 @@ generatexml(named_server_t *server, uint32_t flags, int *buflen, &dumparg, 0); CHECK(dumparg.result); } - dns_stats_detach(&dstats); + isc_statsmulti_detach(&dstats); TRY0(xmlTextWriterEndElement(writer)); /* */ @@ -2492,7 +2548,7 @@ zone_jsonrender(dns_zone_t *zone, void *arg) { if (statlevel == dns_zonestat_full) { isc_stats_t *zonestats; isc_stats_t *gluecachestats; - dns_stats_t *rcvquerystats; + isc_statsmulti_t *rcvquerystats; dns_stats_t *dnssecsignstats; uint64_t nsstat_values[ns_statscounter_max]; uint64_t gluecachestats_values[dns_gluecachestatscounter_max]; @@ -3027,10 +3083,22 @@ generatejson(named_server_t *server, size_t *msglen, const char **msg, dumparg.result = ISC_R_SUCCESS; dumparg.arg = counters; - result = dump_stats(ns_stats_get(server->sctx->nsstats), + result = dump_statsmulti(server->sctx->nsstats, + isc_statsformat_json, counters, NULL, + nsstats_xmldesc, ns_statscounter_max, + nsstats_index, nsstat_values, 0); + if (result != ISC_R_SUCCESS) { + json_object_put(counters); + goto cleanup; + } + + /* Add highwater stats to the same nsstats section */ + uint64_t nshighwaterstat_values[ns_highwater_max]; + result = dump_stats(server->sctx->nshighwaterstats, isc_statsformat_json, counters, NULL, - nsstats_xmldesc, ns_statscounter_max, - nsstats_index, nsstat_values, 0); + nshighwaterstats_xmldesc, ns_highwater_max, + nshighwaterstats_index, + nshighwaterstat_values, 0); if (result != ISC_R_SUCCESS) { json_object_put(counters); goto cleanup; @@ -3160,7 +3228,7 @@ generatejson(named_server_t *server, size_t *msglen, const char **msg, if ((flags & STATS_JSON_SERVER) != 0) { json_object *res = NULL; - dns_stats_t *dstats = NULL; + isc_statsmulti_t *dstats = NULL; isc_stats_t *istats = NULL; res = json_object_new_object(); @@ -3209,18 +3277,19 @@ generatejson(named_server_t *server, size_t *msglen, const char **msg, json_object_object_add(res, "qtypes", counters); - dns_stats_detach(&dstats); + isc_statsmulti_detach(&dstats); } - dstats = dns_db_getrrsetstats(view->cachedb); - if (dstats != NULL) { + dns_stats_t *rrsetstats = + dns_db_getrrsetstats(view->cachedb); + if (rrsetstats != NULL) { counters = json_object_new_object(); CHECKMEM(counters); dumparg.arg = counters; dumparg.result = ISC_R_SUCCESS; dns_rdatasetstats_dump( - dstats, rdatasetstats_dump, + rrsetstats, rdatasetstats_dump, &dumparg, 0); if (dumparg.result != ISC_R_SUCCESS) { json_object_put(counters); @@ -4099,7 +4168,7 @@ named_stats_dump(named_server_t *server, FILE *fp) { fprintf(fp, "++ Outgoing Queries ++\n"); ISC_LIST_FOREACH(server->viewlist, view, link) { - dns_stats_t *dstats = NULL; + isc_statsmulti_t *dstats = NULL; dns_resolver_getquerystats(view->resolver, &dstats); if (dstats == NULL) { continue; @@ -4110,13 +4179,19 @@ named_stats_dump(named_server_t *server, FILE *fp) { fprintf(fp, "[View: %s]\n", view->name); } dns_rdatatypestats_dump(dstats, rdtypestat_dump, &dumparg, 0); - dns_stats_detach(&dstats); + isc_statsmulti_detach(&dstats); } fprintf(fp, "++ Name Server Statistics ++\n"); - (void)dump_stats(ns_stats_get(server->sctx->nsstats), - isc_statsformat_file, fp, NULL, nsstats_desc, - ns_statscounter_max, nsstats_index, nsstat_values, 0); + (void)dump_statsmulti(server->sctx->nsstats, isc_statsformat_file, fp, + NULL, nsstats_desc, ns_statscounter_max, + nsstats_index, nsstat_values, 0); + + /* Add highwater stats to the same Name Server Statistics section */ + uint64_t nshighwaterstat_values[ns_highwater_max]; + (void)dump_stats(server->sctx->nshighwaterstats, isc_statsformat_file, + fp, NULL, nshighwaterstats_desc, ns_highwater_max, + nshighwaterstats_index, nshighwaterstat_values, 0); fprintf(fp, "++ Zone Maintenance Statistics ++\n"); (void)dump_stats(server->zonestats, isc_statsformat_file, fp, NULL, diff --git a/bin/named/zoneconf.c b/bin/named/zoneconf.c index 0e422d33ed2..515e1849136 100644 --- a/bin/named/zoneconf.c +++ b/bin/named/zoneconf.c @@ -989,7 +989,7 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, dns_masterformat_t masterformat; const dns_master_style_t *masterstyle = &dns_master_style_default; isc_stats_t *zoneqrystats; - dns_stats_t *rcvquerystats; + isc_statsmulti_t *rcvquerystats; dns_stats_t *dnssecsignstats; dns_zonestat_level_t statlevel = dns_zonestat_none; dns_ttl_t maxttl = 0; /* unlimited */ @@ -1237,7 +1237,7 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, } if (rcvquerystats != NULL) { - dns_stats_detach(&rcvquerystats); + isc_statsmulti_detach(&rcvquerystats); } if (dnssecsignstats != NULL) { diff --git a/lib/dns/include/dns/resolver.h b/lib/dns/include/dns/resolver.h index 6ec13fc631e..fc73fca716e 100644 --- a/lib/dns/include/dns/resolver.h +++ b/lib/dns/include/dns/resolver.h @@ -53,6 +53,7 @@ #include #include #include +#include #include #include @@ -599,7 +600,7 @@ dns_resolver_incstats(dns_resolver_t *res, isc_statscounter_t counter); */ void -dns_resolver_setquerystats(dns_resolver_t *res, dns_stats_t *stats); +dns_resolver_setquerystats(dns_resolver_t *res, isc_statsmulti_t *stats); /*%< * Set a statistics counter set of rdata type, 'stats', for 'res'. Once the * statistic set is installed, the resolver will count outgoing queries @@ -611,7 +612,7 @@ dns_resolver_setquerystats(dns_resolver_t *res, dns_stats_t *stats); */ void -dns_resolver_getquerystats(dns_resolver_t *res, dns_stats_t **statsp); +dns_resolver_getquerystats(dns_resolver_t *res, isc_statsmulti_t **statsp); /*%< * Get the rdatatype statistics counter set for 'res'. If a statistics set is * set '*statsp' will be attached to the set; otherwise, '*statsp' will be diff --git a/lib/dns/include/dns/stats.h b/lib/dns/include/dns/stats.h index 0bbdde4638c..ed0e9878984 100644 --- a/lib/dns/include/dns/stats.h +++ b/lib/dns/include/dns/stats.h @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -285,7 +286,7 @@ dns_generalstats_create(isc_mem_t *mctx, dns_stats_t **statsp, int ncounters); */ void -dns_rdatatypestats_create(isc_mem_t *mctx, dns_stats_t **statsp); +dns_rdatatypestats_create(isc_mem_t *mctx, isc_statsmulti_t **statsp); /*%< * Create a statistics counter structure per rdatatype. * @@ -307,7 +308,7 @@ dns_rdatasetstats_create(isc_mem_t *mctx, dns_stats_t **statsp); */ void -dns_opcodestats_create(isc_mem_t *mctx, dns_stats_t **statsp); +dns_opcodestats_create(isc_mem_t *mctx, isc_statsmulti_t **statsp); /*%< * Create a statistics counter structure per opcode. * @@ -318,7 +319,7 @@ dns_opcodestats_create(isc_mem_t *mctx, dns_stats_t **statsp); */ void -dns_rcodestats_create(isc_mem_t *mctx, dns_stats_t **statsp); +dns_rcodestats_create(isc_mem_t *mctx, isc_statsmulti_t **statsp); /*%< * Create a statistics counter structure per assigned rcode. * @@ -373,12 +374,13 @@ dns_generalstats_increment(dns_stats_t *stats, isc_statscounter_t counter); */ void -dns_rdatatypestats_increment(dns_stats_t *stats, dns_rdatatype_t type); +dns_rdatatypestats_increment(isc_statsmulti_t *stats, dns_rdatatype_t type); /*%< * Increment the statistics counter for 'type'. * * Requires: - *\li 'stats' is a valid dns_stats_t created by dns_rdatatypestats_create(). + *\li 'stats' is a valid isc_statsmulti_t created by + * dns_rdatatypestats_create(). */ void @@ -403,21 +405,21 @@ dns_rdatasetstats_decrement(dns_stats_t *stats, dns_rdatastatstype_t rrsettype); */ void -dns_opcodestats_increment(dns_stats_t *stats, dns_opcode_t code); +dns_opcodestats_increment(isc_statsmulti_t *stats, dns_opcode_t code); /*%< * Increment the statistics counter for 'code'. * * Requires: - *\li 'stats' is a valid dns_stats_t created by dns_opcodestats_create(). + *\li 'stats' is a valid isc_statsmulti_t created by dns_opcodestats_create(). */ void -dns_rcodestats_increment(dns_stats_t *stats, dns_rcode_t code); +dns_rcodestats_increment(isc_statsmulti_t *stats, dns_rcode_t code); /*%< * Increment the statistics counter for 'code'. * * Requires: - *\li 'stats' is a valid dns_stats_t created by dns_rcodestats_create(). + *\li 'stats' is a valid isc_statsmulti_t created by dns_rcodestats_create(). */ void @@ -457,8 +459,9 @@ dns_generalstats_dump(dns_stats_t *stats, dns_generalstats_dumper_t dump_fn, */ void -dns_rdatatypestats_dump(dns_stats_t *stats, dns_rdatatypestats_dumper_t dump_fn, - void *arg, unsigned int options); +dns_rdatatypestats_dump(isc_statsmulti_t *stats, + dns_rdatatypestats_dumper_t dump_fn, void *arg, + unsigned int options); /*%< * Dump the current statistics counters in a specified way. For each counter * in stats, dump_fn is called with the corresponding type in the form of @@ -467,7 +470,8 @@ dns_rdatatypestats_dump(dns_stats_t *stats, dns_rdatatypestats_dumper_t dump_fn, * the ISC_STATSDUMP_VERBOSE flag, even such counters are dumped. * * Requires: - *\li 'stats' is a valid dns_stats_t created by dns_generalstats_create(). + *\li 'stats' is a valid isc_statsmulti_t created by + * dns_rdatatypestats_create(). */ void @@ -500,7 +504,7 @@ dns_dnssecsignstats_dump(dns_stats_t *stats, dnssecsignstats_type_t operation, */ void -dns_opcodestats_dump(dns_stats_t *stats, dns_opcodestats_dumper_t dump_fn, +dns_opcodestats_dump(isc_statsmulti_t *stats, dns_opcodestats_dumper_t dump_fn, void *arg, unsigned int options); /*%< * Dump the current statistics counters in a specified way. For each counter @@ -510,11 +514,11 @@ dns_opcodestats_dump(dns_stats_t *stats, dns_opcodestats_dumper_t dump_fn, * such counters are dumped. * * Requires: - *\li 'stats' is a valid dns_stats_t created by dns_generalstats_create(). + *\li 'stats' is a valid isc_statsmulti_t created by dns_opcodestats_create(). */ void -dns_rcodestats_dump(dns_stats_t *stats, dns_rcodestats_dumper_t dump_fn, +dns_rcodestats_dump(isc_statsmulti_t *stats, dns_rcodestats_dumper_t dump_fn, void *arg, unsigned int options); /*%< * Dump the current statistics counters in a specified way. For each counter @@ -524,5 +528,5 @@ dns_rcodestats_dump(dns_stats_t *stats, dns_rcodestats_dumper_t dump_fn, * such counters are dumped. * * Requires: - *\li 'stats' is a valid dns_stats_t created by dns_generalstats_create(). + *\li 'stats' is a valid isc_statsmulti_t created by dns_rcodestats_create(). */ diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index 5964e69ead4..04719e4f8e2 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -1966,7 +1967,7 @@ void dns_zone_setrequeststats(dns_zone_t *zone, isc_stats_t *stats); void -dns_zone_setrcvquerystats(dns_zone_t *zone, dns_stats_t *stats); +dns_zone_setrcvquerystats(dns_zone_t *zone, isc_statsmulti_t *stats); void dns_zone_setdnssecsignstats(dns_zone_t *zone, dns_stats_t *stats); @@ -1984,7 +1985,7 @@ dns_zone_setdnssecsignstats(dns_zone_t *zone, dns_stats_t *stats); isc_stats_t * dns_zone_getrequeststats(dns_zone_t *zone); -dns_stats_t * +isc_statsmulti_t * dns_zone_getrcvquerystats(dns_zone_t *zone); dns_stats_t * diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index bf800ff067e..f6e7c8055a5 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -597,7 +597,7 @@ struct dns_resolver { unsigned int maxqueries; isc_result_t quotaresp[2]; isc_stats_t *stats; - dns_stats_t *querystats; + isc_statsmulti_t *querystats; isc_histomulti_t *queryinrttstats; isc_histomulti_t *queryoutrttstats; @@ -9642,7 +9642,7 @@ dns_resolver__destroy(dns_resolver_t *res) { isc_histomulti_detach(&res->queryinrttstats); } if (res->querystats != NULL) { - dns_stats_detach(&res->querystats); + isc_statsmulti_detach(&res->querystats); } if (res->stats != NULL) { isc_stats_detach(&res->stats); @@ -10866,20 +10866,20 @@ dns_resolver_incstats(dns_resolver_t *res, isc_statscounter_t counter) { } void -dns_resolver_setquerystats(dns_resolver_t *res, dns_stats_t *stats) { +dns_resolver_setquerystats(dns_resolver_t *res, isc_statsmulti_t *stats) { REQUIRE(VALID_RESOLVER(res)); REQUIRE(res->querystats == NULL); - dns_stats_attach(stats, &res->querystats); + isc_statsmulti_attach(stats, &res->querystats); } void -dns_resolver_getquerystats(dns_resolver_t *res, dns_stats_t **statsp) { +dns_resolver_getquerystats(dns_resolver_t *res, isc_statsmulti_t **statsp) { REQUIRE(VALID_RESOLVER(res)); REQUIRE(statsp != NULL && *statsp == NULL); if (res->querystats != NULL) { - dns_stats_attach(res->querystats, statsp); + isc_statsmulti_attach(res->querystats, statsp); } } diff --git a/lib/dns/stats.c b/lib/dns/stats.c index 3560312cb8c..cb60fcd10f5 100644 --- a/lib/dns/stats.c +++ b/lib/dns/stats.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -189,15 +190,10 @@ dns_generalstats_create(isc_mem_t *mctx, dns_stats_t **statsp, int ncounters) { } void -dns_rdatatypestats_create(isc_mem_t *mctx, dns_stats_t **statsp) { +dns_rdatatypestats_create(isc_mem_t *mctx, isc_statsmulti_t **statsp) { REQUIRE(statsp != NULL && *statsp == NULL); - /* - * Create rdtype statistics for the first 255 RRtypes, - * plus one additional for other RRtypes. - */ - create_stats(mctx, dns_statstype_rdtype, RDTYPECOUNTER_MAXTYPE + 1, - statsp); + isc_statsmulti_create(mctx, statsp, RDTYPECOUNTER_MAXTYPE + 1); } void @@ -209,18 +205,17 @@ dns_rdatasetstats_create(isc_mem_t *mctx, dns_stats_t **statsp) { } void -dns_opcodestats_create(isc_mem_t *mctx, dns_stats_t **statsp) { +dns_opcodestats_create(isc_mem_t *mctx, isc_statsmulti_t **statsp) { REQUIRE(statsp != NULL && *statsp == NULL); - create_stats(mctx, dns_statstype_opcode, 16, statsp); + isc_statsmulti_create(mctx, statsp, 16); } void -dns_rcodestats_create(isc_mem_t *mctx, dns_stats_t **statsp) { +dns_rcodestats_create(isc_mem_t *mctx, isc_statsmulti_t **statsp) { REQUIRE(statsp != NULL && *statsp == NULL); - create_stats(mctx, dns_statstype_rcode, dns_rcode_badcookie + 1, - statsp); + isc_statsmulti_create(mctx, statsp, dns_rcode_badcookie + 1); } void @@ -254,13 +249,13 @@ rdatatype2counter(dns_rdatatype_t type) { } void -dns_rdatatypestats_increment(dns_stats_t *stats, dns_rdatatype_t type) { +dns_rdatatypestats_increment(isc_statsmulti_t *stats, dns_rdatatype_t type) { isc_statscounter_t counter; - REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_rdtype); + REQUIRE(stats != NULL); counter = rdatatype2counter(type); - isc_stats_increment(stats->counters, counter); + isc_statsmulti_increment(stats, counter); } static void @@ -332,18 +327,18 @@ dns_rdatasetstats_decrement(dns_stats_t *stats, } void -dns_opcodestats_increment(dns_stats_t *stats, dns_opcode_t code) { - REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_opcode); +dns_opcodestats_increment(isc_statsmulti_t *stats, dns_opcode_t code) { + REQUIRE(stats != NULL); - isc_stats_increment(stats->counters, (isc_statscounter_t)code); + isc_statsmulti_increment(stats, (isc_statscounter_t)code); } void -dns_rcodestats_increment(dns_stats_t *stats, dns_rcode_t code) { - REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_rcode); +dns_rcodestats_increment(isc_statsmulti_t *stats, dns_rcode_t code) { + REQUIRE(stats != NULL); if (code <= dns_rcode_badcookie) { - isc_stats_increment(stats->counters, (isc_statscounter_t)code); + isc_statsmulti_increment(stats, (isc_statscounter_t)code); } } @@ -460,14 +455,15 @@ rdatatype_dumpcb(isc_statscounter_t counter, uint64_t value, void *arg) { } void -dns_rdatatypestats_dump(dns_stats_t *stats, dns_rdatatypestats_dumper_t dump_fn, - void *arg0, unsigned int options) { +dns_rdatatypestats_dump(isc_statsmulti_t *stats, + dns_rdatatypestats_dumper_t dump_fn, void *arg0, + unsigned int options) { rdatadumparg_t arg; - REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_rdtype); + REQUIRE(stats != NULL); arg.fn = dump_fn; arg.arg = arg0; - isc_stats_dump(stats->counters, rdatatype_dumpcb, &arg, options); + isc_statsmulti_dump(stats, rdatatype_dumpcb, &arg, options); } static void @@ -584,25 +580,25 @@ rcode_dumpcb(isc_statscounter_t counter, uint64_t value, void *arg) { } void -dns_opcodestats_dump(dns_stats_t *stats, dns_opcodestats_dumper_t dump_fn, +dns_opcodestats_dump(isc_statsmulti_t *stats, dns_opcodestats_dumper_t dump_fn, void *arg0, unsigned int options) { opcodedumparg_t arg; - REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_opcode); + REQUIRE(stats != NULL); arg.fn = dump_fn; arg.arg = arg0; - isc_stats_dump(stats->counters, opcode_dumpcb, &arg, options); + isc_statsmulti_dump(stats, opcode_dumpcb, &arg, options); } void -dns_rcodestats_dump(dns_stats_t *stats, dns_rcodestats_dumper_t dump_fn, +dns_rcodestats_dump(isc_statsmulti_t *stats, dns_rcodestats_dumper_t dump_fn, void *arg0, unsigned int options) { rcodedumparg_t arg; - REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_rcode); + REQUIRE(stats != NULL); arg.fn = dump_fn; arg.arg = arg0; - isc_stats_dump(stats->counters, rcode_dumpcb, &arg, options); + isc_statsmulti_dump(stats, rcode_dumpcb, &arg, options); } diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 741e88a5880..ea3ccb1c16e 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -387,7 +387,7 @@ struct dns_zone { dns_zonestat_level_t statlevel; bool requeststats_on; isc_stats_t *requeststats; - dns_stats_t *rcvquerystats; + isc_statsmulti_t *rcvquerystats; dns_stats_t *dnssecsignstats; dns_isselffunc_t isself; void *isselfarg; @@ -1201,7 +1201,7 @@ dns__zone_free(dns_zone_t *zone) { isc_stats_detach(&zone->requeststats); } if (zone->rcvquerystats != NULL) { - dns_stats_detach(&zone->rcvquerystats); + isc_statsmulti_detach(&zone->rcvquerystats); } if (zone->dnssecsignstats != NULL) { dns_stats_detach(&zone->dnssecsignstats); @@ -18990,13 +18990,13 @@ dns_zone_setrequeststats(dns_zone_t *zone, isc_stats_t *stats) { } void -dns_zone_setrcvquerystats(dns_zone_t *zone, dns_stats_t *stats) { +dns_zone_setrcvquerystats(dns_zone_t *zone, isc_statsmulti_t *stats) { REQUIRE(DNS_ZONE_VALID(zone)); LOCK_ZONE(zone); if (zone->requeststats_on && stats != NULL) { if (zone->rcvquerystats == NULL) { - dns_stats_attach(stats, &zone->rcvquerystats); + isc_statsmulti_attach(stats, &zone->rcvquerystats); zone->requeststats_on = true; } } @@ -19042,7 +19042,7 @@ dns_zone_getrequeststats(dns_zone_t *zone) { * Return the received query stats bucket * see note from dns_zone_getrequeststats() */ -dns_stats_t * +isc_statsmulti_t * dns_zone_getrcvquerystats(dns_zone_t *zone) { if (zone->requeststats_on) { return zone->rcvquerystats; diff --git a/lib/isc/include/isc/statsmulti.h b/lib/isc/include/isc/statsmulti.h new file mode 100644 index 00000000000..7785662d842 --- /dev/null +++ b/lib/isc/include/isc/statsmulti.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +/*! \file isc/statsmulti.h */ + +#include + +#include + +typedef struct isc_statsmulti isc_statsmulti_t; /*%< Statistics Multi */ + +/*%< + * Flag(s) for isc_statsmulti_dump(). + */ +#define ISC_STATSMULTIDUMP_VERBOSE 0x00000001 /*%< dump 0-value counters */ + +/*%< + * Dump callback type. + */ +typedef void (*isc_statsmulti_dumper_t)(isc_statscounter_t, uint64_t, void *); + +void +isc_statsmulti_create(isc_mem_t *mctx, isc_statsmulti_t **statsp, + int ncounters); +/*%< + * Create a statistics counter structure for additive counters. + * All counters are additive (sum across threads). + * + * Requires: + *\li 'mctx' must be a valid memory context. + * + *\li 'statsp' != NULL && '*statsp' == NULL. + */ + +void +isc_statsmulti_attach(isc_statsmulti_t *stats, isc_statsmulti_t **statsp); +/*%< + * Attach to a statistics set. + * + * Requires: + *\li 'stats' is a valid isc_statsmulti_t. + * + *\li 'statsp' != NULL && '*statsp' == NULL + */ + +void +isc_statsmulti_detach(isc_statsmulti_t **statsp); +/*%< + * Detaches from the statistics set. + * + * Requires: + *\li 'statsp' != NULL and '*statsp' is a valid isc_statsmulti_t. + */ + +void +isc_statsmulti_increment(isc_statsmulti_t *stats, isc_statscounter_t counter); +/*%< + * Increment the counter-th counter of stats. + * + * Requires: + *\li 'stats' is a valid isc_statsmulti_t. + * + *\li counter is less than ncounters. + */ + +void +isc_statsmulti_decrement(isc_statsmulti_t *stats, isc_statscounter_t counter); +/*%< + * Decrement the counter-th counter of stats. + * + * Requires: + *\li 'stats' is a valid isc_statsmulti_t. + * + *\li counter is less than ncounters. + */ + +void +isc_statsmulti_dump(isc_statsmulti_t *stats, isc_statsmulti_dumper_t dump_fn, + void *arg, unsigned int options); +/*%< + * Dump the current statistics counters in a specified way. For each counter + * in stats, dump_fn is called with its current value and the given argument + * arg. By default counters that have a value of 0 is skipped; if options has + * the ISC_STATSMULTIDUMP_VERBOSE flag, even such counters are dumped. + * + * Requires: + *\li 'stats' is a valid isc_statsmulti_t. + */ + +isc_statscounter_t +isc_statsmulti_get_counter(isc_statsmulti_t *stats, isc_statscounter_t counter); +/*%< + * Returns value currently stored in counter. + * + * Requires: + *\li 'stats' is a valid isc_statsmulti_t. + * + *\li counter is less than ncounters. + */ + +void +isc_statsmulti_clear(isc_statsmulti_t *stats); +/*%< + * Set all counters to zero. + * + * Requires: + *\li 'stats' is a valid isc_statsmulti_t. + */ diff --git a/lib/isc/meson.build b/lib/isc/meson.build index f2c164a5b71..a99c3450f94 100644 --- a/lib/isc/meson.build +++ b/lib/isc/meson.build @@ -123,6 +123,7 @@ isc_srcset.add( 'signal.c', 'sockaddr.c', 'stats.c', + 'statsmulti.c', 'stdio.c', 'stdtime.c', 'string.c', diff --git a/lib/isc/statsmulti.c b/lib/isc/statsmulti.c new file mode 100644 index 00000000000..70f99182628 --- /dev/null +++ b/lib/isc/statsmulti.c @@ -0,0 +1,187 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ISC_STATSMULTI_MAGIC ISC_MAGIC('S', 'M', 'u', 'l') +#define ISC_STATSMULTI_VALID(x) ISC_MAGIC_VALID(x, ISC_STATSMULTI_MAGIC) + +/* + * Same constraint as stats.c + */ +STATIC_ASSERT(sizeof(isc_statscounter_t) <= sizeof(uint64_t), + "Exported statistics must fit into the statistic counter size"); + +struct isc_statsmulti { + unsigned int magic; + isc_mem_t *mctx; + isc_refcount_t references; + int n_counters; + int per_thread_capacity; + int num_threads_plus_one; + isc_atomic_statscounter_t *counters; +}; + +static int +to_index(isc_statsmulti_t *stats, isc_tid_t tid, + isc_statscounter_t internal_counter) { + int thread_id = tid + 1; + if (thread_id >= stats->num_threads_plus_one) { + thread_id = 0; + } + return thread_id * stats->per_thread_capacity + internal_counter; +} + +void +isc_statsmulti_create(isc_mem_t *mctx, isc_statsmulti_t **statsp, + int ncounters) { + REQUIRE(statsp != NULL && *statsp == NULL); + + size_t size_in_bytes = sizeof(isc_atomic_statscounter_t) * ncounters; + size_t rounded_up = (size_in_bytes + 63) & ~63; /* Round up to next + multiple of 64 */ + int per_thread_capacity = rounded_up / + sizeof(isc_atomic_statscounter_t); + int num_threads_plus_one = isc_tid_count() + 1; + + isc_statsmulti_t *stats = isc_mem_get(mctx, sizeof(*stats)); + *stats = (isc_statsmulti_t){ + .magic = ISC_STATSMULTI_MAGIC, + .counters = isc_mem_cget( + mctx, per_thread_capacity * num_threads_plus_one, + sizeof(isc_atomic_statscounter_t)), + .mctx = isc_mem_ref(mctx), + .n_counters = ncounters, + .num_threads_plus_one = num_threads_plus_one, + .per_thread_capacity = per_thread_capacity, + .references = ISC_REFCOUNT_INITIALIZER(1), + + }; + *statsp = stats; +} + +void +isc_statsmulti_attach(isc_statsmulti_t *stats, isc_statsmulti_t **statsp) { + REQUIRE(ISC_STATSMULTI_VALID(stats)); + REQUIRE(statsp != NULL && *statsp == NULL); + + isc_refcount_increment(&stats->references); + *statsp = stats; +} + +void +isc_statsmulti_detach(isc_statsmulti_t **statsp) { + isc_statsmulti_t *stats; + + REQUIRE(statsp != NULL && ISC_STATSMULTI_VALID(*statsp)); + + stats = *statsp; + *statsp = NULL; + + if (isc_refcount_decrement(&stats->references) == 1) { + isc_refcount_destroy(&stats->references); + size_t alloc_size = stats->per_thread_capacity * + stats->num_threads_plus_one * + sizeof(isc_atomic_statscounter_t); + isc_mem_put(stats->mctx, stats->counters, alloc_size); + isc_mem_putanddetach(&stats->mctx, stats, sizeof(*stats)); + } +} + +void +isc_statsmulti_increment(isc_statsmulti_t *stats, isc_statscounter_t counter) { + REQUIRE(ISC_STATSMULTI_VALID(stats)); + REQUIRE(counter < stats->n_counters); + + int index = to_index(stats, isc_tid(), counter); + if (isc_tid() == -1) { + atomic_fetch_add_relaxed(&stats->counters[index], 1); + } else { + isc_atomic_statscounter_t *ptr = &stats->counters[index]; + atomic_store_relaxed(ptr, atomic_load_relaxed(ptr) + 1); + } +} + +void +isc_statsmulti_decrement(isc_statsmulti_t *stats, isc_statscounter_t counter) { + REQUIRE(ISC_STATSMULTI_VALID(stats)); + REQUIRE(counter < stats->n_counters); + + int index = to_index(stats, isc_tid(), counter); + if (isc_tid() == -1) { + atomic_fetch_sub_relaxed(&stats->counters[index], 1); + } else { + isc_atomic_statscounter_t *ptr = &stats->counters[index]; + int_fast64_t tmp = atomic_load_relaxed(ptr); + atomic_store_relaxed(ptr, tmp - 1); + } +} + +void +isc_statsmulti_dump(isc_statsmulti_t *stats, isc_statsmulti_dumper_t dump_fn, + void *arg, unsigned int options) { + REQUIRE(ISC_STATSMULTI_VALID(stats)); + + for (int counter = 0; counter < stats->n_counters; counter++) { + isc_statscounter_t total = isc_statsmulti_get_counter(stats, + counter); + + if ((options & ISC_STATSMULTIDUMP_VERBOSE) == 0 && total == 0) { + continue; + } + dump_fn((isc_statscounter_t)counter, total, arg); + } +} + +isc_statscounter_t +isc_statsmulti_get_counter(isc_statsmulti_t *stats, + isc_statscounter_t counter) { + REQUIRE(ISC_STATSMULTI_VALID(stats)); + REQUIRE(counter < stats->n_counters); + + int idx_0 = to_index(stats, 0, counter); + isc_statscounter_t total = atomic_load_acquire(&stats->counters[idx_0]); + + for (int thread = 1; thread < stats->num_threads_plus_one; thread++) { + int index = to_index(stats, thread, counter); + total += atomic_load_relaxed(&stats->counters[index]); + } + + return total; +} + +void +isc_statsmulti_clear(isc_statsmulti_t *stats) { + REQUIRE(ISC_STATSMULTI_VALID(stats)); + + for (int idx = 0; + idx < stats->per_thread_capacity * stats->num_threads_plus_one; + idx++) + { + atomic_store_release(&stats->counters[idx], 0); + } +} diff --git a/lib/ns/client.c b/lib/ns/client.c index c0a5649a3bc..6bdcbc3e627 100644 --- a/lib/ns/client.c +++ b/lib/ns/client.c @@ -2515,7 +2515,7 @@ ns__client_tcpconn(isc_nmhandle_t *handle, isc_result_t result, void *arg) { } tcpquota = isc_quota_getused(&sctx->tcpquota); - ns_stats_update_if_greater(sctx->nsstats, ns_statscounter_tcphighwater, + ns_stats_update_if_greater(sctx->nshighwaterstats, ns_highwater_tcp, tcpquota); return ISC_R_SUCCESS; diff --git a/lib/ns/include/ns/server.h b/lib/ns/include/ns/server.h index 32f4e1d66bc..b5bbb391a6e 100644 --- a/lib/ns/include/ns/server.h +++ b/lib/ns/include/ns/server.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -120,10 +121,11 @@ struct ns_server { ns_matchview_t matchingview; /*% Stats counters */ - ns_stats_t *nsstats; - dns_stats_t *rcvquerystats; - dns_stats_t *opcodestats; - dns_stats_t *rcodestats; + isc_statsmulti_t *nsstats; + isc_stats_t *nshighwaterstats; + isc_statsmulti_t *rcvquerystats; + isc_statsmulti_t *opcodestats; + isc_statsmulti_t *rcodestats; isc_histomulti_t *udpinstats4; isc_histomulti_t *udpoutstats4; diff --git a/lib/ns/include/ns/stats.h b/lib/ns/include/ns/stats.h index 496e4cd9a54..c242f2c19c6 100644 --- a/lib/ns/include/ns/stats.h +++ b/lib/ns/include/ns/stats.h @@ -17,6 +17,7 @@ #include #include +#include #include @@ -107,50 +108,54 @@ enum { ns_statscounter_prefetch = 64, ns_statscounter_keytagopt = 65, - ns_statscounter_tcphighwater = 66, + ns_statscounter_reclimitdropped = 66, - ns_statscounter_reclimitdropped = 67, + ns_statscounter_updatequota = 67, + ns_statscounter_dot = 68, + ns_statscounter_doh = 69, + ns_statscounter_dohplain = 70, - ns_statscounter_updatequota = 68, + ns_statscounter_proxyudp = 71, + ns_statscounter_proxytcp = 72, + ns_statscounter_proxydot = 73, + ns_statscounter_proxydoh = 74, + ns_statscounter_proxydohplain = 75, + ns_statscounter_encryptedproxydot = 76, + ns_statscounter_encryptedproxydoh = 77, - ns_statscounter_recurshighwater = 69, - - ns_statscounter_dot = 70, - ns_statscounter_doh = 71, - ns_statscounter_dohplain = 72, + ns_statscounter_max = 78, +}; - ns_statscounter_proxyudp = 73, - ns_statscounter_proxytcp = 74, - ns_statscounter_proxydot = 75, - ns_statscounter_proxydoh = 76, - ns_statscounter_proxydohplain = 77, - ns_statscounter_encryptedproxydot = 78, - ns_statscounter_encryptedproxydoh = 79, +/*% + * Highwater statistics counters. Used as isc_statscounter_t values + * for the separate highwater stats structure. + */ +enum { + ns_highwater_tcp = 0, + ns_highwater_recursive = 1, - ns_statscounter_max = 80, + ns_highwater_max = 2, }; void -ns_stats_attach(ns_stats_t *stats, ns_stats_t **statsp); - -void -ns_stats_detach(ns_stats_t **statsp); +ns_stats_create(isc_mem_t *mctx, isc_statsmulti_t **statsp, + isc_stats_t **hwstatsp); void -ns_stats_create(isc_mem_t *mctx, int ncounters, ns_stats_t **statsp); - -isc_statscounter_t -ns_stats_increment(ns_stats_t *stats, isc_statscounter_t counter); +ns_stats_increment(isc_statsmulti_t *stats, isc_statscounter_t counter); void -ns_stats_decrement(ns_stats_t *stats, isc_statscounter_t counter); - -isc_stats_t * -ns_stats_get(ns_stats_t *stats); +ns_stats_decrement(isc_statsmulti_t *stats, isc_statscounter_t counter); void -ns_stats_update_if_greater(ns_stats_t *stats, isc_statscounter_t counter, +ns_stats_update_if_greater(isc_stats_t *hwstats, isc_statscounter_t counter, isc_statscounter_t value); isc_statscounter_t -ns_stats_get_counter(ns_stats_t *stats, isc_statscounter_t counter); +ns_stats_get_counter(isc_statsmulti_t *stats, isc_statscounter_t counter); + +isc_statscounter_t +ns_stats_get_highwater(isc_stats_t *hwstats, isc_statscounter_t counter); + +void +ns_stats_reset_highwater(isc_stats_t *hwstats, isc_statscounter_t counter); diff --git a/lib/ns/query.c b/lib/ns/query.c index 441a12fe5b0..f0ef5c7a478 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -546,7 +546,7 @@ inc_stats(ns_client_t *client, isc_statscounter_t counter) { dns_rdatatype_t qtype; dns_rdataset_t *rdataset; isc_stats_t *zonestats; - dns_stats_t *querystats = NULL; + isc_statsmulti_t *querystats = NULL; ns_stats_increment(client->manager->sctx->nsstats, counter); @@ -2587,7 +2587,6 @@ free_fresp(ns_client_t *client, dns_fetchresponse_t **frespp) { static isc_result_t recursionquotatype_attach(ns_client_t *client, bool soft_limit) { - isc_statscounter_t recurscount; isc_result_t result; result = isc_quota_acquire(&client->manager->sctx->recursionquota); @@ -2610,12 +2609,13 @@ recursionquotatype_attach(ns_client_t *client, bool soft_limit) { return result; } - recurscount = ns_stats_increment(client->manager->sctx->nsstats, - ns_statscounter_recursclients); + ns_stats_increment(client->manager->sctx->nsstats, + ns_statscounter_recursclients); + isc_statscounter_t recurscount = ns_stats_get_counter( + client->manager->sctx->nsstats, ns_statscounter_recursclients); - ns_stats_update_if_greater(client->manager->sctx->nsstats, - ns_statscounter_recurshighwater, - recurscount + 1); + ns_stats_update_if_greater(client->manager->sctx->nshighwaterstats, + ns_highwater_recursive, recurscount); return result; } diff --git a/lib/ns/server.c b/lib/ns/server.c index 857c6d0b6a0..7152970d99d 100644 --- a/lib/ns/server.c +++ b/lib/ns/server.c @@ -70,7 +70,7 @@ ns_server_create(isc_mem_t *mctx, ns_matchview_t matchingview, ISC_LIST_INIT(sctx->http_quotas); isc_mutex_init(&sctx->http_quotas_lock); - ns_stats_create(mctx, ns_statscounter_max, &sctx->nsstats); + ns_stats_create(mctx, &sctx->nsstats, &sctx->nshighwaterstats); dns_rdatatypestats_create(mctx, &sctx->rcvquerystats); @@ -162,17 +162,21 @@ ns_server_detach(ns_server_t **sctxp) { } if (sctx->nsstats != NULL) { - ns_stats_detach(&sctx->nsstats); + isc_statsmulti_detach(&sctx->nsstats); + } + + if (sctx->nshighwaterstats != NULL) { + isc_stats_detach(&sctx->nshighwaterstats); } if (sctx->rcvquerystats != NULL) { - dns_stats_detach(&sctx->rcvquerystats); + isc_statsmulti_detach(&sctx->rcvquerystats); } if (sctx->opcodestats != NULL) { - dns_stats_detach(&sctx->opcodestats); + isc_statsmulti_detach(&sctx->opcodestats); } if (sctx->rcodestats != NULL) { - dns_stats_detach(&sctx->rcodestats); + isc_statsmulti_detach(&sctx->rcodestats); } if (sctx->udpinstats4 != NULL) { diff --git a/lib/ns/stats.c b/lib/ns/stats.c index 6bc7c1f3464..c380d3594c1 100644 --- a/lib/ns/stats.c +++ b/lib/ns/stats.c @@ -13,103 +13,65 @@ /*! \file */ -#include #include -#include #include +#include #include #include -#define NS_STATS_MAGIC ISC_MAGIC('N', 's', 't', 't') -#define NS_STATS_VALID(x) ISC_MAGIC_VALID(x, NS_STATS_MAGIC) - -struct ns_stats { - /*% Unlocked */ - unsigned int magic; - isc_mem_t *mctx; - isc_stats_t *counters; - isc_refcount_t references; -}; - void -ns_stats_attach(ns_stats_t *stats, ns_stats_t **statsp) { - REQUIRE(NS_STATS_VALID(stats)); +ns_stats_create(isc_mem_t *mctx, isc_statsmulti_t **statsp, + isc_stats_t **hwstatsp) { REQUIRE(statsp != NULL && *statsp == NULL); + REQUIRE(hwstatsp != NULL && *hwstatsp == NULL); - isc_refcount_increment(&stats->references); - - *statsp = stats; + isc_statsmulti_create(mctx, statsp, ns_statscounter_max); + isc_stats_create(mctx, hwstatsp, ns_highwater_max); } +/*% + * Increment/Decrement methods + */ void -ns_stats_detach(ns_stats_t **statsp) { - ns_stats_t *stats; - - REQUIRE(statsp != NULL && NS_STATS_VALID(*statsp)); +ns_stats_increment(isc_statsmulti_t *stats, isc_statscounter_t counter) { + REQUIRE(stats != NULL); - stats = *statsp; - *statsp = NULL; - - if (isc_refcount_decrement(&stats->references) == 1) { - isc_stats_detach(&stats->counters); - isc_refcount_destroy(&stats->references); - isc_mem_putanddetach(&stats->mctx, stats, sizeof(*stats)); - } + isc_statsmulti_increment(stats, counter); } void -ns_stats_create(isc_mem_t *mctx, int ncounters, ns_stats_t **statsp) { - REQUIRE(statsp != NULL && *statsp == NULL); - - ns_stats_t *stats = isc_mem_get(mctx, sizeof(*stats)); - stats->counters = NULL; - - isc_refcount_init(&stats->references, 1); - - isc_stats_create(mctx, &stats->counters, ncounters); - - stats->magic = NS_STATS_MAGIC; - stats->mctx = NULL; - isc_mem_attach(mctx, &stats->mctx); - *statsp = stats; -} +ns_stats_decrement(isc_statsmulti_t *stats, isc_statscounter_t counter) { + REQUIRE(stats != NULL); -/*% - * Increment/Decrement methods - */ -isc_statscounter_t -ns_stats_increment(ns_stats_t *stats, isc_statscounter_t counter) { - REQUIRE(NS_STATS_VALID(stats)); - - return isc_stats_increment(stats->counters, counter); + isc_statsmulti_decrement(stats, counter); } void -ns_stats_decrement(ns_stats_t *stats, isc_statscounter_t counter) { - REQUIRE(NS_STATS_VALID(stats)); +ns_stats_update_if_greater(isc_stats_t *hwstats, isc_statscounter_t counter, + isc_statscounter_t value) { + REQUIRE(hwstats != NULL); - isc_stats_decrement(stats->counters, counter); + isc_stats_update_if_greater(hwstats, counter, value); } -isc_stats_t * -ns_stats_get(ns_stats_t *stats) { - REQUIRE(NS_STATS_VALID(stats)); +isc_statscounter_t +ns_stats_get_counter(isc_statsmulti_t *stats, isc_statscounter_t counter) { + REQUIRE(stats != NULL); - return stats->counters; + return isc_statsmulti_get_counter(stats, counter); } -void -ns_stats_update_if_greater(ns_stats_t *stats, isc_statscounter_t counter, - isc_statscounter_t value) { - REQUIRE(NS_STATS_VALID(stats)); +isc_statscounter_t +ns_stats_get_highwater(isc_stats_t *hwstats, isc_statscounter_t counter) { + REQUIRE(hwstats != NULL); - isc_stats_update_if_greater(stats->counters, counter, value); + return isc_stats_get_counter(hwstats, counter); } -isc_statscounter_t -ns_stats_get_counter(ns_stats_t *stats, isc_statscounter_t counter) { - REQUIRE(NS_STATS_VALID(stats)); +void +ns_stats_reset_highwater(isc_stats_t *hwstats, isc_statscounter_t counter) { + REQUIRE(hwstats != NULL); - return isc_stats_get_counter(stats->counters, counter); + isc_stats_set(hwstats, 0, counter); } diff --git a/tests/isc/stats_test.c b/tests/isc/stats_test.c index 36071e7165e..cfbd823498c 100644 --- a/tests/isc/stats_test.c +++ b/tests/isc/stats_test.c @@ -22,10 +22,16 @@ #define UNIT_TESTING #include +#include +#include #include +#include #include #include #include +#include +#include +#include #include #include @@ -92,9 +98,102 @@ ISC_RUN_TEST_IMPL(isc_stats_basic) { isc_stats_detach(&stats); } +/* test statsmulti */ +ISC_RUN_TEST_IMPL(isc_statsmulti_basic) { + isc_statsmulti_t *stats = NULL; + + /* Create with 3 additive counters */ + isc_statsmulti_create(isc_g_mctx, &stats, 3); + + /* Test increment on additive counters */ + for (int i = 0; i < 3; i++) { + isc_statsmulti_increment(stats, i); + assert_int_equal(isc_statsmulti_get_counter(stats, i), 1); + isc_statsmulti_increment(stats, i); + assert_int_equal(isc_statsmulti_get_counter(stats, i), 2); + } + + /* Test decrement on additive counters */ + for (int i = 0; i < 3; i++) { + isc_statsmulti_decrement(stats, i); + assert_int_equal(isc_statsmulti_get_counter(stats, i), 1); + isc_statsmulti_decrement(stats, i); + assert_int_equal(isc_statsmulti_get_counter(stats, i), 0); + } + + /* Test clear */ + isc_statsmulti_increment(stats, 0); + isc_statsmulti_increment(stats, 1); + isc_statsmulti_clear(stats); + for (int i = 0; i < 3; i++) { + assert_int_equal(isc_statsmulti_get_counter(stats, i), 0); + } + + isc_statsmulti_detach(&stats); +} + +/* test statsmulti with multiple threads */ +static isc_statsmulti_t *mt_stats = NULL; +static atomic_uint_fast32_t mt_workers_completed = 0; +static int mt_counter_id = 0; /* Global counter ID */ + +#define MT_INCREMENTS_PER_THREAD 100000 + +static void +mt_increment_worker(void *arg ISC_ATTR_UNUSED) { + /* Do exactly 100,000 increments */ + for (int i = 0; i < MT_INCREMENTS_PER_THREAD; i++) { + isc_statsmulti_increment(mt_stats, mt_counter_id); + } + + /* Signal completion and check if we're the last one */ + uint32_t completed = atomic_fetch_add(&mt_workers_completed, 1) + 1; + if (completed == isc_loopmgr_nloops()) { + /* Last worker shuts down the loop manager */ + isc_loopmgr_shutdown(); + } +} + +static void +mt_setup_workers(void *arg ISC_ATTR_UNUSED) { + /* Start workers on each loop */ + for (size_t i = 0; i < isc_loopmgr_nloops(); i++) { + isc_async_run(isc_loop_get(i), mt_increment_worker, NULL); + } +} + +ISC_RUN_TEST_IMPL(isc_statsmulti_multithread) { + atomic_store(&mt_workers_completed, 0); + + /* Create stats with 1 additive counter */ + isc_statsmulti_create(isc_g_mctx, &mt_stats, 1); + + isc_loop_setup(isc_loop_main(), mt_setup_workers, NULL); + isc_loopmgr_run(); + + /* Check results - should be exactly threads * increments per thread */ + uint64_t actual_count = isc_statsmulti_get_counter(mt_stats, 0); + uint64_t expected_total = (uint64_t)isc_loopmgr_nloops() * + MT_INCREMENTS_PER_THREAD; + + /* Verify no increments were lost */ + assert_int_equal(actual_count, expected_total); + assert_true(actual_count > 0); + + /* Verify all workers completed */ + assert_int_equal(atomic_load(&mt_workers_completed), + isc_loopmgr_nloops()); + + /* Cleanup */ + isc_statsmulti_detach(&mt_stats); +} + ISC_TEST_LIST_START ISC_TEST_ENTRY(isc_stats_basic) +ISC_TEST_ENTRY(isc_statsmulti_basic) +ISC_TEST_ENTRY_CUSTOM(isc_statsmulti_multithread, setup_loopmgr, + teardown_loopmgr) ISC_TEST_LIST_END