blob: 909e7f8b83c56a73d3e0f48a56f37e906acdae2b [file] [log] [blame]
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +01001/* gprs_ms.c
2 *
3 * Copyright (C) 2015-2020 by Sysmocom s.f.m.c. GmbH
4 * Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010015 */
16
17
18#include "gprs_ms.h"
19#include "bts.h"
20#include "tbf.h"
21#include "tbf_ul.h"
22#include "gprs_debug.h"
23#include "gprs_codel.h"
24#include "pcu_utils.h"
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +010025#include "nacc_fsm.h"
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +020026#include "tbf_ul_ack_fsm.h"
Pau Espin Pedrol8abe13c2022-10-21 18:49:48 +020027#include "gprs_ms_storage.h"
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010028
29#include <time.h>
30
31#include <osmocom/core/talloc.h>
32#include <osmocom/core/utils.h>
33#include <osmocom/core/timer.h>
34#include <osmocom/gsm/protocol/gsm_04_08.h>
35#include <osmocom/gsm/gsm48.h>
36#include <osmocom/core/logging.h>
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +010037#include <osmocom/core/stats.h>
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010038#include "coding_scheme.h"
39
40#define GPRS_CODEL_SLOW_INTERVAL_MS 4000
41
42extern void *tall_pcu_ctx;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +010043static unsigned int next_ms_ctr_group_id;
44
45static const struct rate_ctr_desc ms_ctr_description[] = {
46 [MS_CTR_DL_CTRL_MSG_SCHED] = { "ms:dl_ctrl_msg_sched", "Amount of DL CTRL messages scheduled" },
47};
48
Pau Espin Pedrolf5a251b2021-01-12 20:57:56 +010049static const struct rate_ctr_group_desc ms_ctrg_desc = {
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +010050 .group_name_prefix = "pcu:ms",
51 .group_description = "MS Statistics",
52 .class_id = OSMO_STATS_CLASS_SUBSCRIBER,
53 .num_ctr = ARRAY_SIZE(ms_ctr_description),
54 .ctr_desc = ms_ctr_description,
55};
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010056
57static int64_t now_msec()
58{
59 struct timespec ts;
60 osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
61
62 return (int64_t)(ts.tv_sec) * 1000 + ts.tv_nsec / 1000000;
63}
64
65void gprs_default_cb_ms_idle(struct GprsMs *ms)
66{
67 talloc_free(ms);
68}
69
70void gprs_default_cb_ms_active(struct GprsMs *ms)
71{
72 /* do nothing */
73}
74
75static struct gpr_ms_callback gprs_default_cb = {
76 .ms_idle = gprs_default_cb_ms_idle,
77 .ms_active = gprs_default_cb_ms_active,
78};
79
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +010080static void ms_release_timer_cb(void *data)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010081{
82 struct GprsMs *ms = (struct GprsMs *) data;
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +010083 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Release timer expired\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010084
85 if (ms->timer.data) {
86 ms->timer.data = NULL;
87 ms_unref(ms);
88 }
89}
90
Pau Espin Pedrol14beef62022-10-26 19:44:07 +020091static void ms_llc_timer_cb(void *_ms)
92{
93 struct GprsMs *ms = _ms;
94 struct gprs_rlcmac_dl_tbf *dl_tbf = ms_dl_tbf(ms);
95
96 if (!dl_tbf)
97 return;
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +020098 if (tbf_state(dl_tbf_as_tbf_const(dl_tbf)) != TBF_ST_FLOW)
Pau Espin Pedrol14beef62022-10-26 19:44:07 +020099 return;
100
Pau Espin Pedrolbd1f01f2022-10-27 15:19:39 +0200101 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "LLC receive timeout, requesting DL ACK\n");
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200102
Pau Espin Pedrol8fa3e062022-10-28 17:38:52 +0200103 dl_tbf_request_dl_ack(dl_tbf);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200104}
105
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100106static int ms_talloc_destructor(struct GprsMs *ms);
Pau Espin Pedrolf80cc5b2023-04-17 14:25:51 +0200107struct GprsMs *ms_alloc(struct gprs_rlcmac_bts *bts)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100108{
109 struct GprsMs *ms = talloc_zero(tall_pcu_ctx, struct GprsMs);
Pau Espin Pedrolb4987462022-04-01 17:21:08 +0200110 OSMO_ASSERT(bts);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100111
112 talloc_set_destructor(ms, ms_talloc_destructor);
113
114 ms->bts = bts;
115 ms->cb = gprs_default_cb;
Pau Espin Pedrolf80cc5b2023-04-17 14:25:51 +0200116 ms->tlli = GSM_RESERVED_TMSI;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100117 ms->new_ul_tlli = GSM_RESERVED_TMSI;
118 ms->new_dl_tlli = GSM_RESERVED_TMSI;
119 ms->ta = GSM48_TA_INVALID;
120 ms->current_cs_ul = UNKNOWN;
121 ms->current_cs_dl = UNKNOWN;
122 ms->is_idle = true;
123 INIT_LLIST_HEAD(&ms->list);
124 INIT_LLIST_HEAD(&ms->old_tbfs);
125
126 int codel_interval = LLC_CODEL_USE_DEFAULT;
127
Pau Espin Pedrolf80cc5b2023-04-17 14:25:51 +0200128 LOGP(DRLCMAC, LOGL_INFO, "Creating MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100129
130 ms->imsi[0] = '\0';
Pau Espin Pedrolc7802d92022-05-09 16:07:52 +0200131 osmo_timer_setup(&ms->timer, ms_release_timer_cb, NULL);
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200132 llc_queue_init(&ms->llc_queue, ms);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200133 memset(&ms->llc_timer, 0, sizeof(ms->llc_timer));
134 osmo_timer_setup(&ms->llc_timer, ms_llc_timer_cb, ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100135
136 ms_set_mode(ms, GPRS);
137
Pau Espin Pedrolb4987462022-04-01 17:21:08 +0200138 codel_interval = the_pcu->vty.llc_codel_interval_msec;
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200139 if (codel_interval == LLC_CODEL_USE_DEFAULT)
140 codel_interval = GPRS_CODEL_SLOW_INTERVAL_MS;
141 llc_queue_set_codel_interval(&ms->llc_queue, codel_interval);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100142
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100143 ms->last_cs_not_low = now_msec();
144 ms->app_info_pending = false;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100145
146 ms->ctrs = rate_ctr_group_alloc(ms, &ms_ctrg_desc, next_ms_ctr_group_id++);
147 if (!ms->ctrs)
148 goto free_ret;
149
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100150 return ms;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100151free_ret:
152 talloc_free(ms);
153 return NULL;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100154}
155
156static int ms_talloc_destructor(struct GprsMs *ms)
157{
158 struct llist_item *pos, *tmp;
159
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100160 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Destroying MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100161
162 ms_set_reserved_slots(ms, NULL, 0, 0);
163
Vadim Yanitskiye33c2362022-07-22 03:44:44 +0700164 osmo_timer_del(&ms->timer);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100165
166 if (ms->ul_tbf) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200167 tbf_set_ms(ul_tbf_as_tbf(ms->ul_tbf), NULL);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100168 ms->ul_tbf = NULL;
169 }
170
171 if (ms->dl_tbf) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200172 tbf_set_ms(dl_tbf_as_tbf(ms->dl_tbf), NULL);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100173 ms->dl_tbf = NULL;
174 }
175
176 llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
177 struct gprs_rlcmac_tbf *tbf = (struct gprs_rlcmac_tbf *)pos->entry;
178 tbf_set_ms(tbf, NULL);
179 }
180
181 llc_queue_clear(&ms->llc_queue, ms->bts);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200182 osmo_timer_del(&ms->llc_timer);
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100183
184 if (ms->ctrs)
185 rate_ctr_group_free(ms->ctrs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100186 return 0;
187}
188
189
190void ms_set_callback(struct GprsMs *ms, struct gpr_ms_callback *cb)
191{
192 if (cb)
193 ms->cb = *cb;
194 else
195 ms->cb = gprs_default_cb;
196}
197
198static void ms_update_status(struct GprsMs *ms)
199{
200 if (ms->ref > 0)
201 return;
202
203 if (ms_is_idle(ms) && !ms->is_idle) {
204 ms->is_idle = true;
205 ms->cb.ms_idle(ms);
206 /* this can be deleted by now, do not access it */
207 return;
208 }
209
210 if (!ms_is_idle(ms) && ms->is_idle) {
211 ms->is_idle = false;
212 ms->cb.ms_active(ms);
213 }
214}
215
216struct GprsMs *ms_ref(struct GprsMs *ms)
217{
218 ms->ref += 1;
219 return ms;
220}
221
222void ms_unref(struct GprsMs *ms)
223{
224 OSMO_ASSERT(ms->ref >= 0);
225 ms->ref -= 1;
226 if (ms->ref == 0)
227 ms_update_status(ms);
228}
229
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100230static void ms_release_timer_start(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100231{
Pau Espin Pedrol14379ef2023-04-17 20:43:26 +0200232 /* Immediate free():
233 * Skip delaying free() through release timer if delay is configured to be 0.
234 * This is useful for synced freed during unit tests.
235 */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100236 if (ms->delay == 0)
237 return;
238
Pau Espin Pedrol14379ef2023-04-17 20:43:26 +0200239 /* Immediate free():
240 * Skip delaying free() through release timer if TMSI is not
241 * known, since those cannot really be reused.
242 */
243 if (ms_tlli(ms) == GSM_RESERVED_TMSI)
244 return;
245
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100246 LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Schedule MS release in %u secs\n", ms->delay);
247
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100248 if (!ms->timer.data)
249 ms->timer.data = ms_ref(ms);
250
251 osmo_timer_schedule(&ms->timer, ms->delay, 0);
252}
253
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100254static void ms_release_timer_stop(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100255{
256 if (!ms->timer.data)
257 return;
258
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100259 LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Cancel scheduled MS release\n");
260
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100261 osmo_timer_del(&ms->timer);
262 ms->timer.data = NULL;
263 ms_unref(ms);
264}
265
266void ms_set_mode(struct GprsMs *ms, enum mcs_kind mode)
267{
268 ms->mode = mode;
269
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100270 switch (ms->mode) {
271 case GPRS:
272 if (!mcs_is_gprs(ms->current_cs_ul)) {
273 ms->current_cs_ul = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100274 ms->bts->initial_cs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100275 if (!mcs_is_valid(ms->current_cs_ul))
276 ms->current_cs_ul = CS1;
277 }
278 if (!mcs_is_gprs(ms->current_cs_dl)) {
279 ms->current_cs_dl = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100280 ms->bts->initial_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100281 if (!mcs_is_valid(ms->current_cs_dl))
282 ms->current_cs_dl = CS1;
283 }
284 break;
285
286 case EGPRS_GMSK:
Pau Espin Pedrol7bb8cd62021-01-25 15:08:35 +0100287 if (!mcs_is_edge_gmsk(ms->current_cs_ul)) {
288 ms->current_cs_ul = mcs_get_egprs_by_num(
289 ms->bts->initial_mcs_ul);
290 if (!mcs_is_valid(ms->current_cs_ul))
291 ms->current_cs_ul = MCS1;
292 }
293 if (!mcs_is_edge_gmsk(ms->current_cs_dl)) {
294 ms->current_cs_dl = mcs_get_egprs_by_num(
295 ms->bts->initial_mcs_dl);
296 if (!mcs_is_valid(ms->current_cs_dl))
297 ms->current_cs_dl = MCS1;
298 }
299 break;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100300 case EGPRS:
301 if (!mcs_is_edge(ms->current_cs_ul)) {
302 ms->current_cs_ul = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100303 ms->bts->initial_mcs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100304 if (!mcs_is_valid(ms->current_cs_ul))
305 ms->current_cs_ul = MCS1;
306 }
307 if (!mcs_is_edge(ms->current_cs_dl)) {
308 ms->current_cs_dl = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100309 ms->bts->initial_mcs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100310 if (!mcs_is_valid(ms->current_cs_dl))
311 ms->current_cs_dl = MCS1;
312 }
313 break;
314 }
315}
316
317static void ms_attach_ul_tbf(struct GprsMs *ms, struct gprs_rlcmac_ul_tbf *tbf)
318{
319 if (ms->ul_tbf == tbf)
320 return;
321
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100322 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Attaching UL TBF: %s\n", tbf_name((struct gprs_rlcmac_tbf *)tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100323
324 ms_ref(ms);
325
326 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200327 llist_add_tail(tbf_ms_list(ul_tbf_as_tbf(ms->ul_tbf)), &ms->old_tbfs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100328
329 ms->ul_tbf = tbf;
330
331 if (tbf)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100332 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100333
334 ms_unref(ms);
335}
336
337static void ms_attach_dl_tbf(struct GprsMs *ms, struct gprs_rlcmac_dl_tbf *tbf)
338{
339 if (ms->dl_tbf == tbf)
340 return;
341
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100342 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Attaching DL TBF: %s\n", tbf_name((struct gprs_rlcmac_tbf *)tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100343
344 ms_ref(ms);
345
346 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200347 llist_add_tail(tbf_ms_list(dl_tbf_as_tbf(ms->dl_tbf)), &ms->old_tbfs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100348
349 ms->dl_tbf = tbf;
350
351 if (tbf)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100352 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100353
354 ms_unref(ms);
355}
356
357void ms_attach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
358{
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200359 if (tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF)
Pau Espin Pedrolcc30b052022-10-27 15:25:55 +0200360 ms_attach_dl_tbf(ms, tbf_as_dl_tbf(tbf));
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200361 else
Pau Espin Pedrolcc30b052022-10-27 15:25:55 +0200362 ms_attach_ul_tbf(ms, tbf_as_ul_tbf(tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100363}
364
365void ms_detach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
366{
367 if (tbf == (struct gprs_rlcmac_tbf *)(ms->ul_tbf)) {
368 ms->ul_tbf = NULL;
369 } else if (tbf == (struct gprs_rlcmac_tbf *)(ms->dl_tbf)) {
370 ms->dl_tbf = NULL;
371 } else {
372 bool found = false;
373
374 struct llist_item *pos, *tmp;
375 llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
376 struct gprs_rlcmac_tbf *tmp_tbf = (struct gprs_rlcmac_tbf *)pos->entry;
377 if (tmp_tbf == tbf) {
378 llist_del(&pos->list);
379 found = true;
380 break;
381 }
382 }
383
384 /* Protect against recursive calls via set_ms() */
385 if (!found)
386 return;
387 }
388
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100389 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Detaching TBF: %s\n",
390 tbf_name(tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100391
392 if (tbf_ms(tbf) == ms)
393 tbf_set_ms(tbf, NULL);
394
395 if (!ms->dl_tbf && !ms->ul_tbf) {
396 ms_set_reserved_slots(ms, NULL, 0, 0);
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100397 ms->first_common_ts = NULL;
Pau Espin Pedrol14379ef2023-04-17 20:43:26 +0200398 ms_release_timer_start(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100399 }
400
401 ms_update_status(ms);
402}
403
404void ms_reset(struct GprsMs *ms)
405{
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100406 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Clearing MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100407
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100408 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100409
410 ms->tlli = GSM_RESERVED_TMSI;
411 ms->new_dl_tlli = ms->tlli;
412 ms->new_ul_tlli = ms->tlli;
413 ms->imsi[0] = '\0';
414}
415
Pau Espin Pedrol8abe13c2022-10-21 18:49:48 +0200416/* This function should be called on the MS object of a TBF each time an RLCMAC
417 * block is received for it with TLLI information.
418 * Besides updating the TLLI field on the MS object, it also seeks for other MS
419 * objects in the store and merges them into the current MS object. The MS
420 * duplication happened because we don't learn the TLLI of the created TBF until
421 * a later point. */
422void ms_update_announced_tlli(struct GprsMs *ms, uint32_t tlli)
423{
424 struct GprsMs *old_ms = NULL;
425
426 if (tlli == GSM_RESERVED_TMSI)
427 return;
428
429 /* When the TLLI does not match the ms, check if there is another
430 * MS object that belongs to that TLLI and if yes make sure one of them
431 * gets deleted. */
432 if (!ms_check_tlli(ms, tlli))
433 old_ms = ms_store_get_ms(bts_ms_store(ms->bts), tlli, GSM_RESERVED_TMSI, NULL);
434
435 ms_set_tlli(ms, tlli);
436
437 if (old_ms)
438 ms_merge_and_clear_ms(ms, old_ms);
439 /* old_ms may no longer be available here */
440}
441
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100442/* Merge 'old_ms' object into 'ms' object.
443 * 'old_ms' may be freed during the call to this function, don't use the pointer to it afterwards */
444void ms_merge_and_clear_ms(struct GprsMs *ms, struct GprsMs *old_ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100445{
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100446 char old_ms_name[128];
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100447 OSMO_ASSERT(old_ms != ms);
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100448 ms_ref(old_ms);
449
450 ms_name_buf(old_ms, old_ms_name, sizeof(old_ms_name));
451
452 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Merge MS: %s\n", old_ms_name);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100453
454 if (strlen(ms_imsi(ms)) == 0 && strlen(ms_imsi(old_ms)) != 0)
455 osmo_strlcpy(ms->imsi, ms_imsi(old_ms), sizeof(ms->imsi));
456
457 if (!ms_ms_class(ms) && ms_ms_class(old_ms))
458 ms_set_ms_class(ms, ms_ms_class(old_ms));
459
460 if (!ms_egprs_ms_class(ms) && ms_egprs_ms_class(old_ms))
461 ms_set_egprs_ms_class(ms, ms_egprs_ms_class(old_ms));
462
463 llc_queue_move_and_merge(&ms->llc_queue, &old_ms->llc_queue);
464
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100465 /* Clean up the old MS object */
466 /* TODO: Use timer? */
467 if (ms_ul_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms), T_MAX))
468 tbf_free((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms));
469 if (ms_dl_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms), T_MAX))
470 tbf_free((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms));
471
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100472 ms_reset(old_ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100473
474 ms_unref(old_ms);
475}
476
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100477/* Apply changes to the TLLI directly, used interally by functions below: */
478static void ms_apply_tlli_change(struct GprsMs *ms, uint32_t tlli)
479{
480 ms->tlli = tlli;
481 ms->new_dl_tlli = GSM_RESERVED_TMSI;
482 ms->new_ul_tlli = GSM_RESERVED_TMSI;
483
484 /* Update TBF FSM names: */
485 if (ms->ul_tbf)
486 tbf_update_state_fsm_name(ul_tbf_as_tbf(ms->ul_tbf));
487 if (ms->dl_tbf)
488 tbf_update_state_fsm_name(dl_tbf_as_tbf(ms->dl_tbf));
489}
490
Pau Espin Pedrol0b5997e2022-10-21 14:10:41 +0200491/* Set/update the MS object TLLI based on knowledge gained from the MS side (Uplink direction) */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100492void ms_set_tlli(struct GprsMs *ms, uint32_t tlli)
493{
494 if (tlli == ms->tlli || tlli == ms->new_ul_tlli)
495 return;
496
497 if (tlli != ms->new_dl_tlli) {
498 LOGP(DRLCMAC, LOGL_INFO,
499 "Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
500 "not yet confirmed\n",
501 ms_tlli(ms), tlli);
502 ms->new_ul_tlli = tlli;
503 return;
504 }
505
506 LOGP(DRLCMAC, LOGL_INFO,
507 "Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
508 "already confirmed partly\n",
509 ms->tlli, tlli);
510
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100511 ms_apply_tlli_change(ms, tlli);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100512}
513
Pau Espin Pedrol0b5997e2022-10-21 14:10:41 +0200514/* Set/update the MS object TLLI based on knowledge gained from the SGSN side (Downlink direction) */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100515bool ms_confirm_tlli(struct GprsMs *ms, uint32_t tlli)
516{
517 if (tlli == ms->tlli || tlli == ms->new_dl_tlli)
518 return false;
519
520 if (tlli != ms->new_ul_tlli) {
521 /* The MS has not sent a message with the new TLLI, which may
522 * happen according to the spec [TODO: add reference]. */
523
524 LOGP(DRLCMAC, LOGL_INFO,
525 "The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
526 "partly confirmed\n", tlli);
527 /* Use the network's idea of TLLI as candidate, this does not
528 * change the result value of tlli() */
529 ms->new_dl_tlli = tlli;
530 return false;
531 }
532
533 LOGP(DRLCMAC, LOGL_INFO,
534 "Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
535
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100536 ms_apply_tlli_change(ms, tlli);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100537
538 return true;
539}
540
541void ms_set_imsi(struct GprsMs *ms, const char *imsi)
542{
543 if (!imsi) {
544 LOGP(DRLCMAC, LOGL_ERROR, "Expected IMSI!\n");
545 return;
546 }
547
548 if (imsi[0] && strlen(imsi) < 3) {
549 LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI '%s'!\n",
550 imsi);
551 return;
552 }
553
554 if (strcmp(imsi, ms->imsi) == 0)
555 return;
556
557 LOGP(DRLCMAC, LOGL_INFO,
558 "Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
559 ms_tlli(ms), ms->imsi, imsi);
560
Pau Espin Pedrolcde18c52023-04-17 18:16:48 +0200561 struct GprsMs *old_ms = bts_get_ms_by_imsi(ms->bts, imsi);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100562 /* Check if we are going to store a different MS object with already
563 existing IMSI. This is probably a bug in code calling this function,
564 since it should take care of this explicitly */
565 if (old_ms) {
566 /* We cannot find ms->ms by IMSI since we know that it has a
567 * different IMSI */
568 OSMO_ASSERT(old_ms != ms);
569
570 LOGPMS(ms, DRLCMAC, LOGL_NOTICE,
571 "IMSI '%s' was already assigned to another "
572 "MS object: TLLI = 0x%08x, that IMSI will be removed\n",
573 imsi, ms_tlli(old_ms));
574
575 ms_merge_and_clear_ms(ms, old_ms);
Pau Espin Pedrolb8ff74b2022-10-31 12:58:46 +0100576 /* old_ms may no longer be available here */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100577 }
578
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100579 /* Store the new IMSI: */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100580 osmo_strlcpy(ms->imsi, imsi, sizeof(ms->imsi));
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100581
582 /* Update TBF FSM names: */
583 if (ms->ul_tbf)
584 tbf_update_state_fsm_name(ul_tbf_as_tbf(ms->ul_tbf));
585 if (ms->dl_tbf)
586 tbf_update_state_fsm_name(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100587}
588
589void ms_set_ta(struct GprsMs *ms, uint8_t ta_)
590{
591 if (ta_ == ms->ta)
592 return;
593
594 if (gsm48_ta_is_valid(ta_)) {
595 LOGP(DRLCMAC, LOGL_INFO,
596 "Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
597 ms_tlli(ms), ms->ta, ta_);
598 ms->ta = ta_;
599 } else
600 LOGP(DRLCMAC, LOGL_NOTICE,
601 "MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
602 "value %d kept)\n", ms_tlli(ms), ta_, ms->ta);
603}
604
605void ms_set_ms_class(struct GprsMs *ms, uint8_t ms_class_)
606{
607 if (ms_class_ == ms->ms_class)
608 return;
609
610 LOGP(DRLCMAC, LOGL_INFO,
611 "Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
612 ms_tlli(ms), ms->ms_class, ms_class_);
613
614 ms->ms_class = ms_class_;
615}
616
617void ms_set_egprs_ms_class(struct GprsMs *ms, uint8_t ms_class_)
618{
619 if (ms_class_ == ms->egprs_ms_class)
620 return;
621
622 LOGP(DRLCMAC, LOGL_INFO,
623 "Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
624 ms_tlli(ms), ms->egprs_ms_class, ms_class_);
625
626 ms->egprs_ms_class = ms_class_;
627
628 if (!bts_max_mcs_ul(ms->bts) || !bts_max_mcs_dl(ms->bts)) {
629 LOGPMS(ms, DRLCMAC, LOGL_DEBUG,
630 "Avoid enabling EGPRS because use of MCS is disabled: ul=%u dl=%u\n",
631 bts_max_mcs_ul(ms->bts), bts_max_mcs_dl(ms->bts));
632 return;
633 }
634
635 if (mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts))) &&
636 mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts))) &&
637 ms_mode(ms) != EGPRS)
638 {
639 ms_set_mode(ms, EGPRS_GMSK);
640 } else {
641 ms_set_mode(ms, EGPRS);
642 }
643 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Enabled EGPRS, mode %s\n", mode_name(ms_mode(ms)));
644}
645
646void ms_update_error_rate(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, int error_rate)
647{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100648 int64_t now;
649 enum CodingScheme max_cs_dl = ms_max_cs_dl(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100650 OSMO_ASSERT(max_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100651
652 if (error_rate < 0)
653 return;
654
655 now = now_msec();
656
657 /* TODO: Check for TBF direction */
658 /* TODO: Support different CS values for UL and DL */
659
660 ms->nack_rate_dl = error_rate;
661
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100662 if (error_rate > the_pcu->vty.cs_adj_upper_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100663 if (mcs_chan_code(ms->current_cs_dl) > 0) {
664 mcs_dec_kind(&ms->current_cs_dl, ms_mode(ms));
665 LOGP(DRLCMACDL, LOGL_INFO,
666 "MS (IMSI %s): High error rate %d%%, "
667 "reducing CS level to %s\n",
668 ms_imsi(ms), error_rate, mcs_name(ms->current_cs_dl));
669 ms->last_cs_not_low = now;
670 }
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100671 } else if (error_rate < the_pcu->vty.cs_adj_lower_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100672 if (ms->current_cs_dl < max_cs_dl) {
673 if (now - ms->last_cs_not_low > 1000) {
674 mcs_inc_kind(&ms->current_cs_dl, ms_mode(ms));
675
676 LOGP(DRLCMACDL, LOGL_INFO,
677 "MS (IMSI %s): Low error rate %d%%, "
678 "increasing DL CS level to %s\n",
679 ms_imsi(ms), error_rate,
680 mcs_name(ms->current_cs_dl));
681 ms->last_cs_not_low = now;
682 } else {
683 LOGP(DRLCMACDL, LOGL_DEBUG,
684 "MS (IMSI %s): Low error rate %d%%, "
685 "ignored (within blocking period)\n",
686 ms_imsi(ms), error_rate);
687 }
688 }
689 } else {
690 LOGP(DRLCMACDL, LOGL_DEBUG,
691 "MS (IMSI %s): Medium error rate %d%%, ignored\n",
692 ms_imsi(ms), error_rate);
693 ms->last_cs_not_low = now;
694 }
695}
696
697enum CodingScheme ms_max_cs_ul(const struct GprsMs *ms)
698{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100699 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100700 OSMO_ASSERT(ms->bts != NULL);
701
702 if (mcs_is_gprs(ms->current_cs_ul)) {
703 if (!bts_max_cs_ul(ms->bts)) {
704 return CS4;
705 }
706
707 return mcs_get_gprs_by_num(bts_max_cs_ul(ms->bts));
708 }
709
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100710 cs = mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts));
711 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
712 cs = MCS4;
713 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100714}
715
716void ms_set_current_cs_dl(struct GprsMs *ms, enum CodingScheme scheme)
717{
718 ms->current_cs_dl = scheme;
719}
720
721enum CodingScheme ms_max_cs_dl(const struct GprsMs *ms)
722{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100723 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100724 OSMO_ASSERT(ms->bts != NULL);
725
726 if (mcs_is_gprs(ms->current_cs_dl)) {
727 if (!bts_max_cs_dl(ms->bts)) {
728 return CS4;
729 }
730
731 return mcs_get_gprs_by_num(bts_max_cs_dl(ms->bts));
732 }
733
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100734 cs = mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts));
735 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
736 cs = MCS4;
737 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100738}
739
740void ms_update_cs_ul(struct GprsMs *ms, const struct pcu_l1_meas *meas)
741{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100742 enum CodingScheme max_cs_ul = ms_max_cs_ul(ms);
743
744 int old_link_qual;
745 int low;
746 int high;
747 enum CodingScheme new_cs_ul = ms->current_cs_ul;
748 uint8_t current_cs = mcs_chan_code(ms->current_cs_ul);
749
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100750 if (!max_cs_ul) {
751 LOGP(DRLCMACMEAS, LOGL_ERROR,
752 "max_cs_ul cannot be derived (current UL CS: %s)\n",
753 mcs_name(ms->current_cs_ul));
754 return;
755 }
756
757 if (!ms->current_cs_ul) {
758 LOGP(DRLCMACMEAS, LOGL_ERROR,
759 "Unable to update UL (M)CS because it's not set: %s\n",
760 mcs_name(ms->current_cs_ul));
761 return;
762 }
763
764 if (!meas->have_link_qual) {
765 LOGP(DRLCMACMEAS, LOGL_ERROR,
766 "Unable to update UL (M)CS %s because we don't have link quality measurements.\n",
767 mcs_name(ms->current_cs_ul));
768 return;
769 }
770
771 if (mcs_is_gprs(ms->current_cs_ul)) {
772 if (current_cs >= MAX_GPRS_CS)
773 current_cs = MAX_GPRS_CS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100774 low = the_pcu->vty.cs_lqual_ranges[current_cs].low;
775 high = the_pcu->vty.cs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100776 } else if (mcs_is_edge(ms->current_cs_ul)) {
777 if (current_cs >= MAX_EDGE_MCS)
778 current_cs = MAX_EDGE_MCS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100779 low = the_pcu->vty.mcs_lqual_ranges[current_cs].low;
780 high = the_pcu->vty.mcs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100781 } else {
782 LOGP(DRLCMACMEAS, LOGL_ERROR,
783 "Unable to update UL (M)CS because it's neither GPRS nor EDGE: %s\n",
784 mcs_name(ms->current_cs_ul));
785 return;
786 }
787
788 /* To avoid rapid changes of the coding scheme, we also take
789 * the old link quality value into account (if present). */
790 if (ms->l1_meas.have_link_qual)
791 old_link_qual = ms->l1_meas.link_qual;
792 else
793 old_link_qual = meas->link_qual;
794
795 if (meas->link_qual < low && old_link_qual < low)
796 mcs_dec_kind(&new_cs_ul, ms_mode(ms));
797 else if (meas->link_qual > high && old_link_qual > high &&
798 ms->current_cs_ul < max_cs_ul)
799 mcs_inc_kind(&new_cs_ul, ms_mode(ms));
800
801 if (ms->current_cs_ul != new_cs_ul) {
802 LOGPMS(ms, DRLCMACMEAS, LOGL_INFO,
803 "Link quality %ddB (old %ddB) left window [%d, %d], "
804 "modifying uplink CS level: %s -> %s\n",
805 meas->link_qual, old_link_qual,
806 low, high,
807 mcs_name(ms->current_cs_ul), mcs_name(new_cs_ul));
808
809 ms->current_cs_ul = new_cs_ul;
810 }
811}
812
813void ms_update_l1_meas(struct GprsMs *ms, const struct pcu_l1_meas *meas)
814{
815 unsigned i;
816
817 ms_update_cs_ul(ms, meas);
818
819 if (meas->have_rssi)
820 pcu_l1_meas_set_rssi(&ms->l1_meas, meas->rssi);
821 if (meas->have_bto)
822 pcu_l1_meas_set_bto(&ms->l1_meas, meas->bto);
823 if (meas->have_ber)
824 pcu_l1_meas_set_ber(&ms->l1_meas, meas->ber);
825 if (meas->have_link_qual)
826 pcu_l1_meas_set_link_qual(&ms->l1_meas, meas->link_qual);
827
828 if (meas->have_ms_rx_qual)
829 pcu_l1_meas_set_ms_rx_qual(&ms->l1_meas, meas->ms_rx_qual);
830 if (meas->have_ms_c_value)
831 pcu_l1_meas_set_ms_c_value(&ms->l1_meas, meas->ms_c_value);
832 if (meas->have_ms_sign_var)
833 pcu_l1_meas_set_ms_sign_var(&ms->l1_meas, meas->ms_sign_var);
834
835 if (meas->have_ms_i_level) {
836 for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
837 if (meas->ts[i].have_ms_i_level)
838 pcu_l1_meas_set_ms_i_level(&ms->l1_meas, i, meas->ts[i].ms_i_level);
839 else
840 ms->l1_meas.ts[i].have_ms_i_level = 0;
841 }
842 }
843}
844
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100845/* req_mcs_kind acts as a set filter, where EGPRS means any and GPRS is the most restrictive */
846enum CodingScheme ms_current_cs_dl(const struct GprsMs *ms, enum mcs_kind req_mcs_kind)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100847{
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100848 enum CodingScheme orig_cs = ms->current_cs_dl;
849 struct gprs_rlcmac_bts *bts = ms->bts;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100850 size_t unencoded_octets;
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100851 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100852
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100853 /* It could be that a TBF requests a GPRS CS despite the MS currently
854 being upgraded to EGPRS (hence reporting MCS). That could happen
855 because the TBF was created early in the process where we didn't have
856 yet enough information about the MS, and only AFTER it was created we
857 upgraded the MS to be EGPRS capable.
858 As a result, when the MS is queried for the target CS here, we could be
859 returning an MCS despite the current TBF being established as GPRS,
860 but we rather stick to the TBF type we assigned to the MS rather than
861 magically sending EGPRS data blocks to a GPRS TBF.
862 It could also be that the caller requests specific MCS kind
863 explicitly too due to scheduling restrictions (GPRS+EGPRS multiplexing). */
864 if (req_mcs_kind == EGPRS_GMSK && mcs_is_edge(orig_cs) && orig_cs > MCS4) {
865 cs = bts_cs_dl_is_supported(bts, MCS4) ? MCS4 :
866 bts_cs_dl_is_supported(bts, MCS3) ? MCS3 :
867 bts_cs_dl_is_supported(bts, MCS2) ? MCS2 :
868 MCS1;
869 } else if (req_mcs_kind == GPRS && mcs_is_edge(orig_cs)) { /* GPRS */
870 int i;
871 cs = orig_cs > MCS4 ? MCS4 : orig_cs;
872 cs -= (MCS1 - CS1); /* MCSx -> CSx */
873 /* Find suitable CS starting from equivalent MCS which is supported by BTS: */
874 for (i = mcs_chan_code(cs); !bts_cs_dl_is_supported(bts, CS1 + i); i--);
875 OSMO_ASSERT(i >= 0 && i <= 3); /* CS1 is always supported */
876 cs = CS1 + i;
877 } else {
878 cs = orig_cs;
879 }
880
881 if (orig_cs != cs)
882 LOGPMS(ms, DRLCMACDL, LOGL_INFO, "MS (mode=%s) suggests transmitting "
883 "DL %s, downgrade to %s in order to match TBF & scheduler requirements\n",
884 mode_name(ms_mode(ms)), mcs_name(orig_cs), mcs_name(cs));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100885
886 unencoded_octets = llc_queue_octets(&ms->llc_queue);
887
888 /* If the DL TBF is active, add number of unencoded chunk octets */
889 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200890 unencoded_octets += llc_chunk_size(tbf_llc(dl_tbf_as_tbf(ms->dl_tbf)));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100891
892 /* There are many unencoded octets, don't reduce */
Pau Espin Pedrolad79b852021-01-14 13:20:55 +0100893 if (unencoded_octets >= the_pcu->vty.cs_downgrade_threshold)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100894 return cs;
895
896 /* RF conditions are good, don't reduce */
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100897 if (ms->nack_rate_dl < the_pcu->vty.cs_adj_lower_limit)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100898 return cs;
899
900 /* The throughput would probably be better if the CS level was reduced */
901 mcs_dec_kind(&cs, ms_mode(ms));
902
903 /* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
904 if (cs == CS2)
905 mcs_dec_kind(&cs, ms_mode(ms));
906
907 return cs;
908}
909
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100910struct gprs_rlcmac_pdch *ms_first_common_ts(const struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100911{
Pau Espin Pedrol345d9ad2022-12-12 19:22:44 +0100912 return ms->first_common_ts;
913}
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100914
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100915void ms_set_first_common_ts(struct GprsMs *ms, struct gprs_rlcmac_pdch *pdch)
Pau Espin Pedrol345d9ad2022-12-12 19:22:44 +0100916{
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100917 OSMO_ASSERT(pdch);
918 ms->first_common_ts = pdch;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100919}
920
921uint8_t ms_dl_slots(const struct GprsMs *ms)
922{
923 uint8_t slots = 0;
924
925 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200926 slots |= tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100927
928 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200929 slots |= tbf_dl_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100930
931 return slots;
932}
933
934uint8_t ms_ul_slots(const struct GprsMs *ms)
935{
936 uint8_t slots = 0;
937
938 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200939 slots |= tbf_ul_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100940
941 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200942 slots |= tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100943
944 return slots;
945}
946
947uint8_t ms_current_pacch_slots(const struct GprsMs *ms)
948{
949 uint8_t slots = 0;
950
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200951 bool is_dl_active = ms->dl_tbf && tbf_is_tfi_assigned(dl_tbf_as_tbf(ms->dl_tbf));
952 bool is_ul_active = ms->ul_tbf && tbf_is_tfi_assigned(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100953
954 if (!is_dl_active && !is_ul_active)
955 return 0;
956
957 /* see TS 44.060, 8.1.1.2.2 */
958 if (is_dl_active && !is_ul_active)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200959 slots = tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100960 else if (!is_dl_active && is_ul_active)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200961 slots = tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100962 else
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200963 slots = tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf)) &
964 tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100965
966 /* Assume a multislot class 1 device */
967 /* TODO: For class 2 devices, this could be removed */
968 slots = pcu_lsb(slots);
969
970 return slots;
971}
972
973void ms_set_reserved_slots(struct GprsMs *ms, struct gprs_rlcmac_trx *trx,
974 uint8_t ul_slots, uint8_t dl_slots)
975{
976 if (ms->current_trx) {
977 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
978 ms->reserved_dl_slots);
979 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
980 ms->reserved_ul_slots);
981 ms->reserved_dl_slots = 0;
982 ms->reserved_ul_slots = 0;
983 }
984 ms->current_trx = trx;
985 if (trx) {
986 ms->reserved_dl_slots = dl_slots;
987 ms->reserved_ul_slots = ul_slots;
988 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
989 ms->reserved_dl_slots);
990 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
991 ms->reserved_ul_slots);
992 }
993}
994
995struct gprs_rlcmac_tbf *ms_tbf(const struct GprsMs *ms, enum gprs_rlcmac_tbf_direction dir)
996{
997 switch (dir) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200998 case GPRS_RLCMAC_DL_TBF: return dl_tbf_as_tbf(ms->dl_tbf);
999 case GPRS_RLCMAC_UL_TBF: return ul_tbf_as_tbf(ms->ul_tbf);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +01001000 }
1001
1002 return NULL;
1003}
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001004
Pau Espin Pedrol3547d642022-10-21 15:00:08 +02001005const char *ms_name(const struct GprsMs *ms)
1006{
1007 static char _ms_name_buf[128];
1008 return ms_name_buf(ms, _ms_name_buf, sizeof(_ms_name_buf));
1009}
1010
1011char *ms_name_buf(const struct GprsMs *ms, char *buf, unsigned int buf_size)
1012{
Pau Espin Pedrol94f82582022-11-03 14:16:17 +01001013 struct osmo_strbuf sb = { .buf = buf, .len = buf_size };
1014 uint32_t tlli = ms_tlli(ms);
1015
1016 OSMO_STRBUF_PRINTF(sb, "MS(");
1017 if (ms_imsi_is_valid(ms))
1018 OSMO_STRBUF_PRINTF(sb, "IMSI-%s:", ms_imsi(ms));
1019 if (tlli != GSM_RESERVED_TMSI)
1020 OSMO_STRBUF_PRINTF(sb, "TLLI-0x%08x:", tlli);
1021 OSMO_STRBUF_PRINTF(sb, "TA-%" PRIu8 ":MSCLS-%" PRIu8 "-%" PRIu8,
1022 ms_ta(ms), ms_ms_class(ms), ms_egprs_ms_class(ms));
1023 if (ms->ul_tbf)
1024 OSMO_STRBUF_PRINTF(sb, ":UL");
1025 if (ms->dl_tbf)
1026 OSMO_STRBUF_PRINTF(sb, ":DL");
1027
1028 OSMO_STRBUF_PRINTF(sb, ")");
Pau Espin Pedrol3547d642022-10-21 15:00:08 +02001029 return buf;
1030}
1031
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001032int ms_nacc_start(struct GprsMs *ms, Packet_Cell_Change_Notification_t *notif)
1033{
1034 if (!ms->nacc)
1035 ms->nacc = nacc_fsm_alloc(ms);
1036 if (!ms->nacc)
1037 return -EINVAL;
1038 return osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_RX_CELL_CHG_NOTIFICATION, notif);
1039}
1040
1041bool ms_nacc_rts(const struct GprsMs *ms)
1042{
1043 if (!ms->nacc)
1044 return false;
1045 if (ms->nacc->fi->state == NACC_ST_TX_NEIGHBOUR_DATA ||
1046 ms->nacc->fi->state == NACC_ST_TX_CELL_CHG_CONTINUE)
1047 return true;
1048 return false;
1049}
1050
Pau Espin Pedrol5ba3ef92022-12-12 18:02:25 +01001051struct msgb *ms_nacc_create_rlcmac_msg(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf,
1052 const struct gprs_rlcmac_pdch *pdch, uint32_t fn)
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001053{
1054 int rc;
1055 struct nacc_ev_create_rlcmac_msg_ctx data_ctx;
1056
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001057 data_ctx = (struct nacc_ev_create_rlcmac_msg_ctx) {
1058 .tbf = tbf,
Pau Espin Pedrol5ba3ef92022-12-12 18:02:25 +01001059 .pdch = pdch,
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001060 .fn = fn,
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001061 .msg = NULL,
1062 };
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001063
1064 rc = osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_CREATE_RLCMAC_MSG, &data_ctx);
1065 if (rc != 0 || !data_ctx.msg)
1066 return NULL;
1067 return data_ctx.msg;
1068}
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001069
1070static void ms_start_llc_timer(struct GprsMs *ms)
1071{
1072 if (the_pcu->vty.llc_idle_ack_csec > 0) {
1073 struct timespec tv;
1074 csecs_to_timespec(the_pcu->vty.llc_idle_ack_csec, &tv);
1075 osmo_timer_schedule(&ms->llc_timer, tv.tv_sec, tv.tv_nsec / 1000);
1076 }
1077}
1078
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001079/* Can we get to send a DL TBF ass to the MS? */
1080static bool ms_is_reachable_for_dl_ass(const struct GprsMs *ms)
1081{
1082 struct gprs_rlcmac_ul_tbf *ul_tbf = ms_ul_tbf(ms);
1083
1084 /* This function assumes it is called when no DL TBF is present */
1085 OSMO_ASSERT(!ms_dl_tbf(ms));
1086
1087 /* 3GPP TS 44.060 sec 7.1.3.1 Initiation of the Packet resource request procedure:
1088 * "Furthermore, the mobile station shall not respond to PACKET DOWNLINK ASSIGNMENT
1089 * or MULTIPLE TBF DOWNLINK ASSIGNMENT messages before contention resolution is
1090 * completed on the mobile station side." */
1091 /* The possible uplink TBF is used to trigger downlink assignment:
1092 * - If there is no uplink TBF the MS is potentially in packet idle mode
1093 * and hence assignment will be done over CCCH (PCH)
1094 * - If there's an uplink TBF but it is finished (waiting for last PKT
1095 * CTRL ACK after sending last Pkt UL ACK/NACK with FINAL_ACK=1, then we
1096 * have no ways to contact the MS right now. Assignment will be delayed
1097 * until PKT CTRL ACK is received and the TBF is released at the MS side
1098 * (then assignment goes through PCH).
1099 */
1100 if (!ul_tbf)
1101 return true;
1102 if (ul_tbf_contention_resolution_done(ul_tbf) &&
1103 !tbf_ul_ack_waiting_cnf_final_ack(ul_tbf))
1104 return true;
1105
1106 return false;
1107
1108}
1109
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001110/* Alloc a UL TBF to be assigned over PACCH. Called when an MS requests to
1111 * create a new UL TBF during the end of life of a previous UL TBF (or an SBA).
1112 * In summary, this TBF is allocated as a consequence of receiving a "Pkt
1113 * Resource Req" or "Pkt Ctrl Ack" from the MS.
1114 * See TS 44.060 9.3.2.4.2 "Non-extended uplink TBF mode".
1115 */
1116struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_pacch(struct GprsMs *ms, int8_t use_trx)
1117{
1118 const bool single_slot = false;
1119 struct gprs_rlcmac_ul_tbf *ul_tbf;
1120
Pau Espin Pedrolbda7bb72022-10-31 14:33:09 +01001121 ul_tbf = ul_tbf_alloc(ms->bts, ms, use_trx, single_slot);
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001122 if (!ul_tbf) {
1123 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1124 /* Caller will most probably send a Imm Ass Reject after return */
1125 return NULL;
1126 }
1127 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_PACCH, NULL);
1128 /* Contention resolution is considered to be done since TLLI is known in MS */
1129 return ul_tbf;
1130}
1131
1132/* Alloc a UL TBF to be assigned over AGCH. Used by request of a "One phase
1133 * packet access", where MS requested only 1 PDCH TS (TS 44.018 Table 9.1.8.1). */
1134struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_agch(struct GprsMs *ms)
1135{
1136 const int8_t trx_no = -1;
1137 const bool single_slot = true;
1138 struct gprs_rlcmac_ul_tbf *ul_tbf;
1139
Pau Espin Pedrolbda7bb72022-10-31 14:33:09 +01001140 ul_tbf = ul_tbf_alloc(ms->bts, ms, trx_no, single_slot);
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001141 if (!ul_tbf) {
1142 LOGP(DTBF, LOGL_NOTICE, "No PDCH resource for Uplink TBF\n");
1143 /* Caller will most probably send a Imm Ass Reject after return */
1144 return NULL;
1145 }
1146 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_CCCH, NULL);
1147 return ul_tbf;
1148}
1149
Pau Espin Pedrola621d592022-12-13 17:35:04 +01001150/* Create a temporary dummy TBF to Tx a ImmAssReject if allocating a new one during
1151 * packet resource Request failed. This is similar as ul_tbf_alloc() but without
1152 * calling tbf->setup() (in charge of TFI/USF allocation), and reusing resources
1153 * from Packet Resource Request we received. See TS 44.060 sec 7.1.3.2.1 */
1154struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_rejected_pacch(struct GprsMs *ms, struct gprs_rlcmac_pdch *pdch)
1155{
1156 struct gprs_rlcmac_ul_tbf *ul_tbf;
1157 ul_tbf = ul_tbf_alloc_rejected(ms->bts, ms, pdch);
1158 if (!ul_tbf)
1159 return NULL;
1160 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_PACCH, NULL);
1161 osmo_fsm_inst_dispatch(tbf_ul_ass_fi(ul_tbf_as_tbf(ul_tbf)), TBF_UL_ASS_EV_SCHED_ASS_REJ, NULL);
1162
1163 return ul_tbf;
1164}
1165
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001166/* A new DL-TBF is allocated and assigned through PACCH using "tbf".
1167 * "tbf" may be either a UL-TBF or a DL-TBF.
1168 * Note: This should be called only when MS is reachable, see ms_is_reachable_for_dl_ass().
1169 */
1170int ms_new_dl_tbf_assigned_on_pacch(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001171{
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001172 OSMO_ASSERT(tbf);
1173 const int8_t trx_no = tbf_get_trx(tbf)->trx_no;
1174 const bool single_slot = false;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001175 struct gprs_rlcmac_dl_tbf *dl_tbf;
1176
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001177 dl_tbf = dl_tbf_alloc(ms->bts, ms, trx_no, single_slot);
1178 if (!dl_tbf) {
1179 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1180 return -EBUSY;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001181 }
1182
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001183 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START (PACCH)\n");
1184 dl_tbf_trigger_ass_on_pacch(dl_tbf, tbf);
1185 return 0;
1186}
1187
1188/* A new DL-TBF is allocated and assigned through PCH.
1189 * Note: This should be called only when MS is reachable, see ms_is_reachable_for_dl_ass().
1190 */
1191int ms_new_dl_tbf_assigned_on_pch(struct GprsMs *ms)
1192{
1193 const int8_t trx_no = -1;
1194 const bool single_slot = true;
1195 struct gprs_rlcmac_dl_tbf *dl_tbf;
1196
1197 dl_tbf = dl_tbf_alloc(ms->bts, ms, trx_no, single_slot);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001198 if (!dl_tbf) {
1199 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1200 return -EBUSY;
1201 }
1202
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001203 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START (PCH)\n");
1204 dl_tbf_trigger_ass_on_pch(dl_tbf);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001205 return 0;
1206}
1207
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001208int ms_append_llc_dl_data(struct GprsMs *ms, uint16_t pdu_delay_csec, const uint8_t *data, uint16_t len)
1209{
1210 struct timespec expire_time;
1211 struct gprs_rlcmac_dl_tbf *dl_tbf;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001212 int rc = 0;
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001213
1214 LOGPMS(ms, DTBFDL, LOGL_DEBUG, "appending %u bytes to DL LLC queue\n", len);
1215
1216 struct msgb *llc_msg = msgb_alloc(len, "llc_pdu_queue");
1217 if (!llc_msg)
1218 return -ENOMEM;
1219
1220 llc_queue_calc_pdu_lifetime(ms->bts, pdu_delay_csec, &expire_time);
1221 memcpy(msgb_put(llc_msg, len), data, len);
1222 llc_queue_enqueue(ms_llc_queue(ms), llc_msg, &expire_time);
1223 ms_start_llc_timer(ms);
1224
1225 dl_tbf = ms_dl_tbf(ms);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001226 if (dl_tbf) {
1227 if (tbf_state(dl_tbf_as_tbf_const(dl_tbf)) == TBF_ST_WAIT_RELEASE) {
1228 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "in WAIT RELEASE state (T3193), so reuse TBF\n");
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001229 rc = ms_new_dl_tbf_assigned_on_pacch(ms, dl_tbf_as_tbf(dl_tbf));
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001230 }
1231 } else {
1232 /* Check if we can create a DL TBF to start sending the enqueued
1233 * data. Otherwise it will be triggered later when it is reachable
1234 * again. */
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001235 if (ms_is_reachable_for_dl_ass(ms)) {
1236 if (ms_ul_tbf(ms))
1237 rc = ms_new_dl_tbf_assigned_on_pacch(ms, ul_tbf_as_tbf(ms_ul_tbf(ms)));
1238 else
1239 rc = ms_new_dl_tbf_assigned_on_pch(ms);
1240 }
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001241 }
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001242 return rc;
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001243}