blob: 6daab3194b5cc28d0aeb0af5a3de3d875ee9d452 [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
145 if (osmo_timer_pending(&ms->timer))
146 osmo_timer_del(&ms->timer);
147
148 if (ms->ul_tbf) {
149 tbf_set_ms((struct gprs_rlcmac_tbf *)ms->ul_tbf, NULL);
150 ms->ul_tbf = NULL;
151 }
152
153 if (ms->dl_tbf) {
154 tbf_set_ms((struct gprs_rlcmac_tbf *)ms->dl_tbf, NULL);
155 ms->dl_tbf = NULL;
156 }
157
158 llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
159 struct gprs_rlcmac_tbf *tbf = (struct gprs_rlcmac_tbf *)pos->entry;
160 tbf_set_ms(tbf, NULL);
161 }
162
163 llc_queue_clear(&ms->llc_queue, ms->bts);
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100164
165 if (ms->ctrs)
166 rate_ctr_group_free(ms->ctrs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100167 return 0;
168}
169
170
171void ms_set_callback(struct GprsMs *ms, struct gpr_ms_callback *cb)
172{
173 if (cb)
174 ms->cb = *cb;
175 else
176 ms->cb = gprs_default_cb;
177}
178
179static void ms_update_status(struct GprsMs *ms)
180{
181 if (ms->ref > 0)
182 return;
183
184 if (ms_is_idle(ms) && !ms->is_idle) {
185 ms->is_idle = true;
186 ms->cb.ms_idle(ms);
187 /* this can be deleted by now, do not access it */
188 return;
189 }
190
191 if (!ms_is_idle(ms) && ms->is_idle) {
192 ms->is_idle = false;
193 ms->cb.ms_active(ms);
194 }
195}
196
197struct GprsMs *ms_ref(struct GprsMs *ms)
198{
199 ms->ref += 1;
200 return ms;
201}
202
203void ms_unref(struct GprsMs *ms)
204{
205 OSMO_ASSERT(ms->ref >= 0);
206 ms->ref -= 1;
207 if (ms->ref == 0)
208 ms_update_status(ms);
209}
210
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100211static void ms_release_timer_start(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100212{
213 if (ms->delay == 0)
214 return;
215
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100216 LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Schedule MS release in %u secs\n", ms->delay);
217
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100218 if (!ms->timer.data)
219 ms->timer.data = ms_ref(ms);
220
221 osmo_timer_schedule(&ms->timer, ms->delay, 0);
222}
223
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100224static void ms_release_timer_stop(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100225{
226 if (!ms->timer.data)
227 return;
228
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100229 LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Cancel scheduled MS release\n");
230
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100231 osmo_timer_del(&ms->timer);
232 ms->timer.data = NULL;
233 ms_unref(ms);
234}
235
236void ms_set_mode(struct GprsMs *ms, enum mcs_kind mode)
237{
238 ms->mode = mode;
239
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100240 switch (ms->mode) {
241 case GPRS:
242 if (!mcs_is_gprs(ms->current_cs_ul)) {
243 ms->current_cs_ul = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100244 ms->bts->initial_cs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100245 if (!mcs_is_valid(ms->current_cs_ul))
246 ms->current_cs_ul = CS1;
247 }
248 if (!mcs_is_gprs(ms->current_cs_dl)) {
249 ms->current_cs_dl = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100250 ms->bts->initial_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100251 if (!mcs_is_valid(ms->current_cs_dl))
252 ms->current_cs_dl = CS1;
253 }
254 break;
255
256 case EGPRS_GMSK:
Pau Espin Pedrol7bb8cd62021-01-25 15:08:35 +0100257 if (!mcs_is_edge_gmsk(ms->current_cs_ul)) {
258 ms->current_cs_ul = mcs_get_egprs_by_num(
259 ms->bts->initial_mcs_ul);
260 if (!mcs_is_valid(ms->current_cs_ul))
261 ms->current_cs_ul = MCS1;
262 }
263 if (!mcs_is_edge_gmsk(ms->current_cs_dl)) {
264 ms->current_cs_dl = mcs_get_egprs_by_num(
265 ms->bts->initial_mcs_dl);
266 if (!mcs_is_valid(ms->current_cs_dl))
267 ms->current_cs_dl = MCS1;
268 }
269 break;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100270 case EGPRS:
271 if (!mcs_is_edge(ms->current_cs_ul)) {
272 ms->current_cs_ul = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100273 ms->bts->initial_mcs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100274 if (!mcs_is_valid(ms->current_cs_ul))
275 ms->current_cs_ul = MCS1;
276 }
277 if (!mcs_is_edge(ms->current_cs_dl)) {
278 ms->current_cs_dl = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100279 ms->bts->initial_mcs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100280 if (!mcs_is_valid(ms->current_cs_dl))
281 ms->current_cs_dl = MCS1;
282 }
283 break;
284 }
285}
286
287static void ms_attach_ul_tbf(struct GprsMs *ms, struct gprs_rlcmac_ul_tbf *tbf)
288{
289 if (ms->ul_tbf == tbf)
290 return;
291
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100292 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 +0100293
294 ms_ref(ms);
295
296 if (ms->ul_tbf)
297 llist_add_tail(tbf_ms_list((struct gprs_rlcmac_tbf *)ms->ul_tbf), &ms->old_tbfs);
298
299 ms->ul_tbf = tbf;
300
301 if (tbf)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100302 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100303
304 ms_unref(ms);
305}
306
307static void ms_attach_dl_tbf(struct GprsMs *ms, struct gprs_rlcmac_dl_tbf *tbf)
308{
309 if (ms->dl_tbf == tbf)
310 return;
311
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100312 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 +0100313
314 ms_ref(ms);
315
316 if (ms->dl_tbf)
317 llist_add_tail(tbf_ms_list((struct gprs_rlcmac_tbf *)ms->dl_tbf), &ms->old_tbfs);
318
319 ms->dl_tbf = tbf;
320
321 if (tbf)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100322 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100323
324 ms_unref(ms);
325}
326
327void ms_attach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
328{
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200329 if (tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100330 ms_attach_dl_tbf(ms, as_dl_tbf(tbf));
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200331 else
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100332 ms_attach_ul_tbf(ms, as_ul_tbf(tbf));
333}
334
335void ms_detach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
336{
337 if (tbf == (struct gprs_rlcmac_tbf *)(ms->ul_tbf)) {
338 ms->ul_tbf = NULL;
339 } else if (tbf == (struct gprs_rlcmac_tbf *)(ms->dl_tbf)) {
340 ms->dl_tbf = NULL;
341 } else {
342 bool found = false;
343
344 struct llist_item *pos, *tmp;
345 llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
346 struct gprs_rlcmac_tbf *tmp_tbf = (struct gprs_rlcmac_tbf *)pos->entry;
347 if (tmp_tbf == tbf) {
348 llist_del(&pos->list);
349 found = true;
350 break;
351 }
352 }
353
354 /* Protect against recursive calls via set_ms() */
355 if (!found)
356 return;
357 }
358
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100359 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Detaching TBF: %s\n",
360 tbf_name(tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100361
362 if (tbf_ms(tbf) == ms)
363 tbf_set_ms(tbf, NULL);
364
365 if (!ms->dl_tbf && !ms->ul_tbf) {
366 ms_set_reserved_slots(ms, NULL, 0, 0);
367
368 if (ms_tlli(ms) != 0)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100369 ms_release_timer_start(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100370 }
371
372 ms_update_status(ms);
373}
374
375void ms_reset(struct GprsMs *ms)
376{
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100377 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Clearing MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100378
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100379 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100380
381 ms->tlli = GSM_RESERVED_TMSI;
382 ms->new_dl_tlli = ms->tlli;
383 ms->new_ul_tlli = ms->tlli;
384 ms->imsi[0] = '\0';
385}
386
387static void ms_merge_old_ms(struct GprsMs *ms, struct GprsMs *old_ms)
388{
389 OSMO_ASSERT(old_ms != ms);
390
391 if (strlen(ms_imsi(ms)) == 0 && strlen(ms_imsi(old_ms)) != 0)
392 osmo_strlcpy(ms->imsi, ms_imsi(old_ms), sizeof(ms->imsi));
393
394 if (!ms_ms_class(ms) && ms_ms_class(old_ms))
395 ms_set_ms_class(ms, ms_ms_class(old_ms));
396
397 if (!ms_egprs_ms_class(ms) && ms_egprs_ms_class(old_ms))
398 ms_set_egprs_ms_class(ms, ms_egprs_ms_class(old_ms));
399
400 llc_queue_move_and_merge(&ms->llc_queue, &old_ms->llc_queue);
401
402 ms_reset(old_ms);
403}
404
405void ms_merge_and_clear_ms(struct GprsMs *ms, struct GprsMs *old_ms)
406{
407 OSMO_ASSERT(old_ms != ms);
408
409 ms_ref(old_ms);
410
411 /* Clean up the old MS object */
412 /* TODO: Use timer? */
413 if (ms_ul_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms), T_MAX))
414 tbf_free((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms));
415 if (ms_dl_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms), T_MAX))
416 tbf_free((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms));
417
418 ms_merge_old_ms(ms, old_ms);
419
420 ms_unref(old_ms);
421}
422
423void 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
447bool ms_confirm_tlli(struct GprsMs *ms, uint32_t tlli)
448{
449 if (tlli == ms->tlli || tlli == ms->new_dl_tlli)
450 return false;
451
452 if (tlli != ms->new_ul_tlli) {
453 /* The MS has not sent a message with the new TLLI, which may
454 * happen according to the spec [TODO: add reference]. */
455
456 LOGP(DRLCMAC, LOGL_INFO,
457 "The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
458 "partly confirmed\n", tlli);
459 /* Use the network's idea of TLLI as candidate, this does not
460 * change the result value of tlli() */
461 ms->new_dl_tlli = tlli;
462 return false;
463 }
464
465 LOGP(DRLCMAC, LOGL_INFO,
466 "Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
467
468 ms->tlli = tlli;
469 ms->new_dl_tlli = GSM_RESERVED_TMSI;
470 ms->new_ul_tlli = GSM_RESERVED_TMSI;
471
472 return true;
473}
474
475void ms_set_imsi(struct GprsMs *ms, const char *imsi)
476{
477 if (!imsi) {
478 LOGP(DRLCMAC, LOGL_ERROR, "Expected IMSI!\n");
479 return;
480 }
481
482 if (imsi[0] && strlen(imsi) < 3) {
483 LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI '%s'!\n",
484 imsi);
485 return;
486 }
487
488 if (strcmp(imsi, ms->imsi) == 0)
489 return;
490
491 LOGP(DRLCMAC, LOGL_INFO,
492 "Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
493 ms_tlli(ms), ms->imsi, imsi);
494
495 struct GprsMs *old_ms = bts_ms_by_imsi(ms->bts, imsi);
496 /* Check if we are going to store a different MS object with already
497 existing IMSI. This is probably a bug in code calling this function,
498 since it should take care of this explicitly */
499 if (old_ms) {
500 /* We cannot find ms->ms by IMSI since we know that it has a
501 * different IMSI */
502 OSMO_ASSERT(old_ms != ms);
503
504 LOGPMS(ms, DRLCMAC, LOGL_NOTICE,
505 "IMSI '%s' was already assigned to another "
506 "MS object: TLLI = 0x%08x, that IMSI will be removed\n",
507 imsi, ms_tlli(old_ms));
508
509 ms_merge_and_clear_ms(ms, old_ms);
510 }
511
512
513 osmo_strlcpy(ms->imsi, imsi, sizeof(ms->imsi));
514}
515
Pau Espin Pedrol5deac142021-11-12 18:01:50 +0100516uint16_t ms_paging_group(struct GprsMs *ms)
517{
518 uint16_t pgroup;
519 if (!ms_imsi_is_valid(ms))
520 return 0; /* 000 is the special "all paging" group */
521 if ((pgroup = imsi2paging_group(ms_imsi(ms))) > 999) {
522 LOGPMS(ms, DRLCMAC, LOGL_ERROR, "IMSI to paging group failed!\n");
523 return 0;
524 }
525 return pgroup;
526}
527
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100528void ms_set_ta(struct GprsMs *ms, uint8_t ta_)
529{
530 if (ta_ == ms->ta)
531 return;
532
533 if (gsm48_ta_is_valid(ta_)) {
534 LOGP(DRLCMAC, LOGL_INFO,
535 "Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
536 ms_tlli(ms), ms->ta, ta_);
537 ms->ta = ta_;
538 } else
539 LOGP(DRLCMAC, LOGL_NOTICE,
540 "MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
541 "value %d kept)\n", ms_tlli(ms), ta_, ms->ta);
542}
543
544void ms_set_ms_class(struct GprsMs *ms, uint8_t ms_class_)
545{
546 if (ms_class_ == ms->ms_class)
547 return;
548
549 LOGP(DRLCMAC, LOGL_INFO,
550 "Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
551 ms_tlli(ms), ms->ms_class, ms_class_);
552
553 ms->ms_class = ms_class_;
554}
555
556void ms_set_egprs_ms_class(struct GprsMs *ms, uint8_t ms_class_)
557{
558 if (ms_class_ == ms->egprs_ms_class)
559 return;
560
561 LOGP(DRLCMAC, LOGL_INFO,
562 "Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
563 ms_tlli(ms), ms->egprs_ms_class, ms_class_);
564
565 ms->egprs_ms_class = ms_class_;
566
567 if (!bts_max_mcs_ul(ms->bts) || !bts_max_mcs_dl(ms->bts)) {
568 LOGPMS(ms, DRLCMAC, LOGL_DEBUG,
569 "Avoid enabling EGPRS because use of MCS is disabled: ul=%u dl=%u\n",
570 bts_max_mcs_ul(ms->bts), bts_max_mcs_dl(ms->bts));
571 return;
572 }
573
574 if (mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts))) &&
575 mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts))) &&
576 ms_mode(ms) != EGPRS)
577 {
578 ms_set_mode(ms, EGPRS_GMSK);
579 } else {
580 ms_set_mode(ms, EGPRS);
581 }
582 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Enabled EGPRS, mode %s\n", mode_name(ms_mode(ms)));
583}
584
585void ms_update_error_rate(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, int error_rate)
586{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100587 int64_t now;
588 enum CodingScheme max_cs_dl = ms_max_cs_dl(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100589 OSMO_ASSERT(max_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100590
591 if (error_rate < 0)
592 return;
593
594 now = now_msec();
595
596 /* TODO: Check for TBF direction */
597 /* TODO: Support different CS values for UL and DL */
598
599 ms->nack_rate_dl = error_rate;
600
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100601 if (error_rate > the_pcu->vty.cs_adj_upper_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100602 if (mcs_chan_code(ms->current_cs_dl) > 0) {
603 mcs_dec_kind(&ms->current_cs_dl, ms_mode(ms));
604 LOGP(DRLCMACDL, LOGL_INFO,
605 "MS (IMSI %s): High error rate %d%%, "
606 "reducing CS level to %s\n",
607 ms_imsi(ms), error_rate, mcs_name(ms->current_cs_dl));
608 ms->last_cs_not_low = now;
609 }
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100610 } else if (error_rate < the_pcu->vty.cs_adj_lower_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100611 if (ms->current_cs_dl < max_cs_dl) {
612 if (now - ms->last_cs_not_low > 1000) {
613 mcs_inc_kind(&ms->current_cs_dl, ms_mode(ms));
614
615 LOGP(DRLCMACDL, LOGL_INFO,
616 "MS (IMSI %s): Low error rate %d%%, "
617 "increasing DL CS level to %s\n",
618 ms_imsi(ms), error_rate,
619 mcs_name(ms->current_cs_dl));
620 ms->last_cs_not_low = now;
621 } else {
622 LOGP(DRLCMACDL, LOGL_DEBUG,
623 "MS (IMSI %s): Low error rate %d%%, "
624 "ignored (within blocking period)\n",
625 ms_imsi(ms), error_rate);
626 }
627 }
628 } else {
629 LOGP(DRLCMACDL, LOGL_DEBUG,
630 "MS (IMSI %s): Medium error rate %d%%, ignored\n",
631 ms_imsi(ms), error_rate);
632 ms->last_cs_not_low = now;
633 }
634}
635
636enum CodingScheme ms_max_cs_ul(const struct GprsMs *ms)
637{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100638 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100639 OSMO_ASSERT(ms->bts != NULL);
640
641 if (mcs_is_gprs(ms->current_cs_ul)) {
642 if (!bts_max_cs_ul(ms->bts)) {
643 return CS4;
644 }
645
646 return mcs_get_gprs_by_num(bts_max_cs_ul(ms->bts));
647 }
648
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100649 cs = mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts));
650 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
651 cs = MCS4;
652 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100653}
654
655void ms_set_current_cs_dl(struct GprsMs *ms, enum CodingScheme scheme)
656{
657 ms->current_cs_dl = scheme;
658}
659
660enum CodingScheme ms_max_cs_dl(const struct GprsMs *ms)
661{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100662 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100663 OSMO_ASSERT(ms->bts != NULL);
664
665 if (mcs_is_gprs(ms->current_cs_dl)) {
666 if (!bts_max_cs_dl(ms->bts)) {
667 return CS4;
668 }
669
670 return mcs_get_gprs_by_num(bts_max_cs_dl(ms->bts));
671 }
672
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100673 cs = mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts));
674 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
675 cs = MCS4;
676 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100677}
678
679void ms_update_cs_ul(struct GprsMs *ms, const struct pcu_l1_meas *meas)
680{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100681 enum CodingScheme max_cs_ul = ms_max_cs_ul(ms);
682
683 int old_link_qual;
684 int low;
685 int high;
686 enum CodingScheme new_cs_ul = ms->current_cs_ul;
687 uint8_t current_cs = mcs_chan_code(ms->current_cs_ul);
688
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100689 if (!max_cs_ul) {
690 LOGP(DRLCMACMEAS, LOGL_ERROR,
691 "max_cs_ul cannot be derived (current UL CS: %s)\n",
692 mcs_name(ms->current_cs_ul));
693 return;
694 }
695
696 if (!ms->current_cs_ul) {
697 LOGP(DRLCMACMEAS, LOGL_ERROR,
698 "Unable to update UL (M)CS because it's not set: %s\n",
699 mcs_name(ms->current_cs_ul));
700 return;
701 }
702
703 if (!meas->have_link_qual) {
704 LOGP(DRLCMACMEAS, LOGL_ERROR,
705 "Unable to update UL (M)CS %s because we don't have link quality measurements.\n",
706 mcs_name(ms->current_cs_ul));
707 return;
708 }
709
710 if (mcs_is_gprs(ms->current_cs_ul)) {
711 if (current_cs >= MAX_GPRS_CS)
712 current_cs = MAX_GPRS_CS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100713 low = the_pcu->vty.cs_lqual_ranges[current_cs].low;
714 high = the_pcu->vty.cs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100715 } else if (mcs_is_edge(ms->current_cs_ul)) {
716 if (current_cs >= MAX_EDGE_MCS)
717 current_cs = MAX_EDGE_MCS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100718 low = the_pcu->vty.mcs_lqual_ranges[current_cs].low;
719 high = the_pcu->vty.mcs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100720 } else {
721 LOGP(DRLCMACMEAS, LOGL_ERROR,
722 "Unable to update UL (M)CS because it's neither GPRS nor EDGE: %s\n",
723 mcs_name(ms->current_cs_ul));
724 return;
725 }
726
727 /* To avoid rapid changes of the coding scheme, we also take
728 * the old link quality value into account (if present). */
729 if (ms->l1_meas.have_link_qual)
730 old_link_qual = ms->l1_meas.link_qual;
731 else
732 old_link_qual = meas->link_qual;
733
734 if (meas->link_qual < low && old_link_qual < low)
735 mcs_dec_kind(&new_cs_ul, ms_mode(ms));
736 else if (meas->link_qual > high && old_link_qual > high &&
737 ms->current_cs_ul < max_cs_ul)
738 mcs_inc_kind(&new_cs_ul, ms_mode(ms));
739
740 if (ms->current_cs_ul != new_cs_ul) {
741 LOGPMS(ms, DRLCMACMEAS, LOGL_INFO,
742 "Link quality %ddB (old %ddB) left window [%d, %d], "
743 "modifying uplink CS level: %s -> %s\n",
744 meas->link_qual, old_link_qual,
745 low, high,
746 mcs_name(ms->current_cs_ul), mcs_name(new_cs_ul));
747
748 ms->current_cs_ul = new_cs_ul;
749 }
750}
751
752void ms_update_l1_meas(struct GprsMs *ms, const struct pcu_l1_meas *meas)
753{
754 unsigned i;
755
756 ms_update_cs_ul(ms, meas);
757
758 if (meas->have_rssi)
759 pcu_l1_meas_set_rssi(&ms->l1_meas, meas->rssi);
760 if (meas->have_bto)
761 pcu_l1_meas_set_bto(&ms->l1_meas, meas->bto);
762 if (meas->have_ber)
763 pcu_l1_meas_set_ber(&ms->l1_meas, meas->ber);
764 if (meas->have_link_qual)
765 pcu_l1_meas_set_link_qual(&ms->l1_meas, meas->link_qual);
766
767 if (meas->have_ms_rx_qual)
768 pcu_l1_meas_set_ms_rx_qual(&ms->l1_meas, meas->ms_rx_qual);
769 if (meas->have_ms_c_value)
770 pcu_l1_meas_set_ms_c_value(&ms->l1_meas, meas->ms_c_value);
771 if (meas->have_ms_sign_var)
772 pcu_l1_meas_set_ms_sign_var(&ms->l1_meas, meas->ms_sign_var);
773
774 if (meas->have_ms_i_level) {
775 for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
776 if (meas->ts[i].have_ms_i_level)
777 pcu_l1_meas_set_ms_i_level(&ms->l1_meas, i, meas->ts[i].ms_i_level);
778 else
779 ms->l1_meas.ts[i].have_ms_i_level = 0;
780 }
781 }
782}
783
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100784/* req_mcs_kind acts as a set filter, where EGPRS means any and GPRS is the most restrictive */
785enum CodingScheme ms_current_cs_dl(const struct GprsMs *ms, enum mcs_kind req_mcs_kind)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100786{
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100787 enum CodingScheme orig_cs = ms->current_cs_dl;
788 struct gprs_rlcmac_bts *bts = ms->bts;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100789 size_t unencoded_octets;
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100790 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100791
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100792 /* It could be that a TBF requests a GPRS CS despite the MS currently
793 being upgraded to EGPRS (hence reporting MCS). That could happen
794 because the TBF was created early in the process where we didn't have
795 yet enough information about the MS, and only AFTER it was created we
796 upgraded the MS to be EGPRS capable.
797 As a result, when the MS is queried for the target CS here, we could be
798 returning an MCS despite the current TBF being established as GPRS,
799 but we rather stick to the TBF type we assigned to the MS rather than
800 magically sending EGPRS data blocks to a GPRS TBF.
801 It could also be that the caller requests specific MCS kind
802 explicitly too due to scheduling restrictions (GPRS+EGPRS multiplexing). */
803 if (req_mcs_kind == EGPRS_GMSK && mcs_is_edge(orig_cs) && orig_cs > MCS4) {
804 cs = bts_cs_dl_is_supported(bts, MCS4) ? MCS4 :
805 bts_cs_dl_is_supported(bts, MCS3) ? MCS3 :
806 bts_cs_dl_is_supported(bts, MCS2) ? MCS2 :
807 MCS1;
808 } else if (req_mcs_kind == GPRS && mcs_is_edge(orig_cs)) { /* GPRS */
809 int i;
810 cs = orig_cs > MCS4 ? MCS4 : orig_cs;
811 cs -= (MCS1 - CS1); /* MCSx -> CSx */
812 /* Find suitable CS starting from equivalent MCS which is supported by BTS: */
813 for (i = mcs_chan_code(cs); !bts_cs_dl_is_supported(bts, CS1 + i); i--);
814 OSMO_ASSERT(i >= 0 && i <= 3); /* CS1 is always supported */
815 cs = CS1 + i;
816 } else {
817 cs = orig_cs;
818 }
819
820 if (orig_cs != cs)
821 LOGPMS(ms, DRLCMACDL, LOGL_INFO, "MS (mode=%s) suggests transmitting "
822 "DL %s, downgrade to %s in order to match TBF & scheduler requirements\n",
823 mode_name(ms_mode(ms)), mcs_name(orig_cs), mcs_name(cs));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100824
825 unencoded_octets = llc_queue_octets(&ms->llc_queue);
826
827 /* If the DL TBF is active, add number of unencoded chunk octets */
828 if (ms->dl_tbf)
829 unencoded_octets += llc_chunk_size(tbf_llc((struct gprs_rlcmac_tbf *)ms->dl_tbf));
830
831 /* There are many unencoded octets, don't reduce */
Pau Espin Pedrolad79b852021-01-14 13:20:55 +0100832 if (unencoded_octets >= the_pcu->vty.cs_downgrade_threshold)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100833 return cs;
834
835 /* RF conditions are good, don't reduce */
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100836 if (ms->nack_rate_dl < the_pcu->vty.cs_adj_lower_limit)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100837 return cs;
838
839 /* The throughput would probably be better if the CS level was reduced */
840 mcs_dec_kind(&cs, ms_mode(ms));
841
842 /* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
843 if (cs == CS2)
844 mcs_dec_kind(&cs, ms_mode(ms));
845
846 return cs;
847}
848
849int ms_first_common_ts(const struct GprsMs *ms)
850{
851 if (ms->dl_tbf)
852 return tbf_first_common_ts((struct gprs_rlcmac_tbf *)ms->dl_tbf);
853
854 if (ms->ul_tbf)
855 return tbf_first_common_ts((struct gprs_rlcmac_tbf *)ms->ul_tbf);
856
857 return -1;
858}
859
860uint8_t ms_dl_slots(const struct GprsMs *ms)
861{
862 uint8_t slots = 0;
863
864 if (ms->dl_tbf)
865 slots |= tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
866
867 if (ms->ul_tbf)
868 slots |= tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf);
869
870 return slots;
871}
872
873uint8_t ms_ul_slots(const struct GprsMs *ms)
874{
875 uint8_t slots = 0;
876
877 if (ms->dl_tbf)
878 slots |= tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
879
880 if (ms->ul_tbf)
881 slots |= tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf);
882
883 return slots;
884}
885
886uint8_t ms_current_pacch_slots(const struct GprsMs *ms)
887{
888 uint8_t slots = 0;
889
890 bool is_dl_active = ms->dl_tbf && tbf_is_tfi_assigned((struct gprs_rlcmac_tbf *)ms->dl_tbf);
891 bool is_ul_active = ms->ul_tbf && tbf_is_tfi_assigned((struct gprs_rlcmac_tbf *)ms->ul_tbf);
892
893 if (!is_dl_active && !is_ul_active)
894 return 0;
895
896 /* see TS 44.060, 8.1.1.2.2 */
897 if (is_dl_active && !is_ul_active)
898 slots = tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
899 else if (!is_dl_active && is_ul_active)
900 slots = tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf);
901 else
902 slots = tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf) &
903 tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
904
905 /* Assume a multislot class 1 device */
906 /* TODO: For class 2 devices, this could be removed */
907 slots = pcu_lsb(slots);
908
909 return slots;
910}
911
912void ms_set_reserved_slots(struct GprsMs *ms, struct gprs_rlcmac_trx *trx,
913 uint8_t ul_slots, uint8_t dl_slots)
914{
915 if (ms->current_trx) {
916 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
917 ms->reserved_dl_slots);
918 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
919 ms->reserved_ul_slots);
920 ms->reserved_dl_slots = 0;
921 ms->reserved_ul_slots = 0;
922 }
923 ms->current_trx = trx;
924 if (trx) {
925 ms->reserved_dl_slots = dl_slots;
926 ms->reserved_ul_slots = ul_slots;
927 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
928 ms->reserved_dl_slots);
929 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
930 ms->reserved_ul_slots);
931 }
932}
933
934struct gprs_rlcmac_tbf *ms_tbf(const struct GprsMs *ms, enum gprs_rlcmac_tbf_direction dir)
935{
936 switch (dir) {
937 case GPRS_RLCMAC_DL_TBF: return (struct gprs_rlcmac_tbf *)ms->dl_tbf;
938 case GPRS_RLCMAC_UL_TBF: return (struct gprs_rlcmac_tbf *)ms->ul_tbf;
939 }
940
941 return NULL;
942}
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +0100943
944int ms_nacc_start(struct GprsMs *ms, Packet_Cell_Change_Notification_t *notif)
945{
946 if (!ms->nacc)
947 ms->nacc = nacc_fsm_alloc(ms);
948 if (!ms->nacc)
949 return -EINVAL;
950 return osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_RX_CELL_CHG_NOTIFICATION, notif);
951}
952
953bool ms_nacc_rts(const struct GprsMs *ms)
954{
955 if (!ms->nacc)
956 return false;
957 if (ms->nacc->fi->state == NACC_ST_TX_NEIGHBOUR_DATA ||
958 ms->nacc->fi->state == NACC_ST_TX_CELL_CHG_CONTINUE)
959 return true;
960 return false;
961}
962
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +0100963struct 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 +0100964{
965 int rc;
966 struct nacc_ev_create_rlcmac_msg_ctx data_ctx;
967
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +0100968 data_ctx = (struct nacc_ev_create_rlcmac_msg_ctx) {
969 .tbf = tbf,
970 .fn = fn,
971 .ts = ts,
972 .msg = NULL,
973 };
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +0100974
975 rc = osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_CREATE_RLCMAC_MSG, &data_ctx);
976 if (rc != 0 || !data_ctx.msg)
977 return NULL;
978 return data_ctx.msg;
979}