diff options
-rw-r--r-- | include/osmocom/bsc/chan_alloc.h | 6 | ||||
-rw-r--r-- | include/osmocom/bsc/gsm_data.h | 3 | ||||
-rw-r--r-- | include/osmocom/bsc/gsm_data_shared.h | 13 | ||||
-rw-r--r-- | src/libbsc/abis_rsl.c | 41 | ||||
-rw-r--r-- | src/libbsc/bsc_init.c | 2 | ||||
-rw-r--r-- | src/libbsc/bsc_vty.c | 2 | ||||
-rw-r--r-- | src/libbsc/chan_alloc.c | 75 | ||||
-rw-r--r-- | src/libbsc/net_init.c | 24 | ||||
-rw-r--r-- | src/libcommon/gsm_data_shared.c | 3 | ||||
-rw-r--r-- | tests/channel/Makefile.am | 1 | ||||
-rw-r--r-- | tests/channel/channel_test.c | 3 |
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() {} |