blob: b9a1b76dd1b73615bbf63d177820512efa82eb77 [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.
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010015 */
16
17
18#include "gprs_ms.h"
19#include "bts.h"
20#include "tbf.h"
21#include "tbf_ul.h"
22#include "gprs_debug.h"
23#include "gprs_codel.h"
24#include "pcu_utils.h"
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +010025#include "nacc_fsm.h"
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +020026#include "tbf_ul_ack_fsm.h"
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010027
28#include <time.h>
29
30#include <osmocom/core/talloc.h>
31#include <osmocom/core/utils.h>
32#include <osmocom/core/timer.h>
33#include <osmocom/gsm/protocol/gsm_04_08.h>
34#include <osmocom/gsm/gsm48.h>
35#include <osmocom/core/logging.h>
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +010036#include <osmocom/core/stats.h>
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010037#include "coding_scheme.h"
38
39#define GPRS_CODEL_SLOW_INTERVAL_MS 4000
40
41extern void *tall_pcu_ctx;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +010042static unsigned int next_ms_ctr_group_id;
43
44static const struct rate_ctr_desc ms_ctr_description[] = {
45 [MS_CTR_DL_CTRL_MSG_SCHED] = { "ms:dl_ctrl_msg_sched", "Amount of DL CTRL messages scheduled" },
46};
47
Pau Espin Pedrolf5a251b2021-01-12 20:57:56 +010048static const struct rate_ctr_group_desc ms_ctrg_desc = {
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +010049 .group_name_prefix = "pcu:ms",
50 .group_description = "MS Statistics",
51 .class_id = OSMO_STATS_CLASS_SUBSCRIBER,
52 .num_ctr = ARRAY_SIZE(ms_ctr_description),
53 .ctr_desc = ms_ctr_description,
54};
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010055
56static int64_t now_msec()
57{
58 struct timespec ts;
59 osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
60
61 return (int64_t)(ts.tv_sec) * 1000 + ts.tv_nsec / 1000000;
62}
63
64void gprs_default_cb_ms_idle(struct GprsMs *ms)
65{
Pau Espin Pedrol9da06862023-04-17 19:00:04 +020066 if (ms_is_idle(ms))
67 talloc_free(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010068}
69
70void gprs_default_cb_ms_active(struct GprsMs *ms)
71{
72 /* do nothing */
73}
74
75static struct gpr_ms_callback gprs_default_cb = {
76 .ms_idle = gprs_default_cb_ms_idle,
77 .ms_active = gprs_default_cb_ms_active,
78};
79
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +010080static void ms_release_timer_cb(void *data)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010081{
82 struct GprsMs *ms = (struct GprsMs *) data;
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +010083 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Release timer expired\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010084
85 if (ms->timer.data) {
86 ms->timer.data = NULL;
87 ms_unref(ms);
88 }
89}
90
Pau Espin Pedrol14beef62022-10-26 19:44:07 +020091static void ms_llc_timer_cb(void *_ms)
92{
93 struct GprsMs *ms = _ms;
94 struct gprs_rlcmac_dl_tbf *dl_tbf = ms_dl_tbf(ms);
95
96 if (!dl_tbf)
97 return;
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +020098 if (tbf_state(dl_tbf_as_tbf_const(dl_tbf)) != TBF_ST_FLOW)
Pau Espin Pedrol14beef62022-10-26 19:44:07 +020099 return;
100
Pau Espin Pedrolbd1f01f2022-10-27 15:19:39 +0200101 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "LLC receive timeout, requesting DL ACK\n");
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200102
Pau Espin Pedrol8fa3e062022-10-28 17:38:52 +0200103 dl_tbf_request_dl_ack(dl_tbf);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200104}
105
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100106static int ms_talloc_destructor(struct GprsMs *ms);
Pau Espin Pedrolf80cc5b2023-04-17 14:25:51 +0200107struct GprsMs *ms_alloc(struct gprs_rlcmac_bts *bts)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100108{
109 struct GprsMs *ms = talloc_zero(tall_pcu_ctx, struct GprsMs);
Pau Espin Pedrolb4987462022-04-01 17:21:08 +0200110 OSMO_ASSERT(bts);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100111
112 talloc_set_destructor(ms, ms_talloc_destructor);
113
Pau Espin Pedrolfe7aee92023-04-17 18:23:56 +0200114 llist_add(&ms->list, &bts->ms_list);
Pau Espin Pedrol9da06862023-04-17 19:00:04 +0200115 bts_stat_item_inc(bts, STAT_MS_PRESENT);
Pau Espin Pedrolfe7aee92023-04-17 18:23:56 +0200116
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100117 ms->bts = bts;
118 ms->cb = gprs_default_cb;
Pau Espin Pedrolf80cc5b2023-04-17 14:25:51 +0200119 ms->tlli = GSM_RESERVED_TMSI;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100120 ms->new_ul_tlli = GSM_RESERVED_TMSI;
121 ms->new_dl_tlli = GSM_RESERVED_TMSI;
122 ms->ta = GSM48_TA_INVALID;
123 ms->current_cs_ul = UNKNOWN;
124 ms->current_cs_dl = UNKNOWN;
125 ms->is_idle = true;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100126 INIT_LLIST_HEAD(&ms->old_tbfs);
127
128 int codel_interval = LLC_CODEL_USE_DEFAULT;
129
Pau Espin Pedrolf80cc5b2023-04-17 14:25:51 +0200130 LOGP(DRLCMAC, LOGL_INFO, "Creating MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100131
132 ms->imsi[0] = '\0';
Pau Espin Pedrolc7802d92022-05-09 16:07:52 +0200133 osmo_timer_setup(&ms->timer, ms_release_timer_cb, NULL);
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200134 llc_queue_init(&ms->llc_queue, ms);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200135 memset(&ms->llc_timer, 0, sizeof(ms->llc_timer));
136 osmo_timer_setup(&ms->llc_timer, ms_llc_timer_cb, ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100137
138 ms_set_mode(ms, GPRS);
139
Pau Espin Pedrolb4987462022-04-01 17:21:08 +0200140 codel_interval = the_pcu->vty.llc_codel_interval_msec;
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200141 if (codel_interval == LLC_CODEL_USE_DEFAULT)
142 codel_interval = GPRS_CODEL_SLOW_INTERVAL_MS;
143 llc_queue_set_codel_interval(&ms->llc_queue, codel_interval);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100144
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100145 ms->last_cs_not_low = now_msec();
146 ms->app_info_pending = false;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100147
148 ms->ctrs = rate_ctr_group_alloc(ms, &ms_ctrg_desc, next_ms_ctr_group_id++);
149 if (!ms->ctrs)
150 goto free_ret;
151
Pau Espin Pedrol9da06862023-04-17 19:00:04 +0200152 ms_set_timeout(ms, osmo_tdef_get(bts->pcu->T_defs, -2030, OSMO_TDEF_S, -1));
153
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100154 return ms;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100155free_ret:
156 talloc_free(ms);
157 return NULL;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100158}
159
160static int ms_talloc_destructor(struct GprsMs *ms)
161{
162 struct llist_item *pos, *tmp;
163
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100164 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Destroying MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100165
Pau Espin Pedrol9da06862023-04-17 19:00:04 +0200166 bts_stat_item_dec(ms->bts, STAT_MS_PRESENT);
Pau Espin Pedrolfe7aee92023-04-17 18:23:56 +0200167 llist_del(&ms->list);
168
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100169 ms_set_reserved_slots(ms, NULL, 0, 0);
170
Vadim Yanitskiye33c2362022-07-22 03:44:44 +0700171 osmo_timer_del(&ms->timer);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100172
173 if (ms->ul_tbf) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200174 tbf_set_ms(ul_tbf_as_tbf(ms->ul_tbf), NULL);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100175 ms->ul_tbf = NULL;
176 }
177
178 if (ms->dl_tbf) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200179 tbf_set_ms(dl_tbf_as_tbf(ms->dl_tbf), NULL);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100180 ms->dl_tbf = NULL;
181 }
182
183 llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
184 struct gprs_rlcmac_tbf *tbf = (struct gprs_rlcmac_tbf *)pos->entry;
185 tbf_set_ms(tbf, NULL);
186 }
187
188 llc_queue_clear(&ms->llc_queue, ms->bts);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200189 osmo_timer_del(&ms->llc_timer);
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100190
191 if (ms->ctrs)
192 rate_ctr_group_free(ms->ctrs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100193 return 0;
194}
195
196
197void ms_set_callback(struct GprsMs *ms, struct gpr_ms_callback *cb)
198{
199 if (cb)
200 ms->cb = *cb;
201 else
202 ms->cb = gprs_default_cb;
203}
204
205static void ms_update_status(struct GprsMs *ms)
206{
207 if (ms->ref > 0)
208 return;
209
210 if (ms_is_idle(ms) && !ms->is_idle) {
211 ms->is_idle = true;
212 ms->cb.ms_idle(ms);
213 /* this can be deleted by now, do not access it */
214 return;
215 }
216
217 if (!ms_is_idle(ms) && ms->is_idle) {
218 ms->is_idle = false;
219 ms->cb.ms_active(ms);
220 }
221}
222
223struct GprsMs *ms_ref(struct GprsMs *ms)
224{
225 ms->ref += 1;
226 return ms;
227}
228
229void ms_unref(struct GprsMs *ms)
230{
231 OSMO_ASSERT(ms->ref >= 0);
232 ms->ref -= 1;
233 if (ms->ref == 0)
234 ms_update_status(ms);
235}
236
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100237static void ms_release_timer_start(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100238{
Pau Espin Pedrol14379ef2023-04-17 20:43:26 +0200239 /* Immediate free():
240 * Skip delaying free() through release timer if delay is configured to be 0.
241 * This is useful for synced freed during unit tests.
242 */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100243 if (ms->delay == 0)
244 return;
245
Pau Espin Pedrol14379ef2023-04-17 20:43:26 +0200246 /* Immediate free():
247 * Skip delaying free() through release timer if TMSI is not
248 * known, since those cannot really be reused.
249 */
250 if (ms_tlli(ms) == GSM_RESERVED_TMSI)
251 return;
252
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100253 LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Schedule MS release in %u secs\n", ms->delay);
254
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100255 if (!ms->timer.data)
256 ms->timer.data = ms_ref(ms);
257
258 osmo_timer_schedule(&ms->timer, ms->delay, 0);
259}
260
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100261static void ms_release_timer_stop(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100262{
263 if (!ms->timer.data)
264 return;
265
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100266 LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Cancel scheduled MS release\n");
267
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100268 osmo_timer_del(&ms->timer);
269 ms->timer.data = NULL;
270 ms_unref(ms);
271}
272
273void ms_set_mode(struct GprsMs *ms, enum mcs_kind mode)
274{
275 ms->mode = mode;
276
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100277 switch (ms->mode) {
278 case GPRS:
279 if (!mcs_is_gprs(ms->current_cs_ul)) {
280 ms->current_cs_ul = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100281 ms->bts->initial_cs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100282 if (!mcs_is_valid(ms->current_cs_ul))
283 ms->current_cs_ul = CS1;
284 }
285 if (!mcs_is_gprs(ms->current_cs_dl)) {
286 ms->current_cs_dl = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100287 ms->bts->initial_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100288 if (!mcs_is_valid(ms->current_cs_dl))
289 ms->current_cs_dl = CS1;
290 }
291 break;
292
293 case EGPRS_GMSK:
Pau Espin Pedrol7bb8cd62021-01-25 15:08:35 +0100294 if (!mcs_is_edge_gmsk(ms->current_cs_ul)) {
295 ms->current_cs_ul = mcs_get_egprs_by_num(
296 ms->bts->initial_mcs_ul);
297 if (!mcs_is_valid(ms->current_cs_ul))
298 ms->current_cs_ul = MCS1;
299 }
300 if (!mcs_is_edge_gmsk(ms->current_cs_dl)) {
301 ms->current_cs_dl = mcs_get_egprs_by_num(
302 ms->bts->initial_mcs_dl);
303 if (!mcs_is_valid(ms->current_cs_dl))
304 ms->current_cs_dl = MCS1;
305 }
306 break;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100307 case EGPRS:
308 if (!mcs_is_edge(ms->current_cs_ul)) {
309 ms->current_cs_ul = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100310 ms->bts->initial_mcs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100311 if (!mcs_is_valid(ms->current_cs_ul))
312 ms->current_cs_ul = MCS1;
313 }
314 if (!mcs_is_edge(ms->current_cs_dl)) {
315 ms->current_cs_dl = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100316 ms->bts->initial_mcs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100317 if (!mcs_is_valid(ms->current_cs_dl))
318 ms->current_cs_dl = MCS1;
319 }
320 break;
321 }
322}
323
324static void ms_attach_ul_tbf(struct GprsMs *ms, struct gprs_rlcmac_ul_tbf *tbf)
325{
326 if (ms->ul_tbf == tbf)
327 return;
328
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100329 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 +0100330
331 ms_ref(ms);
332
333 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200334 llist_add_tail(tbf_ms_list(ul_tbf_as_tbf(ms->ul_tbf)), &ms->old_tbfs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100335
336 ms->ul_tbf = tbf;
337
338 if (tbf)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100339 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100340
341 ms_unref(ms);
342}
343
344static void ms_attach_dl_tbf(struct GprsMs *ms, struct gprs_rlcmac_dl_tbf *tbf)
345{
346 if (ms->dl_tbf == tbf)
347 return;
348
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100349 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 +0100350
351 ms_ref(ms);
352
353 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200354 llist_add_tail(tbf_ms_list(dl_tbf_as_tbf(ms->dl_tbf)), &ms->old_tbfs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100355
356 ms->dl_tbf = tbf;
357
358 if (tbf)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100359 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100360
361 ms_unref(ms);
362}
363
364void ms_attach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
365{
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200366 if (tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF)
Pau Espin Pedrolcc30b052022-10-27 15:25:55 +0200367 ms_attach_dl_tbf(ms, tbf_as_dl_tbf(tbf));
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200368 else
Pau Espin Pedrolcc30b052022-10-27 15:25:55 +0200369 ms_attach_ul_tbf(ms, tbf_as_ul_tbf(tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100370}
371
372void ms_detach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
373{
374 if (tbf == (struct gprs_rlcmac_tbf *)(ms->ul_tbf)) {
375 ms->ul_tbf = NULL;
376 } else if (tbf == (struct gprs_rlcmac_tbf *)(ms->dl_tbf)) {
377 ms->dl_tbf = NULL;
378 } else {
379 bool found = false;
380
381 struct llist_item *pos, *tmp;
382 llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
383 struct gprs_rlcmac_tbf *tmp_tbf = (struct gprs_rlcmac_tbf *)pos->entry;
384 if (tmp_tbf == tbf) {
385 llist_del(&pos->list);
386 found = true;
387 break;
388 }
389 }
390
391 /* Protect against recursive calls via set_ms() */
392 if (!found)
393 return;
394 }
395
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100396 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Detaching TBF: %s\n",
397 tbf_name(tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100398
399 if (tbf_ms(tbf) == ms)
400 tbf_set_ms(tbf, NULL);
401
402 if (!ms->dl_tbf && !ms->ul_tbf) {
403 ms_set_reserved_slots(ms, NULL, 0, 0);
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100404 ms->first_common_ts = NULL;
Pau Espin Pedrol14379ef2023-04-17 20:43:26 +0200405 ms_release_timer_start(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100406 }
407
408 ms_update_status(ms);
409}
410
411void ms_reset(struct GprsMs *ms)
412{
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100413 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Clearing MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100414
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100415 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100416
417 ms->tlli = GSM_RESERVED_TMSI;
418 ms->new_dl_tlli = ms->tlli;
419 ms->new_ul_tlli = ms->tlli;
420 ms->imsi[0] = '\0';
421}
422
Pau Espin Pedrol8abe13c2022-10-21 18:49:48 +0200423/* This function should be called on the MS object of a TBF each time an RLCMAC
424 * block is received for it with TLLI information.
425 * Besides updating the TLLI field on the MS object, it also seeks for other MS
426 * objects in the store and merges them into the current MS object. The MS
427 * duplication happened because we don't learn the TLLI of the created TBF until
428 * a later point. */
429void ms_update_announced_tlli(struct GprsMs *ms, uint32_t tlli)
430{
431 struct GprsMs *old_ms = NULL;
432
433 if (tlli == GSM_RESERVED_TMSI)
434 return;
435
436 /* When the TLLI does not match the ms, check if there is another
437 * MS object that belongs to that TLLI and if yes make sure one of them
438 * gets deleted. */
439 if (!ms_check_tlli(ms, tlli))
Pau Espin Pedroleb0a0522023-04-17 16:33:35 +0200440 old_ms = bts_get_ms_by_tlli(ms->bts, tlli, GSM_RESERVED_TMSI);
Pau Espin Pedrol8abe13c2022-10-21 18:49:48 +0200441
442 ms_set_tlli(ms, tlli);
443
444 if (old_ms)
445 ms_merge_and_clear_ms(ms, old_ms);
446 /* old_ms may no longer be available here */
447}
448
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100449/* Merge 'old_ms' object into 'ms' object.
450 * 'old_ms' may be freed during the call to this function, don't use the pointer to it afterwards */
451void ms_merge_and_clear_ms(struct GprsMs *ms, struct GprsMs *old_ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100452{
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100453 char old_ms_name[128];
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100454 OSMO_ASSERT(old_ms != ms);
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100455 ms_ref(old_ms);
456
457 ms_name_buf(old_ms, old_ms_name, sizeof(old_ms_name));
458
459 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Merge MS: %s\n", old_ms_name);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100460
461 if (strlen(ms_imsi(ms)) == 0 && strlen(ms_imsi(old_ms)) != 0)
462 osmo_strlcpy(ms->imsi, ms_imsi(old_ms), sizeof(ms->imsi));
463
464 if (!ms_ms_class(ms) && ms_ms_class(old_ms))
465 ms_set_ms_class(ms, ms_ms_class(old_ms));
466
467 if (!ms_egprs_ms_class(ms) && ms_egprs_ms_class(old_ms))
468 ms_set_egprs_ms_class(ms, ms_egprs_ms_class(old_ms));
469
470 llc_queue_move_and_merge(&ms->llc_queue, &old_ms->llc_queue);
471
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100472 /* Clean up the old MS object */
473 /* TODO: Use timer? */
474 if (ms_ul_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms), T_MAX))
475 tbf_free((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms));
476 if (ms_dl_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms), T_MAX))
477 tbf_free((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms));
478
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100479 ms_reset(old_ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100480
481 ms_unref(old_ms);
482}
483
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100484/* Apply changes to the TLLI directly, used interally by functions below: */
485static void ms_apply_tlli_change(struct GprsMs *ms, uint32_t tlli)
486{
487 ms->tlli = tlli;
488 ms->new_dl_tlli = GSM_RESERVED_TMSI;
489 ms->new_ul_tlli = GSM_RESERVED_TMSI;
490
491 /* Update TBF FSM names: */
492 if (ms->ul_tbf)
493 tbf_update_state_fsm_name(ul_tbf_as_tbf(ms->ul_tbf));
494 if (ms->dl_tbf)
495 tbf_update_state_fsm_name(dl_tbf_as_tbf(ms->dl_tbf));
496}
497
Pau Espin Pedrol0b5997e2022-10-21 14:10:41 +0200498/* Set/update the MS object TLLI based on knowledge gained from the MS side (Uplink direction) */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100499void ms_set_tlli(struct GprsMs *ms, uint32_t tlli)
500{
501 if (tlli == ms->tlli || tlli == ms->new_ul_tlli)
502 return;
503
504 if (tlli != ms->new_dl_tlli) {
505 LOGP(DRLCMAC, LOGL_INFO,
506 "Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
507 "not yet confirmed\n",
508 ms_tlli(ms), tlli);
509 ms->new_ul_tlli = tlli;
510 return;
511 }
512
513 LOGP(DRLCMAC, LOGL_INFO,
514 "Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
515 "already confirmed partly\n",
516 ms->tlli, tlli);
517
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100518 ms_apply_tlli_change(ms, tlli);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100519}
520
Pau Espin Pedrol0b5997e2022-10-21 14:10:41 +0200521/* Set/update the MS object TLLI based on knowledge gained from the SGSN side (Downlink direction) */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100522bool ms_confirm_tlli(struct GprsMs *ms, uint32_t tlli)
523{
524 if (tlli == ms->tlli || tlli == ms->new_dl_tlli)
525 return false;
526
527 if (tlli != ms->new_ul_tlli) {
528 /* The MS has not sent a message with the new TLLI, which may
529 * happen according to the spec [TODO: add reference]. */
530
531 LOGP(DRLCMAC, LOGL_INFO,
532 "The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
533 "partly confirmed\n", tlli);
534 /* Use the network's idea of TLLI as candidate, this does not
535 * change the result value of tlli() */
536 ms->new_dl_tlli = tlli;
537 return false;
538 }
539
540 LOGP(DRLCMAC, LOGL_INFO,
541 "Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
542
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100543 ms_apply_tlli_change(ms, tlli);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100544
545 return true;
546}
547
548void ms_set_imsi(struct GprsMs *ms, const char *imsi)
549{
550 if (!imsi) {
551 LOGP(DRLCMAC, LOGL_ERROR, "Expected IMSI!\n");
552 return;
553 }
554
555 if (imsi[0] && strlen(imsi) < 3) {
556 LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI '%s'!\n",
557 imsi);
558 return;
559 }
560
561 if (strcmp(imsi, ms->imsi) == 0)
562 return;
563
564 LOGP(DRLCMAC, LOGL_INFO,
565 "Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
566 ms_tlli(ms), ms->imsi, imsi);
567
Pau Espin Pedrolcde18c52023-04-17 18:16:48 +0200568 struct GprsMs *old_ms = bts_get_ms_by_imsi(ms->bts, imsi);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100569 /* Check if we are going to store a different MS object with already
570 existing IMSI. This is probably a bug in code calling this function,
571 since it should take care of this explicitly */
572 if (old_ms) {
573 /* We cannot find ms->ms by IMSI since we know that it has a
574 * different IMSI */
575 OSMO_ASSERT(old_ms != ms);
576
577 LOGPMS(ms, DRLCMAC, LOGL_NOTICE,
578 "IMSI '%s' was already assigned to another "
579 "MS object: TLLI = 0x%08x, that IMSI will be removed\n",
580 imsi, ms_tlli(old_ms));
581
582 ms_merge_and_clear_ms(ms, old_ms);
Pau Espin Pedrolb8ff74b2022-10-31 12:58:46 +0100583 /* old_ms may no longer be available here */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100584 }
585
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100586 /* Store the new IMSI: */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100587 osmo_strlcpy(ms->imsi, imsi, sizeof(ms->imsi));
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100588
589 /* Update TBF FSM names: */
590 if (ms->ul_tbf)
591 tbf_update_state_fsm_name(ul_tbf_as_tbf(ms->ul_tbf));
592 if (ms->dl_tbf)
593 tbf_update_state_fsm_name(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100594}
595
596void ms_set_ta(struct GprsMs *ms, uint8_t ta_)
597{
598 if (ta_ == ms->ta)
599 return;
600
601 if (gsm48_ta_is_valid(ta_)) {
602 LOGP(DRLCMAC, LOGL_INFO,
603 "Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
604 ms_tlli(ms), ms->ta, ta_);
605 ms->ta = ta_;
606 } else
607 LOGP(DRLCMAC, LOGL_NOTICE,
608 "MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
609 "value %d kept)\n", ms_tlli(ms), ta_, ms->ta);
610}
611
612void ms_set_ms_class(struct GprsMs *ms, uint8_t ms_class_)
613{
614 if (ms_class_ == ms->ms_class)
615 return;
616
617 LOGP(DRLCMAC, LOGL_INFO,
618 "Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
619 ms_tlli(ms), ms->ms_class, ms_class_);
620
621 ms->ms_class = ms_class_;
622}
623
624void ms_set_egprs_ms_class(struct GprsMs *ms, uint8_t ms_class_)
625{
626 if (ms_class_ == ms->egprs_ms_class)
627 return;
628
629 LOGP(DRLCMAC, LOGL_INFO,
630 "Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
631 ms_tlli(ms), ms->egprs_ms_class, ms_class_);
632
633 ms->egprs_ms_class = ms_class_;
634
635 if (!bts_max_mcs_ul(ms->bts) || !bts_max_mcs_dl(ms->bts)) {
636 LOGPMS(ms, DRLCMAC, LOGL_DEBUG,
637 "Avoid enabling EGPRS because use of MCS is disabled: ul=%u dl=%u\n",
638 bts_max_mcs_ul(ms->bts), bts_max_mcs_dl(ms->bts));
639 return;
640 }
641
642 if (mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts))) &&
643 mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts))) &&
644 ms_mode(ms) != EGPRS)
645 {
646 ms_set_mode(ms, EGPRS_GMSK);
647 } else {
648 ms_set_mode(ms, EGPRS);
649 }
650 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Enabled EGPRS, mode %s\n", mode_name(ms_mode(ms)));
651}
652
653void ms_update_error_rate(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, int error_rate)
654{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100655 int64_t now;
656 enum CodingScheme max_cs_dl = ms_max_cs_dl(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100657 OSMO_ASSERT(max_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100658
659 if (error_rate < 0)
660 return;
661
662 now = now_msec();
663
664 /* TODO: Check for TBF direction */
665 /* TODO: Support different CS values for UL and DL */
666
667 ms->nack_rate_dl = error_rate;
668
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100669 if (error_rate > the_pcu->vty.cs_adj_upper_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100670 if (mcs_chan_code(ms->current_cs_dl) > 0) {
671 mcs_dec_kind(&ms->current_cs_dl, ms_mode(ms));
672 LOGP(DRLCMACDL, LOGL_INFO,
673 "MS (IMSI %s): High error rate %d%%, "
674 "reducing CS level to %s\n",
675 ms_imsi(ms), error_rate, mcs_name(ms->current_cs_dl));
676 ms->last_cs_not_low = now;
677 }
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100678 } else if (error_rate < the_pcu->vty.cs_adj_lower_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100679 if (ms->current_cs_dl < max_cs_dl) {
680 if (now - ms->last_cs_not_low > 1000) {
681 mcs_inc_kind(&ms->current_cs_dl, ms_mode(ms));
682
683 LOGP(DRLCMACDL, LOGL_INFO,
684 "MS (IMSI %s): Low error rate %d%%, "
685 "increasing DL CS level to %s\n",
686 ms_imsi(ms), error_rate,
687 mcs_name(ms->current_cs_dl));
688 ms->last_cs_not_low = now;
689 } else {
690 LOGP(DRLCMACDL, LOGL_DEBUG,
691 "MS (IMSI %s): Low error rate %d%%, "
692 "ignored (within blocking period)\n",
693 ms_imsi(ms), error_rate);
694 }
695 }
696 } else {
697 LOGP(DRLCMACDL, LOGL_DEBUG,
698 "MS (IMSI %s): Medium error rate %d%%, ignored\n",
699 ms_imsi(ms), error_rate);
700 ms->last_cs_not_low = now;
701 }
702}
703
704enum CodingScheme ms_max_cs_ul(const struct GprsMs *ms)
705{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100706 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100707 OSMO_ASSERT(ms->bts != NULL);
708
709 if (mcs_is_gprs(ms->current_cs_ul)) {
710 if (!bts_max_cs_ul(ms->bts)) {
711 return CS4;
712 }
713
714 return mcs_get_gprs_by_num(bts_max_cs_ul(ms->bts));
715 }
716
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100717 cs = mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts));
718 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
719 cs = MCS4;
720 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100721}
722
723void ms_set_current_cs_dl(struct GprsMs *ms, enum CodingScheme scheme)
724{
725 ms->current_cs_dl = scheme;
726}
727
728enum CodingScheme ms_max_cs_dl(const struct GprsMs *ms)
729{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100730 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100731 OSMO_ASSERT(ms->bts != NULL);
732
733 if (mcs_is_gprs(ms->current_cs_dl)) {
734 if (!bts_max_cs_dl(ms->bts)) {
735 return CS4;
736 }
737
738 return mcs_get_gprs_by_num(bts_max_cs_dl(ms->bts));
739 }
740
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100741 cs = mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts));
742 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
743 cs = MCS4;
744 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100745}
746
747void ms_update_cs_ul(struct GprsMs *ms, const struct pcu_l1_meas *meas)
748{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100749 enum CodingScheme max_cs_ul = ms_max_cs_ul(ms);
750
751 int old_link_qual;
752 int low;
753 int high;
754 enum CodingScheme new_cs_ul = ms->current_cs_ul;
755 uint8_t current_cs = mcs_chan_code(ms->current_cs_ul);
756
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100757 if (!max_cs_ul) {
758 LOGP(DRLCMACMEAS, LOGL_ERROR,
759 "max_cs_ul cannot be derived (current UL CS: %s)\n",
760 mcs_name(ms->current_cs_ul));
761 return;
762 }
763
764 if (!ms->current_cs_ul) {
765 LOGP(DRLCMACMEAS, LOGL_ERROR,
766 "Unable to update UL (M)CS because it's not set: %s\n",
767 mcs_name(ms->current_cs_ul));
768 return;
769 }
770
771 if (!meas->have_link_qual) {
772 LOGP(DRLCMACMEAS, LOGL_ERROR,
773 "Unable to update UL (M)CS %s because we don't have link quality measurements.\n",
774 mcs_name(ms->current_cs_ul));
775 return;
776 }
777
778 if (mcs_is_gprs(ms->current_cs_ul)) {
779 if (current_cs >= MAX_GPRS_CS)
780 current_cs = MAX_GPRS_CS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100781 low = the_pcu->vty.cs_lqual_ranges[current_cs].low;
782 high = the_pcu->vty.cs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100783 } else if (mcs_is_edge(ms->current_cs_ul)) {
784 if (current_cs >= MAX_EDGE_MCS)
785 current_cs = MAX_EDGE_MCS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100786 low = the_pcu->vty.mcs_lqual_ranges[current_cs].low;
787 high = the_pcu->vty.mcs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100788 } else {
789 LOGP(DRLCMACMEAS, LOGL_ERROR,
790 "Unable to update UL (M)CS because it's neither GPRS nor EDGE: %s\n",
791 mcs_name(ms->current_cs_ul));
792 return;
793 }
794
795 /* To avoid rapid changes of the coding scheme, we also take
796 * the old link quality value into account (if present). */
797 if (ms->l1_meas.have_link_qual)
798 old_link_qual = ms->l1_meas.link_qual;
799 else
800 old_link_qual = meas->link_qual;
801
802 if (meas->link_qual < low && old_link_qual < low)
803 mcs_dec_kind(&new_cs_ul, ms_mode(ms));
804 else if (meas->link_qual > high && old_link_qual > high &&
805 ms->current_cs_ul < max_cs_ul)
806 mcs_inc_kind(&new_cs_ul, ms_mode(ms));
807
808 if (ms->current_cs_ul != new_cs_ul) {
809 LOGPMS(ms, DRLCMACMEAS, LOGL_INFO,
810 "Link quality %ddB (old %ddB) left window [%d, %d], "
811 "modifying uplink CS level: %s -> %s\n",
812 meas->link_qual, old_link_qual,
813 low, high,
814 mcs_name(ms->current_cs_ul), mcs_name(new_cs_ul));
815
816 ms->current_cs_ul = new_cs_ul;
817 }
818}
819
820void ms_update_l1_meas(struct GprsMs *ms, const struct pcu_l1_meas *meas)
821{
822 unsigned i;
823
824 ms_update_cs_ul(ms, meas);
825
826 if (meas->have_rssi)
827 pcu_l1_meas_set_rssi(&ms->l1_meas, meas->rssi);
828 if (meas->have_bto)
829 pcu_l1_meas_set_bto(&ms->l1_meas, meas->bto);
830 if (meas->have_ber)
831 pcu_l1_meas_set_ber(&ms->l1_meas, meas->ber);
832 if (meas->have_link_qual)
833 pcu_l1_meas_set_link_qual(&ms->l1_meas, meas->link_qual);
834
835 if (meas->have_ms_rx_qual)
836 pcu_l1_meas_set_ms_rx_qual(&ms->l1_meas, meas->ms_rx_qual);
837 if (meas->have_ms_c_value)
838 pcu_l1_meas_set_ms_c_value(&ms->l1_meas, meas->ms_c_value);
839 if (meas->have_ms_sign_var)
840 pcu_l1_meas_set_ms_sign_var(&ms->l1_meas, meas->ms_sign_var);
841
842 if (meas->have_ms_i_level) {
843 for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
844 if (meas->ts[i].have_ms_i_level)
845 pcu_l1_meas_set_ms_i_level(&ms->l1_meas, i, meas->ts[i].ms_i_level);
846 else
847 ms->l1_meas.ts[i].have_ms_i_level = 0;
848 }
849 }
850}
851
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100852/* req_mcs_kind acts as a set filter, where EGPRS means any and GPRS is the most restrictive */
853enum CodingScheme ms_current_cs_dl(const struct GprsMs *ms, enum mcs_kind req_mcs_kind)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100854{
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100855 enum CodingScheme orig_cs = ms->current_cs_dl;
856 struct gprs_rlcmac_bts *bts = ms->bts;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100857 size_t unencoded_octets;
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100858 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100859
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100860 /* It could be that a TBF requests a GPRS CS despite the MS currently
861 being upgraded to EGPRS (hence reporting MCS). That could happen
862 because the TBF was created early in the process where we didn't have
863 yet enough information about the MS, and only AFTER it was created we
864 upgraded the MS to be EGPRS capable.
865 As a result, when the MS is queried for the target CS here, we could be
866 returning an MCS despite the current TBF being established as GPRS,
867 but we rather stick to the TBF type we assigned to the MS rather than
868 magically sending EGPRS data blocks to a GPRS TBF.
869 It could also be that the caller requests specific MCS kind
870 explicitly too due to scheduling restrictions (GPRS+EGPRS multiplexing). */
871 if (req_mcs_kind == EGPRS_GMSK && mcs_is_edge(orig_cs) && orig_cs > MCS4) {
872 cs = bts_cs_dl_is_supported(bts, MCS4) ? MCS4 :
873 bts_cs_dl_is_supported(bts, MCS3) ? MCS3 :
874 bts_cs_dl_is_supported(bts, MCS2) ? MCS2 :
875 MCS1;
876 } else if (req_mcs_kind == GPRS && mcs_is_edge(orig_cs)) { /* GPRS */
877 int i;
878 cs = orig_cs > MCS4 ? MCS4 : orig_cs;
879 cs -= (MCS1 - CS1); /* MCSx -> CSx */
880 /* Find suitable CS starting from equivalent MCS which is supported by BTS: */
881 for (i = mcs_chan_code(cs); !bts_cs_dl_is_supported(bts, CS1 + i); i--);
882 OSMO_ASSERT(i >= 0 && i <= 3); /* CS1 is always supported */
883 cs = CS1 + i;
884 } else {
885 cs = orig_cs;
886 }
887
888 if (orig_cs != cs)
889 LOGPMS(ms, DRLCMACDL, LOGL_INFO, "MS (mode=%s) suggests transmitting "
890 "DL %s, downgrade to %s in order to match TBF & scheduler requirements\n",
891 mode_name(ms_mode(ms)), mcs_name(orig_cs), mcs_name(cs));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100892
893 unencoded_octets = llc_queue_octets(&ms->llc_queue);
894
895 /* If the DL TBF is active, add number of unencoded chunk octets */
896 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200897 unencoded_octets += llc_chunk_size(tbf_llc(dl_tbf_as_tbf(ms->dl_tbf)));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100898
899 /* There are many unencoded octets, don't reduce */
Pau Espin Pedrolad79b852021-01-14 13:20:55 +0100900 if (unencoded_octets >= the_pcu->vty.cs_downgrade_threshold)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100901 return cs;
902
903 /* RF conditions are good, don't reduce */
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100904 if (ms->nack_rate_dl < the_pcu->vty.cs_adj_lower_limit)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100905 return cs;
906
907 /* The throughput would probably be better if the CS level was reduced */
908 mcs_dec_kind(&cs, ms_mode(ms));
909
910 /* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
911 if (cs == CS2)
912 mcs_dec_kind(&cs, ms_mode(ms));
913
914 return cs;
915}
916
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100917struct gprs_rlcmac_pdch *ms_first_common_ts(const struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100918{
Pau Espin Pedrol345d9ad2022-12-12 19:22:44 +0100919 return ms->first_common_ts;
920}
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100921
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100922void ms_set_first_common_ts(struct GprsMs *ms, struct gprs_rlcmac_pdch *pdch)
Pau Espin Pedrol345d9ad2022-12-12 19:22:44 +0100923{
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100924 OSMO_ASSERT(pdch);
925 ms->first_common_ts = pdch;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100926}
927
928uint8_t ms_dl_slots(const struct GprsMs *ms)
929{
930 uint8_t slots = 0;
931
932 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200933 slots |= tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100934
935 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200936 slots |= tbf_dl_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100937
938 return slots;
939}
940
941uint8_t ms_ul_slots(const struct GprsMs *ms)
942{
943 uint8_t slots = 0;
944
945 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200946 slots |= tbf_ul_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100947
948 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200949 slots |= tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100950
951 return slots;
952}
953
954uint8_t ms_current_pacch_slots(const struct GprsMs *ms)
955{
956 uint8_t slots = 0;
957
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200958 bool is_dl_active = ms->dl_tbf && tbf_is_tfi_assigned(dl_tbf_as_tbf(ms->dl_tbf));
959 bool is_ul_active = ms->ul_tbf && tbf_is_tfi_assigned(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100960
961 if (!is_dl_active && !is_ul_active)
962 return 0;
963
964 /* see TS 44.060, 8.1.1.2.2 */
965 if (is_dl_active && !is_ul_active)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200966 slots = tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100967 else if (!is_dl_active && is_ul_active)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200968 slots = tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100969 else
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200970 slots = tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf)) &
971 tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100972
973 /* Assume a multislot class 1 device */
974 /* TODO: For class 2 devices, this could be removed */
975 slots = pcu_lsb(slots);
976
977 return slots;
978}
979
980void ms_set_reserved_slots(struct GprsMs *ms, struct gprs_rlcmac_trx *trx,
981 uint8_t ul_slots, uint8_t dl_slots)
982{
983 if (ms->current_trx) {
984 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
985 ms->reserved_dl_slots);
986 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
987 ms->reserved_ul_slots);
988 ms->reserved_dl_slots = 0;
989 ms->reserved_ul_slots = 0;
990 }
991 ms->current_trx = trx;
992 if (trx) {
993 ms->reserved_dl_slots = dl_slots;
994 ms->reserved_ul_slots = ul_slots;
995 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
996 ms->reserved_dl_slots);
997 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
998 ms->reserved_ul_slots);
999 }
1000}
1001
1002struct gprs_rlcmac_tbf *ms_tbf(const struct GprsMs *ms, enum gprs_rlcmac_tbf_direction dir)
1003{
1004 switch (dir) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +02001005 case GPRS_RLCMAC_DL_TBF: return dl_tbf_as_tbf(ms->dl_tbf);
1006 case GPRS_RLCMAC_UL_TBF: return ul_tbf_as_tbf(ms->ul_tbf);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +01001007 }
1008
1009 return NULL;
1010}
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001011
Pau Espin Pedrol3547d642022-10-21 15:00:08 +02001012const char *ms_name(const struct GprsMs *ms)
1013{
1014 static char _ms_name_buf[128];
1015 return ms_name_buf(ms, _ms_name_buf, sizeof(_ms_name_buf));
1016}
1017
1018char *ms_name_buf(const struct GprsMs *ms, char *buf, unsigned int buf_size)
1019{
Pau Espin Pedrol94f82582022-11-03 14:16:17 +01001020 struct osmo_strbuf sb = { .buf = buf, .len = buf_size };
1021 uint32_t tlli = ms_tlli(ms);
1022
1023 OSMO_STRBUF_PRINTF(sb, "MS(");
1024 if (ms_imsi_is_valid(ms))
1025 OSMO_STRBUF_PRINTF(sb, "IMSI-%s:", ms_imsi(ms));
1026 if (tlli != GSM_RESERVED_TMSI)
1027 OSMO_STRBUF_PRINTF(sb, "TLLI-0x%08x:", tlli);
1028 OSMO_STRBUF_PRINTF(sb, "TA-%" PRIu8 ":MSCLS-%" PRIu8 "-%" PRIu8,
1029 ms_ta(ms), ms_ms_class(ms), ms_egprs_ms_class(ms));
1030 if (ms->ul_tbf)
1031 OSMO_STRBUF_PRINTF(sb, ":UL");
1032 if (ms->dl_tbf)
1033 OSMO_STRBUF_PRINTF(sb, ":DL");
1034
1035 OSMO_STRBUF_PRINTF(sb, ")");
Pau Espin Pedrol3547d642022-10-21 15:00:08 +02001036 return buf;
1037}
1038
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001039int ms_nacc_start(struct GprsMs *ms, Packet_Cell_Change_Notification_t *notif)
1040{
1041 if (!ms->nacc)
1042 ms->nacc = nacc_fsm_alloc(ms);
1043 if (!ms->nacc)
1044 return -EINVAL;
1045 return osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_RX_CELL_CHG_NOTIFICATION, notif);
1046}
1047
1048bool ms_nacc_rts(const struct GprsMs *ms)
1049{
1050 if (!ms->nacc)
1051 return false;
1052 if (ms->nacc->fi->state == NACC_ST_TX_NEIGHBOUR_DATA ||
1053 ms->nacc->fi->state == NACC_ST_TX_CELL_CHG_CONTINUE)
1054 return true;
1055 return false;
1056}
1057
Pau Espin Pedrol5ba3ef92022-12-12 18:02:25 +01001058struct msgb *ms_nacc_create_rlcmac_msg(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf,
1059 const struct gprs_rlcmac_pdch *pdch, uint32_t fn)
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001060{
1061 int rc;
1062 struct nacc_ev_create_rlcmac_msg_ctx data_ctx;
1063
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001064 data_ctx = (struct nacc_ev_create_rlcmac_msg_ctx) {
1065 .tbf = tbf,
Pau Espin Pedrol5ba3ef92022-12-12 18:02:25 +01001066 .pdch = pdch,
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001067 .fn = fn,
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001068 .msg = NULL,
1069 };
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001070
1071 rc = osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_CREATE_RLCMAC_MSG, &data_ctx);
1072 if (rc != 0 || !data_ctx.msg)
1073 return NULL;
1074 return data_ctx.msg;
1075}
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001076
1077static void ms_start_llc_timer(struct GprsMs *ms)
1078{
1079 if (the_pcu->vty.llc_idle_ack_csec > 0) {
1080 struct timespec tv;
1081 csecs_to_timespec(the_pcu->vty.llc_idle_ack_csec, &tv);
1082 osmo_timer_schedule(&ms->llc_timer, tv.tv_sec, tv.tv_nsec / 1000);
1083 }
1084}
1085
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001086/* Can we get to send a DL TBF ass to the MS? */
1087static bool ms_is_reachable_for_dl_ass(const struct GprsMs *ms)
1088{
1089 struct gprs_rlcmac_ul_tbf *ul_tbf = ms_ul_tbf(ms);
1090
1091 /* This function assumes it is called when no DL TBF is present */
1092 OSMO_ASSERT(!ms_dl_tbf(ms));
1093
1094 /* 3GPP TS 44.060 sec 7.1.3.1 Initiation of the Packet resource request procedure:
1095 * "Furthermore, the mobile station shall not respond to PACKET DOWNLINK ASSIGNMENT
1096 * or MULTIPLE TBF DOWNLINK ASSIGNMENT messages before contention resolution is
1097 * completed on the mobile station side." */
1098 /* The possible uplink TBF is used to trigger downlink assignment:
1099 * - If there is no uplink TBF the MS is potentially in packet idle mode
1100 * and hence assignment will be done over CCCH (PCH)
1101 * - If there's an uplink TBF but it is finished (waiting for last PKT
1102 * CTRL ACK after sending last Pkt UL ACK/NACK with FINAL_ACK=1, then we
1103 * have no ways to contact the MS right now. Assignment will be delayed
1104 * until PKT CTRL ACK is received and the TBF is released at the MS side
1105 * (then assignment goes through PCH).
1106 */
1107 if (!ul_tbf)
1108 return true;
1109 if (ul_tbf_contention_resolution_done(ul_tbf) &&
1110 !tbf_ul_ack_waiting_cnf_final_ack(ul_tbf))
1111 return true;
1112
1113 return false;
1114
1115}
1116
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001117/* Alloc a UL TBF to be assigned over PACCH. Called when an MS requests to
1118 * create a new UL TBF during the end of life of a previous UL TBF (or an SBA).
1119 * In summary, this TBF is allocated as a consequence of receiving a "Pkt
1120 * Resource Req" or "Pkt Ctrl Ack" from the MS.
1121 * See TS 44.060 9.3.2.4.2 "Non-extended uplink TBF mode".
1122 */
1123struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_pacch(struct GprsMs *ms, int8_t use_trx)
1124{
1125 const bool single_slot = false;
1126 struct gprs_rlcmac_ul_tbf *ul_tbf;
1127
Pau Espin Pedrolbda7bb72022-10-31 14:33:09 +01001128 ul_tbf = ul_tbf_alloc(ms->bts, ms, use_trx, single_slot);
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001129 if (!ul_tbf) {
1130 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1131 /* Caller will most probably send a Imm Ass Reject after return */
1132 return NULL;
1133 }
1134 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_PACCH, NULL);
1135 /* Contention resolution is considered to be done since TLLI is known in MS */
1136 return ul_tbf;
1137}
1138
1139/* Alloc a UL TBF to be assigned over AGCH. Used by request of a "One phase
1140 * packet access", where MS requested only 1 PDCH TS (TS 44.018 Table 9.1.8.1). */
1141struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_agch(struct GprsMs *ms)
1142{
1143 const int8_t trx_no = -1;
1144 const bool single_slot = true;
1145 struct gprs_rlcmac_ul_tbf *ul_tbf;
1146
Pau Espin Pedrolbda7bb72022-10-31 14:33:09 +01001147 ul_tbf = ul_tbf_alloc(ms->bts, ms, trx_no, single_slot);
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001148 if (!ul_tbf) {
1149 LOGP(DTBF, LOGL_NOTICE, "No PDCH resource for Uplink TBF\n");
1150 /* Caller will most probably send a Imm Ass Reject after return */
1151 return NULL;
1152 }
1153 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_CCCH, NULL);
1154 return ul_tbf;
1155}
1156
Pau Espin Pedrola621d592022-12-13 17:35:04 +01001157/* Create a temporary dummy TBF to Tx a ImmAssReject if allocating a new one during
1158 * packet resource Request failed. This is similar as ul_tbf_alloc() but without
1159 * calling tbf->setup() (in charge of TFI/USF allocation), and reusing resources
1160 * from Packet Resource Request we received. See TS 44.060 sec 7.1.3.2.1 */
1161struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_rejected_pacch(struct GprsMs *ms, struct gprs_rlcmac_pdch *pdch)
1162{
1163 struct gprs_rlcmac_ul_tbf *ul_tbf;
1164 ul_tbf = ul_tbf_alloc_rejected(ms->bts, ms, pdch);
1165 if (!ul_tbf)
1166 return NULL;
1167 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_PACCH, NULL);
1168 osmo_fsm_inst_dispatch(tbf_ul_ass_fi(ul_tbf_as_tbf(ul_tbf)), TBF_UL_ASS_EV_SCHED_ASS_REJ, NULL);
1169
1170 return ul_tbf;
1171}
1172
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001173/* A new DL-TBF is allocated and assigned through PACCH using "tbf".
1174 * "tbf" may be either a UL-TBF or a DL-TBF.
1175 * Note: This should be called only when MS is reachable, see ms_is_reachable_for_dl_ass().
1176 */
1177int ms_new_dl_tbf_assigned_on_pacch(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001178{
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001179 OSMO_ASSERT(tbf);
1180 const int8_t trx_no = tbf_get_trx(tbf)->trx_no;
1181 const bool single_slot = false;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001182 struct gprs_rlcmac_dl_tbf *dl_tbf;
1183
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001184 dl_tbf = dl_tbf_alloc(ms->bts, ms, trx_no, single_slot);
1185 if (!dl_tbf) {
1186 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1187 return -EBUSY;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001188 }
1189
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001190 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START (PACCH)\n");
1191 dl_tbf_trigger_ass_on_pacch(dl_tbf, tbf);
1192 return 0;
1193}
1194
1195/* A new DL-TBF is allocated and assigned through PCH.
1196 * Note: This should be called only when MS is reachable, see ms_is_reachable_for_dl_ass().
1197 */
1198int ms_new_dl_tbf_assigned_on_pch(struct GprsMs *ms)
1199{
1200 const int8_t trx_no = -1;
1201 const bool single_slot = true;
1202 struct gprs_rlcmac_dl_tbf *dl_tbf;
1203
1204 dl_tbf = dl_tbf_alloc(ms->bts, ms, trx_no, single_slot);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001205 if (!dl_tbf) {
1206 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1207 return -EBUSY;
1208 }
1209
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001210 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START (PCH)\n");
1211 dl_tbf_trigger_ass_on_pch(dl_tbf);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001212 return 0;
1213}
1214
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001215int ms_append_llc_dl_data(struct GprsMs *ms, uint16_t pdu_delay_csec, const uint8_t *data, uint16_t len)
1216{
1217 struct timespec expire_time;
1218 struct gprs_rlcmac_dl_tbf *dl_tbf;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001219 int rc = 0;
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001220
1221 LOGPMS(ms, DTBFDL, LOGL_DEBUG, "appending %u bytes to DL LLC queue\n", len);
1222
1223 struct msgb *llc_msg = msgb_alloc(len, "llc_pdu_queue");
1224 if (!llc_msg)
1225 return -ENOMEM;
1226
1227 llc_queue_calc_pdu_lifetime(ms->bts, pdu_delay_csec, &expire_time);
1228 memcpy(msgb_put(llc_msg, len), data, len);
1229 llc_queue_enqueue(ms_llc_queue(ms), llc_msg, &expire_time);
1230 ms_start_llc_timer(ms);
1231
1232 dl_tbf = ms_dl_tbf(ms);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001233 if (dl_tbf) {
1234 if (tbf_state(dl_tbf_as_tbf_const(dl_tbf)) == TBF_ST_WAIT_RELEASE) {
1235 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "in WAIT RELEASE state (T3193), so reuse TBF\n");
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001236 rc = ms_new_dl_tbf_assigned_on_pacch(ms, dl_tbf_as_tbf(dl_tbf));
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001237 }
1238 } else {
1239 /* Check if we can create a DL TBF to start sending the enqueued
1240 * data. Otherwise it will be triggered later when it is reachable
1241 * again. */
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001242 if (ms_is_reachable_for_dl_ass(ms)) {
1243 if (ms_ul_tbf(ms))
1244 rc = ms_new_dl_tbf_assigned_on_pacch(ms, ul_tbf_as_tbf(ms_ul_tbf(ms)));
1245 else
1246 rc = ms_new_dl_tbf_assigned_on_pch(ms);
1247 }
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001248 }
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001249 return rc;
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001250}