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


Reply via email to