blob: 8453f2086cb6604e5e33854531f9ef9dca649037 [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
Pau Espin Pedrol0b5997e2022-10-21 14:10:41 +0200422/* Set/update the MS object TLLI based on knowledge gained from the MS side (Uplink direction) */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100423void ms_set_tlli(struct GprsMs *ms, uint32_t tlli)
424{
425 if (tlli == ms->tlli || tlli == ms->new_ul_tlli)
426 return;
427
428 if (tlli != ms->new_dl_tlli) {
429 LOGP(DRLCMAC, LOGL_INFO,
430 "Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
431 "not yet confirmed\n",
432 ms_tlli(ms), tlli);
433 ms->new_ul_tlli = tlli;
434 return;
435 }
436
437 LOGP(DRLCMAC, LOGL_INFO,
438 "Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
439 "already confirmed partly\n",
440 ms->tlli, tlli);
441
442 ms->tlli = tlli;
443 ms->new_dl_tlli = GSM_RESERVED_TMSI;
444 ms->new_ul_tlli = GSM_RESERVED_TMSI;
445}
446
Pau Espin Pedrol0b5997e2022-10-21 14:10:41 +0200447/* Set/update the MS object TLLI based on knowledge gained from the SGSN side (Downlink direction) */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100448bool ms_confirm_tlli(struct GprsMs *ms, uint32_t tlli)
449{
450 if (tlli == ms->tlli || tlli == ms->new_dl_tlli)
451 return false;
452
453 if (tlli != ms->new_ul_tlli) {
454 /* The MS has not sent a message with the new TLLI, which may
455 * happen according to the spec [TODO: add reference]. */
456
457 LOGP(DRLCMAC, LOGL_INFO,
458 "The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
459 "partly confirmed\n", tlli);
460 /* Use the network's idea of TLLI as candidate, this does not
461 * change the result value of tlli() */
462 ms->new_dl_tlli = tlli;
463 return false;
464 }
465
466 LOGP(DRLCMAC, LOGL_INFO,
467 "Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
468
469 ms->tlli = tlli;
470 ms->new_dl_tlli = GSM_RESERVED_TMSI;
471 ms->new_ul_tlli = GSM_RESERVED_TMSI;
472
473 return true;
474}
475
476void ms_set_imsi(struct GprsMs *ms, const char *imsi)
477{
478 if (!imsi) {
479 LOGP(DRLCMAC, LOGL_ERROR, "Expected IMSI!\n");
480 return;
481 }
482
483 if (imsi[0] && strlen(imsi) < 3) {
484 LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI '%s'!\n",
485 imsi);
486 return;
487 }
488
489 if (strcmp(imsi, ms->imsi) == 0)
490 return;
491
492 LOGP(DRLCMAC, LOGL_INFO,
493 "Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
494 ms_tlli(ms), ms->imsi, imsi);
495
496 struct GprsMs *old_ms = bts_ms_by_imsi(ms->bts, imsi);
497 /* Check if we are going to store a different MS object with already
498 existing IMSI. This is probably a bug in code calling this function,
499 since it should take care of this explicitly */
500 if (old_ms) {
501 /* We cannot find ms->ms by IMSI since we know that it has a
502 * different IMSI */
503 OSMO_ASSERT(old_ms != ms);
504
505 LOGPMS(ms, DRLCMAC, LOGL_NOTICE,
506 "IMSI '%s' was already assigned to another "
507 "MS object: TLLI = 0x%08x, that IMSI will be removed\n",
508 imsi, ms_tlli(old_ms));
509
510 ms_merge_and_clear_ms(ms, old_ms);
511 }
512
513
514 osmo_strlcpy(ms->imsi, imsi, sizeof(ms->imsi));
515}
516
Pau Espin Pedrol5deac142021-11-12 18:01:50 +0100517uint16_t ms_paging_group(struct GprsMs *ms)
518{
519 uint16_t pgroup;
520 if (!ms_imsi_is_valid(ms))
521 return 0; /* 000 is the special "all paging" group */
522 if ((pgroup = imsi2paging_group(ms_imsi(ms))) > 999) {
523 LOGPMS(ms, DRLCMAC, LOGL_ERROR, "IMSI to paging group failed!\n");
524 return 0;
525 }
526 return pgroup;
527}
528
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100529void ms_set_ta(struct GprsMs *ms, uint8_t ta_)
530{
531 if (ta_ == ms->ta)
532 return;
533
534 if (gsm48_ta_is_valid(ta_)) {
535 LOGP(DRLCMAC, LOGL_INFO,
536 "Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
537 ms_tlli(ms), ms->ta, ta_);
538 ms->ta = ta_;
539 } else
540 LOGP(DRLCMAC, LOGL_NOTICE,
541 "MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
542 "value %d kept)\n", ms_tlli(ms), ta_, ms->ta);
543}
544
545void ms_set_ms_class(struct GprsMs *ms, uint8_t ms_class_)
546{
547 if (ms_class_ == ms->ms_class)
548 return;
549
550 LOGP(DRLCMAC, LOGL_INFO,
551 "Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
552 ms_tlli(ms), ms->ms_class, ms_class_);
553
554 ms->ms_class = ms_class_;
555}
556
557void ms_set_egprs_ms_class(struct GprsMs *ms, uint8_t ms_class_)
558{
559 if (ms_class_ == ms->egprs_ms_class)
560 return;
561
562 LOGP(DRLCMAC, LOGL_INFO,
563 "Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
564 ms_tlli(ms), ms->egprs_ms_class, ms_class_);
565
566 ms->egprs_ms_class = ms_class_;
567
568 if (!bts_max_mcs_ul(ms->bts) || !bts_max_mcs_dl(ms->bts)) {
569 LOGPMS(ms, DRLCMAC, LOGL_DEBUG,
570 "Avoid enabling EGPRS because use of MCS is disabled: ul=%u dl=%u\n",
571 bts_max_mcs_ul(ms->bts), bts_max_mcs_dl(ms->bts));
572 return;
573 }
574
575 if (mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts))) &&
576 mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts))) &&
577 ms_mode(ms) != EGPRS)
578 {
579 ms_set_mode(ms, EGPRS_GMSK);
580 } else {
581 ms_set_mode(ms, EGPRS);
582 }
583 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Enabled EGPRS, mode %s\n", mode_name(ms_mode(ms)));
584}
585
586void ms_update_error_rate(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, int error_rate)
587{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100588 int64_t now;
589 enum CodingScheme max_cs_dl = ms_max_cs_dl(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100590 OSMO_ASSERT(max_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100591
592 if (error_rate < 0)
593 return;
594
595 now = now_msec();
596
597 /* TODO: Check for TBF direction */
598 /* TODO: Support different CS values for UL and DL */
599
600 ms->nack_rate_dl = error_rate;
601
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100602 if (error_rate > the_pcu->vty.cs_adj_upper_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100603 if (mcs_chan_code(ms->current_cs_dl) > 0) {
604 mcs_dec_kind(&ms->current_cs_dl, ms_mode(ms));
605 LOGP(DRLCMACDL, LOGL_INFO,
606 "MS (IMSI %s): High error rate %d%%, "
607 "reducing CS level to %s\n",
608 ms_imsi(ms), error_rate, mcs_name(ms->current_cs_dl));
609 ms->last_cs_not_low = now;
610 }
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100611 } else if (error_rate < the_pcu->vty.cs_adj_lower_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100612 if (ms->current_cs_dl < max_cs_dl) {
613 if (now - ms->last_cs_not_low > 1000) {
614 mcs_inc_kind(&ms->current_cs_dl, ms_mode(ms));
615
616 LOGP(DRLCMACDL, LOGL_INFO,
617 "MS (IMSI %s): Low error rate %d%%, "
618 "increasing DL CS level to %s\n",
619 ms_imsi(ms), error_rate,
620 mcs_name(ms->current_cs_dl));
621 ms->last_cs_not_low = now;
622 } else {
623 LOGP(DRLCMACDL, LOGL_DEBUG,
624 "MS (IMSI %s): Low error rate %d%%, "
625 "ignored (within blocking period)\n",
626 ms_imsi(ms), error_rate);
627 }
628 }
629 } else {
630 LOGP(DRLCMACDL, LOGL_DEBUG,
631 "MS (IMSI %s): Medium error rate %d%%, ignored\n",
632 ms_imsi(ms), error_rate);
633 ms->last_cs_not_low = now;
634 }
635}
636
637enum CodingScheme ms_max_cs_ul(const struct GprsMs *ms)
638{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100639 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100640 OSMO_ASSERT(ms->bts != NULL);
641
642 if (mcs_is_gprs(ms->current_cs_ul)) {
643 if (!bts_max_cs_ul(ms->bts)) {
644 return CS4;
645 }
646
647 return mcs_get_gprs_by_num(bts_max_cs_ul(ms->bts));
648 }
649
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100650 cs = mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts));
651 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
652 cs = MCS4;
653 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100654}
655
656void ms_set_current_cs_dl(struct GprsMs *ms, enum CodingScheme scheme)
657{
658 ms->current_cs_dl = scheme;
659}
660
661enum CodingScheme ms_max_cs_dl(const struct GprsMs *ms)
662{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100663 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100664 OSMO_ASSERT(ms->bts != NULL);
665
666 if (mcs_is_gprs(ms->current_cs_dl)) {
667 if (!bts_max_cs_dl(ms->bts)) {
668 return CS4;
669 }
670
671 return mcs_get_gprs_by_num(bts_max_cs_dl(ms->bts));
672 }
673
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100674 cs = mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts));
675 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
676 cs = MCS4;
677 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100678}
679
680void ms_update_cs_ul(struct GprsMs *ms, const struct pcu_l1_meas *meas)
681{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100682 enum CodingScheme max_cs_ul = ms_max_cs_ul(ms);
683
684 int old_link_qual;
685 int low;
686 int high;
687 enum CodingScheme new_cs_ul = ms->current_cs_ul;
688 uint8_t current_cs = mcs_chan_code(ms->current_cs_ul);
689
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100690 if (!max_cs_ul) {
691 LOGP(DRLCMACMEAS, LOGL_ERROR,
692 "max_cs_ul cannot be derived (current UL CS: %s)\n",
693 mcs_name(ms->current_cs_ul));
694 return;
695 }
696
697 if (!ms->current_cs_ul) {
698 LOGP(DRLCMACMEAS, LOGL_ERROR,
699 "Unable to update UL (M)CS because it's not set: %s\n",
700 mcs_name(ms->current_cs_ul));
701 return;
702 }
703
704 if (!meas->have_link_qual) {
705 LOGP(DRLCMACMEAS, LOGL_ERROR,
706 "Unable to update UL (M)CS %s because we don't have link quality measurements.\n",
707 mcs_name(ms->current_cs_ul));
708 return;
709 }
710
711 if (mcs_is_gprs(ms->current_cs_ul)) {
712 if (current_cs >= MAX_GPRS_CS)
713 current_cs = MAX_GPRS_CS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100714 low = the_pcu->vty.cs_lqual_ranges[current_cs].low;
715 high = the_pcu->vty.cs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100716 } else if (mcs_is_edge(ms->current_cs_ul)) {
717 if (current_cs >= MAX_EDGE_MCS)
718 current_cs = MAX_EDGE_MCS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100719 low = the_pcu->vty.mcs_lqual_ranges[current_cs].low;
720 high = the_pcu->vty.mcs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100721 } else {
722 LOGP(DRLCMACMEAS, LOGL_ERROR,
723 "Unable to update UL (M)CS because it's neither GPRS nor EDGE: %s\n",
724 mcs_name(ms->current_cs_ul));
725 return;
726 }
727
728 /* To avoid rapid changes of the coding scheme, we also take
729 * the old link quality value into account (if present). */
730 if (ms->l1_meas.have_link_qual)
731 old_link_qual = ms->l1_meas.link_qual;
732 else
733 old_link_qual = meas->link_qual;
734
735 if (meas->link_qual < low && old_link_qual < low)
736 mcs_dec_kind(&new_cs_ul, ms_mode(ms));
737 else if (meas->link_qual > high && old_link_qual > high &&
738 ms->current_cs_ul < max_cs_ul)
739 mcs_inc_kind(&new_cs_ul, ms_mode(ms));
740
741 if (ms->current_cs_ul != new_cs_ul) {
742 LOGPMS(ms, DRLCMACMEAS, LOGL_INFO,
743 "Link quality %ddB (old %ddB) left window [%d, %d], "
744 "modifying uplink CS level: %s -> %s\n",
745 meas->link_qual, old_link_qual,
746 low, high,
747 mcs_name(ms->current_cs_ul), mcs_name(new_cs_ul));
748
749 ms->current_cs_ul = new_cs_ul;
750 }
751}
752
753void ms_update_l1_meas(struct GprsMs *ms, const struct pcu_l1_meas *meas)
754{
755 unsigned i;
756
757 ms_update_cs_ul(ms, meas);
758
759 if (meas->have_rssi)
760 pcu_l1_meas_set_rssi(&ms->l1_meas, meas->rssi);
761 if (meas->have_bto)
762 pcu_l1_meas_set_bto(&ms->l1_meas, meas->bto);
763 if (meas->have_ber)
764 pcu_l1_meas_set_ber(&ms->l1_meas, meas->ber);
765 if (meas->have_link_qual)
766 pcu_l1_meas_set_link_qual(&ms->l1_meas, meas->link_qual);
767
768 if (meas->have_ms_rx_qual)
769 pcu_l1_meas_set_ms_rx_qual(&ms->l1_meas, meas->ms_rx_qual);
770 if (meas->have_ms_c_value)
771 pcu_l1_meas_set_ms_c_value(&ms->l1_meas, meas->ms_c_value);
772 if (meas->have_ms_sign_var)
773 pcu_l1_meas_set_ms_sign_var(&ms->l1_meas, meas->ms_sign_var);
774
775 if (meas->have_ms_i_level) {
776 for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
777 if (meas->ts[i].have_ms_i_level)
778 pcu_l1_meas_set_ms_i_level(&ms->l1_meas, i, meas->ts[i].ms_i_level);
779 else
780 ms->l1_meas.ts[i].have_ms_i_level = 0;
781 }
782 }
783}
784
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100785/* req_mcs_kind acts as a set filter, where EGPRS means any and GPRS is the most restrictive */
786enum CodingScheme ms_current_cs_dl(const struct GprsMs *ms, enum mcs_kind req_mcs_kind)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100787{
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100788 enum CodingScheme orig_cs = ms->current_cs_dl;
789 struct gprs_rlcmac_bts *bts = ms->bts;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100790 size_t unencoded_octets;
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100791 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100792
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100793 /* It could be that a TBF requests a GPRS CS despite the MS currently
794 being upgraded to EGPRS (hence reporting MCS). That could happen
795 because the TBF was created early in the process where we didn't have
796 yet enough information about the MS, and only AFTER it was created we
797 upgraded the MS to be EGPRS capable.
798 As a result, when the MS is queried for the target CS here, we could be
799 returning an MCS despite the current TBF being established as GPRS,
800 but we rather stick to the TBF type we assigned to the MS rather than
801 magically sending EGPRS data blocks to a GPRS TBF.
802 It could also be that the caller requests specific MCS kind
803 explicitly too due to scheduling restrictions (GPRS+EGPRS multiplexing). */
804 if (req_mcs_kind == EGPRS_GMSK && mcs_is_edge(orig_cs) && orig_cs > MCS4) {
805 cs = bts_cs_dl_is_supported(bts, MCS4) ? MCS4 :
806 bts_cs_dl_is_supported(bts, MCS3) ? MCS3 :
807 bts_cs_dl_is_supported(bts, MCS2) ? MCS2 :
808 MCS1;
809 } else if (req_mcs_kind == GPRS && mcs_is_edge(orig_cs)) { /* GPRS */
810 int i;
811 cs = orig_cs > MCS4 ? MCS4 : orig_cs;
812 cs -= (MCS1 - CS1); /* MCSx -> CSx */
813 /* Find suitable CS starting from equivalent MCS which is supported by BTS: */
814 for (i = mcs_chan_code(cs); !bts_cs_dl_is_supported(bts, CS1 + i); i--);
815 OSMO_ASSERT(i >= 0 && i <= 3); /* CS1 is always supported */
816 cs = CS1 + i;
817 } else {
818 cs = orig_cs;
819 }
820
821 if (orig_cs != cs)
822 LOGPMS(ms, DRLCMACDL, LOGL_INFO, "MS (mode=%s) suggests transmitting "
823 "DL %s, downgrade to %s in order to match TBF & scheduler requirements\n",
824 mode_name(ms_mode(ms)), mcs_name(orig_cs), mcs_name(cs));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100825
826 unencoded_octets = llc_queue_octets(&ms->llc_queue);
827
828 /* If the DL TBF is active, add number of unencoded chunk octets */
829 if (ms->dl_tbf)
830 unencoded_octets += llc_chunk_size(tbf_llc((struct gprs_rlcmac_tbf *)ms->dl_tbf));
831
832 /* There are many unencoded octets, don't reduce */
Pau Espin Pedrolad79b852021-01-14 13:20:55 +0100833 if (unencoded_octets >= the_pcu->vty.cs_downgrade_threshold)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100834 return cs;
835
836 /* RF conditions are good, don't reduce */
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100837 if (ms->nack_rate_dl < the_pcu->vty.cs_adj_lower_limit)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100838 return cs;
839
840 /* The throughput would probably be better if the CS level was reduced */
841 mcs_dec_kind(&cs, ms_mode(ms));
842
843 /* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
844 if (cs == CS2)
845 mcs_dec_kind(&cs, ms_mode(ms));
846
847 return cs;
848}
849
850int ms_first_common_ts(const struct GprsMs *ms)
851{
852 if (ms->dl_tbf)
853 return tbf_first_common_ts((struct gprs_rlcmac_tbf *)ms->dl_tbf);
854
855 if (ms->ul_tbf)
856 return tbf_first_common_ts((struct gprs_rlcmac_tbf *)ms->ul_tbf);
857
858 return -1;
859}
860
861uint8_t ms_dl_slots(const struct GprsMs *ms)
862{
863 uint8_t slots = 0;
864
865 if (ms->dl_tbf)
866 slots |= tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
867
868 if (ms->ul_tbf)
869 slots |= tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf);
870
871 return slots;
872}
873
874uint8_t ms_ul_slots(const struct GprsMs *ms)
875{
876 uint8_t slots = 0;
877
878 if (ms->dl_tbf)
879 slots |= tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
880
881 if (ms->ul_tbf)
882 slots |= tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf);
883
884 return slots;
885}
886
887uint8_t ms_current_pacch_slots(const struct GprsMs *ms)
888{
889 uint8_t slots = 0;
890
891 bool is_dl_active = ms->dl_tbf && tbf_is_tfi_assigned((struct gprs_rlcmac_tbf *)ms->dl_tbf);
892 bool is_ul_active = ms->ul_tbf && tbf_is_tfi_assigned((struct gprs_rlcmac_tbf *)ms->ul_tbf);
893
894 if (!is_dl_active && !is_ul_active)
895 return 0;
896
897 /* see TS 44.060, 8.1.1.2.2 */
898 if (is_dl_active && !is_ul_active)
899 slots = tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
900 else if (!is_dl_active && is_ul_active)
901 slots = tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf);
902 else
903 slots = tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf) &
904 tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
905
906 /* Assume a multislot class 1 device */
907 /* TODO: For class 2 devices, this could be removed */
908 slots = pcu_lsb(slots);
909
910 return slots;
911}
912
913void ms_set_reserved_slots(struct GprsMs *ms, struct gprs_rlcmac_trx *trx,
914 uint8_t ul_slots, uint8_t dl_slots)
915{
916 if (ms->current_trx) {
917 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
918 ms->reserved_dl_slots);
919 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
920 ms->reserved_ul_slots);
921 ms->reserved_dl_slots = 0;
922 ms->reserved_ul_slots = 0;
923 }
924 ms->current_trx = trx;
925 if (trx) {
926 ms->reserved_dl_slots = dl_slots;
927 ms->reserved_ul_slots = ul_slots;
928 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
929 ms->reserved_dl_slots);
930 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
931 ms->reserved_ul_slots);
932 }
933}
934
935struct gprs_rlcmac_tbf *ms_tbf(const struct GprsMs *ms, enum gprs_rlcmac_tbf_direction dir)
936{
937 switch (dir) {
938 case GPRS_RLCMAC_DL_TBF: return (struct gprs_rlcmac_tbf *)ms->dl_tbf;
939 case GPRS_RLCMAC_UL_TBF: return (struct gprs_rlcmac_tbf *)ms->ul_tbf;
940 }
941
942 return NULL;
943}
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +0100944
945int ms_nacc_start(struct GprsMs *ms, Packet_Cell_Change_Notification_t *notif)
946{
947 if (!ms->nacc)
948 ms->nacc = nacc_fsm_alloc(ms);
949 if (!ms->nacc)
950 return -EINVAL;
951 return osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_RX_CELL_CHG_NOTIFICATION, notif);
952}
953
954bool ms_nacc_rts(const struct GprsMs *ms)
955{
956 if (!ms->nacc)
957 return false;
958 if (ms->nacc->fi->state == NACC_ST_TX_NEIGHBOUR_DATA ||
959 ms->nacc->fi->state == NACC_ST_TX_CELL_CHG_CONTINUE)
960 return true;
961 return false;
962}
963
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +0100964struct 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 +0100965{
966 int rc;
967 struct nacc_ev_create_rlcmac_msg_ctx data_ctx;
968
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +0100969 data_ctx = (struct nacc_ev_create_rlcmac_msg_ctx) {
970 .tbf = tbf,
971 .fn = fn,
972 .ts = ts,
973 .msg = NULL,
974 };
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +0100975
976 rc = osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_CREATE_RLCMAC_MSG, &data_ctx);
977 if (rc != 0 || !data_ctx.msg)
978 return NULL;
979 return data_ctx.msg;
980}