blob: f157d131aac138098f5573748a7d79bf71487385 [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{
635 OSMO_ASSERT(ms->bts != NULL);
636
637 if (mcs_is_gprs(ms->current_cs_ul)) {
638 if (!bts_max_cs_ul(ms->bts)) {
639 return CS4;
640 }
641
642 return mcs_get_gprs_by_num(bts_max_cs_ul(ms->bts));
643 }
644
645 if (!mcs_is_edge(ms->current_cs_ul))
646 return UNKNOWN;
647
648 if (bts_max_mcs_ul(ms->bts))
649 return mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts));
650 else if (bts_max_cs_ul(ms->bts))
651 return mcs_get_gprs_by_num(bts_max_cs_ul(ms->bts));
652
653 return MCS4;
654}
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{
663 OSMO_ASSERT(ms->bts != NULL);
664
665 if (mcs_is_gprs(ms->current_cs_dl)) {
666 if (!bts_max_cs_dl(ms->bts)) {
667 return CS4;
668 }
669
670 return mcs_get_gprs_by_num(bts_max_cs_dl(ms->bts));
671 }
672
673 if (!mcs_is_edge(ms->current_cs_dl))
674 return UNKNOWN;
675
676 if (bts_max_mcs_dl(ms->bts))
677 return mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts));
678 else if (bts_max_cs_dl(ms->bts))
679 return mcs_get_gprs_by_num(bts_max_cs_dl(ms->bts));
680
681 return MCS4;
682}
683
684void ms_update_cs_ul(struct GprsMs *ms, const struct pcu_l1_meas *meas)
685{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100686 enum CodingScheme max_cs_ul = ms_max_cs_ul(ms);
687
688 int old_link_qual;
689 int low;
690 int high;
691 enum CodingScheme new_cs_ul = ms->current_cs_ul;
692 uint8_t current_cs = mcs_chan_code(ms->current_cs_ul);
693
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100694 if (!max_cs_ul) {
695 LOGP(DRLCMACMEAS, LOGL_ERROR,
696 "max_cs_ul cannot be derived (current UL CS: %s)\n",
697 mcs_name(ms->current_cs_ul));
698 return;
699 }
700
701 if (!ms->current_cs_ul) {
702 LOGP(DRLCMACMEAS, LOGL_ERROR,
703 "Unable to update UL (M)CS because it's not set: %s\n",
704 mcs_name(ms->current_cs_ul));
705 return;
706 }
707
708 if (!meas->have_link_qual) {
709 LOGP(DRLCMACMEAS, LOGL_ERROR,
710 "Unable to update UL (M)CS %s because we don't have link quality measurements.\n",
711 mcs_name(ms->current_cs_ul));
712 return;
713 }
714
715 if (mcs_is_gprs(ms->current_cs_ul)) {
716 if (current_cs >= MAX_GPRS_CS)
717 current_cs = MAX_GPRS_CS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100718 low = the_pcu->vty.cs_lqual_ranges[current_cs].low;
719 high = the_pcu->vty.cs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100720 } else if (mcs_is_edge(ms->current_cs_ul)) {
721 if (current_cs >= MAX_EDGE_MCS)
722 current_cs = MAX_EDGE_MCS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100723 low = the_pcu->vty.mcs_lqual_ranges[current_cs].low;
724 high = the_pcu->vty.mcs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100725 } else {
726 LOGP(DRLCMACMEAS, LOGL_ERROR,
727 "Unable to update UL (M)CS because it's neither GPRS nor EDGE: %s\n",
728 mcs_name(ms->current_cs_ul));
729 return;
730 }
731
732 /* To avoid rapid changes of the coding scheme, we also take
733 * the old link quality value into account (if present). */
734 if (ms->l1_meas.have_link_qual)
735 old_link_qual = ms->l1_meas.link_qual;
736 else
737 old_link_qual = meas->link_qual;
738
739 if (meas->link_qual < low && old_link_qual < low)
740 mcs_dec_kind(&new_cs_ul, ms_mode(ms));
741 else if (meas->link_qual > high && old_link_qual > high &&
742 ms->current_cs_ul < max_cs_ul)
743 mcs_inc_kind(&new_cs_ul, ms_mode(ms));
744
745 if (ms->current_cs_ul != new_cs_ul) {
746 LOGPMS(ms, DRLCMACMEAS, LOGL_INFO,
747 "Link quality %ddB (old %ddB) left window [%d, %d], "
748 "modifying uplink CS level: %s -> %s\n",
749 meas->link_qual, old_link_qual,
750 low, high,
751 mcs_name(ms->current_cs_ul), mcs_name(new_cs_ul));
752
753 ms->current_cs_ul = new_cs_ul;
754 }
755}
756
757void ms_update_l1_meas(struct GprsMs *ms, const struct pcu_l1_meas *meas)
758{
759 unsigned i;
760
761 ms_update_cs_ul(ms, meas);
762
763 if (meas->have_rssi)
764 pcu_l1_meas_set_rssi(&ms->l1_meas, meas->rssi);
765 if (meas->have_bto)
766 pcu_l1_meas_set_bto(&ms->l1_meas, meas->bto);
767 if (meas->have_ber)
768 pcu_l1_meas_set_ber(&ms->l1_meas, meas->ber);
769 if (meas->have_link_qual)
770 pcu_l1_meas_set_link_qual(&ms->l1_meas, meas->link_qual);
771
772 if (meas->have_ms_rx_qual)
773 pcu_l1_meas_set_ms_rx_qual(&ms->l1_meas, meas->ms_rx_qual);
774 if (meas->have_ms_c_value)
775 pcu_l1_meas_set_ms_c_value(&ms->l1_meas, meas->ms_c_value);
776 if (meas->have_ms_sign_var)
777 pcu_l1_meas_set_ms_sign_var(&ms->l1_meas, meas->ms_sign_var);
778
779 if (meas->have_ms_i_level) {
780 for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
781 if (meas->ts[i].have_ms_i_level)
782 pcu_l1_meas_set_ms_i_level(&ms->l1_meas, i, meas->ts[i].ms_i_level);
783 else
784 ms->l1_meas.ts[i].have_ms_i_level = 0;
785 }
786 }
787}
788
789enum CodingScheme ms_current_cs_dl(const struct GprsMs *ms)
790{
791 enum CodingScheme cs = ms->current_cs_dl;
792 size_t unencoded_octets;
793
794 if (!ms->bts)
795 return cs;
796
797 unencoded_octets = llc_queue_octets(&ms->llc_queue);
798
799 /* If the DL TBF is active, add number of unencoded chunk octets */
800 if (ms->dl_tbf)
801 unencoded_octets += llc_chunk_size(tbf_llc((struct gprs_rlcmac_tbf *)ms->dl_tbf));
802
803 /* There are many unencoded octets, don't reduce */
Pau Espin Pedrolad79b852021-01-14 13:20:55 +0100804 if (unencoded_octets >= the_pcu->vty.cs_downgrade_threshold)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100805 return cs;
806
807 /* RF conditions are good, don't reduce */
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100808 if (ms->nack_rate_dl < the_pcu->vty.cs_adj_lower_limit)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100809 return cs;
810
811 /* The throughput would probably be better if the CS level was reduced */
812 mcs_dec_kind(&cs, ms_mode(ms));
813
814 /* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
815 if (cs == CS2)
816 mcs_dec_kind(&cs, ms_mode(ms));
817
818 return cs;
819}
820
821int ms_first_common_ts(const struct GprsMs *ms)
822{
823 if (ms->dl_tbf)
824 return tbf_first_common_ts((struct gprs_rlcmac_tbf *)ms->dl_tbf);
825
826 if (ms->ul_tbf)
827 return tbf_first_common_ts((struct gprs_rlcmac_tbf *)ms->ul_tbf);
828
829 return -1;
830}
831
832uint8_t ms_dl_slots(const struct GprsMs *ms)
833{
834 uint8_t slots = 0;
835
836 if (ms->dl_tbf)
837 slots |= tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
838
839 if (ms->ul_tbf)
840 slots |= tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf);
841
842 return slots;
843}
844
845uint8_t ms_ul_slots(const struct GprsMs *ms)
846{
847 uint8_t slots = 0;
848
849 if (ms->dl_tbf)
850 slots |= tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
851
852 if (ms->ul_tbf)
853 slots |= tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf);
854
855 return slots;
856}
857
858uint8_t ms_current_pacch_slots(const struct GprsMs *ms)
859{
860 uint8_t slots = 0;
861
862 bool is_dl_active = ms->dl_tbf && tbf_is_tfi_assigned((struct gprs_rlcmac_tbf *)ms->dl_tbf);
863 bool is_ul_active = ms->ul_tbf && tbf_is_tfi_assigned((struct gprs_rlcmac_tbf *)ms->ul_tbf);
864
865 if (!is_dl_active && !is_ul_active)
866 return 0;
867
868 /* see TS 44.060, 8.1.1.2.2 */
869 if (is_dl_active && !is_ul_active)
870 slots = tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
871 else if (!is_dl_active && is_ul_active)
872 slots = tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf);
873 else
874 slots = tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf) &
875 tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
876
877 /* Assume a multislot class 1 device */
878 /* TODO: For class 2 devices, this could be removed */
879 slots = pcu_lsb(slots);
880
881 return slots;
882}
883
884void ms_set_reserved_slots(struct GprsMs *ms, struct gprs_rlcmac_trx *trx,
885 uint8_t ul_slots, uint8_t dl_slots)
886{
887 if (ms->current_trx) {
888 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
889 ms->reserved_dl_slots);
890 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
891 ms->reserved_ul_slots);
892 ms->reserved_dl_slots = 0;
893 ms->reserved_ul_slots = 0;
894 }
895 ms->current_trx = trx;
896 if (trx) {
897 ms->reserved_dl_slots = dl_slots;
898 ms->reserved_ul_slots = ul_slots;
899 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
900 ms->reserved_dl_slots);
901 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
902 ms->reserved_ul_slots);
903 }
904}
905
906struct gprs_rlcmac_tbf *ms_tbf(const struct GprsMs *ms, enum gprs_rlcmac_tbf_direction dir)
907{
908 switch (dir) {
909 case GPRS_RLCMAC_DL_TBF: return (struct gprs_rlcmac_tbf *)ms->dl_tbf;
910 case GPRS_RLCMAC_UL_TBF: return (struct gprs_rlcmac_tbf *)ms->ul_tbf;
911 }
912
913 return NULL;
914}