From a019e9af8bdb8dd01e801f7686cfaf99a21241b9 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Thu, 16 Jun 2022 02:10:16 +0700 Subject: [PATCH] lchan_select: implement dynamic selection mode for assignment This change implements an additional channel allocation mode, which can be employed during a TCH channel allocation for assignment. Selection between ascending and descending order is performed depending on pre-configured parameters: * Uplink RxLev threshold and number of samples for averaging, * C0 (BCCH carrier) channel load threshold. This is useful in setups where Tx power of the RF carriers cannot be adjusted +dynamically at run-time and thus BS Power Control cannot be performed. In such setups the BCCH carrier is transmitting at relatively higher power than the other RF carriers. The key idea is to allocate channels in a smarter way, so that UEs with poor signal would get channels on carriers with high Tx power, while UEs with good signal could use carriers with lower Tx power. Change-Id: I1b7a0d706976b73cc5c30a8714b830811addfe8d Related: osmo-ttcn3-hacks.git Ia522f37c1c001b3a36f5145b8875fbb88311c2e5 Related: SYS#5460 --- doc/manuals/chapters/chan_alloc.adoc | 67 ++++++++++++++++- include/osmocom/bsc/bts.h | 10 +++ include/osmocom/bsc/lchan_select.h | 12 ++- src/osmo-bsc/abis_rsl.c | 15 ++-- src/osmo-bsc/assignment_fsm.c | 2 +- src/osmo-bsc/bsc_vty.c | 6 +- src/osmo-bsc/bts.c | 3 + src/osmo-bsc/bts_vty.c | 103 +++++++++++++++++++++++++- src/osmo-bsc/handover_decision_2.c | 6 +- src/osmo-bsc/handover_fsm.c | 5 +- src/osmo-bsc/lchan_select.c | 105 ++++++++++++++++++++++++--- tests/handover/handover_test.c | 2 +- tests/osmo-bsc.vty | 26 +++++++ 13 files changed, 329 insertions(+), 33 deletions(-) diff --git a/doc/manuals/chapters/chan_alloc.adoc b/doc/manuals/chapters/chan_alloc.adoc index b6b7b9285..ae2ce211e 100644 --- a/doc/manuals/chapters/chan_alloc.adoc +++ b/doc/manuals/chapters/chan_alloc.adoc @@ -28,12 +28,15 @@ network ==== Channel allocation modes -Currently only the following two simple channel allocation modes are supported: +Currently the following channel allocation modes are supported: - ascending (default): allocates channels in ascending order, starting from timeslot 0 of the first TRX (also called C0, the BCCH carrier); - descending: allocates channels in descending order, -starting from timeslot 7 of the last TRX. +starting from timeslot 7 of the last TRX; +- dynamic (only for assignment): dynamically choose between ascending +and descending order depending on some additional parameters +(see <>). NOTE: Regardless of the chosen mode, logical channels (sub-slots) are always selected in ascending order. For example, if a timeslot is configured as SDCCH/8 @@ -52,12 +55,72 @@ OsmoBSC(config-net-bts)# channel allocator mode ? <1> OsmoBSC(config-net-bts)# channel allocator mode set-all ? <2> ascending Allocate Timeslots and Transceivers in ascending order descending Allocate Timeslots and Transceivers in descending order + +OsmoBSC(config-net-bts)# channel allocator mode assignment ? <3> + ascending Allocate Timeslots and Transceivers in ascending order + descending Allocate Timeslots and Transceivers in descending order + dynamic Dynamic lchan selection based on configured parameters <3> ---- <1> It's optionally possible to configure different allocation modes for different allocation causes, e.g. `ascending` for `chan-req` and `descending` for both `assignment` and `handover`. <2> `set-all` is equivalent to the old (deprecated) command syntax: `channel allocator (ascending|descending)`. +<3> The `dynamic` mode can be selected only for `assignment`. + +[[chan_alloc_dyn_mode]] +===== Dynamic channel allocation mode + +There exists an additional channel allocation mode, which can be employed +during a TCH channel allocation for assignment. This mode selects between +ascending and descending order depending on pre-configured parameters: + +- Uplink RxLev threshold and number of samples for averaging, +- C0 (BCCH carrier) channel load threshold. + +This is useful in setups where Tx power of the RF carriers cannot be adjusted +dynamically at run-time and thus no BS Power Control can be performed. In +such setups the BCCH carrier is transmitting at relatively higher power than +the other RF carriers. The key idea is to allocate channels in a smarter way, +so that UEs with poor signal would get channels on carriers with high Tx power, +while UEs with good signal could use carriers with lower Tx power. + +The configuration parameters for dynamic selection are listed below: + +---- +OsmoBSC(config-net-bts)# channel allocator dynamic-param ? + sort-by-trx-power Whether to sort TRX instances by their respective power levels + ul-rxlev Uplink RxLev + c0-chan-load C0 (BCCH carrier) channel load + +channel allocator dynamic-param sort-by-trx-power ? + 0 Do not sort, use the same order as in the configuration file + 1 Sort TRX instances by their power levels in descending order + +OsmoBSC(config-net-bts)# channel allocator dynamic-param ul-rxlev thresh ? + <0-63> Uplink RxLev threshold +OsmoBSC(config-net-bts)# channel allocator dynamic-param ul-rxlev thresh 50 avg-num ? + <1-10> Minimum number of RxLev samples for averaging +OsmoBSC(config-net-bts)# channel allocator dynamic-param c0-chan-load thresh ? + <0-100> Channel load threshold (in %) +---- + +The default values are: + +---- +network + bts 0 + channel allocator dynamic-param sort-by-trx-power 0 <1> + channel allocator dynamic-param ul-rxlev thresh 50 avg-num 2 <2> + channel allocator dynamic-param c0-chan-load thresh 60 <3> +---- +<1> Assume that RF carriers are listed in descending order sorted by Tx power. +<2> Use descending order if AVG of at least two Uplink RxLev samples >= 50 (-60 dBm). +<3> Use descending order if more than 60% logical channels of C0 are occupied. + +NOTE: The final ascending/descending order decision is based on the two conditions. +The descending order will be used only if *both conditions are met*, otherwise the +allocator will use ascending order. ==== Interference aware channel allocation diff --git a/include/osmocom/bsc/bts.h b/include/osmocom/bsc/bts.h index 76945a136..752a00a73 100644 --- a/include/osmocom/bsc/bts.h +++ b/include/osmocom/bsc/bts.h @@ -521,6 +521,16 @@ struct gsm_bts { bool chan_alloc_assignment_reverse; bool chan_alloc_handover_reverse; + /* Whether to use dynamic allocation mode for assignment */ + bool chan_alloc_assignment_dynamic; + /* Parameters used for dynamic mode of allocation */ + struct { + bool sort_by_trx_power; + uint8_t ul_rxlev_thresh; + uint8_t ul_rxlev_avg_num; + uint8_t c0_chan_load_thresh; + } chan_alloc_dyn_params; + /* When true, interference measurements from the BTS are used in the channel allocator to favor lchans with less * interference reported in RSL Resource Indication. */ bool chan_alloc_avoid_interf; diff --git a/include/osmocom/bsc/lchan_select.h b/include/osmocom/bsc/lchan_select.h index b5eb078db..8f1dc628a 100644 --- a/include/osmocom/bsc/lchan_select.h +++ b/include/osmocom/bsc/lchan_select.h @@ -13,12 +13,16 @@ static inline const char *lchan_select_reason_name(enum lchan_select_reason reas struct gsm_lchan *lchan_select_by_type(struct gsm_bts *bts, enum gsm_chan_t type, - enum lchan_select_reason reason); + enum lchan_select_reason reason, + void *ctx); enum gsm_chan_t chan_mode_to_chan_type(enum gsm48_chan_mode chan_mode, enum channel_rate chan_rate); struct gsm_lchan *lchan_select_by_chan_mode(struct gsm_bts *bts, enum gsm48_chan_mode chan_mode, enum channel_rate chan_rate, - enum lchan_select_reason reason); -struct gsm_lchan *lchan_avail_by_type(struct gsm_bts *bts, enum gsm_chan_t type, - enum lchan_select_reason reason, bool log); + enum lchan_select_reason reason, + void *ctx); +struct gsm_lchan *lchan_avail_by_type(struct gsm_bts *bts, + enum gsm_chan_t type, + enum lchan_select_reason reason, + void *ctx, bool log); void lchan_select_set_type(struct gsm_lchan *lchan, enum gsm_chan_t type); diff --git a/src/osmo-bsc/abis_rsl.c b/src/osmo-bsc/abis_rsl.c index 7f88096e0..527706a02 100644 --- a/src/osmo-bsc/abis_rsl.c +++ b/src/osmo-bsc/abis_rsl.c @@ -2007,12 +2007,12 @@ static bool force_free_lchan_for_emergency(struct chan_rqd *rqd) /* First check the situation on the BTS, if we have TCH/H or TCH/F resources available for another (EMERGENCY) * call. If yes, then no (further) action has to be carried out. */ - if (lchan_avail_by_type(rqd->bts, GSM_LCHAN_TCH_F, SELECT_FOR_MS_CHAN_REQ, true)) { + if (lchan_avail_by_type(rqd->bts, GSM_LCHAN_TCH_F, SELECT_FOR_MS_CHAN_REQ, NULL, true)) { LOG_BTS(rqd->bts, DRSL, LOGL_NOTICE, "CHAN RQD/EMERGENCY-PRIORITY: at least one TCH/F is (now) available!\n"); return false; } - if (lchan_avail_by_type(rqd->bts, GSM_LCHAN_TCH_H, SELECT_FOR_MS_CHAN_REQ, true)) { + if (lchan_avail_by_type(rqd->bts, GSM_LCHAN_TCH_H, SELECT_FOR_MS_CHAN_REQ, NULL, true)) { LOG_BTS(rqd->bts, DRSL, LOGL_NOTICE, "CHAN RQD/EMERGENCY-PRIORITY: at least one TCH/H is (now) available!\n"); return false; @@ -2083,7 +2083,7 @@ struct gsm_lchan *_select_sdcch_for_call(struct gsm_bts *bts, const struct chan_ int free_tchf, free_tchh; bool needs_dyn_switch; - lchan = lchan_avail_by_type(bts, GSM_LCHAN_SDCCH, SELECT_FOR_MS_CHAN_REQ, false); + lchan = lchan_avail_by_type(bts, GSM_LCHAN_SDCCH, SELECT_FOR_MS_CHAN_REQ, NULL, false); if (!lchan) return NULL; @@ -2173,7 +2173,8 @@ void abis_rsl_chan_rqd_queue_poll(struct gsm_bts *bts) lchan = _select_sdcch_for_call(bts, rqd, lctype); } else if (rqd->reason != GSM_CHREQ_REASON_EMERG) { lchan = lchan_select_by_type(bts, GSM_LCHAN_SDCCH, - SELECT_FOR_MS_CHAN_REQ); + SELECT_FOR_MS_CHAN_REQ, + NULL); } /* else: Emergency calls will be put on a free TCH/H or TCH/F directly * in the code below, all other channel requests will get an SDCCH first @@ -2189,14 +2190,16 @@ void abis_rsl_chan_rqd_queue_poll(struct gsm_bts *bts) get_value_string(gsm_chreq_descs, rqd->reason), gsm_lchant_name(GSM_LCHAN_SDCCH), rqd->ref.ra, gsm_lchant_name(GSM_LCHAN_TCH_H)); lchan = lchan_select_by_type(bts, GSM_LCHAN_TCH_H, - SELECT_FOR_MS_CHAN_REQ); + SELECT_FOR_MS_CHAN_REQ, + NULL); } if (!lchan) { LOG_BTS(bts, DRSL, LOGL_NOTICE, "CHAN RQD[%s]: no resources for %s 0x%x, retrying with %s\n", get_value_string(gsm_chreq_descs, rqd->reason), gsm_lchant_name(GSM_LCHAN_SDCCH), rqd->ref.ra, gsm_lchant_name(GSM_LCHAN_TCH_F)); lchan = lchan_select_by_type(bts, GSM_LCHAN_TCH_F, - SELECT_FOR_MS_CHAN_REQ); + SELECT_FOR_MS_CHAN_REQ, + NULL); } } if (!lchan) { diff --git a/src/osmo-bsc/assignment_fsm.c b/src/osmo-bsc/assignment_fsm.c index 209545fd9..84f8b38cd 100644 --- a/src/osmo-bsc/assignment_fsm.c +++ b/src/osmo-bsc/assignment_fsm.c @@ -606,7 +606,7 @@ void assignment_fsm_start(struct gsm_subscriber_connection *conn, struct gsm_bts conn->assignment.new_lchan = lchan_select_by_chan_mode(bts, req->ch_mode_rate_list[i].chan_mode, req->ch_mode_rate_list[i].chan_rate, - SELECT_FOR_ASSIGNMENT); + SELECT_FOR_ASSIGNMENT, conn->lchan); if (!conn->assignment.new_lchan) continue; LOG_ASSIGNMENT(conn, LOGL_DEBUG, "selected new lchan %s for mode[%d] = %s channel_rate=%d\n", diff --git a/src/osmo-bsc/bsc_vty.c b/src/osmo-bsc/bsc_vty.c index 0df5712b7..13ec8fb67 100644 --- a/src/osmo-bsc/bsc_vty.c +++ b/src/osmo-bsc/bsc_vty.c @@ -772,7 +772,8 @@ static int trigger_as(struct vty *vty, struct gsm_lchan *from_lchan, struct gsm_ if (!to_lchan) { struct gsm_bts *bts = from_lchan->ts->trx->bts; to_lchan = lchan_select_by_type(bts, from_lchan->type, - SELECT_FOR_ASSIGNMENT); + SELECT_FOR_ASSIGNMENT, + from_lchan); vty_out(vty, "Error: cannot find free lchan of type %s%s", gsm_lchant_name(from_lchan->type), VTY_NEWLINE); } @@ -958,7 +959,8 @@ static struct gsm_bts *find_other_bts_with_free_slots(struct vty *vty, struct gs llist_for_each_entry(trx, &bts->trx_list, list) { struct gsm_lchan *lchan = lchan_select_by_type(bts, free_type, - SELECT_FOR_HANDOVER); + SELECT_FOR_HANDOVER, + NULL); if (!lchan) continue; diff --git a/src/osmo-bsc/bts.c b/src/osmo-bsc/bts.c index 2f6661190..4976fe660 100644 --- a/src/osmo-bsc/bts.c +++ b/src/osmo-bsc/bts.c @@ -292,6 +292,9 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, struct gsm_bts_sm *bts_sm bts->early_classmark_allowed_3g = true; /* 3g Early Classmark Sending controlled by bts->early_classmark_allowed param */ bts->si_unused_send_empty = true; bts->chan_alloc_tch_signalling_policy = BTS_TCH_SIGNALLING_ALWAYS; + bts->chan_alloc_dyn_params.ul_rxlev_thresh = 50; /* >= -60 dBm */ + bts->chan_alloc_dyn_params.ul_rxlev_avg_num = 2; /* at least 2 samples */ + bts->chan_alloc_dyn_params.c0_chan_load_thresh = 60; /* >= 60% */ bts->si_common.cell_sel_par.cell_resel_hyst = 2; /* 4 dB */ bts->si_common.cell_sel_par.rxlev_acc_min = 0; bts->si_common.si2quater_neigh_list.arfcn = bts->si_common.data.earfcn_list; diff --git a/src/osmo-bsc/bts_vty.c b/src/osmo-bsc/bts_vty.c index 1a9b1e339..492cb9a5b 100644 --- a/src/osmo-bsc/bts_vty.c +++ b/src/osmo-bsc/bts_vty.c @@ -550,6 +550,7 @@ DEFUN_ATTR(cfg_bts_challoc_mode_all, bts->chan_alloc_chan_req_reverse = reverse; bts->chan_alloc_assignment_reverse = reverse; bts->chan_alloc_handover_reverse = reverse; + bts->chan_alloc_assignment_dynamic = false; return CMD_SUCCESS; } @@ -574,14 +575,89 @@ DEFUN_ATTR(cfg_bts_challoc_mode, if (set_all || !strcmp(argv[0], "chan-req")) bts->chan_alloc_chan_req_reverse = reverse; - if (set_all || !strcmp(argv[0], "assignment")) + if (set_all || !strcmp(argv[0], "assignment")) { bts->chan_alloc_assignment_reverse = reverse; + bts->chan_alloc_assignment_dynamic = false; + } if (set_all || !strcmp(argv[0], "handover")) bts->chan_alloc_handover_reverse = reverse; return CMD_SUCCESS; } +DEFUN_ATTR(cfg_bts_challoc_mode_ass_dynamic, + cfg_bts_challoc_mode_ass_dynamic_cmd, + CHAN_ALLOC_CMD " mode assignment dynamic", + CHAN_ALLOC_DESC + "Channel allocation mode\n" + "Channel allocation for assignment\n" + "Dynamic lchan selection based on configured parameters\n", + CMD_ATTR_IMMEDIATE) +{ + struct gsm_bts *bts = vty->index; + + bts->chan_alloc_assignment_dynamic = true; + + return CMD_SUCCESS; +} + +#define CHAN_ALLOC_DYN_PARAM_CMD \ + CHAN_ALLOC_CMD " dynamic-param" +#define CHAN_ALLOC_DYN_PARAM_DESC \ + CHAN_ALLOC_DESC \ + "Parameters for dynamic channel allocation mode\n" + +DEFUN_ATTR(cfg_bts_challoc_dynamic_param_sort_by_trx_power, + cfg_bts_challoc_dynamic_param_sort_by_trx_power_cmd, + CHAN_ALLOC_DYN_PARAM_CMD " sort-by-trx-power (0|1)", + CHAN_ALLOC_DYN_PARAM_DESC + "Whether to sort TRX instances by their respective power levels\n" + "Do not sort, use the same order as in the configuration file\n" + "Sort TRX instances by their power levels in descending order\n", + CMD_ATTR_IMMEDIATE) +{ + struct gsm_bts *bts = vty->index; + + bts->chan_alloc_dyn_params.sort_by_trx_power = (argv[0][0] == '1'); + + return CMD_SUCCESS; +} + +DEFUN_ATTR(cfg_bts_challoc_dynamic_param_ul_rxlev, + cfg_bts_challoc_dynamic_param_ul_rxlev_cmd, + CHAN_ALLOC_DYN_PARAM_CMD " ul-rxlev thresh <0-63> avg-num <1-10>", + CHAN_ALLOC_DYN_PARAM_DESC + "Uplink RxLev\n" + "Uplink RxLev threshold\n" + "Uplink RxLev threshold\n" + "Minimum number of RxLev samples for averaging\n" + "Minimum number of RxLev samples for averaging\n", + CMD_ATTR_IMMEDIATE) +{ + struct gsm_bts *bts = vty->index; + + bts->chan_alloc_dyn_params.ul_rxlev_thresh = atoi(argv[0]); + bts->chan_alloc_dyn_params.ul_rxlev_avg_num = atoi(argv[1]); + + return CMD_SUCCESS; +} + +DEFUN_ATTR(cfg_bts_challoc_dynamic_param_c0_chan_load, + cfg_bts_challoc_dynamic_param_c0_chan_load_cmd, + CHAN_ALLOC_DYN_PARAM_CMD " c0-chan-load thresh <0-100>", + CHAN_ALLOC_DYN_PARAM_DESC + "C0 (BCCH carrier) channel load\n" + "Channel load threshold\n" + "Channel load threshold (in %)\n", + CMD_ATTR_IMMEDIATE) +{ + struct gsm_bts *bts = vty->index; + + bts->chan_alloc_dyn_params.c0_chan_load_thresh = atoi(argv[0]); + + return CMD_SUCCESS; +} + DEFUN_ATTR(cfg_bts_chan_alloc_interf, cfg_bts_chan_alloc_interf_cmd, CHAN_ALLOC_CMD " avoid-interference (0|1)", @@ -4246,9 +4322,24 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts) vty_out(vty, " channel allocator mode chan-req %s%s", bts->chan_alloc_chan_req_reverse ? "descending" : "ascending", VTY_NEWLINE); - vty_out(vty, " channel allocator mode assignment %s%s", - bts->chan_alloc_assignment_reverse ? "descending" : "ascending", - VTY_NEWLINE); + if (bts->chan_alloc_assignment_dynamic) { + vty_out(vty, " channel allocator mode assignment dynamic%s", + VTY_NEWLINE); + vty_out(vty, " channel allocator dynamic-param sort-by-trx-power %c%s", + bts->chan_alloc_dyn_params.sort_by_trx_power ? '1' : '0', + VTY_NEWLINE); + vty_out(vty, " channel allocator dynamic-param ul-rxlev thresh %u avg-num %u%s", + bts->chan_alloc_dyn_params.ul_rxlev_thresh, + bts->chan_alloc_dyn_params.ul_rxlev_avg_num, + VTY_NEWLINE); + vty_out(vty, " channel allocator dynamic-param c0-chan-load thresh %u%s", + bts->chan_alloc_dyn_params.c0_chan_load_thresh, + VTY_NEWLINE); + } else { + vty_out(vty, " channel allocator mode assignment %s%s", + bts->chan_alloc_assignment_reverse ? "descending" : "ascending", + VTY_NEWLINE); + } vty_out(vty, " channel allocator mode handover %s%s", bts->chan_alloc_handover_reverse ? "descending" : "ascending", VTY_NEWLINE); @@ -4582,6 +4673,10 @@ int bts_vty_init(void) install_element(BTS_NODE, &cfg_bts_oml_e1_tei_cmd); install_element(BTS_NODE, &cfg_bts_challoc_mode_cmd); install_element(BTS_NODE, &cfg_bts_challoc_mode_all_cmd); + install_element(BTS_NODE, &cfg_bts_challoc_mode_ass_dynamic_cmd); + install_element(BTS_NODE, &cfg_bts_challoc_dynamic_param_sort_by_trx_power_cmd); + install_element(BTS_NODE, &cfg_bts_challoc_dynamic_param_ul_rxlev_cmd); + install_element(BTS_NODE, &cfg_bts_challoc_dynamic_param_c0_chan_load_cmd); install_element(BTS_NODE, &cfg_bts_chan_alloc_interf_cmd); install_element(BTS_NODE, &cfg_bts_chan_alloc_tch_signalling_policy_cmd); install_element(BTS_NODE, &cfg_bts_chan_alloc_allow_tch_for_signalling_cmd); diff --git a/src/osmo-bsc/handover_decision_2.c b/src/osmo-bsc/handover_decision_2.c index 3c4b3baac..a4720894d 100644 --- a/src/osmo-bsc/handover_decision_2.c +++ b/src/osmo-bsc/handover_decision_2.c @@ -1050,7 +1050,8 @@ static void candidate_set_free_tch(struct ho_candidate *c) /* Would the next TCH/F lchan occupy a dynamic timeslot that currently counts for free TCH/H timeslots? */ - next_lchan = lchan_avail_by_type(c->target.bts, GSM_LCHAN_TCH_F, SELECT_FOR_HANDOVER, false); + next_lchan = lchan_avail_by_type(c->target.bts, GSM_LCHAN_TCH_F, + SELECT_FOR_HANDOVER, NULL, false); if (next_lchan && next_lchan->ts->pchan_on_init == GSM_PCHAN_OSMO_DYN) c->target.next_tchf_reduces_tchh = 2; else @@ -1058,7 +1059,8 @@ static void candidate_set_free_tch(struct ho_candidate *c) /* Would the next TCH/H lchan occupy a dynamic timeslot that currently counts for free TCH/F timeslots? * Note that a dyn TS already in TCH/H mode (half occupied) would not reduce free TCH/F. */ - next_lchan = lchan_avail_by_type(c->target.bts, GSM_LCHAN_TCH_H, SELECT_FOR_HANDOVER, false); + next_lchan = lchan_avail_by_type(c->target.bts, GSM_LCHAN_TCH_H, + SELECT_FOR_HANDOVER, NULL, false); if (next_lchan && next_lchan->ts->pchan_on_init == GSM_PCHAN_OSMO_DYN && next_lchan->ts->pchan_is != GSM_PCHAN_TCH_H) c->target.next_tchh_reduces_tchf = 1; diff --git a/src/osmo-bsc/handover_fsm.c b/src/osmo-bsc/handover_fsm.c index 0797bf3f8..2f452437e 100644 --- a/src/osmo-bsc/handover_fsm.c +++ b/src/osmo-bsc/handover_fsm.c @@ -381,7 +381,8 @@ static void handover_start_intra_bsc(struct gsm_subscriber_connection *conn) ho->new_lchan = lchan_select_by_type(ho->new_bts, ho->new_lchan_type, - SELECT_FOR_HANDOVER); + SELECT_FOR_HANDOVER, + NULL); if (ho->scope & HO_INTRA_CELL) { ho_count(bts, CTR_INTRA_CELL_HO_ATTEMPTED); @@ -701,7 +702,7 @@ void handover_start_inter_bsc_in(struct gsm_subscriber_connection *conn, lchan = lchan_select_by_chan_mode(bts, ch_mode_rate.chan_mode, ch_mode_rate.chan_rate, - SELECT_FOR_HANDOVER); + SELECT_FOR_HANDOVER, NULL); if (!lchan) { LOG_HO(conn, LOGL_DEBUG, "BTS %u has no matching free channels\n", bts->nr); continue; diff --git a/src/osmo-bsc/lchan_select.c b/src/osmo-bsc/lchan_select.c index 8b6da4036..ebc7ede8b 100644 --- a/src/osmo-bsc/lchan_select.c +++ b/src/osmo-bsc/lchan_select.c @@ -21,6 +21,8 @@ * */ +#include + #include #include @@ -195,15 +197,41 @@ enum gsm_chan_t chan_mode_to_chan_type(enum gsm48_chan_mode chan_mode, enum chan } } +static int qsort_func(const void *_a, const void *_b) +{ + const struct gsm_bts_trx *trx_a = *(const struct gsm_bts_trx **)_a; + const struct gsm_bts_trx *trx_b = *(const struct gsm_bts_trx **)_b; + + int pwr_a = trx_a->nominal_power - trx_a->max_power_red; + int pwr_b = trx_b->nominal_power - trx_b->max_power_red; + + /* Sort in descending order */ + return pwr_b - pwr_a; +} + static void populate_ts_list(struct lchan_select_ts_list *ts_list, struct gsm_bts *bts, bool chan_alloc_reverse, + bool sort_by_trx_power, bool log) { + struct gsm_bts_trx **trx_list; struct gsm_bts_trx *trx; unsigned int num = 0; - llist_for_each_entry(trx, &bts->trx_list, list) { + /* Allocate an array with pointers to all TRX instances of a BTS */ + trx_list = talloc_array_ptrtype(bts, trx_list, bts->num_trx); + OSMO_ASSERT(trx_list != NULL); + + llist_for_each_entry(trx, &bts->trx_list, list) + trx_list[trx->nr] = trx; + + /* Sort by TRX power in descending order (if needed) */ + if (sort_by_trx_power) + qsort(&trx_list[0], bts->num_trx, sizeof(trx), &qsort_func); + + for (unsigned int trxn = 0; trxn < bts->num_trx; trxn++) { + trx = trx_list[trxn]; for (unsigned int tn = 0; tn < ARRAY_SIZE(trx->ts); tn++) { struct gsm_bts_trx_ts *ts = &trx->ts[tn]; if (ts_is_usable(ts)) @@ -213,6 +241,7 @@ static void populate_ts_list(struct lchan_select_ts_list *ts_list, } } + talloc_free(trx_list); ts_list->num = num; /* Reverse the timeslot list if required */ @@ -225,23 +254,75 @@ static void populate_ts_list(struct lchan_select_ts_list *ts_list, } } +static bool chan_alloc_ass_dynamic_reverse(struct gsm_bts *bts, + void *ctx, bool log) +{ + const struct load_counter *ll = &bts->c0->lchan_load; + const struct gsm_lchan *old_lchan = ctx; + unsigned int lchan_load; + int avg_ul_rxlev; + + OSMO_ASSERT(old_lchan != NULL); + OSMO_ASSERT(old_lchan->ts->trx->bts == bts); + +#define LOG_COND(fmt, args...) do { \ + if (log) \ + LOG_LCHAN(old_lchan, LOGL_DEBUG, fmt, ## args); \ + } while (0) + + /* Condition a) Channel load on the C0 (BCCH carrier) */ + lchan_load = ll->total ? ll->used * 100 / ll->total : 0; + if (lchan_load < bts->chan_alloc_dyn_params.c0_chan_load_thresh) { + LOG_COND("C0 Channel Load %u%% < thresh %u%% => using ascending order\n", + lchan_load, bts->chan_alloc_dyn_params.c0_chan_load_thresh); + return false; + } + + /* Condition b) average Uplink RxLev */ + avg_ul_rxlev = get_meas_rep_avg(old_lchan, TDMA_MEAS_FIELD_RXLEV, + TDMA_MEAS_DIR_UL, TDMA_MEAS_SET_AUTO, + bts->chan_alloc_dyn_params.ul_rxlev_avg_num); + if (avg_ul_rxlev < 0) { + LOG_COND("Unknown AVG UL RxLev => using ascending order\n"); + return false; + } + if (avg_ul_rxlev < bts->chan_alloc_dyn_params.ul_rxlev_thresh) { + LOG_COND("AVG UL RxLev %u < thresh %u => using ascending order\n", + avg_ul_rxlev, bts->chan_alloc_dyn_params.ul_rxlev_thresh); + return false; + } + + LOG_COND("C0 Channel Load %u%% >= thresh %u%% and " + "AVG UL RxLev %u >= thresh %u => using descending order\n", + lchan_load, bts->chan_alloc_dyn_params.c0_chan_load_thresh, + avg_ul_rxlev, bts->chan_alloc_dyn_params.ul_rxlev_thresh); + +#undef LOG_COND + + return true; +} + struct gsm_lchan *lchan_select_by_chan_mode(struct gsm_bts *bts, enum gsm48_chan_mode chan_mode, enum channel_rate chan_rate, - enum lchan_select_reason reason) + enum lchan_select_reason reason, + void *ctx) { enum gsm_chan_t type = chan_mode_to_chan_type(chan_mode, chan_rate); if (type == GSM_LCHAN_NONE) return NULL; - return lchan_select_by_type(bts, type, reason); + return lchan_select_by_type(bts, type, reason, ctx); } -struct gsm_lchan *lchan_avail_by_type(struct gsm_bts *bts, enum gsm_chan_t type, - enum lchan_select_reason reason, bool log) +struct gsm_lchan *lchan_avail_by_type(struct gsm_bts *bts, + enum gsm_chan_t type, + enum lchan_select_reason reason, + void *ctx, bool log) { struct gsm_lchan *lchan = NULL; enum gsm_phys_chan_config first, first_cbch, second, second_cbch; struct lchan_select_ts_list ts_list; + bool sort_by_trx_power = false; bool chan_alloc_reverse; if (log) { @@ -254,7 +335,12 @@ struct gsm_lchan *lchan_avail_by_type(struct gsm_bts *bts, enum gsm_chan_t type, chan_alloc_reverse = bts->chan_alloc_chan_req_reverse; break; case SELECT_FOR_ASSIGNMENT: - chan_alloc_reverse = bts->chan_alloc_assignment_reverse; + if (bts->chan_alloc_assignment_dynamic) { + chan_alloc_reverse = chan_alloc_ass_dynamic_reverse(bts, ctx, log); + sort_by_trx_power = bts->chan_alloc_dyn_params.sort_by_trx_power; + } else { + chan_alloc_reverse = bts->chan_alloc_assignment_reverse; + } break; case SELECT_FOR_HANDOVER: chan_alloc_reverse = bts->chan_alloc_handover_reverse; @@ -267,7 +353,7 @@ struct gsm_lchan *lchan_avail_by_type(struct gsm_bts *bts, enum gsm_chan_t type, return NULL; /* Populate this array with the actual pointers */ - populate_ts_list(&ts_list, bts, chan_alloc_reverse, log); + populate_ts_list(&ts_list, bts, chan_alloc_reverse, sort_by_trx_power, log); switch (type) { case GSM_LCHAN_SDCCH: @@ -330,14 +416,15 @@ struct gsm_lchan *lchan_avail_by_type(struct gsm_bts *bts, enum gsm_chan_t type, * the lchan and timeslot FSMs. */ struct gsm_lchan *lchan_select_by_type(struct gsm_bts *bts, enum gsm_chan_t type, - enum lchan_select_reason reason) + enum lchan_select_reason reason, + void *ctx) { struct gsm_lchan *lchan = NULL; LOG_BTS(bts, DRLL, LOGL_DEBUG, "lchan_select_by_type(type=%s, reason=%s)\n", gsm_lchant_name(type), lchan_select_reason_name(reason)); - lchan = lchan_avail_by_type(bts, type, reason, true); + lchan = lchan_avail_by_type(bts, type, reason, ctx, true); if (!lchan) { LOG_BTS(bts, DRLL, LOGL_NOTICE, "Failed to select %s channel (%s)\n", diff --git a/tests/handover/handover_test.c b/tests/handover/handover_test.c index 77fa3712e..26c72ab0f 100644 --- a/tests/handover/handover_test.c +++ b/tests/handover/handover_test.c @@ -489,7 +489,7 @@ struct gsm_lchan *create_lchan(struct gsm_bts *bts, int full_rate, const char *c struct gsm_lchan *lchan; lchan = lchan_select_by_type(bts, (full_rate) ? GSM_LCHAN_TCH_F : GSM_LCHAN_TCH_H, - SELECT_FOR_HANDOVER); + SELECT_FOR_HANDOVER, NULL); if (!lchan) { fprintf(stderr, "No resource for lchan\n"); exit(EXIT_FAILURE); diff --git a/tests/osmo-bsc.vty b/tests/osmo-bsc.vty index e0b9bdc18..4be3d5632 100644 --- a/tests/osmo-bsc.vty +++ b/tests/osmo-bsc.vty @@ -167,6 +167,7 @@ OsmoBSC(config-net-bts)# channel ? OsmoBSC(config-net-bts)# channel allocator ? mode Channel allocation mode + dynamic-param Parameters for dynamic channel allocation mode avoid-interference Configure whether reported interference levels from RES IND are used in channel allocation tch-signalling-policy Configure when TCH/H or TCH/F channels can be used to serve signalling if SDCCHs are exhausted @@ -180,6 +181,31 @@ OsmoBSC(config-net-bts)# channel allocator mode set-all ? ascending Allocate Timeslots and Transceivers in ascending order descending Allocate Timeslots and Transceivers in descending order +OsmoBSC(config-net-bts)# channel allocator mode handover ? + ascending Allocate Timeslots and Transceivers in ascending order + descending Allocate Timeslots and Transceivers in descending order + +OsmoBSC(config-net-bts)# channel allocator mode assignment ? + ascending Allocate Timeslots and Transceivers in ascending order + descending Allocate Timeslots and Transceivers in descending order + dynamic Dynamic lchan selection based on configured parameters + +OsmoBSC(config-net-bts)# channel allocator dynamic-param ? + sort-by-trx-power Whether to sort TRX instances by their respective power levels + ul-rxlev Uplink RxLev + c0-chan-load C0 (BCCH carrier) channel load + +OsmoBSC(config-net-bts)# channel allocator dynamic-param sort-by-trx-power ? + 0 Do not sort, use the same order as in the configuration file + 1 Sort TRX instances by their power levels in descending order + +OsmoBSC(config-net-bts)# channel allocator dynamic-param ul-rxlev thresh ? + <0-63> Uplink RxLev threshold +OsmoBSC(config-net-bts)# channel allocator dynamic-param ul-rxlev thresh 50 avg-num ? + <1-10> Minimum number of RxLev samples for averaging +OsmoBSC(config-net-bts)# channel allocator dynamic-param c0-chan-load thresh ? + <0-100> Channel load threshold (in %) + OsmoBSC(config-net-bts)# channel allocator avoid-interference ? 0 Ignore interference levels (default). Always assign lchans in a deterministic order. 1 In channel allocation, prefer lchans with less interference.