blob: 073dd4a75fd20047fcc216305b86ee76d18031d9 [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;
Pau Espin Pedrol345d9ad2022-12-12 19:22:44 +0100123 ms->first_common_ts = TBF_TS_UNSET;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100124 INIT_LLIST_HEAD(&ms->list);
125 INIT_LLIST_HEAD(&ms->old_tbfs);
126
127 int codel_interval = LLC_CODEL_USE_DEFAULT;
128
129 LOGP(DRLCMAC, LOGL_INFO, "Creating MS object, TLLI = 0x%08x\n", tlli);
130
131 ms->imsi[0] = '\0';
Pau Espin Pedrolc7802d92022-05-09 16:07:52 +0200132 osmo_timer_setup(&ms->timer, ms_release_timer_cb, NULL);
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200133 llc_queue_init(&ms->llc_queue, ms);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200134 memset(&ms->llc_timer, 0, sizeof(ms->llc_timer));
135 osmo_timer_setup(&ms->llc_timer, ms_llc_timer_cb, ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100136
137 ms_set_mode(ms, GPRS);
138
Pau Espin Pedrolb4987462022-04-01 17:21:08 +0200139 codel_interval = the_pcu->vty.llc_codel_interval_msec;
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200140 if (codel_interval == LLC_CODEL_USE_DEFAULT)
141 codel_interval = GPRS_CODEL_SLOW_INTERVAL_MS;
142 llc_queue_set_codel_interval(&ms->llc_queue, codel_interval);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100143
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100144 ms->last_cs_not_low = now_msec();
145 ms->app_info_pending = false;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100146
147 ms->ctrs = rate_ctr_group_alloc(ms, &ms_ctrg_desc, next_ms_ctr_group_id++);
148 if (!ms->ctrs)
149 goto free_ret;
150
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100151 return ms;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100152free_ret:
153 talloc_free(ms);
154 return NULL;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100155}
156
157static int ms_talloc_destructor(struct GprsMs *ms)
158{
159 struct llist_item *pos, *tmp;
160
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100161 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Destroying MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100162
163 ms_set_reserved_slots(ms, NULL, 0, 0);
164
Vadim Yanitskiye33c2362022-07-22 03:44:44 +0700165 osmo_timer_del(&ms->timer);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100166
167 if (ms->ul_tbf) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200168 tbf_set_ms(ul_tbf_as_tbf(ms->ul_tbf), NULL);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100169 ms->ul_tbf = NULL;
170 }
171
172 if (ms->dl_tbf) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200173 tbf_set_ms(dl_tbf_as_tbf(ms->dl_tbf), NULL);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100174 ms->dl_tbf = NULL;
175 }
176
177 llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
178 struct gprs_rlcmac_tbf *tbf = (struct gprs_rlcmac_tbf *)pos->entry;
179 tbf_set_ms(tbf, NULL);
180 }
181
182 llc_queue_clear(&ms->llc_queue, ms->bts);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200183 osmo_timer_del(&ms->llc_timer);
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100184
185 if (ms->ctrs)
186 rate_ctr_group_free(ms->ctrs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100187 return 0;
188}
189
190
191void ms_set_callback(struct GprsMs *ms, struct gpr_ms_callback *cb)
192{
193 if (cb)
194 ms->cb = *cb;
195 else
196 ms->cb = gprs_default_cb;
197}
198
199static void ms_update_status(struct GprsMs *ms)
200{
201 if (ms->ref > 0)
202 return;
203
204 if (ms_is_idle(ms) && !ms->is_idle) {
205 ms->is_idle = true;
206 ms->cb.ms_idle(ms);
207 /* this can be deleted by now, do not access it */
208 return;
209 }
210
211 if (!ms_is_idle(ms) && ms->is_idle) {
212 ms->is_idle = false;
213 ms->cb.ms_active(ms);
214 }
215}
216
217struct GprsMs *ms_ref(struct GprsMs *ms)
218{
219 ms->ref += 1;
220 return ms;
221}
222
223void ms_unref(struct GprsMs *ms)
224{
225 OSMO_ASSERT(ms->ref >= 0);
226 ms->ref -= 1;
227 if (ms->ref == 0)
228 ms_update_status(ms);
229}
230
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100231static void ms_release_timer_start(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100232{
233 if (ms->delay == 0)
234 return;
235
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100236 LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Schedule MS release in %u secs\n", ms->delay);
237
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100238 if (!ms->timer.data)
239 ms->timer.data = ms_ref(ms);
240
241 osmo_timer_schedule(&ms->timer, ms->delay, 0);
242}
243
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100244static void ms_release_timer_stop(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100245{
246 if (!ms->timer.data)
247 return;
248
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100249 LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Cancel scheduled MS release\n");
250
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100251 osmo_timer_del(&ms->timer);
252 ms->timer.data = NULL;
253 ms_unref(ms);
254}
255
256void ms_set_mode(struct GprsMs *ms, enum mcs_kind mode)
257{
258 ms->mode = mode;
259
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100260 switch (ms->mode) {
261 case GPRS:
262 if (!mcs_is_gprs(ms->current_cs_ul)) {
263 ms->current_cs_ul = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100264 ms->bts->initial_cs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100265 if (!mcs_is_valid(ms->current_cs_ul))
266 ms->current_cs_ul = CS1;
267 }
268 if (!mcs_is_gprs(ms->current_cs_dl)) {
269 ms->current_cs_dl = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100270 ms->bts->initial_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100271 if (!mcs_is_valid(ms->current_cs_dl))
272 ms->current_cs_dl = CS1;
273 }
274 break;
275
276 case EGPRS_GMSK:
Pau Espin Pedrol7bb8cd62021-01-25 15:08:35 +0100277 if (!mcs_is_edge_gmsk(ms->current_cs_ul)) {
278 ms->current_cs_ul = mcs_get_egprs_by_num(
279 ms->bts->initial_mcs_ul);
280 if (!mcs_is_valid(ms->current_cs_ul))
281 ms->current_cs_ul = MCS1;
282 }
283 if (!mcs_is_edge_gmsk(ms->current_cs_dl)) {
284 ms->current_cs_dl = mcs_get_egprs_by_num(
285 ms->bts->initial_mcs_dl);
286 if (!mcs_is_valid(ms->current_cs_dl))
287 ms->current_cs_dl = MCS1;
288 }
289 break;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100290 case EGPRS:
291 if (!mcs_is_edge(ms->current_cs_ul)) {
292 ms->current_cs_ul = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100293 ms->bts->initial_mcs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100294 if (!mcs_is_valid(ms->current_cs_ul))
295 ms->current_cs_ul = MCS1;
296 }
297 if (!mcs_is_edge(ms->current_cs_dl)) {
298 ms->current_cs_dl = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100299 ms->bts->initial_mcs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100300 if (!mcs_is_valid(ms->current_cs_dl))
301 ms->current_cs_dl = MCS1;
302 }
303 break;
304 }
305}
306
307static void ms_attach_ul_tbf(struct GprsMs *ms, struct gprs_rlcmac_ul_tbf *tbf)
308{
309 if (ms->ul_tbf == tbf)
310 return;
311
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100312 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 +0100313
314 ms_ref(ms);
315
316 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200317 llist_add_tail(tbf_ms_list(ul_tbf_as_tbf(ms->ul_tbf)), &ms->old_tbfs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100318
319 ms->ul_tbf = tbf;
320
321 if (tbf)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100322 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100323
324 ms_unref(ms);
325}
326
327static void ms_attach_dl_tbf(struct GprsMs *ms, struct gprs_rlcmac_dl_tbf *tbf)
328{
329 if (ms->dl_tbf == tbf)
330 return;
331
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100332 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 +0100333
334 ms_ref(ms);
335
336 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200337 llist_add_tail(tbf_ms_list(dl_tbf_as_tbf(ms->dl_tbf)), &ms->old_tbfs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100338
339 ms->dl_tbf = tbf;
340
341 if (tbf)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100342 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100343
344 ms_unref(ms);
345}
346
347void ms_attach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
348{
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200349 if (tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF)
Pau Espin Pedrolcc30b052022-10-27 15:25:55 +0200350 ms_attach_dl_tbf(ms, tbf_as_dl_tbf(tbf));
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200351 else
Pau Espin Pedrolcc30b052022-10-27 15:25:55 +0200352 ms_attach_ul_tbf(ms, tbf_as_ul_tbf(tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100353}
354
355void ms_detach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
356{
357 if (tbf == (struct gprs_rlcmac_tbf *)(ms->ul_tbf)) {
358 ms->ul_tbf = NULL;
359 } else if (tbf == (struct gprs_rlcmac_tbf *)(ms->dl_tbf)) {
360 ms->dl_tbf = NULL;
361 } else {
362 bool found = false;
363
364 struct llist_item *pos, *tmp;
365 llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
366 struct gprs_rlcmac_tbf *tmp_tbf = (struct gprs_rlcmac_tbf *)pos->entry;
367 if (tmp_tbf == tbf) {
368 llist_del(&pos->list);
369 found = true;
370 break;
371 }
372 }
373
374 /* Protect against recursive calls via set_ms() */
375 if (!found)
376 return;
377 }
378
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100379 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Detaching TBF: %s\n",
380 tbf_name(tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100381
382 if (tbf_ms(tbf) == ms)
383 tbf_set_ms(tbf, NULL);
384
385 if (!ms->dl_tbf && !ms->ul_tbf) {
386 ms_set_reserved_slots(ms, NULL, 0, 0);
Pau Espin Pedrol345d9ad2022-12-12 19:22:44 +0100387 ms->first_common_ts = TBF_TS_UNSET;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100388
389 if (ms_tlli(ms) != 0)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100390 ms_release_timer_start(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100391 }
392
393 ms_update_status(ms);
394}
395
396void ms_reset(struct GprsMs *ms)
397{
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100398 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Clearing MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100399
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100400 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100401
402 ms->tlli = GSM_RESERVED_TMSI;
403 ms->new_dl_tlli = ms->tlli;
404 ms->new_ul_tlli = ms->tlli;
405 ms->imsi[0] = '\0';
406}
407
Pau Espin Pedrol8abe13c2022-10-21 18:49:48 +0200408/* This function should be called on the MS object of a TBF each time an RLCMAC
409 * block is received for it with TLLI information.
410 * Besides updating the TLLI field on the MS object, it also seeks for other MS
411 * objects in the store and merges them into the current MS object. The MS
412 * duplication happened because we don't learn the TLLI of the created TBF until
413 * a later point. */
414void ms_update_announced_tlli(struct GprsMs *ms, uint32_t tlli)
415{
416 struct GprsMs *old_ms = NULL;
417
418 if (tlli == GSM_RESERVED_TMSI)
419 return;
420
421 /* When the TLLI does not match the ms, check if there is another
422 * MS object that belongs to that TLLI and if yes make sure one of them
423 * gets deleted. */
424 if (!ms_check_tlli(ms, tlli))
425 old_ms = ms_store_get_ms(bts_ms_store(ms->bts), tlli, GSM_RESERVED_TMSI, NULL);
426
427 ms_set_tlli(ms, tlli);
428
429 if (old_ms)
430 ms_merge_and_clear_ms(ms, old_ms);
431 /* old_ms may no longer be available here */
432}
433
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100434/* Merge 'old_ms' object into 'ms' object.
435 * 'old_ms' may be freed during the call to this function, don't use the pointer to it afterwards */
436void ms_merge_and_clear_ms(struct GprsMs *ms, struct GprsMs *old_ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100437{
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100438 char old_ms_name[128];
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100439 OSMO_ASSERT(old_ms != ms);
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100440 ms_ref(old_ms);
441
442 ms_name_buf(old_ms, old_ms_name, sizeof(old_ms_name));
443
444 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Merge MS: %s\n", old_ms_name);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100445
446 if (strlen(ms_imsi(ms)) == 0 && strlen(ms_imsi(old_ms)) != 0)
447 osmo_strlcpy(ms->imsi, ms_imsi(old_ms), sizeof(ms->imsi));
448
449 if (!ms_ms_class(ms) && ms_ms_class(old_ms))
450 ms_set_ms_class(ms, ms_ms_class(old_ms));
451
452 if (!ms_egprs_ms_class(ms) && ms_egprs_ms_class(old_ms))
453 ms_set_egprs_ms_class(ms, ms_egprs_ms_class(old_ms));
454
455 llc_queue_move_and_merge(&ms->llc_queue, &old_ms->llc_queue);
456
Pau Espin Pedrol677bebb2022-10-31 13:56:33 +0100457 if (!ms_dl_tbf(ms) && ms_dl_tbf(old_ms)) {
458 struct gprs_rlcmac_dl_tbf *dl_tbf = ms_dl_tbf(old_ms);
459 LOGPTBFDL(dl_tbf, LOGL_NOTICE,
460 "Merge MS: Move DL TBF: %s => %s\n",
461 old_ms_name, ms_name(ms));
462 /* Move the DL TBF to the new MS */
463 tbf_set_ms(dl_tbf_as_tbf(dl_tbf), ms);
464 }
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100465
466 /* Clean up the old MS object */
467 /* TODO: Use timer? */
468 if (ms_ul_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms), T_MAX))
469 tbf_free((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms));
470 if (ms_dl_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms), T_MAX))
471 tbf_free((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms));
472
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100473 ms_reset(old_ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100474
475 ms_unref(old_ms);
476}
477
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100478/* Apply changes to the TLLI directly, used interally by functions below: */
479static void ms_apply_tlli_change(struct GprsMs *ms, uint32_t tlli)
480{
481 ms->tlli = tlli;
482 ms->new_dl_tlli = GSM_RESERVED_TMSI;
483 ms->new_ul_tlli = GSM_RESERVED_TMSI;
484
485 /* Update TBF FSM names: */
486 if (ms->ul_tbf)
487 tbf_update_state_fsm_name(ul_tbf_as_tbf(ms->ul_tbf));
488 if (ms->dl_tbf)
489 tbf_update_state_fsm_name(dl_tbf_as_tbf(ms->dl_tbf));
490}
491
Pau Espin Pedrol0b5997e2022-10-21 14:10:41 +0200492/* Set/update the MS object TLLI based on knowledge gained from the MS side (Uplink direction) */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100493void ms_set_tlli(struct GprsMs *ms, uint32_t tlli)
494{
495 if (tlli == ms->tlli || tlli == ms->new_ul_tlli)
496 return;
497
498 if (tlli != ms->new_dl_tlli) {
499 LOGP(DRLCMAC, LOGL_INFO,
500 "Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
501 "not yet confirmed\n",
502 ms_tlli(ms), tlli);
503 ms->new_ul_tlli = tlli;
504 return;
505 }
506
507 LOGP(DRLCMAC, LOGL_INFO,
508 "Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
509 "already confirmed partly\n",
510 ms->tlli, tlli);
511
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100512 ms_apply_tlli_change(ms, tlli);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100513}
514
Pau Espin Pedrol0b5997e2022-10-21 14:10:41 +0200515/* Set/update the MS object TLLI based on knowledge gained from the SGSN side (Downlink direction) */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100516bool ms_confirm_tlli(struct GprsMs *ms, uint32_t tlli)
517{
518 if (tlli == ms->tlli || tlli == ms->new_dl_tlli)
519 return false;
520
521 if (tlli != ms->new_ul_tlli) {
522 /* The MS has not sent a message with the new TLLI, which may
523 * happen according to the spec [TODO: add reference]. */
524
525 LOGP(DRLCMAC, LOGL_INFO,
526 "The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
527 "partly confirmed\n", tlli);
528 /* Use the network's idea of TLLI as candidate, this does not
529 * change the result value of tlli() */
530 ms->new_dl_tlli = tlli;
531 return false;
532 }
533
534 LOGP(DRLCMAC, LOGL_INFO,
535 "Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
536
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100537 ms_apply_tlli_change(ms, tlli);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100538
539 return true;
540}
541
542void ms_set_imsi(struct GprsMs *ms, const char *imsi)
543{
544 if (!imsi) {
545 LOGP(DRLCMAC, LOGL_ERROR, "Expected IMSI!\n");
546 return;
547 }
548
549 if (imsi[0] && strlen(imsi) < 3) {
550 LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI '%s'!\n",
551 imsi);
552 return;
553 }
554
555 if (strcmp(imsi, ms->imsi) == 0)
556 return;
557
558 LOGP(DRLCMAC, LOGL_INFO,
559 "Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
560 ms_tlli(ms), ms->imsi, imsi);
561
562 struct GprsMs *old_ms = bts_ms_by_imsi(ms->bts, imsi);
563 /* Check if we are going to store a different MS object with already
564 existing IMSI. This is probably a bug in code calling this function,
565 since it should take care of this explicitly */
566 if (old_ms) {
567 /* We cannot find ms->ms by IMSI since we know that it has a
568 * different IMSI */
569 OSMO_ASSERT(old_ms != ms);
570
571 LOGPMS(ms, DRLCMAC, LOGL_NOTICE,
572 "IMSI '%s' was already assigned to another "
573 "MS object: TLLI = 0x%08x, that IMSI will be removed\n",
574 imsi, ms_tlli(old_ms));
575
576 ms_merge_and_clear_ms(ms, old_ms);
Pau Espin Pedrolb8ff74b2022-10-31 12:58:46 +0100577 /* old_ms may no longer be available here */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100578 }
579
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100580 /* Store the new IMSI: */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100581 osmo_strlcpy(ms->imsi, imsi, sizeof(ms->imsi));
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100582
583 /* Update TBF FSM names: */
584 if (ms->ul_tbf)
585 tbf_update_state_fsm_name(ul_tbf_as_tbf(ms->ul_tbf));
586 if (ms->dl_tbf)
587 tbf_update_state_fsm_name(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100588}
589
Pau Espin Pedrol5deac142021-11-12 18:01:50 +0100590uint16_t ms_paging_group(struct GprsMs *ms)
591{
592 uint16_t pgroup;
593 if (!ms_imsi_is_valid(ms))
594 return 0; /* 000 is the special "all paging" group */
595 if ((pgroup = imsi2paging_group(ms_imsi(ms))) > 999) {
596 LOGPMS(ms, DRLCMAC, LOGL_ERROR, "IMSI to paging group failed!\n");
597 return 0;
598 }
599 return pgroup;
600}
601
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100602void ms_set_ta(struct GprsMs *ms, uint8_t ta_)
603{
604 if (ta_ == ms->ta)
605 return;
606
607 if (gsm48_ta_is_valid(ta_)) {
608 LOGP(DRLCMAC, LOGL_INFO,
609 "Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
610 ms_tlli(ms), ms->ta, ta_);
611 ms->ta = ta_;
612 } else
613 LOGP(DRLCMAC, LOGL_NOTICE,
614 "MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
615 "value %d kept)\n", ms_tlli(ms), ta_, ms->ta);
616}
617
618void ms_set_ms_class(struct GprsMs *ms, uint8_t ms_class_)
619{
620 if (ms_class_ == ms->ms_class)
621 return;
622
623 LOGP(DRLCMAC, LOGL_INFO,
624 "Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
625 ms_tlli(ms), ms->ms_class, ms_class_);
626
627 ms->ms_class = ms_class_;
628}
629
630void ms_set_egprs_ms_class(struct GprsMs *ms, uint8_t ms_class_)
631{
632 if (ms_class_ == ms->egprs_ms_class)
633 return;
634
635 LOGP(DRLCMAC, LOGL_INFO,
636 "Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
637 ms_tlli(ms), ms->egprs_ms_class, ms_class_);
638
639 ms->egprs_ms_class = ms_class_;
640
641 if (!bts_max_mcs_ul(ms->bts) || !bts_max_mcs_dl(ms->bts)) {
642 LOGPMS(ms, DRLCMAC, LOGL_DEBUG,
643 "Avoid enabling EGPRS because use of MCS is disabled: ul=%u dl=%u\n",
644 bts_max_mcs_ul(ms->bts), bts_max_mcs_dl(ms->bts));
645 return;
646 }
647
648 if (mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts))) &&
649 mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts))) &&
650 ms_mode(ms) != EGPRS)
651 {
652 ms_set_mode(ms, EGPRS_GMSK);
653 } else {
654 ms_set_mode(ms, EGPRS);
655 }
656 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Enabled EGPRS, mode %s\n", mode_name(ms_mode(ms)));
657}
658
659void ms_update_error_rate(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, int error_rate)
660{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100661 int64_t now;
662 enum CodingScheme max_cs_dl = ms_max_cs_dl(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100663 OSMO_ASSERT(max_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100664
665 if (error_rate < 0)
666 return;
667
668 now = now_msec();
669
670 /* TODO: Check for TBF direction */
671 /* TODO: Support different CS values for UL and DL */
672
673 ms->nack_rate_dl = error_rate;
674
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100675 if (error_rate > the_pcu->vty.cs_adj_upper_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100676 if (mcs_chan_code(ms->current_cs_dl) > 0) {
677 mcs_dec_kind(&ms->current_cs_dl, ms_mode(ms));
678 LOGP(DRLCMACDL, LOGL_INFO,
679 "MS (IMSI %s): High error rate %d%%, "
680 "reducing CS level to %s\n",
681 ms_imsi(ms), error_rate, mcs_name(ms->current_cs_dl));
682 ms->last_cs_not_low = now;
683 }
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100684 } else if (error_rate < the_pcu->vty.cs_adj_lower_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100685 if (ms->current_cs_dl < max_cs_dl) {
686 if (now - ms->last_cs_not_low > 1000) {
687 mcs_inc_kind(&ms->current_cs_dl, ms_mode(ms));
688
689 LOGP(DRLCMACDL, LOGL_INFO,
690 "MS (IMSI %s): Low error rate %d%%, "
691 "increasing DL CS level to %s\n",
692 ms_imsi(ms), error_rate,
693 mcs_name(ms->current_cs_dl));
694 ms->last_cs_not_low = now;
695 } else {
696 LOGP(DRLCMACDL, LOGL_DEBUG,
697 "MS (IMSI %s): Low error rate %d%%, "
698 "ignored (within blocking period)\n",
699 ms_imsi(ms), error_rate);
700 }
701 }
702 } else {
703 LOGP(DRLCMACDL, LOGL_DEBUG,
704 "MS (IMSI %s): Medium error rate %d%%, ignored\n",
705 ms_imsi(ms), error_rate);
706 ms->last_cs_not_low = now;
707 }
708}
709
710enum CodingScheme ms_max_cs_ul(const struct GprsMs *ms)
711{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100712 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100713 OSMO_ASSERT(ms->bts != NULL);
714
715 if (mcs_is_gprs(ms->current_cs_ul)) {
716 if (!bts_max_cs_ul(ms->bts)) {
717 return CS4;
718 }
719
720 return mcs_get_gprs_by_num(bts_max_cs_ul(ms->bts));
721 }
722
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100723 cs = mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts));
724 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
725 cs = MCS4;
726 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100727}
728
729void ms_set_current_cs_dl(struct GprsMs *ms, enum CodingScheme scheme)
730{
731 ms->current_cs_dl = scheme;
732}
733
734enum CodingScheme ms_max_cs_dl(const struct GprsMs *ms)
735{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100736 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100737 OSMO_ASSERT(ms->bts != NULL);
738
739 if (mcs_is_gprs(ms->current_cs_dl)) {
740 if (!bts_max_cs_dl(ms->bts)) {
741 return CS4;
742 }
743
744 return mcs_get_gprs_by_num(bts_max_cs_dl(ms->bts));
745 }
746
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100747 cs = mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts));
748 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
749 cs = MCS4;
750 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100751}
752
753void ms_update_cs_ul(struct GprsMs *ms, const struct pcu_l1_meas *meas)
754{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100755 enum CodingScheme max_cs_ul = ms_max_cs_ul(ms);
756
757 int old_link_qual;
758 int low;
759 int high;
760 enum CodingScheme new_cs_ul = ms->current_cs_ul;
761 uint8_t current_cs = mcs_chan_code(ms->current_cs_ul);
762
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100763 if (!max_cs_ul) {
764 LOGP(DRLCMACMEAS, LOGL_ERROR,
765 "max_cs_ul cannot be derived (current UL CS: %s)\n",
766 mcs_name(ms->current_cs_ul));
767 return;
768 }
769
770 if (!ms->current_cs_ul) {
771 LOGP(DRLCMACMEAS, LOGL_ERROR,
772 "Unable to update UL (M)CS because it's not set: %s\n",
773 mcs_name(ms->current_cs_ul));
774 return;
775 }
776
777 if (!meas->have_link_qual) {
778 LOGP(DRLCMACMEAS, LOGL_ERROR,
779 "Unable to update UL (M)CS %s because we don't have link quality measurements.\n",
780 mcs_name(ms->current_cs_ul));
781 return;
782 }
783
784 if (mcs_is_gprs(ms->current_cs_ul)) {
785 if (current_cs >= MAX_GPRS_CS)
786 current_cs = MAX_GPRS_CS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100787 low = the_pcu->vty.cs_lqual_ranges[current_cs].low;
788 high = the_pcu->vty.cs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100789 } else if (mcs_is_edge(ms->current_cs_ul)) {
790 if (current_cs >= MAX_EDGE_MCS)
791 current_cs = MAX_EDGE_MCS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100792 low = the_pcu->vty.mcs_lqual_ranges[current_cs].low;
793 high = the_pcu->vty.mcs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100794 } else {
795 LOGP(DRLCMACMEAS, LOGL_ERROR,
796 "Unable to update UL (M)CS because it's neither GPRS nor EDGE: %s\n",
797 mcs_name(ms->current_cs_ul));
798 return;
799 }
800
801 /* To avoid rapid changes of the coding scheme, we also take
802 * the old link quality value into account (if present). */
803 if (ms->l1_meas.have_link_qual)
804 old_link_qual = ms->l1_meas.link_qual;
805 else
806 old_link_qual = meas->link_qual;
807
808 if (meas->link_qual < low && old_link_qual < low)
809 mcs_dec_kind(&new_cs_ul, ms_mode(ms));
810 else if (meas->link_qual > high && old_link_qual > high &&
811 ms->current_cs_ul < max_cs_ul)
812 mcs_inc_kind(&new_cs_ul, ms_mode(ms));
813
814 if (ms->current_cs_ul != new_cs_ul) {
815 LOGPMS(ms, DRLCMACMEAS, LOGL_INFO,
816 "Link quality %ddB (old %ddB) left window [%d, %d], "
817 "modifying uplink CS level: %s -> %s\n",
818 meas->link_qual, old_link_qual,
819 low, high,
820 mcs_name(ms->current_cs_ul), mcs_name(new_cs_ul));
821
822 ms->current_cs_ul = new_cs_ul;
823 }
824}
825
826void ms_update_l1_meas(struct GprsMs *ms, const struct pcu_l1_meas *meas)
827{
828 unsigned i;
829
830 ms_update_cs_ul(ms, meas);
831
832 if (meas->have_rssi)
833 pcu_l1_meas_set_rssi(&ms->l1_meas, meas->rssi);
834 if (meas->have_bto)
835 pcu_l1_meas_set_bto(&ms->l1_meas, meas->bto);
836 if (meas->have_ber)
837 pcu_l1_meas_set_ber(&ms->l1_meas, meas->ber);
838 if (meas->have_link_qual)
839 pcu_l1_meas_set_link_qual(&ms->l1_meas, meas->link_qual);
840
841 if (meas->have_ms_rx_qual)
842 pcu_l1_meas_set_ms_rx_qual(&ms->l1_meas, meas->ms_rx_qual);
843 if (meas->have_ms_c_value)
844 pcu_l1_meas_set_ms_c_value(&ms->l1_meas, meas->ms_c_value);
845 if (meas->have_ms_sign_var)
846 pcu_l1_meas_set_ms_sign_var(&ms->l1_meas, meas->ms_sign_var);
847
848 if (meas->have_ms_i_level) {
849 for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
850 if (meas->ts[i].have_ms_i_level)
851 pcu_l1_meas_set_ms_i_level(&ms->l1_meas, i, meas->ts[i].ms_i_level);
852 else
853 ms->l1_meas.ts[i].have_ms_i_level = 0;
854 }
855 }
856}
857
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100858/* req_mcs_kind acts as a set filter, where EGPRS means any and GPRS is the most restrictive */
859enum CodingScheme ms_current_cs_dl(const struct GprsMs *ms, enum mcs_kind req_mcs_kind)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100860{
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100861 enum CodingScheme orig_cs = ms->current_cs_dl;
862 struct gprs_rlcmac_bts *bts = ms->bts;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100863 size_t unencoded_octets;
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100864 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100865
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100866 /* It could be that a TBF requests a GPRS CS despite the MS currently
867 being upgraded to EGPRS (hence reporting MCS). That could happen
868 because the TBF was created early in the process where we didn't have
869 yet enough information about the MS, and only AFTER it was created we
870 upgraded the MS to be EGPRS capable.
871 As a result, when the MS is queried for the target CS here, we could be
872 returning an MCS despite the current TBF being established as GPRS,
873 but we rather stick to the TBF type we assigned to the MS rather than
874 magically sending EGPRS data blocks to a GPRS TBF.
875 It could also be that the caller requests specific MCS kind
876 explicitly too due to scheduling restrictions (GPRS+EGPRS multiplexing). */
877 if (req_mcs_kind == EGPRS_GMSK && mcs_is_edge(orig_cs) && orig_cs > MCS4) {
878 cs = bts_cs_dl_is_supported(bts, MCS4) ? MCS4 :
879 bts_cs_dl_is_supported(bts, MCS3) ? MCS3 :
880 bts_cs_dl_is_supported(bts, MCS2) ? MCS2 :
881 MCS1;
882 } else if (req_mcs_kind == GPRS && mcs_is_edge(orig_cs)) { /* GPRS */
883 int i;
884 cs = orig_cs > MCS4 ? MCS4 : orig_cs;
885 cs -= (MCS1 - CS1); /* MCSx -> CSx */
886 /* Find suitable CS starting from equivalent MCS which is supported by BTS: */
887 for (i = mcs_chan_code(cs); !bts_cs_dl_is_supported(bts, CS1 + i); i--);
888 OSMO_ASSERT(i >= 0 && i <= 3); /* CS1 is always supported */
889 cs = CS1 + i;
890 } else {
891 cs = orig_cs;
892 }
893
894 if (orig_cs != cs)
895 LOGPMS(ms, DRLCMACDL, LOGL_INFO, "MS (mode=%s) suggests transmitting "
896 "DL %s, downgrade to %s in order to match TBF & scheduler requirements\n",
897 mode_name(ms_mode(ms)), mcs_name(orig_cs), mcs_name(cs));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100898
899 unencoded_octets = llc_queue_octets(&ms->llc_queue);
900
901 /* If the DL TBF is active, add number of unencoded chunk octets */
902 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200903 unencoded_octets += llc_chunk_size(tbf_llc(dl_tbf_as_tbf(ms->dl_tbf)));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100904
905 /* There are many unencoded octets, don't reduce */
Pau Espin Pedrolad79b852021-01-14 13:20:55 +0100906 if (unencoded_octets >= the_pcu->vty.cs_downgrade_threshold)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100907 return cs;
908
909 /* RF conditions are good, don't reduce */
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100910 if (ms->nack_rate_dl < the_pcu->vty.cs_adj_lower_limit)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100911 return cs;
912
913 /* The throughput would probably be better if the CS level was reduced */
914 mcs_dec_kind(&cs, ms_mode(ms));
915
916 /* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
917 if (cs == CS2)
918 mcs_dec_kind(&cs, ms_mode(ms));
919
920 return cs;
921}
922
Pau Espin Pedrol345d9ad2022-12-12 19:22:44 +0100923int8_t ms_first_common_ts(const struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100924{
Pau Espin Pedrol345d9ad2022-12-12 19:22:44 +0100925 return ms->first_common_ts;
926}
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100927
Pau Espin Pedrol345d9ad2022-12-12 19:22:44 +0100928void ms_set_first_common_ts(struct GprsMs *ms, uint8_t first_common_ts)
929{
930 OSMO_ASSERT(first_common_ts < 8);
931 ms->first_common_ts = first_common_ts;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100932}
933
934uint8_t ms_dl_slots(const struct GprsMs *ms)
935{
936 uint8_t slots = 0;
937
938 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200939 slots |= tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100940
941 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200942 slots |= tbf_dl_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100943
944 return slots;
945}
946
947uint8_t ms_ul_slots(const struct GprsMs *ms)
948{
949 uint8_t slots = 0;
950
951 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200952 slots |= tbf_ul_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100953
954 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200955 slots |= tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100956
957 return slots;
958}
959
960uint8_t ms_current_pacch_slots(const struct GprsMs *ms)
961{
962 uint8_t slots = 0;
963
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200964 bool is_dl_active = ms->dl_tbf && tbf_is_tfi_assigned(dl_tbf_as_tbf(ms->dl_tbf));
965 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 +0100966
967 if (!is_dl_active && !is_ul_active)
968 return 0;
969
970 /* see TS 44.060, 8.1.1.2.2 */
971 if (is_dl_active && !is_ul_active)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200972 slots = tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100973 else if (!is_dl_active && is_ul_active)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200974 slots = tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100975 else
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200976 slots = tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf)) &
977 tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100978
979 /* Assume a multislot class 1 device */
980 /* TODO: For class 2 devices, this could be removed */
981 slots = pcu_lsb(slots);
982
983 return slots;
984}
985
986void ms_set_reserved_slots(struct GprsMs *ms, struct gprs_rlcmac_trx *trx,
987 uint8_t ul_slots, uint8_t dl_slots)
988{
989 if (ms->current_trx) {
990 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
991 ms->reserved_dl_slots);
992 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
993 ms->reserved_ul_slots);
994 ms->reserved_dl_slots = 0;
995 ms->reserved_ul_slots = 0;
996 }
997 ms->current_trx = trx;
998 if (trx) {
999 ms->reserved_dl_slots = dl_slots;
1000 ms->reserved_ul_slots = ul_slots;
1001 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
1002 ms->reserved_dl_slots);
1003 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
1004 ms->reserved_ul_slots);
1005 }
1006}
1007
1008struct gprs_rlcmac_tbf *ms_tbf(const struct GprsMs *ms, enum gprs_rlcmac_tbf_direction dir)
1009{
1010 switch (dir) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +02001011 case GPRS_RLCMAC_DL_TBF: return dl_tbf_as_tbf(ms->dl_tbf);
1012 case GPRS_RLCMAC_UL_TBF: return ul_tbf_as_tbf(ms->ul_tbf);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +01001013 }
1014
1015 return NULL;
1016}
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001017
Pau Espin Pedrol3547d642022-10-21 15:00:08 +02001018const char *ms_name(const struct GprsMs *ms)
1019{
1020 static char _ms_name_buf[128];
1021 return ms_name_buf(ms, _ms_name_buf, sizeof(_ms_name_buf));
1022}
1023
1024char *ms_name_buf(const struct GprsMs *ms, char *buf, unsigned int buf_size)
1025{
Pau Espin Pedrol94f82582022-11-03 14:16:17 +01001026 struct osmo_strbuf sb = { .buf = buf, .len = buf_size };
1027 uint32_t tlli = ms_tlli(ms);
1028
1029 OSMO_STRBUF_PRINTF(sb, "MS(");
1030 if (ms_imsi_is_valid(ms))
1031 OSMO_STRBUF_PRINTF(sb, "IMSI-%s:", ms_imsi(ms));
1032 if (tlli != GSM_RESERVED_TMSI)
1033 OSMO_STRBUF_PRINTF(sb, "TLLI-0x%08x:", tlli);
1034 OSMO_STRBUF_PRINTF(sb, "TA-%" PRIu8 ":MSCLS-%" PRIu8 "-%" PRIu8,
1035 ms_ta(ms), ms_ms_class(ms), ms_egprs_ms_class(ms));
1036 if (ms->ul_tbf)
1037 OSMO_STRBUF_PRINTF(sb, ":UL");
1038 if (ms->dl_tbf)
1039 OSMO_STRBUF_PRINTF(sb, ":DL");
1040
1041 OSMO_STRBUF_PRINTF(sb, ")");
Pau Espin Pedrol3547d642022-10-21 15:00:08 +02001042 return buf;
1043}
1044
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001045int ms_nacc_start(struct GprsMs *ms, Packet_Cell_Change_Notification_t *notif)
1046{
1047 if (!ms->nacc)
1048 ms->nacc = nacc_fsm_alloc(ms);
1049 if (!ms->nacc)
1050 return -EINVAL;
1051 return osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_RX_CELL_CHG_NOTIFICATION, notif);
1052}
1053
1054bool ms_nacc_rts(const struct GprsMs *ms)
1055{
1056 if (!ms->nacc)
1057 return false;
1058 if (ms->nacc->fi->state == NACC_ST_TX_NEIGHBOUR_DATA ||
1059 ms->nacc->fi->state == NACC_ST_TX_CELL_CHG_CONTINUE)
1060 return true;
1061 return false;
1062}
1063
Pau Espin Pedrol5ba3ef92022-12-12 18:02:25 +01001064struct msgb *ms_nacc_create_rlcmac_msg(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf,
1065 const struct gprs_rlcmac_pdch *pdch, uint32_t fn)
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001066{
1067 int rc;
1068 struct nacc_ev_create_rlcmac_msg_ctx data_ctx;
1069
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001070 data_ctx = (struct nacc_ev_create_rlcmac_msg_ctx) {
1071 .tbf = tbf,
Pau Espin Pedrol5ba3ef92022-12-12 18:02:25 +01001072 .pdch = pdch,
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001073 .fn = fn,
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001074 .msg = NULL,
1075 };
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001076
1077 rc = osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_CREATE_RLCMAC_MSG, &data_ctx);
1078 if (rc != 0 || !data_ctx.msg)
1079 return NULL;
1080 return data_ctx.msg;
1081}
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001082
1083static void ms_start_llc_timer(struct GprsMs *ms)
1084{
1085 if (the_pcu->vty.llc_idle_ack_csec > 0) {
1086 struct timespec tv;
1087 csecs_to_timespec(the_pcu->vty.llc_idle_ack_csec, &tv);
1088 osmo_timer_schedule(&ms->llc_timer, tv.tv_sec, tv.tv_nsec / 1000);
1089 }
1090}
1091
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001092/* Can we get to send a DL TBF ass to the MS? */
1093static bool ms_is_reachable_for_dl_ass(const struct GprsMs *ms)
1094{
1095 struct gprs_rlcmac_ul_tbf *ul_tbf = ms_ul_tbf(ms);
1096
1097 /* This function assumes it is called when no DL TBF is present */
1098 OSMO_ASSERT(!ms_dl_tbf(ms));
1099
1100 /* 3GPP TS 44.060 sec 7.1.3.1 Initiation of the Packet resource request procedure:
1101 * "Furthermore, the mobile station shall not respond to PACKET DOWNLINK ASSIGNMENT
1102 * or MULTIPLE TBF DOWNLINK ASSIGNMENT messages before contention resolution is
1103 * completed on the mobile station side." */
1104 /* The possible uplink TBF is used to trigger downlink assignment:
1105 * - If there is no uplink TBF the MS is potentially in packet idle mode
1106 * and hence assignment will be done over CCCH (PCH)
1107 * - If there's an uplink TBF but it is finished (waiting for last PKT
1108 * CTRL ACK after sending last Pkt UL ACK/NACK with FINAL_ACK=1, then we
1109 * have no ways to contact the MS right now. Assignment will be delayed
1110 * until PKT CTRL ACK is received and the TBF is released at the MS side
1111 * (then assignment goes through PCH).
1112 */
1113 if (!ul_tbf)
1114 return true;
1115 if (ul_tbf_contention_resolution_done(ul_tbf) &&
1116 !tbf_ul_ack_waiting_cnf_final_ack(ul_tbf))
1117 return true;
1118
1119 return false;
1120
1121}
1122
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001123/* Alloc a UL TBF to be assigned over PACCH. Called when an MS requests to
1124 * create a new UL TBF during the end of life of a previous UL TBF (or an SBA).
1125 * In summary, this TBF is allocated as a consequence of receiving a "Pkt
1126 * Resource Req" or "Pkt Ctrl Ack" from the MS.
1127 * See TS 44.060 9.3.2.4.2 "Non-extended uplink TBF mode".
1128 */
1129struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_pacch(struct GprsMs *ms, int8_t use_trx)
1130{
1131 const bool single_slot = false;
1132 struct gprs_rlcmac_ul_tbf *ul_tbf;
1133
Pau Espin Pedrolbda7bb72022-10-31 14:33:09 +01001134 ul_tbf = ul_tbf_alloc(ms->bts, ms, use_trx, single_slot);
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001135 if (!ul_tbf) {
1136 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1137 /* Caller will most probably send a Imm Ass Reject after return */
1138 return NULL;
1139 }
1140 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_PACCH, NULL);
1141 /* Contention resolution is considered to be done since TLLI is known in MS */
1142 return ul_tbf;
1143}
1144
1145/* Alloc a UL TBF to be assigned over AGCH. Used by request of a "One phase
1146 * packet access", where MS requested only 1 PDCH TS (TS 44.018 Table 9.1.8.1). */
1147struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_agch(struct GprsMs *ms)
1148{
1149 const int8_t trx_no = -1;
1150 const bool single_slot = true;
1151 struct gprs_rlcmac_ul_tbf *ul_tbf;
1152
Pau Espin Pedrolbda7bb72022-10-31 14:33:09 +01001153 ul_tbf = ul_tbf_alloc(ms->bts, ms, trx_no, single_slot);
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001154 if (!ul_tbf) {
1155 LOGP(DTBF, LOGL_NOTICE, "No PDCH resource for Uplink TBF\n");
1156 /* Caller will most probably send a Imm Ass Reject after return */
1157 return NULL;
1158 }
1159 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_CCCH, NULL);
1160 return ul_tbf;
1161}
1162
Pau Espin Pedrola621d592022-12-13 17:35:04 +01001163/* Create a temporary dummy TBF to Tx a ImmAssReject if allocating a new one during
1164 * packet resource Request failed. This is similar as ul_tbf_alloc() but without
1165 * calling tbf->setup() (in charge of TFI/USF allocation), and reusing resources
1166 * from Packet Resource Request we received. See TS 44.060 sec 7.1.3.2.1 */
1167struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_rejected_pacch(struct GprsMs *ms, struct gprs_rlcmac_pdch *pdch)
1168{
1169 struct gprs_rlcmac_ul_tbf *ul_tbf;
1170 ul_tbf = ul_tbf_alloc_rejected(ms->bts, ms, pdch);
1171 if (!ul_tbf)
1172 return NULL;
1173 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_PACCH, NULL);
1174 osmo_fsm_inst_dispatch(tbf_ul_ass_fi(ul_tbf_as_tbf(ul_tbf)), TBF_UL_ASS_EV_SCHED_ASS_REJ, NULL);
1175
1176 return ul_tbf;
1177}
1178
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001179/* A new DL-TBF is allocated and assigned through PACCH using "tbf".
1180 * "tbf" may be either a UL-TBF or a DL-TBF.
1181 * Note: This should be called only when MS is reachable, see ms_is_reachable_for_dl_ass().
1182 */
1183int ms_new_dl_tbf_assigned_on_pacch(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001184{
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001185 OSMO_ASSERT(tbf);
1186 const int8_t trx_no = tbf_get_trx(tbf)->trx_no;
1187 const bool single_slot = false;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001188 struct gprs_rlcmac_dl_tbf *dl_tbf;
1189
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001190 dl_tbf = dl_tbf_alloc(ms->bts, ms, trx_no, single_slot);
1191 if (!dl_tbf) {
1192 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1193 return -EBUSY;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001194 }
1195
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001196 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START (PACCH)\n");
1197 dl_tbf_trigger_ass_on_pacch(dl_tbf, tbf);
1198 return 0;
1199}
1200
1201/* A new DL-TBF is allocated and assigned through PCH.
1202 * Note: This should be called only when MS is reachable, see ms_is_reachable_for_dl_ass().
1203 */
1204int ms_new_dl_tbf_assigned_on_pch(struct GprsMs *ms)
1205{
1206 const int8_t trx_no = -1;
1207 const bool single_slot = true;
1208 struct gprs_rlcmac_dl_tbf *dl_tbf;
1209
1210 dl_tbf = dl_tbf_alloc(ms->bts, ms, trx_no, single_slot);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001211 if (!dl_tbf) {
1212 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1213 return -EBUSY;
1214 }
1215
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001216 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START (PCH)\n");
1217 dl_tbf_trigger_ass_on_pch(dl_tbf);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001218 return 0;
1219}
1220
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001221int ms_append_llc_dl_data(struct GprsMs *ms, uint16_t pdu_delay_csec, const uint8_t *data, uint16_t len)
1222{
1223 struct timespec expire_time;
1224 struct gprs_rlcmac_dl_tbf *dl_tbf;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001225 int rc = 0;
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001226
1227 LOGPMS(ms, DTBFDL, LOGL_DEBUG, "appending %u bytes to DL LLC queue\n", len);
1228
1229 struct msgb *llc_msg = msgb_alloc(len, "llc_pdu_queue");
1230 if (!llc_msg)
1231 return -ENOMEM;
1232
1233 llc_queue_calc_pdu_lifetime(ms->bts, pdu_delay_csec, &expire_time);
1234 memcpy(msgb_put(llc_msg, len), data, len);
1235 llc_queue_enqueue(ms_llc_queue(ms), llc_msg, &expire_time);
1236 ms_start_llc_timer(ms);
1237
1238 dl_tbf = ms_dl_tbf(ms);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001239 if (dl_tbf) {
1240 if (tbf_state(dl_tbf_as_tbf_const(dl_tbf)) == TBF_ST_WAIT_RELEASE) {
1241 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "in WAIT RELEASE state (T3193), so reuse TBF\n");
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001242 rc = ms_new_dl_tbf_assigned_on_pacch(ms, dl_tbf_as_tbf(dl_tbf));
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001243 }
1244 } else {
1245 /* Check if we can create a DL TBF to start sending the enqueued
1246 * data. Otherwise it will be triggered later when it is reachable
1247 * again. */
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001248 if (ms_is_reachable_for_dl_ass(ms)) {
1249 if (ms_ul_tbf(ms))
1250 rc = ms_new_dl_tbf_assigned_on_pacch(ms, ul_tbf_as_tbf(ms_ul_tbf(ms)));
1251 else
1252 rc = ms_new_dl_tbf_assigned_on_pch(ms);
1253 }
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001254 }
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001255 return rc;
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001256}