blob: 29d226fad4b2f095c33e663dbacd7c0566e22042 [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 Pedrol8abe13c2022-10-21 18:49:48 +020027#include "gprs_ms_storage.h"
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010028
29#include <time.h>
30
31#include <osmocom/core/talloc.h>
32#include <osmocom/core/utils.h>
33#include <osmocom/core/timer.h>
34#include <osmocom/gsm/protocol/gsm_04_08.h>
35#include <osmocom/gsm/gsm48.h>
36#include <osmocom/core/logging.h>
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +010037#include <osmocom/core/stats.h>
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010038#include "coding_scheme.h"
39
40#define GPRS_CODEL_SLOW_INTERVAL_MS 4000
41
42extern void *tall_pcu_ctx;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +010043static unsigned int next_ms_ctr_group_id;
44
45static const struct rate_ctr_desc ms_ctr_description[] = {
46 [MS_CTR_DL_CTRL_MSG_SCHED] = { "ms:dl_ctrl_msg_sched", "Amount of DL CTRL messages scheduled" },
47};
48
Pau Espin Pedrolf5a251b2021-01-12 20:57:56 +010049static const struct rate_ctr_group_desc ms_ctrg_desc = {
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +010050 .group_name_prefix = "pcu:ms",
51 .group_description = "MS Statistics",
52 .class_id = OSMO_STATS_CLASS_SUBSCRIBER,
53 .num_ctr = ARRAY_SIZE(ms_ctr_description),
54 .ctr_desc = ms_ctr_description,
55};
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010056
57static int64_t now_msec()
58{
59 struct timespec ts;
60 osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
61
62 return (int64_t)(ts.tv_sec) * 1000 + ts.tv_nsec / 1000000;
63}
64
65void gprs_default_cb_ms_idle(struct GprsMs *ms)
66{
67 talloc_free(ms);
68}
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 Pedrol2182e622021-01-14 16:48:38 +0100107struct GprsMs *ms_alloc(struct gprs_rlcmac_bts *bts, uint32_t tlli)
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
114 ms->bts = bts;
115 ms->cb = gprs_default_cb;
116 ms->tlli = tlli;
117 ms->new_ul_tlli = GSM_RESERVED_TMSI;
118 ms->new_dl_tlli = GSM_RESERVED_TMSI;
119 ms->ta = GSM48_TA_INVALID;
120 ms->current_cs_ul = UNKNOWN;
121 ms->current_cs_dl = UNKNOWN;
122 ms->is_idle = true;
123 INIT_LLIST_HEAD(&ms->list);
124 INIT_LLIST_HEAD(&ms->old_tbfs);
125
126 int codel_interval = LLC_CODEL_USE_DEFAULT;
127
128 LOGP(DRLCMAC, LOGL_INFO, "Creating MS object, TLLI = 0x%08x\n", tlli);
129
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
162 ms_set_reserved_slots(ms, NULL, 0, 0);
163
Vadim Yanitskiye33c2362022-07-22 03:44:44 +0700164 osmo_timer_del(&ms->timer);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100165
166 if (ms->ul_tbf) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200167 tbf_set_ms(ul_tbf_as_tbf(ms->ul_tbf), NULL);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100168 ms->ul_tbf = NULL;
169 }
170
171 if (ms->dl_tbf) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200172 tbf_set_ms(dl_tbf_as_tbf(ms->dl_tbf), NULL);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100173 ms->dl_tbf = NULL;
174 }
175
176 llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
177 struct gprs_rlcmac_tbf *tbf = (struct gprs_rlcmac_tbf *)pos->entry;
178 tbf_set_ms(tbf, NULL);
179 }
180
181 llc_queue_clear(&ms->llc_queue, ms->bts);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200182 osmo_timer_del(&ms->llc_timer);
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100183
184 if (ms->ctrs)
185 rate_ctr_group_free(ms->ctrs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100186 return 0;
187}
188
189
190void ms_set_callback(struct GprsMs *ms, struct gpr_ms_callback *cb)
191{
192 if (cb)
193 ms->cb = *cb;
194 else
195 ms->cb = gprs_default_cb;
196}
197
198static void ms_update_status(struct GprsMs *ms)
199{
200 if (ms->ref > 0)
201 return;
202
203 if (ms_is_idle(ms) && !ms->is_idle) {
204 ms->is_idle = true;
205 ms->cb.ms_idle(ms);
206 /* this can be deleted by now, do not access it */
207 return;
208 }
209
210 if (!ms_is_idle(ms) && ms->is_idle) {
211 ms->is_idle = false;
212 ms->cb.ms_active(ms);
213 }
214}
215
216struct GprsMs *ms_ref(struct GprsMs *ms)
217{
218 ms->ref += 1;
219 return ms;
220}
221
222void ms_unref(struct GprsMs *ms)
223{
224 OSMO_ASSERT(ms->ref >= 0);
225 ms->ref -= 1;
226 if (ms->ref == 0)
227 ms_update_status(ms);
228}
229
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100230static void ms_release_timer_start(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100231{
232 if (ms->delay == 0)
233 return;
234
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100235 LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Schedule MS release in %u secs\n", ms->delay);
236
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100237 if (!ms->timer.data)
238 ms->timer.data = ms_ref(ms);
239
240 osmo_timer_schedule(&ms->timer, ms->delay, 0);
241}
242
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100243static void ms_release_timer_stop(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100244{
245 if (!ms->timer.data)
246 return;
247
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100248 LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Cancel scheduled MS release\n");
249
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100250 osmo_timer_del(&ms->timer);
251 ms->timer.data = NULL;
252 ms_unref(ms);
253}
254
255void ms_set_mode(struct GprsMs *ms, enum mcs_kind mode)
256{
257 ms->mode = mode;
258
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100259 switch (ms->mode) {
260 case GPRS:
261 if (!mcs_is_gprs(ms->current_cs_ul)) {
262 ms->current_cs_ul = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100263 ms->bts->initial_cs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100264 if (!mcs_is_valid(ms->current_cs_ul))
265 ms->current_cs_ul = CS1;
266 }
267 if (!mcs_is_gprs(ms->current_cs_dl)) {
268 ms->current_cs_dl = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100269 ms->bts->initial_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100270 if (!mcs_is_valid(ms->current_cs_dl))
271 ms->current_cs_dl = CS1;
272 }
273 break;
274
275 case EGPRS_GMSK:
Pau Espin Pedrol7bb8cd62021-01-25 15:08:35 +0100276 if (!mcs_is_edge_gmsk(ms->current_cs_ul)) {
277 ms->current_cs_ul = mcs_get_egprs_by_num(
278 ms->bts->initial_mcs_ul);
279 if (!mcs_is_valid(ms->current_cs_ul))
280 ms->current_cs_ul = MCS1;
281 }
282 if (!mcs_is_edge_gmsk(ms->current_cs_dl)) {
283 ms->current_cs_dl = mcs_get_egprs_by_num(
284 ms->bts->initial_mcs_dl);
285 if (!mcs_is_valid(ms->current_cs_dl))
286 ms->current_cs_dl = MCS1;
287 }
288 break;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100289 case EGPRS:
290 if (!mcs_is_edge(ms->current_cs_ul)) {
291 ms->current_cs_ul = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100292 ms->bts->initial_mcs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100293 if (!mcs_is_valid(ms->current_cs_ul))
294 ms->current_cs_ul = MCS1;
295 }
296 if (!mcs_is_edge(ms->current_cs_dl)) {
297 ms->current_cs_dl = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100298 ms->bts->initial_mcs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100299 if (!mcs_is_valid(ms->current_cs_dl))
300 ms->current_cs_dl = MCS1;
301 }
302 break;
303 }
304}
305
306static void ms_attach_ul_tbf(struct GprsMs *ms, struct gprs_rlcmac_ul_tbf *tbf)
307{
308 if (ms->ul_tbf == tbf)
309 return;
310
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100311 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 +0100312
313 ms_ref(ms);
314
315 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200316 llist_add_tail(tbf_ms_list(ul_tbf_as_tbf(ms->ul_tbf)), &ms->old_tbfs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100317
318 ms->ul_tbf = tbf;
319
320 if (tbf)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100321 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100322
323 ms_unref(ms);
324}
325
326static void ms_attach_dl_tbf(struct GprsMs *ms, struct gprs_rlcmac_dl_tbf *tbf)
327{
328 if (ms->dl_tbf == tbf)
329 return;
330
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100331 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 +0100332
333 ms_ref(ms);
334
335 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200336 llist_add_tail(tbf_ms_list(dl_tbf_as_tbf(ms->dl_tbf)), &ms->old_tbfs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100337
338 ms->dl_tbf = tbf;
339
340 if (tbf)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100341 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100342
343 ms_unref(ms);
344}
345
346void ms_attach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
347{
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200348 if (tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF)
Pau Espin Pedrolcc30b052022-10-27 15:25:55 +0200349 ms_attach_dl_tbf(ms, tbf_as_dl_tbf(tbf));
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200350 else
Pau Espin Pedrolcc30b052022-10-27 15:25:55 +0200351 ms_attach_ul_tbf(ms, tbf_as_ul_tbf(tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100352}
353
354void ms_detach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
355{
356 if (tbf == (struct gprs_rlcmac_tbf *)(ms->ul_tbf)) {
357 ms->ul_tbf = NULL;
358 } else if (tbf == (struct gprs_rlcmac_tbf *)(ms->dl_tbf)) {
359 ms->dl_tbf = NULL;
360 } else {
361 bool found = false;
362
363 struct llist_item *pos, *tmp;
364 llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
365 struct gprs_rlcmac_tbf *tmp_tbf = (struct gprs_rlcmac_tbf *)pos->entry;
366 if (tmp_tbf == tbf) {
367 llist_del(&pos->list);
368 found = true;
369 break;
370 }
371 }
372
373 /* Protect against recursive calls via set_ms() */
374 if (!found)
375 return;
376 }
377
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100378 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Detaching TBF: %s\n",
379 tbf_name(tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100380
381 if (tbf_ms(tbf) == ms)
382 tbf_set_ms(tbf, NULL);
383
384 if (!ms->dl_tbf && !ms->ul_tbf) {
385 ms_set_reserved_slots(ms, NULL, 0, 0);
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100386 ms->first_common_ts = NULL;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100387
388 if (ms_tlli(ms) != 0)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100389 ms_release_timer_start(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100390 }
391
392 ms_update_status(ms);
393}
394
395void ms_reset(struct GprsMs *ms)
396{
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100397 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Clearing MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100398
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100399 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100400
401 ms->tlli = GSM_RESERVED_TMSI;
402 ms->new_dl_tlli = ms->tlli;
403 ms->new_ul_tlli = ms->tlli;
404 ms->imsi[0] = '\0';
405}
406
Pau Espin Pedrol8abe13c2022-10-21 18:49:48 +0200407/* This function should be called on the MS object of a TBF each time an RLCMAC
408 * block is received for it with TLLI information.
409 * Besides updating the TLLI field on the MS object, it also seeks for other MS
410 * objects in the store and merges them into the current MS object. The MS
411 * duplication happened because we don't learn the TLLI of the created TBF until
412 * a later point. */
413void ms_update_announced_tlli(struct GprsMs *ms, uint32_t tlli)
414{
415 struct GprsMs *old_ms = NULL;
416
417 if (tlli == GSM_RESERVED_TMSI)
418 return;
419
420 /* When the TLLI does not match the ms, check if there is another
421 * MS object that belongs to that TLLI and if yes make sure one of them
422 * gets deleted. */
423 if (!ms_check_tlli(ms, tlli))
424 old_ms = ms_store_get_ms(bts_ms_store(ms->bts), tlli, GSM_RESERVED_TMSI, NULL);
425
426 ms_set_tlli(ms, tlli);
427
428 if (old_ms)
429 ms_merge_and_clear_ms(ms, old_ms);
430 /* old_ms may no longer be available here */
431}
432
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100433/* Merge 'old_ms' object into 'ms' object.
434 * 'old_ms' may be freed during the call to this function, don't use the pointer to it afterwards */
435void ms_merge_and_clear_ms(struct GprsMs *ms, struct GprsMs *old_ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100436{
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100437 char old_ms_name[128];
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100438 OSMO_ASSERT(old_ms != ms);
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100439 ms_ref(old_ms);
440
441 ms_name_buf(old_ms, old_ms_name, sizeof(old_ms_name));
442
443 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Merge MS: %s\n", old_ms_name);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100444
445 if (strlen(ms_imsi(ms)) == 0 && strlen(ms_imsi(old_ms)) != 0)
446 osmo_strlcpy(ms->imsi, ms_imsi(old_ms), sizeof(ms->imsi));
447
448 if (!ms_ms_class(ms) && ms_ms_class(old_ms))
449 ms_set_ms_class(ms, ms_ms_class(old_ms));
450
451 if (!ms_egprs_ms_class(ms) && ms_egprs_ms_class(old_ms))
452 ms_set_egprs_ms_class(ms, ms_egprs_ms_class(old_ms));
453
454 llc_queue_move_and_merge(&ms->llc_queue, &old_ms->llc_queue);
455
Pau Espin Pedrol677bebb2022-10-31 13:56:33 +0100456 if (!ms_dl_tbf(ms) && ms_dl_tbf(old_ms)) {
457 struct gprs_rlcmac_dl_tbf *dl_tbf = ms_dl_tbf(old_ms);
458 LOGPTBFDL(dl_tbf, LOGL_NOTICE,
459 "Merge MS: Move DL TBF: %s => %s\n",
460 old_ms_name, ms_name(ms));
461 /* Move the DL TBF to the new MS */
462 tbf_set_ms(dl_tbf_as_tbf(dl_tbf), ms);
463 }
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100464
465 /* Clean up the old MS object */
466 /* TODO: Use timer? */
467 if (ms_ul_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms), T_MAX))
468 tbf_free((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms));
469 if (ms_dl_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms), T_MAX))
470 tbf_free((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms));
471
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100472 ms_reset(old_ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100473
474 ms_unref(old_ms);
475}
476
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100477/* Apply changes to the TLLI directly, used interally by functions below: */
478static void ms_apply_tlli_change(struct GprsMs *ms, uint32_t tlli)
479{
480 ms->tlli = tlli;
481 ms->new_dl_tlli = GSM_RESERVED_TMSI;
482 ms->new_ul_tlli = GSM_RESERVED_TMSI;
483
484 /* Update TBF FSM names: */
485 if (ms->ul_tbf)
486 tbf_update_state_fsm_name(ul_tbf_as_tbf(ms->ul_tbf));
487 if (ms->dl_tbf)
488 tbf_update_state_fsm_name(dl_tbf_as_tbf(ms->dl_tbf));
489}
490
Pau Espin Pedrol0b5997e2022-10-21 14:10:41 +0200491/* Set/update the MS object TLLI based on knowledge gained from the MS side (Uplink direction) */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100492void ms_set_tlli(struct GprsMs *ms, uint32_t tlli)
493{
494 if (tlli == ms->tlli || tlli == ms->new_ul_tlli)
495 return;
496
497 if (tlli != ms->new_dl_tlli) {
498 LOGP(DRLCMAC, LOGL_INFO,
499 "Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
500 "not yet confirmed\n",
501 ms_tlli(ms), tlli);
502 ms->new_ul_tlli = tlli;
503 return;
504 }
505
506 LOGP(DRLCMAC, LOGL_INFO,
507 "Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
508 "already confirmed partly\n",
509 ms->tlli, tlli);
510
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100511 ms_apply_tlli_change(ms, tlli);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100512}
513
Pau Espin Pedrol0b5997e2022-10-21 14:10:41 +0200514/* Set/update the MS object TLLI based on knowledge gained from the SGSN side (Downlink direction) */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100515bool ms_confirm_tlli(struct GprsMs *ms, uint32_t tlli)
516{
517 if (tlli == ms->tlli || tlli == ms->new_dl_tlli)
518 return false;
519
520 if (tlli != ms->new_ul_tlli) {
521 /* The MS has not sent a message with the new TLLI, which may
522 * happen according to the spec [TODO: add reference]. */
523
524 LOGP(DRLCMAC, LOGL_INFO,
525 "The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
526 "partly confirmed\n", tlli);
527 /* Use the network's idea of TLLI as candidate, this does not
528 * change the result value of tlli() */
529 ms->new_dl_tlli = tlli;
530 return false;
531 }
532
533 LOGP(DRLCMAC, LOGL_INFO,
534 "Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
535
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100536 ms_apply_tlli_change(ms, tlli);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100537
538 return true;
539}
540
541void ms_set_imsi(struct GprsMs *ms, const char *imsi)
542{
543 if (!imsi) {
544 LOGP(DRLCMAC, LOGL_ERROR, "Expected IMSI!\n");
545 return;
546 }
547
548 if (imsi[0] && strlen(imsi) < 3) {
549 LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI '%s'!\n",
550 imsi);
551 return;
552 }
553
554 if (strcmp(imsi, ms->imsi) == 0)
555 return;
556
557 LOGP(DRLCMAC, LOGL_INFO,
558 "Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
559 ms_tlli(ms), ms->imsi, imsi);
560
561 struct GprsMs *old_ms = bts_ms_by_imsi(ms->bts, imsi);
562 /* Check if we are going to store a different MS object with already
563 existing IMSI. This is probably a bug in code calling this function,
564 since it should take care of this explicitly */
565 if (old_ms) {
566 /* We cannot find ms->ms by IMSI since we know that it has a
567 * different IMSI */
568 OSMO_ASSERT(old_ms != ms);
569
570 LOGPMS(ms, DRLCMAC, LOGL_NOTICE,
571 "IMSI '%s' was already assigned to another "
572 "MS object: TLLI = 0x%08x, that IMSI will be removed\n",
573 imsi, ms_tlli(old_ms));
574
575 ms_merge_and_clear_ms(ms, old_ms);
Pau Espin Pedrolb8ff74b2022-10-31 12:58:46 +0100576 /* old_ms may no longer be available here */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100577 }
578
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100579 /* Store the new IMSI: */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100580 osmo_strlcpy(ms->imsi, imsi, sizeof(ms->imsi));
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100581
582 /* Update TBF FSM names: */
583 if (ms->ul_tbf)
584 tbf_update_state_fsm_name(ul_tbf_as_tbf(ms->ul_tbf));
585 if (ms->dl_tbf)
586 tbf_update_state_fsm_name(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100587}
588
Pau Espin Pedrol5deac142021-11-12 18:01:50 +0100589uint16_t ms_paging_group(struct GprsMs *ms)
590{
591 uint16_t pgroup;
592 if (!ms_imsi_is_valid(ms))
593 return 0; /* 000 is the special "all paging" group */
594 if ((pgroup = imsi2paging_group(ms_imsi(ms))) > 999) {
595 LOGPMS(ms, DRLCMAC, LOGL_ERROR, "IMSI to paging group failed!\n");
596 return 0;
597 }
598 return pgroup;
599}
600
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100601void ms_set_ta(struct GprsMs *ms, uint8_t ta_)
602{
603 if (ta_ == ms->ta)
604 return;
605
606 if (gsm48_ta_is_valid(ta_)) {
607 LOGP(DRLCMAC, LOGL_INFO,
608 "Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
609 ms_tlli(ms), ms->ta, ta_);
610 ms->ta = ta_;
611 } else
612 LOGP(DRLCMAC, LOGL_NOTICE,
613 "MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
614 "value %d kept)\n", ms_tlli(ms), ta_, ms->ta);
615}
616
617void ms_set_ms_class(struct GprsMs *ms, uint8_t ms_class_)
618{
619 if (ms_class_ == ms->ms_class)
620 return;
621
622 LOGP(DRLCMAC, LOGL_INFO,
623 "Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
624 ms_tlli(ms), ms->ms_class, ms_class_);
625
626 ms->ms_class = ms_class_;
627}
628
629void ms_set_egprs_ms_class(struct GprsMs *ms, uint8_t ms_class_)
630{
631 if (ms_class_ == ms->egprs_ms_class)
632 return;
633
634 LOGP(DRLCMAC, LOGL_INFO,
635 "Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
636 ms_tlli(ms), ms->egprs_ms_class, ms_class_);
637
638 ms->egprs_ms_class = ms_class_;
639
640 if (!bts_max_mcs_ul(ms->bts) || !bts_max_mcs_dl(ms->bts)) {
641 LOGPMS(ms, DRLCMAC, LOGL_DEBUG,
642 "Avoid enabling EGPRS because use of MCS is disabled: ul=%u dl=%u\n",
643 bts_max_mcs_ul(ms->bts), bts_max_mcs_dl(ms->bts));
644 return;
645 }
646
647 if (mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts))) &&
648 mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts))) &&
649 ms_mode(ms) != EGPRS)
650 {
651 ms_set_mode(ms, EGPRS_GMSK);
652 } else {
653 ms_set_mode(ms, EGPRS);
654 }
655 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Enabled EGPRS, mode %s\n", mode_name(ms_mode(ms)));
656}
657
658void ms_update_error_rate(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, int error_rate)
659{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100660 int64_t now;
661 enum CodingScheme max_cs_dl = ms_max_cs_dl(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100662 OSMO_ASSERT(max_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100663
664 if (error_rate < 0)
665 return;
666
667 now = now_msec();
668
669 /* TODO: Check for TBF direction */
670 /* TODO: Support different CS values for UL and DL */
671
672 ms->nack_rate_dl = error_rate;
673
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100674 if (error_rate > the_pcu->vty.cs_adj_upper_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100675 if (mcs_chan_code(ms->current_cs_dl) > 0) {
676 mcs_dec_kind(&ms->current_cs_dl, ms_mode(ms));
677 LOGP(DRLCMACDL, LOGL_INFO,
678 "MS (IMSI %s): High error rate %d%%, "
679 "reducing CS level to %s\n",
680 ms_imsi(ms), error_rate, mcs_name(ms->current_cs_dl));
681 ms->last_cs_not_low = now;
682 }
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100683 } else if (error_rate < the_pcu->vty.cs_adj_lower_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100684 if (ms->current_cs_dl < max_cs_dl) {
685 if (now - ms->last_cs_not_low > 1000) {
686 mcs_inc_kind(&ms->current_cs_dl, ms_mode(ms));
687
688 LOGP(DRLCMACDL, LOGL_INFO,
689 "MS (IMSI %s): Low error rate %d%%, "
690 "increasing DL CS level to %s\n",
691 ms_imsi(ms), error_rate,
692 mcs_name(ms->current_cs_dl));
693 ms->last_cs_not_low = now;
694 } else {
695 LOGP(DRLCMACDL, LOGL_DEBUG,
696 "MS (IMSI %s): Low error rate %d%%, "
697 "ignored (within blocking period)\n",
698 ms_imsi(ms), error_rate);
699 }
700 }
701 } else {
702 LOGP(DRLCMACDL, LOGL_DEBUG,
703 "MS (IMSI %s): Medium error rate %d%%, ignored\n",
704 ms_imsi(ms), error_rate);
705 ms->last_cs_not_low = now;
706 }
707}
708
709enum CodingScheme ms_max_cs_ul(const struct GprsMs *ms)
710{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100711 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100712 OSMO_ASSERT(ms->bts != NULL);
713
714 if (mcs_is_gprs(ms->current_cs_ul)) {
715 if (!bts_max_cs_ul(ms->bts)) {
716 return CS4;
717 }
718
719 return mcs_get_gprs_by_num(bts_max_cs_ul(ms->bts));
720 }
721
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100722 cs = mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts));
723 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
724 cs = MCS4;
725 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100726}
727
728void ms_set_current_cs_dl(struct GprsMs *ms, enum CodingScheme scheme)
729{
730 ms->current_cs_dl = scheme;
731}
732
733enum CodingScheme ms_max_cs_dl(const struct GprsMs *ms)
734{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100735 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100736 OSMO_ASSERT(ms->bts != NULL);
737
738 if (mcs_is_gprs(ms->current_cs_dl)) {
739 if (!bts_max_cs_dl(ms->bts)) {
740 return CS4;
741 }
742
743 return mcs_get_gprs_by_num(bts_max_cs_dl(ms->bts));
744 }
745
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100746 cs = mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts));
747 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
748 cs = MCS4;
749 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100750}
751
752void ms_update_cs_ul(struct GprsMs *ms, const struct pcu_l1_meas *meas)
753{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100754 enum CodingScheme max_cs_ul = ms_max_cs_ul(ms);
755
756 int old_link_qual;
757 int low;
758 int high;
759 enum CodingScheme new_cs_ul = ms->current_cs_ul;
760 uint8_t current_cs = mcs_chan_code(ms->current_cs_ul);
761
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100762 if (!max_cs_ul) {
763 LOGP(DRLCMACMEAS, LOGL_ERROR,
764 "max_cs_ul cannot be derived (current UL CS: %s)\n",
765 mcs_name(ms->current_cs_ul));
766 return;
767 }
768
769 if (!ms->current_cs_ul) {
770 LOGP(DRLCMACMEAS, LOGL_ERROR,
771 "Unable to update UL (M)CS because it's not set: %s\n",
772 mcs_name(ms->current_cs_ul));
773 return;
774 }
775
776 if (!meas->have_link_qual) {
777 LOGP(DRLCMACMEAS, LOGL_ERROR,
778 "Unable to update UL (M)CS %s because we don't have link quality measurements.\n",
779 mcs_name(ms->current_cs_ul));
780 return;
781 }
782
783 if (mcs_is_gprs(ms->current_cs_ul)) {
784 if (current_cs >= MAX_GPRS_CS)
785 current_cs = MAX_GPRS_CS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100786 low = the_pcu->vty.cs_lqual_ranges[current_cs].low;
787 high = the_pcu->vty.cs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100788 } else if (mcs_is_edge(ms->current_cs_ul)) {
789 if (current_cs >= MAX_EDGE_MCS)
790 current_cs = MAX_EDGE_MCS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100791 low = the_pcu->vty.mcs_lqual_ranges[current_cs].low;
792 high = the_pcu->vty.mcs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100793 } else {
794 LOGP(DRLCMACMEAS, LOGL_ERROR,
795 "Unable to update UL (M)CS because it's neither GPRS nor EDGE: %s\n",
796 mcs_name(ms->current_cs_ul));
797 return;
798 }
799
800 /* To avoid rapid changes of the coding scheme, we also take
801 * the old link quality value into account (if present). */
802 if (ms->l1_meas.have_link_qual)
803 old_link_qual = ms->l1_meas.link_qual;
804 else
805 old_link_qual = meas->link_qual;
806
807 if (meas->link_qual < low && old_link_qual < low)
808 mcs_dec_kind(&new_cs_ul, ms_mode(ms));
809 else if (meas->link_qual > high && old_link_qual > high &&
810 ms->current_cs_ul < max_cs_ul)
811 mcs_inc_kind(&new_cs_ul, ms_mode(ms));
812
813 if (ms->current_cs_ul != new_cs_ul) {
814 LOGPMS(ms, DRLCMACMEAS, LOGL_INFO,
815 "Link quality %ddB (old %ddB) left window [%d, %d], "
816 "modifying uplink CS level: %s -> %s\n",
817 meas->link_qual, old_link_qual,
818 low, high,
819 mcs_name(ms->current_cs_ul), mcs_name(new_cs_ul));
820
821 ms->current_cs_ul = new_cs_ul;
822 }
823}
824
825void ms_update_l1_meas(struct GprsMs *ms, const struct pcu_l1_meas *meas)
826{
827 unsigned i;
828
829 ms_update_cs_ul(ms, meas);
830
831 if (meas->have_rssi)
832 pcu_l1_meas_set_rssi(&ms->l1_meas, meas->rssi);
833 if (meas->have_bto)
834 pcu_l1_meas_set_bto(&ms->l1_meas, meas->bto);
835 if (meas->have_ber)
836 pcu_l1_meas_set_ber(&ms->l1_meas, meas->ber);
837 if (meas->have_link_qual)
838 pcu_l1_meas_set_link_qual(&ms->l1_meas, meas->link_qual);
839
840 if (meas->have_ms_rx_qual)
841 pcu_l1_meas_set_ms_rx_qual(&ms->l1_meas, meas->ms_rx_qual);
842 if (meas->have_ms_c_value)
843 pcu_l1_meas_set_ms_c_value(&ms->l1_meas, meas->ms_c_value);
844 if (meas->have_ms_sign_var)
845 pcu_l1_meas_set_ms_sign_var(&ms->l1_meas, meas->ms_sign_var);
846
847 if (meas->have_ms_i_level) {
848 for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
849 if (meas->ts[i].have_ms_i_level)
850 pcu_l1_meas_set_ms_i_level(&ms->l1_meas, i, meas->ts[i].ms_i_level);
851 else
852 ms->l1_meas.ts[i].have_ms_i_level = 0;
853 }
854 }
855}
856
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100857/* req_mcs_kind acts as a set filter, where EGPRS means any and GPRS is the most restrictive */
858enum CodingScheme ms_current_cs_dl(const struct GprsMs *ms, enum mcs_kind req_mcs_kind)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100859{
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100860 enum CodingScheme orig_cs = ms->current_cs_dl;
861 struct gprs_rlcmac_bts *bts = ms->bts;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100862 size_t unencoded_octets;
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100863 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100864
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100865 /* It could be that a TBF requests a GPRS CS despite the MS currently
866 being upgraded to EGPRS (hence reporting MCS). That could happen
867 because the TBF was created early in the process where we didn't have
868 yet enough information about the MS, and only AFTER it was created we
869 upgraded the MS to be EGPRS capable.
870 As a result, when the MS is queried for the target CS here, we could be
871 returning an MCS despite the current TBF being established as GPRS,
872 but we rather stick to the TBF type we assigned to the MS rather than
873 magically sending EGPRS data blocks to a GPRS TBF.
874 It could also be that the caller requests specific MCS kind
875 explicitly too due to scheduling restrictions (GPRS+EGPRS multiplexing). */
876 if (req_mcs_kind == EGPRS_GMSK && mcs_is_edge(orig_cs) && orig_cs > MCS4) {
877 cs = bts_cs_dl_is_supported(bts, MCS4) ? MCS4 :
878 bts_cs_dl_is_supported(bts, MCS3) ? MCS3 :
879 bts_cs_dl_is_supported(bts, MCS2) ? MCS2 :
880 MCS1;
881 } else if (req_mcs_kind == GPRS && mcs_is_edge(orig_cs)) { /* GPRS */
882 int i;
883 cs = orig_cs > MCS4 ? MCS4 : orig_cs;
884 cs -= (MCS1 - CS1); /* MCSx -> CSx */
885 /* Find suitable CS starting from equivalent MCS which is supported by BTS: */
886 for (i = mcs_chan_code(cs); !bts_cs_dl_is_supported(bts, CS1 + i); i--);
887 OSMO_ASSERT(i >= 0 && i <= 3); /* CS1 is always supported */
888 cs = CS1 + i;
889 } else {
890 cs = orig_cs;
891 }
892
893 if (orig_cs != cs)
894 LOGPMS(ms, DRLCMACDL, LOGL_INFO, "MS (mode=%s) suggests transmitting "
895 "DL %s, downgrade to %s in order to match TBF & scheduler requirements\n",
896 mode_name(ms_mode(ms)), mcs_name(orig_cs), mcs_name(cs));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100897
898 unencoded_octets = llc_queue_octets(&ms->llc_queue);
899
900 /* If the DL TBF is active, add number of unencoded chunk octets */
901 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200902 unencoded_octets += llc_chunk_size(tbf_llc(dl_tbf_as_tbf(ms->dl_tbf)));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100903
904 /* There are many unencoded octets, don't reduce */
Pau Espin Pedrolad79b852021-01-14 13:20:55 +0100905 if (unencoded_octets >= the_pcu->vty.cs_downgrade_threshold)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100906 return cs;
907
908 /* RF conditions are good, don't reduce */
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100909 if (ms->nack_rate_dl < the_pcu->vty.cs_adj_lower_limit)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100910 return cs;
911
912 /* The throughput would probably be better if the CS level was reduced */
913 mcs_dec_kind(&cs, ms_mode(ms));
914
915 /* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
916 if (cs == CS2)
917 mcs_dec_kind(&cs, ms_mode(ms));
918
919 return cs;
920}
921
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100922struct gprs_rlcmac_pdch *ms_first_common_ts(const struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100923{
Pau Espin Pedrol345d9ad2022-12-12 19:22:44 +0100924 return ms->first_common_ts;
925}
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100926
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100927void ms_set_first_common_ts(struct GprsMs *ms, struct gprs_rlcmac_pdch *pdch)
Pau Espin Pedrol345d9ad2022-12-12 19:22:44 +0100928{
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100929 OSMO_ASSERT(pdch);
930 ms->first_common_ts = pdch;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100931}
932
933uint8_t ms_dl_slots(const struct GprsMs *ms)
934{
935 uint8_t slots = 0;
936
937 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200938 slots |= tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100939
940 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200941 slots |= tbf_dl_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100942
943 return slots;
944}
945
946uint8_t ms_ul_slots(const struct GprsMs *ms)
947{
948 uint8_t slots = 0;
949
950 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200951 slots |= tbf_ul_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100952
953 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200954 slots |= tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100955
956 return slots;
957}
958
959uint8_t ms_current_pacch_slots(const struct GprsMs *ms)
960{
961 uint8_t slots = 0;
962
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200963 bool is_dl_active = ms->dl_tbf && tbf_is_tfi_assigned(dl_tbf_as_tbf(ms->dl_tbf));
964 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 +0100965
966 if (!is_dl_active && !is_ul_active)
967 return 0;
968
969 /* see TS 44.060, 8.1.1.2.2 */
970 if (is_dl_active && !is_ul_active)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200971 slots = tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100972 else if (!is_dl_active && is_ul_active)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200973 slots = tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100974 else
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200975 slots = tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf)) &
976 tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100977
978 /* Assume a multislot class 1 device */
979 /* TODO: For class 2 devices, this could be removed */
980 slots = pcu_lsb(slots);
981
982 return slots;
983}
984
985void ms_set_reserved_slots(struct GprsMs *ms, struct gprs_rlcmac_trx *trx,
986 uint8_t ul_slots, uint8_t dl_slots)
987{
988 if (ms->current_trx) {
989 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
990 ms->reserved_dl_slots);
991 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
992 ms->reserved_ul_slots);
993 ms->reserved_dl_slots = 0;
994 ms->reserved_ul_slots = 0;
995 }
996 ms->current_trx = trx;
997 if (trx) {
998 ms->reserved_dl_slots = dl_slots;
999 ms->reserved_ul_slots = ul_slots;
1000 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
1001 ms->reserved_dl_slots);
1002 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
1003 ms->reserved_ul_slots);
1004 }
1005}
1006
1007struct gprs_rlcmac_tbf *ms_tbf(const struct GprsMs *ms, enum gprs_rlcmac_tbf_direction dir)
1008{
1009 switch (dir) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +02001010 case GPRS_RLCMAC_DL_TBF: return dl_tbf_as_tbf(ms->dl_tbf);
1011 case GPRS_RLCMAC_UL_TBF: return ul_tbf_as_tbf(ms->ul_tbf);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +01001012 }
1013
1014 return NULL;
1015}
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001016
Pau Espin Pedrol3547d642022-10-21 15:00:08 +02001017const char *ms_name(const struct GprsMs *ms)
1018{
1019 static char _ms_name_buf[128];
1020 return ms_name_buf(ms, _ms_name_buf, sizeof(_ms_name_buf));
1021}
1022
1023char *ms_name_buf(const struct GprsMs *ms, char *buf, unsigned int buf_size)
1024{
Pau Espin Pedrol94f82582022-11-03 14:16:17 +01001025 struct osmo_strbuf sb = { .buf = buf, .len = buf_size };
1026 uint32_t tlli = ms_tlli(ms);
1027
1028 OSMO_STRBUF_PRINTF(sb, "MS(");
1029 if (ms_imsi_is_valid(ms))
1030 OSMO_STRBUF_PRINTF(sb, "IMSI-%s:", ms_imsi(ms));
1031 if (tlli != GSM_RESERVED_TMSI)
1032 OSMO_STRBUF_PRINTF(sb, "TLLI-0x%08x:", tlli);
1033 OSMO_STRBUF_PRINTF(sb, "TA-%" PRIu8 ":MSCLS-%" PRIu8 "-%" PRIu8,
1034 ms_ta(ms), ms_ms_class(ms), ms_egprs_ms_class(ms));
1035 if (ms->ul_tbf)
1036 OSMO_STRBUF_PRINTF(sb, ":UL");
1037 if (ms->dl_tbf)
1038 OSMO_STRBUF_PRINTF(sb, ":DL");
1039
1040 OSMO_STRBUF_PRINTF(sb, ")");
Pau Espin Pedrol3547d642022-10-21 15:00:08 +02001041 return buf;
1042}
1043
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001044int ms_nacc_start(struct GprsMs *ms, Packet_Cell_Change_Notification_t *notif)
1045{
1046 if (!ms->nacc)
1047 ms->nacc = nacc_fsm_alloc(ms);
1048 if (!ms->nacc)
1049 return -EINVAL;
1050 return osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_RX_CELL_CHG_NOTIFICATION, notif);
1051}
1052
1053bool ms_nacc_rts(const struct GprsMs *ms)
1054{
1055 if (!ms->nacc)
1056 return false;
1057 if (ms->nacc->fi->state == NACC_ST_TX_NEIGHBOUR_DATA ||
1058 ms->nacc->fi->state == NACC_ST_TX_CELL_CHG_CONTINUE)
1059 return true;
1060 return false;
1061}
1062
Pau Espin Pedrol5ba3ef92022-12-12 18:02:25 +01001063struct msgb *ms_nacc_create_rlcmac_msg(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf,
1064 const struct gprs_rlcmac_pdch *pdch, uint32_t fn)
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001065{
1066 int rc;
1067 struct nacc_ev_create_rlcmac_msg_ctx data_ctx;
1068
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001069 data_ctx = (struct nacc_ev_create_rlcmac_msg_ctx) {
1070 .tbf = tbf,
Pau Espin Pedrol5ba3ef92022-12-12 18:02:25 +01001071 .pdch = pdch,
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001072 .fn = fn,
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001073 .msg = NULL,
1074 };
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001075
1076 rc = osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_CREATE_RLCMAC_MSG, &data_ctx);
1077 if (rc != 0 || !data_ctx.msg)
1078 return NULL;
1079 return data_ctx.msg;
1080}
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001081
1082static void ms_start_llc_timer(struct GprsMs *ms)
1083{
1084 if (the_pcu->vty.llc_idle_ack_csec > 0) {
1085 struct timespec tv;
1086 csecs_to_timespec(the_pcu->vty.llc_idle_ack_csec, &tv);
1087 osmo_timer_schedule(&ms->llc_timer, tv.tv_sec, tv.tv_nsec / 1000);
1088 }
1089}
1090
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001091/* Can we get to send a DL TBF ass to the MS? */
1092static bool ms_is_reachable_for_dl_ass(const struct GprsMs *ms)
1093{
1094 struct gprs_rlcmac_ul_tbf *ul_tbf = ms_ul_tbf(ms);
1095
1096 /* This function assumes it is called when no DL TBF is present */
1097 OSMO_ASSERT(!ms_dl_tbf(ms));
1098
1099 /* 3GPP TS 44.060 sec 7.1.3.1 Initiation of the Packet resource request procedure:
1100 * "Furthermore, the mobile station shall not respond to PACKET DOWNLINK ASSIGNMENT
1101 * or MULTIPLE TBF DOWNLINK ASSIGNMENT messages before contention resolution is
1102 * completed on the mobile station side." */
1103 /* The possible uplink TBF is used to trigger downlink assignment:
1104 * - If there is no uplink TBF the MS is potentially in packet idle mode
1105 * and hence assignment will be done over CCCH (PCH)
1106 * - If there's an uplink TBF but it is finished (waiting for last PKT
1107 * CTRL ACK after sending last Pkt UL ACK/NACK with FINAL_ACK=1, then we
1108 * have no ways to contact the MS right now. Assignment will be delayed
1109 * until PKT CTRL ACK is received and the TBF is released at the MS side
1110 * (then assignment goes through PCH).
1111 */
1112 if (!ul_tbf)
1113 return true;
1114 if (ul_tbf_contention_resolution_done(ul_tbf) &&
1115 !tbf_ul_ack_waiting_cnf_final_ack(ul_tbf))
1116 return true;
1117
1118 return false;
1119
1120}
1121
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001122/* Alloc a UL TBF to be assigned over PACCH. Called when an MS requests to
1123 * create a new UL TBF during the end of life of a previous UL TBF (or an SBA).
1124 * In summary, this TBF is allocated as a consequence of receiving a "Pkt
1125 * Resource Req" or "Pkt Ctrl Ack" from the MS.
1126 * See TS 44.060 9.3.2.4.2 "Non-extended uplink TBF mode".
1127 */
1128struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_pacch(struct GprsMs *ms, int8_t use_trx)
1129{
1130 const bool single_slot = false;
1131 struct gprs_rlcmac_ul_tbf *ul_tbf;
1132
Pau Espin Pedrolbda7bb72022-10-31 14:33:09 +01001133 ul_tbf = ul_tbf_alloc(ms->bts, ms, use_trx, single_slot);
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001134 if (!ul_tbf) {
1135 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1136 /* Caller will most probably send a Imm Ass Reject after return */
1137 return NULL;
1138 }
1139 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_PACCH, NULL);
1140 /* Contention resolution is considered to be done since TLLI is known in MS */
1141 return ul_tbf;
1142}
1143
1144/* Alloc a UL TBF to be assigned over AGCH. Used by request of a "One phase
1145 * packet access", where MS requested only 1 PDCH TS (TS 44.018 Table 9.1.8.1). */
1146struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_agch(struct GprsMs *ms)
1147{
1148 const int8_t trx_no = -1;
1149 const bool single_slot = true;
1150 struct gprs_rlcmac_ul_tbf *ul_tbf;
1151
Pau Espin Pedrolbda7bb72022-10-31 14:33:09 +01001152 ul_tbf = ul_tbf_alloc(ms->bts, ms, trx_no, single_slot);
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001153 if (!ul_tbf) {
1154 LOGP(DTBF, LOGL_NOTICE, "No PDCH resource for Uplink TBF\n");
1155 /* Caller will most probably send a Imm Ass Reject after return */
1156 return NULL;
1157 }
1158 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_CCCH, NULL);
1159 return ul_tbf;
1160}
1161
Pau Espin Pedrola621d592022-12-13 17:35:04 +01001162/* Create a temporary dummy TBF to Tx a ImmAssReject if allocating a new one during
1163 * packet resource Request failed. This is similar as ul_tbf_alloc() but without
1164 * calling tbf->setup() (in charge of TFI/USF allocation), and reusing resources
1165 * from Packet Resource Request we received. See TS 44.060 sec 7.1.3.2.1 */
1166struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_rejected_pacch(struct GprsMs *ms, struct gprs_rlcmac_pdch *pdch)
1167{
1168 struct gprs_rlcmac_ul_tbf *ul_tbf;
1169 ul_tbf = ul_tbf_alloc_rejected(ms->bts, ms, pdch);
1170 if (!ul_tbf)
1171 return NULL;
1172 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_PACCH, NULL);
1173 osmo_fsm_inst_dispatch(tbf_ul_ass_fi(ul_tbf_as_tbf(ul_tbf)), TBF_UL_ASS_EV_SCHED_ASS_REJ, NULL);
1174
1175 return ul_tbf;
1176}
1177
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001178/* A new DL-TBF is allocated and assigned through PACCH using "tbf".
1179 * "tbf" may be either a UL-TBF or a DL-TBF.
1180 * Note: This should be called only when MS is reachable, see ms_is_reachable_for_dl_ass().
1181 */
1182int ms_new_dl_tbf_assigned_on_pacch(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001183{
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001184 OSMO_ASSERT(tbf);
1185 const int8_t trx_no = tbf_get_trx(tbf)->trx_no;
1186 const bool single_slot = false;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001187 struct gprs_rlcmac_dl_tbf *dl_tbf;
1188
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001189 dl_tbf = dl_tbf_alloc(ms->bts, ms, trx_no, single_slot);
1190 if (!dl_tbf) {
1191 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1192 return -EBUSY;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001193 }
1194
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001195 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START (PACCH)\n");
1196 dl_tbf_trigger_ass_on_pacch(dl_tbf, tbf);
1197 return 0;
1198}
1199
1200/* A new DL-TBF is allocated and assigned through PCH.
1201 * Note: This should be called only when MS is reachable, see ms_is_reachable_for_dl_ass().
1202 */
1203int ms_new_dl_tbf_assigned_on_pch(struct GprsMs *ms)
1204{
1205 const int8_t trx_no = -1;
1206 const bool single_slot = true;
1207 struct gprs_rlcmac_dl_tbf *dl_tbf;
1208
1209 dl_tbf = dl_tbf_alloc(ms->bts, ms, trx_no, single_slot);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001210 if (!dl_tbf) {
1211 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1212 return -EBUSY;
1213 }
1214
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001215 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START (PCH)\n");
1216 dl_tbf_trigger_ass_on_pch(dl_tbf);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001217 return 0;
1218}
1219
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001220int ms_append_llc_dl_data(struct GprsMs *ms, uint16_t pdu_delay_csec, const uint8_t *data, uint16_t len)
1221{
1222 struct timespec expire_time;
1223 struct gprs_rlcmac_dl_tbf *dl_tbf;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001224 int rc = 0;
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001225
1226 LOGPMS(ms, DTBFDL, LOGL_DEBUG, "appending %u bytes to DL LLC queue\n", len);
1227
1228 struct msgb *llc_msg = msgb_alloc(len, "llc_pdu_queue");
1229 if (!llc_msg)
1230 return -ENOMEM;
1231
1232 llc_queue_calc_pdu_lifetime(ms->bts, pdu_delay_csec, &expire_time);
1233 memcpy(msgb_put(llc_msg, len), data, len);
1234 llc_queue_enqueue(ms_llc_queue(ms), llc_msg, &expire_time);
1235 ms_start_llc_timer(ms);
1236
1237 dl_tbf = ms_dl_tbf(ms);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001238 if (dl_tbf) {
1239 if (tbf_state(dl_tbf_as_tbf_const(dl_tbf)) == TBF_ST_WAIT_RELEASE) {
1240 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "in WAIT RELEASE state (T3193), so reuse TBF\n");
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001241 rc = ms_new_dl_tbf_assigned_on_pacch(ms, dl_tbf_as_tbf(dl_tbf));
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001242 }
1243 } else {
1244 /* Check if we can create a DL TBF to start sending the enqueued
1245 * data. Otherwise it will be triggered later when it is reachable
1246 * again. */
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001247 if (ms_is_reachable_for_dl_ass(ms)) {
1248 if (ms_ul_tbf(ms))
1249 rc = ms_new_dl_tbf_assigned_on_pacch(ms, ul_tbf_as_tbf(ms_ul_tbf(ms)));
1250 else
1251 rc = ms_new_dl_tbf_assigned_on_pch(ms);
1252 }
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001253 }
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001254 return rc;
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001255}