This is merely a historical archive of years 2008-2021, before the migration to mailman3.
A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/OpenBSC@lists.osmocom.org/.
Jacob Erlbeck jerlbeck at sysmocom.deCurrently, the AGCH queue length is not limited. This can lead to large delays and network malfunction if there are many IMM.ASS.REJ messages. This patch adds two features: - Don't accept msgs from the RSL layer when the queue is way too full (safety measure, mainly if bts_ccch_copy_msg() is not being called by the L1 layer, currently hard coded to 1000 messages) - Selectively drop IMM.ASS.REJ from the queue output depending on the queue length Ticket: #224 Sponsored-by: On-Waves ehf --- include/osmo-bts/gsm_data.h | 9 ++++ src/common/bts.c | 123 ++++++++++++++++++++++++++++++++++++++----- tests/agch/agch_test.c | 4 ++ tests/agch/agch_test.ok | 2 +- 4 files changed, 125 insertions(+), 13 deletions(-) diff --git a/include/osmo-bts/gsm_data.h b/include/osmo-bts/gsm_data.h index b139903..c7a0fc6 100644 --- a/include/osmo-bts/gsm_data.h +++ b/include/osmo-bts/gsm_data.h @@ -7,6 +7,11 @@ #include <osmo-bts/paging.h> +#define GSM_BTS_AGCH_QUEUE_THRESH_LEVEL_DEFAULT 41 +#define GSM_BTS_AGCH_QUEUE_THRESH_LEVEL_DISABLE 999999 +#define GSM_BTS_AGCH_QUEUE_LOW_LEVEL_DEFAULT 41 +#define GSM_BTS_AGCH_QUEUE_HIGH_LEVEL_DEFAULT 91 + struct pcu_sock_state; struct gsm_network { @@ -54,6 +59,10 @@ struct gsm_bts_role_bts { int agch_queue_length; int agch_max_queue_length; + int agch_queue_thresh_level; /* Cleanup threshold in percent of max len */ + int agch_queue_low_level; /* Low water mark in percent of max len */ + int agch_queue_high_level; /* High water mark in percent of max len */ + /* TODO: Use a rate counter group instead */ uint64_t agch_queue_dropped_msgs; uint64_t agch_queue_merged_msgs; diff --git a/src/common/bts.c b/src/common/bts.c index 4ba2ec7..0b14020 100644 --- a/src/common/bts.c +++ b/src/common/bts.c @@ -89,6 +89,14 @@ int bts_init(struct gsm_bts *bts) INIT_LLIST_HEAD(&btsb->agch_queue); btsb->agch_queue_length = 0; + /* enable management with default levels, + * raise threshold to GSM_BTS_AGCH_QUEUE_THRESH_LEVEL_DISABLE to + * disable this feature. + */ + btsb->agch_queue_low_level = GSM_BTS_AGCH_QUEUE_LOW_LEVEL_DEFAULT; + btsb->agch_queue_high_level = GSM_BTS_AGCH_QUEUE_HIGH_LEVEL_DEFAULT; + btsb->agch_queue_thresh_level = GSM_BTS_AGCH_QUEUE_THRESH_LEVEL_DEFAULT; + /* configurable via VTY */ btsb->paging_state = paging_init(btsb, 200, 0); @@ -297,6 +305,18 @@ void bts_update_agch_max_queue_length(struct gsm_bts *bts) int bts_agch_enqueue(struct gsm_bts *bts, struct msgb *msg) { struct gsm_bts_role_bts *btsb = bts_role_bts(bts); + int hard_limit = 1000; + + if (btsb->agch_queue_length > hard_limit) { + LOGP(DSUM, LOGL_ERROR, + "AGCH: too many messages in queue, " + "refusing message type 0x%02x, length = %d/%d\n", + ((struct gsm48_imm_ass *)msgb_l3(msg))->msg_type, + btsb->agch_queue_length, btsb->agch_max_queue_length); + + btsb->agch_queue_rejected_msgs++; + return -ENOMEM; + } msgb_enqueue(&btsb->agch_queue, msg); btsb->agch_queue_length++; @@ -325,30 +345,109 @@ struct msgb *bts_agch_dequeue(struct gsm_bts *bts) return msg; } +/* Remove lower prio messages if the queue has grown to long. + * + * \return 0 iff the number of messages in the queue would fit into the AGCH + * reserved part of the CCCH. + */ +static int compact_agch_queue(struct gsm_bts *bts) +{ + struct gsm_bts_role_bts *btsb = bts_role_bts(bts); + struct msgb *msg, *msg2; + int max_len, slope, offs; + int level_low = btsb->agch_queue_low_level; + int level_high = btsb->agch_queue_high_level; + int level_thres = btsb->agch_queue_thresh_level; + struct gsm48_system_information_type_3 *si3 = GSM_BTS_SI(bts, SYSINFO_TYPE_3); + int bs_ag_blks_res = si3->control_channel_desc.bs_ag_blks_res; + + max_len = btsb->agch_max_queue_length; + + if (max_len == 0) + max_len = 1; + + /* TODO: Make the constants configurable */ + if (btsb->agch_queue_length < max_len * level_thres / 100) + goto out; + + /* p^ + * 1+ /''''' + * | / + * | / + * 0+---/--+----+--> Q length + * low high max_len + */ + + offs = max_len * level_low / 100; + if (level_high > level_low) + slope = 0x10000 * 100 / (level_high - level_low); + else + slope = 0x10000 * max_len; /* p_drop >= 1 if len > offs */ + + llist_for_each_entry_safe(msg, msg2, &btsb->agch_queue, list) { + struct gsm48_imm_ass *imm_ass_cmd = msgb_l3(msg); + int p_drop; + LOGP(DSUM, LOGL_DEBUG, + "AGCH: head is message type 0x%02x, length = %d/%d\n", + imm_ass_cmd->msg_type, + btsb->agch_queue_length, btsb->agch_max_queue_length); + + if (imm_ass_cmd->msg_type != GSM48_MT_RR_IMM_ASS_REJ) + goto out; + + /* IMMEDIATE ASSIGN REJECT */ + + p_drop = (btsb->agch_queue_length - offs) * slope / max_len; + + if ((random() & 0xffff) >= p_drop) + goto out; + + LOGP(DSUM, LOGL_DEBUG, + "AGCH: dropping message type 0x%02x, length = %d/%d, " + "p = %f\n", + imm_ass_cmd->msg_type, + btsb->agch_queue_length, btsb->agch_max_queue_length, + p_drop / 65536.0); + + llist_del(&msg->list); + btsb->agch_queue_length--; + msgb_free(msg); + + btsb->agch_queue_dropped_msgs++; + } +out: + return btsb->agch_queue_length > bs_ag_blks_res; +} + int bts_ccch_copy_msg(struct gsm_bts *bts, uint8_t *out_buf, struct gsm_time *gt, int is_ag_res) { - struct msgb *msg; + struct msgb *msg = NULL; struct gsm_bts_role_bts *btsb = bts->role; - int rc; + int rc = 0; int is_empty = 1; - struct gsm48_system_information_type_3 *si3 = - GSM_BTS_SI(bts, SYSINFO_TYPE_3); - int bs_ag_blks_res = si3->control_channel_desc.bs_ag_blks_res; - int agch_res_full = btsb->agch_queue_length > bs_ag_blks_res; + int agch_res_full; + + /* Do queue house keeping. + * This needs to be done every time a CCCH message is requested, since + * the queue max length is calculated based on the CCCH block rate and + * PCH messages also reduce the drain of the AGCH queue. + */ + agch_res_full = compact_agch_queue(bts); - if (!is_ag_res) { + /* Check for paging messages first if this is PCH */ + if (!is_ag_res) rc = paging_gen_msg(btsb->paging_state, out_buf, gt, &is_empty); - if (!is_empty || !agch_res_full) - return rc; - } + /* Check whether the block may be overwritten */ + if (!is_empty || (!is_ag_res && !agch_res_full)) + return rc; - /* special queue of messages from IMM ASS CMD */ msg = bts_agch_dequeue(bts); if (!msg) - return 0; + return rc; + /* Copy AGCH message */ memcpy(out_buf, msgb_l3(msg), msgb_l3len(msg)); rc = msgb_l3len(msg); LOGP(DSUM, LOGL_DEBUG, diff --git a/tests/agch/agch_test.c b/tests/agch/agch_test.c index 7e87bcf..a3868c1 100644 --- a/tests/agch/agch_test.c +++ b/tests/agch/agch_test.c @@ -129,6 +129,10 @@ static void test_agch_queue(void) printf("Testing AGCH messages queue handling.\n"); btsb->agch_max_queue_length = 32; + btsb->agch_queue_low_level = 30; + btsb->agch_queue_high_level = 30; + btsb->agch_queue_thresh_level = 60; + for (round = 1; round <= num_rounds; round++) { for (idx = 0; idx < num_ima_per_round; idx++) { msg = msgb_alloc(GSM_MACBLOCK_LEN, __FUNCTION__); diff --git a/tests/agch/agch_test.ok b/tests/agch/agch_test.ok index 70a9797..57439ed 100644 --- a/tests/agch/agch_test.ok +++ b/tests/agch/agch_test.ok @@ -1,4 +1,4 @@ Testing AGCH messages queue handling. AGCH filled: count 720, imm.ass 80, imm.ass.rej 640 (refs 640), queue limit 32, occupied 720, dropped 0, merged 0, rejected 0, ag-res 0, non-res 0 -AGCH drained: multiframes 241, imm.ass 80, imm.ass.rej 641 (refs 641), queue limit 32, occupied 0, dropped 0, merged 0, rejected 0, ag-res 240, non-res 480 +AGCH drained: multiframes 33, imm.ass 80, imm.ass.rej 17 (refs 17), queue limit 32, occupied 0, dropped 624, merged 0, rejected 0, ag-res 32, non-res 64 Success -- 1.7.9.5