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