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