1 1.12 christos /* $NetBSD: stats.c,v 1.12 2025/01/26 16:25:38 christos Exp $ */ 2 1.1 christos 3 1.1 christos /* 4 1.1 christos * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 1.1 christos * 6 1.10 christos * SPDX-License-Identifier: MPL-2.0 7 1.10 christos * 8 1.1 christos * This Source Code Form is subject to the terms of the Mozilla Public 9 1.1 christos * License, v. 2.0. If a copy of the MPL was not distributed with this 10 1.8 christos * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 1.1 christos * 12 1.1 christos * See the COPYRIGHT file distributed with this work for additional 13 1.1 christos * information regarding copyright ownership. 14 1.1 christos */ 15 1.1 christos 16 1.1 christos /*! \file */ 17 1.1 christos 18 1.3 christos #include <inttypes.h> 19 1.1 christos #include <string.h> 20 1.1 christos 21 1.1 christos #include <isc/atomic.h> 22 1.1 christos #include <isc/buffer.h> 23 1.1 christos #include <isc/magic.h> 24 1.1 christos #include <isc/mem.h> 25 1.5 christos #include <isc/refcount.h> 26 1.1 christos #include <isc/stats.h> 27 1.1 christos #include <isc/util.h> 28 1.1 christos 29 1.7 christos #define ISC_STATS_MAGIC ISC_MAGIC('S', 't', 'a', 't') 30 1.7 christos #define ISC_STATS_VALID(x) ISC_MAGIC_VALID(x, ISC_STATS_MAGIC) 31 1.1 christos 32 1.11 christos /* 33 1.11 christos * Statistics are counted with an atomic int_fast64_t but exported to functions 34 1.11 christos * taking uint64_t (isc_stats_dumper_t). A 128-bit native and fast architecture 35 1.11 christos * doesn't exist in reality so these two are the same thing in practise. 36 1.11 christos * However, a silent truncation happening silently in the future is still not 37 1.11 christos * acceptable. 38 1.11 christos */ 39 1.11 christos STATIC_ASSERT(sizeof(isc_statscounter_t) <= sizeof(uint64_t), 40 1.11 christos "Exported statistics must fit into the statistic counter size"); 41 1.1 christos 42 1.1 christos struct isc_stats { 43 1.7 christos unsigned int magic; 44 1.7 christos isc_mem_t *mctx; 45 1.7 christos isc_refcount_t references; 46 1.7 christos int ncounters; 47 1.11 christos isc_atomic_statscounter_t *counters; 48 1.1 christos }; 49 1.1 christos 50 1.1 christos void 51 1.1 christos isc_stats_attach(isc_stats_t *stats, isc_stats_t **statsp) { 52 1.1 christos REQUIRE(ISC_STATS_VALID(stats)); 53 1.1 christos REQUIRE(statsp != NULL && *statsp == NULL); 54 1.1 christos 55 1.7 christos isc_refcount_increment(&stats->references); 56 1.1 christos *statsp = stats; 57 1.1 christos } 58 1.1 christos 59 1.1 christos void 60 1.1 christos isc_stats_detach(isc_stats_t **statsp) { 61 1.1 christos isc_stats_t *stats; 62 1.1 christos 63 1.1 christos REQUIRE(statsp != NULL && ISC_STATS_VALID(*statsp)); 64 1.1 christos 65 1.1 christos stats = *statsp; 66 1.1 christos *statsp = NULL; 67 1.1 christos 68 1.7 christos if (isc_refcount_decrement(&stats->references) == 1) { 69 1.7 christos isc_refcount_destroy(&stats->references); 70 1.12 christos isc_mem_cput(stats->mctx, stats->counters, stats->ncounters, 71 1.12 christos sizeof(isc_atomic_statscounter_t)); 72 1.1 christos isc_mem_putanddetach(&stats->mctx, stats, sizeof(*stats)); 73 1.1 christos } 74 1.1 christos } 75 1.1 christos 76 1.1 christos int 77 1.1 christos isc_stats_ncounters(isc_stats_t *stats) { 78 1.1 christos REQUIRE(ISC_STATS_VALID(stats)); 79 1.1 christos 80 1.12 christos return stats->ncounters; 81 1.1 christos } 82 1.1 christos 83 1.12 christos void 84 1.1 christos isc_stats_create(isc_mem_t *mctx, isc_stats_t **statsp, int ncounters) { 85 1.1 christos REQUIRE(statsp != NULL && *statsp == NULL); 86 1.1 christos 87 1.12 christos isc_stats_t *stats = isc_mem_get(mctx, sizeof(*stats)); 88 1.12 christos size_t counters_alloc_size = sizeof(isc_atomic_statscounter_t) * 89 1.12 christos ncounters; 90 1.12 christos stats->counters = isc_mem_get(mctx, counters_alloc_size); 91 1.12 christos isc_refcount_init(&stats->references, 1); 92 1.12 christos for (int i = 0; i < ncounters; i++) { 93 1.12 christos atomic_init(&stats->counters[i], 0); 94 1.12 christos } 95 1.12 christos stats->mctx = NULL; 96 1.12 christos isc_mem_attach(mctx, &stats->mctx); 97 1.12 christos stats->ncounters = ncounters; 98 1.12 christos stats->magic = ISC_STATS_MAGIC; 99 1.12 christos *statsp = stats; 100 1.1 christos } 101 1.1 christos 102 1.12 christos isc_statscounter_t 103 1.1 christos isc_stats_increment(isc_stats_t *stats, isc_statscounter_t counter) { 104 1.1 christos REQUIRE(ISC_STATS_VALID(stats)); 105 1.1 christos REQUIRE(counter < stats->ncounters); 106 1.1 christos 107 1.12 christos return atomic_fetch_add_relaxed(&stats->counters[counter], 1); 108 1.1 christos } 109 1.1 christos 110 1.1 christos void 111 1.1 christos isc_stats_decrement(isc_stats_t *stats, isc_statscounter_t counter) { 112 1.1 christos REQUIRE(ISC_STATS_VALID(stats)); 113 1.1 christos REQUIRE(counter < stats->ncounters); 114 1.12 christos #if ISC_STATS_CHECKUNDERFLOW 115 1.12 christos REQUIRE(atomic_fetch_sub_release(&stats->counters[counter], 1) > 0); 116 1.12 christos #else 117 1.7 christos atomic_fetch_sub_release(&stats->counters[counter], 1); 118 1.12 christos #endif 119 1.1 christos } 120 1.1 christos 121 1.1 christos void 122 1.7 christos isc_stats_dump(isc_stats_t *stats, isc_stats_dumper_t dump_fn, void *arg, 123 1.7 christos unsigned int options) { 124 1.1 christos int i; 125 1.1 christos 126 1.1 christos REQUIRE(ISC_STATS_VALID(stats)); 127 1.1 christos 128 1.3 christos for (i = 0; i < stats->ncounters; i++) { 129 1.11 christos isc_statscounter_t counter = 130 1.11 christos atomic_load_acquire(&stats->counters[i]); 131 1.5 christos if ((options & ISC_STATSDUMP_VERBOSE) == 0 && counter == 0) { 132 1.5 christos continue; 133 1.5 christos } 134 1.5 christos dump_fn((isc_statscounter_t)i, counter, arg); 135 1.1 christos } 136 1.1 christos } 137 1.1 christos 138 1.1 christos void 139 1.7 christos isc_stats_set(isc_stats_t *stats, uint64_t val, isc_statscounter_t counter) { 140 1.1 christos REQUIRE(ISC_STATS_VALID(stats)); 141 1.1 christos REQUIRE(counter < stats->ncounters); 142 1.1 christos 143 1.7 christos atomic_store_release(&stats->counters[counter], val); 144 1.1 christos } 145 1.6 christos 146 1.7 christos void 147 1.7 christos isc_stats_update_if_greater(isc_stats_t *stats, isc_statscounter_t counter, 148 1.7 christos isc_statscounter_t value) { 149 1.6 christos REQUIRE(ISC_STATS_VALID(stats)); 150 1.6 christos REQUIRE(counter < stats->ncounters); 151 1.6 christos 152 1.6 christos isc_statscounter_t curr_value = 153 1.7 christos atomic_load_acquire(&stats->counters[counter]); 154 1.6 christos do { 155 1.6 christos if (curr_value >= value) { 156 1.6 christos break; 157 1.6 christos } 158 1.7 christos } while (!atomic_compare_exchange_weak_acq_rel( 159 1.7 christos &stats->counters[counter], &curr_value, value)); 160 1.6 christos } 161 1.6 christos 162 1.6 christos isc_statscounter_t 163 1.7 christos isc_stats_get_counter(isc_stats_t *stats, isc_statscounter_t counter) { 164 1.6 christos REQUIRE(ISC_STATS_VALID(stats)); 165 1.6 christos REQUIRE(counter < stats->ncounters); 166 1.6 christos 167 1.12 christos return atomic_load_acquire(&stats->counters[counter]); 168 1.6 christos } 169 1.10 christos 170 1.10 christos void 171 1.10 christos isc_stats_resize(isc_stats_t **statsp, int ncounters) { 172 1.10 christos isc_stats_t *stats; 173 1.10 christos size_t counters_alloc_size; 174 1.11 christos isc_atomic_statscounter_t *newcounters; 175 1.10 christos 176 1.10 christos REQUIRE(statsp != NULL && *statsp != NULL); 177 1.10 christos REQUIRE(ISC_STATS_VALID(*statsp)); 178 1.10 christos REQUIRE(ncounters > 0); 179 1.10 christos 180 1.10 christos stats = *statsp; 181 1.10 christos if (stats->ncounters >= ncounters) { 182 1.10 christos /* We already have enough counters. */ 183 1.10 christos return; 184 1.10 christos } 185 1.10 christos 186 1.10 christos /* Grow number of counters. */ 187 1.11 christos counters_alloc_size = sizeof(isc_atomic_statscounter_t) * ncounters; 188 1.10 christos newcounters = isc_mem_get(stats->mctx, counters_alloc_size); 189 1.10 christos for (int i = 0; i < ncounters; i++) { 190 1.10 christos atomic_init(&newcounters[i], 0); 191 1.10 christos } 192 1.10 christos for (int i = 0; i < stats->ncounters; i++) { 193 1.10 christos uint32_t counter = atomic_load_acquire(&stats->counters[i]); 194 1.10 christos atomic_store_release(&newcounters[i], counter); 195 1.10 christos } 196 1.12 christos isc_mem_cput(stats->mctx, stats->counters, stats->ncounters, 197 1.12 christos sizeof(isc_atomic_statscounter_t)); 198 1.10 christos stats->counters = newcounters; 199 1.10 christos stats->ncounters = ncounters; 200 1.10 christos } 201