blob: 3756b9dda2de01abc0519d0bf96c73681f58807c [file] [log] [blame]
Pau Espin Pedrol582a15e2021-03-12 15:40:48 +01001/* PDCH UL Controller test
2 *
3 * Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
4 * Author: Pau Espin Pedrol <pespin@sysmocom.de>
5 * All Rights Reserved
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include <string.h>
22#include <stdio.h>
Pau Espin Pedrol95f8fa12021-03-12 17:50:10 +010023#include <stdint.h>
24#include <unistd.h>
25#include <inttypes.h>
Pau Espin Pedrol582a15e2021-03-12 15:40:48 +010026
27extern "C" {
28#include <osmocom/core/application.h>
29#include <osmocom/gsm/gsm_utils.h>
30#include <osmocom/core/talloc.h>
31#include <osmocom/core/utils.h>
32}
33
Pau Espin Pedrol95f8fa12021-03-12 17:50:10 +010034#include "gprs_ms.h"
Pau Espin Pedrol582a15e2021-03-12 15:40:48 +010035#include "bts.h"
36#include "sba.h"
37#include "pdch_ul_controller.h"
38
39/* globals used by the code */
40void *tall_pcu_ctx;
41
Pau Espin Pedrol95f8fa12021-03-12 17:50:10 +010042static void print_ulc_nodes(struct pdch_ulc *ulc)
43{
44 struct rb_node *node;
45 for (node = rb_first(&ulc->tree_root); node; node = rb_next(node)) {
46 struct pdch_ulc_node *it = container_of(node, struct pdch_ulc_node, node);
47 printf("FN=%" PRIu32 " type=%s\n",
48 it->fn, get_value_string(pdch_ul_node_names, it->type));
49 }
50}
51
Pau Espin Pedrol4f67a9b2021-06-30 16:03:06 +020052static struct gprs_rlcmac_bts *setup_new_bts(void)
53{
54 struct gprs_rlcmac_bts *bts = bts_alloc(the_pcu, 0);
55 struct gprs_rlcmac_pdch *pdch = &bts->trx[0].pdch[0];
56 pdch->enable();
57 return bts;
58}
59
Pau Espin Pedrol582a15e2021-03-12 15:40:48 +010060static void test_reserve_multiple()
61{
62 printf("=== start: %s ===\n", __FUNCTION__);
63 const uint32_t fn = 20;
Pau Espin Pedrol4f67a9b2021-06-30 16:03:06 +020064 struct gprs_rlcmac_bts *bts = setup_new_bts();
Pau Espin Pedrol582a15e2021-03-12 15:40:48 +010065 struct gprs_rlcmac_pdch *pdch = &bts->trx[0].pdch[0];
66 struct gprs_rlcmac_tbf *tbf1 = (struct gprs_rlcmac_tbf*)0x1234; /*Dummy pointer */
67 struct gprs_rlcmac_tbf *tbf2 = (struct gprs_rlcmac_tbf*)0x5678; /*Dummy pointer */
68 struct gprs_rlcmac_sba *sba1, *sba2;
69 pdch->last_rts_fn = fn; /* This is used by sba_alloc to set + reserve FN */
70 sba1 = sba_alloc(bts, pdch, 0);
71 pdch->last_rts_fn = fn_next_block(pdch->last_rts_fn);
72 sba2 = sba_alloc(bts, pdch, 0);
73 uint32_t tbf1_poll_fn1 = fn_next_block(sba2->fn);
74 uint32_t tbf2_poll_fn1 = fn_next_block(tbf1_poll_fn1);
75 uint32_t tbf1_poll_fn2 = fn_next_block(tbf2_poll_fn1);
76 int rc;
77 struct pdch_ulc_node *node;
78
79 /* SBAs are reserved directly during allocation: */
80 OSMO_ASSERT(pdch_ulc_fn_is_free(pdch->ulc, sba1->fn) == false);
81 OSMO_ASSERT(pdch_ulc_get_sba(pdch->ulc, sba1->fn) == sba1);
82 OSMO_ASSERT(pdch_ulc_fn_is_free(pdch->ulc, sba2->fn) == false);
83 OSMO_ASSERT(pdch_ulc_get_sba(pdch->ulc, sba2->fn) == sba2);
84
85 rc = pdch_ulc_reserve_sba(pdch->ulc, sba1);
86 OSMO_ASSERT(rc == -EEXIST);
87 OSMO_ASSERT(pdch_ulc_fn_is_free(pdch->ulc, sba1->fn) == false);
88 node = pdch_ulc_get_node(pdch->ulc, sba1->fn);
89 OSMO_ASSERT(node->type == PDCH_ULC_NODE_SBA && node->sba.sba == sba1);
90 OSMO_ASSERT(pdch_ulc_fn_is_free(pdch->ulc, sba1->fn) == false);
91
92 rc = pdch_ulc_reserve_sba(pdch->ulc, sba2);
93 OSMO_ASSERT(rc == -EEXIST);
94 OSMO_ASSERT(pdch_ulc_get_sba(pdch->ulc, sba1->fn) == sba1);
95 OSMO_ASSERT(pdch_ulc_get_sba(pdch->ulc, sba2->fn) == sba2);
96 node = pdch_ulc_get_node(pdch->ulc, sba2->fn);
97 OSMO_ASSERT(node->type == PDCH_ULC_NODE_SBA && node->sba.sba == sba2);
98
Pau Espin Pedrol86580e12021-03-29 18:15:30 +020099 rc = pdch_ulc_reserve_tbf_poll(pdch->ulc, sba1->fn, tbf1, PDCH_ULC_POLL_UL_ASS);
Pau Espin Pedrol582a15e2021-03-12 15:40:48 +0100100 OSMO_ASSERT(rc == -EEXIST);
101 OSMO_ASSERT(pdch_ulc_get_tbf_poll(pdch->ulc, sba1->fn) == NULL);
Pau Espin Pedrol86580e12021-03-29 18:15:30 +0200102 rc = pdch_ulc_reserve_tbf_poll(pdch->ulc, sba2->fn, tbf1, PDCH_ULC_POLL_UL_ASS);
Pau Espin Pedrol582a15e2021-03-12 15:40:48 +0100103 OSMO_ASSERT(rc == -EEXIST);
104 OSMO_ASSERT(pdch_ulc_get_tbf_poll(pdch->ulc, sba2->fn) == NULL);
105
106 /* Now Reserve correctly TBF1 */
107 OSMO_ASSERT(pdch_ulc_fn_is_free(pdch->ulc, tbf1_poll_fn1) == true);
Pau Espin Pedrol86580e12021-03-29 18:15:30 +0200108 rc = pdch_ulc_reserve_tbf_poll(pdch->ulc, tbf1_poll_fn1, tbf1, PDCH_ULC_POLL_UL_ASS);
Pau Espin Pedrol582a15e2021-03-12 15:40:48 +0100109 OSMO_ASSERT(rc == 0);
110 OSMO_ASSERT(pdch_ulc_get_tbf_poll(pdch->ulc, tbf1_poll_fn1) == tbf1);
111 OSMO_ASSERT(pdch_ulc_fn_is_free(pdch->ulc, tbf1_poll_fn1) == false);
112 node = pdch_ulc_get_node(pdch->ulc, tbf1_poll_fn1);
113 OSMO_ASSERT(node->type == PDCH_ULC_NODE_TBF_POLL && node->tbf_poll.poll_tbf == tbf1);
114 OSMO_ASSERT(pdch_ulc_fn_is_free(pdch->ulc, tbf1_poll_fn1) == false);
115
116 /* Now reserve correctly TBF2 */
117 OSMO_ASSERT(pdch_ulc_fn_is_free(pdch->ulc, tbf2_poll_fn1) == true);
Pau Espin Pedrol86580e12021-03-29 18:15:30 +0200118 rc = pdch_ulc_reserve_tbf_poll(pdch->ulc, tbf2_poll_fn1, tbf2, PDCH_ULC_POLL_UL_ASS);
Pau Espin Pedrol582a15e2021-03-12 15:40:48 +0100119 OSMO_ASSERT(rc == 0);
120 OSMO_ASSERT(pdch_ulc_get_tbf_poll(pdch->ulc, tbf2_poll_fn1) == tbf2);
121 OSMO_ASSERT(pdch_ulc_fn_is_free(pdch->ulc, tbf2_poll_fn1) == false);
122 node = pdch_ulc_get_node(pdch->ulc, tbf2_poll_fn1);
123 OSMO_ASSERT(node->type == PDCH_ULC_NODE_TBF_POLL && node->tbf_poll.poll_tbf == tbf2);
124 OSMO_ASSERT(pdch_ulc_fn_is_free(pdch->ulc, tbf2_poll_fn1) == false);
125
126 /* Now Reserve TBF1 for POLL again on a later FN, which is totally expected: */
127 OSMO_ASSERT(pdch_ulc_fn_is_free(pdch->ulc, tbf1_poll_fn2) == true);
Pau Espin Pedrol86580e12021-03-29 18:15:30 +0200128 rc = pdch_ulc_reserve_tbf_poll(pdch->ulc, tbf1_poll_fn2, tbf1, PDCH_ULC_POLL_UL_ASS);
Pau Espin Pedrol582a15e2021-03-12 15:40:48 +0100129 OSMO_ASSERT(rc == 0);
130 OSMO_ASSERT(pdch_ulc_get_tbf_poll(pdch->ulc, tbf1_poll_fn2) == tbf1);
131 OSMO_ASSERT(pdch_ulc_fn_is_free(pdch->ulc, tbf1_poll_fn2) == false);
132 node = pdch_ulc_get_node(pdch->ulc, tbf1_poll_fn2);
133 OSMO_ASSERT(node->type == PDCH_ULC_NODE_TBF_POLL && node->tbf_poll.poll_tbf == tbf1);
134 OSMO_ASSERT(pdch_ulc_fn_is_free(pdch->ulc, tbf1_poll_fn2) == false);
135
136 /* Now release them in different ways: */
137 node = pdch_ulc_pop_node(pdch->ulc, sba2->fn);
138 OSMO_ASSERT(node->type == PDCH_ULC_NODE_SBA && node->sba.sba == sba2);
139 OSMO_ASSERT(pdch_ulc_get_sba(pdch->ulc, sba2->fn) == NULL);
140 OSMO_ASSERT(pdch_ulc_fn_is_free(pdch->ulc, sba2->fn) == true);
141 /* This will probably print a warning since in general SBAs are expected
142 * to be released from ULC during sba_free() time: */
143 sba_free(sba2);
144
145 pdch_ulc_expire_fn(pdch->ulc, sba1->fn);
146
147 /* here the 2 tbf1 entries should be removed, so Ul Controller should
148 only have 1 entry for tbf2 after the call: */
149 pdch_ulc_release_tbf(pdch->ulc, tbf1);
150 OSMO_ASSERT(pdch_ulc_get_tbf_poll(pdch->ulc, tbf1_poll_fn1) == NULL);
151 OSMO_ASSERT(pdch_ulc_get_tbf_poll(pdch->ulc, tbf1_poll_fn2) == NULL);
152 OSMO_ASSERT(pdch_ulc_get_tbf_poll(pdch->ulc, tbf2_poll_fn1) == tbf2);
153
154 rc = pdch_ulc_release_fn(pdch->ulc, tbf1_poll_fn1);
155 OSMO_ASSERT(rc == -ENOKEY);
156 rc = pdch_ulc_release_fn(pdch->ulc, tbf1_poll_fn2);
157 OSMO_ASSERT(rc == -ENOKEY);
158 rc = pdch_ulc_release_fn(pdch->ulc, tbf2_poll_fn1);
159 OSMO_ASSERT(rc == 0);
160
161 /* Make sure the store is empty now: */
162 OSMO_ASSERT(!rb_first(&pdch->ulc->tree_root));
163
164 talloc_free(bts);
165 printf("=== end: %s ===\n", __FUNCTION__);
166}
167
Pau Espin Pedrol95f8fa12021-03-12 17:50:10 +0100168int _alloc_algorithm_dummy(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf,
169 bool single, int8_t use_tbf)
170{
Pau Espin Pedrol1a1557a2021-05-13 18:39:36 +0200171 tbf->trx = &bts->trx[0];
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100172 ms_set_first_common_ts(tbf_ms(tbf), &tbf->trx->pdch[0]);
Pau Espin Pedrol95f8fa12021-03-12 17:50:10 +0100173 return 0;
174}
175
176
177static void test_fn_wrap_around()
178{
179 printf("=== start: %s ===\n", __FUNCTION__);
180 const uint32_t start_fn = GSM_MAX_FN - 40;
181
182 the_pcu->alloc_algorithm = _alloc_algorithm_dummy;
183
Pau Espin Pedrol4f67a9b2021-06-30 16:03:06 +0200184 struct gprs_rlcmac_bts *bts = setup_new_bts();
Pau Espin Pedroleae91472023-04-19 19:42:05 +0200185 struct GprsMs *ms = ms_alloc(bts, NULL);
Pau Espin Pedrolf80cc5b2023-04-17 14:25:51 +0200186 ms_confirm_tlli(ms, 0x12345678);
Pau Espin Pedrol87573842022-10-26 20:23:45 +0200187 struct gprs_rlcmac_tbf *tbf1 = dl_tbf_alloc(bts, ms, 0, true);
Pau Espin Pedrol95f8fa12021-03-12 17:50:10 +0100188 struct gprs_rlcmac_pdch *pdch = &tbf1->trx->pdch[0];
189 int rc;
190 uint32_t fn, last_fn;
191
192 fn = start_fn;
193 while (fn < 40 || fn >= start_fn) {
194 printf("*** RESERVE FN=%" PRIu32 ":\n", fn);
Pau Espin Pedrol86580e12021-03-29 18:15:30 +0200195 rc = pdch_ulc_reserve_tbf_poll(pdch->ulc, fn, tbf1, PDCH_ULC_POLL_UL_ASS);
Pau Espin Pedrol95f8fa12021-03-12 17:50:10 +0100196 OSMO_ASSERT(rc == 0);
197 print_ulc_nodes(pdch->ulc);
198 fn = fn_next_block(fn);
199 }
200 last_fn = fn;
201
202 /* Expiring fn_next_block(start_fn) should only expire first 2 entries here: */
203 fn = fn_next_block(start_fn);
204 printf("*** EXPIRE FN=%" PRIu32 ":\n", fn);
205 pdch_ulc_expire_fn(pdch->ulc, fn);
206 print_ulc_nodes(pdch->ulc);
207
208 /* We should still be able to release FN=0 here, since it came later: */
209 printf("*** RELEASE fn=%" PRIu32 ":\n", 0);
210 rc = pdch_ulc_release_fn(pdch->ulc, 0);
211 print_ulc_nodes(pdch->ulc);
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +0100212 OSMO_ASSERT(rc == 0);
Pau Espin Pedrol95f8fa12021-03-12 17:50:10 +0100213
214 /* Expiring last FN should expire all entries */
215 printf("*** EXPIRE FN=%" PRIu32 ":\n", last_fn);
216 pdch_ulc_expire_fn(pdch->ulc, last_fn);
217 print_ulc_nodes(pdch->ulc);
218 /* Make sure the store is empty now: */
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +0100219 OSMO_ASSERT(!rb_first(&pdch->ulc->tree_root));
Pau Espin Pedrol95f8fa12021-03-12 17:50:10 +0100220
221 talloc_free(bts);
222 printf("=== end: %s ===\n", __FUNCTION__);
223}
224
Pau Espin Pedrolce3bd252021-03-29 12:13:13 +0200225static void test_next_free_fn_sba()
226{
227 printf("=== start: %s ===\n", __FUNCTION__);
Pau Espin Pedrol4f67a9b2021-06-30 16:03:06 +0200228 struct gprs_rlcmac_bts *bts = setup_new_bts();
Pau Espin Pedrolce3bd252021-03-29 12:13:13 +0200229 struct gprs_rlcmac_pdch *pdch = &bts->trx[0].pdch[0];
230 struct gprs_rlcmac_sba *sba1, *sba2, *sba3, *sba4;
231
232 pdch->last_rts_fn = 52;
233 printf("*** ALLOC 1 SBA FN=%" PRIu32 ":\n", pdch->last_rts_fn);
234 sba1 = sba_alloc(bts, pdch, 0);
235 print_ulc_nodes(pdch->ulc);
236
237 pdch->last_rts_fn = 65;
238 printf("*** ALLOC 3 SBA FN=%" PRIu32 ":\n", pdch->last_rts_fn);
239 sba2 = sba_alloc(bts, pdch, 0);
240 sba3 = sba_alloc(bts, pdch, 0);
241 sba4 = sba_alloc(bts, pdch, 0);
242 print_ulc_nodes(pdch->ulc);
243 (void)sba1; (void)sba2; (void)sba3; (void)sba4;
244
245 talloc_free(bts);
246 printf("=== end: %s ===\n", __FUNCTION__);
247}
248
Pau Espin Pedrol50a1ede2021-03-29 13:49:43 +0200249static void test_next_free_fn_rrbp()
250{
251 printf("=== start: %s ===\n", __FUNCTION__);
Pau Espin Pedrol4f67a9b2021-06-30 16:03:06 +0200252 struct gprs_rlcmac_bts *bts = setup_new_bts();
Pau Espin Pedrol50a1ede2021-03-29 13:49:43 +0200253 struct gprs_rlcmac_pdch *pdch = &bts->trx[0].pdch[0];
254 struct gprs_rlcmac_sba *sba1;
255 uint32_t poll_fn, curr_fn;
256 unsigned int rrbp;
257 int rc;
258
259 rc = pdch_ulc_get_next_free_rrbp_fn(pdch->ulc, 26, &poll_fn, &rrbp);
260 OSMO_ASSERT(rc == 0);
261 OSMO_ASSERT(poll_fn == 26+13);
262 OSMO_ASSERT(rrbp == RRBP_N_plus_13);
263
264
265 pdch->last_rts_fn = 52;
266 printf("*** ALLOC 1 SBA FN=%" PRIu32 ":\n", pdch->last_rts_fn);
267 sba1 = sba_alloc(bts, pdch, 0); (void)sba1;
268 print_ulc_nodes(pdch->ulc);
269 curr_fn = sba1->fn - 13;
270 rc = pdch_ulc_get_next_free_rrbp_fn(pdch->ulc, curr_fn, &poll_fn, &rrbp);
271 OSMO_ASSERT(rc == 0);
272 printf("***NEXT FREE RRBP FN=%" PRIu32 ":\n", poll_fn);
273 OSMO_ASSERT(poll_fn == (curr_fn+17) || poll_fn == (curr_fn+18));
274 OSMO_ASSERT(rrbp == RRBP_N_plus_17_18);
275
276 pdch->last_rts_fn = fn_next_block(pdch->last_rts_fn);
277 printf("*** ALLOC 1 SBA FN=%" PRIu32 ":\n", pdch->last_rts_fn);
278 sba1 = sba_alloc(bts, pdch, 0); (void)sba1;
279 print_ulc_nodes(pdch->ulc);
280 rc = pdch_ulc_get_next_free_rrbp_fn(pdch->ulc, curr_fn, &poll_fn, &rrbp);
281 OSMO_ASSERT(rc == 0);
282 printf("***NEXT FREE RRBP FN=%" PRIu32 ":\n", poll_fn);
283 OSMO_ASSERT(poll_fn == (curr_fn+21) || poll_fn == (curr_fn+22));
284 OSMO_ASSERT(rrbp == RRBP_N_plus_21_22);
285
286 pdch->last_rts_fn = fn_next_block(pdch->last_rts_fn);
287 printf("*** ALLOC 1 SBA FN=%" PRIu32 ":\n", pdch->last_rts_fn);
288 sba1 = sba_alloc(bts, pdch, 0); (void)sba1;
289 print_ulc_nodes(pdch->ulc);
290 rc = pdch_ulc_get_next_free_rrbp_fn(pdch->ulc, curr_fn, &poll_fn, &rrbp);
291 OSMO_ASSERT(rc == 0);
292 printf("***NEXT FREE RRBP FN=%" PRIu32 ":\n", poll_fn);
293 OSMO_ASSERT(poll_fn == (curr_fn+26));
294 OSMO_ASSERT(rrbp == RRBP_N_plus_26);
295
296 pdch->last_rts_fn = fn_next_block(pdch->last_rts_fn);
297 printf("*** ALLOC 1 SBA FN=%" PRIu32 ":\n", pdch->last_rts_fn);
298 sba1 = sba_alloc(bts, pdch, 0); (void)sba1;
299 print_ulc_nodes(pdch->ulc);
300 rc = pdch_ulc_get_next_free_rrbp_fn(pdch->ulc, curr_fn, &poll_fn, &rrbp);
301 OSMO_ASSERT(rc == -EBUSY);
302
303 talloc_free(bts);
304 printf("=== end: %s ===\n", __FUNCTION__);
305}
306
Pau Espin Pedrol582a15e2021-03-12 15:40:48 +0100307int main(int argc, char **argv)
308{
309 tall_pcu_ctx = talloc_named_const(NULL, 1, "pdch_ulc test context");
310 if (!tall_pcu_ctx)
311 abort();
312
313 msgb_talloc_ctx_init(tall_pcu_ctx, 0);
314 osmo_init_logging2(tall_pcu_ctx, &gprs_log_info);
315 log_set_use_color(osmo_stderr_target, 0);
316 log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
317 log_set_log_level(osmo_stderr_target, LOGL_DEBUG);
318 log_set_print_category_hex(osmo_stderr_target, 0);
319 log_set_print_category(osmo_stderr_target, 0);
320 log_parse_category_mask(osmo_stderr_target, "DPCU,1:DRLCMAC,1:DRLCMACUL,1");
321
322 the_pcu = gprs_pcu_alloc(tall_pcu_ctx);
323
324 test_reserve_multiple();
Pau Espin Pedrol95f8fa12021-03-12 17:50:10 +0100325 test_fn_wrap_around();
Pau Espin Pedrolce3bd252021-03-29 12:13:13 +0200326 test_next_free_fn_sba();
Pau Espin Pedrol50a1ede2021-03-29 13:49:43 +0200327 test_next_free_fn_rrbp();
Pau Espin Pedrol582a15e2021-03-12 15:40:48 +0100328 talloc_free(the_pcu);
329 return EXIT_SUCCESS;
330}
331
332/*
333 * stubs that should not be reached
334 */
335int16_t spoof_mnc = 0, spoof_mcc = 0;
336bool spoof_mnc_3_digits = false;
337extern "C" {
338 void l1if_pdch_req() {
339 abort();
340 } void l1if_connect_pdch() {
341 abort();
Philipp Maier72ed3332023-02-27 15:32:00 +0100342 } void l1if_disconnect_pdch() {
343 abort();
Pau Espin Pedrol582a15e2021-03-12 15:40:48 +0100344 }
345 void l1if_close_pdch() {
346 abort();
347 }
348 void l1if_open_pdch() {
349 abort();
350 }
351}