blob: 3ed424456087f214e6ace817bc43970218832e31 [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 Pedrolda971ee2020-12-16 15:59:45 +010026
27#include <time.h>
28
29#include <osmocom/core/talloc.h>
30#include <osmocom/core/utils.h>
31#include <osmocom/core/timer.h>
32#include <osmocom/gsm/protocol/gsm_04_08.h>
33#include <osmocom/gsm/gsm48.h>
34#include <osmocom/core/logging.h>
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +010035#include <osmocom/core/stats.h>
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010036#include "coding_scheme.h"
37
38#define GPRS_CODEL_SLOW_INTERVAL_MS 4000
39
40extern void *tall_pcu_ctx;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +010041static unsigned int next_ms_ctr_group_id;
42
43static const struct rate_ctr_desc ms_ctr_description[] = {
44 [MS_CTR_DL_CTRL_MSG_SCHED] = { "ms:dl_ctrl_msg_sched", "Amount of DL CTRL messages scheduled" },
45};
46
Pau Espin Pedrolf5a251b2021-01-12 20:57:56 +010047static const struct rate_ctr_group_desc ms_ctrg_desc = {
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +010048 .group_name_prefix = "pcu:ms",
49 .group_description = "MS Statistics",
50 .class_id = OSMO_STATS_CLASS_SUBSCRIBER,
51 .num_ctr = ARRAY_SIZE(ms_ctr_description),
52 .ctr_desc = ms_ctr_description,
53};
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010054
55static int64_t now_msec()
56{
57 struct timespec ts;
58 osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
59
60 return (int64_t)(ts.tv_sec) * 1000 + ts.tv_nsec / 1000000;
61}
62
63void gprs_default_cb_ms_idle(struct GprsMs *ms)
64{
65 talloc_free(ms);
66}
67
68void gprs_default_cb_ms_active(struct GprsMs *ms)
69{
70 /* do nothing */
71}
72
73static struct gpr_ms_callback gprs_default_cb = {
74 .ms_idle = gprs_default_cb_ms_idle,
75 .ms_active = gprs_default_cb_ms_active,
76};
77
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +010078static void ms_release_timer_cb(void *data)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010079{
80 struct GprsMs *ms = (struct GprsMs *) data;
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +010081 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Release timer expired\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010082
83 if (ms->timer.data) {
84 ms->timer.data = NULL;
85 ms_unref(ms);
86 }
87}
88
89static int ms_talloc_destructor(struct GprsMs *ms);
Pau Espin Pedrol2182e622021-01-14 16:48:38 +010090struct GprsMs *ms_alloc(struct gprs_rlcmac_bts *bts, uint32_t tlli)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010091{
92 struct GprsMs *ms = talloc_zero(tall_pcu_ctx, struct GprsMs);
Pau Espin Pedrolb4987462022-04-01 17:21:08 +020093 OSMO_ASSERT(bts);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010094
95 talloc_set_destructor(ms, ms_talloc_destructor);
96
97 ms->bts = bts;
98 ms->cb = gprs_default_cb;
99 ms->tlli = tlli;
100 ms->new_ul_tlli = GSM_RESERVED_TMSI;
101 ms->new_dl_tlli = GSM_RESERVED_TMSI;
102 ms->ta = GSM48_TA_INVALID;
103 ms->current_cs_ul = UNKNOWN;
104 ms->current_cs_dl = UNKNOWN;
105 ms->is_idle = true;
106 INIT_LLIST_HEAD(&ms->list);
107 INIT_LLIST_HEAD(&ms->old_tbfs);
108
109 int codel_interval = LLC_CODEL_USE_DEFAULT;
110
111 LOGP(DRLCMAC, LOGL_INFO, "Creating MS object, TLLI = 0x%08x\n", tlli);
112
113 ms->imsi[0] = '\0';
Pau Espin Pedrolc7802d92022-05-09 16:07:52 +0200114 osmo_timer_setup(&ms->timer, ms_release_timer_cb, NULL);
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200115 llc_queue_init(&ms->llc_queue, ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100116
117 ms_set_mode(ms, GPRS);
118
Pau Espin Pedrolb4987462022-04-01 17:21:08 +0200119 codel_interval = the_pcu->vty.llc_codel_interval_msec;
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200120 if (codel_interval == LLC_CODEL_USE_DEFAULT)
121 codel_interval = GPRS_CODEL_SLOW_INTERVAL_MS;
122 llc_queue_set_codel_interval(&ms->llc_queue, codel_interval);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100123
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100124 ms->last_cs_not_low = now_msec();
125 ms->app_info_pending = false;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100126
127 ms->ctrs = rate_ctr_group_alloc(ms, &ms_ctrg_desc, next_ms_ctr_group_id++);
128 if (!ms->ctrs)
129 goto free_ret;
130
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100131 return ms;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100132free_ret:
133 talloc_free(ms);
134 return NULL;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100135}
136
137static int ms_talloc_destructor(struct GprsMs *ms)
138{
139 struct llist_item *pos, *tmp;
140
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100141 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Destroying MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100142
143 ms_set_reserved_slots(ms, NULL, 0, 0);
144
Vadim Yanitskiye33c2362022-07-22 03:44:44 +0700145 osmo_timer_del(&ms->timer);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100146
147 if (ms->ul_tbf) {
148 tbf_set_ms((struct gprs_rlcmac_tbf *)ms->ul_tbf, NULL);
149 ms->ul_tbf = NULL;
150 }
151
152 if (ms->dl_tbf) {
153 tbf_set_ms((struct gprs_rlcmac_tbf *)ms->dl_tbf, NULL);
154 ms->dl_tbf = NULL;
155 }
156
157 llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
158 struct gprs_rlcmac_tbf *tbf = (struct gprs_rlcmac_tbf *)pos->entry;
159 tbf_set_ms(tbf, NULL);
160 }
161
162 llc_queue_clear(&ms->llc_queue, ms->bts);
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100163
164 if (ms->ctrs)
165 rate_ctr_group_free(ms->ctrs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100166 return 0;
167}
168
169
170void ms_set_callback(struct GprsMs *ms, struct gpr_ms_callback *cb)
171{
172 if (cb)
173 ms->cb = *cb;
174 else
175 ms->cb = gprs_default_cb;
176}
177
178static void ms_update_status(struct GprsMs *ms)
179{
180 if (ms->ref > 0)
181 return;
182
183 if (ms_is_idle(ms) && !ms->is_idle) {
184 ms->is_idle = true;
185 ms->cb.ms_idle(ms);
186 /* this can be deleted by now, do not access it */
187 return;
188 }
189
190 if (!ms_is_idle(ms) && ms->is_idle) {
191 ms->is_idle = false;
192 ms->cb.ms_active(ms);
193 }
194}
195
196struct GprsMs *ms_ref(struct GprsMs *ms)
197{
198 ms->ref += 1;
199 return ms;
200}
201
202void ms_unref(struct GprsMs *ms)
203{
204 OSMO_ASSERT(ms->ref >= 0);
205 ms->ref -= 1;
206 if (ms->ref == 0)
207 ms_update_status(ms);
208}
209
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100210static void ms_release_timer_start(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100211{
212 if (ms->delay == 0)
213 return;
214
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100215 LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Schedule MS release in %u secs\n", ms->delay);
216
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100217 if (!ms->timer.data)
218 ms->timer.data = ms_ref(ms);
219
220 osmo_timer_schedule(&ms->timer, ms->delay, 0);
221}
222
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100223static void ms_release_timer_stop(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100224{
225 if (!ms->timer.data)
226 return;
227
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100228 LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Cancel scheduled MS release\n");
229
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100230 osmo_timer_del(&ms->timer);
231 ms->timer.data = NULL;
232 ms_unref(ms);
233}
234
235void ms_set_mode(struct GprsMs *ms, enum mcs_kind mode)
236{
237 ms->mode = mode;
238
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100239 switch (ms->mode) {
240 case GPRS:
241 if (!mcs_is_gprs(ms->current_cs_ul)) {
242 ms->current_cs_ul = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100243 ms->bts->initial_cs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100244 if (!mcs_is_valid(ms->current_cs_ul))
245 ms->current_cs_ul = CS1;
246 }
247 if (!mcs_is_gprs(ms->current_cs_dl)) {
248 ms->current_cs_dl = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100249 ms->bts->initial_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100250 if (!mcs_is_valid(ms->current_cs_dl))
251 ms->current_cs_dl = CS1;
252 }
253 break;
254
255 case EGPRS_GMSK:
Pau Espin Pedrol7bb8cd62021-01-25 15:08:35 +0100256 if (!mcs_is_edge_gmsk(ms->current_cs_ul)) {
257 ms->current_cs_ul = mcs_get_egprs_by_num(
258 ms->bts->initial_mcs_ul);
259 if (!mcs_is_valid(ms->current_cs_ul))
260 ms->current_cs_ul = MCS1;
261 }
262 if (!mcs_is_edge_gmsk(ms->current_cs_dl)) {
263 ms->current_cs_dl = mcs_get_egprs_by_num(
264 ms->bts->initial_mcs_dl);
265 if (!mcs_is_valid(ms->current_cs_dl))
266 ms->current_cs_dl = MCS1;
267 }
268 break;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100269 case EGPRS:
270 if (!mcs_is_edge(ms->current_cs_ul)) {
271 ms->current_cs_ul = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100272 ms->bts->initial_mcs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100273 if (!mcs_is_valid(ms->current_cs_ul))
274 ms->current_cs_ul = MCS1;
275 }
276 if (!mcs_is_edge(ms->current_cs_dl)) {
277 ms->current_cs_dl = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100278 ms->bts->initial_mcs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100279 if (!mcs_is_valid(ms->current_cs_dl))
280 ms->current_cs_dl = MCS1;
281 }
282 break;
283 }
284}
285
286static void ms_attach_ul_tbf(struct GprsMs *ms, struct gprs_rlcmac_ul_tbf *tbf)
287{
288 if (ms->ul_tbf == tbf)
289 return;
290
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100291 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 +0100292
293 ms_ref(ms);
294
295 if (ms->ul_tbf)
296 llist_add_tail(tbf_ms_list((struct gprs_rlcmac_tbf *)ms->ul_tbf), &ms->old_tbfs);
297
298 ms->ul_tbf = tbf;
299
300 if (tbf)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100301 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100302
303 ms_unref(ms);
304}
305
306static void ms_attach_dl_tbf(struct GprsMs *ms, struct gprs_rlcmac_dl_tbf *tbf)
307{
308 if (ms->dl_tbf == tbf)
309 return;
310
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100311 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 +0100312
313 ms_ref(ms);
314
315 if (ms->dl_tbf)
316 llist_add_tail(tbf_ms_list((struct gprs_rlcmac_tbf *)ms->dl_tbf), &ms->old_tbfs);
317
318 ms->dl_tbf = tbf;
319
320 if (tbf)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100321 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100322
323 ms_unref(ms);
324}
325
326void ms_attach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
327{
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200328 if (tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100329 ms_attach_dl_tbf(ms, as_dl_tbf(tbf));
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200330 else
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100331 ms_attach_ul_tbf(ms, as_ul_tbf(tbf));
332}
333
334void ms_detach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
335{
336 if (tbf == (struct gprs_rlcmac_tbf *)(ms->ul_tbf)) {
337 ms->ul_tbf = NULL;
338 } else if (tbf == (struct gprs_rlcmac_tbf *)(ms->dl_tbf)) {
339 ms->dl_tbf = NULL;
340 } else {
341 bool found = false;
342
343 struct llist_item *pos, *tmp;
344 llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
345 struct gprs_rlcmac_tbf *tmp_tbf = (struct gprs_rlcmac_tbf *)pos->entry;
346 if (tmp_tbf == tbf) {
347 llist_del(&pos->list);
348 found = true;
349 break;
350 }
351 }
352
353 /* Protect against recursive calls via set_ms() */
354 if (!found)
355 return;
356 }
357
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100358 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Detaching TBF: %s\n",
359 tbf_name(tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100360
361 if (tbf_ms(tbf) == ms)
362 tbf_set_ms(tbf, NULL);
363
364 if (!ms->dl_tbf && !ms->ul_tbf) {
365 ms_set_reserved_slots(ms, NULL, 0, 0);
366
367 if (ms_tlli(ms) != 0)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100368 ms_release_timer_start(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100369 }
370
371 ms_update_status(ms);
372}
373
374void ms_reset(struct GprsMs *ms)
375{
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100376 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Clearing MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100377
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100378 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100379
380 ms->tlli = GSM_RESERVED_TMSI;
381 ms->new_dl_tlli = ms->tlli;
382 ms->new_ul_tlli = ms->tlli;
383 ms->imsi[0] = '\0';
384}
385
386static void ms_merge_old_ms(struct GprsMs *ms, struct GprsMs *old_ms)
387{
388 OSMO_ASSERT(old_ms != ms);
389
390 if (strlen(ms_imsi(ms)) == 0 && strlen(ms_imsi(old_ms)) != 0)
391 osmo_strlcpy(ms->imsi, ms_imsi(old_ms), sizeof(ms->imsi));
392
393 if (!ms_ms_class(ms) && ms_ms_class(old_ms))
394 ms_set_ms_class(ms, ms_ms_class(old_ms));
395
396 if (!ms_egprs_ms_class(ms) && ms_egprs_ms_class(old_ms))
397 ms_set_egprs_ms_class(ms, ms_egprs_ms_class(old_ms));
398
399 llc_queue_move_and_merge(&ms->llc_queue, &old_ms->llc_queue);
400
401 ms_reset(old_ms);
402}
403
404void ms_merge_and_clear_ms(struct GprsMs *ms, struct GprsMs *old_ms)
405{
406 OSMO_ASSERT(old_ms != ms);
407
408 ms_ref(old_ms);
409
410 /* Clean up the old MS object */
411 /* TODO: Use timer? */
412 if (ms_ul_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms), T_MAX))
413 tbf_free((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms));
414 if (ms_dl_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms), T_MAX))
415 tbf_free((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms));
416
417 ms_merge_old_ms(ms, old_ms);
418
419 ms_unref(old_ms);
420}
421
422void ms_set_tlli(struct GprsMs *ms, uint32_t tlli)
423{
424 if (tlli == ms->tlli || tlli == ms->new_ul_tlli)
425 return;
426
427 if (tlli != ms->new_dl_tlli) {
428 LOGP(DRLCMAC, LOGL_INFO,
429 "Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
430 "not yet confirmed\n",
431 ms_tlli(ms), tlli);
432 ms->new_ul_tlli = tlli;
433 return;
434 }
435
436 LOGP(DRLCMAC, LOGL_INFO,
437 "Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
438 "already confirmed partly\n",
439 ms->tlli, tlli);
440
441 ms->tlli = tlli;
442 ms->new_dl_tlli = GSM_RESERVED_TMSI;
443 ms->new_ul_tlli = GSM_RESERVED_TMSI;
444}
445
446bool ms_confirm_tlli(struct GprsMs *ms, uint32_t tlli)
447{
448 if (tlli == ms->tlli || tlli == ms->new_dl_tlli)
449 return false;
450
451 if (tlli != ms->new_ul_tlli) {
452 /* The MS has not sent a message with the new TLLI, which may
453 * happen according to the spec [TODO: add reference]. */
454
455 LOGP(DRLCMAC, LOGL_INFO,
456 "The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
457 "partly confirmed\n", tlli);
458 /* Use the network's idea of TLLI as candidate, this does not
459 * change the result value of tlli() */
460 ms->new_dl_tlli = tlli;
461 return false;
462 }
463
464 LOGP(DRLCMAC, LOGL_INFO,
465 "Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
466
467 ms->tlli = tlli;
468 ms->new_dl_tlli = GSM_RESERVED_TMSI;
469 ms->new_ul_tlli = GSM_RESERVED_TMSI;
470
471 return true;
472}
473
474void ms_set_imsi(struct GprsMs *ms, const char *imsi)
475{
476 if (!imsi) {
477 LOGP(DRLCMAC, LOGL_ERROR, "Expected IMSI!\n");
478 return;
479 }
480
481 if (imsi[0] && strlen(imsi) < 3) {
482 LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI '%s'!\n",
483 imsi);
484 return;
485 }
486
487 if (strcmp(imsi, ms->imsi) == 0)
488 return;
489
490 LOGP(DRLCMAC, LOGL_INFO,
491 "Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
492 ms_tlli(ms), ms->imsi, imsi);
493
494 struct GprsMs *old_ms = bts_ms_by_imsi(ms->bts, imsi);
495 /* Check if we are going to store a different MS object with already
496 existing IMSI. This is probably a bug in code calling this function,
497 since it should take care of this explicitly */
498 if (old_ms) {
499 /* We cannot find ms->ms by IMSI since we know that it has a
500 * different IMSI */
501 OSMO_ASSERT(old_ms != ms);
502
503 LOGPMS(ms, DRLCMAC, LOGL_NOTICE,
504 "IMSI '%s' was already assigned to another "
505 "MS object: TLLI = 0x%08x, that IMSI will be removed\n",
506 imsi, ms_tlli(old_ms));
507
508 ms_merge_and_clear_ms(ms, old_ms);
509 }
510
511
512 osmo_strlcpy(ms->imsi, imsi, sizeof(ms->imsi));
513}
514
Pau Espin Pedrol5deac142021-11-12 18:01:50 +0100515uint16_t ms_paging_group(struct GprsMs *ms)
516{
517 uint16_t pgroup;
518 if (!ms_imsi_is_valid(ms))
519 return 0; /* 000 is the special "all paging" group */
520 if ((pgroup = imsi2paging_group(ms_imsi(ms))) > 999) {
521 LOGPMS(ms, DRLCMAC, LOGL_ERROR, "IMSI to paging group failed!\n");
522 return 0;
523 }
524 return pgroup;
525}
526
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100527void ms_set_ta(struct GprsMs *ms, uint8_t ta_)
528{
529 if (ta_ == ms->ta)
530 return;
531
532 if (gsm48_ta_is_valid(ta_)) {
533 LOGP(DRLCMAC, LOGL_INFO,
534 "Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
535 ms_tlli(ms), ms->ta, ta_);
536 ms->ta = ta_;
537 } else
538 LOGP(DRLCMAC, LOGL_NOTICE,
539 "MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
540 "value %d kept)\n", ms_tlli(ms), ta_, ms->ta);
541}
542
543void ms_set_ms_class(struct GprsMs *ms, uint8_t ms_class_)
544{
545 if (ms_class_ == ms->ms_class)
546 return;
547
548 LOGP(DRLCMAC, LOGL_INFO,
549 "Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
550 ms_tlli(ms), ms->ms_class, ms_class_);
551
552 ms->ms_class = ms_class_;
553}
554
555void ms_set_egprs_ms_class(struct GprsMs *ms, uint8_t ms_class_)
556{
557 if (ms_class_ == ms->egprs_ms_class)
558 return;
559
560 LOGP(DRLCMAC, LOGL_INFO,
561 "Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
562 ms_tlli(ms), ms->egprs_ms_class, ms_class_);
563
564 ms->egprs_ms_class = ms_class_;
565
566 if (!bts_max_mcs_ul(ms->bts) || !bts_max_mcs_dl(ms->bts)) {
567 LOGPMS(ms, DRLCMAC, LOGL_DEBUG,
568 "Avoid enabling EGPRS because use of MCS is disabled: ul=%u dl=%u\n",
569 bts_max_mcs_ul(ms->bts), bts_max_mcs_dl(ms->bts));
570 return;
571 }
572
573 if (mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts))) &&
574 mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts))) &&
575 ms_mode(ms) != EGPRS)
576 {
577 ms_set_mode(ms, EGPRS_GMSK);
578 } else {
579 ms_set_mode(ms, EGPRS);
580 }
581 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Enabled EGPRS, mode %s\n", mode_name(ms_mode(ms)));
582}
583
584void ms_update_error_rate(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, int error_rate)
585{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100586 int64_t now;
587 enum CodingScheme max_cs_dl = ms_max_cs_dl(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100588 OSMO_ASSERT(max_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100589
590 if (error_rate < 0)
591 return;
592
593 now = now_msec();
594
595 /* TODO: Check for TBF direction */
596 /* TODO: Support different CS values for UL and DL */
597
598 ms->nack_rate_dl = error_rate;
599
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100600 if (error_rate > the_pcu->vty.cs_adj_upper_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100601 if (mcs_chan_code(ms->current_cs_dl) > 0) {
602 mcs_dec_kind(&ms->current_cs_dl, ms_mode(ms));
603 LOGP(DRLCMACDL, LOGL_INFO,
604 "MS (IMSI %s): High error rate %d%%, "
605 "reducing CS level to %s\n",
606 ms_imsi(ms), error_rate, mcs_name(ms->current_cs_dl));
607 ms->last_cs_not_low = now;
608 }
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100609 } else if (error_rate < the_pcu->vty.cs_adj_lower_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100610 if (ms->current_cs_dl < max_cs_dl) {
611 if (now - ms->last_cs_not_low > 1000) {
612 mcs_inc_kind(&ms->current_cs_dl, ms_mode(ms));
613
614 LOGP(DRLCMACDL, LOGL_INFO,
615 "MS (IMSI %s): Low error rate %d%%, "
616 "increasing DL CS level to %s\n",
617 ms_imsi(ms), error_rate,
618 mcs_name(ms->current_cs_dl));
619 ms->last_cs_not_low = now;
620 } else {
621 LOGP(DRLCMACDL, LOGL_DEBUG,
622 "MS (IMSI %s): Low error rate %d%%, "
623 "ignored (within blocking period)\n",
624 ms_imsi(ms), error_rate);
625 }
626 }
627 } else {
628 LOGP(DRLCMACDL, LOGL_DEBUG,
629 "MS (IMSI %s): Medium error rate %d%%, ignored\n",
630 ms_imsi(ms), error_rate);
631 ms->last_cs_not_low = now;
632 }
633}
634
635enum CodingScheme ms_max_cs_ul(const struct GprsMs *ms)
636{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100637 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100638 OSMO_ASSERT(ms->bts != NULL);
639
640 if (mcs_is_gprs(ms->current_cs_ul)) {
641 if (!bts_max_cs_ul(ms->bts)) {
642 return CS4;
643 }
644
645 return mcs_get_gprs_by_num(bts_max_cs_ul(ms->bts));
646 }
647
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100648 cs = mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts));
649 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
650 cs = MCS4;
651 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100652}
653
654void ms_set_current_cs_dl(struct GprsMs *ms, enum CodingScheme scheme)
655{
656 ms->current_cs_dl = scheme;
657}
658
659enum CodingScheme ms_max_cs_dl(const struct GprsMs *ms)
660{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100661 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100662 OSMO_ASSERT(ms->bts != NULL);
663
664 if (mcs_is_gprs(ms->current_cs_dl)) {
665 if (!bts_max_cs_dl(ms->bts)) {
666 return CS4;
667 }
668
669 return mcs_get_gprs_by_num(bts_max_cs_dl(ms->bts));
670 }
671
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100672 cs = mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts));
673 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
674 cs = MCS4;
675 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100676}
677
678void ms_update_cs_ul(struct GprsMs *ms, const struct pcu_l1_meas *meas)
679{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100680 enum CodingScheme max_cs_ul = ms_max_cs_ul(ms);
681
682 int old_link_qual;
683 int low;
684 int high;
685 enum CodingScheme new_cs_ul = ms->current_cs_ul;
686 uint8_t current_cs = mcs_chan_code(ms->current_cs_ul);
687
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100688 if (!max_cs_ul) {
689 LOGP(DRLCMACMEAS, LOGL_ERROR,
690 "max_cs_ul cannot be derived (current UL CS: %s)\n",
691 mcs_name(ms->current_cs_ul));
692 return;
693 }
694
695 if (!ms->current_cs_ul) {
696 LOGP(DRLCMACMEAS, LOGL_ERROR,
697 "Unable to update UL (M)CS because it's not set: %s\n",
698 mcs_name(ms->current_cs_ul));
699 return;
700 }
701
702 if (!meas->have_link_qual) {
703 LOGP(DRLCMACMEAS, LOGL_ERROR,
704 "Unable to update UL (M)CS %s because we don't have link quality measurements.\n",
705 mcs_name(ms->current_cs_ul));
706 return;
707 }
708
709 if (mcs_is_gprs(ms->current_cs_ul)) {
710 if (current_cs >= MAX_GPRS_CS)
711 current_cs = MAX_GPRS_CS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100712 low = the_pcu->vty.cs_lqual_ranges[current_cs].low;
713 high = the_pcu->vty.cs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100714 } else if (mcs_is_edge(ms->current_cs_ul)) {
715 if (current_cs >= MAX_EDGE_MCS)
716 current_cs = MAX_EDGE_MCS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100717 low = the_pcu->vty.mcs_lqual_ranges[current_cs].low;
718 high = the_pcu->vty.mcs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100719 } else {
720 LOGP(DRLCMACMEAS, LOGL_ERROR,
721 "Unable to update UL (M)CS because it's neither GPRS nor EDGE: %s\n",
722 mcs_name(ms->current_cs_ul));
723 return;
724 }
725
726 /* To avoid rapid changes of the coding scheme, we also take
727 * the old link quality value into account (if present). */
728 if (ms->l1_meas.have_link_qual)
729 old_link_qual = ms->l1_meas.link_qual;
730 else
731 old_link_qual = meas->link_qual;
732
733 if (meas->link_qual < low && old_link_qual < low)
734 mcs_dec_kind(&new_cs_ul, ms_mode(ms));
735 else if (meas->link_qual > high && old_link_qual > high &&
736 ms->current_cs_ul < max_cs_ul)
737 mcs_inc_kind(&new_cs_ul, ms_mode(ms));
738
739 if (ms->current_cs_ul != new_cs_ul) {
740 LOGPMS(ms, DRLCMACMEAS, LOGL_INFO,
741 "Link quality %ddB (old %ddB) left window [%d, %d], "
742 "modifying uplink CS level: %s -> %s\n",
743 meas->link_qual, old_link_qual,
744 low, high,
745 mcs_name(ms->current_cs_ul), mcs_name(new_cs_ul));
746
747 ms->current_cs_ul = new_cs_ul;
748 }
749}
750
751void ms_update_l1_meas(struct GprsMs *ms, const struct pcu_l1_meas *meas)
752{
753 unsigned i;
754
755 ms_update_cs_ul(ms, meas);
756
757 if (meas->have_rssi)
758 pcu_l1_meas_set_rssi(&ms->l1_meas, meas->rssi);
759 if (meas->have_bto)
760 pcu_l1_meas_set_bto(&ms->l1_meas, meas->bto);
761 if (meas->have_ber)
762 pcu_l1_meas_set_ber(&ms->l1_meas, meas->ber);
763 if (meas->have_link_qual)
764 pcu_l1_meas_set_link_qual(&ms->l1_meas, meas->link_qual);
765
766 if (meas->have_ms_rx_qual)
767 pcu_l1_meas_set_ms_rx_qual(&ms->l1_meas, meas->ms_rx_qual);
768 if (meas->have_ms_c_value)
769 pcu_l1_meas_set_ms_c_value(&ms->l1_meas, meas->ms_c_value);
770 if (meas->have_ms_sign_var)
771 pcu_l1_meas_set_ms_sign_var(&ms->l1_meas, meas->ms_sign_var);
772
773 if (meas->have_ms_i_level) {
774 for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
775 if (meas->ts[i].have_ms_i_level)
776 pcu_l1_meas_set_ms_i_level(&ms->l1_meas, i, meas->ts[i].ms_i_level);
777 else
778 ms->l1_meas.ts[i].have_ms_i_level = 0;
779 }
780 }
781}
782
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100783/* req_mcs_kind acts as a set filter, where EGPRS means any and GPRS is the most restrictive */
784enum CodingScheme ms_current_cs_dl(const struct GprsMs *ms, enum mcs_kind req_mcs_kind)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100785{
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100786 enum CodingScheme orig_cs = ms->current_cs_dl;
787 struct gprs_rlcmac_bts *bts = ms->bts;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100788 size_t unencoded_octets;
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100789 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100790
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100791 /* It could be that a TBF requests a GPRS CS despite the MS currently
792 being upgraded to EGPRS (hence reporting MCS). That could happen
793 because the TBF was created early in the process where we didn't have
794 yet enough information about the MS, and only AFTER it was created we
795 upgraded the MS to be EGPRS capable.
796 As a result, when the MS is queried for the target CS here, we could be
797 returning an MCS despite the current TBF being established as GPRS,
798 but we rather stick to the TBF type we assigned to the MS rather than
799 magically sending EGPRS data blocks to a GPRS TBF.
800 It could also be that the caller requests specific MCS kind
801 explicitly too due to scheduling restrictions (GPRS+EGPRS multiplexing). */
802 if (req_mcs_kind == EGPRS_GMSK && mcs_is_edge(orig_cs) && orig_cs > MCS4) {
803 cs = bts_cs_dl_is_supported(bts, MCS4) ? MCS4 :
804 bts_cs_dl_is_supported(bts, MCS3) ? MCS3 :
805 bts_cs_dl_is_supported(bts, MCS2) ? MCS2 :
806 MCS1;
807 } else if (req_mcs_kind == GPRS && mcs_is_edge(orig_cs)) { /* GPRS */
808 int i;
809 cs = orig_cs > MCS4 ? MCS4 : orig_cs;
810 cs -= (MCS1 - CS1); /* MCSx -> CSx */
811 /* Find suitable CS starting from equivalent MCS which is supported by BTS: */
812 for (i = mcs_chan_code(cs); !bts_cs_dl_is_supported(bts, CS1 + i); i--);
813 OSMO_ASSERT(i >= 0 && i <= 3); /* CS1 is always supported */
814 cs = CS1 + i;
815 } else {
816 cs = orig_cs;
817 }
818
819 if (orig_cs != cs)
820 LOGPMS(ms, DRLCMACDL, LOGL_INFO, "MS (mode=%s) suggests transmitting "
821 "DL %s, downgrade to %s in order to match TBF & scheduler requirements\n",
822 mode_name(ms_mode(ms)), mcs_name(orig_cs), mcs_name(cs));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100823
824 unencoded_octets = llc_queue_octets(&ms->llc_queue);
825
826 /* If the DL TBF is active, add number of unencoded chunk octets */
827 if (ms->dl_tbf)
828 unencoded_octets += llc_chunk_size(tbf_llc((struct gprs_rlcmac_tbf *)ms->dl_tbf));
829
830 /* There are many unencoded octets, don't reduce */
Pau Espin Pedrolad79b852021-01-14 13:20:55 +0100831 if (unencoded_octets >= the_pcu->vty.cs_downgrade_threshold)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100832 return cs;
833
834 /* RF conditions are good, don't reduce */
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100835 if (ms->nack_rate_dl < the_pcu->vty.cs_adj_lower_limit)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100836 return cs;
837
838 /* The throughput would probably be better if the CS level was reduced */
839 mcs_dec_kind(&cs, ms_mode(ms));
840
841 /* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
842 if (cs == CS2)
843 mcs_dec_kind(&cs, ms_mode(ms));
844
845 return cs;
846}
847
848int ms_first_common_ts(const struct GprsMs *ms)
849{
850 if (ms->dl_tbf)
851 return tbf_first_common_ts((struct gprs_rlcmac_tbf *)ms->dl_tbf);
852
853 if (ms->ul_tbf)
854 return tbf_first_common_ts((struct gprs_rlcmac_tbf *)ms->ul_tbf);
855
856 return -1;
857}
858
859uint8_t ms_dl_slots(const struct GprsMs *ms)
860{
861 uint8_t slots = 0;
862
863 if (ms->dl_tbf)
864 slots |= tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
865
866 if (ms->ul_tbf)
867 slots |= tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf);
868
869 return slots;
870}
871
872uint8_t ms_ul_slots(const struct GprsMs *ms)
873{
874 uint8_t slots = 0;
875
876 if (ms->dl_tbf)
877 slots |= tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
878
879 if (ms->ul_tbf)
880 slots |= tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf);
881
882 return slots;
883}
884
885uint8_t ms_current_pacch_slots(const struct GprsMs *ms)
886{
887 uint8_t slots = 0;
888
889 bool is_dl_active = ms->dl_tbf && tbf_is_tfi_assigned((struct gprs_rlcmac_tbf *)ms->dl_tbf);
890 bool is_ul_active = ms->ul_tbf && tbf_is_tfi_assigned((struct gprs_rlcmac_tbf *)ms->ul_tbf);
891
892 if (!is_dl_active && !is_ul_active)
893 return 0;
894
895 /* see TS 44.060, 8.1.1.2.2 */
896 if (is_dl_active && !is_ul_active)
897 slots = tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
898 else if (!is_dl_active && is_ul_active)
899 slots = tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf);
900 else
901 slots = tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf) &
902 tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
903
904 /* Assume a multislot class 1 device */
905 /* TODO: For class 2 devices, this could be removed */
906 slots = pcu_lsb(slots);
907
908 return slots;
909}
910
911void ms_set_reserved_slots(struct GprsMs *ms, struct gprs_rlcmac_trx *trx,
912 uint8_t ul_slots, uint8_t dl_slots)
913{
914 if (ms->current_trx) {
915 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
916 ms->reserved_dl_slots);
917 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
918 ms->reserved_ul_slots);
919 ms->reserved_dl_slots = 0;
920 ms->reserved_ul_slots = 0;
921 }
922 ms->current_trx = trx;
923 if (trx) {
924 ms->reserved_dl_slots = dl_slots;
925 ms->reserved_ul_slots = ul_slots;
926 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
927 ms->reserved_dl_slots);
928 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
929 ms->reserved_ul_slots);
930 }
931}
932
933struct gprs_rlcmac_tbf *ms_tbf(const struct GprsMs *ms, enum gprs_rlcmac_tbf_direction dir)
934{
935 switch (dir) {
936 case GPRS_RLCMAC_DL_TBF: return (struct gprs_rlcmac_tbf *)ms->dl_tbf;
937 case GPRS_RLCMAC_UL_TBF: return (struct gprs_rlcmac_tbf *)ms->ul_tbf;
938 }
939
940 return NULL;
941}
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +0100942
943int ms_nacc_start(struct GprsMs *ms, Packet_Cell_Change_Notification_t *notif)
944{
945 if (!ms->nacc)
946 ms->nacc = nacc_fsm_alloc(ms);
947 if (!ms->nacc)
948 return -EINVAL;
949 return osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_RX_CELL_CHG_NOTIFICATION, notif);
950}
951
952bool ms_nacc_rts(const struct GprsMs *ms)
953{
954 if (!ms->nacc)
955 return false;
956 if (ms->nacc->fi->state == NACC_ST_TX_NEIGHBOUR_DATA ||
957 ms->nacc->fi->state == NACC_ST_TX_CELL_CHG_CONTINUE)
958 return true;
959 return false;
960}
961
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +0100962struct msgb *ms_nacc_create_rlcmac_msg(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, uint32_t fn, uint8_t ts)
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +0100963{
964 int rc;
965 struct nacc_ev_create_rlcmac_msg_ctx data_ctx;
966
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +0100967 data_ctx = (struct nacc_ev_create_rlcmac_msg_ctx) {
968 .tbf = tbf,
969 .fn = fn,
970 .ts = ts,
971 .msg = NULL,
972 };
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +0100973
974 rc = osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_CREATE_RLCMAC_MSG, &data_ctx);
975 if (rc != 0 || !data_ctx.msg)
976 return NULL;
977 return data_ctx.msg;
978}