blob: 5e75d067126e2365c399e474966f9bbc0d5f9f44 [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
Pau Espin Pedrol5deac142021-11-12 18:01:50 +0100525uint16_t ms_paging_group(struct GprsMs *ms)
526{
527 uint16_t pgroup;
528 if (!ms_imsi_is_valid(ms))
529 return 0; /* 000 is the special "all paging" group */
530 if ((pgroup = imsi2paging_group(ms_imsi(ms))) > 999) {
531 LOGPMS(ms, DRLCMAC, LOGL_ERROR, "IMSI to paging group failed!\n");
532 return 0;
533 }
534 return pgroup;
535}
536
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100537void ms_set_ta(struct GprsMs *ms, uint8_t ta_)
538{
539 if (ta_ == ms->ta)
540 return;
541
542 if (gsm48_ta_is_valid(ta_)) {
543 LOGP(DRLCMAC, LOGL_INFO,
544 "Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
545 ms_tlli(ms), ms->ta, ta_);
546 ms->ta = ta_;
547 } else
548 LOGP(DRLCMAC, LOGL_NOTICE,
549 "MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
550 "value %d kept)\n", ms_tlli(ms), ta_, ms->ta);
551}
552
553void ms_set_ms_class(struct GprsMs *ms, uint8_t ms_class_)
554{
555 if (ms_class_ == ms->ms_class)
556 return;
557
558 LOGP(DRLCMAC, LOGL_INFO,
559 "Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
560 ms_tlli(ms), ms->ms_class, ms_class_);
561
562 ms->ms_class = ms_class_;
563}
564
565void ms_set_egprs_ms_class(struct GprsMs *ms, uint8_t ms_class_)
566{
567 if (ms_class_ == ms->egprs_ms_class)
568 return;
569
570 LOGP(DRLCMAC, LOGL_INFO,
571 "Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
572 ms_tlli(ms), ms->egprs_ms_class, ms_class_);
573
574 ms->egprs_ms_class = ms_class_;
575
576 if (!bts_max_mcs_ul(ms->bts) || !bts_max_mcs_dl(ms->bts)) {
577 LOGPMS(ms, DRLCMAC, LOGL_DEBUG,
578 "Avoid enabling EGPRS because use of MCS is disabled: ul=%u dl=%u\n",
579 bts_max_mcs_ul(ms->bts), bts_max_mcs_dl(ms->bts));
580 return;
581 }
582
583 if (mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts))) &&
584 mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts))) &&
585 ms_mode(ms) != EGPRS)
586 {
587 ms_set_mode(ms, EGPRS_GMSK);
588 } else {
589 ms_set_mode(ms, EGPRS);
590 }
591 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Enabled EGPRS, mode %s\n", mode_name(ms_mode(ms)));
592}
593
594void ms_update_error_rate(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, int error_rate)
595{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100596 int64_t now;
597 enum CodingScheme max_cs_dl = ms_max_cs_dl(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100598 OSMO_ASSERT(max_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100599
600 if (error_rate < 0)
601 return;
602
603 now = now_msec();
604
605 /* TODO: Check for TBF direction */
606 /* TODO: Support different CS values for UL and DL */
607
608 ms->nack_rate_dl = error_rate;
609
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100610 if (error_rate > the_pcu->vty.cs_adj_upper_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100611 if (mcs_chan_code(ms->current_cs_dl) > 0) {
612 mcs_dec_kind(&ms->current_cs_dl, ms_mode(ms));
613 LOGP(DRLCMACDL, LOGL_INFO,
614 "MS (IMSI %s): High error rate %d%%, "
615 "reducing CS level to %s\n",
616 ms_imsi(ms), error_rate, mcs_name(ms->current_cs_dl));
617 ms->last_cs_not_low = now;
618 }
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100619 } else if (error_rate < the_pcu->vty.cs_adj_lower_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100620 if (ms->current_cs_dl < max_cs_dl) {
621 if (now - ms->last_cs_not_low > 1000) {
622 mcs_inc_kind(&ms->current_cs_dl, ms_mode(ms));
623
624 LOGP(DRLCMACDL, LOGL_INFO,
625 "MS (IMSI %s): Low error rate %d%%, "
626 "increasing DL CS level to %s\n",
627 ms_imsi(ms), error_rate,
628 mcs_name(ms->current_cs_dl));
629 ms->last_cs_not_low = now;
630 } else {
631 LOGP(DRLCMACDL, LOGL_DEBUG,
632 "MS (IMSI %s): Low error rate %d%%, "
633 "ignored (within blocking period)\n",
634 ms_imsi(ms), error_rate);
635 }
636 }
637 } else {
638 LOGP(DRLCMACDL, LOGL_DEBUG,
639 "MS (IMSI %s): Medium error rate %d%%, ignored\n",
640 ms_imsi(ms), error_rate);
641 ms->last_cs_not_low = now;
642 }
643}
644
645enum CodingScheme ms_max_cs_ul(const struct GprsMs *ms)
646{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100647 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100648 OSMO_ASSERT(ms->bts != NULL);
649
650 if (mcs_is_gprs(ms->current_cs_ul)) {
651 if (!bts_max_cs_ul(ms->bts)) {
652 return CS4;
653 }
654
655 return mcs_get_gprs_by_num(bts_max_cs_ul(ms->bts));
656 }
657
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100658 cs = mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts));
659 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
660 cs = MCS4;
661 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100662}
663
664void ms_set_current_cs_dl(struct GprsMs *ms, enum CodingScheme scheme)
665{
666 ms->current_cs_dl = scheme;
667}
668
669enum CodingScheme ms_max_cs_dl(const struct GprsMs *ms)
670{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100671 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100672 OSMO_ASSERT(ms->bts != NULL);
673
674 if (mcs_is_gprs(ms->current_cs_dl)) {
675 if (!bts_max_cs_dl(ms->bts)) {
676 return CS4;
677 }
678
679 return mcs_get_gprs_by_num(bts_max_cs_dl(ms->bts));
680 }
681
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100682 cs = mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts));
683 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
684 cs = MCS4;
685 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100686}
687
688void ms_update_cs_ul(struct GprsMs *ms, const struct pcu_l1_meas *meas)
689{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100690 enum CodingScheme max_cs_ul = ms_max_cs_ul(ms);
691
692 int old_link_qual;
693 int low;
694 int high;
695 enum CodingScheme new_cs_ul = ms->current_cs_ul;
696 uint8_t current_cs = mcs_chan_code(ms->current_cs_ul);
697
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100698 if (!max_cs_ul) {
699 LOGP(DRLCMACMEAS, LOGL_ERROR,
700 "max_cs_ul cannot be derived (current UL CS: %s)\n",
701 mcs_name(ms->current_cs_ul));
702 return;
703 }
704
705 if (!ms->current_cs_ul) {
706 LOGP(DRLCMACMEAS, LOGL_ERROR,
707 "Unable to update UL (M)CS because it's not set: %s\n",
708 mcs_name(ms->current_cs_ul));
709 return;
710 }
711
712 if (!meas->have_link_qual) {
713 LOGP(DRLCMACMEAS, LOGL_ERROR,
714 "Unable to update UL (M)CS %s because we don't have link quality measurements.\n",
715 mcs_name(ms->current_cs_ul));
716 return;
717 }
718
719 if (mcs_is_gprs(ms->current_cs_ul)) {
720 if (current_cs >= MAX_GPRS_CS)
721 current_cs = MAX_GPRS_CS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100722 low = the_pcu->vty.cs_lqual_ranges[current_cs].low;
723 high = the_pcu->vty.cs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100724 } else if (mcs_is_edge(ms->current_cs_ul)) {
725 if (current_cs >= MAX_EDGE_MCS)
726 current_cs = MAX_EDGE_MCS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100727 low = the_pcu->vty.mcs_lqual_ranges[current_cs].low;
728 high = the_pcu->vty.mcs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100729 } else {
730 LOGP(DRLCMACMEAS, LOGL_ERROR,
731 "Unable to update UL (M)CS because it's neither GPRS nor EDGE: %s\n",
732 mcs_name(ms->current_cs_ul));
733 return;
734 }
735
736 /* To avoid rapid changes of the coding scheme, we also take
737 * the old link quality value into account (if present). */
738 if (ms->l1_meas.have_link_qual)
739 old_link_qual = ms->l1_meas.link_qual;
740 else
741 old_link_qual = meas->link_qual;
742
743 if (meas->link_qual < low && old_link_qual < low)
744 mcs_dec_kind(&new_cs_ul, ms_mode(ms));
745 else if (meas->link_qual > high && old_link_qual > high &&
746 ms->current_cs_ul < max_cs_ul)
747 mcs_inc_kind(&new_cs_ul, ms_mode(ms));
748
749 if (ms->current_cs_ul != new_cs_ul) {
750 LOGPMS(ms, DRLCMACMEAS, LOGL_INFO,
751 "Link quality %ddB (old %ddB) left window [%d, %d], "
752 "modifying uplink CS level: %s -> %s\n",
753 meas->link_qual, old_link_qual,
754 low, high,
755 mcs_name(ms->current_cs_ul), mcs_name(new_cs_ul));
756
757 ms->current_cs_ul = new_cs_ul;
758 }
759}
760
761void ms_update_l1_meas(struct GprsMs *ms, const struct pcu_l1_meas *meas)
762{
763 unsigned i;
764
765 ms_update_cs_ul(ms, meas);
766
767 if (meas->have_rssi)
768 pcu_l1_meas_set_rssi(&ms->l1_meas, meas->rssi);
769 if (meas->have_bto)
770 pcu_l1_meas_set_bto(&ms->l1_meas, meas->bto);
771 if (meas->have_ber)
772 pcu_l1_meas_set_ber(&ms->l1_meas, meas->ber);
773 if (meas->have_link_qual)
774 pcu_l1_meas_set_link_qual(&ms->l1_meas, meas->link_qual);
775
776 if (meas->have_ms_rx_qual)
777 pcu_l1_meas_set_ms_rx_qual(&ms->l1_meas, meas->ms_rx_qual);
778 if (meas->have_ms_c_value)
779 pcu_l1_meas_set_ms_c_value(&ms->l1_meas, meas->ms_c_value);
780 if (meas->have_ms_sign_var)
781 pcu_l1_meas_set_ms_sign_var(&ms->l1_meas, meas->ms_sign_var);
782
783 if (meas->have_ms_i_level) {
784 for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
785 if (meas->ts[i].have_ms_i_level)
786 pcu_l1_meas_set_ms_i_level(&ms->l1_meas, i, meas->ts[i].ms_i_level);
787 else
788 ms->l1_meas.ts[i].have_ms_i_level = 0;
789 }
790 }
791}
792
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100793/* req_mcs_kind acts as a set filter, where EGPRS means any and GPRS is the most restrictive */
794enum CodingScheme ms_current_cs_dl(const struct GprsMs *ms, enum mcs_kind req_mcs_kind)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100795{
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100796 enum CodingScheme orig_cs = ms->current_cs_dl;
797 struct gprs_rlcmac_bts *bts = ms->bts;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100798 size_t unencoded_octets;
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100799 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100800
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100801 /* It could be that a TBF requests a GPRS CS despite the MS currently
802 being upgraded to EGPRS (hence reporting MCS). That could happen
803 because the TBF was created early in the process where we didn't have
804 yet enough information about the MS, and only AFTER it was created we
805 upgraded the MS to be EGPRS capable.
806 As a result, when the MS is queried for the target CS here, we could be
807 returning an MCS despite the current TBF being established as GPRS,
808 but we rather stick to the TBF type we assigned to the MS rather than
809 magically sending EGPRS data blocks to a GPRS TBF.
810 It could also be that the caller requests specific MCS kind
811 explicitly too due to scheduling restrictions (GPRS+EGPRS multiplexing). */
812 if (req_mcs_kind == EGPRS_GMSK && mcs_is_edge(orig_cs) && orig_cs > MCS4) {
813 cs = bts_cs_dl_is_supported(bts, MCS4) ? MCS4 :
814 bts_cs_dl_is_supported(bts, MCS3) ? MCS3 :
815 bts_cs_dl_is_supported(bts, MCS2) ? MCS2 :
816 MCS1;
817 } else if (req_mcs_kind == GPRS && mcs_is_edge(orig_cs)) { /* GPRS */
818 int i;
819 cs = orig_cs > MCS4 ? MCS4 : orig_cs;
820 cs -= (MCS1 - CS1); /* MCSx -> CSx */
821 /* Find suitable CS starting from equivalent MCS which is supported by BTS: */
822 for (i = mcs_chan_code(cs); !bts_cs_dl_is_supported(bts, CS1 + i); i--);
823 OSMO_ASSERT(i >= 0 && i <= 3); /* CS1 is always supported */
824 cs = CS1 + i;
825 } else {
826 cs = orig_cs;
827 }
828
829 if (orig_cs != cs)
830 LOGPMS(ms, DRLCMACDL, LOGL_INFO, "MS (mode=%s) suggests transmitting "
831 "DL %s, downgrade to %s in order to match TBF & scheduler requirements\n",
832 mode_name(ms_mode(ms)), mcs_name(orig_cs), mcs_name(cs));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100833
834 unencoded_octets = llc_queue_octets(&ms->llc_queue);
835
836 /* If the DL TBF is active, add number of unencoded chunk octets */
837 if (ms->dl_tbf)
838 unencoded_octets += llc_chunk_size(tbf_llc((struct gprs_rlcmac_tbf *)ms->dl_tbf));
839
840 /* There are many unencoded octets, don't reduce */
Pau Espin Pedrolad79b852021-01-14 13:20:55 +0100841 if (unencoded_octets >= the_pcu->vty.cs_downgrade_threshold)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100842 return cs;
843
844 /* RF conditions are good, don't reduce */
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100845 if (ms->nack_rate_dl < the_pcu->vty.cs_adj_lower_limit)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100846 return cs;
847
848 /* The throughput would probably be better if the CS level was reduced */
849 mcs_dec_kind(&cs, ms_mode(ms));
850
851 /* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
852 if (cs == CS2)
853 mcs_dec_kind(&cs, ms_mode(ms));
854
855 return cs;
856}
857
858int ms_first_common_ts(const struct GprsMs *ms)
859{
860 if (ms->dl_tbf)
861 return tbf_first_common_ts((struct gprs_rlcmac_tbf *)ms->dl_tbf);
862
863 if (ms->ul_tbf)
864 return tbf_first_common_ts((struct gprs_rlcmac_tbf *)ms->ul_tbf);
865
866 return -1;
867}
868
869uint8_t ms_dl_slots(const struct GprsMs *ms)
870{
871 uint8_t slots = 0;
872
873 if (ms->dl_tbf)
874 slots |= tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
875
876 if (ms->ul_tbf)
877 slots |= tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf);
878
879 return slots;
880}
881
882uint8_t ms_ul_slots(const struct GprsMs *ms)
883{
884 uint8_t slots = 0;
885
886 if (ms->dl_tbf)
887 slots |= tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
888
889 if (ms->ul_tbf)
890 slots |= tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf);
891
892 return slots;
893}
894
895uint8_t ms_current_pacch_slots(const struct GprsMs *ms)
896{
897 uint8_t slots = 0;
898
899 bool is_dl_active = ms->dl_tbf && tbf_is_tfi_assigned((struct gprs_rlcmac_tbf *)ms->dl_tbf);
900 bool is_ul_active = ms->ul_tbf && tbf_is_tfi_assigned((struct gprs_rlcmac_tbf *)ms->ul_tbf);
901
902 if (!is_dl_active && !is_ul_active)
903 return 0;
904
905 /* see TS 44.060, 8.1.1.2.2 */
906 if (is_dl_active && !is_ul_active)
907 slots = tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
908 else if (!is_dl_active && is_ul_active)
909 slots = tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf);
910 else
911 slots = tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf) &
912 tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
913
914 /* Assume a multislot class 1 device */
915 /* TODO: For class 2 devices, this could be removed */
916 slots = pcu_lsb(slots);
917
918 return slots;
919}
920
921void ms_set_reserved_slots(struct GprsMs *ms, struct gprs_rlcmac_trx *trx,
922 uint8_t ul_slots, uint8_t dl_slots)
923{
924 if (ms->current_trx) {
925 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
926 ms->reserved_dl_slots);
927 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
928 ms->reserved_ul_slots);
929 ms->reserved_dl_slots = 0;
930 ms->reserved_ul_slots = 0;
931 }
932 ms->current_trx = trx;
933 if (trx) {
934 ms->reserved_dl_slots = dl_slots;
935 ms->reserved_ul_slots = ul_slots;
936 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
937 ms->reserved_dl_slots);
938 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
939 ms->reserved_ul_slots);
940 }
941}
942
943struct gprs_rlcmac_tbf *ms_tbf(const struct GprsMs *ms, enum gprs_rlcmac_tbf_direction dir)
944{
945 switch (dir) {
946 case GPRS_RLCMAC_DL_TBF: return (struct gprs_rlcmac_tbf *)ms->dl_tbf;
947 case GPRS_RLCMAC_UL_TBF: return (struct gprs_rlcmac_tbf *)ms->ul_tbf;
948 }
949
950 return NULL;
951}
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +0100952
953int ms_nacc_start(struct GprsMs *ms, Packet_Cell_Change_Notification_t *notif)
954{
955 if (!ms->nacc)
956 ms->nacc = nacc_fsm_alloc(ms);
957 if (!ms->nacc)
958 return -EINVAL;
959 return osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_RX_CELL_CHG_NOTIFICATION, notif);
960}
961
962bool ms_nacc_rts(const struct GprsMs *ms)
963{
964 if (!ms->nacc)
965 return false;
966 if (ms->nacc->fi->state == NACC_ST_TX_NEIGHBOUR_DATA ||
967 ms->nacc->fi->state == NACC_ST_TX_CELL_CHG_CONTINUE)
968 return true;
969 return false;
970}
971
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +0100972struct 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 +0100973{
974 int rc;
975 struct nacc_ev_create_rlcmac_msg_ctx data_ctx;
976
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +0100977 data_ctx = (struct nacc_ev_create_rlcmac_msg_ctx) {
978 .tbf = tbf,
979 .fn = fn,
980 .ts = ts,
981 .msg = NULL,
982 };
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +0100983
984 rc = osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_CREATE_RLCMAC_MSG, &data_ctx);
985 if (rc != 0 || !data_ctx.msg)
986 return NULL;
987 return data_ctx.msg;
988}