blob: ea497a3ca201e069dd6d0b9fefb3db9878e08726 [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
245 if (!ms->bts)
246 return;
247
248 switch (ms->mode) {
249 case GPRS:
250 if (!mcs_is_gprs(ms->current_cs_ul)) {
251 ms->current_cs_ul = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100252 ms->bts->initial_cs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100253 if (!mcs_is_valid(ms->current_cs_ul))
254 ms->current_cs_ul = CS1;
255 }
256 if (!mcs_is_gprs(ms->current_cs_dl)) {
257 ms->current_cs_dl = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100258 ms->bts->initial_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100259 if (!mcs_is_valid(ms->current_cs_dl))
260 ms->current_cs_dl = CS1;
261 }
262 break;
263
264 case EGPRS_GMSK:
265 case EGPRS:
266 if (!mcs_is_edge(ms->current_cs_ul)) {
267 ms->current_cs_ul = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100268 ms->bts->initial_mcs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100269 if (!mcs_is_valid(ms->current_cs_ul))
270 ms->current_cs_ul = MCS1;
271 }
272 if (!mcs_is_edge(ms->current_cs_dl)) {
273 ms->current_cs_dl = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100274 ms->bts->initial_mcs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100275 if (!mcs_is_valid(ms->current_cs_dl))
276 ms->current_cs_dl = MCS1;
277 }
278 break;
279 }
280}
281
282static void ms_attach_ul_tbf(struct GprsMs *ms, struct gprs_rlcmac_ul_tbf *tbf)
283{
284 if (ms->ul_tbf == tbf)
285 return;
286
287 LOGP(DRLCMAC, LOGL_INFO, "Attaching TBF to MS object, TLLI = 0x%08x, TBF = %s\n",
288 ms_tlli(ms), tbf_name((struct gprs_rlcmac_tbf *)tbf));
289
290 ms_ref(ms);
291
292 if (ms->ul_tbf)
293 llist_add_tail(tbf_ms_list((struct gprs_rlcmac_tbf *)ms->ul_tbf), &ms->old_tbfs);
294
295 ms->ul_tbf = tbf;
296
297 if (tbf)
298 ms_stop_timer(ms);
299
300 ms_unref(ms);
301}
302
303static void ms_attach_dl_tbf(struct GprsMs *ms, struct gprs_rlcmac_dl_tbf *tbf)
304{
305 if (ms->dl_tbf == tbf)
306 return;
307
308 LOGP(DRLCMAC, LOGL_INFO, "Attaching TBF to MS object, TLLI = 0x%08x, TBF = %s\n",
309 ms_tlli(ms), tbf_name((struct gprs_rlcmac_tbf *)tbf));
310
311 ms_ref(ms);
312
313 if (ms->dl_tbf)
314 llist_add_tail(tbf_ms_list((struct gprs_rlcmac_tbf *)ms->dl_tbf), &ms->old_tbfs);
315
316 ms->dl_tbf = tbf;
317
318 if (tbf)
319 ms_stop_timer(ms);
320
321 ms_unref(ms);
322}
323
324void ms_attach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
325{
326 if (tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF)
327 ms_attach_dl_tbf(ms, as_dl_tbf(tbf));
328 else
329 ms_attach_ul_tbf(ms, as_ul_tbf(tbf));
330}
331
332void ms_detach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
333{
334 if (tbf == (struct gprs_rlcmac_tbf *)(ms->ul_tbf)) {
335 ms->ul_tbf = NULL;
336 } else if (tbf == (struct gprs_rlcmac_tbf *)(ms->dl_tbf)) {
337 ms->dl_tbf = NULL;
338 } else {
339 bool found = false;
340
341 struct llist_item *pos, *tmp;
342 llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
343 struct gprs_rlcmac_tbf *tmp_tbf = (struct gprs_rlcmac_tbf *)pos->entry;
344 if (tmp_tbf == tbf) {
345 llist_del(&pos->list);
346 found = true;
347 break;
348 }
349 }
350
351 /* Protect against recursive calls via set_ms() */
352 if (!found)
353 return;
354 }
355
356 LOGP(DRLCMAC, LOGL_INFO, "Detaching TBF from MS object, TLLI = 0x%08x, TBF = %s\n",
357 ms_tlli(ms), tbf_name(tbf));
358
359 if (tbf_ms(tbf) == ms)
360 tbf_set_ms(tbf, NULL);
361
362 if (!ms->dl_tbf && !ms->ul_tbf) {
363 ms_set_reserved_slots(ms, NULL, 0, 0);
364
365 if (ms_tlli(ms) != 0)
366 ms_start_timer(ms);
367 }
368
369 ms_update_status(ms);
370}
371
372void ms_reset(struct GprsMs *ms)
373{
374 LOGP(DRLCMAC, LOGL_INFO,
375 "Clearing MS object, TLLI: 0x%08x, IMSI: '%s'\n",
376 ms_tlli(ms), ms_imsi(ms));
377
378 ms_stop_timer(ms);
379
380 ms->tlli = GSM_RESERVED_TMSI;
381 ms->new_dl_tlli = ms->tlli;
382 ms->new_ul_tlli = ms->tlli;
383 ms->imsi[0] = '\0';
384}
385
386static void ms_merge_old_ms(struct GprsMs *ms, struct GprsMs *old_ms)
387{
388 OSMO_ASSERT(old_ms != ms);
389
390 if (strlen(ms_imsi(ms)) == 0 && strlen(ms_imsi(old_ms)) != 0)
391 osmo_strlcpy(ms->imsi, ms_imsi(old_ms), sizeof(ms->imsi));
392
393 if (!ms_ms_class(ms) && ms_ms_class(old_ms))
394 ms_set_ms_class(ms, ms_ms_class(old_ms));
395
396 if (!ms_egprs_ms_class(ms) && ms_egprs_ms_class(old_ms))
397 ms_set_egprs_ms_class(ms, ms_egprs_ms_class(old_ms));
398
399 llc_queue_move_and_merge(&ms->llc_queue, &old_ms->llc_queue);
400
401 ms_reset(old_ms);
402}
403
404void ms_merge_and_clear_ms(struct GprsMs *ms, struct GprsMs *old_ms)
405{
406 OSMO_ASSERT(old_ms != ms);
407
408 ms_ref(old_ms);
409
410 /* Clean up the old MS object */
411 /* TODO: Use timer? */
412 if (ms_ul_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms), T_MAX))
413 tbf_free((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms));
414 if (ms_dl_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms), T_MAX))
415 tbf_free((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms));
416
417 ms_merge_old_ms(ms, old_ms);
418
419 ms_unref(old_ms);
420}
421
422void ms_set_tlli(struct GprsMs *ms, uint32_t tlli)
423{
424 if (tlli == ms->tlli || tlli == ms->new_ul_tlli)
425 return;
426
427 if (tlli != ms->new_dl_tlli) {
428 LOGP(DRLCMAC, LOGL_INFO,
429 "Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
430 "not yet confirmed\n",
431 ms_tlli(ms), tlli);
432 ms->new_ul_tlli = tlli;
433 return;
434 }
435
436 LOGP(DRLCMAC, LOGL_INFO,
437 "Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
438 "already confirmed partly\n",
439 ms->tlli, tlli);
440
441 ms->tlli = tlli;
442 ms->new_dl_tlli = GSM_RESERVED_TMSI;
443 ms->new_ul_tlli = GSM_RESERVED_TMSI;
444}
445
446bool ms_confirm_tlli(struct GprsMs *ms, uint32_t tlli)
447{
448 if (tlli == ms->tlli || tlli == ms->new_dl_tlli)
449 return false;
450
451 if (tlli != ms->new_ul_tlli) {
452 /* The MS has not sent a message with the new TLLI, which may
453 * happen according to the spec [TODO: add reference]. */
454
455 LOGP(DRLCMAC, LOGL_INFO,
456 "The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
457 "partly confirmed\n", tlli);
458 /* Use the network's idea of TLLI as candidate, this does not
459 * change the result value of tlli() */
460 ms->new_dl_tlli = tlli;
461 return false;
462 }
463
464 LOGP(DRLCMAC, LOGL_INFO,
465 "Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
466
467 ms->tlli = tlli;
468 ms->new_dl_tlli = GSM_RESERVED_TMSI;
469 ms->new_ul_tlli = GSM_RESERVED_TMSI;
470
471 return true;
472}
473
474void ms_set_imsi(struct GprsMs *ms, const char *imsi)
475{
476 if (!imsi) {
477 LOGP(DRLCMAC, LOGL_ERROR, "Expected IMSI!\n");
478 return;
479 }
480
481 if (imsi[0] && strlen(imsi) < 3) {
482 LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI '%s'!\n",
483 imsi);
484 return;
485 }
486
487 if (strcmp(imsi, ms->imsi) == 0)
488 return;
489
490 LOGP(DRLCMAC, LOGL_INFO,
491 "Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
492 ms_tlli(ms), ms->imsi, imsi);
493
494 struct GprsMs *old_ms = bts_ms_by_imsi(ms->bts, imsi);
495 /* Check if we are going to store a different MS object with already
496 existing IMSI. This is probably a bug in code calling this function,
497 since it should take care of this explicitly */
498 if (old_ms) {
499 /* We cannot find ms->ms by IMSI since we know that it has a
500 * different IMSI */
501 OSMO_ASSERT(old_ms != ms);
502
503 LOGPMS(ms, DRLCMAC, LOGL_NOTICE,
504 "IMSI '%s' was already assigned to another "
505 "MS object: TLLI = 0x%08x, that IMSI will be removed\n",
506 imsi, ms_tlli(old_ms));
507
508 ms_merge_and_clear_ms(ms, old_ms);
509 }
510
511
512 osmo_strlcpy(ms->imsi, imsi, sizeof(ms->imsi));
513}
514
515void ms_set_ta(struct GprsMs *ms, uint8_t ta_)
516{
517 if (ta_ == ms->ta)
518 return;
519
520 if (gsm48_ta_is_valid(ta_)) {
521 LOGP(DRLCMAC, LOGL_INFO,
522 "Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
523 ms_tlli(ms), ms->ta, ta_);
524 ms->ta = ta_;
525 } else
526 LOGP(DRLCMAC, LOGL_NOTICE,
527 "MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
528 "value %d kept)\n", ms_tlli(ms), ta_, ms->ta);
529}
530
531void ms_set_ms_class(struct GprsMs *ms, uint8_t ms_class_)
532{
533 if (ms_class_ == ms->ms_class)
534 return;
535
536 LOGP(DRLCMAC, LOGL_INFO,
537 "Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
538 ms_tlli(ms), ms->ms_class, ms_class_);
539
540 ms->ms_class = ms_class_;
541}
542
543void ms_set_egprs_ms_class(struct GprsMs *ms, uint8_t ms_class_)
544{
545 if (ms_class_ == ms->egprs_ms_class)
546 return;
547
548 LOGP(DRLCMAC, LOGL_INFO,
549 "Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
550 ms_tlli(ms), ms->egprs_ms_class, ms_class_);
551
552 ms->egprs_ms_class = ms_class_;
553
554 if (!bts_max_mcs_ul(ms->bts) || !bts_max_mcs_dl(ms->bts)) {
555 LOGPMS(ms, DRLCMAC, LOGL_DEBUG,
556 "Avoid enabling EGPRS because use of MCS is disabled: ul=%u dl=%u\n",
557 bts_max_mcs_ul(ms->bts), bts_max_mcs_dl(ms->bts));
558 return;
559 }
560
561 if (mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts))) &&
562 mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts))) &&
563 ms_mode(ms) != EGPRS)
564 {
565 ms_set_mode(ms, EGPRS_GMSK);
566 } else {
567 ms_set_mode(ms, EGPRS);
568 }
569 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Enabled EGPRS, mode %s\n", mode_name(ms_mode(ms)));
570}
571
572void ms_update_error_rate(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, int error_rate)
573{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100574 int64_t now;
575 enum CodingScheme max_cs_dl = ms_max_cs_dl(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100576 OSMO_ASSERT(max_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100577
578 if (error_rate < 0)
579 return;
580
581 now = now_msec();
582
583 /* TODO: Check for TBF direction */
584 /* TODO: Support different CS values for UL and DL */
585
586 ms->nack_rate_dl = error_rate;
587
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100588 if (error_rate > the_pcu->vty.cs_adj_upper_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100589 if (mcs_chan_code(ms->current_cs_dl) > 0) {
590 mcs_dec_kind(&ms->current_cs_dl, ms_mode(ms));
591 LOGP(DRLCMACDL, LOGL_INFO,
592 "MS (IMSI %s): High error rate %d%%, "
593 "reducing CS level to %s\n",
594 ms_imsi(ms), error_rate, mcs_name(ms->current_cs_dl));
595 ms->last_cs_not_low = now;
596 }
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100597 } else if (error_rate < the_pcu->vty.cs_adj_lower_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100598 if (ms->current_cs_dl < max_cs_dl) {
599 if (now - ms->last_cs_not_low > 1000) {
600 mcs_inc_kind(&ms->current_cs_dl, ms_mode(ms));
601
602 LOGP(DRLCMACDL, LOGL_INFO,
603 "MS (IMSI %s): Low error rate %d%%, "
604 "increasing DL CS level to %s\n",
605 ms_imsi(ms), error_rate,
606 mcs_name(ms->current_cs_dl));
607 ms->last_cs_not_low = now;
608 } else {
609 LOGP(DRLCMACDL, LOGL_DEBUG,
610 "MS (IMSI %s): Low error rate %d%%, "
611 "ignored (within blocking period)\n",
612 ms_imsi(ms), error_rate);
613 }
614 }
615 } else {
616 LOGP(DRLCMACDL, LOGL_DEBUG,
617 "MS (IMSI %s): Medium error rate %d%%, ignored\n",
618 ms_imsi(ms), error_rate);
619 ms->last_cs_not_low = now;
620 }
621}
622
623enum CodingScheme ms_max_cs_ul(const struct GprsMs *ms)
624{
625 OSMO_ASSERT(ms->bts != NULL);
626
627 if (mcs_is_gprs(ms->current_cs_ul)) {
628 if (!bts_max_cs_ul(ms->bts)) {
629 return CS4;
630 }
631
632 return mcs_get_gprs_by_num(bts_max_cs_ul(ms->bts));
633 }
634
635 if (!mcs_is_edge(ms->current_cs_ul))
636 return UNKNOWN;
637
638 if (bts_max_mcs_ul(ms->bts))
639 return mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts));
640 else if (bts_max_cs_ul(ms->bts))
641 return mcs_get_gprs_by_num(bts_max_cs_ul(ms->bts));
642
643 return MCS4;
644}
645
646void ms_set_current_cs_dl(struct GprsMs *ms, enum CodingScheme scheme)
647{
648 ms->current_cs_dl = scheme;
649}
650
651enum CodingScheme ms_max_cs_dl(const struct GprsMs *ms)
652{
653 OSMO_ASSERT(ms->bts != NULL);
654
655 if (mcs_is_gprs(ms->current_cs_dl)) {
656 if (!bts_max_cs_dl(ms->bts)) {
657 return CS4;
658 }
659
660 return mcs_get_gprs_by_num(bts_max_cs_dl(ms->bts));
661 }
662
663 if (!mcs_is_edge(ms->current_cs_dl))
664 return UNKNOWN;
665
666 if (bts_max_mcs_dl(ms->bts))
667 return mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts));
668 else if (bts_max_cs_dl(ms->bts))
669 return mcs_get_gprs_by_num(bts_max_cs_dl(ms->bts));
670
671 return MCS4;
672}
673
674void ms_update_cs_ul(struct GprsMs *ms, const struct pcu_l1_meas *meas)
675{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100676 enum CodingScheme max_cs_ul = ms_max_cs_ul(ms);
677
678 int old_link_qual;
679 int low;
680 int high;
681 enum CodingScheme new_cs_ul = ms->current_cs_ul;
682 uint8_t current_cs = mcs_chan_code(ms->current_cs_ul);
683
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100684 if (!max_cs_ul) {
685 LOGP(DRLCMACMEAS, LOGL_ERROR,
686 "max_cs_ul cannot be derived (current UL CS: %s)\n",
687 mcs_name(ms->current_cs_ul));
688 return;
689 }
690
691 if (!ms->current_cs_ul) {
692 LOGP(DRLCMACMEAS, LOGL_ERROR,
693 "Unable to update UL (M)CS because it's not set: %s\n",
694 mcs_name(ms->current_cs_ul));
695 return;
696 }
697
698 if (!meas->have_link_qual) {
699 LOGP(DRLCMACMEAS, LOGL_ERROR,
700 "Unable to update UL (M)CS %s because we don't have link quality measurements.\n",
701 mcs_name(ms->current_cs_ul));
702 return;
703 }
704
705 if (mcs_is_gprs(ms->current_cs_ul)) {
706 if (current_cs >= MAX_GPRS_CS)
707 current_cs = MAX_GPRS_CS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100708 low = the_pcu->vty.cs_lqual_ranges[current_cs].low;
709 high = the_pcu->vty.cs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100710 } else if (mcs_is_edge(ms->current_cs_ul)) {
711 if (current_cs >= MAX_EDGE_MCS)
712 current_cs = MAX_EDGE_MCS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100713 low = the_pcu->vty.mcs_lqual_ranges[current_cs].low;
714 high = the_pcu->vty.mcs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100715 } else {
716 LOGP(DRLCMACMEAS, LOGL_ERROR,
717 "Unable to update UL (M)CS because it's neither GPRS nor EDGE: %s\n",
718 mcs_name(ms->current_cs_ul));
719 return;
720 }
721
722 /* To avoid rapid changes of the coding scheme, we also take
723 * the old link quality value into account (if present). */
724 if (ms->l1_meas.have_link_qual)
725 old_link_qual = ms->l1_meas.link_qual;
726 else
727 old_link_qual = meas->link_qual;
728
729 if (meas->link_qual < low && old_link_qual < low)
730 mcs_dec_kind(&new_cs_ul, ms_mode(ms));
731 else if (meas->link_qual > high && old_link_qual > high &&
732 ms->current_cs_ul < max_cs_ul)
733 mcs_inc_kind(&new_cs_ul, ms_mode(ms));
734
735 if (ms->current_cs_ul != new_cs_ul) {
736 LOGPMS(ms, DRLCMACMEAS, LOGL_INFO,
737 "Link quality %ddB (old %ddB) left window [%d, %d], "
738 "modifying uplink CS level: %s -> %s\n",
739 meas->link_qual, old_link_qual,
740 low, high,
741 mcs_name(ms->current_cs_ul), mcs_name(new_cs_ul));
742
743 ms->current_cs_ul = new_cs_ul;
744 }
745}
746
747void ms_update_l1_meas(struct GprsMs *ms, const struct pcu_l1_meas *meas)
748{
749 unsigned i;
750
751 ms_update_cs_ul(ms, meas);
752
753 if (meas->have_rssi)
754 pcu_l1_meas_set_rssi(&ms->l1_meas, meas->rssi);
755 if (meas->have_bto)
756 pcu_l1_meas_set_bto(&ms->l1_meas, meas->bto);
757 if (meas->have_ber)
758 pcu_l1_meas_set_ber(&ms->l1_meas, meas->ber);
759 if (meas->have_link_qual)
760 pcu_l1_meas_set_link_qual(&ms->l1_meas, meas->link_qual);
761
762 if (meas->have_ms_rx_qual)
763 pcu_l1_meas_set_ms_rx_qual(&ms->l1_meas, meas->ms_rx_qual);
764 if (meas->have_ms_c_value)
765 pcu_l1_meas_set_ms_c_value(&ms->l1_meas, meas->ms_c_value);
766 if (meas->have_ms_sign_var)
767 pcu_l1_meas_set_ms_sign_var(&ms->l1_meas, meas->ms_sign_var);
768
769 if (meas->have_ms_i_level) {
770 for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
771 if (meas->ts[i].have_ms_i_level)
772 pcu_l1_meas_set_ms_i_level(&ms->l1_meas, i, meas->ts[i].ms_i_level);
773 else
774 ms->l1_meas.ts[i].have_ms_i_level = 0;
775 }
776 }
777}
778
779enum CodingScheme ms_current_cs_dl(const struct GprsMs *ms)
780{
781 enum CodingScheme cs = ms->current_cs_dl;
782 size_t unencoded_octets;
783
784 if (!ms->bts)
785 return cs;
786
787 unencoded_octets = llc_queue_octets(&ms->llc_queue);
788
789 /* If the DL TBF is active, add number of unencoded chunk octets */
790 if (ms->dl_tbf)
791 unencoded_octets += llc_chunk_size(tbf_llc((struct gprs_rlcmac_tbf *)ms->dl_tbf));
792
793 /* There are many unencoded octets, don't reduce */
Pau Espin Pedrolad79b852021-01-14 13:20:55 +0100794 if (unencoded_octets >= the_pcu->vty.cs_downgrade_threshold)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100795 return cs;
796
797 /* RF conditions are good, don't reduce */
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100798 if (ms->nack_rate_dl < the_pcu->vty.cs_adj_lower_limit)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100799 return cs;
800
801 /* The throughput would probably be better if the CS level was reduced */
802 mcs_dec_kind(&cs, ms_mode(ms));
803
804 /* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
805 if (cs == CS2)
806 mcs_dec_kind(&cs, ms_mode(ms));
807
808 return cs;
809}
810
811int ms_first_common_ts(const struct GprsMs *ms)
812{
813 if (ms->dl_tbf)
814 return tbf_first_common_ts((struct gprs_rlcmac_tbf *)ms->dl_tbf);
815
816 if (ms->ul_tbf)
817 return tbf_first_common_ts((struct gprs_rlcmac_tbf *)ms->ul_tbf);
818
819 return -1;
820}
821
822uint8_t ms_dl_slots(const struct GprsMs *ms)
823{
824 uint8_t slots = 0;
825
826 if (ms->dl_tbf)
827 slots |= tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
828
829 if (ms->ul_tbf)
830 slots |= tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf);
831
832 return slots;
833}
834
835uint8_t ms_ul_slots(const struct GprsMs *ms)
836{
837 uint8_t slots = 0;
838
839 if (ms->dl_tbf)
840 slots |= tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
841
842 if (ms->ul_tbf)
843 slots |= tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf);
844
845 return slots;
846}
847
848uint8_t ms_current_pacch_slots(const struct GprsMs *ms)
849{
850 uint8_t slots = 0;
851
852 bool is_dl_active = ms->dl_tbf && tbf_is_tfi_assigned((struct gprs_rlcmac_tbf *)ms->dl_tbf);
853 bool is_ul_active = ms->ul_tbf && tbf_is_tfi_assigned((struct gprs_rlcmac_tbf *)ms->ul_tbf);
854
855 if (!is_dl_active && !is_ul_active)
856 return 0;
857
858 /* see TS 44.060, 8.1.1.2.2 */
859 if (is_dl_active && !is_ul_active)
860 slots = tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
861 else if (!is_dl_active && is_ul_active)
862 slots = tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf);
863 else
864 slots = tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf) &
865 tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
866
867 /* Assume a multislot class 1 device */
868 /* TODO: For class 2 devices, this could be removed */
869 slots = pcu_lsb(slots);
870
871 return slots;
872}
873
874void ms_set_reserved_slots(struct GprsMs *ms, struct gprs_rlcmac_trx *trx,
875 uint8_t ul_slots, uint8_t dl_slots)
876{
877 if (ms->current_trx) {
878 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
879 ms->reserved_dl_slots);
880 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
881 ms->reserved_ul_slots);
882 ms->reserved_dl_slots = 0;
883 ms->reserved_ul_slots = 0;
884 }
885 ms->current_trx = trx;
886 if (trx) {
887 ms->reserved_dl_slots = dl_slots;
888 ms->reserved_ul_slots = ul_slots;
889 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
890 ms->reserved_dl_slots);
891 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
892 ms->reserved_ul_slots);
893 }
894}
895
896struct gprs_rlcmac_tbf *ms_tbf(const struct GprsMs *ms, enum gprs_rlcmac_tbf_direction dir)
897{
898 switch (dir) {
899 case GPRS_RLCMAC_DL_TBF: return (struct gprs_rlcmac_tbf *)ms->dl_tbf;
900 case GPRS_RLCMAC_UL_TBF: return (struct gprs_rlcmac_tbf *)ms->ul_tbf;
901 }
902
903 return NULL;
904}