aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/osmocom/bsc/chan_alloc.h6
-rw-r--r--include/osmocom/bsc/gsm_data.h3
-rw-r--r--include/osmocom/bsc/gsm_data_shared.h13
-rw-r--r--src/libbsc/abis_rsl.c41
-rw-r--r--src/libbsc/bsc_init.c2
-rw-r--r--src/libbsc/bsc_vty.c2
-rw-r--r--src/libbsc/chan_alloc.c75
-rw-r--r--src/libbsc/net_init.c24
-rw-r--r--src/libcommon/gsm_data_shared.c3
-rw-r--r--tests/channel/Makefile.am1
-rw-r--r--tests/channel/channel_test.c3
11 files changed, 143 insertions, 30 deletions
diff --git a/include/osmocom/bsc/chan_alloc.h b/include/osmocom/bsc/chan_alloc.h
index 748e9cd9f..98568a50f 100644
--- a/include/osmocom/bsc/chan_alloc.h
+++ b/include/osmocom/bsc/chan_alloc.h
@@ -37,17 +37,13 @@ void lchan_reset(struct gsm_lchan *lchan);
/* Release the given lchan */
int lchan_release(struct gsm_lchan *lchan, int sacch_deact, enum rsl_rel_mode release_mode);
-struct load_counter {
- unsigned int total;
- unsigned int used;
-};
-
struct pchan_load {
struct load_counter pchan[_GSM_PCHAN_MAX];
};
void bts_chan_load(struct pchan_load *cl, const struct gsm_bts *bts);
void network_chan_load(struct pchan_load *pl, struct gsm_network *net);
+void bts_update_t3122_chan_load(struct gsm_bts *bts);
bool trx_is_usable(const struct gsm_bts_trx *trx);
bool ts_is_usable(const struct gsm_bts_trx_ts *ts);
diff --git a/include/osmocom/bsc/gsm_data.h b/include/osmocom/bsc/gsm_data.h
index bdf7cfba7..25dee7876 100644
--- a/include/osmocom/bsc/gsm_data.h
+++ b/include/osmocom/bsc/gsm_data.h
@@ -308,6 +308,9 @@ struct gsm_network {
/* Periodic location update default value */
uint8_t t3212;
+ /* Timer for periodic channel load measurements to maintain each BTS's T3122. */
+ struct osmo_timer_list t3122_chan_load_timer;
+
struct {
struct mgcp_client_conf *conf;
struct mgcp_client *client;
diff --git a/include/osmocom/bsc/gsm_data_shared.h b/include/osmocom/bsc/gsm_data_shared.h
index 86c5ca928..504b42a17 100644
--- a/include/osmocom/bsc/gsm_data_shared.h
+++ b/include/osmocom/bsc/gsm_data_shared.h
@@ -573,6 +573,12 @@ struct bts_location {
double height;
};
+/* Channel load counter */
+struct load_counter {
+ unsigned int total;
+ unsigned int used;
+};
+
/* One BTS */
struct gsm_bts {
/* list header in net->bts_list */
@@ -802,6 +808,13 @@ struct gsm_bts {
struct rate_ctr_group *bts_ctrs;
struct handover_cfg *ho;
+
+ /* BTS-specific overrides for timer values from struct gsm_network. */
+ uint8_t T3122; /* ASSIGMENT REJECT wait indication */
+
+ /* Periodic channel load measurements are used to maintain T3122. */
+ struct load_counter chan_load_samples[7];
+ int chan_load_samples_idx;
};
diff --git a/src/libbsc/abis_rsl.c b/src/libbsc/abis_rsl.c
index eced0e25c..3f8bbc904 100644
--- a/src/libbsc/abis_rsl.c
+++ b/src/libbsc/abis_rsl.c
@@ -1776,8 +1776,7 @@ static void t3109_expired(void *data)
/* Format an IMM ASS REJ according to 04.08 Chapter 9.1.20 */
static int rsl_send_imm_ass_rej(struct gsm_bts *bts,
- unsigned int num_req_refs,
- struct gsm48_req_ref *rqd_refs,
+ struct gsm48_req_ref *rqd_ref,
uint8_t wait_ind)
{
uint8_t buf[GSM_MACBLOCK_LEN];
@@ -1789,25 +1788,19 @@ static int rsl_send_imm_ass_rej(struct gsm_bts *bts,
iar->msg_type = GSM48_MT_RR_IMM_ASS_REJ;
iar->page_mode = GSM48_PM_SAME;
- memcpy(&iar->req_ref1, &rqd_refs[0], sizeof(iar->req_ref1));
+ /*
+ * Set all request references and wait indications to the same value.
+ * 3GPP TS 44.018 v4.5.0 release 4 (section 9.1.20.2) requires that
+ * we duplicate reference and wait indication to fill the message.
+ * The BTS will aggregate up to 4 of our ASS REJ messages if possible.
+ */
+ memcpy(&iar->req_ref1, rqd_ref, sizeof(iar->req_ref1));
iar->wait_ind1 = wait_ind;
-
- if (num_req_refs >= 2)
- memcpy(&iar->req_ref2, &rqd_refs[1], sizeof(iar->req_ref2));
- else
- memcpy(&iar->req_ref2, &rqd_refs[0], sizeof(iar->req_ref2));
+ memcpy(&iar->req_ref2, rqd_ref, sizeof(iar->req_ref2));
iar->wait_ind2 = wait_ind;
-
- if (num_req_refs >= 3)
- memcpy(&iar->req_ref3, &rqd_refs[2], sizeof(iar->req_ref3));
- else
- memcpy(&iar->req_ref3, &rqd_refs[0], sizeof(iar->req_ref3));
+ memcpy(&iar->req_ref3, rqd_ref, sizeof(iar->req_ref3));
iar->wait_ind3 = wait_ind;
-
- if (num_req_refs >= 4)
- memcpy(&iar->req_ref4, &rqd_refs[3], sizeof(iar->req_ref4));
- else
- memcpy(&iar->req_ref4, &rqd_refs[0], sizeof(iar->req_ref4));
+ memcpy(&iar->req_ref4, rqd_ref, sizeof(iar->req_ref4));
iar->wait_ind4 = wait_ind;
/* we need to subtract 1 byte from sizeof(*iar) since ia includes the l2_plen field */
@@ -1913,12 +1906,18 @@ static int rsl_rx_chan_rqd(struct msgb *msg)
lchan = lchan_alloc(bts, lctype, 0);
}
if (!lchan) {
+ uint8_t wait_ind;
LOGP(DRSL, LOGL_NOTICE, "BTS %d CHAN RQD: no resources for %s 0x%x\n",
msg->lchan->ts->trx->bts->nr, gsm_lchant_name(lctype), rqd_ref->ra);
rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_CHREQ_NO_CHANNEL]);
- /* FIXME gather multiple CHAN RQD and reject up to 4 at the same time */
- if (bts->network->T3122)
- rsl_send_imm_ass_rej(bts, 1, rqd_ref, bts->network->T3122 & 0xff);
+ if (bts->T3122)
+ wait_ind = bts->T3122;
+ else if (bts->network->T3122)
+ wait_ind = bts->network->T3122 & 0xff;
+ else
+ wait_ind = GSM_T3122_DEFAULT;
+ /* The BTS will gather multiple CHAN RQD and reject up to 4 MS at the same time. */
+ rsl_send_imm_ass_rej(bts, rqd_ref, wait_ind);
return 0;
}
diff --git a/src/libbsc/bsc_init.c b/src/libbsc/bsc_init.c
index c5a75aa5b..2b1d53bbb 100644
--- a/src/libbsc/bsc_init.c
+++ b/src/libbsc/bsc_init.c
@@ -523,6 +523,8 @@ static int bootstrap_bts(struct gsm_bts *bts)
bts->si_common.ncc_permitted = 0xff;
+ bts->chan_load_samples_idx = 0;
+
/* Initialize the BTS state */
gsm_bts_mo_reset(bts);
diff --git a/src/libbsc/bsc_vty.c b/src/libbsc/bsc_vty.c
index b4a8e2ddc..2246349fd 100644
--- a/src/libbsc/bsc_vty.c
+++ b/src/libbsc/bsc_vty.c
@@ -1711,7 +1711,7 @@ DECLARE_TIMER(3113, "Set the time to try paging a subscriber")
DECLARE_TIMER(3115, "Currently not used")
DECLARE_TIMER(3117, "Currently not used")
DECLARE_TIMER(3119, "Currently not used")
-DECLARE_TIMER(3122, "Waiting time (seconds) after IMM ASS REJECT")
+DECLARE_TIMER(3122, "Default waiting time (seconds) after IMM ASS REJECT")
DECLARE_TIMER(3141, "Currently not used")
DEFUN_DEPRECATED(cfg_net_dtx,
diff --git a/src/libbsc/chan_alloc.c b/src/libbsc/chan_alloc.c
index 21c81aa75..5e2c0ee22 100644
--- a/src/libbsc/chan_alloc.c
+++ b/src/libbsc/chan_alloc.c
@@ -593,3 +593,78 @@ void network_chan_load(struct pchan_load *pl, struct gsm_network *net)
bts_chan_load(pl, bts);
}
+/* Update T3122 wait indicator based on samples of BTS channel load. */
+void
+bts_update_t3122_chan_load(struct gsm_bts *bts)
+{
+ struct pchan_load pl;
+ uint64_t used = 0;
+ uint32_t total = 0;
+ uint64_t load;
+ uint64_t wait_ind;
+ static const uint8_t min_wait_ind = GSM_T3122_DEFAULT;
+ static const uint8_t max_wait_ind = 128; /* max wait ~2 minutes */
+ int i;
+
+ /* Sum up current load across all channels. */
+ memset(&pl, 0, sizeof(pl));
+ bts_chan_load(&pl, bts);
+ for (i = 0; i < ARRAY_SIZE(pl.pchan); i++) {
+ struct load_counter *lc = &pl.pchan[i];
+
+ /* Ignore samples too large for fixed-point calculations (shouldn't happen). */
+ if (lc->used > UINT16_MAX || lc->total > UINT16_MAX) {
+ LOGP(DRLL, LOGL_NOTICE, "(bts=%d) numbers in channel load sample "
+ "too large (used=%u / total=%u)\n", bts->nr, lc->used, lc->total);
+ continue;
+ }
+
+ used += lc->used;
+ total += lc->total;
+ }
+
+ /* Check for invalid samples (shouldn't happen). */
+ if (total == 0 || used > total) {
+ LOGP(DRLL, LOGL_NOTICE, "(bts=%d) bogus channel load sample (used=%lu / total=%u)\n",
+ bts->nr, used, total);
+ bts->T3122 = 0; /* disable override of network-wide default value */
+ bts->chan_load_samples_idx = 0; /* invalidate other samples collected so far */
+ return;
+ }
+
+ /* If we haven't got enough samples yet, store measurement for later use. */
+ if (bts->chan_load_samples_idx < ARRAY_SIZE(bts->chan_load_samples)) {
+ struct load_counter *sample = &bts->chan_load_samples[bts->chan_load_samples_idx++];
+ sample->total = (unsigned int)total;
+ sample->used = (unsigned int)used;
+ return;
+ }
+
+ /* We have enough samples and will overwrite our current samples later. */
+ bts->chan_load_samples_idx = 0;
+
+ /* Add all previous samples to the current sample. */
+ for (i = 0; i < ARRAY_SIZE(bts->chan_load_samples); i++) {
+ struct load_counter *sample = &bts->chan_load_samples[i];
+ total += sample->total;
+ used += sample->used;
+ }
+
+ used <<= 8; /* convert to fixed-point */
+
+ /* Log channel load average. */
+ load = ((used / total) * 100);
+ LOGP(DRLL, LOGL_DEBUG, "(bts=%d) channel load average is %lu.%.2lu%%\n",
+ bts->nr, (load & 0xffffff00) >> 8, (load & 0xff) / 10);
+
+ /* Calculate new T3122 wait indicator. */
+ wait_ind = ((used / total) * max_wait_ind);
+ wait_ind >>= 8; /* convert from fixed-point to integer */
+ if (wait_ind < min_wait_ind)
+ wait_ind = min_wait_ind;
+ else if (wait_ind > max_wait_ind)
+ wait_ind = max_wait_ind;
+
+ LOGP(DRLL, LOGL_DEBUG, "(bts=%d) T3122 wait indicator set to %lu seconds\n", bts->nr, wait_ind);
+ bts->T3122 = (uint8_t)wait_ind;
+}
diff --git a/src/libbsc/net_init.c b/src/libbsc/net_init.c
index 57d824156..435c0ce6a 100644
--- a/src/libbsc/net_init.c
+++ b/src/libbsc/net_init.c
@@ -22,6 +22,22 @@
#include <osmocom/bsc/bsc_msc_data.h>
#include <osmocom/bsc/gsm_04_08_utils.h>
#include <osmocom/bsc/handover_cfg.h>
+#include <osmocom/bsc/chan_alloc.h>
+
+/* XXX hard-coded for now */
+#define T3122_CHAN_LOAD_SAMPLE_INTERVAL 1 /* in seconds */
+
+static void update_t3122_chan_load_timer(void *data)
+{
+ struct gsm_network *net = data;
+ struct gsm_bts *bts;
+
+ llist_for_each_entry(bts, &net->bts_list, list)
+ bts_update_t3122_chan_load(bts);
+
+ /* Keep this timer ticking. */
+ osmo_timer_schedule(&net->t3122_chan_load_timer, T3122_CHAN_LOAD_SAMPLE_INTERVAL, 0);
+}
struct gsm_network *bsc_network_init(void *ctx,
uint16_t country_code,
@@ -60,6 +76,14 @@ struct gsm_network *bsc_network_init(void *ctx,
INIT_LLIST_HEAD(&net->bts_list);
+ /*
+ * At present all BTS in the network share one channel load timeout.
+ * If this becomes a problem for networks with a lot of BTS, this
+ * code could be refactored to run the timeout individually per BTS.
+ */
+ osmo_timer_setup(&net->t3122_chan_load_timer, update_t3122_chan_load_timer, net);
+ osmo_timer_schedule(&net->t3122_chan_load_timer, T3122_CHAN_LOAD_SAMPLE_INTERVAL, 0);
+
/* init statistics */
net->bsc_ctrs = rate_ctr_group_alloc(net, &bsc_ctrg_desc, 0);
if (!net->bsc_ctrs) {
diff --git a/src/libcommon/gsm_data_shared.c b/src/libcommon/gsm_data_shared.c
index e4ec594f8..3afc67ebc 100644
--- a/src/libcommon/gsm_data_shared.c
+++ b/src/libcommon/gsm_data_shared.c
@@ -377,6 +377,9 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, uint8_t bts_num)
bts->ho = ho_cfg_init(bts, net->ho);
+ /* timer overrides */
+ bts->T3122 = 0; /* not overriden by default */
+
return bts;
}
diff --git a/tests/channel/Makefile.am b/tests/channel/Makefile.am
index 12f18f85c..aae7a4532 100644
--- a/tests/channel/Makefile.am
+++ b/tests/channel/Makefile.am
@@ -29,4 +29,5 @@ channel_test_LDADD = \
$(top_builddir)/src/libcommon/libcommon.a \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
+ $(LIBOSMOABIS_LIBS) \
$(NULL)
diff --git a/tests/channel/channel_test.c b/tests/channel/channel_test.c
index 7957b14a7..933df9c9d 100644
--- a/tests/channel/channel_test.c
+++ b/tests/channel/channel_test.c
@@ -98,12 +98,9 @@ int main(int argc, char **argv)
return EXIT_SUCCESS;
}
-void _abis_nm_sendmsg() {}
void sms_alloc() {}
void sms_free() {}
-void gsm_net_update_ctype(struct gsm_network *network) {}
void gsm48_secure_channel() {}
-void paging_request_stop() {}
void vty_out() {}
void ipa_client_conn_clear_queue() {}