blob: 6dc11a6afca4dde067a0b13d2c4ba836c777c4ef [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';
114 memset(&ms->timer, 0, sizeof(ms->timer));
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100115 ms->timer.cb = ms_release_timer_cb;
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200116 llc_queue_init(&ms->llc_queue, ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100117
118 ms_set_mode(ms, GPRS);
119
Pau Espin Pedrolb4987462022-04-01 17:21:08 +0200120 codel_interval = the_pcu->vty.llc_codel_interval_msec;
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200121 if (codel_interval == LLC_CODEL_USE_DEFAULT)
122 codel_interval = GPRS_CODEL_SLOW_INTERVAL_MS;
123 llc_queue_set_codel_interval(&ms->llc_queue, codel_interval);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100124
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100125 ms->last_cs_not_low = now_msec();
126 ms->app_info_pending = false;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100127
128 ms->ctrs = rate_ctr_group_alloc(ms, &ms_ctrg_desc, next_ms_ctr_group_id++);
129 if (!ms->ctrs)
130 goto free_ret;
131
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100132 return ms;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100133free_ret:
134 talloc_free(ms);
135 return NULL;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100136}
137
138static int ms_talloc_destructor(struct GprsMs *ms)
139{
140 struct llist_item *pos, *tmp;
141
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100142 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Destroying MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100143
144 ms_set_reserved_slots(ms, NULL, 0, 0);
145
146 if (osmo_timer_pending(&ms->timer))
147 osmo_timer_del(&ms->timer);
148
149 if (ms->ul_tbf) {
150 tbf_set_ms((struct gprs_rlcmac_tbf *)ms->ul_tbf, NULL);
151 ms->ul_tbf = NULL;
152 }
153
154 if (ms->dl_tbf) {
155 tbf_set_ms((struct gprs_rlcmac_tbf *)ms->dl_tbf, NULL);
156 ms->dl_tbf = NULL;
157 }
158
159 llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
160 struct gprs_rlcmac_tbf *tbf = (struct gprs_rlcmac_tbf *)pos->entry;
161 tbf_set_ms(tbf, NULL);
162 }
163
164 llc_queue_clear(&ms->llc_queue, ms->bts);
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100165
166 if (ms->ctrs)
167 rate_ctr_group_free(ms->ctrs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100168 return 0;
169}
170
171
172void ms_set_callback(struct GprsMs *ms, struct gpr_ms_callback *cb)
173{
174 if (cb)
175 ms->cb = *cb;
176 else
177 ms->cb = gprs_default_cb;
178}
179
180static void ms_update_status(struct GprsMs *ms)
181{
182 if (ms->ref > 0)
183 return;
184
185 if (ms_is_idle(ms) && !ms->is_idle) {
186 ms->is_idle = true;
187 ms->cb.ms_idle(ms);
188 /* this can be deleted by now, do not access it */
189 return;
190 }
191
192 if (!ms_is_idle(ms) && ms->is_idle) {
193 ms->is_idle = false;
194 ms->cb.ms_active(ms);
195 }
196}
197
198struct GprsMs *ms_ref(struct GprsMs *ms)
199{
200 ms->ref += 1;
201 return ms;
202}
203
204void ms_unref(struct GprsMs *ms)
205{
206 OSMO_ASSERT(ms->ref >= 0);
207 ms->ref -= 1;
208 if (ms->ref == 0)
209 ms_update_status(ms);
210}
211
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100212static void ms_release_timer_start(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100213{
214 if (ms->delay == 0)
215 return;
216
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100217 LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Schedule MS release in %u secs\n", ms->delay);
218
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100219 if (!ms->timer.data)
220 ms->timer.data = ms_ref(ms);
221
222 osmo_timer_schedule(&ms->timer, ms->delay, 0);
223}
224
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100225static void ms_release_timer_stop(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100226{
227 if (!ms->timer.data)
228 return;
229
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100230 LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Cancel scheduled MS release\n");
231
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100232 osmo_timer_del(&ms->timer);
233 ms->timer.data = NULL;
234 ms_unref(ms);
235}
236
237void ms_set_mode(struct GprsMs *ms, enum mcs_kind mode)
238{
239 ms->mode = mode;
240
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100241 switch (ms->mode) {
242 case GPRS:
243 if (!mcs_is_gprs(ms->current_cs_ul)) {
244 ms->current_cs_ul = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100245 ms->bts->initial_cs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100246 if (!mcs_is_valid(ms->current_cs_ul))
247 ms->current_cs_ul = CS1;
248 }
249 if (!mcs_is_gprs(ms->current_cs_dl)) {
250 ms->current_cs_dl = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100251 ms->bts->initial_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100252 if (!mcs_is_valid(ms->current_cs_dl))
253 ms->current_cs_dl = CS1;
254 }
255 break;
256
257 case EGPRS_GMSK:
Pau Espin Pedrol7bb8cd62021-01-25 15:08:35 +0100258 if (!mcs_is_edge_gmsk(ms->current_cs_ul)) {
259 ms->current_cs_ul = mcs_get_egprs_by_num(
260 ms->bts->initial_mcs_ul);
261 if (!mcs_is_valid(ms->current_cs_ul))
262 ms->current_cs_ul = MCS1;
263 }
264 if (!mcs_is_edge_gmsk(ms->current_cs_dl)) {
265 ms->current_cs_dl = mcs_get_egprs_by_num(
266 ms->bts->initial_mcs_dl);
267 if (!mcs_is_valid(ms->current_cs_dl))
268 ms->current_cs_dl = MCS1;
269 }
270 break;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100271 case EGPRS:
272 if (!mcs_is_edge(ms->current_cs_ul)) {
273 ms->current_cs_ul = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100274 ms->bts->initial_mcs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100275 if (!mcs_is_valid(ms->current_cs_ul))
276 ms->current_cs_ul = MCS1;
277 }
278 if (!mcs_is_edge(ms->current_cs_dl)) {
279 ms->current_cs_dl = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100280 ms->bts->initial_mcs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100281 if (!mcs_is_valid(ms->current_cs_dl))
282 ms->current_cs_dl = MCS1;
283 }
284 break;
285 }
286}
287
288static void ms_attach_ul_tbf(struct GprsMs *ms, struct gprs_rlcmac_ul_tbf *tbf)
289{
290 if (ms->ul_tbf == tbf)
291 return;
292
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100293 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 +0100294
295 ms_ref(ms);
296
297 if (ms->ul_tbf)
298 llist_add_tail(tbf_ms_list((struct gprs_rlcmac_tbf *)ms->ul_tbf), &ms->old_tbfs);
299
300 ms->ul_tbf = tbf;
301
302 if (tbf)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100303 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100304
305 ms_unref(ms);
306}
307
308static void ms_attach_dl_tbf(struct GprsMs *ms, struct gprs_rlcmac_dl_tbf *tbf)
309{
310 if (ms->dl_tbf == tbf)
311 return;
312
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100313 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 +0100314
315 ms_ref(ms);
316
317 if (ms->dl_tbf)
318 llist_add_tail(tbf_ms_list((struct gprs_rlcmac_tbf *)ms->dl_tbf), &ms->old_tbfs);
319
320 ms->dl_tbf = tbf;
321
322 if (tbf)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100323 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100324
325 ms_unref(ms);
326}
327
328void ms_attach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
329{
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200330 if (tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100331 ms_attach_dl_tbf(ms, as_dl_tbf(tbf));
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200332 else
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100333 ms_attach_ul_tbf(ms, as_ul_tbf(tbf));
334}
335
336void ms_detach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
337{
338 if (tbf == (struct gprs_rlcmac_tbf *)(ms->ul_tbf)) {
339 ms->ul_tbf = NULL;
340 } else if (tbf == (struct gprs_rlcmac_tbf *)(ms->dl_tbf)) {
341 ms->dl_tbf = NULL;
342 } else {
343 bool found = false;
344
345 struct llist_item *pos, *tmp;
346 llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
347 struct gprs_rlcmac_tbf *tmp_tbf = (struct gprs_rlcmac_tbf *)pos->entry;
348 if (tmp_tbf == tbf) {
349 llist_del(&pos->list);
350 found = true;
351 break;
352 }
353 }
354
355 /* Protect against recursive calls via set_ms() */
356 if (!found)
357 return;
358 }
359
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100360 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Detaching TBF: %s\n",
361 tbf_name(tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100362
363 if (tbf_ms(tbf) == ms)
364 tbf_set_ms(tbf, NULL);
365
366 if (!ms->dl_tbf && !ms->ul_tbf) {
367 ms_set_reserved_slots(ms, NULL, 0, 0);
368
369 if (ms_tlli(ms) != 0)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100370 ms_release_timer_start(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100371 }
372
373 ms_update_status(ms);
374}
375
376void ms_reset(struct GprsMs *ms)
377{
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100378 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Clearing MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100379
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100380 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100381
382 ms->tlli = GSM_RESERVED_TMSI;
383 ms->new_dl_tlli = ms->tlli;
384 ms->new_ul_tlli = ms->tlli;
385 ms->imsi[0] = '\0';
386}
387
388static void ms_merge_old_ms(struct GprsMs *ms, struct GprsMs *old_ms)
389{
390 OSMO_ASSERT(old_ms != ms);
391
392 if (strlen(ms_imsi(ms)) == 0 && strlen(ms_imsi(old_ms)) != 0)
393 osmo_strlcpy(ms->imsi, ms_imsi(old_ms), sizeof(ms->imsi));
394
395 if (!ms_ms_class(ms) && ms_ms_class(old_ms))
396 ms_set_ms_class(ms, ms_ms_class(old_ms));
397
398 if (!ms_egprs_ms_class(ms) && ms_egprs_ms_class(old_ms))
399 ms_set_egprs_ms_class(ms, ms_egprs_ms_class(old_ms));
400
401 llc_queue_move_and_merge(&ms->llc_queue, &old_ms->llc_queue);
402
403 ms_reset(old_ms);
404}
405
406void ms_merge_and_clear_ms(struct GprsMs *ms, struct GprsMs *old_ms)
407{
408 OSMO_ASSERT(old_ms != ms);
409
410 ms_ref(old_ms);
411
412 /* Clean up the old MS object */
413 /* TODO: Use timer? */
414 if (ms_ul_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms), T_MAX))
415 tbf_free((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms));
416 if (ms_dl_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms), T_MAX))
417 tbf_free((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms));
418
419 ms_merge_old_ms(ms, old_ms);
420
421 ms_unref(old_ms);
422}
423
424void ms_set_tlli(struct GprsMs *ms, uint32_t tlli)
425{
426 if (tlli == ms->tlli || tlli == ms->new_ul_tlli)
427 return;
428
429 if (tlli != ms->new_dl_tlli) {
430 LOGP(DRLCMAC, LOGL_INFO,
431 "Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
432 "not yet confirmed\n",
433 ms_tlli(ms), tlli);
434 ms->new_ul_tlli = tlli;
435 return;
436 }
437
438 LOGP(DRLCMAC, LOGL_INFO,
439 "Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
440 "already confirmed partly\n",
441 ms->tlli, tlli);
442
443 ms->tlli = tlli;
444 ms->new_dl_tlli = GSM_RESERVED_TMSI;
445 ms->new_ul_tlli = GSM_RESERVED_TMSI;
446}
447
448bool 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}