aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeels Hofmeyr <nhofmeyr@sysmocom.de>2022-05-30 02:39:17 +0200
committerNeels Hofmeyr <nhofmeyr@sysmocom.de>2022-06-01 23:34:47 +0200
commit11978153e6b0911b083dca770181a08f6c1cb9f6 (patch)
tree04c958991c2d33bac408a51949e9484e3d87a131
parent611e5a85037d2a05ca06df99f07de67bdd91e2c4 (diff)
fix performance for chan_counts and all_allocated statsneels/perf
The all_allocated_update_bsc() does inefficient iterating to count active/inactive lchans, which scales badly for high numbers of TRX managed by osmo-bsc. We need to update the all_allocated flags immediately (periodic counting alone would suffer from undersampling), so, until now, we are calling this inefficient function every time a channel state changes. Instead of iterating all channels for any chan state changes anywhere, keep global state of the current channel counts, and on channel state change only update those ts, trx, bts counts that actually change. A desirable side effect: for connection stats and handover decision 2, we can now also use the globally updated channel counts and save a bunch of inefficient iterations. To get accurate channel counts at all times, spread around some chan_counts_ts_update() calls in pivotal places. It re-counts the given timeslot and cascades counter changes, iff required. Just in case I missed some channel accounting, still run an inefficient iterating count regularly that detects errors, logs them and fixes them. No real harm done if such error appears. None show in ttcn3 BSC_Tests. It is fine to do the inefficient iteration once per second; channel state changes can realistically happen hundreds of times per second. Related: SYS#5976 Change-Id: I580bfae329aac8d4552723164741536af6512011
-rw-r--r--include/osmocom/bsc/bsc_stats.h2
-rw-r--r--include/osmocom/bsc/bts.h1
-rw-r--r--include/osmocom/bsc/bts_trx.h2
-rw-r--r--include/osmocom/bsc/chan_counts.h43
-rw-r--r--include/osmocom/bsc/gsm_data.h4
-rw-r--r--src/osmo-bsc/abis_rsl.c6
-rw-r--r--src/osmo-bsc/bsc_stats.c26
-rw-r--r--src/osmo-bsc/chan_counts.c278
-rw-r--r--src/osmo-bsc/handover_decision_2.c22
-rw-r--r--src/osmo-bsc/lchan_fsm.c13
-rw-r--r--src/osmo-bsc/osmo_bsc_main.c1
-rw-r--r--src/osmo-bsc/timeslot_fsm.c12
-rw-r--r--tests/handover/handover_test.c3
13 files changed, 322 insertions, 91 deletions
diff --git a/include/osmocom/bsc/bsc_stats.h b/include/osmocom/bsc/bsc_stats.h
index edd36b656..e8837aa1e 100644
--- a/include/osmocom/bsc/bsc_stats.h
+++ b/include/osmocom/bsc/bsc_stats.h
@@ -22,6 +22,7 @@
struct osmo_stat_item_group_desc;
struct gsm_network;
+struct gsm_bts;
/* OsmoBSC rate_ctr indexes */
enum {
@@ -111,4 +112,5 @@ extern const struct osmo_stat_item_group_desc bsc_statg_desc;
void bsc_update_connection_stats(struct gsm_network *net);
+void all_allocated_update_bts(struct gsm_bts *bts);
void all_allocated_update_bsc();
diff --git a/include/osmocom/bsc/bts.h b/include/osmocom/bsc/bts.h
index fc3ebce12..66cf68f02 100644
--- a/include/osmocom/bsc/bts.h
+++ b/include/osmocom/bsc/bts.h
@@ -637,6 +637,7 @@ struct gsm_bts {
/* At what point in the channel allocation sequence to dispatch the Immediate Assignment (Abis optimization) */
enum imm_ass_time imm_ass_time;
+ struct chan_counts chan_counts;
struct all_allocated all_allocated;
};
diff --git a/include/osmocom/bsc/bts_trx.h b/include/osmocom/bsc/bts_trx.h
index c8df9d9c4..eab5fecd5 100644
--- a/include/osmocom/bsc/bts_trx.h
+++ b/include/osmocom/bsc/bts_trx.h
@@ -79,6 +79,8 @@ struct gsm_bts_trx {
} rbs2000;
};
struct gsm_bts_trx_ts ts[TRX_NR_TS];
+
+ struct chan_counts chan_counts;
};
static inline struct gsm_bts_trx *gsm_bts_bb_trx_get_trx(struct gsm_bts_bb_trx *bb_transc) {
diff --git a/include/osmocom/bsc/chan_counts.h b/include/osmocom/bsc/chan_counts.h
index 31a1adc92..c11473e71 100644
--- a/include/osmocom/bsc/chan_counts.h
+++ b/include/osmocom/bsc/chan_counts.h
@@ -3,6 +3,14 @@
struct gsm_bts;
struct gsm_bts_trx;
+struct gsm_bts_trx_ts;
+struct gsm_lchan;
+
+void chan_counts_sig_init();
+void chan_counts_ts_update(struct gsm_bts_trx_ts *ts);
+void chan_counts_ts_clear(struct gsm_bts_trx_ts *ts);
+void chan_counts_trx_update(struct gsm_bts_trx *trx);
+void chan_counts_bsc_verify();
/* First array index to chan_counts.val. */
enum chan_counts_dim1 {
@@ -29,17 +37,29 @@ enum chan_counts_dim2 {
};
struct chan_counts {
- unsigned int val[_CHAN_COUNTS1_NUM][_CHAN_COUNTS2_NUM][_GSM_LCHAN_MAX];
+ /* Signed type, so that chan_counts_diff() can return negative values. */
+ int val[_CHAN_COUNTS1_NUM][_CHAN_COUNTS2_NUM][_GSM_LCHAN_MAX];
};
-void chan_counts_for_bts(struct chan_counts *bts_counts, const struct gsm_bts *bts);
-void chan_counts_for_trx(struct chan_counts *trx_counts, const struct gsm_bts_trx *trx);
-
static inline void chan_counts_zero(struct chan_counts *counts)
{
*counts = (struct chan_counts){0};
}
+static inline bool chan_counts_is_zero(const struct chan_counts *counts)
+{
+ int i1, i2, i3;
+ for (i1 = 0; i1 < _CHAN_COUNTS1_NUM; i1++) {
+ for (i2 = 0; i2 < _CHAN_COUNTS2_NUM; i2++) {
+ for (i3 = 0; i3 < _GSM_LCHAN_MAX; i3++) {
+ if (counts->val[i1][i2][i3])
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
static inline void chan_counts_dim3_add(struct chan_counts *dst,
enum chan_counts_dim1 dst_dim1, enum chan_counts_dim2 dst_dim2,
const struct chan_counts *add,
@@ -68,9 +88,24 @@ static inline void chan_counts_dim2_add(struct chan_counts *dst, enum chan_count
chan_counts_dim3_add(dst, dst_dim1, i, add, add_dim1, i);
}
+static inline void chan_counts_dim2_sub(struct chan_counts *dst, enum chan_counts_dim1 dst_dim1,
+ const struct chan_counts *sub, enum chan_counts_dim1 sub_dim1)
+{
+ int i;
+ for (i = 0; i < _CHAN_COUNTS2_NUM; i++)
+ chan_counts_dim3_sub(dst, dst_dim1, i, sub, sub_dim1, i);
+}
+
static inline void chan_counts_add(struct chan_counts *dst, const struct chan_counts *add)
{
int i;
for (i = 0; i < _CHAN_COUNTS1_NUM; i++)
chan_counts_dim2_add(dst, i, add, i);
}
+
+static inline void chan_counts_sub(struct chan_counts *dst, const struct chan_counts *sub)
+{
+ int i;
+ for (i = 0; i < _CHAN_COUNTS1_NUM; i++)
+ chan_counts_dim2_sub(dst, i, sub, i);
+}
diff --git a/include/osmocom/bsc/gsm_data.h b/include/osmocom/bsc/gsm_data.h
index 88c6474c9..126c7a79b 100644
--- a/include/osmocom/bsc/gsm_data.h
+++ b/include/osmocom/bsc/gsm_data.h
@@ -34,6 +34,7 @@
#include <osmocom/bsc/meas_rep.h>
#include <osmocom/bsc/acc.h>
#include <osmocom/bsc/osmux.h>
+#include <osmocom/bsc/chan_counts.h>
#define GSM_T3122_DEFAULT 10
@@ -962,6 +963,8 @@ struct gsm_bts_trx_ts {
* Does not include count of secondary VAMOS lchans. */
uint8_t max_primary_lchans;
struct gsm_lchan lchan[TS_MAX_LCHAN];
+
+ struct chan_counts chan_counts;
};
#define GSM_LCHAN_SI(lchan, i) (void *)((lchan)->si.buf[i][0])
@@ -1320,6 +1323,7 @@ struct gsm_network {
struct smlc_config *smlc;
+ struct chan_counts chan_counts;
struct all_allocated all_allocated;
};
diff --git a/src/osmo-bsc/abis_rsl.c b/src/osmo-bsc/abis_rsl.c
index 03412350d..d37fac8e2 100644
--- a/src/osmo-bsc/abis_rsl.c
+++ b/src/osmo-bsc/abis_rsl.c
@@ -2079,7 +2079,6 @@ static bool force_free_lchan_for_emergency(struct chan_rqd *rqd)
struct gsm_lchan *_select_sdcch_for_call(struct gsm_bts *bts, const struct chan_rqd *rqd, enum gsm_chan_t lctype)
{
- struct chan_counts bts_counts;
struct gsm_lchan *lchan = NULL;
int free_tchf, free_tchh;
bool needs_dyn_switch;
@@ -2091,9 +2090,8 @@ struct gsm_lchan *_select_sdcch_for_call(struct gsm_bts *bts, const struct chan_
needs_dyn_switch = lchan->ts->pchan_on_init == GSM_PCHAN_OSMO_DYN &&
lchan->ts->pchan_is != GSM_PCHAN_SDCCH8_SACCH8C;
- chan_counts_for_bts(&bts_counts, bts);
- free_tchf = bts_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_F];
- free_tchh = bts_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_H];
+ free_tchf = bts->chan_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_F];
+ free_tchh = bts->chan_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_H];
if (free_tchf == 0 && free_tchh == 0) {
LOG_BTS(bts, DRSL, LOGL_INFO,
"CHAN RQD: 0x%x Requesting %s reason=call but no TCH available\n",
diff --git a/src/osmo-bsc/bsc_stats.c b/src/osmo-bsc/bsc_stats.c
index e540f299f..e8c39bece 100644
--- a/src/osmo-bsc/bsc_stats.c
+++ b/src/osmo-bsc/bsc_stats.c
@@ -183,6 +183,8 @@ void bsc_update_connection_stats(struct gsm_network *net)
bts_oml_connected++;
if (trx_rsl_connected == num_trx)
bts_rsl_all_trx_connected++;
+
+ all_allocated_update_bts(bts);
}
osmo_stat_item_set(osmo_stat_item_group_get_item(net->bsc_statg, BSC_STAT_NUM_BTS_OML_CONNECTED),
@@ -194,8 +196,9 @@ void bsc_update_connection_stats(struct gsm_network *net)
trx_rsl_connected_total);
osmo_stat_item_set(osmo_stat_item_group_get_item(net->bsc_statg, BSC_STAT_NUM_TRX_TOTAL), num_trx_total);
- /* Make sure to notice cells that become disconnected */
- all_allocated_update_bsc();
+ /* This is optional, just running this to catch bugs in chan_counts accounting. If there is a bug, there will be
+ * a DLGLOBAL ERROR logged, and the error gets fixed. */
+ chan_counts_bsc_verify();
}
static void all_allocated_update(struct all_allocated *all_allocated, const struct chan_counts *c)
@@ -221,20 +224,13 @@ static void all_allocated_update(struct all_allocated *all_allocated, const stru
+ c->val[CHAN_COUNTS1_STATIC][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_H]));
}
+void all_allocated_update_bts(struct gsm_bts *bts)
+{
+ all_allocated_update(&bts->all_allocated, &bts->chan_counts);
+}
+
void all_allocated_update_bsc()
{
struct gsm_network *net = bsc_gsmnet;
- struct gsm_bts *bts;
- struct chan_counts bsc_counts;
-
- chan_counts_zero(&bsc_counts);
-
- llist_for_each_entry(bts, &net->bts_list, list) {
- struct chan_counts bts_counts;
- chan_counts_for_bts(&bts_counts, bts);
- all_allocated_update(&bts->all_allocated, &bts_counts);
- chan_counts_add(&bsc_counts, &bts_counts);
- }
-
- all_allocated_update(&net->all_allocated, &bsc_counts);
+ all_allocated_update(&net->all_allocated, &net->chan_counts);
}
diff --git a/src/osmo-bsc/chan_counts.c b/src/osmo-bsc/chan_counts.c
index 99e6e7649..04e162b3a 100644
--- a/src/osmo-bsc/chan_counts.c
+++ b/src/osmo-bsc/chan_counts.c
@@ -26,6 +26,8 @@
#include <osmocom/bsc/bts_trx.h>
#include <osmocom/bsc/lchan_fsm.h>
#include <osmocom/bsc/chan_counts.h>
+#include <osmocom/bsc/bsc_stats.h>
+#include <osmocom/bsc/signal.h>
static const unsigned int lchans_per_pchan[_GSM_PCHAN_MAX][_GSM_LCHAN_MAX] = {
[GSM_PCHAN_NONE] = {0},
@@ -68,75 +70,243 @@ static inline void chan_counts_per_pchan_add(struct chan_counts *dst,
dst->val[dim1][dim2][i] += lchans_per_pchan[pchan][i];
}
-void chan_counts_for_trx(struct chan_counts *trx_counts, const struct gsm_bts_trx *trx)
+static const char *chan_counts_dim1_name[_CHAN_COUNTS1_NUM] = {
+ [CHAN_COUNTS1_ALL] = "all",
+ [CHAN_COUNTS1_STATIC] = "static",
+ [CHAN_COUNTS1_DYNAMIC] = "dynamic",
+};
+
+static const char *chan_counts_dim2_name[_CHAN_COUNTS2_NUM] = {
+ [CHAN_COUNTS2_MAX_TOTAL] = "max",
+ [CHAN_COUNTS2_CURRENT_TOTAL] = "current",
+ [CHAN_COUNTS2_ALLOCATED] = "alloc",
+ [CHAN_COUNTS2_FREE] = "free",
+};
+
+int chan_counts_to_str_buf(char *buf, size_t buflen, const struct chan_counts *c)
+{
+ struct osmo_strbuf sb = { .buf = buf, .len = buflen };
+ int i1, i2, i3;
+ OSMO_STRBUF_PRINTF(sb, "{");
+ for (i1 = 0; i1 < _CHAN_COUNTS1_NUM; i1++) {
+ for (i2 = 0; i2 < _CHAN_COUNTS2_NUM; i2++) {
+ bool p12 = false;
+
+ for (i3 = 0; i3 < _GSM_LCHAN_MAX; i3++) {
+
+ int v = c->val[i1][i2][i3];
+ if (v) {
+ if (!p12) {
+ p12 = true;
+ OSMO_STRBUF_PRINTF(sb, " %s.%s{", chan_counts_dim1_name[i1],
+ chan_counts_dim2_name[i2]);
+ }
+ OSMO_STRBUF_PRINTF(sb, " %s=%d", gsm_lchant_name(i3), v);
+ }
+ }
+
+ if (p12)
+ OSMO_STRBUF_PRINTF(sb, " }");
+ }
+ }
+ OSMO_STRBUF_PRINTF(sb, " }");
+ return sb.chars_needed;
+}
+
+char *chan_counts_to_str_c(void *ctx, const struct chan_counts *c)
+{
+ OSMO_NAME_C_IMPL(ctx, 64, "ERROR", chan_counts_to_str_buf, c)
+}
+
+void chan_counts_for_ts(struct chan_counts *ts_counts, const struct gsm_bts_trx_ts *ts)
{
- const struct gsm_bts_trx_ts *ts;
const struct gsm_lchan *lchan;
- int i;
+ bool ts_is_dynamic;
- chan_counts_zero(trx_counts);
+ chan_counts_zero(ts_counts);
- if (!trx_is_usable(trx))
+ if (!ts_is_usable(ts))
return;
- for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
- bool ts_is_dynamic;
- struct chan_counts ts_count = {0};
- ts = &trx->ts[i];
- if (!ts_is_usable(ts))
- continue;
-
- /* Count the full potential nr of lchans for dynamic TS */
- chan_counts_per_pchan_add(&ts_count, CHAN_COUNTS1_ALL, CHAN_COUNTS2_MAX_TOTAL, ts->pchan_on_init);
-
- switch (ts->pchan_on_init) {
- case GSM_PCHAN_TCH_F_PDCH:
- case GSM_PCHAN_OSMO_DYN:
- ts_is_dynamic = true;
- break;
- default:
- ts_is_dynamic = false;
- break;
- }
+ /* Count the full potential nr of lchans for dynamic TS */
+ chan_counts_per_pchan_add(ts_counts, CHAN_COUNTS1_ALL, CHAN_COUNTS2_MAX_TOTAL, ts->pchan_on_init);
- if (ts_is_dynamic && ts->pchan_is == GSM_PCHAN_PDCH) {
- /* Dynamic timeslots in PDCH mode can become TCH or SDCCH immediately,
- * so set CURRENT_TOTAL = MAX_TOTAL. */
- chan_counts_dim3_add(&ts_count, CHAN_COUNTS1_ALL, CHAN_COUNTS2_CURRENT_TOTAL,
- &ts_count, CHAN_COUNTS1_ALL, CHAN_COUNTS2_MAX_TOTAL);
- } else {
- /* Static TS, or dyn TS that are currently fixed on a specific pchan: count lchans for the
- * current pchan mode. */
- chan_counts_per_pchan_add(&ts_count, CHAN_COUNTS1_ALL, CHAN_COUNTS2_CURRENT_TOTAL, ts->pchan_is);
- }
+ switch (ts->pchan_on_init) {
+ case GSM_PCHAN_TCH_F_PDCH:
+ case GSM_PCHAN_OSMO_DYN:
+ ts_is_dynamic = true;
+ break;
+ default:
+ ts_is_dynamic = false;
+ break;
+ }
- /* Count currently allocated lchans */
- ts_for_n_lchans(lchan, ts, ts->max_primary_lchans) {
- if (!lchan_state_is(lchan, LCHAN_ST_UNUSED))
- ts_count.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_ALLOCATED][lchan->type]++;
- }
+ if (ts_is_dynamic && ts->pchan_is == GSM_PCHAN_PDCH) {
+ /* Dynamic timeslots in PDCH mode can become TCH or SDCCH immediately,
+ * so set CURRENT_TOTAL = MAX_TOTAL. */
+ chan_counts_dim3_add(ts_counts, CHAN_COUNTS1_ALL, CHAN_COUNTS2_CURRENT_TOTAL,
+ ts_counts, CHAN_COUNTS1_ALL, CHAN_COUNTS2_MAX_TOTAL);
+ } else {
+ /* Static TS, or dyn TS that are currently fixed on a specific pchan: count lchans for the
+ * current pchan mode. */
+ chan_counts_per_pchan_add(ts_counts, CHAN_COUNTS1_ALL, CHAN_COUNTS2_CURRENT_TOTAL, ts->pchan_is);
+ }
+
+ /* Count currently allocated lchans */
+ ts_for_n_lchans(lchan, ts, ts->max_primary_lchans) {
+ if (!lchan_state_is(lchan, LCHAN_ST_UNUSED))
+ ts_counts->val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_ALLOCATED][lchan->type]++;
+ }
+
+ chan_counts_dim3_add(ts_counts, CHAN_COUNTS1_ALL, CHAN_COUNTS2_FREE,
+ ts_counts, CHAN_COUNTS1_ALL, CHAN_COUNTS2_CURRENT_TOTAL);
+ chan_counts_dim3_sub(ts_counts, CHAN_COUNTS1_ALL, CHAN_COUNTS2_FREE,
+ ts_counts, CHAN_COUNTS1_ALL, CHAN_COUNTS2_ALLOCATED);
- chan_counts_dim3_add(&ts_count, CHAN_COUNTS1_ALL, CHAN_COUNTS2_FREE,
- &ts_count, CHAN_COUNTS1_ALL, CHAN_COUNTS2_CURRENT_TOTAL);
- chan_counts_dim3_sub(&ts_count, CHAN_COUNTS1_ALL, CHAN_COUNTS2_FREE,
- &ts_count, CHAN_COUNTS1_ALL, CHAN_COUNTS2_ALLOCATED);
+ if (ts_is_dynamic)
+ chan_counts_dim2_add(ts_counts, CHAN_COUNTS1_DYNAMIC, ts_counts, CHAN_COUNTS1_ALL);
+ else
+ chan_counts_dim2_add(ts_counts, CHAN_COUNTS1_STATIC, ts_counts, CHAN_COUNTS1_ALL);
+}
+
+static void chan_counts_diff(struct chan_counts *diff, const struct chan_counts *left, const struct chan_counts *right)
+{
+ chan_counts_zero(diff);
+ chan_counts_add(diff, right);
+ chan_counts_sub(diff, left);
+}
+
+static void _chan_counts_ts_update(struct gsm_bts_trx_ts *ts, const struct chan_counts *ts_new_counts)
+{
+ struct chan_counts diff;
+
+ chan_counts_diff(&diff, &ts->chan_counts, ts_new_counts);
+ if (chan_counts_is_zero(&diff))
+ return;
- if (ts_is_dynamic)
- chan_counts_dim2_add(trx_counts, CHAN_COUNTS1_DYNAMIC, &ts_count, CHAN_COUNTS1_ALL);
- else
- chan_counts_dim2_add(trx_counts, CHAN_COUNTS1_STATIC, &ts_count, CHAN_COUNTS1_ALL);
- chan_counts_dim2_add(trx_counts, CHAN_COUNTS1_ALL, &ts_count, CHAN_COUNTS1_ALL);
+ ts->chan_counts = *ts_new_counts;
+ chan_counts_add(&ts->trx->chan_counts, &diff);
+ chan_counts_add(&ts->trx->bts->chan_counts, &diff);
+ chan_counts_add(&bsc_gsmnet->chan_counts, &diff);
+
+ all_allocated_update_bts(ts->trx->bts);
+ all_allocated_update_bsc();
+
+ LOGP(DLGLOBAL, LOGL_DEBUG, "change in channel counts: ts %u-%u-%u: %s\n",
+ ts->trx->bts->nr, ts->trx->nr, ts->nr, chan_counts_to_str_c(OTC_SELECT, &diff));
+ LOGP(DLGLOBAL, LOGL_DEBUG, "bsc channel counts: %s\n",
+ chan_counts_to_str_c(OTC_SELECT, &bsc_gsmnet->chan_counts));
+}
+
+/* Re-count this TS, and update ts->chan_counts. If the new ts->chan_counts differ, propagate the difference to
+ * trx->chan_counts, bts->chan_counts and gsm_network->chan_counts. */
+void chan_counts_ts_update(struct gsm_bts_trx_ts *ts)
+{
+ struct chan_counts ts_new_counts;
+ chan_counts_for_ts(&ts_new_counts, ts);
+ _chan_counts_ts_update(ts, &ts_new_counts);
+}
+
+void chan_counts_ts_clear(struct gsm_bts_trx_ts *ts)
+{
+ struct chan_counts ts_new_counts = {0};
+ _chan_counts_ts_update(ts, &ts_new_counts);
+}
+
+void chan_counts_trx_update(struct gsm_bts_trx *trx)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
+ struct gsm_bts_trx_ts *ts = &trx->ts[i];
+ chan_counts_ts_update(ts);
}
}
-void chan_counts_for_bts(struct chan_counts *bts_counts, const struct gsm_bts *bts)
+static int chan_counts_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data)
{
+ struct nm_running_chg_signal_data *nsd;
struct gsm_bts_trx *trx;
- chan_counts_zero(bts_counts);
+ /* This should be S_NM_RUNNING_CHG, but e.g. when switching rf_locked the NM FSMs fail to emit this signal.
+ * Using S_NM_STATECHG is overkill but works. */
+ if (signal != S_NM_STATECHG)
+ return 0;
+ nsd = signal_data;
+ switch (nsd->obj_class) {
+ case NM_OC_RADIO_CARRIER:
+ trx = (struct gsm_bts_trx *)nsd->obj;
+ break;
+ case NM_OC_BASEB_TRANSC:
+ trx = gsm_bts_bb_trx_get_trx((struct gsm_bts_bb_trx *)nsd->obj);
+ break;
+ default:
+ return 0;
+ }
+ chan_counts_trx_update(trx);
+ return 0;
+}
+
+void chan_counts_sig_init(void)
+{
+ osmo_signal_register_handler(SS_NM, chan_counts_sig_cb, NULL);
+}
+
+void chan_counts_bsc_verify()
+{
+ struct gsm_bts *bts;
+ struct chan_counts bsc_counts = {0};
+ struct chan_counts diff;
+
+ llist_for_each_entry(bts, &bsc_gsmnet->bts_list, list) {
+ struct gsm_bts_trx *trx;
+ struct chan_counts bts_counts = {0};
+
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ struct chan_counts trx_counts = {0};
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
+ struct chan_counts ts_counts;
+ struct gsm_bts_trx_ts *ts = &trx->ts[i];
+ chan_counts_for_ts(&ts_counts, ts);
+
+ chan_counts_diff(&diff, &ts->chan_counts, &ts_counts);
+ if (!chan_counts_is_zero(&diff)) {
+ LOGP(DLGLOBAL, LOGL_ERROR,
+ "internal error in channel counts, on bts-trx-ts %u-%u-%u, fixing."
+ " diff: %s\n",
+ bts->nr, trx->nr, ts->nr,
+ chan_counts_to_str_c(OTC_SELECT, &diff));
+ ts->chan_counts = ts_counts;
+ }
+
+ chan_counts_add(&trx_counts, &ts_counts);
+ }
+
+ chan_counts_diff(&diff, &trx->chan_counts, &trx_counts);
+ if (!chan_counts_is_zero(&diff)) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "internal error in channel counts, on bts-trx %u-%u, fixing."
+ " diff: %s\n",
+ bts->nr, trx->nr, chan_counts_to_str_c(OTC_SELECT, &diff));
+ trx->chan_counts = trx_counts;
+ }
+
+ chan_counts_add(&bts_counts, &trx_counts);
+ }
+
+ chan_counts_diff(&diff, &bts->chan_counts, &bts_counts);
+ if (!chan_counts_is_zero(&diff)) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "internal error in channel counts, on bts %u, fixing. diff: %s\n",
+ bts->nr, chan_counts_to_str_c(OTC_SELECT, &diff));
+ bts->chan_counts = bts_counts;
+ }
+
+ chan_counts_add(&bsc_counts, &bts_counts);
+ }
- llist_for_each_entry(trx, &bts->trx_list, list) {
- struct chan_counts trx_counts;
- chan_counts_for_trx(&trx_counts, trx);
- chan_counts_add(bts_counts, &trx_counts);
+ chan_counts_diff(&diff, &bsc_gsmnet->chan_counts, &bsc_counts);
+ if (!chan_counts_is_zero(&diff)) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "internal error in overall channel counts, fixing. diff: %s\n",
+ chan_counts_to_str_c(OTC_SELECT, &diff));
+ bsc_gsmnet->chan_counts = bsc_counts;
}
}
diff --git a/src/osmo-bsc/handover_decision_2.c b/src/osmo-bsc/handover_decision_2.c
index c88e84668..a1a8ffa8f 100644
--- a/src/osmo-bsc/handover_decision_2.c
+++ b/src/osmo-bsc/handover_decision_2.c
@@ -1004,13 +1004,13 @@ static inline void debug_candidate(struct ho_candidate *candidate)
static void candidate_set_free_tch(struct ho_candidate *c)
{
- struct chan_counts bts_counts;
+ struct chan_counts *bts_counts;
struct gsm_lchan *next_lchan;
- chan_counts_for_bts(&bts_counts, c->current.bts);
- c->current.free_tchf = bts_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_F];
+ bts_counts = &c->current.bts->chan_counts;
+ c->current.free_tchf = bts_counts->val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_F];
c->current.min_free_tchf = ho_get_hodec2_tchf_min_slots(c->current.bts->ho);
- c->current.free_tchh = bts_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_H];
+ c->current.free_tchh = bts_counts->val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_H];
c->current.min_free_tchh = ho_get_hodec2_tchh_min_slots(c->current.bts->ho);
switch (c->current.lchan->ts->pchan_is) {
@@ -1042,10 +1042,10 @@ static void candidate_set_free_tch(struct ho_candidate *c)
/* For inter-BSC handover, the target BTS is in a different BSC and hence NULL here. */
if (c->target.bts) {
- chan_counts_for_bts(&bts_counts, c->target.bts);
- c->target.free_tchf = bts_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_F];
+ bts_counts = &c->target.bts->chan_counts;
+ c->target.free_tchf = bts_counts->val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_F];
c->target.min_free_tchf = ho_get_hodec2_tchf_min_slots(c->target.bts->ho);
- c->target.free_tchh = bts_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_H];
+ c->target.free_tchh = bts_counts->val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_H];
c->target.min_free_tchh = ho_get_hodec2_tchh_min_slots(c->target.bts->ho);
/* Would the next TCH/F lchan occupy a dynamic timeslot that currently counts for free TCH/H timeslots?
@@ -1959,7 +1959,7 @@ exit:
static void bts_congestion_check(struct gsm_bts *bts)
{
- struct chan_counts bts_counts;
+ struct chan_counts *bts_counts;
int min_free_tchf, min_free_tchh;
int free_tchf, free_tchh;
@@ -1987,9 +1987,9 @@ static void bts_congestion_check(struct gsm_bts *bts)
return;
}
- chan_counts_for_bts(&bts_counts, bts);
- free_tchf = bts_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_F];
- free_tchh = bts_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_H];
+ bts_counts = &bts->chan_counts;
+ free_tchf = bts_counts->val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_F];
+ free_tchh = bts_counts->val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_H];
LOGPHOBTS(bts, LOGL_INFO, "Congestion check: (free/want-free) TCH/F=%d/%d TCH/H=%d/%d\n",
free_tchf, min_free_tchf, free_tchh, min_free_tchh);
diff --git a/src/osmo-bsc/lchan_fsm.c b/src/osmo-bsc/lchan_fsm.c
index e3ae4a67f..2d566b698 100644
--- a/src/osmo-bsc/lchan_fsm.c
+++ b/src/osmo-bsc/lchan_fsm.c
@@ -523,15 +523,20 @@ static void lchan_fsm_unused_onenter(struct osmo_fsm_inst *fi, uint32_t prev_sta
struct gsm_lchan *lchan = lchan_fi_lchan(fi);
struct gsm_bts *bts = lchan->ts->trx->bts;
lchan_reset(lchan);
+ chan_counts_ts_update(lchan->ts);
osmo_fsm_inst_dispatch(lchan->ts->fi, TS_EV_LCHAN_UNUSED, lchan);
- all_allocated_update_bsc();
-
/* Poll the channel request queue, so that waiting calls can make use of the lchan that just
* has become unused now. */
abis_rsl_chan_rqd_queue_poll(bts);
}
+static void lchan_fsm_cbch_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_lchan *lchan = lchan_fi_lchan(fi);
+ chan_counts_ts_update(lchan->ts);
+}
+
static void lchan_fsm_wait_after_error_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_lchan *lchan = lchan_fi_lchan(fi);
@@ -707,7 +712,7 @@ static void lchan_fsm_wait_ts_ready_onenter(struct osmo_fsm_inst *fi, uint32_t p
return;
}
- all_allocated_update_bsc();
+ chan_counts_ts_update(lchan->ts);
lchan->conn = info->for_conn;
@@ -1477,6 +1482,7 @@ static void lchan_fsm_borken_onenter(struct osmo_fsm_inst *fi, uint32_t prev_sta
/* The actual action besides all the beancounting above */
lchan_reset(lchan);
+ chan_counts_ts_update(lchan->ts);
}
static void lchan_fsm_borken(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -1550,6 +1556,7 @@ static const struct osmo_fsm_state lchan_fsm_states[] = {
},
[LCHAN_ST_CBCH] = {
.name = "CBCH",
+ .onenter = lchan_fsm_cbch_onenter,
.out_state_mask = 0
| S(LCHAN_ST_UNUSED)
,
diff --git a/src/osmo-bsc/osmo_bsc_main.c b/src/osmo-bsc/osmo_bsc_main.c
index c78bb45ef..f6da74a81 100644
--- a/src/osmo-bsc/osmo_bsc_main.c
+++ b/src/osmo-bsc/osmo_bsc_main.c
@@ -1026,6 +1026,7 @@ int main(int argc, char **argv)
osmo_init_ignore_signals();
update_connection_stats_cb(NULL);
+ chan_counts_sig_init();
if (daemonize) {
rc = osmo_daemonize();
diff --git a/src/osmo-bsc/timeslot_fsm.c b/src/osmo-bsc/timeslot_fsm.c
index 86fea3c8d..fbf6c5e5f 100644
--- a/src/osmo-bsc/timeslot_fsm.c
+++ b/src/osmo-bsc/timeslot_fsm.c
@@ -229,6 +229,7 @@ void ts_set_pchan_is(struct gsm_bts_trx_ts *ts, enum gsm_phys_chan_config pchan_
}
break;
}
+ chan_counts_ts_update(ts);
}
static void ts_setup_lchans(struct gsm_bts_trx_ts *ts)
@@ -315,11 +316,19 @@ static void ts_fsm_not_initialized(struct osmo_fsm_inst *fi, uint32_t event, voi
osmo_fsm_inst_state_chg(fi, TS_ST_UNUSED, 0, 0);
}
+static void ts_fsm_not_initialized_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct gsm_bts_trx_ts *ts = ts_fi_ts(fi);
+ chan_counts_ts_clear(ts);
+}
+
static void ts_fsm_unused_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_bts_trx_ts *ts = ts_fi_ts(fi);
struct gsm_bts *bts = ts->trx->bts;
+ chan_counts_ts_update(ts);
+
/* We are entering the unused state. There must by definition not be any lchans waiting to be
* activated. */
if (ts_lchans_waiting(ts)) {
@@ -665,6 +674,8 @@ static void ts_fsm_in_use_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
return;
}
+ chan_counts_ts_update(ts);
+
/* Make sure dyn TS pchan_is is updated. For TCH/F_PDCH, there are only PDCH or TCH/F modes, but
* for Osmocom style TCH/F_TCH/H_SDCCH8_PDCH the pchan_is == NONE until an lchan is activated. */
if (ts->pchan_on_init == GSM_PCHAN_OSMO_DYN)
@@ -848,6 +859,7 @@ static void ts_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause ca
static const struct osmo_fsm_state ts_fsm_states[] = {
[TS_ST_NOT_INITIALIZED] = {
.name = "NOT_INITIALIZED",
+ .onenter = ts_fsm_not_initialized_onenter,
.action = ts_fsm_not_initialized,
.in_event_mask = 0
| S(TS_EV_OML_READY)
diff --git a/tests/handover/handover_test.c b/tests/handover/handover_test.c
index c01b6ee83..d82ba9f90 100644
--- a/tests/handover/handover_test.c
+++ b/tests/handover/handover_test.c
@@ -479,6 +479,8 @@ struct gsm_lchan *lchan_act(struct gsm_lchan *lchan, int full_rate, const char *
.len = 5,
};
+ chan_counts_ts_update(lchan->ts);
+
return lchan;
}
@@ -522,6 +524,7 @@ static void ts_clear(struct gsm_bts_trx_ts *ts)
continue;
lchan_clear(lchan);
}
+ chan_counts_ts_update(ts);
}
bool _set_ts_use(struct gsm_bts *bts, struct gsm_bts_trx *trx, const char * const *ts_use)