blob: 3fc49922e68c3c5817834c4ea6a741c5f0e7f1f9 [file] [log] [blame]
Harald Welteae510dc2017-10-03 17:46:14 +08001/* (C) 2009-2017 by Harald Welte <laforge@gnumonks.org>
Harald Welte7b45d602010-05-13 11:35:30 +02002 *
3 * All Rights Reserved
4 *
Harald Weltee08da972017-11-13 01:00:26 +09005 * SPDX-License-Identifier: GPL-2.0+
6 *
Harald Welte7b45d602010-05-13 11:35:30 +02007 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
Harald Welte7b45d602010-05-13 11:35:30 +020017 */
18
Harald Welte9327c6d2011-08-17 16:06:06 +020019/*! \addtogroup rate_ctr
20 * @{
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020021 * Counters about events and their event rates.
22 *
Harald Welte53de0d32017-10-16 14:09:16 +020023 * As \ref osmo_counter and \ref osmo_stat_item are concerned only with
24 * a single given value that may be increased/decreased, or the difference
25 * to one given previous value, this module adds some support for keeping
26 * long term information about a given event rate.
27 *
28 * A \ref rate_ctr keeps information on the amount of events per second,
29 * per minute, per hour and per day.
30 *
31 * \ref rate_ctr come in groups: An application describes a group of counters
32 * with their names and identities once in a (typically const) \ref
33 * rate_ctr_group_desc.
34 *
35 * As objects (such as e.g. a subscriber or a PDP context) are
36 * allocated dynamically at runtime, the application calls \ref
37 * rate_ctr_group_alloc with a refernce to the \ref
38 * rate_ctr_group_desc, which causes the library to allocate one set of
39 * \ref rate_ctr: One for each in the group.
40 *
41 * The application then uses functions like \ref rate_ctr_add or \ref
42 * rate_ctr_inc to increment the value as certain events (e.g. location
43 * update) happens.
44 *
45 * The library internally keeps a timer once per second which iterates
46 * over all registered counters and which updates the per-second,
47 * per-minute, per-hour and per-day averages based on the current
48 * value.
49 *
50 * The counters can be reported using \ref stats or by VTY
51 * introspection, as well as by any application-specific code accessing
52 * the \ref rate_ctr.intv array directly.
53 *
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020054 * \file rate_ctr.c */
Harald Welte9327c6d2011-08-17 16:06:06 +020055
Max7c1b6cb2022-11-24 17:42:02 +030056#include <errno.h>
Harald Welteae510dc2017-10-03 17:46:14 +080057#include <stdbool.h>
Harald Welte7b45d602010-05-13 11:35:30 +020058#include <stdint.h>
Harald Welte7b45d602010-05-13 11:35:30 +020059#include <string.h>
Max7c1b6cb2022-11-24 17:42:02 +030060#include <unistd.h>
61#include <inttypes.h>
Harald Welte7b45d602010-05-13 11:35:30 +020062
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010063#include <osmocom/core/utils.h>
64#include <osmocom/core/linuxlist.h>
65#include <osmocom/core/talloc.h>
Max7c1b6cb2022-11-24 17:42:02 +030066#include <osmocom/core/select.h>
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010067#include <osmocom/core/rate_ctr.h>
Harald Welteae510dc2017-10-03 17:46:14 +080068#include <osmocom/core/logging.h>
Harald Welte7b45d602010-05-13 11:35:30 +020069
70static LLIST_HEAD(rate_ctr_groups);
Harald Welteea8272c2019-05-10 11:52:02 +020071
Harald Welte7b45d602010-05-13 11:35:30 +020072static void *tall_rate_ctr_ctx;
73
Harald Welteae510dc2017-10-03 17:46:14 +080074
Pau Espin Pedroldfc52a12018-06-27 16:53:31 +020075static bool rate_ctrl_group_desc_validate(const struct rate_ctr_group_desc *desc)
Harald Welteae510dc2017-10-03 17:46:14 +080076{
77 unsigned int i;
Harald Welteb48e82c2017-10-24 18:35:24 +020078 const struct rate_ctr_desc *ctr_desc;
Harald Welteae510dc2017-10-03 17:46:14 +080079
80 if (!desc) {
81 LOGP(DLGLOBAL, LOGL_ERROR, "NULL is not a valid counter group descriptor\n");
82 return false;
83 }
Harald Welteb48e82c2017-10-24 18:35:24 +020084 ctr_desc = desc->ctr_desc;
Harald Welteae510dc2017-10-03 17:46:14 +080085
86 DEBUGP(DLGLOBAL, "validating counter group %p(%s) with %u counters\n", desc,
87 desc->group_name_prefix, desc->num_ctr);
88
89 if (!osmo_identifier_valid(desc->group_name_prefix)) {
Pau Espin Pedroldfc52a12018-06-27 16:53:31 +020090 LOGP(DLGLOBAL, LOGL_ERROR, "'%s' is not a valid counter group identifier\n",
91 desc->group_name_prefix);
Harald Welteae510dc2017-10-03 17:46:14 +080092 return false;
93 }
94
95 for (i = 0; i < desc->num_ctr; i++) {
96 if (!osmo_identifier_valid(ctr_desc[i].name)) {
Pau Espin Pedroldfc52a12018-06-27 16:53:31 +020097 LOGP(DLGLOBAL, LOGL_ERROR, "'%s' is not a valid counter identifier\n",
98 ctr_desc[i].name);
Harald Welteae510dc2017-10-03 17:46:14 +080099 return false;
100 }
101 }
102
103 return true;
104}
105
Pau Espin Pedroldfc52a12018-06-27 16:53:31 +0200106/* return 'in' if it doesn't contain any '.'; otherwise allocate a copy and
Harald Welteae510dc2017-10-03 17:46:14 +0800107 * replace all '.' with ':' */
108static char *mangle_identifier_ifneeded(const void *ctx, const char *in)
109{
110 char *out;
111 unsigned int i;
Pau Espin Pedroldfc52a12018-06-27 16:53:31 +0200112 bool modified = false;
Harald Welteae510dc2017-10-03 17:46:14 +0800113
114 if (!in)
115 return NULL;
116
117 if (!strchr(in, '.'))
118 return (char *)in;
119
120 out = talloc_strdup(ctx, in);
121 OSMO_ASSERT(out);
122
123 for (i = 0; i < strlen(out); i++) {
Pau Espin Pedroldfc52a12018-06-27 16:53:31 +0200124 if (out[i] == '.') {
Harald Welteae510dc2017-10-03 17:46:14 +0800125 out[i] = ':';
Pau Espin Pedroldfc52a12018-06-27 16:53:31 +0200126 modified = true;
127 }
Harald Welteae510dc2017-10-03 17:46:14 +0800128 }
129
Pau Espin Pedroldfc52a12018-06-27 16:53:31 +0200130 if (modified)
131 LOGP(DLGLOBAL, LOGL_NOTICE, "counter group name mangled: '%s' -> '%s'\n",
132 in, out);
133
Harald Welteae510dc2017-10-03 17:46:14 +0800134 return out;
135}
136
137/* "mangle" a rate counter group descriptor, i.e. replace any '.' with ':' */
138static struct rate_ctr_group_desc *
139rate_ctr_group_desc_mangle(void *ctx, const struct rate_ctr_group_desc *desc)
140{
141 struct rate_ctr_group_desc *desc_new = talloc_zero(ctx, struct rate_ctr_group_desc);
142 int i;
143
144 OSMO_ASSERT(desc_new);
145
Pau Espin Pedroldfc52a12018-06-27 16:53:31 +0200146 LOGP(DLGLOBAL, LOGL_INFO, "Needed to mangle counter group '%s' names: it is still using '.' as "
147 "separator, which is not allowed. please consider updating the application\n",
148 desc->group_name_prefix);
149
Harald Welteae510dc2017-10-03 17:46:14 +0800150 /* mangle the name_prefix but copy/keep the rest */
151 desc_new->group_name_prefix = mangle_identifier_ifneeded(desc_new, desc->group_name_prefix);
152 desc_new->group_description = desc->group_description;
153 desc_new->class_id = desc->class_id;
154 desc_new->num_ctr = desc->num_ctr;
155 desc_new->ctr_desc = talloc_array(desc_new, struct rate_ctr_desc, desc_new->num_ctr);
156 OSMO_ASSERT(desc_new->ctr_desc);
157
158 for (i = 0; i < desc->num_ctr; i++) {
159 struct rate_ctr_desc *ctrd_new = (struct rate_ctr_desc *) desc_new->ctr_desc;
160 const struct rate_ctr_desc *ctrd = desc->ctr_desc;
161
162 if (!ctrd[i].name) {
163 LOGP(DLGLOBAL, LOGL_ERROR, "counter group '%s'[%d] == NULL, aborting\n",
164 desc->group_name_prefix, i);
165 goto err_free;
166 }
167
168 ctrd_new[i].name = mangle_identifier_ifneeded(desc_new->ctr_desc, ctrd[i].name);
169 ctrd_new[i].description = ctrd[i].description;
170 }
171
Pau Espin Pedroldfc52a12018-06-27 16:53:31 +0200172 if (!rate_ctrl_group_desc_validate(desc_new)) {
Harald Welteae510dc2017-10-03 17:46:14 +0800173 /* simple mangling of identifiers ('.' -> ':') was not sufficient to render a valid
174 * descriptor, we have to bail out */
175 LOGP(DLGLOBAL, LOGL_ERROR, "counter group '%s' still invalid after mangling\n",
176 desc->group_name_prefix);
177 goto err_free;
178 }
179
Harald Welteae510dc2017-10-03 17:46:14 +0800180 return desc_new;
181err_free:
182 talloc_free(desc_new);
183 return NULL;
184}
185
Neels Hofmeyr554f7b82017-12-20 01:14:31 +0100186/*! Find an unused index for this rate counter group.
187 * \param[in] name Name of the counter group
188 * \returns the largest used index number + 1, or 0 if none exist yet. */
189static unsigned int rate_ctr_get_unused_name_idx(const char *name)
190{
191 unsigned int idx = 0;
192 struct rate_ctr_group *ctrg;
193
194 llist_for_each_entry(ctrg, &rate_ctr_groups, list) {
195 if (!ctrg->desc)
196 continue;
197
198 if (strcmp(ctrg->desc->group_name_prefix, name))
199 continue;
200
201 if (idx <= ctrg->idx)
202 idx = ctrg->idx + 1;
203 }
204 return idx;
205}
206
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200207/*! Allocate a new group of counters according to description
Vadim Yanitskiyed3a3c62019-03-26 00:57:45 +0700208 * \param[in] ctx parent talloc context
Harald Welte9327c6d2011-08-17 16:06:06 +0200209 * \param[in] desc Rate counter group description
210 * \param[in] idx Index of new counter group
211 */
Harald Welte7b45d602010-05-13 11:35:30 +0200212struct rate_ctr_group *rate_ctr_group_alloc(void *ctx,
213 const struct rate_ctr_group_desc *desc,
214 unsigned int idx)
215{
216 unsigned int size;
217 struct rate_ctr_group *group;
218
Harald Welted589f1d2017-12-18 17:13:38 +0100219 if (rate_ctr_get_group_by_name_idx(desc->group_name_prefix, idx)) {
Neels Hofmeyr554f7b82017-12-20 01:14:31 +0100220 unsigned int new_idx = rate_ctr_get_unused_name_idx(desc->group_name_prefix);
221 LOGP(DLGLOBAL, LOGL_ERROR, "counter group '%s' already exists for index %u,"
222 " instead using index %u. This is a software bug that needs fixing.\n",
223 desc->group_name_prefix, idx, new_idx);
224 idx = new_idx;
Harald Welted589f1d2017-12-18 17:13:38 +0100225 }
Max3ef14a22017-12-15 20:19:10 +0100226
Harald Welte7b45d602010-05-13 11:35:30 +0200227 size = sizeof(struct rate_ctr_group) +
228 desc->num_ctr * sizeof(struct rate_ctr);
229
230 if (!ctx)
231 ctx = tall_rate_ctr_ctx;
232
233 group = talloc_zero_size(ctx, size);
234 if (!group)
235 return NULL;
236
Neels Hofmeyr10ee73a2017-11-16 18:31:57 +0100237 /* attempt to mangle all '.' in identifiers to ':' for backwards compat */
Pau Espin Pedroldfc52a12018-06-27 16:53:31 +0200238 if (!rate_ctrl_group_desc_validate(desc)) {
Neels Hofmeyr10ee73a2017-11-16 18:31:57 +0100239 desc = rate_ctr_group_desc_mangle(group, desc);
240 if (!desc) {
241 talloc_free(group);
242 return NULL;
243 }
244 }
245
Harald Welte7b45d602010-05-13 11:35:30 +0200246 group->desc = desc;
Harald Welte087fcff2010-05-13 12:16:17 +0200247 group->idx = idx;
Harald Welte7b45d602010-05-13 11:35:30 +0200248
249 llist_add(&group->list, &rate_ctr_groups);
250
251 return group;
252}
253
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200254/*! Free the memory for the specified group of counters */
Harald Welte7b45d602010-05-13 11:35:30 +0200255void rate_ctr_group_free(struct rate_ctr_group *grp)
256{
Maxfb6f43e2019-03-18 15:41:26 +0100257 if (!grp)
258 return;
259
260 if (!llist_empty(&grp->list))
261 llist_del(&grp->list);
Harald Welte7b45d602010-05-13 11:35:30 +0200262 talloc_free(grp);
263}
264
Pau Espin Pedrol5fe3de52021-05-31 13:39:07 +0200265/*! Get rate counter from group, identified by index idx
266 * \param[in] grp Rate counter group
267 * \param[in] idx Index of the counter to retrieve
268 * \returns rate counter requested
269 */
270struct rate_ctr *rate_ctr_group_get_ctr(struct rate_ctr_group *grp, unsigned int idx)
271{
272 return &grp->ctr[idx];
273}
274
Pau Espin Pedrol09f075f2021-05-31 13:10:24 +0200275/*! Set a name for the group of counters be used instead of index value
276 at report time.
277 * \param[in] grp Rate counter group
278 * \param[in] name Name identifier to assign to the rate counter group
279 */
280void rate_ctr_group_set_name(struct rate_ctr_group *grp, const char *name)
281{
282 osmo_talloc_replace_string(grp, &grp->name, name);
283}
284
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200285/*! Add a number to the counter */
Harald Welte7b45d602010-05-13 11:35:30 +0200286void rate_ctr_add(struct rate_ctr *ctr, int inc)
287{
288 ctr->current += inc;
289}
290
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200291/*! Return the counter difference since the last call to this function */
Jacob Erlbeck423c1e52015-10-19 13:45:42 +0200292int64_t rate_ctr_difference(struct rate_ctr *ctr)
293{
294 int64_t result = ctr->current - ctr->previous;
295 ctr->previous = ctr->current;
296
297 return result;
298}
299
Jacob Erlbeckee702cd2015-11-10 11:38:25 +0100300/* TODO: support update intervals > 1s */
301/* TODO: implement this as a special stats reporter */
302
Harald Welte7b45d602010-05-13 11:35:30 +0200303static void interval_expired(struct rate_ctr *ctr, enum rate_ctr_intv intv)
304{
305 /* calculate rate over last interval */
306 ctr->intv[intv].rate = ctr->current - ctr->intv[intv].last;
307 /* save current counter for next interval */
308 ctr->intv[intv].last = ctr->current;
Harald Welted2dce6d2010-05-13 13:28:12 +0200309
310 /* update the rate of the next bigger interval. This will
311 * be overwritten when that next larger interval expires */
312 if (intv + 1 < ARRAY_SIZE(ctr->intv))
313 ctr->intv[intv+1].rate += ctr->intv[intv].rate;
Harald Welte7b45d602010-05-13 11:35:30 +0200314}
315
Max7c1b6cb2022-11-24 17:42:02 +0300316static struct osmo_fd rate_ctr_timer = { .fd = -1 };
Harald Welte7b45d602010-05-13 11:35:30 +0200317static uint64_t timer_ticks;
318
319/* The one-second interval has expired */
320static void rate_ctr_group_intv(struct rate_ctr_group *grp)
321{
322 unsigned int i;
323
324 for (i = 0; i < grp->desc->num_ctr; i++) {
325 struct rate_ctr *ctr = &grp->ctr[i];
326
327 interval_expired(ctr, RATE_CTR_INTV_SEC);
328 if ((timer_ticks % 60) == 0)
329 interval_expired(ctr, RATE_CTR_INTV_MIN);
330 if ((timer_ticks % (60*60)) == 0)
331 interval_expired(ctr, RATE_CTR_INTV_HOUR);
332 if ((timer_ticks % (24*60*60)) == 0)
333 interval_expired(ctr, RATE_CTR_INTV_DAY);
334 }
335}
336
Max7c1b6cb2022-11-24 17:42:02 +0300337static int rate_ctr_timer_cb(struct osmo_fd *ofd, unsigned int what)
Harald Welte7b45d602010-05-13 11:35:30 +0200338{
339 struct rate_ctr_group *ctrg;
Max7c1b6cb2022-11-24 17:42:02 +0300340 uint64_t expire_count;
341 int rc;
Harald Welte7b45d602010-05-13 11:35:30 +0200342
Max7c1b6cb2022-11-24 17:42:02 +0300343 /* check that the timer has actually expired */
344 if (!(what & OSMO_FD_READ))
345 return 0;
Harald Welte7b45d602010-05-13 11:35:30 +0200346
Max7c1b6cb2022-11-24 17:42:02 +0300347 /* read from timerfd: number of expirations of periodic timer */
348 rc = read(ofd->fd, (void *) &expire_count, sizeof(expire_count));
349 if (rc < 0 && errno == EAGAIN)
350 return 0;
Harald Welte7b45d602010-05-13 11:35:30 +0200351
Max7c1b6cb2022-11-24 17:42:02 +0300352 OSMO_ASSERT(rc == sizeof(expire_count));
353
354 if (expire_count > 1)
355 LOGP(DLGLOBAL, LOGL_NOTICE, "Stats timer expire_count=%" PRIu64 ": We missed %" PRIu64 " timers\n",
356 expire_count, expire_count - 1);
357
358 do { /* Increment number of ticks before we calculate intervals,
359 * as a counter value of 0 would already wrap all counters */
360 timer_ticks++;
361 llist_for_each_entry(ctrg, &rate_ctr_groups, list)
362 rate_ctr_group_intv(ctrg);
363 } while (--expire_count);
364
365 return 0;
Harald Welte7b45d602010-05-13 11:35:30 +0200366}
367
Harald Welte53de0d32017-10-16 14:09:16 +0200368/*! Initialize the counter module. Call this once from your application.
369 * \param[in] tall_ctx Talloc context from which rate_ctr_group will be allocated
370 * \returns 0 on success; negative on error */
Harald Welte7b45d602010-05-13 11:35:30 +0200371int rate_ctr_init(void *tall_ctx)
372{
Max7c1b6cb2022-11-24 17:42:02 +0300373 struct timespec ts_interval = { .tv_sec = 1, .tv_nsec = 0 };
374 int rc;
375
Harald Welte5eb67c22021-11-14 20:45:07 +0100376 /* ignore repeated initialization */
Max7c1b6cb2022-11-24 17:42:02 +0300377 if (osmo_fd_is_registered(&rate_ctr_timer))
Harald Welte5eb67c22021-11-14 20:45:07 +0100378 return 0;
379
Harald Welte7b45d602010-05-13 11:35:30 +0200380 tall_rate_ctr_ctx = tall_ctx;
Max7c1b6cb2022-11-24 17:42:02 +0300381
382 rc = osmo_timerfd_setup(&rate_ctr_timer, rate_ctr_timer_cb, NULL);
383 if (rc < 0) {
384 LOGP(DLGLOBAL, LOGL_ERROR, "Failed to setup the timer with error code %d (fd=%d)\n",
385 rc, rate_ctr_timer.fd);
386 return rc;
387 }
388
389 rc = osmo_timerfd_schedule(&rate_ctr_timer, NULL, &ts_interval);
390 if (rc < 0) {
391 LOGP(DLGLOBAL, LOGL_ERROR, "Failed to schedule the timer with error code %d (fd=%d)\n",
392 rc, rate_ctr_timer.fd);
393 }
Harald Welte7b45d602010-05-13 11:35:30 +0200394
395 return 0;
396}
Daniel Willmann2d42dde2011-04-08 10:46:18 +0200397
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200398/*! Search for counter group based on group name and index
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200399 * \param[in] name Name of the counter group you're looking for
400 * \param[in] idx Index inside the counter group
401 * \returns \ref rate_ctr_group or NULL in case of error */
Daniel Willmann2d42dde2011-04-08 10:46:18 +0200402struct rate_ctr_group *rate_ctr_get_group_by_name_idx(const char *name, const unsigned int idx)
403{
404 struct rate_ctr_group *ctrg;
405
406 llist_for_each_entry(ctrg, &rate_ctr_groups, list) {
407 if (!ctrg->desc)
408 continue;
409
410 if (!strcmp(ctrg->desc->group_name_prefix, name) &&
411 ctrg->idx == idx) {
412 return ctrg;
413 }
414 }
415 return NULL;
416}
417
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200418/*! Search for counter based on group + name
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200419 * \param[in] ctrg pointer to \ref rate_ctr_group
420 * \param[in] name name of counter inside group
Harald Welte53de0d32017-10-16 14:09:16 +0200421 * \returns \ref rate_ctr or NULL in case of error
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200422 */
Holger Hans Peter Freythera9f526a2011-04-18 16:45:45 +0200423const struct rate_ctr *rate_ctr_get_by_name(const struct rate_ctr_group *ctrg, const char *name)
Daniel Willmann2d42dde2011-04-08 10:46:18 +0200424{
425 int i;
Holger Hans Peter Freythera9f526a2011-04-18 16:45:45 +0200426 const struct rate_ctr_desc *ctr_desc;
Daniel Willmann2d42dde2011-04-08 10:46:18 +0200427
428 if (!ctrg->desc)
429 return NULL;
430
431 for (i = 0; i < ctrg->desc->num_ctr; i++) {
432 ctr_desc = &ctrg->desc->ctr_desc[i];
433
434 if (!strcmp(ctr_desc->name, name)) {
435 return &ctrg->ctr[i];
436 }
437 }
438 return NULL;
439}
Harald Welte9327c6d2011-08-17 16:06:06 +0200440
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200441/*! Iterate over each counter in group and call function
Vadim Yanitskiyc7610442019-03-26 00:48:30 +0700442 * \param[in] ctrg counter group over which to iterate
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200443 * \param[in] handle_counter function pointer
Vadim Yanitskiyed3a3c62019-03-26 00:57:45 +0700444 * \param[in] data Data to hand transparently to handle_counter()
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200445 * \returns 0 on success; negative otherwise
446 */
Jacob Erlbeck423c1e52015-10-19 13:45:42 +0200447int rate_ctr_for_each_counter(struct rate_ctr_group *ctrg,
448 rate_ctr_handler_t handle_counter, void *data)
449{
450 int rc = 0;
451 int i;
452
453 for (i = 0; i < ctrg->desc->num_ctr; i++) {
454 struct rate_ctr *ctr = &ctrg->ctr[i];
455 rc = handle_counter(ctrg,
456 ctr, &ctrg->desc->ctr_desc[i], data);
457 if (rc < 0)
458 return rc;
459 }
460
461 return rc;
462}
463
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200464/*! Iterate over all counter groups
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200465 * \param[in] handle_group function pointer of callback function
Vadim Yanitskiyed3a3c62019-03-26 00:57:45 +0700466 * \param[in] data Data to hand transparently to handle_group()
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200467 * \returns 0 on success; negative otherwise
468 */
Jacob Erlbeck423c1e52015-10-19 13:45:42 +0200469int rate_ctr_for_each_group(rate_ctr_group_handler_t handle_group, void *data)
470{
471 struct rate_ctr_group *statg;
472 int rc = 0;
473
474 llist_for_each_entry(statg, &rate_ctr_groups, list) {
475 rc = handle_group(statg, data);
476 if (rc < 0)
477 return rc;
478 }
479
480 return rc;
481}
482
Daniel Willmann26a95392020-07-14 18:04:18 +0200483/*! Reset a rate counter back to zero
484 * \param[in] ctr counter to reset
485 */
486void rate_ctr_reset(struct rate_ctr *ctr)
487{
488 memset(ctr, 0, sizeof(*ctr));
489}
490
491/*! Reset all counters in a group
492 * \param[in] ctrg counter group to reset
493 */
494void rate_ctr_group_reset(struct rate_ctr_group *ctrg)
495{
496 int i;
497
498 for (i = 0; i < ctrg->desc->num_ctr; i++) {
499 struct rate_ctr *ctr = &ctrg->ctr[i];
500 rate_ctr_reset(ctr);
501 }
502}
503
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200504/*! @} */