blob: 0d6be4d59029fa6928af04becbcdd366044d2baf [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
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +010082static void ms_release_timer_cb(void *data)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010083{
84 struct GprsMs *ms = (struct GprsMs *) data;
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +010085 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Release timer expired\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010086
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));
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100118 ms->timer.cb = ms_release_timer_cb;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100119 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
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100150 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Destroying MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100151
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
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100220static void ms_release_timer_start(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100221{
222 if (ms->delay == 0)
223 return;
224
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100225 LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Schedule MS release in %u secs\n", ms->delay);
226
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100227 if (!ms->timer.data)
228 ms->timer.data = ms_ref(ms);
229
230 osmo_timer_schedule(&ms->timer, ms->delay, 0);
231}
232
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100233static void ms_release_timer_stop(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100234{
235 if (!ms->timer.data)
236 return;
237
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100238 LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Cancel scheduled MS release\n");
239
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100240 osmo_timer_del(&ms->timer);
241 ms->timer.data = NULL;
242 ms_unref(ms);
243}
244
245void ms_set_mode(struct GprsMs *ms, enum mcs_kind mode)
246{
247 ms->mode = mode;
248
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100249 switch (ms->mode) {
250 case GPRS:
251 if (!mcs_is_gprs(ms->current_cs_ul)) {
252 ms->current_cs_ul = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100253 ms->bts->initial_cs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100254 if (!mcs_is_valid(ms->current_cs_ul))
255 ms->current_cs_ul = CS1;
256 }
257 if (!mcs_is_gprs(ms->current_cs_dl)) {
258 ms->current_cs_dl = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100259 ms->bts->initial_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100260 if (!mcs_is_valid(ms->current_cs_dl))
261 ms->current_cs_dl = CS1;
262 }
263 break;
264
265 case EGPRS_GMSK:
Pau Espin Pedrol7bb8cd62021-01-25 15:08:35 +0100266 if (!mcs_is_edge_gmsk(ms->current_cs_ul)) {
267 ms->current_cs_ul = mcs_get_egprs_by_num(
268 ms->bts->initial_mcs_ul);
269 if (!mcs_is_valid(ms->current_cs_ul))
270 ms->current_cs_ul = MCS1;
271 }
272 if (!mcs_is_edge_gmsk(ms->current_cs_dl)) {
273 ms->current_cs_dl = mcs_get_egprs_by_num(
274 ms->bts->initial_mcs_dl);
275 if (!mcs_is_valid(ms->current_cs_dl))
276 ms->current_cs_dl = MCS1;
277 }
278 break;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100279 case EGPRS:
280 if (!mcs_is_edge(ms->current_cs_ul)) {
281 ms->current_cs_ul = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100282 ms->bts->initial_mcs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100283 if (!mcs_is_valid(ms->current_cs_ul))
284 ms->current_cs_ul = MCS1;
285 }
286 if (!mcs_is_edge(ms->current_cs_dl)) {
287 ms->current_cs_dl = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100288 ms->bts->initial_mcs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100289 if (!mcs_is_valid(ms->current_cs_dl))
290 ms->current_cs_dl = MCS1;
291 }
292 break;
293 }
294}
295
296static void ms_attach_ul_tbf(struct GprsMs *ms, struct gprs_rlcmac_ul_tbf *tbf)
297{
298 if (ms->ul_tbf == tbf)
299 return;
300
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100301 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Attaching UL TBF: %s\n", tbf_name((struct gprs_rlcmac_tbf *)tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100302
303 ms_ref(ms);
304
305 if (ms->ul_tbf)
306 llist_add_tail(tbf_ms_list((struct gprs_rlcmac_tbf *)ms->ul_tbf), &ms->old_tbfs);
307
308 ms->ul_tbf = tbf;
309
310 if (tbf)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100311 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100312
313 ms_unref(ms);
314}
315
316static void ms_attach_dl_tbf(struct GprsMs *ms, struct gprs_rlcmac_dl_tbf *tbf)
317{
318 if (ms->dl_tbf == tbf)
319 return;
320
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100321 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Attaching DL TBF: %s\n", tbf_name((struct gprs_rlcmac_tbf *)tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100322
323 ms_ref(ms);
324
325 if (ms->dl_tbf)
326 llist_add_tail(tbf_ms_list((struct gprs_rlcmac_tbf *)ms->dl_tbf), &ms->old_tbfs);
327
328 ms->dl_tbf = tbf;
329
330 if (tbf)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100331 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100332
333 ms_unref(ms);
334}
335
336void ms_attach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
337{
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200338 if (tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100339 ms_attach_dl_tbf(ms, as_dl_tbf(tbf));
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200340 else
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100341 ms_attach_ul_tbf(ms, as_ul_tbf(tbf));
342}
343
344void ms_detach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
345{
346 if (tbf == (struct gprs_rlcmac_tbf *)(ms->ul_tbf)) {
347 ms->ul_tbf = NULL;
348 } else if (tbf == (struct gprs_rlcmac_tbf *)(ms->dl_tbf)) {
349 ms->dl_tbf = NULL;
350 } else {
351 bool found = false;
352
353 struct llist_item *pos, *tmp;
354 llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
355 struct gprs_rlcmac_tbf *tmp_tbf = (struct gprs_rlcmac_tbf *)pos->entry;
356 if (tmp_tbf == tbf) {
357 llist_del(&pos->list);
358 found = true;
359 break;
360 }
361 }
362
363 /* Protect against recursive calls via set_ms() */
364 if (!found)
365 return;
366 }
367
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100368 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Detaching TBF: %s\n",
369 tbf_name(tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100370
371 if (tbf_ms(tbf) == ms)
372 tbf_set_ms(tbf, NULL);
373
374 if (!ms->dl_tbf && !ms->ul_tbf) {
375 ms_set_reserved_slots(ms, NULL, 0, 0);
376
377 if (ms_tlli(ms) != 0)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100378 ms_release_timer_start(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100379 }
380
381 ms_update_status(ms);
382}
383
384void ms_reset(struct GprsMs *ms)
385{
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100386 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Clearing MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100387
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100388 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100389
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}
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +0100940
941int ms_nacc_start(struct GprsMs *ms, Packet_Cell_Change_Notification_t *notif)
942{
943 if (!ms->nacc)
944 ms->nacc = nacc_fsm_alloc(ms);
945 if (!ms->nacc)
946 return -EINVAL;
947 return osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_RX_CELL_CHG_NOTIFICATION, notif);
948}
949
950bool ms_nacc_rts(const struct GprsMs *ms)
951{
952 if (!ms->nacc)
953 return false;
954 if (ms->nacc->fi->state == NACC_ST_TX_NEIGHBOUR_DATA ||
955 ms->nacc->fi->state == NACC_ST_TX_CELL_CHG_CONTINUE)
956 return true;
957 return false;
958}
959
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +0100960struct msgb *ms_nacc_create_rlcmac_msg(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, uint32_t fn, uint8_t ts)
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +0100961{
962 int rc;
963 struct nacc_ev_create_rlcmac_msg_ctx data_ctx;
964
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +0100965 data_ctx = (struct nacc_ev_create_rlcmac_msg_ctx) {
966 .tbf = tbf,
967 .fn = fn,
968 .ts = ts,
969 .msg = NULL,
970 };
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +0100971
972 rc = osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_CREATE_RLCMAC_MSG, &data_ctx);
973 if (rc != 0 || !data_ctx.msg)
974 return NULL;
975 return data_ctx.msg;
976}