]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Add low contention stats counter
authorAlessio Podda <alessio@isc.org>
Fri, 5 Sep 2025 13:39:48 +0000 (15:39 +0200)
committerAlessio Podda <alessio@isc.org>
Thu, 26 Mar 2026 09:19:25 +0000 (10:19 +0100)
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.

20 files changed:
bin/delv/delv.c
bin/named/server.c
bin/named/statschannel.c
bin/named/zoneconf.c
lib/dns/include/dns/resolver.h
lib/dns/include/dns/stats.h
lib/dns/include/dns/zone.h
lib/dns/resolver.c
lib/dns/stats.c
lib/dns/zone.c
lib/isc/include/isc/statsmulti.h [new file with mode: 0644]
lib/isc/meson.build
lib/isc/statsmulti.c [new file with mode: 0644]
lib/ns/client.c
lib/ns/include/ns/server.h
lib/ns/include/ns/stats.h
lib/ns/query.c
lib/ns/server.c
lib/ns/stats.c
tests/isc/stats_test.c

index 6ddef282d003b33beac6a3dddd118d37a5515e8b..6e2370e060291ba08176aa80b8a9af8f3154d4d6 100644 (file)
@@ -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);
 
index 70efe66b08d6d57016e2d64fef05dbacdefe6310..4b9138453d9789aa7da96e27b754f2a52f8f45ed 100644 (file)
@@ -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);
index 56b473cf3ddfbde4768eea83ff7b0127aa9047d0..17b33d7cf81756bc526a109ffa7cf1f609ad959e 100644 (file)
@@ -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));
 
                /* <resstats> */
@@ -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,
index 0e422d33ed280126a5b5184bf434d1f43dbd336e..515e1849136714cadff0e4710d2f0a77ff88de0c 100644 (file)
@@ -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) {
index 6ec13fc631ed728810a7cb57021011387bc3cf56..fc73fca716e0ea9cc7d159aea1073b20c8a75af8 100644 (file)
@@ -53,6 +53,7 @@
 #include <isc/loop.h>
 #include <isc/refcount.h>
 #include <isc/stats.h>
+#include <isc/statsmulti.h>
 #include <isc/tls.h>
 #include <isc/types.h>
 
@@ -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
index 0bbdde4638c18c1034d4e0d567aac39fbf7c3a52..ed0e9878984eca352abcbe83584fd73145209dd8 100644 (file)
@@ -18,6 +18,7 @@
 #include <inttypes.h>
 
 #include <isc/histo.h>
+#include <isc/statsmulti.h>
 
 #include <dns/resolver.h>
 #include <dns/types.h>
@@ -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().
  */
index 5964e69ead451da03eeb83a2ca9d483224406a54..04719e4f8e261a130b77f6b1368cceb929364882 100644 (file)
@@ -25,6 +25,7 @@
 
 #include <isc/formatcheck.h>
 #include <isc/rwlock.h>
+#include <isc/statsmulti.h>
 #include <isc/tls.h>
 
 #include <dns/catz.h>
@@ -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 *
index bf800ff067ed013e65b44b7a792c7d6b84c47996..f6e7c8055a50822df7ab9069aede6033d18f29e5 100644 (file)
@@ -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);
        }
 }
 
index 3560312cb8cc3b2d12c836c45212a25f7c67d6e3..cb60fcd10f59d62975c9a23d986203430e68c8a6 100644 (file)
@@ -21,6 +21,7 @@
 #include <isc/mem.h>
 #include <isc/refcount.h>
 #include <isc/stats.h>
+#include <isc/statsmulti.h>
 #include <isc/util.h>
 
 #include <dns/opcode.h>
@@ -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);
 }
index 741e88a58802a7e19625d41bc46d7efbf08e1bc7..ea3ccb1c16e9045dfd09fe4db4d2d4c84610f155 100644 (file)
@@ -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 (file)
index 0000000..7785662
--- /dev/null
@@ -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 <inttypes.h>
+
+#include <isc/types.h>
+
+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.
+ */
index f2c164a5b717e3822e4e9f0d12c2e27ca8bb2eaf..a99c3450f949e40b2167280e49fd41599cdd3778 100644 (file)
@@ -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 (file)
index 0000000..70f9918
--- /dev/null
@@ -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 <inttypes.h>
+#include <string.h>
+
+#include <isc/atomic.h>
+#include <isc/buffer.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/os.h>
+#include <isc/refcount.h>
+#include <isc/stats.h>
+#include <isc/statsmulti.h>
+#include <isc/tid.h>
+#include <isc/util.h>
+
+#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);
+       }
+}
index c0a5649a3bcec0fe1583ea9956ca77a44eeb9505..6bdcbc3e627f25e062f54ebd3088b2d4734c660c 100644 (file)
@@ -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;
index 32f4e1d66bc8b87b541739e401821c111408edf2..b5bbb391a6e2c168d33609418e94825eed83a832 100644 (file)
@@ -25,6 +25,7 @@
 #include <isc/quota.h>
 #include <isc/random.h>
 #include <isc/sockaddr.h>
+#include <isc/statsmulti.h>
 #include <isc/types.h>
 
 #include <dns/acl.h>
@@ -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;
index 496e4cd9a549876eb04faddb2c538b3396564238..c242f2c19c68fdbc7369b228b11d6b28becb48a0 100644 (file)
@@ -17,6 +17,7 @@
 
 #include <isc/mem.h>
 #include <isc/stats.h>
+#include <isc/statsmulti.h>
 
 #include <ns/types.h>
 
@@ -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);
index 441a12fe5b039c4ea15f63bd4e7dea9d93c4622f..f0ef5c7a4786476aa4c5823cb2753fac9cacfd59 100644 (file)
@@ -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;
 }
index 857c6d0b6a0f9509b3d8c9d83adc117e91097140..7152970d99d5c3a22872c612e16d509cdd74becd 100644 (file)
@@ -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) {
index 6bc7c1f34641be012597a5be13dfcf93d48d3e03..c380d3594c1944b52929a9a3408e651f61e29be1 100644 (file)
 
 /*! \file */
 
-#include <isc/magic.h>
 #include <isc/mem.h>
-#include <isc/refcount.h>
 #include <isc/stats.h>
+#include <isc/statsmulti.h>
 #include <isc/util.h>
 
 #include <ns/stats.h>
 
-#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);
 }
index 36071e7165efa573c92233230db9f6bfa9b652a3..cfbd823498c3675da0fd9a896e0561bc78861ffa 100644 (file)
 #define UNIT_TESTING
 #include <cmocka.h>
 
+#include <isc/async.h>
+#include <isc/atomic.h>
 #include <isc/lib.h>
+#include <isc/loop.h>
 #include <isc/mem.h>
 #include <isc/result.h>
 #include <isc/stats.h>
+#include <isc/statsmulti.h>
+#include <isc/time.h>
+#include <isc/timer.h>
 #include <isc/util.h>
 
 #include <tests/isc.h>
@@ -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