fix performance for chan_counts and all_allocated stats

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
This commit is contained in:
Neels Hofmeyr 2022-05-30 02:39:17 +02:00
parent 4c89001ccb
commit c1bfc88b6b
13 changed files with 324 additions and 95 deletions

View File

@ -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();

View File

@ -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;
};

View File

@ -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) {

View File

@ -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);
}

View File

@ -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;
};

View File

@ -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",

View File

@ -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);
}

View File

@ -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,241 @@ 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_counts, CHAN_COUNTS1_ALL, CHAN_COUNTS2_MAX_TOTAL, ts->pchan_on_init);
/* 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;
}
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);
}
/* 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]++;
}
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(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);
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;
}
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);
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);
}
void chan_counts_for_bts(struct chan_counts *bts_counts, const struct gsm_bts *bts)
static void chan_counts_diff(struct chan_counts *diff, const struct chan_counts *left, const struct chan_counts *right)
{
struct gsm_bts_trx *trx;
chan_counts_zero(bts_counts);
chan_counts_zero(diff);
chan_counts_add(diff, right);
chan_counts_sub(diff, left);
}
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);
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;
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);
}
}
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;
if (signal != S_NM_RUNNING_CHG)
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);
}
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;
}
}

View File

@ -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);

View File

@ -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)
,

View File

@ -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();

View File

@ -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)

View File

@ -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)