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
This commit is contained in:
Vadim Yanitskiy 2022-06-16 02:10:16 +07:00 committed by fixeria
parent c2928ac269
commit a019e9af8b
13 changed files with 329 additions and 33 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -21,6 +21,8 @@
*
*/
#include <stdlib.h>
#include <osmocom/bsc/debug.h>
#include <osmocom/bsc/gsm_data.h>
@ -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",

View File

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

View File

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