blob: 3e95103d8b0dfd3cd27a9e729221312a4c9a47dc [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{
66 talloc_free(ms);
67}
68
69void gprs_default_cb_ms_active(struct GprsMs *ms)
70{
71 /* do nothing */
72}
73
74static struct gpr_ms_callback gprs_default_cb = {
75 .ms_idle = gprs_default_cb_ms_idle,
76 .ms_active = gprs_default_cb_ms_active,
77};
78
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +010079static void ms_release_timer_cb(void *data)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010080{
81 struct GprsMs *ms = (struct GprsMs *) data;
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +010082 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Release timer expired\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010083
84 if (ms->timer.data) {
85 ms->timer.data = NULL;
86 ms_unref(ms);
87 }
88}
89
Pau Espin Pedrol14beef62022-10-26 19:44:07 +020090static void ms_llc_timer_cb(void *_ms)
91{
92 struct GprsMs *ms = _ms;
93 struct gprs_rlcmac_dl_tbf *dl_tbf = ms_dl_tbf(ms);
94
95 if (!dl_tbf)
96 return;
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +020097 if (tbf_state(dl_tbf_as_tbf_const(dl_tbf)) != TBF_ST_FLOW)
Pau Espin Pedrol14beef62022-10-26 19:44:07 +020098 return;
99
Pau Espin Pedrolbd1f01f2022-10-27 15:19:39 +0200100 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "LLC receive timeout, requesting DL ACK\n");
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200101
Pau Espin Pedrol8fa3e062022-10-28 17:38:52 +0200102 dl_tbf_request_dl_ack(dl_tbf);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200103}
104
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100105static int ms_talloc_destructor(struct GprsMs *ms);
Pau Espin Pedrolf80cc5b2023-04-17 14:25:51 +0200106struct GprsMs *ms_alloc(struct gprs_rlcmac_bts *bts)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100107{
108 struct GprsMs *ms = talloc_zero(tall_pcu_ctx, struct GprsMs);
Pau Espin Pedrolb4987462022-04-01 17:21:08 +0200109 OSMO_ASSERT(bts);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100110
111 talloc_set_destructor(ms, ms_talloc_destructor);
112
Pau Espin Pedrolfe7aee92023-04-17 18:23:56 +0200113 llist_add(&ms->list, &bts->ms_list);
114
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100115 ms->bts = bts;
116 ms->cb = gprs_default_cb;
Pau Espin Pedrolf80cc5b2023-04-17 14:25:51 +0200117 ms->tlli = GSM_RESERVED_TMSI;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100118 ms->new_ul_tlli = GSM_RESERVED_TMSI;
119 ms->new_dl_tlli = GSM_RESERVED_TMSI;
120 ms->ta = GSM48_TA_INVALID;
121 ms->current_cs_ul = UNKNOWN;
122 ms->current_cs_dl = UNKNOWN;
123 ms->is_idle = true;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100124 INIT_LLIST_HEAD(&ms->old_tbfs);
125
126 int codel_interval = LLC_CODEL_USE_DEFAULT;
127
Pau Espin Pedrolf80cc5b2023-04-17 14:25:51 +0200128 LOGP(DRLCMAC, LOGL_INFO, "Creating MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100129
130 ms->imsi[0] = '\0';
Pau Espin Pedrolc7802d92022-05-09 16:07:52 +0200131 osmo_timer_setup(&ms->timer, ms_release_timer_cb, NULL);
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200132 llc_queue_init(&ms->llc_queue, ms);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200133 memset(&ms->llc_timer, 0, sizeof(ms->llc_timer));
134 osmo_timer_setup(&ms->llc_timer, ms_llc_timer_cb, ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100135
136 ms_set_mode(ms, GPRS);
137
Pau Espin Pedrolb4987462022-04-01 17:21:08 +0200138 codel_interval = the_pcu->vty.llc_codel_interval_msec;
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200139 if (codel_interval == LLC_CODEL_USE_DEFAULT)
140 codel_interval = GPRS_CODEL_SLOW_INTERVAL_MS;
141 llc_queue_set_codel_interval(&ms->llc_queue, codel_interval);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100142
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100143 ms->last_cs_not_low = now_msec();
144 ms->app_info_pending = false;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100145
146 ms->ctrs = rate_ctr_group_alloc(ms, &ms_ctrg_desc, next_ms_ctr_group_id++);
147 if (!ms->ctrs)
148 goto free_ret;
149
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100150 return ms;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100151free_ret:
152 talloc_free(ms);
153 return NULL;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100154}
155
156static int ms_talloc_destructor(struct GprsMs *ms)
157{
158 struct llist_item *pos, *tmp;
159
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100160 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Destroying MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100161
Pau Espin Pedrolfe7aee92023-04-17 18:23:56 +0200162 llist_del(&ms->list);
163
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100164 ms_set_reserved_slots(ms, NULL, 0, 0);
165
Vadim Yanitskiye33c2362022-07-22 03:44:44 +0700166 osmo_timer_del(&ms->timer);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100167
168 if (ms->ul_tbf) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200169 tbf_set_ms(ul_tbf_as_tbf(ms->ul_tbf), NULL);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100170 ms->ul_tbf = NULL;
171 }
172
173 if (ms->dl_tbf) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200174 tbf_set_ms(dl_tbf_as_tbf(ms->dl_tbf), NULL);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100175 ms->dl_tbf = NULL;
176 }
177
178 llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
179 struct gprs_rlcmac_tbf *tbf = (struct gprs_rlcmac_tbf *)pos->entry;
180 tbf_set_ms(tbf, NULL);
181 }
182
183 llc_queue_clear(&ms->llc_queue, ms->bts);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200184 osmo_timer_del(&ms->llc_timer);
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100185
186 if (ms->ctrs)
187 rate_ctr_group_free(ms->ctrs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100188 return 0;
189}
190
191
192void ms_set_callback(struct GprsMs *ms, struct gpr_ms_callback *cb)
193{
194 if (cb)
195 ms->cb = *cb;
196 else
197 ms->cb = gprs_default_cb;
198}
199
200static void ms_update_status(struct GprsMs *ms)
201{
202 if (ms->ref > 0)
203 return;
204
205 if (ms_is_idle(ms) && !ms->is_idle) {
206 ms->is_idle = true;
207 ms->cb.ms_idle(ms);
208 /* this can be deleted by now, do not access it */
209 return;
210 }
211
212 if (!ms_is_idle(ms) && ms->is_idle) {
213 ms->is_idle = false;
214 ms->cb.ms_active(ms);
215 }
216}
217
218struct GprsMs *ms_ref(struct GprsMs *ms)
219{
220 ms->ref += 1;
221 return ms;
222}
223
224void ms_unref(struct GprsMs *ms)
225{
226 OSMO_ASSERT(ms->ref >= 0);
227 ms->ref -= 1;
228 if (ms->ref == 0)
229 ms_update_status(ms);
230}
231
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100232static void ms_release_timer_start(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100233{
Pau Espin Pedrol14379ef2023-04-17 20:43:26 +0200234 /* Immediate free():
235 * Skip delaying free() through release timer if delay is configured to be 0.
236 * This is useful for synced freed during unit tests.
237 */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100238 if (ms->delay == 0)
239 return;
240
Pau Espin Pedrol14379ef2023-04-17 20:43:26 +0200241 /* Immediate free():
242 * Skip delaying free() through release timer if TMSI is not
243 * known, since those cannot really be reused.
244 */
245 if (ms_tlli(ms) == GSM_RESERVED_TMSI)
246 return;
247
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100248 LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Schedule MS release in %u secs\n", ms->delay);
249
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100250 if (!ms->timer.data)
251 ms->timer.data = ms_ref(ms);
252
253 osmo_timer_schedule(&ms->timer, ms->delay, 0);
254}
255
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100256static void ms_release_timer_stop(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100257{
258 if (!ms->timer.data)
259 return;
260
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100261 LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Cancel scheduled MS release\n");
262
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100263 osmo_timer_del(&ms->timer);
264 ms->timer.data = NULL;
265 ms_unref(ms);
266}
267
268void ms_set_mode(struct GprsMs *ms, enum mcs_kind mode)
269{
270 ms->mode = mode;
271
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100272 switch (ms->mode) {
273 case GPRS:
274 if (!mcs_is_gprs(ms->current_cs_ul)) {
275 ms->current_cs_ul = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100276 ms->bts->initial_cs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100277 if (!mcs_is_valid(ms->current_cs_ul))
278 ms->current_cs_ul = CS1;
279 }
280 if (!mcs_is_gprs(ms->current_cs_dl)) {
281 ms->current_cs_dl = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100282 ms->bts->initial_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100283 if (!mcs_is_valid(ms->current_cs_dl))
284 ms->current_cs_dl = CS1;
285 }
286 break;
287
288 case EGPRS_GMSK:
Pau Espin Pedrol7bb8cd62021-01-25 15:08:35 +0100289 if (!mcs_is_edge_gmsk(ms->current_cs_ul)) {
290 ms->current_cs_ul = mcs_get_egprs_by_num(
291 ms->bts->initial_mcs_ul);
292 if (!mcs_is_valid(ms->current_cs_ul))
293 ms->current_cs_ul = MCS1;
294 }
295 if (!mcs_is_edge_gmsk(ms->current_cs_dl)) {
296 ms->current_cs_dl = mcs_get_egprs_by_num(
297 ms->bts->initial_mcs_dl);
298 if (!mcs_is_valid(ms->current_cs_dl))
299 ms->current_cs_dl = MCS1;
300 }
301 break;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100302 case EGPRS:
303 if (!mcs_is_edge(ms->current_cs_ul)) {
304 ms->current_cs_ul = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100305 ms->bts->initial_mcs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100306 if (!mcs_is_valid(ms->current_cs_ul))
307 ms->current_cs_ul = MCS1;
308 }
309 if (!mcs_is_edge(ms->current_cs_dl)) {
310 ms->current_cs_dl = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100311 ms->bts->initial_mcs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100312 if (!mcs_is_valid(ms->current_cs_dl))
313 ms->current_cs_dl = MCS1;
314 }
315 break;
316 }
317}
318
319static void ms_attach_ul_tbf(struct GprsMs *ms, struct gprs_rlcmac_ul_tbf *tbf)
320{
321 if (ms->ul_tbf == tbf)
322 return;
323
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100324 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 +0100325
326 ms_ref(ms);
327
328 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200329 llist_add_tail(tbf_ms_list(ul_tbf_as_tbf(ms->ul_tbf)), &ms->old_tbfs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100330
331 ms->ul_tbf = tbf;
332
333 if (tbf)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100334 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100335
336 ms_unref(ms);
337}
338
339static void ms_attach_dl_tbf(struct GprsMs *ms, struct gprs_rlcmac_dl_tbf *tbf)
340{
341 if (ms->dl_tbf == tbf)
342 return;
343
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100344 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 +0100345
346 ms_ref(ms);
347
348 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200349 llist_add_tail(tbf_ms_list(dl_tbf_as_tbf(ms->dl_tbf)), &ms->old_tbfs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100350
351 ms->dl_tbf = tbf;
352
353 if (tbf)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100354 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100355
356 ms_unref(ms);
357}
358
359void ms_attach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
360{
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200361 if (tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF)
Pau Espin Pedrolcc30b052022-10-27 15:25:55 +0200362 ms_attach_dl_tbf(ms, tbf_as_dl_tbf(tbf));
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200363 else
Pau Espin Pedrolcc30b052022-10-27 15:25:55 +0200364 ms_attach_ul_tbf(ms, tbf_as_ul_tbf(tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100365}
366
367void ms_detach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
368{
369 if (tbf == (struct gprs_rlcmac_tbf *)(ms->ul_tbf)) {
370 ms->ul_tbf = NULL;
371 } else if (tbf == (struct gprs_rlcmac_tbf *)(ms->dl_tbf)) {
372 ms->dl_tbf = NULL;
373 } else {
374 bool found = false;
375
376 struct llist_item *pos, *tmp;
377 llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
378 struct gprs_rlcmac_tbf *tmp_tbf = (struct gprs_rlcmac_tbf *)pos->entry;
379 if (tmp_tbf == tbf) {
380 llist_del(&pos->list);
381 found = true;
382 break;
383 }
384 }
385
386 /* Protect against recursive calls via set_ms() */
387 if (!found)
388 return;
389 }
390
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100391 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Detaching TBF: %s\n",
392 tbf_name(tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100393
394 if (tbf_ms(tbf) == ms)
395 tbf_set_ms(tbf, NULL);
396
397 if (!ms->dl_tbf && !ms->ul_tbf) {
398 ms_set_reserved_slots(ms, NULL, 0, 0);
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100399 ms->first_common_ts = NULL;
Pau Espin Pedrol14379ef2023-04-17 20:43:26 +0200400 ms_release_timer_start(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100401 }
402
403 ms_update_status(ms);
404}
405
406void ms_reset(struct GprsMs *ms)
407{
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100408 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Clearing MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100409
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100410 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100411
412 ms->tlli = GSM_RESERVED_TMSI;
413 ms->new_dl_tlli = ms->tlli;
414 ms->new_ul_tlli = ms->tlli;
415 ms->imsi[0] = '\0';
416}
417
Pau Espin Pedrol8abe13c2022-10-21 18:49:48 +0200418/* This function should be called on the MS object of a TBF each time an RLCMAC
419 * block is received for it with TLLI information.
420 * Besides updating the TLLI field on the MS object, it also seeks for other MS
421 * objects in the store and merges them into the current MS object. The MS
422 * duplication happened because we don't learn the TLLI of the created TBF until
423 * a later point. */
424void ms_update_announced_tlli(struct GprsMs *ms, uint32_t tlli)
425{
426 struct GprsMs *old_ms = NULL;
427
428 if (tlli == GSM_RESERVED_TMSI)
429 return;
430
431 /* When the TLLI does not match the ms, check if there is another
432 * MS object that belongs to that TLLI and if yes make sure one of them
433 * gets deleted. */
434 if (!ms_check_tlli(ms, tlli))
Pau Espin Pedroleb0a0522023-04-17 16:33:35 +0200435 old_ms = bts_get_ms_by_tlli(ms->bts, tlli, GSM_RESERVED_TMSI);
Pau Espin Pedrol8abe13c2022-10-21 18:49:48 +0200436
437 ms_set_tlli(ms, tlli);
438
439 if (old_ms)
440 ms_merge_and_clear_ms(ms, old_ms);
441 /* old_ms may no longer be available here */
442}
443
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100444/* Merge 'old_ms' object into 'ms' object.
445 * 'old_ms' may be freed during the call to this function, don't use the pointer to it afterwards */
446void ms_merge_and_clear_ms(struct GprsMs *ms, struct GprsMs *old_ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100447{
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100448 char old_ms_name[128];
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100449 OSMO_ASSERT(old_ms != ms);
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100450 ms_ref(old_ms);
451
452 ms_name_buf(old_ms, old_ms_name, sizeof(old_ms_name));
453
454 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Merge MS: %s\n", old_ms_name);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100455
456 if (strlen(ms_imsi(ms)) == 0 && strlen(ms_imsi(old_ms)) != 0)
457 osmo_strlcpy(ms->imsi, ms_imsi(old_ms), sizeof(ms->imsi));
458
459 if (!ms_ms_class(ms) && ms_ms_class(old_ms))
460 ms_set_ms_class(ms, ms_ms_class(old_ms));
461
462 if (!ms_egprs_ms_class(ms) && ms_egprs_ms_class(old_ms))
463 ms_set_egprs_ms_class(ms, ms_egprs_ms_class(old_ms));
464
465 llc_queue_move_and_merge(&ms->llc_queue, &old_ms->llc_queue);
466
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100467 /* Clean up the old MS object */
468 /* TODO: Use timer? */
469 if (ms_ul_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms), T_MAX))
470 tbf_free((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms));
471 if (ms_dl_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms), T_MAX))
472 tbf_free((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms));
473
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100474 ms_reset(old_ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100475
476 ms_unref(old_ms);
477}
478
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100479/* Apply changes to the TLLI directly, used interally by functions below: */
480static void ms_apply_tlli_change(struct GprsMs *ms, uint32_t tlli)
481{
482 ms->tlli = tlli;
483 ms->new_dl_tlli = GSM_RESERVED_TMSI;
484 ms->new_ul_tlli = GSM_RESERVED_TMSI;
485
486 /* Update TBF FSM names: */
487 if (ms->ul_tbf)
488 tbf_update_state_fsm_name(ul_tbf_as_tbf(ms->ul_tbf));
489 if (ms->dl_tbf)
490 tbf_update_state_fsm_name(dl_tbf_as_tbf(ms->dl_tbf));
491}
492
Pau Espin Pedrol0b5997e2022-10-21 14:10:41 +0200493/* Set/update the MS object TLLI based on knowledge gained from the MS side (Uplink direction) */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100494void ms_set_tlli(struct GprsMs *ms, uint32_t tlli)
495{
496 if (tlli == ms->tlli || tlli == ms->new_ul_tlli)
497 return;
498
499 if (tlli != ms->new_dl_tlli) {
500 LOGP(DRLCMAC, LOGL_INFO,
501 "Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
502 "not yet confirmed\n",
503 ms_tlli(ms), tlli);
504 ms->new_ul_tlli = tlli;
505 return;
506 }
507
508 LOGP(DRLCMAC, LOGL_INFO,
509 "Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
510 "already confirmed partly\n",
511 ms->tlli, tlli);
512
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100513 ms_apply_tlli_change(ms, tlli);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100514}
515
Pau Espin Pedrol0b5997e2022-10-21 14:10:41 +0200516/* Set/update the MS object TLLI based on knowledge gained from the SGSN side (Downlink direction) */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100517bool ms_confirm_tlli(struct GprsMs *ms, uint32_t tlli)
518{
519 if (tlli == ms->tlli || tlli == ms->new_dl_tlli)
520 return false;
521
522 if (tlli != ms->new_ul_tlli) {
523 /* The MS has not sent a message with the new TLLI, which may
524 * happen according to the spec [TODO: add reference]. */
525
526 LOGP(DRLCMAC, LOGL_INFO,
527 "The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
528 "partly confirmed\n", tlli);
529 /* Use the network's idea of TLLI as candidate, this does not
530 * change the result value of tlli() */
531 ms->new_dl_tlli = tlli;
532 return false;
533 }
534
535 LOGP(DRLCMAC, LOGL_INFO,
536 "Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
537
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100538 ms_apply_tlli_change(ms, tlli);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100539
540 return true;
541}
542
543void ms_set_imsi(struct GprsMs *ms, const char *imsi)
544{
545 if (!imsi) {
546 LOGP(DRLCMAC, LOGL_ERROR, "Expected IMSI!\n");
547 return;
548 }
549
550 if (imsi[0] && strlen(imsi) < 3) {
551 LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI '%s'!\n",
552 imsi);
553 return;
554 }
555
556 if (strcmp(imsi, ms->imsi) == 0)
557 return;
558
559 LOGP(DRLCMAC, LOGL_INFO,
560 "Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
561 ms_tlli(ms), ms->imsi, imsi);
562
Pau Espin Pedrolcde18c52023-04-17 18:16:48 +0200563 struct GprsMs *old_ms = bts_get_ms_by_imsi(ms->bts, imsi);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100564 /* Check if we are going to store a different MS object with already
565 existing IMSI. This is probably a bug in code calling this function,
566 since it should take care of this explicitly */
567 if (old_ms) {
568 /* We cannot find ms->ms by IMSI since we know that it has a
569 * different IMSI */
570 OSMO_ASSERT(old_ms != ms);
571
572 LOGPMS(ms, DRLCMAC, LOGL_NOTICE,
573 "IMSI '%s' was already assigned to another "
574 "MS object: TLLI = 0x%08x, that IMSI will be removed\n",
575 imsi, ms_tlli(old_ms));
576
577 ms_merge_and_clear_ms(ms, old_ms);
Pau Espin Pedrolb8ff74b2022-10-31 12:58:46 +0100578 /* old_ms may no longer be available here */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100579 }
580
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100581 /* Store the new IMSI: */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100582 osmo_strlcpy(ms->imsi, imsi, sizeof(ms->imsi));
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100583
584 /* Update TBF FSM names: */
585 if (ms->ul_tbf)
586 tbf_update_state_fsm_name(ul_tbf_as_tbf(ms->ul_tbf));
587 if (ms->dl_tbf)
588 tbf_update_state_fsm_name(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100589}
590
591void ms_set_ta(struct GprsMs *ms, uint8_t ta_)
592{
593 if (ta_ == ms->ta)
594 return;
595
596 if (gsm48_ta_is_valid(ta_)) {
597 LOGP(DRLCMAC, LOGL_INFO,
598 "Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
599 ms_tlli(ms), ms->ta, ta_);
600 ms->ta = ta_;
601 } else
602 LOGP(DRLCMAC, LOGL_NOTICE,
603 "MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
604 "value %d kept)\n", ms_tlli(ms), ta_, ms->ta);
605}
606
607void ms_set_ms_class(struct GprsMs *ms, uint8_t ms_class_)
608{
609 if (ms_class_ == ms->ms_class)
610 return;
611
612 LOGP(DRLCMAC, LOGL_INFO,
613 "Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
614 ms_tlli(ms), ms->ms_class, ms_class_);
615
616 ms->ms_class = ms_class_;
617}
618
619void ms_set_egprs_ms_class(struct GprsMs *ms, uint8_t ms_class_)
620{
621 if (ms_class_ == ms->egprs_ms_class)
622 return;
623
624 LOGP(DRLCMAC, LOGL_INFO,
625 "Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
626 ms_tlli(ms), ms->egprs_ms_class, ms_class_);
627
628 ms->egprs_ms_class = ms_class_;
629
630 if (!bts_max_mcs_ul(ms->bts) || !bts_max_mcs_dl(ms->bts)) {
631 LOGPMS(ms, DRLCMAC, LOGL_DEBUG,
632 "Avoid enabling EGPRS because use of MCS is disabled: ul=%u dl=%u\n",
633 bts_max_mcs_ul(ms->bts), bts_max_mcs_dl(ms->bts));
634 return;
635 }
636
637 if (mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts))) &&
638 mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts))) &&
639 ms_mode(ms) != EGPRS)
640 {
641 ms_set_mode(ms, EGPRS_GMSK);
642 } else {
643 ms_set_mode(ms, EGPRS);
644 }
645 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Enabled EGPRS, mode %s\n", mode_name(ms_mode(ms)));
646}
647
648void ms_update_error_rate(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, int error_rate)
649{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100650 int64_t now;
651 enum CodingScheme max_cs_dl = ms_max_cs_dl(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100652 OSMO_ASSERT(max_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100653
654 if (error_rate < 0)
655 return;
656
657 now = now_msec();
658
659 /* TODO: Check for TBF direction */
660 /* TODO: Support different CS values for UL and DL */
661
662 ms->nack_rate_dl = error_rate;
663
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100664 if (error_rate > the_pcu->vty.cs_adj_upper_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100665 if (mcs_chan_code(ms->current_cs_dl) > 0) {
666 mcs_dec_kind(&ms->current_cs_dl, ms_mode(ms));
667 LOGP(DRLCMACDL, LOGL_INFO,
668 "MS (IMSI %s): High error rate %d%%, "
669 "reducing CS level to %s\n",
670 ms_imsi(ms), error_rate, mcs_name(ms->current_cs_dl));
671 ms->last_cs_not_low = now;
672 }
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100673 } else if (error_rate < the_pcu->vty.cs_adj_lower_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100674 if (ms->current_cs_dl < max_cs_dl) {
675 if (now - ms->last_cs_not_low > 1000) {
676 mcs_inc_kind(&ms->current_cs_dl, ms_mode(ms));
677
678 LOGP(DRLCMACDL, LOGL_INFO,
679 "MS (IMSI %s): Low error rate %d%%, "
680 "increasing DL CS level to %s\n",
681 ms_imsi(ms), error_rate,
682 mcs_name(ms->current_cs_dl));
683 ms->last_cs_not_low = now;
684 } else {
685 LOGP(DRLCMACDL, LOGL_DEBUG,
686 "MS (IMSI %s): Low error rate %d%%, "
687 "ignored (within blocking period)\n",
688 ms_imsi(ms), error_rate);
689 }
690 }
691 } else {
692 LOGP(DRLCMACDL, LOGL_DEBUG,
693 "MS (IMSI %s): Medium error rate %d%%, ignored\n",
694 ms_imsi(ms), error_rate);
695 ms->last_cs_not_low = now;
696 }
697}
698
699enum CodingScheme ms_max_cs_ul(const struct GprsMs *ms)
700{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100701 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100702 OSMO_ASSERT(ms->bts != NULL);
703
704 if (mcs_is_gprs(ms->current_cs_ul)) {
705 if (!bts_max_cs_ul(ms->bts)) {
706 return CS4;
707 }
708
709 return mcs_get_gprs_by_num(bts_max_cs_ul(ms->bts));
710 }
711
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100712 cs = mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts));
713 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
714 cs = MCS4;
715 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100716}
717
718void ms_set_current_cs_dl(struct GprsMs *ms, enum CodingScheme scheme)
719{
720 ms->current_cs_dl = scheme;
721}
722
723enum CodingScheme ms_max_cs_dl(const struct GprsMs *ms)
724{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100725 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100726 OSMO_ASSERT(ms->bts != NULL);
727
728 if (mcs_is_gprs(ms->current_cs_dl)) {
729 if (!bts_max_cs_dl(ms->bts)) {
730 return CS4;
731 }
732
733 return mcs_get_gprs_by_num(bts_max_cs_dl(ms->bts));
734 }
735
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100736 cs = mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts));
737 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
738 cs = MCS4;
739 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100740}
741
742void ms_update_cs_ul(struct GprsMs *ms, const struct pcu_l1_meas *meas)
743{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100744 enum CodingScheme max_cs_ul = ms_max_cs_ul(ms);
745
746 int old_link_qual;
747 int low;
748 int high;
749 enum CodingScheme new_cs_ul = ms->current_cs_ul;
750 uint8_t current_cs = mcs_chan_code(ms->current_cs_ul);
751
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100752 if (!max_cs_ul) {
753 LOGP(DRLCMACMEAS, LOGL_ERROR,
754 "max_cs_ul cannot be derived (current UL CS: %s)\n",
755 mcs_name(ms->current_cs_ul));
756 return;
757 }
758
759 if (!ms->current_cs_ul) {
760 LOGP(DRLCMACMEAS, LOGL_ERROR,
761 "Unable to update UL (M)CS because it's not set: %s\n",
762 mcs_name(ms->current_cs_ul));
763 return;
764 }
765
766 if (!meas->have_link_qual) {
767 LOGP(DRLCMACMEAS, LOGL_ERROR,
768 "Unable to update UL (M)CS %s because we don't have link quality measurements.\n",
769 mcs_name(ms->current_cs_ul));
770 return;
771 }
772
773 if (mcs_is_gprs(ms->current_cs_ul)) {
774 if (current_cs >= MAX_GPRS_CS)
775 current_cs = MAX_GPRS_CS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100776 low = the_pcu->vty.cs_lqual_ranges[current_cs].low;
777 high = the_pcu->vty.cs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100778 } else if (mcs_is_edge(ms->current_cs_ul)) {
779 if (current_cs >= MAX_EDGE_MCS)
780 current_cs = MAX_EDGE_MCS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100781 low = the_pcu->vty.mcs_lqual_ranges[current_cs].low;
782 high = the_pcu->vty.mcs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100783 } else {
784 LOGP(DRLCMACMEAS, LOGL_ERROR,
785 "Unable to update UL (M)CS because it's neither GPRS nor EDGE: %s\n",
786 mcs_name(ms->current_cs_ul));
787 return;
788 }
789
790 /* To avoid rapid changes of the coding scheme, we also take
791 * the old link quality value into account (if present). */
792 if (ms->l1_meas.have_link_qual)
793 old_link_qual = ms->l1_meas.link_qual;
794 else
795 old_link_qual = meas->link_qual;
796
797 if (meas->link_qual < low && old_link_qual < low)
798 mcs_dec_kind(&new_cs_ul, ms_mode(ms));
799 else if (meas->link_qual > high && old_link_qual > high &&
800 ms->current_cs_ul < max_cs_ul)
801 mcs_inc_kind(&new_cs_ul, ms_mode(ms));
802
803 if (ms->current_cs_ul != new_cs_ul) {
804 LOGPMS(ms, DRLCMACMEAS, LOGL_INFO,
805 "Link quality %ddB (old %ddB) left window [%d, %d], "
806 "modifying uplink CS level: %s -> %s\n",
807 meas->link_qual, old_link_qual,
808 low, high,
809 mcs_name(ms->current_cs_ul), mcs_name(new_cs_ul));
810
811 ms->current_cs_ul = new_cs_ul;
812 }
813}
814
815void ms_update_l1_meas(struct GprsMs *ms, const struct pcu_l1_meas *meas)
816{
817 unsigned i;
818
819 ms_update_cs_ul(ms, meas);
820
821 if (meas->have_rssi)
822 pcu_l1_meas_set_rssi(&ms->l1_meas, meas->rssi);
823 if (meas->have_bto)
824 pcu_l1_meas_set_bto(&ms->l1_meas, meas->bto);
825 if (meas->have_ber)
826 pcu_l1_meas_set_ber(&ms->l1_meas, meas->ber);
827 if (meas->have_link_qual)
828 pcu_l1_meas_set_link_qual(&ms->l1_meas, meas->link_qual);
829
830 if (meas->have_ms_rx_qual)
831 pcu_l1_meas_set_ms_rx_qual(&ms->l1_meas, meas->ms_rx_qual);
832 if (meas->have_ms_c_value)
833 pcu_l1_meas_set_ms_c_value(&ms->l1_meas, meas->ms_c_value);
834 if (meas->have_ms_sign_var)
835 pcu_l1_meas_set_ms_sign_var(&ms->l1_meas, meas->ms_sign_var);
836
837 if (meas->have_ms_i_level) {
838 for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
839 if (meas->ts[i].have_ms_i_level)
840 pcu_l1_meas_set_ms_i_level(&ms->l1_meas, i, meas->ts[i].ms_i_level);
841 else
842 ms->l1_meas.ts[i].have_ms_i_level = 0;
843 }
844 }
845}
846
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100847/* req_mcs_kind acts as a set filter, where EGPRS means any and GPRS is the most restrictive */
848enum CodingScheme ms_current_cs_dl(const struct GprsMs *ms, enum mcs_kind req_mcs_kind)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100849{
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100850 enum CodingScheme orig_cs = ms->current_cs_dl;
851 struct gprs_rlcmac_bts *bts = ms->bts;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100852 size_t unencoded_octets;
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100853 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100854
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100855 /* It could be that a TBF requests a GPRS CS despite the MS currently
856 being upgraded to EGPRS (hence reporting MCS). That could happen
857 because the TBF was created early in the process where we didn't have
858 yet enough information about the MS, and only AFTER it was created we
859 upgraded the MS to be EGPRS capable.
860 As a result, when the MS is queried for the target CS here, we could be
861 returning an MCS despite the current TBF being established as GPRS,
862 but we rather stick to the TBF type we assigned to the MS rather than
863 magically sending EGPRS data blocks to a GPRS TBF.
864 It could also be that the caller requests specific MCS kind
865 explicitly too due to scheduling restrictions (GPRS+EGPRS multiplexing). */
866 if (req_mcs_kind == EGPRS_GMSK && mcs_is_edge(orig_cs) && orig_cs > MCS4) {
867 cs = bts_cs_dl_is_supported(bts, MCS4) ? MCS4 :
868 bts_cs_dl_is_supported(bts, MCS3) ? MCS3 :
869 bts_cs_dl_is_supported(bts, MCS2) ? MCS2 :
870 MCS1;
871 } else if (req_mcs_kind == GPRS && mcs_is_edge(orig_cs)) { /* GPRS */
872 int i;
873 cs = orig_cs > MCS4 ? MCS4 : orig_cs;
874 cs -= (MCS1 - CS1); /* MCSx -> CSx */
875 /* Find suitable CS starting from equivalent MCS which is supported by BTS: */
876 for (i = mcs_chan_code(cs); !bts_cs_dl_is_supported(bts, CS1 + i); i--);
877 OSMO_ASSERT(i >= 0 && i <= 3); /* CS1 is always supported */
878 cs = CS1 + i;
879 } else {
880 cs = orig_cs;
881 }
882
883 if (orig_cs != cs)
884 LOGPMS(ms, DRLCMACDL, LOGL_INFO, "MS (mode=%s) suggests transmitting "
885 "DL %s, downgrade to %s in order to match TBF & scheduler requirements\n",
886 mode_name(ms_mode(ms)), mcs_name(orig_cs), mcs_name(cs));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100887
888 unencoded_octets = llc_queue_octets(&ms->llc_queue);
889
890 /* If the DL TBF is active, add number of unencoded chunk octets */
891 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200892 unencoded_octets += llc_chunk_size(tbf_llc(dl_tbf_as_tbf(ms->dl_tbf)));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100893
894 /* There are many unencoded octets, don't reduce */
Pau Espin Pedrolad79b852021-01-14 13:20:55 +0100895 if (unencoded_octets >= the_pcu->vty.cs_downgrade_threshold)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100896 return cs;
897
898 /* RF conditions are good, don't reduce */
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100899 if (ms->nack_rate_dl < the_pcu->vty.cs_adj_lower_limit)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100900 return cs;
901
902 /* The throughput would probably be better if the CS level was reduced */
903 mcs_dec_kind(&cs, ms_mode(ms));
904
905 /* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
906 if (cs == CS2)
907 mcs_dec_kind(&cs, ms_mode(ms));
908
909 return cs;
910}
911
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100912struct gprs_rlcmac_pdch *ms_first_common_ts(const struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100913{
Pau Espin Pedrol345d9ad2022-12-12 19:22:44 +0100914 return ms->first_common_ts;
915}
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100916
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100917void ms_set_first_common_ts(struct GprsMs *ms, struct gprs_rlcmac_pdch *pdch)
Pau Espin Pedrol345d9ad2022-12-12 19:22:44 +0100918{
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100919 OSMO_ASSERT(pdch);
920 ms->first_common_ts = pdch;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100921}
922
923uint8_t ms_dl_slots(const struct GprsMs *ms)
924{
925 uint8_t slots = 0;
926
927 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200928 slots |= tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100929
930 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200931 slots |= tbf_dl_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100932
933 return slots;
934}
935
936uint8_t ms_ul_slots(const struct GprsMs *ms)
937{
938 uint8_t slots = 0;
939
940 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200941 slots |= tbf_ul_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100942
943 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200944 slots |= tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100945
946 return slots;
947}
948
949uint8_t ms_current_pacch_slots(const struct GprsMs *ms)
950{
951 uint8_t slots = 0;
952
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200953 bool is_dl_active = ms->dl_tbf && tbf_is_tfi_assigned(dl_tbf_as_tbf(ms->dl_tbf));
954 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 +0100955
956 if (!is_dl_active && !is_ul_active)
957 return 0;
958
959 /* see TS 44.060, 8.1.1.2.2 */
960 if (is_dl_active && !is_ul_active)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200961 slots = tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100962 else if (!is_dl_active && is_ul_active)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200963 slots = tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100964 else
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200965 slots = tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf)) &
966 tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100967
968 /* Assume a multislot class 1 device */
969 /* TODO: For class 2 devices, this could be removed */
970 slots = pcu_lsb(slots);
971
972 return slots;
973}
974
975void ms_set_reserved_slots(struct GprsMs *ms, struct gprs_rlcmac_trx *trx,
976 uint8_t ul_slots, uint8_t dl_slots)
977{
978 if (ms->current_trx) {
979 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
980 ms->reserved_dl_slots);
981 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
982 ms->reserved_ul_slots);
983 ms->reserved_dl_slots = 0;
984 ms->reserved_ul_slots = 0;
985 }
986 ms->current_trx = trx;
987 if (trx) {
988 ms->reserved_dl_slots = dl_slots;
989 ms->reserved_ul_slots = ul_slots;
990 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
991 ms->reserved_dl_slots);
992 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
993 ms->reserved_ul_slots);
994 }
995}
996
997struct gprs_rlcmac_tbf *ms_tbf(const struct GprsMs *ms, enum gprs_rlcmac_tbf_direction dir)
998{
999 switch (dir) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +02001000 case GPRS_RLCMAC_DL_TBF: return dl_tbf_as_tbf(ms->dl_tbf);
1001 case GPRS_RLCMAC_UL_TBF: return ul_tbf_as_tbf(ms->ul_tbf);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +01001002 }
1003
1004 return NULL;
1005}
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001006
Pau Espin Pedrol3547d642022-10-21 15:00:08 +02001007const char *ms_name(const struct GprsMs *ms)
1008{
1009 static char _ms_name_buf[128];
1010 return ms_name_buf(ms, _ms_name_buf, sizeof(_ms_name_buf));
1011}
1012
1013char *ms_name_buf(const struct GprsMs *ms, char *buf, unsigned int buf_size)
1014{
Pau Espin Pedrol94f82582022-11-03 14:16:17 +01001015 struct osmo_strbuf sb = { .buf = buf, .len = buf_size };
1016 uint32_t tlli = ms_tlli(ms);
1017
1018 OSMO_STRBUF_PRINTF(sb, "MS(");
1019 if (ms_imsi_is_valid(ms))
1020 OSMO_STRBUF_PRINTF(sb, "IMSI-%s:", ms_imsi(ms));
1021 if (tlli != GSM_RESERVED_TMSI)
1022 OSMO_STRBUF_PRINTF(sb, "TLLI-0x%08x:", tlli);
1023 OSMO_STRBUF_PRINTF(sb, "TA-%" PRIu8 ":MSCLS-%" PRIu8 "-%" PRIu8,
1024 ms_ta(ms), ms_ms_class(ms), ms_egprs_ms_class(ms));
1025 if (ms->ul_tbf)
1026 OSMO_STRBUF_PRINTF(sb, ":UL");
1027 if (ms->dl_tbf)
1028 OSMO_STRBUF_PRINTF(sb, ":DL");
1029
1030 OSMO_STRBUF_PRINTF(sb, ")");
Pau Espin Pedrol3547d642022-10-21 15:00:08 +02001031 return buf;
1032}
1033
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001034int ms_nacc_start(struct GprsMs *ms, Packet_Cell_Change_Notification_t *notif)
1035{
1036 if (!ms->nacc)
1037 ms->nacc = nacc_fsm_alloc(ms);
1038 if (!ms->nacc)
1039 return -EINVAL;
1040 return osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_RX_CELL_CHG_NOTIFICATION, notif);
1041}
1042
1043bool ms_nacc_rts(const struct GprsMs *ms)
1044{
1045 if (!ms->nacc)
1046 return false;
1047 if (ms->nacc->fi->state == NACC_ST_TX_NEIGHBOUR_DATA ||
1048 ms->nacc->fi->state == NACC_ST_TX_CELL_CHG_CONTINUE)
1049 return true;
1050 return false;
1051}
1052
Pau Espin Pedrol5ba3ef92022-12-12 18:02:25 +01001053struct msgb *ms_nacc_create_rlcmac_msg(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf,
1054 const struct gprs_rlcmac_pdch *pdch, uint32_t fn)
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001055{
1056 int rc;
1057 struct nacc_ev_create_rlcmac_msg_ctx data_ctx;
1058
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001059 data_ctx = (struct nacc_ev_create_rlcmac_msg_ctx) {
1060 .tbf = tbf,
Pau Espin Pedrol5ba3ef92022-12-12 18:02:25 +01001061 .pdch = pdch,
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001062 .fn = fn,
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001063 .msg = NULL,
1064 };
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001065
1066 rc = osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_CREATE_RLCMAC_MSG, &data_ctx);
1067 if (rc != 0 || !data_ctx.msg)
1068 return NULL;
1069 return data_ctx.msg;
1070}
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001071
1072static void ms_start_llc_timer(struct GprsMs *ms)
1073{
1074 if (the_pcu->vty.llc_idle_ack_csec > 0) {
1075 struct timespec tv;
1076 csecs_to_timespec(the_pcu->vty.llc_idle_ack_csec, &tv);
1077 osmo_timer_schedule(&ms->llc_timer, tv.tv_sec, tv.tv_nsec / 1000);
1078 }
1079}
1080
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001081/* Can we get to send a DL TBF ass to the MS? */
1082static bool ms_is_reachable_for_dl_ass(const struct GprsMs *ms)
1083{
1084 struct gprs_rlcmac_ul_tbf *ul_tbf = ms_ul_tbf(ms);
1085
1086 /* This function assumes it is called when no DL TBF is present */
1087 OSMO_ASSERT(!ms_dl_tbf(ms));
1088
1089 /* 3GPP TS 44.060 sec 7.1.3.1 Initiation of the Packet resource request procedure:
1090 * "Furthermore, the mobile station shall not respond to PACKET DOWNLINK ASSIGNMENT
1091 * or MULTIPLE TBF DOWNLINK ASSIGNMENT messages before contention resolution is
1092 * completed on the mobile station side." */
1093 /* The possible uplink TBF is used to trigger downlink assignment:
1094 * - If there is no uplink TBF the MS is potentially in packet idle mode
1095 * and hence assignment will be done over CCCH (PCH)
1096 * - If there's an uplink TBF but it is finished (waiting for last PKT
1097 * CTRL ACK after sending last Pkt UL ACK/NACK with FINAL_ACK=1, then we
1098 * have no ways to contact the MS right now. Assignment will be delayed
1099 * until PKT CTRL ACK is received and the TBF is released at the MS side
1100 * (then assignment goes through PCH).
1101 */
1102 if (!ul_tbf)
1103 return true;
1104 if (ul_tbf_contention_resolution_done(ul_tbf) &&
1105 !tbf_ul_ack_waiting_cnf_final_ack(ul_tbf))
1106 return true;
1107
1108 return false;
1109
1110}
1111
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001112/* Alloc a UL TBF to be assigned over PACCH. Called when an MS requests to
1113 * create a new UL TBF during the end of life of a previous UL TBF (or an SBA).
1114 * In summary, this TBF is allocated as a consequence of receiving a "Pkt
1115 * Resource Req" or "Pkt Ctrl Ack" from the MS.
1116 * See TS 44.060 9.3.2.4.2 "Non-extended uplink TBF mode".
1117 */
1118struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_pacch(struct GprsMs *ms, int8_t use_trx)
1119{
1120 const bool single_slot = false;
1121 struct gprs_rlcmac_ul_tbf *ul_tbf;
1122
Pau Espin Pedrolbda7bb72022-10-31 14:33:09 +01001123 ul_tbf = ul_tbf_alloc(ms->bts, ms, use_trx, single_slot);
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001124 if (!ul_tbf) {
1125 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1126 /* Caller will most probably send a Imm Ass Reject after return */
1127 return NULL;
1128 }
1129 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_PACCH, NULL);
1130 /* Contention resolution is considered to be done since TLLI is known in MS */
1131 return ul_tbf;
1132}
1133
1134/* Alloc a UL TBF to be assigned over AGCH. Used by request of a "One phase
1135 * packet access", where MS requested only 1 PDCH TS (TS 44.018 Table 9.1.8.1). */
1136struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_agch(struct GprsMs *ms)
1137{
1138 const int8_t trx_no = -1;
1139 const bool single_slot = true;
1140 struct gprs_rlcmac_ul_tbf *ul_tbf;
1141
Pau Espin Pedrolbda7bb72022-10-31 14:33:09 +01001142 ul_tbf = ul_tbf_alloc(ms->bts, ms, trx_no, single_slot);
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001143 if (!ul_tbf) {
1144 LOGP(DTBF, LOGL_NOTICE, "No PDCH resource for Uplink TBF\n");
1145 /* Caller will most probably send a Imm Ass Reject after return */
1146 return NULL;
1147 }
1148 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_CCCH, NULL);
1149 return ul_tbf;
1150}
1151
Pau Espin Pedrola621d592022-12-13 17:35:04 +01001152/* Create a temporary dummy TBF to Tx a ImmAssReject if allocating a new one during
1153 * packet resource Request failed. This is similar as ul_tbf_alloc() but without
1154 * calling tbf->setup() (in charge of TFI/USF allocation), and reusing resources
1155 * from Packet Resource Request we received. See TS 44.060 sec 7.1.3.2.1 */
1156struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_rejected_pacch(struct GprsMs *ms, struct gprs_rlcmac_pdch *pdch)
1157{
1158 struct gprs_rlcmac_ul_tbf *ul_tbf;
1159 ul_tbf = ul_tbf_alloc_rejected(ms->bts, ms, pdch);
1160 if (!ul_tbf)
1161 return NULL;
1162 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_PACCH, NULL);
1163 osmo_fsm_inst_dispatch(tbf_ul_ass_fi(ul_tbf_as_tbf(ul_tbf)), TBF_UL_ASS_EV_SCHED_ASS_REJ, NULL);
1164
1165 return ul_tbf;
1166}
1167
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001168/* A new DL-TBF is allocated and assigned through PACCH using "tbf".
1169 * "tbf" may be either a UL-TBF or a DL-TBF.
1170 * Note: This should be called only when MS is reachable, see ms_is_reachable_for_dl_ass().
1171 */
1172int ms_new_dl_tbf_assigned_on_pacch(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001173{
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001174 OSMO_ASSERT(tbf);
1175 const int8_t trx_no = tbf_get_trx(tbf)->trx_no;
1176 const bool single_slot = false;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001177 struct gprs_rlcmac_dl_tbf *dl_tbf;
1178
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001179 dl_tbf = dl_tbf_alloc(ms->bts, ms, trx_no, single_slot);
1180 if (!dl_tbf) {
1181 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1182 return -EBUSY;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001183 }
1184
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001185 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START (PACCH)\n");
1186 dl_tbf_trigger_ass_on_pacch(dl_tbf, tbf);
1187 return 0;
1188}
1189
1190/* A new DL-TBF is allocated and assigned through PCH.
1191 * Note: This should be called only when MS is reachable, see ms_is_reachable_for_dl_ass().
1192 */
1193int ms_new_dl_tbf_assigned_on_pch(struct GprsMs *ms)
1194{
1195 const int8_t trx_no = -1;
1196 const bool single_slot = true;
1197 struct gprs_rlcmac_dl_tbf *dl_tbf;
1198
1199 dl_tbf = dl_tbf_alloc(ms->bts, ms, trx_no, single_slot);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001200 if (!dl_tbf) {
1201 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1202 return -EBUSY;
1203 }
1204
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001205 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START (PCH)\n");
1206 dl_tbf_trigger_ass_on_pch(dl_tbf);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001207 return 0;
1208}
1209
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001210int ms_append_llc_dl_data(struct GprsMs *ms, uint16_t pdu_delay_csec, const uint8_t *data, uint16_t len)
1211{
1212 struct timespec expire_time;
1213 struct gprs_rlcmac_dl_tbf *dl_tbf;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001214 int rc = 0;
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001215
1216 LOGPMS(ms, DTBFDL, LOGL_DEBUG, "appending %u bytes to DL LLC queue\n", len);
1217
1218 struct msgb *llc_msg = msgb_alloc(len, "llc_pdu_queue");
1219 if (!llc_msg)
1220 return -ENOMEM;
1221
1222 llc_queue_calc_pdu_lifetime(ms->bts, pdu_delay_csec, &expire_time);
1223 memcpy(msgb_put(llc_msg, len), data, len);
1224 llc_queue_enqueue(ms_llc_queue(ms), llc_msg, &expire_time);
1225 ms_start_llc_timer(ms);
1226
1227 dl_tbf = ms_dl_tbf(ms);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001228 if (dl_tbf) {
1229 if (tbf_state(dl_tbf_as_tbf_const(dl_tbf)) == TBF_ST_WAIT_RELEASE) {
1230 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "in WAIT RELEASE state (T3193), so reuse TBF\n");
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001231 rc = ms_new_dl_tbf_assigned_on_pacch(ms, dl_tbf_as_tbf(dl_tbf));
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001232 }
1233 } else {
1234 /* Check if we can create a DL TBF to start sending the enqueued
1235 * data. Otherwise it will be triggered later when it is reachable
1236 * again. */
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001237 if (ms_is_reachable_for_dl_ass(ms)) {
1238 if (ms_ul_tbf(ms))
1239 rc = ms_new_dl_tbf_assigned_on_pacch(ms, ul_tbf_as_tbf(ms_ul_tbf(ms)));
1240 else
1241 rc = ms_new_dl_tbf_assigned_on_pch(ms);
1242 }
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001243 }
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001244 return rc;
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001245}