blob: 36fdee46db894e7fa70d84bbbbf7a5e39da3ecad [file] [log] [blame]
Neels Hofmeyr25c97412021-11-13 23:19:33 +01001/*! \file time_cc.h
2 * Report the cumulative counter of time for which a flag is true as rate counter.
3 */
4/* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
5 *
6 * All Rights Reserved
7 *
8 * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Affero General Public License as published by
12 * the Free Software Foundation; either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Affero General Public License for more details.
19 *
20 * You should have received a copy of the GNU Affero General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 *
23 */
24#pragma once
25
26#include <stdint.h>
27
28#include <osmocom/core/timer.h>
29
30/*! \defgroup time_cc Cumulative counter of time as rate counter.
31 * @{
32 * \file time_cc.h
33 */
34
35struct osmo_tdef;
36struct rate_ctr;
37
38/*! Configuration for osmo_time_cc.
39 * Report the cumulative counter of time for which a flag is true as rate counter.
40 * For example, for each second that the flag is true, increment a rate counter.
41 *
42 * The flag to be monitored is reported by osmo_time_cc_set_flag().
43 *
44 * The granularity defines how much time one rate counter increment represents:
45 * the default configuration is gran_usec = 1000000, i.e. one rate counter increment represents one second.
46 *
47 * Reporting as rate counter is configurable by round_threshold_usec and forget_sum_usec, examples:
48 *
49 * round_threshold_usec:
50 * - To get "ceil()" behavior, set round_threshold_usec = 1. This increments the rate counter for each gran_usec period
51 * where the flag was seen true, even if it was true for only a very short fraction of a gran_usec period.
52 * - To get "round()" behavior, set round_threshold_usec = half of gran_usec. The rate counter increments when the flag
53 * has been true for 0.5 of a gran_usec (and then again at 1.5 * gran_usec) of 'true' flag. round_threshold_usec = 0
54 * is a special value that means to use half of gran_usec.
55 * - To get "floor()" behavior, set round_threshold_usec >= gran_usec. The rate counter increments when reaching full
56 * gran_usec periods of the flag being true.
57 *
58 * forget_sum_usec:
59 * This is a tradeoff between the accuracy of the reported rate counter and making sure that the events reported are not
60 * irrelevantly long ago.
61 * - To keep sub-granularity-period surplus time forever, set forget_sum_usec = 0.
62 * - To keep surplus time for up to a minute, set forget_sum_usec = 60000000 (60 seconds).
63 * - To get rid of "leftover" time (almost) immediately after the flag goes false, set forget_sum_usec = 1.
64 * - If gran_usec is set to one second and forget_sum_usec is set to one minute, the reported rate counter has a
65 * possible inaccuracy of 1/60th, but makes sure that no timings older than a minute affect the current reports.
66 *
67 * Reporting modes in detail:
68 *
69 * The rate_ctr increments when the cumulative counter passes round_threshold_usec (default: half of gran_usec).
70 *
71 * sum ^
72 * | ________
73 * | /
74 * | /
75 * | /
76 * 3*gran --+--------------------------------------+
77 * | /:
78 * | / :
79 * | - - - - - - - - - - - - - - - - - / :
80 * | /. :
81 * | / . :
82 * 2*gran --+--------------------------------+ . :
83 * | /: . :
84 * | / : . :
85 * | - - - - - - - - - -_________/ : . :
86 * | / . : . :
87 * | / . : . :
88 * 1*gran --+-----------------+ . : . :
89 * | /: . : . :
90 * | / : . : . :
91 * | - - - - - - -/ : . : . :
92 * | /. : . : . :
93 * | ....-------' . : . : . :
94 * 0 +------------------------------------------------------------------------> elapsed time
95 * . : . : . :
96 * _ _ _______ ____________
97 * flag: __| |_| |____| . : |_______|. : . : |__________
98 * f t f t f t . : f t. : . : f
99 * round_threshold_usec : . : . : . :
100 * = 1 usec: 0 1 . :2 . :3 . :4 = "ceil()"
101 * = 0 == gran_usec/2: 0 1 : 2 : 3 : = "round()"
102 * >= gran_usec: 0 1 2 3 = "floor()"
103 *
104 */
105struct osmo_time_cc_cfg {
106 /*! Granularity in microseconds: nr of microseconds that one rate_ctr increment represents. A typical value is
107 * gran_usec = 1000000, meaning one rate counter increment represents one second. When zero, use 1000000. */
108 uint64_t gran_usec;
109 /*! Nr of microseconds above n * gran_usec at which to trigger a counter increment. When zero, use half a
110 * gran_usec. */
111 uint64_t round_threshold_usec;
112 /*! Forget counted sub-gran time after the flag was false for this long. */
113 uint64_t forget_sum_usec;
114 /*! Rate counter to report to, or NULL to not use it. */
115 struct rate_ctr *rate_ctr;
116
117 /*! Update gran_usec from this T timer value, or zero to not use any T timer. */
118 int T_gran;
119 /*! Update round_threshold_usec from this T timer value, or zero to not use any T timer. */
120 int T_round_threshold;
121 /*! Update forget_sum_usec from this T timer value, or zero to not use any T timer. */
122 int T_forget_sum;
123 /*! Look up T_gran and T_forget_sum in this list of timers, or NULL to not use any T timers. */
124 struct osmo_tdef *T_defs;
125};
126
127/*! Report the cumulative counter of time for which a flag is true as rate counter.
128 * See also osmo_time_cc_cfg for details on configuring.
129 *
130 * Usage:
131 *
132 * struct my_obj {
133 * struct osmo_time_cc flag_cc;
134 * };
135 *
136 * void my_obj_init(struct my_obj *my_obj)
137 * {
138 * osmo_time_cc_init(&my_obj->flag_cc);
139 * my_obj->flag_cc.cfg = (struct osmo_time_cc_cfg){
140 * .gran_usec = 1000000,
141 * .forget_sum_usec = 60000000,
142 * .rate_ctr = rate_ctr_group_get_ctr(my_ctrg, MY_CTR_IDX),
143 * };
144 * // optional: set initial flag state, default is 'false':
145 * // osmo_time_cc_set_flag(&my_obj->flag_cc, false);
146 * }
147 *
148 * void my_obj_event(struct my_obj *my_obj, bool flag)
149 * {
150 * osmo_time_cc_set_flag(&my_obj->flag_cc, flag);
151 * }
152 *
153 * void my_obj_destruct(struct my_obj *my_obj)
154 * {
155 * osmo_time_cc_cleanup(&my_obj->flag_cc);
156 * }
157 */
158struct osmo_time_cc {
159 struct osmo_time_cc_cfg cfg;
160
161 bool flag_state;
162
163 /*! Overall cumulative sum. Does not get reset for the entire lifetime of an osmo_time_cc.
164 * (Informational only, not used by the osmo_time_cc implementation.) */
165 uint64_t total_sum;
166
167 struct osmo_timer_list timer;
168
169 /*! CLOCK_MONOTONIC reading in microseconds, at the time when the osmo_time_cc instance started counting. */
170 uint64_t start_time;
171 /*! CLOCK_MONOTONIC reading in microseconds, at the time when the osmo_time_cc last evaluated the flag state and
172 * possibly added to the cumulated sum. */
173 uint64_t last_counted_time;
174
175 /*! Internal cumulative counter of time that flag_state was true. It may get reset to zero regularly, depending
176 * on cfg.forget_sum_usec. This is the basis for incrementing cfg.rate_ctr. */
177 uint64_t sum;
178 /*! The amount of time that already reported cfg.rate_ctr increments account for. This may be ahead of or behind
179 * 'sum', depending on cfg.round_threshold_usec. */
180 uint64_t reported_sum;
181};
182
183void osmo_time_cc_init(struct osmo_time_cc *tc);
184void osmo_time_cc_set_flag(struct osmo_time_cc *tc, bool flag);
185void osmo_time_cc_cleanup(struct osmo_time_cc *tc);
186
187/*! @} */