[PATCH 8/8] agch: Merge IMM.ASS.REJ if possible when enqueueing

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.de
Fri Feb 21 23:36:09 UTC 2014


This patch implements merging of IMMEDIATE ASSIGN REJECT messages as
sugegsted in GSM 08.58, 5.7. When a new IMM.ASS.REJ is to be appended
to the AGCH queue and the last message in that queue is of the same
type, the individual entries (up to 4 per message) of both messages
are extracted, combined and stored back. If there are less than 5
entries all entries fit into the old message and the new one is
discarded. Otherwise, the old message will contain 4 entries and the
remaining ones are stored into the new one which is then appended to
the queue.

Ticket: #224
Sponsored-by: On-Waves ehf
---
 src/common/bts.c        |  146 +++++++++++++++++++++++++++++++++++++++++++++++
 src/common/vty.c        |   12 ++--
 tests/agch/agch_test.ok |    4 +-
 3 files changed, 154 insertions(+), 8 deletions(-)

diff --git a/src/common/bts.c b/src/common/bts.c
index 0b14020..566cadb 100644
--- a/src/common/bts.c
+++ b/src/common/bts.c
@@ -302,10 +302,138 @@ void bts_update_agch_max_queue_length(struct gsm_bts *bts)
 		     si3->control_channel_desc.bs_ag_blks_res);
 }
 
+#define REQ_REFS_PER_IMM_ASS_REJ 4
+static int store_imm_ass_rej_refs(struct gsm48_imm_ass_rej *rej,
+				    struct gsm48_req_ref *req_refs,
+				    uint8_t *wait_inds,
+				    int count)
+{
+	switch (count) {
+	case 0:
+		/* TODO: Warning ? */
+		return 0;
+	default:
+		count = 4;
+		rej->req_ref4 = req_refs[3];
+		rej->wait_ind4 = wait_inds[3];
+		/* no break */
+	case 3:
+		rej->req_ref3 = req_refs[2];
+		rej->wait_ind3 = wait_inds[2];
+		/* no break */
+	case 2:
+		rej->req_ref2 = req_refs[1];
+		rej->wait_ind2 = wait_inds[1];
+		/* no break */
+	case 1:
+		rej->req_ref1 = req_refs[0];
+		rej->wait_ind1 = wait_inds[0];
+		break;
+	}
+
+	switch (count) {
+	case 1:
+		rej->req_ref2 = req_refs[0];
+		rej->wait_ind2 = wait_inds[0];
+		/* no break */
+	case 2:
+		rej->req_ref3 = req_refs[0];
+		rej->wait_ind3 = wait_inds[0];
+		/* no break */
+	case 3:
+		rej->req_ref4 = req_refs[0];
+		rej->wait_ind4 = wait_inds[0];
+		/* no break */
+	default:
+		break;
+	}
+
+	return count;
+}
+
+static int extract_imm_ass_rej_refs(struct gsm48_imm_ass_rej *rej,
+				    struct gsm48_req_ref *req_refs,
+				    uint8_t *wait_inds)
+{
+	int count = 0;
+	req_refs[count] = rej->req_ref1;
+	wait_inds[count] = rej->wait_ind1;
+	count++;
+
+	if (memcmp(&rej->req_ref1, &rej->req_ref2, sizeof(rej->req_ref2))) {
+		req_refs[count] = rej->req_ref2;
+		wait_inds[count] = rej->wait_ind2;
+		count++;
+	}
+
+	if (memcmp(&rej->req_ref1, &rej->req_ref3, sizeof(rej->req_ref3))
+	    && memcmp(&rej->req_ref2, &rej->req_ref3, sizeof(rej->req_ref3))) {
+		req_refs[count] = rej->req_ref3;
+		wait_inds[count] = rej->wait_ind3;
+		count++;
+	}
+
+	if (memcmp(&rej->req_ref1, &rej->req_ref4, sizeof(rej->req_ref4))
+	    && memcmp(&rej->req_ref2, &rej->req_ref4, sizeof(rej->req_ref4))
+	    && memcmp(&rej->req_ref3, &rej->req_ref4, sizeof(rej->req_ref4))) {
+		req_refs[count] = rej->req_ref4;
+		wait_inds[count] = rej->wait_ind4;
+		count++;
+	}
+
+	return count;
+}
+
+static int try_merge_imm_ass_rej(struct gsm48_imm_ass_rej *old_rej,
+				 struct gsm48_imm_ass_rej *new_rej)
+{
+	struct gsm48_req_ref req_refs[2 * REQ_REFS_PER_IMM_ASS_REJ];
+	uint8_t wait_inds[2 * REQ_REFS_PER_IMM_ASS_REJ];
+	int count = 0;
+	int stored = 0;
+
+	if (new_rej->msg_type != GSM48_MT_RR_IMM_ASS_REJ)
+		return 0;
+	if (old_rej->msg_type != GSM48_MT_RR_IMM_ASS_REJ)
+		return 0;
+
+	/* GSM 08.58, 5.7
+	 * -> The BTS may combine serveral IMM.ASS.REJ messages
+	 * -> Identical request refs in one message may be squeezed
+	 *
+	 * GSM 04.08, 9.1.20.2
+	 * -> Request ref and wait ind are duplicated to fill the message
+	 */
+
+	/* Extract all entries */
+	count = extract_imm_ass_rej_refs(old_rej,
+					 &req_refs[count], &wait_inds[count]);
+	if (count == REQ_REFS_PER_IMM_ASS_REJ)
+		return 0;
+
+	count += extract_imm_ass_rej_refs(new_rej,
+					  &req_refs[count], &wait_inds[count]);
+
+	/* Store entries into old message */
+	stored = store_imm_ass_rej_refs(old_rej,
+					&req_refs[stored], &wait_inds[stored],
+					count);
+	count -= stored;
+	if (count == 0)
+		return 1;
+
+	/* Store remaining entries into new message */
+	stored += store_imm_ass_rej_refs(new_rej,
+					 &req_refs[stored], &wait_inds[stored],
+					 count);
+	return 0;
+}
+
 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;
+	struct gsm48_imm_ass_rej *imm_ass_cmd = msgb_l3(msg);
 
 	if (btsb->agch_queue_length > hard_limit) {
 		LOGP(DSUM, LOGL_ERROR,
@@ -318,6 +446,24 @@ int bts_agch_enqueue(struct gsm_bts *bts, struct msgb *msg)
 		return -ENOMEM;
 	}
 
+	if (btsb->agch_queue_length > 0) {
+		struct msgb *last_msg =
+			llist_entry(btsb->agch_queue.prev, struct msgb, list);
+		struct gsm48_imm_ass_rej *last_imm_ass_rej = msgb_l3(last_msg);
+
+		if (try_merge_imm_ass_rej(last_imm_ass_rej, imm_ass_cmd)) {
+			LOGP(DSUM, LOGL_DEBUG,
+			     "AGCH: merged message type 0x%02x, length = %d/%d, "
+			     "msg = %s\n",
+			     last_imm_ass_rej->msg_type,
+			     btsb->agch_queue_length, btsb->agch_max_queue_length,
+			     osmo_hexdump(msgb_l3(last_msg), msgb_l3len(last_msg)));
+
+			btsb->agch_queue_merged_msgs++;
+			return 0;
+		}
+	}
+
 	msgb_enqueue(&btsb->agch_queue, msg);
 	btsb->agch_queue_length++;
 
diff --git a/src/common/vty.c b/src/common/vty.c
index f7a04d6..b59cc9b 100644
--- a/src/common/vty.c
+++ b/src/common/vty.c
@@ -158,15 +158,15 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
 		VTY_NEWLINE);
 	vty_out(vty, " paging lifetime %u%s", paging_get_lifetime(btsb->paging_state),
 		VTY_NEWLINE);
-	if (btsb->agch_queue_thres_level == GSM_BTS_AGCH_QUEUE_THRES_LEVEL_DISABLE)
+	if (btsb->agch_queue_thresh_level == GSM_BTS_AGCH_QUEUE_THRESH_LEVEL_DISABLE)
 		vty_out(vty, " no agch-queue-mgmt%s", VTY_NEWLINE);
-	else if (btsb->agch_queue_thres_level == GSM_BTS_AGCH_QUEUE_THRES_LEVEL_DEFAULT
+	else if (btsb->agch_queue_thresh_level == GSM_BTS_AGCH_QUEUE_THRESH_LEVEL_DEFAULT
 		 && btsb->agch_queue_low_level == GSM_BTS_AGCH_QUEUE_LOW_LEVEL_DEFAULT
 		 && btsb->agch_queue_high_level == GSM_BTS_AGCH_QUEUE_HIGH_LEVEL_DEFAULT)
 		vty_out(vty, " agch-queue-mgmt%s", VTY_NEWLINE);
 	else
 		vty_out(vty, " agch-queue-mgmt threshold %d low %d high %d%s",
-			btsb->agch_queue_thres_level, btsb->agch_queue_low_level,
+			btsb->agch_queue_thresh_level, btsb->agch_queue_low_level,
 			btsb->agch_queue_high_level, VTY_NEWLINE);
 
 	bts_model_config_write_bts(vty, bts);
@@ -347,7 +347,7 @@ DEFUN(cfg_bts_agch_queue_mgmt_params,
 	struct gsm_bts *bts = vty->index;
 	struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
 
-	btsb->agch_queue_thres_level = atoi(argv[0]);
+	btsb->agch_queue_thresh_level = atoi(argv[0]);
 	btsb->agch_queue_low_level = atoi(argv[1]);
 	btsb->agch_queue_high_level = atoi(argv[2]);
 
@@ -362,7 +362,7 @@ DEFUN(cfg_bts_agch_queue_mgmt,
 	struct gsm_bts *bts = vty->index;
 	struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
 
-	btsb->agch_queue_thres_level = GSM_BTS_AGCH_QUEUE_THRES_LEVEL_DEFAULT;
+	btsb->agch_queue_thresh_level = GSM_BTS_AGCH_QUEUE_THRESH_LEVEL_DEFAULT;
 	btsb->agch_queue_low_level = GSM_BTS_AGCH_QUEUE_LOW_LEVEL_DEFAULT;
 	btsb->agch_queue_high_level = GSM_BTS_AGCH_QUEUE_HIGH_LEVEL_DEFAULT;
 
@@ -377,7 +377,7 @@ DEFUN(cfg_bts_no_agch_queue_mgmt,
 	struct gsm_bts *bts = vty->index;
 	struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
 
-	btsb->agch_queue_thres_level = GSM_BTS_AGCH_QUEUE_THRES_LEVEL_DISABLE;
+	btsb->agch_queue_thresh_level = GSM_BTS_AGCH_QUEUE_THRESH_LEVEL_DISABLE;
 	btsb->agch_queue_low_level = 0;
 	btsb->agch_queue_high_level = 0;
 
diff --git a/tests/agch/agch_test.ok b/tests/agch/agch_test.ok
index 57439ed..f31676a 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 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
+AGCH filled: count 720, imm.ass 80, imm.ass.rej 640 (refs 640), queue limit 32, occupied 240, dropped 0, merged 480, rejected 0, ag-res 0, non-res 0
+AGCH drained: multiframes 32, imm.ass 80, imm.ass.rej 12 (refs 48), queue limit 32, occupied 0, dropped 148, merged 480, rejected 0, ag-res 31, non-res 61
 Success
-- 
1.7.9.5





More information about the OpenBSC mailing list