<p>pespin <strong>merged</strong> this change.</p><p><a href="https://gerrit.osmocom.org/c/osmo-trx/+/14168">View Change</a></p><div style="white-space:pre-wrap">Approvals:
laforge: Looks good to me, but someone else must approve
pespin: Looks good to me, approved
roh: Looks good to me, but someone else must approve
Jenkins Builder: Verified
</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">Add VTY commands to set error ctr thresholds<br><br>osmo-trx will validate over time that those thresholds are not reached.<br>If they are reached, osmo-trx will die. As a result, osmo-bts-trx will<br>notice and will end up notifying the BSC about it (for instance because<br>it will also restart its process).<br><br>For instance:<br>"""<br>ctr-error-threshold rx_drop_events 2 minute<br>ctr-error-threshold rx_underruns 10 second<br>"""<br><br>In those cases above, osmo-trx will die if rate_ctr rx_drop_events went<br>to a value higher than 2 per minute, or it will die to if rx_underruns<br>went higher than 10 per second.<br><br>Change-Id: I4bcf44dbf064e2e86dfc3b8a2ad18fea76fbd51a<br>---<br>M CommonLibs/trx_rate_ctr.cpp<br>M CommonLibs/trx_rate_ctr.h<br>M CommonLibs/trx_vty.c<br>M doc/manuals/chapters/counters.adoc<br>M doc/manuals/chapters/counters_generated.adoc<br>M doc/manuals/vty/trx_vty_reference.xml<br>6 files changed, 391 insertions(+), 11 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/CommonLibs/trx_rate_ctr.cpp b/CommonLibs/trx_rate_ctr.cpp</span><br><span>index 711b904..d98caff 100644</span><br><span>--- a/CommonLibs/trx_rate_ctr.cpp</span><br><span>+++ b/CommonLibs/trx_rate_ctr.cpp</span><br><span>@@ -56,6 +56,7 @@</span><br><span> #include <osmocom/core/rate_ctr.h></span><br><span> #include <osmocom/core/select.h></span><br><span> #include <osmocom/core/stats.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/timer.h></span><br><span> </span><br><span> #include "osmo_signal.h"</span><br><span> #include "trx_vty.h"</span><br><span>@@ -68,18 +69,34 @@</span><br><span> (non-pending) counter data */</span><br><span> #define PENDING_CHAN_NONE SIZE_MAX</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static void *trx_rate_ctr_ctx;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static struct rate_ctr_group** rate_ctrs;</span><br><span> static struct device_counters* ctrs_pending;</span><br><span> static size_t chan_len;</span><br><span> static struct osmo_fd rate_ctr_timerfd;</span><br><span> static Mutex rate_ctr_mutex;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-enum {</span><br><span style="color: hsl(0, 100%, 40%);">- TRX_CTR_RX_UNDERRUNS,</span><br><span style="color: hsl(0, 100%, 40%);">- TRX_CTR_RX_OVERRUNS,</span><br><span style="color: hsl(0, 100%, 40%);">- TRX_CTR_TX_UNDERRUNS,</span><br><span style="color: hsl(0, 100%, 40%);">- TRX_CTR_RX_DROP_EV,</span><br><span style="color: hsl(0, 100%, 40%);">- TRX_CTR_RX_DROP_SMPL,</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_timer_list threshold_timer;</span><br><span style="color: hsl(120, 100%, 40%);">+static LLIST_HEAD(threshold_list);</span><br><span style="color: hsl(120, 100%, 40%);">+static int threshold_timer_sched_secs;</span><br><span style="color: hsl(120, 100%, 40%);">+static bool threshold_initied;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const struct value_string rate_ctr_intv[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+ { RATE_CTR_INTV_SEC, "per-second" },</span><br><span style="color: hsl(120, 100%, 40%);">+ { RATE_CTR_INTV_MIN, "per-minute" },</span><br><span style="color: hsl(120, 100%, 40%);">+ { RATE_CTR_INTV_HOUR, "per-hour" },</span><br><span style="color: hsl(120, 100%, 40%);">+ { RATE_CTR_INTV_DAY, "per-day" },</span><br><span style="color: hsl(120, 100%, 40%);">+ { 0, NULL }</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const struct value_string trx_chan_ctr_names[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+ { TRX_CTR_RX_UNDERRUNS, "rx_underruns" },</span><br><span style="color: hsl(120, 100%, 40%);">+ { TRX_CTR_RX_OVERRUNS, "rx_overruns" },</span><br><span style="color: hsl(120, 100%, 40%);">+ { TRX_CTR_TX_UNDERRUNS, "tx_underruns" },</span><br><span style="color: hsl(120, 100%, 40%);">+ { TRX_CTR_RX_DROP_EV, "rx_drop_events" },</span><br><span style="color: hsl(120, 100%, 40%);">+ { TRX_CTR_RX_DROP_SMPL, "rx_drop_samples" },</span><br><span style="color: hsl(120, 100%, 40%);">+ { 0, NULL }</span><br><span> };</span><br><span> </span><br><span> static const struct rate_ctr_desc trx_chan_ctr_desc[] = {</span><br><span>@@ -155,10 +172,99 @@</span><br><span> return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-/* Init rate_ctr subsystem. Expected to be called during process start by main thread */</span><br><span style="color: hsl(120, 100%, 40%);">+/************************************</span><br><span style="color: hsl(120, 100%, 40%);">+ * ctr_threshold APIs</span><br><span style="color: hsl(120, 100%, 40%);">+ ************************************/</span><br><span style="color: hsl(120, 100%, 40%);">+static const char* ctr_threshold_2_vty_str(struct ctr_threshold *ctr)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ static char buf[256];</span><br><span style="color: hsl(120, 100%, 40%);">+ int rc = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ rc += snprintf(buf, sizeof(buf), "ctr-error-threshold %s", get_value_string(trx_chan_ctr_names, ctr->ctr_id));</span><br><span style="color: hsl(120, 100%, 40%);">+ rc += snprintf(buf + rc, sizeof(buf) - rc, " %d %s", ctr->val, get_value_string(rate_ctr_intv, ctr->intv));</span><br><span style="color: hsl(120, 100%, 40%);">+ return buf;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void threshold_timer_cb(void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ctr_threshold *ctr_thr;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct rate_ctr *rate_ctr;</span><br><span style="color: hsl(120, 100%, 40%);">+ size_t chan;</span><br><span style="color: hsl(120, 100%, 40%);">+ LOGC(DMAIN, DEBUG) << "threshold_timer_cb fired!";</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ llist_for_each_entry(ctr_thr, &threshold_list, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+ for (chan = 0; chan < chan_len; chan++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ rate_ctr = &rate_ctrs[chan]->ctr[ctr_thr->ctr_id];</span><br><span style="color: hsl(120, 100%, 40%);">+ LOGCHAN(chan, DMAIN, INFO) << "checking threshold: " << ctr_threshold_2_vty_str(ctr_thr)</span><br><span style="color: hsl(120, 100%, 40%);">+ << " ("<< rate_ctr->intv[ctr_thr->intv].rate << " vs " << ctr_thr->val << ")";</span><br><span style="color: hsl(120, 100%, 40%);">+ if (rate_ctr->intv[ctr_thr->intv].rate >= ctr_thr->val) {</span><br><span style="color: hsl(120, 100%, 40%);">+ LOGCHAN(chan, DMAIN, FATAL) << "threshold reached, stopping! " << ctr_threshold_2_vty_str(ctr_thr)</span><br><span style="color: hsl(120, 100%, 40%);">+ << " ("<< rate_ctr->intv[ctr_thr->intv].rate << " vs " << ctr_thr->val << ")";</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ return;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_timer_schedule(&threshold_timer, threshold_timer_sched_secs, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static size_t ctr_threshold_2_seconds(struct ctr_threshold *ctr)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ size_t mult = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (ctr->intv) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case RATE_CTR_INTV_SEC:</span><br><span style="color: hsl(120, 100%, 40%);">+ mult = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ case RATE_CTR_INTV_MIN:</span><br><span style="color: hsl(120, 100%, 40%);">+ mult = 60;</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ case RATE_CTR_INTV_HOUR:</span><br><span style="color: hsl(120, 100%, 40%);">+ mult = 60*60;</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ case RATE_CTR_INTV_DAY:</span><br><span style="color: hsl(120, 100%, 40%);">+ mult = 60*60*24;</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ default:</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_ASSERT(false);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ return mult;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void threshold_timer_update_intv() {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ctr_threshold *ctr, *min_ctr;</span><br><span style="color: hsl(120, 100%, 40%);">+ size_t secs, min_secs;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Avoid scheduling timer until itself and other structures are prepared</span><br><span style="color: hsl(120, 100%, 40%);">+ by trx_rate_ctr_init */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!threshold_initied)</span><br><span style="color: hsl(120, 100%, 40%);">+ return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (llist_empty(&threshold_list)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (osmo_timer_pending(&threshold_timer))</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_timer_del(&threshold_timer);</span><br><span style="color: hsl(120, 100%, 40%);">+ return;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ min_ctr = llist_first_entry(&threshold_list, struct ctr_threshold, list);</span><br><span style="color: hsl(120, 100%, 40%);">+ min_secs = ctr_threshold_2_seconds(min_ctr);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ llist_for_each_entry(ctr, &threshold_list, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+ secs = ctr_threshold_2_seconds(ctr);</span><br><span style="color: hsl(120, 100%, 40%);">+ if( min_secs > secs)</span><br><span style="color: hsl(120, 100%, 40%);">+ min_secs = secs;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ threshold_timer_sched_secs = OSMO_MAX(min_secs / 2 - 1, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+ LOGC(DMAIN, INFO) << "New ctr-error-threshold check interval: "</span><br><span style="color: hsl(120, 100%, 40%);">+ << threshold_timer_sched_secs << " seconds";</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_timer_schedule(&threshold_timer, threshold_timer_sched_secs, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Init rate_ctr subsystem. Expected to be called during process start by main thread before VTY is ready */</span><br><span> void trx_rate_ctr_init(void *ctx, struct trx_ctx* trx_ctx)</span><br><span> {</span><br><span> size_t i;</span><br><span style="color: hsl(120, 100%, 40%);">+ trx_rate_ctr_ctx = ctx;</span><br><span> chan_len = trx_ctx->cfg.num_chans;</span><br><span> ctrs_pending = (struct device_counters*) talloc_zero_size(ctx, chan_len * sizeof(struct device_counters));</span><br><span> rate_ctrs = (struct rate_ctr_group**) talloc_zero_size(ctx, chan_len * sizeof(struct rate_ctr_group*));</span><br><span>@@ -177,4 +283,48 @@</span><br><span> exit(1);</span><br><span> }</span><br><span> osmo_signal_register_handler(SS_DEVICE, device_sig_cb, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Now set up threshold checks */</span><br><span style="color: hsl(120, 100%, 40%);">+ threshold_initied = true;</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_timer_setup(&threshold_timer, threshold_timer_cb, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ threshold_timer_update_intv();</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void trx_rate_ctr_threshold_add(struct ctr_threshold *ctr)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ctr_threshold *new_ctr;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ new_ctr = talloc_zero(trx_rate_ctr_ctx, struct ctr_threshold);</span><br><span style="color: hsl(120, 100%, 40%);">+ *new_ctr = *ctr;</span><br><span style="color: hsl(120, 100%, 40%);">+ LOGC(DMAIN, NOTICE) << "Adding new threshold check: " << ctr_threshold_2_vty_str(new_ctr);</span><br><span style="color: hsl(120, 100%, 40%);">+ llist_add(&new_ctr->list, &threshold_list);</span><br><span style="color: hsl(120, 100%, 40%);">+ threshold_timer_update_intv();</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int trx_rate_ctr_threshold_del(struct ctr_threshold *del_ctr)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ctr_threshold *ctr;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ llist_for_each_entry(ctr, &threshold_list, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ctr->intv != del_ctr->intv ||</span><br><span style="color: hsl(120, 100%, 40%);">+ ctr->ctr_id != del_ctr->ctr_id ||</span><br><span style="color: hsl(120, 100%, 40%);">+ ctr->val != del_ctr->val)</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ LOGC(DMAIN, NOTICE) << "Deleting threshold check: " << ctr_threshold_2_vty_str(del_ctr);</span><br><span style="color: hsl(120, 100%, 40%);">+ llist_del(&ctr->list);</span><br><span style="color: hsl(120, 100%, 40%);">+ talloc_free(ctr);</span><br><span style="color: hsl(120, 100%, 40%);">+ threshold_timer_update_intv();</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void trx_rate_ctr_threshold_write_config(struct vty *vty, char *indent_prefix)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ctr_threshold *ctr;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ llist_for_each_entry(ctr, &threshold_list, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+ vty_out(vty, "%s%s%s", indent_prefix, ctr_threshold_2_vty_str(ctr), VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span> }</span><br><span>diff --git a/CommonLibs/trx_rate_ctr.h b/CommonLibs/trx_rate_ctr.h</span><br><span>index 48131e7..6e4fa4d 100644</span><br><span>--- a/CommonLibs/trx_rate_ctr.h</span><br><span>+++ b/CommonLibs/trx_rate_ctr.h</span><br><span>@@ -1,4 +1,29 @@</span><br><span> #pragma once</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/rate_ctr.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/vty/command.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum TrxCtr {</span><br><span style="color: hsl(120, 100%, 40%);">+ TRX_CTR_RX_UNDERRUNS,</span><br><span style="color: hsl(120, 100%, 40%);">+ TRX_CTR_RX_OVERRUNS,</span><br><span style="color: hsl(120, 100%, 40%);">+ TRX_CTR_TX_UNDERRUNS,</span><br><span style="color: hsl(120, 100%, 40%);">+ TRX_CTR_RX_DROP_EV,</span><br><span style="color: hsl(120, 100%, 40%);">+ TRX_CTR_RX_DROP_SMPL,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ctr_threshold {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! Linked list of all counter groups in the system */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct llist_head list;</span><br><span style="color: hsl(120, 100%, 40%);">+ enum rate_ctr_intv intv;</span><br><span style="color: hsl(120, 100%, 40%);">+ enum TrxCtr ctr_id;</span><br><span style="color: hsl(120, 100%, 40%);">+ uint32_t val;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+extern const struct value_string rate_ctr_intv[];</span><br><span style="color: hsl(120, 100%, 40%);">+extern const struct value_string trx_chan_ctr_names[];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> struct trx_ctx;</span><br><span> void trx_rate_ctr_init(void *ctx, struct trx_ctx* trx_ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+void trx_rate_ctr_threshold_add(struct ctr_threshold *ctr);</span><br><span style="color: hsl(120, 100%, 40%);">+int trx_rate_ctr_threshold_del(struct ctr_threshold *del_ctr);</span><br><span style="color: hsl(120, 100%, 40%);">+void trx_rate_ctr_threshold_write_config(struct vty *vty, char *indent_prefix);</span><br><span>diff --git a/CommonLibs/trx_vty.c b/CommonLibs/trx_vty.c</span><br><span>index 06e20ab..4cc827b 100644</span><br><span>--- a/CommonLibs/trx_vty.c</span><br><span>+++ b/CommonLibs/trx_vty.c</span><br><span>@@ -31,6 +31,7 @@</span><br><span> #include <osmocom/vty/vty.h></span><br><span> #include <osmocom/vty/misc.h></span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#include "trx_rate_ctr.h"</span><br><span> #include "trx_vty.h"</span><br><span> #include "../config.h"</span><br><span> </span><br><span>@@ -347,6 +348,107 @@</span><br><span> return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static int vty_ctr_name_2_id(const char* str) {</span><br><span style="color: hsl(120, 100%, 40%);">+ size_t i;</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; trx_chan_ctr_names[i].str; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strstr(trx_chan_ctr_names[i].str, str)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return i;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int vty_intv_name_2_id(const char* str) {</span><br><span style="color: hsl(120, 100%, 40%);">+ size_t i;</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; rate_ctr_intv[i].str; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(rate_ctr_intv[i].str, str) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return i;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define THRESHOLD_ARGS "(rx_underruns|rx_overruns|tx_underruns|rx_drop_events|rx_drop_samples)"</span><br><span style="color: hsl(120, 100%, 40%);">+#define THRESHOLD_STR_VAL(s) "Set threshold value for rate_ctr device:" OSMO_STRINGIFY_VAL(s) "\n"</span><br><span style="color: hsl(120, 100%, 40%);">+#define THRESHOLD_STRS \</span><br><span style="color: hsl(120, 100%, 40%);">+ THRESHOLD_STR_VAL(rx_underruns) \</span><br><span style="color: hsl(120, 100%, 40%);">+ THRESHOLD_STR_VAL(rx_overruns) \</span><br><span style="color: hsl(120, 100%, 40%);">+ THRESHOLD_STR_VAL(tx_underruns) \</span><br><span style="color: hsl(120, 100%, 40%);">+ THRESHOLD_STR_VAL(rx_drop_events) \</span><br><span style="color: hsl(120, 100%, 40%);">+ THRESHOLD_STR_VAL(rx_drop_samples)</span><br><span style="color: hsl(120, 100%, 40%);">+#define INTV_ARGS "(per-second|per-minute|per-hour|per-day)"</span><br><span style="color: hsl(120, 100%, 40%);">+#define INTV_STR_VAL(s) "Threshold value sampled " OSMO_STRINGIFY_VAL(s) "\n"</span><br><span style="color: hsl(120, 100%, 40%);">+#define INTV_STRS \</span><br><span style="color: hsl(120, 100%, 40%);">+ INTV_STR_VAL(per-second) \</span><br><span style="color: hsl(120, 100%, 40%);">+ INTV_STR_VAL(per-minute) \</span><br><span style="color: hsl(120, 100%, 40%);">+ INTV_STR_VAL(per-hour) \</span><br><span style="color: hsl(120, 100%, 40%);">+ INTV_STR_VAL(per-day)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+DEFUN(cfg_ctr_error_threshold, cfg_ctr_error_threshold_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+ "ctr-error-threshold " THRESHOLD_ARGS " <0-65535> " INTV_ARGS,</span><br><span style="color: hsl(120, 100%, 40%);">+ "Threshold rate for error counter\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ THRESHOLD_STRS</span><br><span style="color: hsl(120, 100%, 40%);">+ "Value to set for threshold\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ INTV_STRS)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ctr_threshold ctr;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ struct trx_ctx *trx = trx_from_vty(vty);</span><br><span style="color: hsl(120, 100%, 40%);">+ rc = vty_ctr_name_2_id(argv[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ vty_out(vty, "No valid ctr_name found for ctr-error-threshold %s%s",</span><br><span style="color: hsl(120, 100%, 40%);">+ argv[0], VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+ return CMD_WARNING;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ctr.ctr_id = (enum TrxCtr)rc;</span><br><span style="color: hsl(120, 100%, 40%);">+ ctr.val = atoi(argv[1]);</span><br><span style="color: hsl(120, 100%, 40%);">+ rc = vty_intv_name_2_id(argv[2]);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ vty_out(vty, "No valid time frame found for ctr-error-threshold %s %d %s%s",</span><br><span style="color: hsl(120, 100%, 40%);">+ argv[0], ctr.val, argv[2], VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+ return CMD_WARNING;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ctr.intv = (enum rate_ctr_intv) rc;</span><br><span style="color: hsl(120, 100%, 40%);">+ trx_rate_ctr_threshold_add(&ctr);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return CMD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+DEFUN(cfg_no_ctr_error_threshold, cfg_no_ctr_error_threshold_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+ "no ctr-error-threshold " THRESHOLD_ARGS " <0-65535> " INTV_ARGS,</span><br><span style="color: hsl(120, 100%, 40%);">+ NO_STR "Threshold rate for error counter\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ THRESHOLD_STRS</span><br><span style="color: hsl(120, 100%, 40%);">+ "Value to set for threshold\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ INTV_STRS)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ctr_threshold ctr;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ struct trx_ctx *trx = trx_from_vty(vty);</span><br><span style="color: hsl(120, 100%, 40%);">+ rc = vty_ctr_name_2_id(argv[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ vty_out(vty, "No valid ctr_name found for ctr-error-threshold %s%s",</span><br><span style="color: hsl(120, 100%, 40%);">+ argv[0], VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+ return CMD_WARNING;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ctr.ctr_id = (enum TrxCtr)rc;</span><br><span style="color: hsl(120, 100%, 40%);">+ ctr.val = atoi(argv[1]);</span><br><span style="color: hsl(120, 100%, 40%);">+ rc = vty_intv_name_2_id(argv[2]);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ vty_out(vty, "No valid time frame found for ctr-error-threshold %s %d %s%s",</span><br><span style="color: hsl(120, 100%, 40%);">+ argv[0], ctr.val, argv[2], VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+ return CMD_WARNING;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ctr.intv = (enum rate_ctr_intv) rc;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (trx_rate_ctr_threshold_del(&ctr) < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ vty_out(vty, "no ctr-error-threshold: Entry to delete not found%s", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+ return CMD_WARNING;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return CMD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> DEFUN(cfg_chan, cfg_chan_cmd,</span><br><span> "chan <0-100>",</span><br><span> "Select a channel to configure\n"</span><br><span>@@ -444,6 +546,7 @@</span><br><span> vty_out(vty, " ext-rach %s%s", trx->cfg.ext_rach ? "enable" : "disable", VTY_NEWLINE);</span><br><span> if (trx->cfg.sched_rr != 0)</span><br><span> vty_out(vty, " rt-prio %u%s", trx->cfg.sched_rr, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+ trx_rate_ctr_threshold_write_config(vty, " ");</span><br><span> </span><br><span> for (i = 0; i < trx->cfg.num_chans; i++) {</span><br><span> chan = &trx->cfg.chans[i];</span><br><span>@@ -593,6 +696,8 @@</span><br><span> install_element(TRX_NODE, &cfg_ext_rach_cmd);</span><br><span> install_element(TRX_NODE, &cfg_rt_prio_cmd);</span><br><span> install_element(TRX_NODE, &cfg_filler_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+ install_element(TRX_NODE, &cfg_ctr_error_threshold_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+ install_element(TRX_NODE, &cfg_no_ctr_error_threshold_cmd);</span><br><span> </span><br><span> install_element(TRX_NODE, &cfg_chan_cmd);</span><br><span> install_node(&chan_node, dummy_config_write);</span><br><span>diff --git a/doc/manuals/chapters/counters.adoc b/doc/manuals/chapters/counters.adoc</span><br><span>index 7fbb10c..79d1962 100644</span><br><span>--- a/doc/manuals/chapters/counters.adoc</span><br><span>+++ b/doc/manuals/chapters/counters.adoc</span><br><span>@@ -2,3 +2,62 @@</span><br><span> == Counters</span><br><span> </span><br><span> include::./counters_generated.adoc[]</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+=== Rate Counter Configurable Error Thresholds</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Some rate counters such as overruns, underruns and dropped packets indicate</span><br><span style="color: hsl(120, 100%, 40%);">+events that can really harm correct operation of the BTS served by OsmoTRX,</span><br><span style="color: hsl(120, 100%, 40%);">+specially if they happen frequently. OsmoTRX is in most cases (depending on</span><br><span style="color: hsl(120, 100%, 40%);">+maturity of device driver) prepared to dodge the temporary failure and keep</span><br><span style="color: hsl(120, 100%, 40%);">+running and providing service.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Still, it is sometimes important for this kind of events to not go unnoticed by</span><br><span style="color: hsl(120, 100%, 40%);">+the operator, since they may indicate issues regarding the set up that may</span><br><span style="color: hsl(120, 100%, 40%);">+require operator intervention to fix it.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+For instance, frequent dropped packets could indicate SDR HW/FW/power errors, or</span><br><span style="color: hsl(120, 100%, 40%);">+a faulty connection against the host running OsmoTRX.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+They can also indicate issues on the host running OsmoTRX itself: For instance,</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoTRX may not be running under a high enough priority (hence other processes</span><br><span style="color: hsl(120, 100%, 40%);">+eventually battling for resources with it), or that simply the HW running</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoTRX is not powerful enough to accomplish all work in a timely fashion all</span><br><span style="color: hsl(120, 100%, 40%);">+the time.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+As a result, OsmoTRX can be configured to exit the process upon certain</span><br><span style="color: hsl(120, 100%, 40%);">+conditions being met, in order to let osmoBTS notice something is wrong and thus</span><br><span style="color: hsl(120, 100%, 40%);">+announcing issues through alarms to the network, where the operator can then</span><br><span style="color: hsl(120, 100%, 40%);">+investigate the issue by looking at OsmoTRX logs.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+These conditions are configured by means of introducing rate counter thresholds</span><br><span style="color: hsl(120, 100%, 40%);">+in the VTY. The OsmoTRX user can provide those threshold commands either in the</span><br><span style="color: hsl(120, 100%, 40%);">+VTY cfg file read by OsmoTRX process during startup, or by adding/removing them</span><br><span style="color: hsl(120, 100%, 40%);">+dynamically through the VTY interactive console.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Each threshold cmd states an event (a rate counter type), a value and an time</span><br><span style="color: hsl(120, 100%, 40%);">+interval (a second, a minute, an hour or a day). A threshold will be reached</span><br><span style="color: hsl(120, 100%, 40%);">+(and OsmoTRX stopped) if its value grows bigger than the configured threshold</span><br><span style="color: hsl(120, 100%, 40%);">+value over the configured time interval. This is the syntax used to manage rate</span><br><span style="color: hsl(120, 100%, 40%);">+counter thresholds:</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+`(no) ctr-error-threshold <EVENT> <VALUE> <INTERVAL>`</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+If several rate counter thresholds are set, then all of them are checked over</span><br><span style="color: hsl(120, 100%, 40%);">+time and the first one reached will stop OsmoTRX.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+.Example: rate counter threshold configuration (VTY .cfg file)</span><br><span style="color: hsl(120, 100%, 40%);">+----</span><br><span style="color: hsl(120, 100%, 40%);">+trx</span><br><span style="color: hsl(120, 100%, 40%);">+ ctr-error-threshold rx_drop_events 2 per-minute <1></span><br><span style="color: hsl(120, 100%, 40%);">+ ctr-error-threshold rx_drop_samples 800 per-second <2></span><br><span style="color: hsl(120, 100%, 40%);">+----</span><br><span style="color: hsl(120, 100%, 40%);">+<1> Stop OsmoTRX if dropped event (any amount of samples) during Rx was detected 2 times or more during a minute.</span><br><span style="color: hsl(120, 100%, 40%);">+<2> Stop OsmoTRX if 800 or more samples were detected during Rx to be dropped by the HW during a second.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+.Example: rate counter threshold configuration (VTY interactive)</span><br><span style="color: hsl(120, 100%, 40%);">+----</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoTRX(config-trx)# ctr-error-threshold tx_underruns 3 per-hour <1></span><br><span style="color: hsl(120, 100%, 40%);">+OsmoTRX(config-trx)# no ctr-error-threshold tx_underruns 3 per-hour <2></span><br><span style="color: hsl(120, 100%, 40%);">+----</span><br><span style="color: hsl(120, 100%, 40%);">+<1> Stop OsmoTRX if 3 or more underruns were detected during Tx over the last hour</span><br><span style="color: hsl(120, 100%, 40%);">+<2> Remove previously set rate counter threshold</span><br><span>diff --git a/doc/manuals/chapters/counters_generated.adoc b/doc/manuals/chapters/counters_generated.adoc</span><br><span>index b40dc37..6955b18 100644</span><br><span>--- a/doc/manuals/chapters/counters_generated.adoc</span><br><span>+++ b/doc/manuals/chapters/counters_generated.adoc</span><br><span>@@ -1,7 +1,17 @@</span><br><span> // autogenerated by show asciidoc counters</span><br><span style="color: hsl(0, 100%, 40%);">-These counters and their description based on OsmoTRX 0.2.0.61-408f (OsmoTRX).</span><br><span style="color: hsl(120, 100%, 40%);">+These counters and their description based on OsmoTRX 1.0.0.43-3f7c0 (OsmoTRX).</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+=== Rate Counters</span><br><span> </span><br><span> // generating tables for rate_ctr_group</span><br><span style="color: hsl(0, 100%, 40%);">-// generating tables for osmo_stat_items</span><br><span style="color: hsl(0, 100%, 40%);">-// generating tables for osmo_counters</span><br><span style="color: hsl(0, 100%, 40%);">-// there are no ungrouped osmo_counters</span><br><span style="color: hsl(120, 100%, 40%);">+// rate_ctr_group table osmo-trx statistics</span><br><span style="color: hsl(120, 100%, 40%);">+.trx:chan - osmo-trx statistics</span><br><span style="color: hsl(120, 100%, 40%);">+[options="header"]</span><br><span style="color: hsl(120, 100%, 40%);">+|===</span><br><span style="color: hsl(120, 100%, 40%);">+| Name | Reference | Description</span><br><span style="color: hsl(120, 100%, 40%);">+| device:rx_underruns | <<trx:chan_device:rx_underruns>> | Number of Rx underruns</span><br><span style="color: hsl(120, 100%, 40%);">+| device:rx_overruns | <<trx:chan_device:rx_overruns>> | Number of Rx overruns</span><br><span style="color: hsl(120, 100%, 40%);">+| device:tx_underruns | <<trx:chan_device:tx_underruns>> | Number of Tx underruns</span><br><span style="color: hsl(120, 100%, 40%);">+| device:rx_drop_events | <<trx:chan_device:rx_drop_events>> | Number of times Rx samples were dropped by HW</span><br><span style="color: hsl(120, 100%, 40%);">+| device:rx_drop_samples | <<trx:chan_device:rx_drop_samples>> | Number of Rx samples dropped by HW</span><br><span style="color: hsl(120, 100%, 40%);">+|===</span><br><span>diff --git a/doc/manuals/vty/trx_vty_reference.xml b/doc/manuals/vty/trx_vty_reference.xml</span><br><span>index d6cd15d..e448a46 100644</span><br><span>--- a/doc/manuals/vty/trx_vty_reference.xml</span><br><span>+++ b/doc/manuals/vty/trx_vty_reference.xml</span><br><span>@@ -1253,6 +1253,37 @@</span><br><span> <param name='dummy' doc='Dummy method' /></span><br><span> </params></span><br><span> </command></span><br><span style="color: hsl(120, 100%, 40%);">+ <command id='ctr-error-threshold (rx_underruns|rx_overruns|tx_underruns|rx_drop_events|rx_drop_samples) <0-65535> (per-second|per-minute|per-hour|per-day)'></span><br><span style="color: hsl(120, 100%, 40%);">+ <params></span><br><span style="color: hsl(120, 100%, 40%);">+ <param name='ctr-error-threshold' doc='Threshold rate for error counter' /></span><br><span style="color: hsl(120, 100%, 40%);">+ <param name='rx_underruns' doc='Set threshold value for rate_ctr device:rx_underruns' /></span><br><span style="color: hsl(120, 100%, 40%);">+ <param name='rx_overruns' doc='Set threshold value for rate_ctr device:rx_overruns' /></span><br><span style="color: hsl(120, 100%, 40%);">+ <param name='tx_underruns' doc='Set threshold value for rate_ctr device:tx_underruns' /></span><br><span style="color: hsl(120, 100%, 40%);">+ <param name='rx_drop_events' doc='Set threshold value for rate_ctr device:rx_drop_events' /></span><br><span style="color: hsl(120, 100%, 40%);">+ <param name='rx_drop_samples' doc='Set threshold value for rate_ctr device:rx_drop_samples' /></span><br><span style="color: hsl(120, 100%, 40%);">+ <param name='<0-65535>' doc='Value to set for threshold' /></span><br><span style="color: hsl(120, 100%, 40%);">+ <param name='per-second' doc='Threshold value sampled per-second' /></span><br><span style="color: hsl(120, 100%, 40%);">+ <param name='per-minute' doc='Threshold value sampled per-minute' /></span><br><span style="color: hsl(120, 100%, 40%);">+ <param name='per-hour' doc='Threshold value sampled per-hour' /></span><br><span style="color: hsl(120, 100%, 40%);">+ <param name='per-day' doc='Threshold value sampled per-day' /></span><br><span style="color: hsl(120, 100%, 40%);">+ </params></span><br><span style="color: hsl(120, 100%, 40%);">+ </command></span><br><span style="color: hsl(120, 100%, 40%);">+ <command id='no ctr-error-threshold (rx_underruns|rx_overruns|tx_underruns|rx_drop_events|rx_drop_samples) <0-65535> (per-second|per-minute|per-hour|per-day)'></span><br><span style="color: hsl(120, 100%, 40%);">+ <params></span><br><span style="color: hsl(120, 100%, 40%);">+ <param name='no' doc='Negate a command or set its defaults' /></span><br><span style="color: hsl(120, 100%, 40%);">+ <param name='ctr-error-threshold' doc='Threshold rate for error counter' /></span><br><span style="color: hsl(120, 100%, 40%);">+ <param name='rx_underruns' doc='Set threshold value for rate_ctr device:rx_underruns' /></span><br><span style="color: hsl(120, 100%, 40%);">+ <param name='rx_overruns' doc='Set threshold value for rate_ctr device:rx_overruns' /></span><br><span style="color: hsl(120, 100%, 40%);">+ <param name='tx_underruns' doc='Set threshold value for rate_ctr device:tx_underruns' /></span><br><span style="color: hsl(120, 100%, 40%);">+ <param name='rx_drop_events' doc='Set threshold value for rate_ctr device:rx_drop_events' /></span><br><span style="color: hsl(120, 100%, 40%);">+ <param name='rx_drop_samples' doc='Set threshold value for rate_ctr device:rx_drop_samples' /></span><br><span style="color: hsl(120, 100%, 40%);">+ <param name='<0-65535>' doc='Value to set for threshold' /></span><br><span style="color: hsl(120, 100%, 40%);">+ <param name='per-second' doc='Threshold value sampled per-second' /></span><br><span style="color: hsl(120, 100%, 40%);">+ <param name='per-minute' doc='Threshold value sampled per-minute' /></span><br><span style="color: hsl(120, 100%, 40%);">+ <param name='per-hour' doc='Threshold value sampled per-hour' /></span><br><span style="color: hsl(120, 100%, 40%);">+ <param name='per-day' doc='Threshold value sampled per-day' /></span><br><span style="color: hsl(120, 100%, 40%);">+ </params></span><br><span style="color: hsl(120, 100%, 40%);">+ </command></span><br><span> <command id='chan <0-100>'></span><br><span> <params></span><br><span> <param name='chan' doc='Select a channel to configure' /></span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/osmo-trx/+/14168">change 14168</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/c/osmo-trx/+/14168"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: osmo-trx </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: I4bcf44dbf064e2e86dfc3b8a2ad18fea76fbd51a </div>
<div style="display:none"> Gerrit-Change-Number: 14168 </div>
<div style="display:none"> Gerrit-PatchSet: 4 </div>
<div style="display:none"> Gerrit-Owner: pespin <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder </div>
<div style="display:none"> Gerrit-Reviewer: laforge <laforge@gnumonks.org> </div>
<div style="display:none"> Gerrit-Reviewer: pespin <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: roh <jsteiger@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>