blob: c10eda7e3c7360cb868c86b11b303ce529e5bfe5 [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);
93
94 talloc_set_destructor(ms, ms_talloc_destructor);
95
96 ms->bts = bts;
97 ms->cb = gprs_default_cb;
98 ms->tlli = tlli;
99 ms->new_ul_tlli = GSM_RESERVED_TMSI;
100 ms->new_dl_tlli = GSM_RESERVED_TMSI;
101 ms->ta = GSM48_TA_INVALID;
102 ms->current_cs_ul = UNKNOWN;
103 ms->current_cs_dl = UNKNOWN;
104 ms->is_idle = true;
105 INIT_LLIST_HEAD(&ms->list);
106 INIT_LLIST_HEAD(&ms->old_tbfs);
107
108 int codel_interval = LLC_CODEL_USE_DEFAULT;
109
110 LOGP(DRLCMAC, LOGL_INFO, "Creating MS object, TLLI = 0x%08x\n", tlli);
111
112 ms->imsi[0] = '\0';
113 memset(&ms->timer, 0, sizeof(ms->timer));
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100114 ms->timer.cb = ms_release_timer_cb;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100115 llc_queue_init(&ms->llc_queue);
116
117 ms_set_mode(ms, GPRS);
118
119 if (ms->bts)
Pau Espin Pedrolf473ec92021-01-14 14:45:14 +0100120 codel_interval = the_pcu->vty.llc_codel_interval_msec;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100121
122 if (codel_interval) {
123 if (codel_interval == LLC_CODEL_USE_DEFAULT)
124 codel_interval = GPRS_CODEL_SLOW_INTERVAL_MS;
125 ms->codel_state = talloc(ms, struct gprs_codel);
126 gprs_codel_init(ms->codel_state);
127 gprs_codel_set_interval(ms->codel_state, codel_interval);
128 }
129 ms->last_cs_not_low = now_msec();
130 ms->app_info_pending = false;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100131
132 ms->ctrs = rate_ctr_group_alloc(ms, &ms_ctrg_desc, next_ms_ctr_group_id++);
133 if (!ms->ctrs)
134 goto free_ret;
135
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100136 return ms;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100137free_ret:
138 talloc_free(ms);
139 return NULL;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100140}
141
142static int ms_talloc_destructor(struct GprsMs *ms)
143{
144 struct llist_item *pos, *tmp;
145
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100146 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Destroying MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100147
148 ms_set_reserved_slots(ms, NULL, 0, 0);
149
150 if (osmo_timer_pending(&ms->timer))
151 osmo_timer_del(&ms->timer);
152
153 if (ms->ul_tbf) {
154 tbf_set_ms((struct gprs_rlcmac_tbf *)ms->ul_tbf, NULL);
155 ms->ul_tbf = NULL;
156 }
157
158 if (ms->dl_tbf) {
159 tbf_set_ms((struct gprs_rlcmac_tbf *)ms->dl_tbf, NULL);
160 ms->dl_tbf = NULL;
161 }
162
163 llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
164 struct gprs_rlcmac_tbf *tbf = (struct gprs_rlcmac_tbf *)pos->entry;
165 tbf_set_ms(tbf, NULL);
166 }
167
168 llc_queue_clear(&ms->llc_queue, ms->bts);
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100169
170 if (ms->ctrs)
171 rate_ctr_group_free(ms->ctrs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100172 return 0;
173}
174
175
176void ms_set_callback(struct GprsMs *ms, struct gpr_ms_callback *cb)
177{
178 if (cb)
179 ms->cb = *cb;
180 else
181 ms->cb = gprs_default_cb;
182}
183
184static void ms_update_status(struct GprsMs *ms)
185{
186 if (ms->ref > 0)
187 return;
188
189 if (ms_is_idle(ms) && !ms->is_idle) {
190 ms->is_idle = true;
191 ms->cb.ms_idle(ms);
192 /* this can be deleted by now, do not access it */
193 return;
194 }
195
196 if (!ms_is_idle(ms) && ms->is_idle) {
197 ms->is_idle = false;
198 ms->cb.ms_active(ms);
199 }
200}
201
202struct GprsMs *ms_ref(struct GprsMs *ms)
203{
204 ms->ref += 1;
205 return ms;
206}
207
208void ms_unref(struct GprsMs *ms)
209{
210 OSMO_ASSERT(ms->ref >= 0);
211 ms->ref -= 1;
212 if (ms->ref == 0)
213 ms_update_status(ms);
214}
215
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100216static void ms_release_timer_start(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100217{
218 if (ms->delay == 0)
219 return;
220
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100221 LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Schedule MS release in %u secs\n", ms->delay);
222
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100223 if (!ms->timer.data)
224 ms->timer.data = ms_ref(ms);
225
226 osmo_timer_schedule(&ms->timer, ms->delay, 0);
227}
228
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100229static void ms_release_timer_stop(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100230{
231 if (!ms->timer.data)
232 return;
233
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100234 LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Cancel scheduled MS release\n");
235
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100236 osmo_timer_del(&ms->timer);
237 ms->timer.data = NULL;
238 ms_unref(ms);
239}
240
241void ms_set_mode(struct GprsMs *ms, enum mcs_kind mode)
242{
243 ms->mode = mode;
244
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100245 switch (ms->mode) {
246 case GPRS:
247 if (!mcs_is_gprs(ms->current_cs_ul)) {
248 ms->current_cs_ul = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100249 ms->bts->initial_cs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100250 if (!mcs_is_valid(ms->current_cs_ul))
251 ms->current_cs_ul = CS1;
252 }
253 if (!mcs_is_gprs(ms->current_cs_dl)) {
254 ms->current_cs_dl = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100255 ms->bts->initial_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100256 if (!mcs_is_valid(ms->current_cs_dl))
257 ms->current_cs_dl = CS1;
258 }
259 break;
260
261 case EGPRS_GMSK:
Pau Espin Pedrol7bb8cd62021-01-25 15:08:35 +0100262 if (!mcs_is_edge_gmsk(ms->current_cs_ul)) {
263 ms->current_cs_ul = mcs_get_egprs_by_num(
264 ms->bts->initial_mcs_ul);
265 if (!mcs_is_valid(ms->current_cs_ul))
266 ms->current_cs_ul = MCS1;
267 }
268 if (!mcs_is_edge_gmsk(ms->current_cs_dl)) {
269 ms->current_cs_dl = mcs_get_egprs_by_num(
270 ms->bts->initial_mcs_dl);
271 if (!mcs_is_valid(ms->current_cs_dl))
272 ms->current_cs_dl = MCS1;
273 }
274 break;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100275 case EGPRS:
276 if (!mcs_is_edge(ms->current_cs_ul)) {
277 ms->current_cs_ul = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100278 ms->bts->initial_mcs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100279 if (!mcs_is_valid(ms->current_cs_ul))
280 ms->current_cs_ul = MCS1;
281 }
282 if (!mcs_is_edge(ms->current_cs_dl)) {
283 ms->current_cs_dl = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100284 ms->bts->initial_mcs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100285 if (!mcs_is_valid(ms->current_cs_dl))
286 ms->current_cs_dl = MCS1;
287 }
288 break;
289 }
290}
291
292static void ms_attach_ul_tbf(struct GprsMs *ms, struct gprs_rlcmac_ul_tbf *tbf)
293{
294 if (ms->ul_tbf == tbf)
295 return;
296
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100297 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 +0100298
299 ms_ref(ms);
300
301 if (ms->ul_tbf)
302 llist_add_tail(tbf_ms_list((struct gprs_rlcmac_tbf *)ms->ul_tbf), &ms->old_tbfs);
303
304 ms->ul_tbf = tbf;
305
306 if (tbf)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100307 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100308
309 ms_unref(ms);
310}
311
312static void ms_attach_dl_tbf(struct GprsMs *ms, struct gprs_rlcmac_dl_tbf *tbf)
313{
314 if (ms->dl_tbf == tbf)
315 return;
316
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100317 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 +0100318
319 ms_ref(ms);
320
321 if (ms->dl_tbf)
322 llist_add_tail(tbf_ms_list((struct gprs_rlcmac_tbf *)ms->dl_tbf), &ms->old_tbfs);
323
324 ms->dl_tbf = tbf;
325
326 if (tbf)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100327 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100328
329 ms_unref(ms);
330}
331
332void ms_attach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
333{
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200334 if (tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100335 ms_attach_dl_tbf(ms, as_dl_tbf(tbf));
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200336 else
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100337 ms_attach_ul_tbf(ms, as_ul_tbf(tbf));
338}
339
340void ms_detach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
341{
342 if (tbf == (struct gprs_rlcmac_tbf *)(ms->ul_tbf)) {
343 ms->ul_tbf = NULL;
344 } else if (tbf == (struct gprs_rlcmac_tbf *)(ms->dl_tbf)) {
345 ms->dl_tbf = NULL;
346 } else {
347 bool found = false;
348
349 struct llist_item *pos, *tmp;
350 llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
351 struct gprs_rlcmac_tbf *tmp_tbf = (struct gprs_rlcmac_tbf *)pos->entry;
352 if (tmp_tbf == tbf) {
353 llist_del(&pos->list);
354 found = true;
355 break;
356 }
357 }
358
359 /* Protect against recursive calls via set_ms() */
360 if (!found)
361 return;
362 }
363
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100364 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Detaching TBF: %s\n",
365 tbf_name(tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100366
367 if (tbf_ms(tbf) == ms)
368 tbf_set_ms(tbf, NULL);
369
370 if (!ms->dl_tbf && !ms->ul_tbf) {
371 ms_set_reserved_slots(ms, NULL, 0, 0);
372
373 if (ms_tlli(ms) != 0)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100374 ms_release_timer_start(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100375 }
376
377 ms_update_status(ms);
378}
379
380void ms_reset(struct GprsMs *ms)
381{
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100382 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Clearing MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100383
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100384 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100385
386 ms->tlli = GSM_RESERVED_TMSI;
387 ms->new_dl_tlli = ms->tlli;
388 ms->new_ul_tlli = ms->tlli;
389 ms->imsi[0] = '\0';
390}
391
392static void ms_merge_old_ms(struct GprsMs *ms, struct GprsMs *old_ms)
393{
394 OSMO_ASSERT(old_ms != ms);
395
396 if (strlen(ms_imsi(ms)) == 0 && strlen(ms_imsi(old_ms)) != 0)
397 osmo_strlcpy(ms->imsi, ms_imsi(old_ms), sizeof(ms->imsi));
398
399 if (!ms_ms_class(ms) && ms_ms_class(old_ms))
400 ms_set_ms_class(ms, ms_ms_class(old_ms));
401
402 if (!ms_egprs_ms_class(ms) && ms_egprs_ms_class(old_ms))
403 ms_set_egprs_ms_class(ms, ms_egprs_ms_class(old_ms));
404
405 llc_queue_move_and_merge(&ms->llc_queue, &old_ms->llc_queue);
406
407 ms_reset(old_ms);
408}
409
410void ms_merge_and_clear_ms(struct GprsMs *ms, struct GprsMs *old_ms)
411{
412 OSMO_ASSERT(old_ms != ms);
413
414 ms_ref(old_ms);
415
416 /* Clean up the old MS object */
417 /* TODO: Use timer? */
418 if (ms_ul_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms), T_MAX))
419 tbf_free((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms));
420 if (ms_dl_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms), T_MAX))
421 tbf_free((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms));
422
423 ms_merge_old_ms(ms, old_ms);
424
425 ms_unref(old_ms);
426}
427
428void ms_set_tlli(struct GprsMs *ms, uint32_t tlli)
429{
430 if (tlli == ms->tlli || tlli == ms->new_ul_tlli)
431 return;
432
433 if (tlli != ms->new_dl_tlli) {
434 LOGP(DRLCMAC, LOGL_INFO,
435 "Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
436 "not yet confirmed\n",
437 ms_tlli(ms), tlli);
438 ms->new_ul_tlli = tlli;
439 return;
440 }
441
442 LOGP(DRLCMAC, LOGL_INFO,
443 "Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
444 "already confirmed partly\n",
445 ms->tlli, tlli);
446
447 ms->tlli = tlli;
448 ms->new_dl_tlli = GSM_RESERVED_TMSI;
449 ms->new_ul_tlli = GSM_RESERVED_TMSI;
450}
451
452bool ms_confirm_tlli(struct GprsMs *ms, uint32_t tlli)
453{
454 if (tlli == ms->tlli || tlli == ms->new_dl_tlli)
455 return false;
456
457 if (tlli != ms->new_ul_tlli) {
458 /* The MS has not sent a message with the new TLLI, which may
459 * happen according to the spec [TODO: add reference]. */
460
461 LOGP(DRLCMAC, LOGL_INFO,
462 "The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
463 "partly confirmed\n", tlli);
464 /* Use the network's idea of TLLI as candidate, this does not
465 * change the result value of tlli() */
466 ms->new_dl_tlli = tlli;
467 return false;
468 }
469
470 LOGP(DRLCMAC, LOGL_INFO,
471 "Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
472
473 ms->tlli = tlli;
474 ms->new_dl_tlli = GSM_RESERVED_TMSI;
475 ms->new_ul_tlli = GSM_RESERVED_TMSI;
476
477 return true;
478}
479
480void ms_set_imsi(struct GprsMs *ms, const char *imsi)
481{
482 if (!imsi) {
483 LOGP(DRLCMAC, LOGL_ERROR, "Expected IMSI!\n");
484 return;
485 }
486
487 if (imsi[0] && strlen(imsi) < 3) {
488 LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI '%s'!\n",
489 imsi);
490 return;
491 }
492
493 if (strcmp(imsi, ms->imsi) == 0)
494 return;
495
496 LOGP(DRLCMAC, LOGL_INFO,
497 "Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
498 ms_tlli(ms), ms->imsi, imsi);
499
500 struct GprsMs *old_ms = bts_ms_by_imsi(ms->bts, imsi);
501 /* Check if we are going to store a different MS object with already
502 existing IMSI. This is probably a bug in code calling this function,
503 since it should take care of this explicitly */
504 if (old_ms) {
505 /* We cannot find ms->ms by IMSI since we know that it has a
506 * different IMSI */
507 OSMO_ASSERT(old_ms != ms);
508
509 LOGPMS(ms, DRLCMAC, LOGL_NOTICE,
510 "IMSI '%s' was already assigned to another "
511 "MS object: TLLI = 0x%08x, that IMSI will be removed\n",
512 imsi, ms_tlli(old_ms));
513
514 ms_merge_and_clear_ms(ms, old_ms);
515 }
516
517
518 osmo_strlcpy(ms->imsi, imsi, sizeof(ms->imsi));
519}
520
Pau Espin Pedrol5deac142021-11-12 18:01:50 +0100521uint16_t ms_paging_group(struct GprsMs *ms)
522{
523 uint16_t pgroup;
524 if (!ms_imsi_is_valid(ms))
525 return 0; /* 000 is the special "all paging" group */
526 if ((pgroup = imsi2paging_group(ms_imsi(ms))) > 999) {
527 LOGPMS(ms, DRLCMAC, LOGL_ERROR, "IMSI to paging group failed!\n");
528 return 0;
529 }
530 return pgroup;
531}
532
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100533void ms_set_ta(struct GprsMs *ms, uint8_t ta_)
534{
535 if (ta_ == ms->ta)
536 return;
537
538 if (gsm48_ta_is_valid(ta_)) {
539 LOGP(DRLCMAC, LOGL_INFO,
540 "Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
541 ms_tlli(ms), ms->ta, ta_);
542 ms->ta = ta_;
543 } else
544 LOGP(DRLCMAC, LOGL_NOTICE,
545 "MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
546 "value %d kept)\n", ms_tlli(ms), ta_, ms->ta);
547}
548
549void ms_set_ms_class(struct GprsMs *ms, uint8_t ms_class_)
550{
551 if (ms_class_ == ms->ms_class)
552 return;
553
554 LOGP(DRLCMAC, LOGL_INFO,
555 "Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
556 ms_tlli(ms), ms->ms_class, ms_class_);
557
558 ms->ms_class = ms_class_;
559}
560
561void ms_set_egprs_ms_class(struct GprsMs *ms, uint8_t ms_class_)
562{
563 if (ms_class_ == ms->egprs_ms_class)
564 return;
565
566 LOGP(DRLCMAC, LOGL_INFO,
567 "Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
568 ms_tlli(ms), ms->egprs_ms_class, ms_class_);
569
570 ms->egprs_ms_class = ms_class_;
571
572 if (!bts_max_mcs_ul(ms->bts) || !bts_max_mcs_dl(ms->bts)) {
573 LOGPMS(ms, DRLCMAC, LOGL_DEBUG,
574 "Avoid enabling EGPRS because use of MCS is disabled: ul=%u dl=%u\n",
575 bts_max_mcs_ul(ms->bts), bts_max_mcs_dl(ms->bts));
576 return;
577 }
578
579 if (mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts))) &&
580 mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts))) &&
581 ms_mode(ms) != EGPRS)
582 {
583 ms_set_mode(ms, EGPRS_GMSK);
584 } else {
585 ms_set_mode(ms, EGPRS);
586 }
587 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Enabled EGPRS, mode %s\n", mode_name(ms_mode(ms)));
588}
589
590void ms_update_error_rate(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, int error_rate)
591{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100592 int64_t now;
593 enum CodingScheme max_cs_dl = ms_max_cs_dl(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100594 OSMO_ASSERT(max_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100595
596 if (error_rate < 0)
597 return;
598
599 now = now_msec();
600
601 /* TODO: Check for TBF direction */
602 /* TODO: Support different CS values for UL and DL */
603
604 ms->nack_rate_dl = error_rate;
605
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100606 if (error_rate > the_pcu->vty.cs_adj_upper_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100607 if (mcs_chan_code(ms->current_cs_dl) > 0) {
608 mcs_dec_kind(&ms->current_cs_dl, ms_mode(ms));
609 LOGP(DRLCMACDL, LOGL_INFO,
610 "MS (IMSI %s): High error rate %d%%, "
611 "reducing CS level to %s\n",
612 ms_imsi(ms), error_rate, mcs_name(ms->current_cs_dl));
613 ms->last_cs_not_low = now;
614 }
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100615 } else if (error_rate < the_pcu->vty.cs_adj_lower_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100616 if (ms->current_cs_dl < max_cs_dl) {
617 if (now - ms->last_cs_not_low > 1000) {
618 mcs_inc_kind(&ms->current_cs_dl, ms_mode(ms));
619
620 LOGP(DRLCMACDL, LOGL_INFO,
621 "MS (IMSI %s): Low error rate %d%%, "
622 "increasing DL CS level to %s\n",
623 ms_imsi(ms), error_rate,
624 mcs_name(ms->current_cs_dl));
625 ms->last_cs_not_low = now;
626 } else {
627 LOGP(DRLCMACDL, LOGL_DEBUG,
628 "MS (IMSI %s): Low error rate %d%%, "
629 "ignored (within blocking period)\n",
630 ms_imsi(ms), error_rate);
631 }
632 }
633 } else {
634 LOGP(DRLCMACDL, LOGL_DEBUG,
635 "MS (IMSI %s): Medium error rate %d%%, ignored\n",
636 ms_imsi(ms), error_rate);
637 ms->last_cs_not_low = now;
638 }
639}
640
641enum CodingScheme ms_max_cs_ul(const struct GprsMs *ms)
642{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100643 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100644 OSMO_ASSERT(ms->bts != NULL);
645
646 if (mcs_is_gprs(ms->current_cs_ul)) {
647 if (!bts_max_cs_ul(ms->bts)) {
648 return CS4;
649 }
650
651 return mcs_get_gprs_by_num(bts_max_cs_ul(ms->bts));
652 }
653
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100654 cs = mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts));
655 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
656 cs = MCS4;
657 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100658}
659
660void ms_set_current_cs_dl(struct GprsMs *ms, enum CodingScheme scheme)
661{
662 ms->current_cs_dl = scheme;
663}
664
665enum CodingScheme ms_max_cs_dl(const struct GprsMs *ms)
666{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100667 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100668 OSMO_ASSERT(ms->bts != NULL);
669
670 if (mcs_is_gprs(ms->current_cs_dl)) {
671 if (!bts_max_cs_dl(ms->bts)) {
672 return CS4;
673 }
674
675 return mcs_get_gprs_by_num(bts_max_cs_dl(ms->bts));
676 }
677
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100678 cs = mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts));
679 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
680 cs = MCS4;
681 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100682}
683
684void ms_update_cs_ul(struct GprsMs *ms, const struct pcu_l1_meas *meas)
685{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100686 enum CodingScheme max_cs_ul = ms_max_cs_ul(ms);
687
688 int old_link_qual;
689 int low;
690 int high;
691 enum CodingScheme new_cs_ul = ms->current_cs_ul;
692 uint8_t current_cs = mcs_chan_code(ms->current_cs_ul);
693
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100694 if (!max_cs_ul) {
695 LOGP(DRLCMACMEAS, LOGL_ERROR,
696 "max_cs_ul cannot be derived (current UL CS: %s)\n",
697 mcs_name(ms->current_cs_ul));
698 return;
699 }
700
701 if (!ms->current_cs_ul) {
702 LOGP(DRLCMACMEAS, LOGL_ERROR,
703 "Unable to update UL (M)CS because it's not set: %s\n",
704 mcs_name(ms->current_cs_ul));
705 return;
706 }
707
708 if (!meas->have_link_qual) {
709 LOGP(DRLCMACMEAS, LOGL_ERROR,
710 "Unable to update UL (M)CS %s because we don't have link quality measurements.\n",
711 mcs_name(ms->current_cs_ul));
712 return;
713 }
714
715 if (mcs_is_gprs(ms->current_cs_ul)) {
716 if (current_cs >= MAX_GPRS_CS)
717 current_cs = MAX_GPRS_CS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100718 low = the_pcu->vty.cs_lqual_ranges[current_cs].low;
719 high = the_pcu->vty.cs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100720 } else if (mcs_is_edge(ms->current_cs_ul)) {
721 if (current_cs >= MAX_EDGE_MCS)
722 current_cs = MAX_EDGE_MCS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100723 low = the_pcu->vty.mcs_lqual_ranges[current_cs].low;
724 high = the_pcu->vty.mcs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100725 } else {
726 LOGP(DRLCMACMEAS, LOGL_ERROR,
727 "Unable to update UL (M)CS because it's neither GPRS nor EDGE: %s\n",
728 mcs_name(ms->current_cs_ul));
729 return;
730 }
731
732 /* To avoid rapid changes of the coding scheme, we also take
733 * the old link quality value into account (if present). */
734 if (ms->l1_meas.have_link_qual)
735 old_link_qual = ms->l1_meas.link_qual;
736 else
737 old_link_qual = meas->link_qual;
738
739 if (meas->link_qual < low && old_link_qual < low)
740 mcs_dec_kind(&new_cs_ul, ms_mode(ms));
741 else if (meas->link_qual > high && old_link_qual > high &&
742 ms->current_cs_ul < max_cs_ul)
743 mcs_inc_kind(&new_cs_ul, ms_mode(ms));
744
745 if (ms->current_cs_ul != new_cs_ul) {
746 LOGPMS(ms, DRLCMACMEAS, LOGL_INFO,
747 "Link quality %ddB (old %ddB) left window [%d, %d], "
748 "modifying uplink CS level: %s -> %s\n",
749 meas->link_qual, old_link_qual,
750 low, high,
751 mcs_name(ms->current_cs_ul), mcs_name(new_cs_ul));
752
753 ms->current_cs_ul = new_cs_ul;
754 }
755}
756
757void ms_update_l1_meas(struct GprsMs *ms, const struct pcu_l1_meas *meas)
758{
759 unsigned i;
760
761 ms_update_cs_ul(ms, meas);
762
763 if (meas->have_rssi)
764 pcu_l1_meas_set_rssi(&ms->l1_meas, meas->rssi);
765 if (meas->have_bto)
766 pcu_l1_meas_set_bto(&ms->l1_meas, meas->bto);
767 if (meas->have_ber)
768 pcu_l1_meas_set_ber(&ms->l1_meas, meas->ber);
769 if (meas->have_link_qual)
770 pcu_l1_meas_set_link_qual(&ms->l1_meas, meas->link_qual);
771
772 if (meas->have_ms_rx_qual)
773 pcu_l1_meas_set_ms_rx_qual(&ms->l1_meas, meas->ms_rx_qual);
774 if (meas->have_ms_c_value)
775 pcu_l1_meas_set_ms_c_value(&ms->l1_meas, meas->ms_c_value);
776 if (meas->have_ms_sign_var)
777 pcu_l1_meas_set_ms_sign_var(&ms->l1_meas, meas->ms_sign_var);
778
779 if (meas->have_ms_i_level) {
780 for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
781 if (meas->ts[i].have_ms_i_level)
782 pcu_l1_meas_set_ms_i_level(&ms->l1_meas, i, meas->ts[i].ms_i_level);
783 else
784 ms->l1_meas.ts[i].have_ms_i_level = 0;
785 }
786 }
787}
788
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100789/* req_mcs_kind acts as a set filter, where EGPRS means any and GPRS is the most restrictive */
790enum CodingScheme ms_current_cs_dl(const struct GprsMs *ms, enum mcs_kind req_mcs_kind)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100791{
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100792 enum CodingScheme orig_cs = ms->current_cs_dl;
793 struct gprs_rlcmac_bts *bts = ms->bts;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100794 size_t unencoded_octets;
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100795 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100796
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100797 /* It could be that a TBF requests a GPRS CS despite the MS currently
798 being upgraded to EGPRS (hence reporting MCS). That could happen
799 because the TBF was created early in the process where we didn't have
800 yet enough information about the MS, and only AFTER it was created we
801 upgraded the MS to be EGPRS capable.
802 As a result, when the MS is queried for the target CS here, we could be
803 returning an MCS despite the current TBF being established as GPRS,
804 but we rather stick to the TBF type we assigned to the MS rather than
805 magically sending EGPRS data blocks to a GPRS TBF.
806 It could also be that the caller requests specific MCS kind
807 explicitly too due to scheduling restrictions (GPRS+EGPRS multiplexing). */
808 if (req_mcs_kind == EGPRS_GMSK && mcs_is_edge(orig_cs) && orig_cs > MCS4) {
809 cs = bts_cs_dl_is_supported(bts, MCS4) ? MCS4 :
810 bts_cs_dl_is_supported(bts, MCS3) ? MCS3 :
811 bts_cs_dl_is_supported(bts, MCS2) ? MCS2 :
812 MCS1;
813 } else if (req_mcs_kind == GPRS && mcs_is_edge(orig_cs)) { /* GPRS */
814 int i;
815 cs = orig_cs > MCS4 ? MCS4 : orig_cs;
816 cs -= (MCS1 - CS1); /* MCSx -> CSx */
817 /* Find suitable CS starting from equivalent MCS which is supported by BTS: */
818 for (i = mcs_chan_code(cs); !bts_cs_dl_is_supported(bts, CS1 + i); i--);
819 OSMO_ASSERT(i >= 0 && i <= 3); /* CS1 is always supported */
820 cs = CS1 + i;
821 } else {
822 cs = orig_cs;
823 }
824
825 if (orig_cs != cs)
826 LOGPMS(ms, DRLCMACDL, LOGL_INFO, "MS (mode=%s) suggests transmitting "
827 "DL %s, downgrade to %s in order to match TBF & scheduler requirements\n",
828 mode_name(ms_mode(ms)), mcs_name(orig_cs), mcs_name(cs));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100829
830 unencoded_octets = llc_queue_octets(&ms->llc_queue);
831
832 /* If the DL TBF is active, add number of unencoded chunk octets */
833 if (ms->dl_tbf)
834 unencoded_octets += llc_chunk_size(tbf_llc((struct gprs_rlcmac_tbf *)ms->dl_tbf));
835
836 /* There are many unencoded octets, don't reduce */
Pau Espin Pedrolad79b852021-01-14 13:20:55 +0100837 if (unencoded_octets >= the_pcu->vty.cs_downgrade_threshold)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100838 return cs;
839
840 /* RF conditions are good, don't reduce */
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100841 if (ms->nack_rate_dl < the_pcu->vty.cs_adj_lower_limit)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100842 return cs;
843
844 /* The throughput would probably be better if the CS level was reduced */
845 mcs_dec_kind(&cs, ms_mode(ms));
846
847 /* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
848 if (cs == CS2)
849 mcs_dec_kind(&cs, ms_mode(ms));
850
851 return cs;
852}
853
854int ms_first_common_ts(const struct GprsMs *ms)
855{
856 if (ms->dl_tbf)
857 return tbf_first_common_ts((struct gprs_rlcmac_tbf *)ms->dl_tbf);
858
859 if (ms->ul_tbf)
860 return tbf_first_common_ts((struct gprs_rlcmac_tbf *)ms->ul_tbf);
861
862 return -1;
863}
864
865uint8_t ms_dl_slots(const struct GprsMs *ms)
866{
867 uint8_t slots = 0;
868
869 if (ms->dl_tbf)
870 slots |= tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
871
872 if (ms->ul_tbf)
873 slots |= tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf);
874
875 return slots;
876}
877
878uint8_t ms_ul_slots(const struct GprsMs *ms)
879{
880 uint8_t slots = 0;
881
882 if (ms->dl_tbf)
883 slots |= tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
884
885 if (ms->ul_tbf)
886 slots |= tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf);
887
888 return slots;
889}
890
891uint8_t ms_current_pacch_slots(const struct GprsMs *ms)
892{
893 uint8_t slots = 0;
894
895 bool is_dl_active = ms->dl_tbf && tbf_is_tfi_assigned((struct gprs_rlcmac_tbf *)ms->dl_tbf);
896 bool is_ul_active = ms->ul_tbf && tbf_is_tfi_assigned((struct gprs_rlcmac_tbf *)ms->ul_tbf);
897
898 if (!is_dl_active && !is_ul_active)
899 return 0;
900
901 /* see TS 44.060, 8.1.1.2.2 */
902 if (is_dl_active && !is_ul_active)
903 slots = tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
904 else if (!is_dl_active && is_ul_active)
905 slots = tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf);
906 else
907 slots = tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf) &
908 tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
909
910 /* Assume a multislot class 1 device */
911 /* TODO: For class 2 devices, this could be removed */
912 slots = pcu_lsb(slots);
913
914 return slots;
915}
916
917void ms_set_reserved_slots(struct GprsMs *ms, struct gprs_rlcmac_trx *trx,
918 uint8_t ul_slots, uint8_t dl_slots)
919{
920 if (ms->current_trx) {
921 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
922 ms->reserved_dl_slots);
923 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
924 ms->reserved_ul_slots);
925 ms->reserved_dl_slots = 0;
926 ms->reserved_ul_slots = 0;
927 }
928 ms->current_trx = trx;
929 if (trx) {
930 ms->reserved_dl_slots = dl_slots;
931 ms->reserved_ul_slots = ul_slots;
932 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
933 ms->reserved_dl_slots);
934 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
935 ms->reserved_ul_slots);
936 }
937}
938
939struct gprs_rlcmac_tbf *ms_tbf(const struct GprsMs *ms, enum gprs_rlcmac_tbf_direction dir)
940{
941 switch (dir) {
942 case GPRS_RLCMAC_DL_TBF: return (struct gprs_rlcmac_tbf *)ms->dl_tbf;
943 case GPRS_RLCMAC_UL_TBF: return (struct gprs_rlcmac_tbf *)ms->ul_tbf;
944 }
945
946 return NULL;
947}
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +0100948
949int ms_nacc_start(struct GprsMs *ms, Packet_Cell_Change_Notification_t *notif)
950{
951 if (!ms->nacc)
952 ms->nacc = nacc_fsm_alloc(ms);
953 if (!ms->nacc)
954 return -EINVAL;
955 return osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_RX_CELL_CHG_NOTIFICATION, notif);
956}
957
958bool ms_nacc_rts(const struct GprsMs *ms)
959{
960 if (!ms->nacc)
961 return false;
962 if (ms->nacc->fi->state == NACC_ST_TX_NEIGHBOUR_DATA ||
963 ms->nacc->fi->state == NACC_ST_TX_CELL_CHG_CONTINUE)
964 return true;
965 return false;
966}
967
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +0100968struct 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 +0100969{
970 int rc;
971 struct nacc_ev_create_rlcmac_msg_ctx data_ctx;
972
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +0100973 data_ctx = (struct nacc_ev_create_rlcmac_msg_ctx) {
974 .tbf = tbf,
975 .fn = fn,
976 .ts = ts,
977 .msg = NULL,
978 };
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +0100979
980 rc = osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_CREATE_RLCMAC_MSG, &data_ctx);
981 if (rc != 0 || !data_ctx.msg)
982 return NULL;
983 return data_ctx.msg;
984}