blob: a7467c15d9f9eec592b1764cafb41848c66b5c27 [file] [log] [blame]
Pau Espin Pedrol4456b6f2019-05-24 16:54:19 +02001/*
2 * Copyright (C) 2019 sysmocom - s.f.m.c. GmbH
3 * All Rights Reserved
4 *
Pau Espin Pedrole9ce77b2019-06-25 12:29:01 +02005 * SPDX-License-Identifier: AGPL-3.0+
6 *
Pau Espin Pedrol4456b6f2019-05-24 16:54:19 +02007 * Author: Pau Espin Pedrol <pespin@sysmocom.de>
8 *
Pau Espin Pedrole9ce77b2019-06-25 12:29:01 +02009 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU Affero General Public License as published by
11 * the Free Software Foundation, either version 3 of the License, or
Pau Espin Pedrol4456b6f2019-05-24 16:54:19 +020012 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Pau Espin Pedrole9ce77b2019-06-25 12:29:01 +020017 * GNU Affero General Public License for more details.
Pau Espin Pedrol4456b6f2019-05-24 16:54:19 +020018 *
Pau Espin Pedrole9ce77b2019-06-25 12:29:01 +020019 * You should have received a copy of the GNU Affero General Public License
Pau Espin Pedrol4456b6f2019-05-24 16:54:19 +020020 * along with this program. If not, see <http://www.gnu.org/licenses/>.
Pau Espin Pedrole9ce77b2019-06-25 12:29:01 +020021 * See the COPYING file in the main directory for details.
Pau Espin Pedrol4456b6f2019-05-24 16:54:19 +020022 */
23
24/*
25 * rate_ctr API uses several osmocom select loop features, and as a result,
26 * calls to it must be done through the main thread (the one running the osmocom
27 * loop in osmo-trx).
28 * Since read/write from/to SDR is done in separate threads (even read and write
29 * each use a different thread), we must use some sort of message passing system
30 * between main thread feeding rate_ctr structures and the Rx/Tx threads
31 * generating the events.
32 * The idea is that upon read/write issues, lower layers (SDR APIs) provide us with
33 * underrun/overrun/droppedPackets information, and in that case we pass that up
34 * the stack through signal <SS_DEVICE,S_DEVICE_COUNTER_CHANGE> with signal_cb
35 * being a pointer to a "struct device_counters" structure, which contains
36 * device (implementation agnostic) statful counters for different kind of
37 * statistics.
38 * That signal is processed here in device_sig_cb, where a copy of the "struct
39 * device_counters" structure is held and the main thread is instructed through
40 * a timerfd to update rate_ctr APIs against this copy. All this is done inside
Martin Hauke066fd042019-10-13 19:08:00 +020041 * a mutex to avoid different race conditions (between Rx andTx threads, and
Pau Espin Pedrol4456b6f2019-05-24 16:54:19 +020042 * between Rx/Tx and main thread). For the same reason, callers of signal
43 * <SS_DEVICE,S_DEVICE_COUNTER_CHANGE> (device_sig_cb), that is Rx/Tx threads,
44 * must do so with PTHREAD_CANCEL_DISABLE, in order to avoid possible deadlocks
45 * in case the main thread decides to cancel other threads due to a shutdown
46 * operation (fi SIGKILL received)
47 */
48
49#include <string.h>
50#include <stdint.h>
51#include <inttypes.h>
52#include <netinet/in.h>
53#include <arpa/inet.h>
54
55extern "C" {
56#include <osmocom/core/talloc.h>
57#include <osmocom/core/utils.h>
58#include <osmocom/core/rate_ctr.h>
59#include <osmocom/core/select.h>
60#include <osmocom/core/stats.h>
Pau Espin Pedrol6a305fe2019-05-24 19:58:20 +020061#include <osmocom/core/timer.h>
Pau Espin Pedrol4456b6f2019-05-24 16:54:19 +020062
63#include "osmo_signal.h"
64#include "trx_vty.h"
65#include "trx_rate_ctr.h"
66}
67#include "Threads.h"
68#include "Logger.h"
69
Pau Espin Pedrol6a3a2b82020-06-29 13:52:26 +020070/* Used in dev_ctrs_pending, when set it means that channel slot contains unused
Pau Espin Pedrol4456b6f2019-05-24 16:54:19 +020071 (non-pending) counter data */
72#define PENDING_CHAN_NONE SIZE_MAX
73
Pau Espin Pedrol6a305fe2019-05-24 19:58:20 +020074static void *trx_rate_ctr_ctx;
75
Pau Espin Pedrol4456b6f2019-05-24 16:54:19 +020076static struct rate_ctr_group** rate_ctrs;
Pau Espin Pedrol6a3a2b82020-06-29 13:52:26 +020077static struct device_counters* dev_ctrs_pending;
Pau Espin Pedrol92ba59d2020-06-29 14:34:59 +020078static struct trx_counters* trx_ctrs_pending;
Pau Espin Pedrol4456b6f2019-05-24 16:54:19 +020079static size_t chan_len;
Pau Espin Pedrol6a3a2b82020-06-29 13:52:26 +020080static struct osmo_fd dev_rate_ctr_timerfd;
Pau Espin Pedrol92ba59d2020-06-29 14:34:59 +020081static struct osmo_fd trx_rate_ctr_timerfd;
Pau Espin Pedrol6a3a2b82020-06-29 13:52:26 +020082static Mutex dev_rate_ctr_mutex;
Pau Espin Pedrol92ba59d2020-06-29 14:34:59 +020083static Mutex trx_rate_ctr_mutex;
Pau Espin Pedrol4456b6f2019-05-24 16:54:19 +020084
Pau Espin Pedrol6a305fe2019-05-24 19:58:20 +020085struct osmo_timer_list threshold_timer;
86static LLIST_HEAD(threshold_list);
Pau Espin Pedrolfb967672020-06-29 16:44:23 +020087static unsigned int threshold_timer_sched_secs;
Pau Espin Pedrol6a305fe2019-05-24 19:58:20 +020088static bool threshold_initied;
89
90const struct value_string rate_ctr_intv[] = {
91 { RATE_CTR_INTV_SEC, "per-second" },
92 { RATE_CTR_INTV_MIN, "per-minute" },
93 { RATE_CTR_INTV_HOUR, "per-hour" },
94 { RATE_CTR_INTV_DAY, "per-day" },
95 { 0, NULL }
96};
97
98const struct value_string trx_chan_ctr_names[] = {
Pau Espin Pedrol6a3a2b82020-06-29 13:52:26 +020099 { TRX_CTR_DEV_RX_OVERRUNS, "rx_overruns" },
100 { TRX_CTR_DEV_TX_UNDERRUNS, "tx_underruns" },
101 { TRX_CTR_DEV_RX_DROP_EV, "rx_drop_events" },
102 { TRX_CTR_DEV_RX_DROP_SMPL, "rx_drop_samples" },
103 { TRX_CTR_DEV_TX_DROP_EV, "tx_drop_events" },
104 { TRX_CTR_DEV_TX_DROP_SMPL, "tx_drop_samples" },
Pau Espin Pedrol92ba59d2020-06-29 14:34:59 +0200105 { TRX_CTR_TRX_TX_STALE_BURSTS, "tx_stale_bursts" },
Pau Espin Pedrol1d0c6fe2020-07-09 18:09:10 +0200106 { TRX_CTR_TRX_TX_UNAVAILABLE_BURSTS, "tx_unavailable_bursts" },
Pau Espin Pedrolc0d6fd22020-07-09 16:51:47 +0200107 { TRX_CTR_TRX_TRXD_FN_REPEATED, "tx_trxd_fn_repeated" },
108 { TRX_CTR_TRX_TRXD_FN_OUTOFORDER, "tx_trxd_fn_outoforder" },
109 { TRX_CTR_TRX_TRXD_FN_SKIPPED, "tx_trxd_fn_skipped" },
Pau Espin Pedrol1d165a02020-07-27 11:52:42 +0200110 { TRX_CTR_TRX_RX_EMPTY_BURST, "rx_empty_burst" },
111 { TRX_CTR_TRX_RX_CLIPPING, "rx_clipping" },
112 { TRX_CTR_TRX_RX_NO_BURST_DETECTED, "rx_no_burst_detected" },
Pau Espin Pedrol6a305fe2019-05-24 19:58:20 +0200113 { 0, NULL }
Pau Espin Pedrol4456b6f2019-05-24 16:54:19 +0200114};
115
116static const struct rate_ctr_desc trx_chan_ctr_desc[] = {
Pau Espin Pedrol6a3a2b82020-06-29 13:52:26 +0200117 [TRX_CTR_DEV_RX_OVERRUNS] = { "device:rx_overruns", "Number of Rx overruns in FIFO queue" },
118 [TRX_CTR_DEV_TX_UNDERRUNS] = { "device:tx_underruns", "Number of Tx underruns in FIFO queue" },
119 [TRX_CTR_DEV_RX_DROP_EV] = { "device:rx_drop_events", "Number of times Rx samples were dropped by HW" },
120 [TRX_CTR_DEV_RX_DROP_SMPL] = { "device:rx_drop_samples", "Number of Rx samples dropped by HW" },
121 [TRX_CTR_DEV_TX_DROP_EV] = { "device:tx_drop_events", "Number of times Tx samples were dropped by HW" },
Pau Espin Pedrol92ba59d2020-06-29 14:34:59 +0200122 [TRX_CTR_DEV_TX_DROP_SMPL] = { "device:tx_drop_samples", "Number of Tx samples dropped by HW" },
123 [TRX_CTR_TRX_TX_STALE_BURSTS] = { "trx:tx_stale_bursts", "Number of Tx burts dropped by TRX due to arriving too late" },
Pau Espin Pedrol1d0c6fe2020-07-09 18:09:10 +0200124 [TRX_CTR_TRX_TX_UNAVAILABLE_BURSTS] = { "trx:tx_unavailable_bursts","Number of Tx burts unavailable (not enqueued) at the time they should be transmitted" },
Pau Espin Pedrolc0d6fd22020-07-09 16:51:47 +0200125 [TRX_CTR_TRX_TRXD_FN_REPEATED] = { "trx:tx_trxd_fn_repeated", "Number of Tx burts received from TRXD with repeated FN" },
126 [TRX_CTR_TRX_TRXD_FN_OUTOFORDER] = { "trx:tx_trxd_fn_outoforder","Number of Tx burts received from TRXD with a past FN" },
127 [TRX_CTR_TRX_TRXD_FN_SKIPPED] = { "trx:tx_trxd_fn_skipped", "Number of Tx burts potentially skipped due to FN jumps" },
Pau Espin Pedrol1d165a02020-07-27 11:52:42 +0200128 [TRX_CTR_TRX_RX_EMPTY_BURST] = { "trx:rx_empty_burst", "Number of Rx bursts empty" },
129 [TRX_CTR_TRX_RX_CLIPPING] = { "trx:rx_clipping", "Number of Rx bursts discarded due to clipping" },
130 [TRX_CTR_TRX_RX_NO_BURST_DETECTED] = { "trx:rx_no_burst_detected", "Number of Rx burts discarded due to burst detection error" },
Pau Espin Pedrol4456b6f2019-05-24 16:54:19 +0200131};
132
133static const struct rate_ctr_group_desc trx_chan_ctr_group_desc = {
134 .group_name_prefix = "trx:chan",
135 .group_description = "osmo-trx statistics",
136 .class_id = OSMO_STATS_CLASS_GLOBAL,
137 .num_ctr = ARRAY_SIZE(trx_chan_ctr_desc),
138 .ctr_desc = trx_chan_ctr_desc,
139};
140
Pau Espin Pedrol6a3a2b82020-06-29 13:52:26 +0200141static int dev_rate_ctr_timerfd_cb(struct osmo_fd *ofd, unsigned int what) {
Pau Espin Pedrol4456b6f2019-05-24 16:54:19 +0200142 size_t chan;
143 struct rate_ctr *ctr;
Pau Espin Pedrol761da1a2020-07-17 18:29:20 +0200144 LOGC(DCTR, INFO) << "Main thread is updating Device counters";
Pau Espin Pedrol6a3a2b82020-06-29 13:52:26 +0200145 dev_rate_ctr_mutex.lock();
Pau Espin Pedrol4456b6f2019-05-24 16:54:19 +0200146 for (chan = 0; chan < chan_len; chan++) {
Pau Espin Pedrol6a3a2b82020-06-29 13:52:26 +0200147 if (dev_ctrs_pending[chan].chan == PENDING_CHAN_NONE)
Pau Espin Pedrol4456b6f2019-05-24 16:54:19 +0200148 continue;
Pau Espin Pedrol761da1a2020-07-17 18:29:20 +0200149 LOGCHAN(chan, DCTR, DEBUG) << "rate_ctr update";
Pau Espin Pedrol0a038222021-06-04 17:21:42 +0200150 ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_DEV_RX_OVERRUNS);
Pau Espin Pedrol6a3a2b82020-06-29 13:52:26 +0200151 rate_ctr_add(ctr, dev_ctrs_pending[chan].rx_overruns - ctr->current);
Pau Espin Pedrol0a038222021-06-04 17:21:42 +0200152 ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_DEV_TX_UNDERRUNS);
Pau Espin Pedrol6a3a2b82020-06-29 13:52:26 +0200153 rate_ctr_add(ctr, dev_ctrs_pending[chan].tx_underruns - ctr->current);
Pau Espin Pedrol0a038222021-06-04 17:21:42 +0200154 ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_DEV_RX_DROP_EV);
Pau Espin Pedrol6a3a2b82020-06-29 13:52:26 +0200155 rate_ctr_add(ctr, dev_ctrs_pending[chan].rx_dropped_events - ctr->current);
Pau Espin Pedrol0a038222021-06-04 17:21:42 +0200156 ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_DEV_RX_DROP_SMPL);
Pau Espin Pedrol6a3a2b82020-06-29 13:52:26 +0200157 rate_ctr_add(ctr, dev_ctrs_pending[chan].rx_dropped_samples - ctr->current);
Pau Espin Pedrol0a038222021-06-04 17:21:42 +0200158 ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_DEV_TX_DROP_EV);
Pau Espin Pedrol6a3a2b82020-06-29 13:52:26 +0200159 rate_ctr_add(ctr, dev_ctrs_pending[chan].tx_dropped_events - ctr->current);
Pau Espin Pedrol0a038222021-06-04 17:21:42 +0200160 ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_DEV_TX_DROP_SMPL);
Pau Espin Pedrol6a3a2b82020-06-29 13:52:26 +0200161 rate_ctr_add(ctr, dev_ctrs_pending[chan].tx_dropped_samples - ctr->current);
Pau Espin Pedrol4456b6f2019-05-24 16:54:19 +0200162
163 /* Mark as done */
Pau Espin Pedrol6a3a2b82020-06-29 13:52:26 +0200164 dev_ctrs_pending[chan].chan = PENDING_CHAN_NONE;
Pau Espin Pedrol4456b6f2019-05-24 16:54:19 +0200165 }
Pau Espin Pedrol6a3a2b82020-06-29 13:52:26 +0200166 if (osmo_timerfd_disable(&dev_rate_ctr_timerfd) < 0)
Pau Espin Pedrol761da1a2020-07-17 18:29:20 +0200167 LOGC(DCTR, ERROR) << "Failed to disable timerfd";
Pau Espin Pedrol6a3a2b82020-06-29 13:52:26 +0200168 dev_rate_ctr_mutex.unlock();
Pau Espin Pedrol4456b6f2019-05-24 16:54:19 +0200169 return 0;
170}
171
Pau Espin Pedrol92ba59d2020-06-29 14:34:59 +0200172static int trx_rate_ctr_timerfd_cb(struct osmo_fd *ofd, unsigned int what) {
173 size_t chan;
174 struct rate_ctr *ctr;
Pau Espin Pedrol761da1a2020-07-17 18:29:20 +0200175 LOGC(DCTR, INFO) << "Main thread is updating Transceiver counters";
Pau Espin Pedrol032c1d82020-07-10 17:20:45 +0200176 trx_rate_ctr_mutex.lock();
Pau Espin Pedrol92ba59d2020-06-29 14:34:59 +0200177 for (chan = 0; chan < chan_len; chan++) {
178 if (trx_ctrs_pending[chan].chan == PENDING_CHAN_NONE)
179 continue;
Pau Espin Pedrol761da1a2020-07-17 18:29:20 +0200180 LOGCHAN(chan, DCTR, DEBUG) << "rate_ctr update";
Pau Espin Pedrol0a038222021-06-04 17:21:42 +0200181 ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_TRX_TX_STALE_BURSTS);
Pau Espin Pedrol92ba59d2020-06-29 14:34:59 +0200182 rate_ctr_add(ctr, trx_ctrs_pending[chan].tx_stale_bursts - ctr->current);
Pau Espin Pedrol0a038222021-06-04 17:21:42 +0200183 ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_TRX_TX_UNAVAILABLE_BURSTS);
Pau Espin Pedrol1d0c6fe2020-07-09 18:09:10 +0200184 rate_ctr_add(ctr, trx_ctrs_pending[chan].tx_unavailable_bursts - ctr->current);
Pau Espin Pedrol0a038222021-06-04 17:21:42 +0200185 ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_TRX_TRXD_FN_REPEATED);
Pau Espin Pedrolc0d6fd22020-07-09 16:51:47 +0200186 rate_ctr_add(ctr, trx_ctrs_pending[chan].tx_trxd_fn_repeated - ctr->current);
Pau Espin Pedrol0a038222021-06-04 17:21:42 +0200187 ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_TRX_TRXD_FN_OUTOFORDER);
Pau Espin Pedrolc0d6fd22020-07-09 16:51:47 +0200188 rate_ctr_add(ctr, trx_ctrs_pending[chan].tx_trxd_fn_outoforder - ctr->current);
Pau Espin Pedrol0a038222021-06-04 17:21:42 +0200189 ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_TRX_TRXD_FN_SKIPPED);
Pau Espin Pedrolc0d6fd22020-07-09 16:51:47 +0200190 rate_ctr_add(ctr, trx_ctrs_pending[chan].tx_trxd_fn_skipped - ctr->current);
Pau Espin Pedrol0a038222021-06-04 17:21:42 +0200191 ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_TRX_RX_EMPTY_BURST);
Pau Espin Pedrol1d165a02020-07-27 11:52:42 +0200192 rate_ctr_add(ctr, trx_ctrs_pending[chan].rx_empty_burst - ctr->current);
Pau Espin Pedrol0a038222021-06-04 17:21:42 +0200193 ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_TRX_RX_CLIPPING);
Pau Espin Pedrol1d165a02020-07-27 11:52:42 +0200194 rate_ctr_add(ctr, trx_ctrs_pending[chan].rx_clipping - ctr->current);
Pau Espin Pedrol0a038222021-06-04 17:21:42 +0200195 ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_TRX_RX_NO_BURST_DETECTED);
Pau Espin Pedrol1d165a02020-07-27 11:52:42 +0200196 rate_ctr_add(ctr, trx_ctrs_pending[chan].rx_no_burst_detected - ctr->current);
Pau Espin Pedrol92ba59d2020-06-29 14:34:59 +0200197 /* Mark as done */
198 trx_ctrs_pending[chan].chan = PENDING_CHAN_NONE;
199 }
200 if (osmo_timerfd_disable(&trx_rate_ctr_timerfd) < 0)
Pau Espin Pedrol761da1a2020-07-17 18:29:20 +0200201 LOGC(DCTR, ERROR) << "Failed to disable timerfd";
Pau Espin Pedrol92ba59d2020-06-29 14:34:59 +0200202 trx_rate_ctr_mutex.unlock();
203 return 0;
204}
205
Pau Espin Pedrol4456b6f2019-05-24 16:54:19 +0200206/* Callback function to be called every time we receive a signal from DEVICE */
207static int device_sig_cb(unsigned int subsys, unsigned int signal,
208 void *handler_data, void *signal_data)
209{
Pau Espin Pedrol92ba59d2020-06-29 14:34:59 +0200210 struct device_counters *dev_ctr;
211 struct trx_counters *trx_ctr;
Pau Espin Pedrol4456b6f2019-05-24 16:54:19 +0200212 /* Delay sched around 20 ms, in case we receive several calls from several
213 * channels batched */
214 struct timespec next_sched = {.tv_sec = 0, .tv_nsec = 20*1000*1000};
215 /* no automatic re-trigger */
216 struct timespec intv_sched = {.tv_sec = 0, .tv_nsec = 0};
Vadim Yanitskiy5e40d922021-10-25 12:56:06 +0300217 char err_buf[256];
Pau Espin Pedrol4456b6f2019-05-24 16:54:19 +0200218
219 switch (signal) {
220 case S_DEVICE_COUNTER_CHANGE:
Pau Espin Pedrol92ba59d2020-06-29 14:34:59 +0200221 dev_ctr = (struct device_counters *)signal_data;
Pau Espin Pedrol761da1a2020-07-17 18:29:20 +0200222 LOGCHAN(dev_ctr->chan, DCTR, INFO) << "Received counter change from radioDevice";
Pau Espin Pedrol6a3a2b82020-06-29 13:52:26 +0200223 dev_rate_ctr_mutex.lock();
Pau Espin Pedrol92ba59d2020-06-29 14:34:59 +0200224 dev_ctrs_pending[dev_ctr->chan] = *dev_ctr;
Pau Espin Pedrol6a3a2b82020-06-29 13:52:26 +0200225 if (osmo_timerfd_schedule(&dev_rate_ctr_timerfd, &next_sched, &intv_sched) < 0) {
Vadim Yanitskiy5e40d922021-10-25 12:56:06 +0300226 LOGC(DCTR, ERROR) << "Failed to schedule timerfd: " << errno
227 << " = "<< strerror_r(errno, err_buf, sizeof(err_buf));
Pau Espin Pedrol4456b6f2019-05-24 16:54:19 +0200228 }
Pau Espin Pedrol6a3a2b82020-06-29 13:52:26 +0200229 dev_rate_ctr_mutex.unlock();
Pau Espin Pedrol4456b6f2019-05-24 16:54:19 +0200230 break;
Pau Espin Pedrol92ba59d2020-06-29 14:34:59 +0200231 case S_TRX_COUNTER_CHANGE:
232 trx_ctr = (struct trx_counters *)signal_data;
Pau Espin Pedrol761da1a2020-07-17 18:29:20 +0200233 LOGCHAN(trx_ctr->chan, DCTR, INFO) << "Received counter change from Transceiver";
Pau Espin Pedrol92ba59d2020-06-29 14:34:59 +0200234 trx_rate_ctr_mutex.lock();
235 trx_ctrs_pending[trx_ctr->chan] = *trx_ctr;
236 if (osmo_timerfd_schedule(&trx_rate_ctr_timerfd, &next_sched, &intv_sched) < 0) {
Vadim Yanitskiy5e40d922021-10-25 12:56:06 +0300237 LOGC(DCTR, ERROR) << "Failed to schedule timerfd: " << errno
238 << " = "<< strerror_r(errno, err_buf, sizeof(err_buf));
Pau Espin Pedrol92ba59d2020-06-29 14:34:59 +0200239 }
240 trx_rate_ctr_mutex.unlock();
241 break;
Pau Espin Pedrol4456b6f2019-05-24 16:54:19 +0200242 default:
243 break;
244 }
245 return 0;
246}
247
Pau Espin Pedrol6a305fe2019-05-24 19:58:20 +0200248/************************************
249 * ctr_threshold APIs
250 ************************************/
251static const char* ctr_threshold_2_vty_str(struct ctr_threshold *ctr)
252{
253 static char buf[256];
254 int rc = 0;
255 rc += snprintf(buf, sizeof(buf), "ctr-error-threshold %s", get_value_string(trx_chan_ctr_names, ctr->ctr_id));
256 rc += snprintf(buf + rc, sizeof(buf) - rc, " %d %s", ctr->val, get_value_string(rate_ctr_intv, ctr->intv));
257 return buf;
258}
259
260static void threshold_timer_cb(void *data)
261{
262 struct ctr_threshold *ctr_thr;
263 struct rate_ctr *rate_ctr;
264 size_t chan;
Pau Espin Pedrol761da1a2020-07-17 18:29:20 +0200265 LOGC(DCTR, DEBUG) << "threshold_timer_cb fired!";
Pau Espin Pedrol6a305fe2019-05-24 19:58:20 +0200266
267 llist_for_each_entry(ctr_thr, &threshold_list, list) {
268 for (chan = 0; chan < chan_len; chan++) {
Pau Espin Pedrol0a038222021-06-04 17:21:42 +0200269 rate_ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], ctr_thr->ctr_id);
Pau Espin Pedrol761da1a2020-07-17 18:29:20 +0200270 LOGCHAN(chan, DCTR, INFO) << "checking threshold: " << ctr_threshold_2_vty_str(ctr_thr)
Pau Espin Pedrol6a305fe2019-05-24 19:58:20 +0200271 << " ("<< rate_ctr->intv[ctr_thr->intv].rate << " vs " << ctr_thr->val << ")";
272 if (rate_ctr->intv[ctr_thr->intv].rate >= ctr_thr->val) {
Pau Espin Pedrol761da1a2020-07-17 18:29:20 +0200273 LOGCHAN(chan, DCTR, FATAL) << "threshold reached, stopping! " << ctr_threshold_2_vty_str(ctr_thr)
Pau Espin Pedrol6a305fe2019-05-24 19:58:20 +0200274 << " ("<< rate_ctr->intv[ctr_thr->intv].rate << " vs " << ctr_thr->val << ")";
275 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
276 return;
277 }
278 }
279 }
280 osmo_timer_schedule(&threshold_timer, threshold_timer_sched_secs, 0);
281}
282
283static size_t ctr_threshold_2_seconds(struct ctr_threshold *ctr)
284{
285 size_t mult = 0;
286 switch (ctr->intv) {
287 case RATE_CTR_INTV_SEC:
288 mult = 1;
289 break;
290 case RATE_CTR_INTV_MIN:
291 mult = 60;
292 break;
293 case RATE_CTR_INTV_HOUR:
294 mult = 60*60;
295 break;
296 case RATE_CTR_INTV_DAY:
297 mult = 60*60*24;
298 break;
299 default:
300 OSMO_ASSERT(false);
301 }
302 return mult;
303}
304
305static void threshold_timer_update_intv() {
306 struct ctr_threshold *ctr, *min_ctr;
307 size_t secs, min_secs;
308
309 /* Avoid scheduling timer until itself and other structures are prepared
310 by trx_rate_ctr_init */
311 if (!threshold_initied)
312 return;
313
314 if (llist_empty(&threshold_list)) {
315 if (osmo_timer_pending(&threshold_timer))
316 osmo_timer_del(&threshold_timer);
317 return;
318 }
319
320 min_ctr = llist_first_entry(&threshold_list, struct ctr_threshold, list);
321 min_secs = ctr_threshold_2_seconds(min_ctr);
322
323 llist_for_each_entry(ctr, &threshold_list, list) {
324 secs = ctr_threshold_2_seconds(ctr);
Pau Espin Pedrol394053e2020-06-29 16:43:25 +0200325 if (min_secs > secs)
Pau Espin Pedrol6a305fe2019-05-24 19:58:20 +0200326 min_secs = secs;
327 }
328
329
Pau Espin Pedrolfb967672020-06-29 16:44:23 +0200330 threshold_timer_sched_secs = OSMO_MAX((int)(min_secs / 2 - 1), 1);
Pau Espin Pedrol761da1a2020-07-17 18:29:20 +0200331 LOGC(DCTR, INFO) << "New ctr-error-threshold check interval: "
Pau Espin Pedrol6a305fe2019-05-24 19:58:20 +0200332 << threshold_timer_sched_secs << " seconds";
333 osmo_timer_schedule(&threshold_timer, threshold_timer_sched_secs, 0);
334}
335
336/* Init rate_ctr subsystem. Expected to be called during process start by main thread before VTY is ready */
Pau Espin Pedrol4456b6f2019-05-24 16:54:19 +0200337void trx_rate_ctr_init(void *ctx, struct trx_ctx* trx_ctx)
338{
339 size_t i;
Pau Espin Pedrol6a305fe2019-05-24 19:58:20 +0200340 trx_rate_ctr_ctx = ctx;
Pau Espin Pedrol4456b6f2019-05-24 16:54:19 +0200341 chan_len = trx_ctx->cfg.num_chans;
Pau Espin Pedrol6a3a2b82020-06-29 13:52:26 +0200342 dev_ctrs_pending = (struct device_counters*) talloc_zero_size(ctx, chan_len * sizeof(struct device_counters));
Pau Espin Pedrol92ba59d2020-06-29 14:34:59 +0200343 trx_ctrs_pending = (struct trx_counters*) talloc_zero_size(ctx, chan_len * sizeof(struct trx_counters));
Pau Espin Pedrol4456b6f2019-05-24 16:54:19 +0200344 rate_ctrs = (struct rate_ctr_group**) talloc_zero_size(ctx, chan_len * sizeof(struct rate_ctr_group*));
345
346 for (i = 0; i < chan_len; i++) {
Pau Espin Pedrol6a3a2b82020-06-29 13:52:26 +0200347 dev_ctrs_pending[i].chan = PENDING_CHAN_NONE;
Pau Espin Pedrol92ba59d2020-06-29 14:34:59 +0200348 trx_ctrs_pending[i].chan = PENDING_CHAN_NONE;
Pau Espin Pedrol4456b6f2019-05-24 16:54:19 +0200349 rate_ctrs[i] = rate_ctr_group_alloc(ctx, &trx_chan_ctr_group_desc, i);
350 if (!rate_ctrs[i]) {
Pau Espin Pedrol761da1a2020-07-17 18:29:20 +0200351 LOGCHAN(i, DCTR, ERROR) << "Failed to allocate rate ctr";
Pau Espin Pedrol4456b6f2019-05-24 16:54:19 +0200352 exit(1);
353 }
354 }
Pau Espin Pedrol6a3a2b82020-06-29 13:52:26 +0200355 dev_rate_ctr_timerfd.fd = -1;
356 if (osmo_timerfd_setup(&dev_rate_ctr_timerfd, dev_rate_ctr_timerfd_cb, NULL) < 0) {
Pau Espin Pedrol761da1a2020-07-17 18:29:20 +0200357 LOGC(DCTR, ERROR) << "Failed to setup timerfd";
Pau Espin Pedrol4456b6f2019-05-24 16:54:19 +0200358 exit(1);
359 }
Pau Espin Pedrol92ba59d2020-06-29 14:34:59 +0200360 trx_rate_ctr_timerfd.fd = -1;
361 if (osmo_timerfd_setup(&trx_rate_ctr_timerfd, trx_rate_ctr_timerfd_cb, NULL) < 0) {
Pau Espin Pedrol761da1a2020-07-17 18:29:20 +0200362 LOGC(DCTR, ERROR) << "Failed to setup timerfd";
Pau Espin Pedrol92ba59d2020-06-29 14:34:59 +0200363 exit(1);
364 }
Pau Espin Pedrol4456b6f2019-05-24 16:54:19 +0200365 osmo_signal_register_handler(SS_DEVICE, device_sig_cb, NULL);
Pau Espin Pedrol6a305fe2019-05-24 19:58:20 +0200366
367 /* Now set up threshold checks */
368 threshold_initied = true;
369 osmo_timer_setup(&threshold_timer, threshold_timer_cb, NULL);
370 threshold_timer_update_intv();
371}
372
373void trx_rate_ctr_threshold_add(struct ctr_threshold *ctr)
374{
375 struct ctr_threshold *new_ctr;
376
377 new_ctr = talloc_zero(trx_rate_ctr_ctx, struct ctr_threshold);
378 *new_ctr = *ctr;
Pau Espin Pedrol761da1a2020-07-17 18:29:20 +0200379 LOGC(DCTR, NOTICE) << "Adding new threshold check: " << ctr_threshold_2_vty_str(new_ctr);
Pau Espin Pedrol6a305fe2019-05-24 19:58:20 +0200380 llist_add(&new_ctr->list, &threshold_list);
381 threshold_timer_update_intv();
382}
383
384int trx_rate_ctr_threshold_del(struct ctr_threshold *del_ctr)
385{
386 struct ctr_threshold *ctr;
387
388 llist_for_each_entry(ctr, &threshold_list, list) {
389 if (ctr->intv != del_ctr->intv ||
390 ctr->ctr_id != del_ctr->ctr_id ||
391 ctr->val != del_ctr->val)
392 continue;
393
Pau Espin Pedrol761da1a2020-07-17 18:29:20 +0200394 LOGC(DCTR, NOTICE) << "Deleting threshold check: " << ctr_threshold_2_vty_str(del_ctr);
Pau Espin Pedrol6a305fe2019-05-24 19:58:20 +0200395 llist_del(&ctr->list);
396 talloc_free(ctr);
397 threshold_timer_update_intv();
398 return 0;
399 }
400 return -1;
401}
402
403void trx_rate_ctr_threshold_write_config(struct vty *vty, char *indent_prefix)
404{
405 struct ctr_threshold *ctr;
406
407 llist_for_each_entry(ctr, &threshold_list, list) {
408 vty_out(vty, "%s%s%s", indent_prefix, ctr_threshold_2_vty_str(ctr), VTY_NEWLINE);
409 }
Pau Espin Pedrol4456b6f2019-05-24 16:54:19 +0200410}