blob: 7ee697e98b94ced9fe4beb648f0ac74e24dffcec [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 Pedrolda971ee2020-12-16 15:59:45 +010027
28#include <time.h>
29
30#include <osmocom/core/talloc.h>
31#include <osmocom/core/utils.h>
32#include <osmocom/core/timer.h>
33#include <osmocom/gsm/protocol/gsm_04_08.h>
34#include <osmocom/gsm/gsm48.h>
35#include <osmocom/core/logging.h>
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +010036#include <osmocom/core/stats.h>
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010037#include "coding_scheme.h"
38
39#define GPRS_CODEL_SLOW_INTERVAL_MS 4000
40
41extern void *tall_pcu_ctx;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +010042static unsigned int next_ms_ctr_group_id;
43
44static const struct rate_ctr_desc ms_ctr_description[] = {
45 [MS_CTR_DL_CTRL_MSG_SCHED] = { "ms:dl_ctrl_msg_sched", "Amount of DL CTRL messages scheduled" },
46};
47
Pau Espin Pedrolf5a251b2021-01-12 20:57:56 +010048static const struct rate_ctr_group_desc ms_ctrg_desc = {
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +010049 .group_name_prefix = "pcu:ms",
50 .group_description = "MS Statistics",
51 .class_id = OSMO_STATS_CLASS_SUBSCRIBER,
52 .num_ctr = ARRAY_SIZE(ms_ctr_description),
53 .ctr_desc = ms_ctr_description,
54};
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010055
56static int64_t now_msec()
57{
58 struct timespec ts;
59 osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
60
61 return (int64_t)(ts.tv_sec) * 1000 + ts.tv_nsec / 1000000;
62}
63
64void gprs_default_cb_ms_idle(struct GprsMs *ms)
65{
66 talloc_free(ms);
67}
68
69void gprs_default_cb_ms_active(struct GprsMs *ms)
70{
71 /* do nothing */
72}
73
74static struct gpr_ms_callback gprs_default_cb = {
75 .ms_idle = gprs_default_cb_ms_idle,
76 .ms_active = gprs_default_cb_ms_active,
77};
78
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +010079static void ms_release_timer_cb(void *data)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010080{
81 struct GprsMs *ms = (struct GprsMs *) data;
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +010082 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Release timer expired\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010083
84 if (ms->timer.data) {
85 ms->timer.data = NULL;
86 ms_unref(ms);
87 }
88}
89
Pau Espin Pedrol14beef62022-10-26 19:44:07 +020090static void ms_llc_timer_cb(void *_ms)
91{
92 struct GprsMs *ms = _ms;
93 struct gprs_rlcmac_dl_tbf *dl_tbf = ms_dl_tbf(ms);
94
95 if (!dl_tbf)
96 return;
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +020097 if (tbf_state(dl_tbf_as_tbf_const(dl_tbf)) != TBF_ST_FLOW)
Pau Espin Pedrol14beef62022-10-26 19:44:07 +020098 return;
99
Pau Espin Pedrolbd1f01f2022-10-27 15:19:39 +0200100 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "LLC receive timeout, requesting DL ACK\n");
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200101
Pau Espin Pedrol8fa3e062022-10-28 17:38:52 +0200102 dl_tbf_request_dl_ack(dl_tbf);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200103}
104
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100105static int ms_talloc_destructor(struct GprsMs *ms);
Pau Espin Pedrolf80cc5b2023-04-17 14:25:51 +0200106struct GprsMs *ms_alloc(struct gprs_rlcmac_bts *bts)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100107{
108 struct GprsMs *ms = talloc_zero(tall_pcu_ctx, struct GprsMs);
Pau Espin Pedrolb4987462022-04-01 17:21:08 +0200109 OSMO_ASSERT(bts);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100110
111 talloc_set_destructor(ms, ms_talloc_destructor);
112
113 ms->bts = bts;
114 ms->cb = gprs_default_cb;
Pau Espin Pedrolf80cc5b2023-04-17 14:25:51 +0200115 ms->tlli = GSM_RESERVED_TMSI;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100116 ms->new_ul_tlli = GSM_RESERVED_TMSI;
117 ms->new_dl_tlli = GSM_RESERVED_TMSI;
118 ms->ta = GSM48_TA_INVALID;
119 ms->current_cs_ul = UNKNOWN;
120 ms->current_cs_dl = UNKNOWN;
121 ms->is_idle = true;
122 INIT_LLIST_HEAD(&ms->list);
123 INIT_LLIST_HEAD(&ms->old_tbfs);
124
125 int codel_interval = LLC_CODEL_USE_DEFAULT;
126
Pau Espin Pedrolf80cc5b2023-04-17 14:25:51 +0200127 LOGP(DRLCMAC, LOGL_INFO, "Creating MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100128
129 ms->imsi[0] = '\0';
Pau Espin Pedrolc7802d92022-05-09 16:07:52 +0200130 osmo_timer_setup(&ms->timer, ms_release_timer_cb, NULL);
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200131 llc_queue_init(&ms->llc_queue, ms);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200132 memset(&ms->llc_timer, 0, sizeof(ms->llc_timer));
133 osmo_timer_setup(&ms->llc_timer, ms_llc_timer_cb, ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100134
135 ms_set_mode(ms, GPRS);
136
Pau Espin Pedrolb4987462022-04-01 17:21:08 +0200137 codel_interval = the_pcu->vty.llc_codel_interval_msec;
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200138 if (codel_interval == LLC_CODEL_USE_DEFAULT)
139 codel_interval = GPRS_CODEL_SLOW_INTERVAL_MS;
140 llc_queue_set_codel_interval(&ms->llc_queue, codel_interval);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100141
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100142 ms->last_cs_not_low = now_msec();
143 ms->app_info_pending = false;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100144
145 ms->ctrs = rate_ctr_group_alloc(ms, &ms_ctrg_desc, next_ms_ctr_group_id++);
146 if (!ms->ctrs)
147 goto free_ret;
148
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100149 return ms;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100150free_ret:
151 talloc_free(ms);
152 return NULL;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100153}
154
155static int ms_talloc_destructor(struct GprsMs *ms)
156{
157 struct llist_item *pos, *tmp;
158
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100159 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Destroying MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100160
161 ms_set_reserved_slots(ms, NULL, 0, 0);
162
Vadim Yanitskiye33c2362022-07-22 03:44:44 +0700163 osmo_timer_del(&ms->timer);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100164
165 if (ms->ul_tbf) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200166 tbf_set_ms(ul_tbf_as_tbf(ms->ul_tbf), NULL);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100167 ms->ul_tbf = NULL;
168 }
169
170 if (ms->dl_tbf) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200171 tbf_set_ms(dl_tbf_as_tbf(ms->dl_tbf), NULL);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100172 ms->dl_tbf = NULL;
173 }
174
175 llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
176 struct gprs_rlcmac_tbf *tbf = (struct gprs_rlcmac_tbf *)pos->entry;
177 tbf_set_ms(tbf, NULL);
178 }
179
180 llc_queue_clear(&ms->llc_queue, ms->bts);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200181 osmo_timer_del(&ms->llc_timer);
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100182
183 if (ms->ctrs)
184 rate_ctr_group_free(ms->ctrs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100185 return 0;
186}
187
188
189void ms_set_callback(struct GprsMs *ms, struct gpr_ms_callback *cb)
190{
191 if (cb)
192 ms->cb = *cb;
193 else
194 ms->cb = gprs_default_cb;
195}
196
197static void ms_update_status(struct GprsMs *ms)
198{
199 if (ms->ref > 0)
200 return;
201
202 if (ms_is_idle(ms) && !ms->is_idle) {
203 ms->is_idle = true;
204 ms->cb.ms_idle(ms);
205 /* this can be deleted by now, do not access it */
206 return;
207 }
208
209 if (!ms_is_idle(ms) && ms->is_idle) {
210 ms->is_idle = false;
211 ms->cb.ms_active(ms);
212 }
213}
214
215struct GprsMs *ms_ref(struct GprsMs *ms)
216{
217 ms->ref += 1;
218 return ms;
219}
220
221void ms_unref(struct GprsMs *ms)
222{
223 OSMO_ASSERT(ms->ref >= 0);
224 ms->ref -= 1;
225 if (ms->ref == 0)
226 ms_update_status(ms);
227}
228
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100229static void ms_release_timer_start(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100230{
Pau Espin Pedrol14379ef2023-04-17 20:43:26 +0200231 /* Immediate free():
232 * Skip delaying free() through release timer if delay is configured to be 0.
233 * This is useful for synced freed during unit tests.
234 */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100235 if (ms->delay == 0)
236 return;
237
Pau Espin Pedrol14379ef2023-04-17 20:43:26 +0200238 /* Immediate free():
239 * Skip delaying free() through release timer if TMSI is not
240 * known, since those cannot really be reused.
241 */
242 if (ms_tlli(ms) == GSM_RESERVED_TMSI)
243 return;
244
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100245 LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Schedule MS release in %u secs\n", ms->delay);
246
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100247 if (!ms->timer.data)
248 ms->timer.data = ms_ref(ms);
249
250 osmo_timer_schedule(&ms->timer, ms->delay, 0);
251}
252
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100253static void ms_release_timer_stop(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100254{
255 if (!ms->timer.data)
256 return;
257
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100258 LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Cancel scheduled MS release\n");
259
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100260 osmo_timer_del(&ms->timer);
261 ms->timer.data = NULL;
262 ms_unref(ms);
263}
264
265void ms_set_mode(struct GprsMs *ms, enum mcs_kind mode)
266{
267 ms->mode = mode;
268
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100269 switch (ms->mode) {
270 case GPRS:
271 if (!mcs_is_gprs(ms->current_cs_ul)) {
272 ms->current_cs_ul = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100273 ms->bts->initial_cs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100274 if (!mcs_is_valid(ms->current_cs_ul))
275 ms->current_cs_ul = CS1;
276 }
277 if (!mcs_is_gprs(ms->current_cs_dl)) {
278 ms->current_cs_dl = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100279 ms->bts->initial_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100280 if (!mcs_is_valid(ms->current_cs_dl))
281 ms->current_cs_dl = CS1;
282 }
283 break;
284
285 case EGPRS_GMSK:
Pau Espin Pedrol7bb8cd62021-01-25 15:08:35 +0100286 if (!mcs_is_edge_gmsk(ms->current_cs_ul)) {
287 ms->current_cs_ul = mcs_get_egprs_by_num(
288 ms->bts->initial_mcs_ul);
289 if (!mcs_is_valid(ms->current_cs_ul))
290 ms->current_cs_ul = MCS1;
291 }
292 if (!mcs_is_edge_gmsk(ms->current_cs_dl)) {
293 ms->current_cs_dl = mcs_get_egprs_by_num(
294 ms->bts->initial_mcs_dl);
295 if (!mcs_is_valid(ms->current_cs_dl))
296 ms->current_cs_dl = MCS1;
297 }
298 break;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100299 case EGPRS:
300 if (!mcs_is_edge(ms->current_cs_ul)) {
301 ms->current_cs_ul = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100302 ms->bts->initial_mcs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100303 if (!mcs_is_valid(ms->current_cs_ul))
304 ms->current_cs_ul = MCS1;
305 }
306 if (!mcs_is_edge(ms->current_cs_dl)) {
307 ms->current_cs_dl = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100308 ms->bts->initial_mcs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100309 if (!mcs_is_valid(ms->current_cs_dl))
310 ms->current_cs_dl = MCS1;
311 }
312 break;
313 }
314}
315
316static void ms_attach_ul_tbf(struct GprsMs *ms, struct gprs_rlcmac_ul_tbf *tbf)
317{
318 if (ms->ul_tbf == tbf)
319 return;
320
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100321 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 +0100322
323 ms_ref(ms);
324
325 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200326 llist_add_tail(tbf_ms_list(ul_tbf_as_tbf(ms->ul_tbf)), &ms->old_tbfs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100327
328 ms->ul_tbf = tbf;
329
330 if (tbf)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100331 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100332
333 ms_unref(ms);
334}
335
336static void ms_attach_dl_tbf(struct GprsMs *ms, struct gprs_rlcmac_dl_tbf *tbf)
337{
338 if (ms->dl_tbf == tbf)
339 return;
340
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100341 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 +0100342
343 ms_ref(ms);
344
345 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200346 llist_add_tail(tbf_ms_list(dl_tbf_as_tbf(ms->dl_tbf)), &ms->old_tbfs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100347
348 ms->dl_tbf = tbf;
349
350 if (tbf)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100351 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100352
353 ms_unref(ms);
354}
355
356void ms_attach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
357{
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200358 if (tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF)
Pau Espin Pedrolcc30b052022-10-27 15:25:55 +0200359 ms_attach_dl_tbf(ms, tbf_as_dl_tbf(tbf));
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200360 else
Pau Espin Pedrolcc30b052022-10-27 15:25:55 +0200361 ms_attach_ul_tbf(ms, tbf_as_ul_tbf(tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100362}
363
364void ms_detach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
365{
366 if (tbf == (struct gprs_rlcmac_tbf *)(ms->ul_tbf)) {
367 ms->ul_tbf = NULL;
368 } else if (tbf == (struct gprs_rlcmac_tbf *)(ms->dl_tbf)) {
369 ms->dl_tbf = NULL;
370 } else {
371 bool found = false;
372
373 struct llist_item *pos, *tmp;
374 llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
375 struct gprs_rlcmac_tbf *tmp_tbf = (struct gprs_rlcmac_tbf *)pos->entry;
376 if (tmp_tbf == tbf) {
377 llist_del(&pos->list);
378 found = true;
379 break;
380 }
381 }
382
383 /* Protect against recursive calls via set_ms() */
384 if (!found)
385 return;
386 }
387
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100388 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Detaching TBF: %s\n",
389 tbf_name(tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100390
391 if (tbf_ms(tbf) == ms)
392 tbf_set_ms(tbf, NULL);
393
394 if (!ms->dl_tbf && !ms->ul_tbf) {
395 ms_set_reserved_slots(ms, NULL, 0, 0);
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100396 ms->first_common_ts = NULL;
Pau Espin Pedrol14379ef2023-04-17 20:43:26 +0200397 ms_release_timer_start(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100398 }
399
400 ms_update_status(ms);
401}
402
403void ms_reset(struct GprsMs *ms)
404{
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100405 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Clearing MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100406
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100407 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100408
409 ms->tlli = GSM_RESERVED_TMSI;
410 ms->new_dl_tlli = ms->tlli;
411 ms->new_ul_tlli = ms->tlli;
412 ms->imsi[0] = '\0';
413}
414
Pau Espin Pedrol8abe13c2022-10-21 18:49:48 +0200415/* This function should be called on the MS object of a TBF each time an RLCMAC
416 * block is received for it with TLLI information.
417 * Besides updating the TLLI field on the MS object, it also seeks for other MS
418 * objects in the store and merges them into the current MS object. The MS
419 * duplication happened because we don't learn the TLLI of the created TBF until
420 * a later point. */
421void ms_update_announced_tlli(struct GprsMs *ms, uint32_t tlli)
422{
423 struct GprsMs *old_ms = NULL;
424
425 if (tlli == GSM_RESERVED_TMSI)
426 return;
427
428 /* When the TLLI does not match the ms, check if there is another
429 * MS object that belongs to that TLLI and if yes make sure one of them
430 * gets deleted. */
431 if (!ms_check_tlli(ms, tlli))
Pau Espin Pedroleb0a0522023-04-17 16:33:35 +0200432 old_ms = bts_get_ms_by_tlli(ms->bts, tlli, GSM_RESERVED_TMSI);
Pau Espin Pedrol8abe13c2022-10-21 18:49:48 +0200433
434 ms_set_tlli(ms, tlli);
435
436 if (old_ms)
437 ms_merge_and_clear_ms(ms, old_ms);
438 /* old_ms may no longer be available here */
439}
440
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100441/* Merge 'old_ms' object into 'ms' object.
442 * 'old_ms' may be freed during the call to this function, don't use the pointer to it afterwards */
443void ms_merge_and_clear_ms(struct GprsMs *ms, struct GprsMs *old_ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100444{
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100445 char old_ms_name[128];
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100446 OSMO_ASSERT(old_ms != ms);
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100447 ms_ref(old_ms);
448
449 ms_name_buf(old_ms, old_ms_name, sizeof(old_ms_name));
450
451 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Merge MS: %s\n", old_ms_name);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100452
453 if (strlen(ms_imsi(ms)) == 0 && strlen(ms_imsi(old_ms)) != 0)
454 osmo_strlcpy(ms->imsi, ms_imsi(old_ms), sizeof(ms->imsi));
455
456 if (!ms_ms_class(ms) && ms_ms_class(old_ms))
457 ms_set_ms_class(ms, ms_ms_class(old_ms));
458
459 if (!ms_egprs_ms_class(ms) && ms_egprs_ms_class(old_ms))
460 ms_set_egprs_ms_class(ms, ms_egprs_ms_class(old_ms));
461
462 llc_queue_move_and_merge(&ms->llc_queue, &old_ms->llc_queue);
463
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100464 /* Clean up the old MS object */
465 /* TODO: Use timer? */
466 if (ms_ul_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms), T_MAX))
467 tbf_free((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms));
468 if (ms_dl_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms), T_MAX))
469 tbf_free((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms));
470
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100471 ms_reset(old_ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100472
473 ms_unref(old_ms);
474}
475
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100476/* Apply changes to the TLLI directly, used interally by functions below: */
477static void ms_apply_tlli_change(struct GprsMs *ms, uint32_t tlli)
478{
479 ms->tlli = tlli;
480 ms->new_dl_tlli = GSM_RESERVED_TMSI;
481 ms->new_ul_tlli = GSM_RESERVED_TMSI;
482
483 /* Update TBF FSM names: */
484 if (ms->ul_tbf)
485 tbf_update_state_fsm_name(ul_tbf_as_tbf(ms->ul_tbf));
486 if (ms->dl_tbf)
487 tbf_update_state_fsm_name(dl_tbf_as_tbf(ms->dl_tbf));
488}
489
Pau Espin Pedrol0b5997e2022-10-21 14:10:41 +0200490/* Set/update the MS object TLLI based on knowledge gained from the MS side (Uplink direction) */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100491void ms_set_tlli(struct GprsMs *ms, uint32_t tlli)
492{
493 if (tlli == ms->tlli || tlli == ms->new_ul_tlli)
494 return;
495
496 if (tlli != ms->new_dl_tlli) {
497 LOGP(DRLCMAC, LOGL_INFO,
498 "Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
499 "not yet confirmed\n",
500 ms_tlli(ms), tlli);
501 ms->new_ul_tlli = tlli;
502 return;
503 }
504
505 LOGP(DRLCMAC, LOGL_INFO,
506 "Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
507 "already confirmed partly\n",
508 ms->tlli, tlli);
509
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100510 ms_apply_tlli_change(ms, tlli);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100511}
512
Pau Espin Pedrol0b5997e2022-10-21 14:10:41 +0200513/* Set/update the MS object TLLI based on knowledge gained from the SGSN side (Downlink direction) */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100514bool ms_confirm_tlli(struct GprsMs *ms, uint32_t tlli)
515{
516 if (tlli == ms->tlli || tlli == ms->new_dl_tlli)
517 return false;
518
519 if (tlli != ms->new_ul_tlli) {
520 /* The MS has not sent a message with the new TLLI, which may
521 * happen according to the spec [TODO: add reference]. */
522
523 LOGP(DRLCMAC, LOGL_INFO,
524 "The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
525 "partly confirmed\n", tlli);
526 /* Use the network's idea of TLLI as candidate, this does not
527 * change the result value of tlli() */
528 ms->new_dl_tlli = tlli;
529 return false;
530 }
531
532 LOGP(DRLCMAC, LOGL_INFO,
533 "Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
534
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100535 ms_apply_tlli_change(ms, tlli);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100536
537 return true;
538}
539
540void ms_set_imsi(struct GprsMs *ms, const char *imsi)
541{
542 if (!imsi) {
543 LOGP(DRLCMAC, LOGL_ERROR, "Expected IMSI!\n");
544 return;
545 }
546
547 if (imsi[0] && strlen(imsi) < 3) {
548 LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI '%s'!\n",
549 imsi);
550 return;
551 }
552
553 if (strcmp(imsi, ms->imsi) == 0)
554 return;
555
556 LOGP(DRLCMAC, LOGL_INFO,
557 "Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
558 ms_tlli(ms), ms->imsi, imsi);
559
Pau Espin Pedrolcde18c52023-04-17 18:16:48 +0200560 struct GprsMs *old_ms = bts_get_ms_by_imsi(ms->bts, imsi);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100561 /* Check if we are going to store a different MS object with already
562 existing IMSI. This is probably a bug in code calling this function,
563 since it should take care of this explicitly */
564 if (old_ms) {
565 /* We cannot find ms->ms by IMSI since we know that it has a
566 * different IMSI */
567 OSMO_ASSERT(old_ms != ms);
568
569 LOGPMS(ms, DRLCMAC, LOGL_NOTICE,
570 "IMSI '%s' was already assigned to another "
571 "MS object: TLLI = 0x%08x, that IMSI will be removed\n",
572 imsi, ms_tlli(old_ms));
573
574 ms_merge_and_clear_ms(ms, old_ms);
Pau Espin Pedrolb8ff74b2022-10-31 12:58:46 +0100575 /* old_ms may no longer be available here */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100576 }
577
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100578 /* Store the new IMSI: */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100579 osmo_strlcpy(ms->imsi, imsi, sizeof(ms->imsi));
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100580
581 /* Update TBF FSM names: */
582 if (ms->ul_tbf)
583 tbf_update_state_fsm_name(ul_tbf_as_tbf(ms->ul_tbf));
584 if (ms->dl_tbf)
585 tbf_update_state_fsm_name(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100586}
587
588void ms_set_ta(struct GprsMs *ms, uint8_t ta_)
589{
590 if (ta_ == ms->ta)
591 return;
592
593 if (gsm48_ta_is_valid(ta_)) {
594 LOGP(DRLCMAC, LOGL_INFO,
595 "Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
596 ms_tlli(ms), ms->ta, ta_);
597 ms->ta = ta_;
598 } else
599 LOGP(DRLCMAC, LOGL_NOTICE,
600 "MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
601 "value %d kept)\n", ms_tlli(ms), ta_, ms->ta);
602}
603
604void ms_set_ms_class(struct GprsMs *ms, uint8_t ms_class_)
605{
606 if (ms_class_ == ms->ms_class)
607 return;
608
609 LOGP(DRLCMAC, LOGL_INFO,
610 "Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
611 ms_tlli(ms), ms->ms_class, ms_class_);
612
613 ms->ms_class = ms_class_;
614}
615
616void ms_set_egprs_ms_class(struct GprsMs *ms, uint8_t ms_class_)
617{
618 if (ms_class_ == ms->egprs_ms_class)
619 return;
620
621 LOGP(DRLCMAC, LOGL_INFO,
622 "Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
623 ms_tlli(ms), ms->egprs_ms_class, ms_class_);
624
625 ms->egprs_ms_class = ms_class_;
626
627 if (!bts_max_mcs_ul(ms->bts) || !bts_max_mcs_dl(ms->bts)) {
628 LOGPMS(ms, DRLCMAC, LOGL_DEBUG,
629 "Avoid enabling EGPRS because use of MCS is disabled: ul=%u dl=%u\n",
630 bts_max_mcs_ul(ms->bts), bts_max_mcs_dl(ms->bts));
631 return;
632 }
633
634 if (mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts))) &&
635 mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts))) &&
636 ms_mode(ms) != EGPRS)
637 {
638 ms_set_mode(ms, EGPRS_GMSK);
639 } else {
640 ms_set_mode(ms, EGPRS);
641 }
642 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Enabled EGPRS, mode %s\n", mode_name(ms_mode(ms)));
643}
644
645void ms_update_error_rate(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, int error_rate)
646{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100647 int64_t now;
648 enum CodingScheme max_cs_dl = ms_max_cs_dl(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100649 OSMO_ASSERT(max_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100650
651 if (error_rate < 0)
652 return;
653
654 now = now_msec();
655
656 /* TODO: Check for TBF direction */
657 /* TODO: Support different CS values for UL and DL */
658
659 ms->nack_rate_dl = error_rate;
660
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100661 if (error_rate > the_pcu->vty.cs_adj_upper_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100662 if (mcs_chan_code(ms->current_cs_dl) > 0) {
663 mcs_dec_kind(&ms->current_cs_dl, ms_mode(ms));
664 LOGP(DRLCMACDL, LOGL_INFO,
665 "MS (IMSI %s): High error rate %d%%, "
666 "reducing CS level to %s\n",
667 ms_imsi(ms), error_rate, mcs_name(ms->current_cs_dl));
668 ms->last_cs_not_low = now;
669 }
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100670 } else if (error_rate < the_pcu->vty.cs_adj_lower_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100671 if (ms->current_cs_dl < max_cs_dl) {
672 if (now - ms->last_cs_not_low > 1000) {
673 mcs_inc_kind(&ms->current_cs_dl, ms_mode(ms));
674
675 LOGP(DRLCMACDL, LOGL_INFO,
676 "MS (IMSI %s): Low error rate %d%%, "
677 "increasing DL CS level to %s\n",
678 ms_imsi(ms), error_rate,
679 mcs_name(ms->current_cs_dl));
680 ms->last_cs_not_low = now;
681 } else {
682 LOGP(DRLCMACDL, LOGL_DEBUG,
683 "MS (IMSI %s): Low error rate %d%%, "
684 "ignored (within blocking period)\n",
685 ms_imsi(ms), error_rate);
686 }
687 }
688 } else {
689 LOGP(DRLCMACDL, LOGL_DEBUG,
690 "MS (IMSI %s): Medium error rate %d%%, ignored\n",
691 ms_imsi(ms), error_rate);
692 ms->last_cs_not_low = now;
693 }
694}
695
696enum CodingScheme ms_max_cs_ul(const struct GprsMs *ms)
697{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100698 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100699 OSMO_ASSERT(ms->bts != NULL);
700
701 if (mcs_is_gprs(ms->current_cs_ul)) {
702 if (!bts_max_cs_ul(ms->bts)) {
703 return CS4;
704 }
705
706 return mcs_get_gprs_by_num(bts_max_cs_ul(ms->bts));
707 }
708
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100709 cs = mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts));
710 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
711 cs = MCS4;
712 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100713}
714
715void ms_set_current_cs_dl(struct GprsMs *ms, enum CodingScheme scheme)
716{
717 ms->current_cs_dl = scheme;
718}
719
720enum CodingScheme ms_max_cs_dl(const struct GprsMs *ms)
721{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100722 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100723 OSMO_ASSERT(ms->bts != NULL);
724
725 if (mcs_is_gprs(ms->current_cs_dl)) {
726 if (!bts_max_cs_dl(ms->bts)) {
727 return CS4;
728 }
729
730 return mcs_get_gprs_by_num(bts_max_cs_dl(ms->bts));
731 }
732
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100733 cs = mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts));
734 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
735 cs = MCS4;
736 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100737}
738
739void ms_update_cs_ul(struct GprsMs *ms, const struct pcu_l1_meas *meas)
740{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100741 enum CodingScheme max_cs_ul = ms_max_cs_ul(ms);
742
743 int old_link_qual;
744 int low;
745 int high;
746 enum CodingScheme new_cs_ul = ms->current_cs_ul;
747 uint8_t current_cs = mcs_chan_code(ms->current_cs_ul);
748
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100749 if (!max_cs_ul) {
750 LOGP(DRLCMACMEAS, LOGL_ERROR,
751 "max_cs_ul cannot be derived (current UL CS: %s)\n",
752 mcs_name(ms->current_cs_ul));
753 return;
754 }
755
756 if (!ms->current_cs_ul) {
757 LOGP(DRLCMACMEAS, LOGL_ERROR,
758 "Unable to update UL (M)CS because it's not set: %s\n",
759 mcs_name(ms->current_cs_ul));
760 return;
761 }
762
763 if (!meas->have_link_qual) {
764 LOGP(DRLCMACMEAS, LOGL_ERROR,
765 "Unable to update UL (M)CS %s because we don't have link quality measurements.\n",
766 mcs_name(ms->current_cs_ul));
767 return;
768 }
769
770 if (mcs_is_gprs(ms->current_cs_ul)) {
771 if (current_cs >= MAX_GPRS_CS)
772 current_cs = MAX_GPRS_CS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100773 low = the_pcu->vty.cs_lqual_ranges[current_cs].low;
774 high = the_pcu->vty.cs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100775 } else if (mcs_is_edge(ms->current_cs_ul)) {
776 if (current_cs >= MAX_EDGE_MCS)
777 current_cs = MAX_EDGE_MCS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100778 low = the_pcu->vty.mcs_lqual_ranges[current_cs].low;
779 high = the_pcu->vty.mcs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100780 } else {
781 LOGP(DRLCMACMEAS, LOGL_ERROR,
782 "Unable to update UL (M)CS because it's neither GPRS nor EDGE: %s\n",
783 mcs_name(ms->current_cs_ul));
784 return;
785 }
786
787 /* To avoid rapid changes of the coding scheme, we also take
788 * the old link quality value into account (if present). */
789 if (ms->l1_meas.have_link_qual)
790 old_link_qual = ms->l1_meas.link_qual;
791 else
792 old_link_qual = meas->link_qual;
793
794 if (meas->link_qual < low && old_link_qual < low)
795 mcs_dec_kind(&new_cs_ul, ms_mode(ms));
796 else if (meas->link_qual > high && old_link_qual > high &&
797 ms->current_cs_ul < max_cs_ul)
798 mcs_inc_kind(&new_cs_ul, ms_mode(ms));
799
800 if (ms->current_cs_ul != new_cs_ul) {
801 LOGPMS(ms, DRLCMACMEAS, LOGL_INFO,
802 "Link quality %ddB (old %ddB) left window [%d, %d], "
803 "modifying uplink CS level: %s -> %s\n",
804 meas->link_qual, old_link_qual,
805 low, high,
806 mcs_name(ms->current_cs_ul), mcs_name(new_cs_ul));
807
808 ms->current_cs_ul = new_cs_ul;
809 }
810}
811
812void ms_update_l1_meas(struct GprsMs *ms, const struct pcu_l1_meas *meas)
813{
814 unsigned i;
815
816 ms_update_cs_ul(ms, meas);
817
818 if (meas->have_rssi)
819 pcu_l1_meas_set_rssi(&ms->l1_meas, meas->rssi);
820 if (meas->have_bto)
821 pcu_l1_meas_set_bto(&ms->l1_meas, meas->bto);
822 if (meas->have_ber)
823 pcu_l1_meas_set_ber(&ms->l1_meas, meas->ber);
824 if (meas->have_link_qual)
825 pcu_l1_meas_set_link_qual(&ms->l1_meas, meas->link_qual);
826
827 if (meas->have_ms_rx_qual)
828 pcu_l1_meas_set_ms_rx_qual(&ms->l1_meas, meas->ms_rx_qual);
829 if (meas->have_ms_c_value)
830 pcu_l1_meas_set_ms_c_value(&ms->l1_meas, meas->ms_c_value);
831 if (meas->have_ms_sign_var)
832 pcu_l1_meas_set_ms_sign_var(&ms->l1_meas, meas->ms_sign_var);
833
834 if (meas->have_ms_i_level) {
835 for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
836 if (meas->ts[i].have_ms_i_level)
837 pcu_l1_meas_set_ms_i_level(&ms->l1_meas, i, meas->ts[i].ms_i_level);
838 else
839 ms->l1_meas.ts[i].have_ms_i_level = 0;
840 }
841 }
842}
843
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100844/* req_mcs_kind acts as a set filter, where EGPRS means any and GPRS is the most restrictive */
845enum CodingScheme ms_current_cs_dl(const struct GprsMs *ms, enum mcs_kind req_mcs_kind)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100846{
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100847 enum CodingScheme orig_cs = ms->current_cs_dl;
848 struct gprs_rlcmac_bts *bts = ms->bts;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100849 size_t unencoded_octets;
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100850 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100851
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100852 /* It could be that a TBF requests a GPRS CS despite the MS currently
853 being upgraded to EGPRS (hence reporting MCS). That could happen
854 because the TBF was created early in the process where we didn't have
855 yet enough information about the MS, and only AFTER it was created we
856 upgraded the MS to be EGPRS capable.
857 As a result, when the MS is queried for the target CS here, we could be
858 returning an MCS despite the current TBF being established as GPRS,
859 but we rather stick to the TBF type we assigned to the MS rather than
860 magically sending EGPRS data blocks to a GPRS TBF.
861 It could also be that the caller requests specific MCS kind
862 explicitly too due to scheduling restrictions (GPRS+EGPRS multiplexing). */
863 if (req_mcs_kind == EGPRS_GMSK && mcs_is_edge(orig_cs) && orig_cs > MCS4) {
864 cs = bts_cs_dl_is_supported(bts, MCS4) ? MCS4 :
865 bts_cs_dl_is_supported(bts, MCS3) ? MCS3 :
866 bts_cs_dl_is_supported(bts, MCS2) ? MCS2 :
867 MCS1;
868 } else if (req_mcs_kind == GPRS && mcs_is_edge(orig_cs)) { /* GPRS */
869 int i;
870 cs = orig_cs > MCS4 ? MCS4 : orig_cs;
871 cs -= (MCS1 - CS1); /* MCSx -> CSx */
872 /* Find suitable CS starting from equivalent MCS which is supported by BTS: */
873 for (i = mcs_chan_code(cs); !bts_cs_dl_is_supported(bts, CS1 + i); i--);
874 OSMO_ASSERT(i >= 0 && i <= 3); /* CS1 is always supported */
875 cs = CS1 + i;
876 } else {
877 cs = orig_cs;
878 }
879
880 if (orig_cs != cs)
881 LOGPMS(ms, DRLCMACDL, LOGL_INFO, "MS (mode=%s) suggests transmitting "
882 "DL %s, downgrade to %s in order to match TBF & scheduler requirements\n",
883 mode_name(ms_mode(ms)), mcs_name(orig_cs), mcs_name(cs));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100884
885 unencoded_octets = llc_queue_octets(&ms->llc_queue);
886
887 /* If the DL TBF is active, add number of unencoded chunk octets */
888 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200889 unencoded_octets += llc_chunk_size(tbf_llc(dl_tbf_as_tbf(ms->dl_tbf)));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100890
891 /* There are many unencoded octets, don't reduce */
Pau Espin Pedrolad79b852021-01-14 13:20:55 +0100892 if (unencoded_octets >= the_pcu->vty.cs_downgrade_threshold)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100893 return cs;
894
895 /* RF conditions are good, don't reduce */
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100896 if (ms->nack_rate_dl < the_pcu->vty.cs_adj_lower_limit)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100897 return cs;
898
899 /* The throughput would probably be better if the CS level was reduced */
900 mcs_dec_kind(&cs, ms_mode(ms));
901
902 /* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
903 if (cs == CS2)
904 mcs_dec_kind(&cs, ms_mode(ms));
905
906 return cs;
907}
908
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100909struct gprs_rlcmac_pdch *ms_first_common_ts(const struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100910{
Pau Espin Pedrol345d9ad2022-12-12 19:22:44 +0100911 return ms->first_common_ts;
912}
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100913
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100914void ms_set_first_common_ts(struct GprsMs *ms, struct gprs_rlcmac_pdch *pdch)
Pau Espin Pedrol345d9ad2022-12-12 19:22:44 +0100915{
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100916 OSMO_ASSERT(pdch);
917 ms->first_common_ts = pdch;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100918}
919
920uint8_t ms_dl_slots(const struct GprsMs *ms)
921{
922 uint8_t slots = 0;
923
924 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200925 slots |= tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100926
927 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200928 slots |= tbf_dl_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100929
930 return slots;
931}
932
933uint8_t ms_ul_slots(const struct GprsMs *ms)
934{
935 uint8_t slots = 0;
936
937 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200938 slots |= tbf_ul_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100939
940 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200941 slots |= tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100942
943 return slots;
944}
945
946uint8_t ms_current_pacch_slots(const struct GprsMs *ms)
947{
948 uint8_t slots = 0;
949
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200950 bool is_dl_active = ms->dl_tbf && tbf_is_tfi_assigned(dl_tbf_as_tbf(ms->dl_tbf));
951 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 +0100952
953 if (!is_dl_active && !is_ul_active)
954 return 0;
955
956 /* see TS 44.060, 8.1.1.2.2 */
957 if (is_dl_active && !is_ul_active)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200958 slots = tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100959 else if (!is_dl_active && is_ul_active)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200960 slots = tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100961 else
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200962 slots = tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf)) &
963 tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100964
965 /* Assume a multislot class 1 device */
966 /* TODO: For class 2 devices, this could be removed */
967 slots = pcu_lsb(slots);
968
969 return slots;
970}
971
972void ms_set_reserved_slots(struct GprsMs *ms, struct gprs_rlcmac_trx *trx,
973 uint8_t ul_slots, uint8_t dl_slots)
974{
975 if (ms->current_trx) {
976 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
977 ms->reserved_dl_slots);
978 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
979 ms->reserved_ul_slots);
980 ms->reserved_dl_slots = 0;
981 ms->reserved_ul_slots = 0;
982 }
983 ms->current_trx = trx;
984 if (trx) {
985 ms->reserved_dl_slots = dl_slots;
986 ms->reserved_ul_slots = ul_slots;
987 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
988 ms->reserved_dl_slots);
989 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
990 ms->reserved_ul_slots);
991 }
992}
993
994struct gprs_rlcmac_tbf *ms_tbf(const struct GprsMs *ms, enum gprs_rlcmac_tbf_direction dir)
995{
996 switch (dir) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200997 case GPRS_RLCMAC_DL_TBF: return dl_tbf_as_tbf(ms->dl_tbf);
998 case GPRS_RLCMAC_UL_TBF: return ul_tbf_as_tbf(ms->ul_tbf);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100999 }
1000
1001 return NULL;
1002}
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001003
Pau Espin Pedrol3547d642022-10-21 15:00:08 +02001004const char *ms_name(const struct GprsMs *ms)
1005{
1006 static char _ms_name_buf[128];
1007 return ms_name_buf(ms, _ms_name_buf, sizeof(_ms_name_buf));
1008}
1009
1010char *ms_name_buf(const struct GprsMs *ms, char *buf, unsigned int buf_size)
1011{
Pau Espin Pedrol94f82582022-11-03 14:16:17 +01001012 struct osmo_strbuf sb = { .buf = buf, .len = buf_size };
1013 uint32_t tlli = ms_tlli(ms);
1014
1015 OSMO_STRBUF_PRINTF(sb, "MS(");
1016 if (ms_imsi_is_valid(ms))
1017 OSMO_STRBUF_PRINTF(sb, "IMSI-%s:", ms_imsi(ms));
1018 if (tlli != GSM_RESERVED_TMSI)
1019 OSMO_STRBUF_PRINTF(sb, "TLLI-0x%08x:", tlli);
1020 OSMO_STRBUF_PRINTF(sb, "TA-%" PRIu8 ":MSCLS-%" PRIu8 "-%" PRIu8,
1021 ms_ta(ms), ms_ms_class(ms), ms_egprs_ms_class(ms));
1022 if (ms->ul_tbf)
1023 OSMO_STRBUF_PRINTF(sb, ":UL");
1024 if (ms->dl_tbf)
1025 OSMO_STRBUF_PRINTF(sb, ":DL");
1026
1027 OSMO_STRBUF_PRINTF(sb, ")");
Pau Espin Pedrol3547d642022-10-21 15:00:08 +02001028 return buf;
1029}
1030
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001031int ms_nacc_start(struct GprsMs *ms, Packet_Cell_Change_Notification_t *notif)
1032{
1033 if (!ms->nacc)
1034 ms->nacc = nacc_fsm_alloc(ms);
1035 if (!ms->nacc)
1036 return -EINVAL;
1037 return osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_RX_CELL_CHG_NOTIFICATION, notif);
1038}
1039
1040bool ms_nacc_rts(const struct GprsMs *ms)
1041{
1042 if (!ms->nacc)
1043 return false;
1044 if (ms->nacc->fi->state == NACC_ST_TX_NEIGHBOUR_DATA ||
1045 ms->nacc->fi->state == NACC_ST_TX_CELL_CHG_CONTINUE)
1046 return true;
1047 return false;
1048}
1049
Pau Espin Pedrol5ba3ef92022-12-12 18:02:25 +01001050struct msgb *ms_nacc_create_rlcmac_msg(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf,
1051 const struct gprs_rlcmac_pdch *pdch, uint32_t fn)
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001052{
1053 int rc;
1054 struct nacc_ev_create_rlcmac_msg_ctx data_ctx;
1055
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001056 data_ctx = (struct nacc_ev_create_rlcmac_msg_ctx) {
1057 .tbf = tbf,
Pau Espin Pedrol5ba3ef92022-12-12 18:02:25 +01001058 .pdch = pdch,
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001059 .fn = fn,
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001060 .msg = NULL,
1061 };
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001062
1063 rc = osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_CREATE_RLCMAC_MSG, &data_ctx);
1064 if (rc != 0 || !data_ctx.msg)
1065 return NULL;
1066 return data_ctx.msg;
1067}
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001068
1069static void ms_start_llc_timer(struct GprsMs *ms)
1070{
1071 if (the_pcu->vty.llc_idle_ack_csec > 0) {
1072 struct timespec tv;
1073 csecs_to_timespec(the_pcu->vty.llc_idle_ack_csec, &tv);
1074 osmo_timer_schedule(&ms->llc_timer, tv.tv_sec, tv.tv_nsec / 1000);
1075 }
1076}
1077
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001078/* Can we get to send a DL TBF ass to the MS? */
1079static bool ms_is_reachable_for_dl_ass(const struct GprsMs *ms)
1080{
1081 struct gprs_rlcmac_ul_tbf *ul_tbf = ms_ul_tbf(ms);
1082
1083 /* This function assumes it is called when no DL TBF is present */
1084 OSMO_ASSERT(!ms_dl_tbf(ms));
1085
1086 /* 3GPP TS 44.060 sec 7.1.3.1 Initiation of the Packet resource request procedure:
1087 * "Furthermore, the mobile station shall not respond to PACKET DOWNLINK ASSIGNMENT
1088 * or MULTIPLE TBF DOWNLINK ASSIGNMENT messages before contention resolution is
1089 * completed on the mobile station side." */
1090 /* The possible uplink TBF is used to trigger downlink assignment:
1091 * - If there is no uplink TBF the MS is potentially in packet idle mode
1092 * and hence assignment will be done over CCCH (PCH)
1093 * - If there's an uplink TBF but it is finished (waiting for last PKT
1094 * CTRL ACK after sending last Pkt UL ACK/NACK with FINAL_ACK=1, then we
1095 * have no ways to contact the MS right now. Assignment will be delayed
1096 * until PKT CTRL ACK is received and the TBF is released at the MS side
1097 * (then assignment goes through PCH).
1098 */
1099 if (!ul_tbf)
1100 return true;
1101 if (ul_tbf_contention_resolution_done(ul_tbf) &&
1102 !tbf_ul_ack_waiting_cnf_final_ack(ul_tbf))
1103 return true;
1104
1105 return false;
1106
1107}
1108
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001109/* Alloc a UL TBF to be assigned over PACCH. Called when an MS requests to
1110 * create a new UL TBF during the end of life of a previous UL TBF (or an SBA).
1111 * In summary, this TBF is allocated as a consequence of receiving a "Pkt
1112 * Resource Req" or "Pkt Ctrl Ack" from the MS.
1113 * See TS 44.060 9.3.2.4.2 "Non-extended uplink TBF mode".
1114 */
1115struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_pacch(struct GprsMs *ms, int8_t use_trx)
1116{
1117 const bool single_slot = false;
1118 struct gprs_rlcmac_ul_tbf *ul_tbf;
1119
Pau Espin Pedrolbda7bb72022-10-31 14:33:09 +01001120 ul_tbf = ul_tbf_alloc(ms->bts, ms, use_trx, single_slot);
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001121 if (!ul_tbf) {
1122 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1123 /* Caller will most probably send a Imm Ass Reject after return */
1124 return NULL;
1125 }
1126 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_PACCH, NULL);
1127 /* Contention resolution is considered to be done since TLLI is known in MS */
1128 return ul_tbf;
1129}
1130
1131/* Alloc a UL TBF to be assigned over AGCH. Used by request of a "One phase
1132 * packet access", where MS requested only 1 PDCH TS (TS 44.018 Table 9.1.8.1). */
1133struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_agch(struct GprsMs *ms)
1134{
1135 const int8_t trx_no = -1;
1136 const bool single_slot = true;
1137 struct gprs_rlcmac_ul_tbf *ul_tbf;
1138
Pau Espin Pedrolbda7bb72022-10-31 14:33:09 +01001139 ul_tbf = ul_tbf_alloc(ms->bts, ms, trx_no, single_slot);
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001140 if (!ul_tbf) {
1141 LOGP(DTBF, LOGL_NOTICE, "No PDCH resource for Uplink TBF\n");
1142 /* Caller will most probably send a Imm Ass Reject after return */
1143 return NULL;
1144 }
1145 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_CCCH, NULL);
1146 return ul_tbf;
1147}
1148
Pau Espin Pedrola621d592022-12-13 17:35:04 +01001149/* Create a temporary dummy TBF to Tx a ImmAssReject if allocating a new one during
1150 * packet resource Request failed. This is similar as ul_tbf_alloc() but without
1151 * calling tbf->setup() (in charge of TFI/USF allocation), and reusing resources
1152 * from Packet Resource Request we received. See TS 44.060 sec 7.1.3.2.1 */
1153struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_rejected_pacch(struct GprsMs *ms, struct gprs_rlcmac_pdch *pdch)
1154{
1155 struct gprs_rlcmac_ul_tbf *ul_tbf;
1156 ul_tbf = ul_tbf_alloc_rejected(ms->bts, ms, pdch);
1157 if (!ul_tbf)
1158 return NULL;
1159 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_PACCH, NULL);
1160 osmo_fsm_inst_dispatch(tbf_ul_ass_fi(ul_tbf_as_tbf(ul_tbf)), TBF_UL_ASS_EV_SCHED_ASS_REJ, NULL);
1161
1162 return ul_tbf;
1163}
1164
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001165/* A new DL-TBF is allocated and assigned through PACCH using "tbf".
1166 * "tbf" may be either a UL-TBF or a DL-TBF.
1167 * Note: This should be called only when MS is reachable, see ms_is_reachable_for_dl_ass().
1168 */
1169int ms_new_dl_tbf_assigned_on_pacch(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001170{
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001171 OSMO_ASSERT(tbf);
1172 const int8_t trx_no = tbf_get_trx(tbf)->trx_no;
1173 const bool single_slot = false;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001174 struct gprs_rlcmac_dl_tbf *dl_tbf;
1175
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001176 dl_tbf = dl_tbf_alloc(ms->bts, ms, trx_no, single_slot);
1177 if (!dl_tbf) {
1178 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1179 return -EBUSY;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001180 }
1181
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001182 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START (PACCH)\n");
1183 dl_tbf_trigger_ass_on_pacch(dl_tbf, tbf);
1184 return 0;
1185}
1186
1187/* A new DL-TBF is allocated and assigned through PCH.
1188 * Note: This should be called only when MS is reachable, see ms_is_reachable_for_dl_ass().
1189 */
1190int ms_new_dl_tbf_assigned_on_pch(struct GprsMs *ms)
1191{
1192 const int8_t trx_no = -1;
1193 const bool single_slot = true;
1194 struct gprs_rlcmac_dl_tbf *dl_tbf;
1195
1196 dl_tbf = dl_tbf_alloc(ms->bts, ms, trx_no, single_slot);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001197 if (!dl_tbf) {
1198 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1199 return -EBUSY;
1200 }
1201
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001202 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START (PCH)\n");
1203 dl_tbf_trigger_ass_on_pch(dl_tbf);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001204 return 0;
1205}
1206
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001207int ms_append_llc_dl_data(struct GprsMs *ms, uint16_t pdu_delay_csec, const uint8_t *data, uint16_t len)
1208{
1209 struct timespec expire_time;
1210 struct gprs_rlcmac_dl_tbf *dl_tbf;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001211 int rc = 0;
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001212
1213 LOGPMS(ms, DTBFDL, LOGL_DEBUG, "appending %u bytes to DL LLC queue\n", len);
1214
1215 struct msgb *llc_msg = msgb_alloc(len, "llc_pdu_queue");
1216 if (!llc_msg)
1217 return -ENOMEM;
1218
1219 llc_queue_calc_pdu_lifetime(ms->bts, pdu_delay_csec, &expire_time);
1220 memcpy(msgb_put(llc_msg, len), data, len);
1221 llc_queue_enqueue(ms_llc_queue(ms), llc_msg, &expire_time);
1222 ms_start_llc_timer(ms);
1223
1224 dl_tbf = ms_dl_tbf(ms);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001225 if (dl_tbf) {
1226 if (tbf_state(dl_tbf_as_tbf_const(dl_tbf)) == TBF_ST_WAIT_RELEASE) {
1227 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "in WAIT RELEASE state (T3193), so reuse TBF\n");
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001228 rc = ms_new_dl_tbf_assigned_on_pacch(ms, dl_tbf_as_tbf(dl_tbf));
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001229 }
1230 } else {
1231 /* Check if we can create a DL TBF to start sending the enqueued
1232 * data. Otherwise it will be triggered later when it is reachable
1233 * again. */
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001234 if (ms_is_reachable_for_dl_ass(ms)) {
1235 if (ms_ul_tbf(ms))
1236 rc = ms_new_dl_tbf_assigned_on_pacch(ms, ul_tbf_as_tbf(ms_ul_tbf(ms)));
1237 else
1238 rc = ms_new_dl_tbf_assigned_on_pch(ms);
1239 }
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001240 }
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001241 return rc;
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001242}