blob: d9e52ea5b095a71225684ce22b5b2c0902d0e031 [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 Pedrol4d363912023-04-21 19:32:16 +020027#include "alloc_algo.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
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +020065static void ms_becomes_idle(struct GprsMs *ms);
Pau Espin Pedrol403e0482023-04-17 20:28:10 +020066
67static int ms_use_cb(struct osmo_use_count_entry *e, int32_t old_use_count, const char *file, int line)
68{
69 struct GprsMs *ms = e->use_count->talloc_object;
70 int32_t total;
71 int level;
72 char buf[1024];
73
74 if (!e->use)
75 return -EINVAL;
76
77 total = osmo_use_count_total(&ms->use_count);
78
79 if (total == 0
80 || (total == 1 && old_use_count == 0 && e->count == 1))
81 level = LOGL_INFO;
82 else
83 level = LOGL_DEBUG;
84
85
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +020086 LOGPSRC(DMS, level, file, line, "%s: %s %s: now used by %s\n",
Pau Espin Pedrol403e0482023-04-17 20:28:10 +020087 ms_name(ms),
88 (e->count - old_use_count) > 0 ? "+" : "-", e->use,
89 (osmo_use_count_to_str_buf(buf, sizeof(buf), &ms->use_count), buf));
90
91 if (e->count < 0)
92 return -ERANGE;
93
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +020094 if (total == 0) {
95 OSMO_ASSERT(ms_is_idle(ms));
96 ms_becomes_idle(ms);
97 }
Pau Espin Pedrol403e0482023-04-17 20:28:10 +020098 return 0;
99}
100
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100101static void ms_release_timer_cb(void *data)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100102{
103 struct GprsMs *ms = (struct GprsMs *) data;
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200104 LOGPMS(ms, DMS, LOGL_INFO, "Release timer expired\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100105
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200106 /* Finally free the MS after being idle for a while according to config */
107 talloc_free(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100108}
109
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200110static void ms_llc_timer_cb(void *_ms)
111{
112 struct GprsMs *ms = _ms;
113 struct gprs_rlcmac_dl_tbf *dl_tbf = ms_dl_tbf(ms);
114
115 if (!dl_tbf)
116 return;
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200117 if (tbf_state(dl_tbf_as_tbf_const(dl_tbf)) != TBF_ST_FLOW)
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200118 return;
119
Pau Espin Pedrolbd1f01f2022-10-27 15:19:39 +0200120 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "LLC receive timeout, requesting DL ACK\n");
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200121
Pau Espin Pedrol8fa3e062022-10-28 17:38:52 +0200122 dl_tbf_request_dl_ack(dl_tbf);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200123}
124
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100125static int ms_talloc_destructor(struct GprsMs *ms);
Pau Espin Pedroleae91472023-04-19 19:42:05 +0200126struct GprsMs *ms_alloc(struct gprs_rlcmac_bts *bts, const char *use_ref)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100127{
128 struct GprsMs *ms = talloc_zero(tall_pcu_ctx, struct GprsMs);
Pau Espin Pedrolb4987462022-04-01 17:21:08 +0200129 OSMO_ASSERT(bts);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100130
131 talloc_set_destructor(ms, ms_talloc_destructor);
132
Pau Espin Pedrolfe7aee92023-04-17 18:23:56 +0200133 llist_add(&ms->list, &bts->ms_list);
Pau Espin Pedrol9da06862023-04-17 19:00:04 +0200134 bts_stat_item_inc(bts, STAT_MS_PRESENT);
Pau Espin Pedrolfe7aee92023-04-17 18:23:56 +0200135
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100136 ms->bts = bts;
Pau Espin Pedrolf80cc5b2023-04-17 14:25:51 +0200137 ms->tlli = GSM_RESERVED_TMSI;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100138 ms->new_ul_tlli = GSM_RESERVED_TMSI;
139 ms->new_dl_tlli = GSM_RESERVED_TMSI;
140 ms->ta = GSM48_TA_INVALID;
141 ms->current_cs_ul = UNKNOWN;
142 ms->current_cs_dl = UNKNOWN;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100143 INIT_LLIST_HEAD(&ms->old_tbfs);
144
Pau Espin Pedrol403e0482023-04-17 20:28:10 +0200145 ms->use_count = (struct osmo_use_count){
146 .talloc_object = ms,
147 .use_cb = ms_use_cb,
148 };
149
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100150 int codel_interval = LLC_CODEL_USE_DEFAULT;
151
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200152 LOGP(DMS, LOGL_INFO, "Creating MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100153
154 ms->imsi[0] = '\0';
Pau Espin Pedrolf086fed2023-04-20 16:52:40 +0200155 osmo_timer_setup(&ms->release_timer, ms_release_timer_cb, ms);
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200156 llc_queue_init(&ms->llc_queue, ms);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200157 memset(&ms->llc_timer, 0, sizeof(ms->llc_timer));
158 osmo_timer_setup(&ms->llc_timer, ms_llc_timer_cb, ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100159
160 ms_set_mode(ms, GPRS);
161
Pau Espin Pedrolb4987462022-04-01 17:21:08 +0200162 codel_interval = the_pcu->vty.llc_codel_interval_msec;
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200163 if (codel_interval == LLC_CODEL_USE_DEFAULT)
164 codel_interval = GPRS_CODEL_SLOW_INTERVAL_MS;
165 llc_queue_set_codel_interval(&ms->llc_queue, codel_interval);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100166
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100167 ms->last_cs_not_low = now_msec();
168 ms->app_info_pending = false;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100169
170 ms->ctrs = rate_ctr_group_alloc(ms, &ms_ctrg_desc, next_ms_ctr_group_id++);
171 if (!ms->ctrs)
172 goto free_ret;
173
Pau Espin Pedroleae91472023-04-19 19:42:05 +0200174 if (use_ref)
175 ms_ref(ms, use_ref);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100176 return ms;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100177free_ret:
178 talloc_free(ms);
179 return NULL;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100180}
181
182static int ms_talloc_destructor(struct GprsMs *ms)
183{
184 struct llist_item *pos, *tmp;
185
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200186 LOGPMS(ms, DMS, LOGL_INFO, "Destroying MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100187
Pau Espin Pedrol9da06862023-04-17 19:00:04 +0200188 bts_stat_item_dec(ms->bts, STAT_MS_PRESENT);
Pau Espin Pedrolfe7aee92023-04-17 18:23:56 +0200189 llist_del(&ms->list);
190
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100191 ms_set_reserved_slots(ms, NULL, 0, 0);
192
Pau Espin Pedrolf086fed2023-04-20 16:52:40 +0200193 osmo_timer_del(&ms->release_timer);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100194
195 if (ms->ul_tbf) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200196 tbf_set_ms(ul_tbf_as_tbf(ms->ul_tbf), NULL);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100197 ms->ul_tbf = NULL;
198 }
199
200 if (ms->dl_tbf) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200201 tbf_set_ms(dl_tbf_as_tbf(ms->dl_tbf), NULL);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100202 ms->dl_tbf = NULL;
203 }
204
205 llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
206 struct gprs_rlcmac_tbf *tbf = (struct gprs_rlcmac_tbf *)pos->entry;
207 tbf_set_ms(tbf, NULL);
208 }
209
210 llc_queue_clear(&ms->llc_queue, ms->bts);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200211 osmo_timer_del(&ms->llc_timer);
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100212
213 if (ms->ctrs)
214 rate_ctr_group_free(ms->ctrs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100215 return 0;
216}
217
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200218/* MS has no attached TBFs anymore. */
219static void ms_becomes_idle(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100220{
Pau Espin Pedrolb53230a2023-04-20 16:42:54 +0200221 unsigned long delay_rel_sec = osmo_tdef_get(ms->bts->pcu->T_defs, -2030, OSMO_TDEF_S, -1);
222
Pau Espin Pedrol4673eb02023-04-20 17:18:34 +0200223 osmo_gettimeofday(&ms->tv_idle_start, NULL);
224
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200225 ms_set_reserved_slots(ms, NULL, 0, 0);
226 ms->first_common_ts = NULL;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100227
Pau Espin Pedrol14379ef2023-04-17 20:43:26 +0200228 /* Immediate free():
229 * Skip delaying free() through release timer if delay is configured to be 0.
230 * This is useful for synced freed during unit tests.
231 */
Pau Espin Pedrolb53230a2023-04-20 16:42:54 +0200232 if (delay_rel_sec == 0) {
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200233 talloc_free(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100234 return;
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200235 }
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100236
Pau Espin Pedrol14379ef2023-04-17 20:43:26 +0200237 /* Immediate free():
238 * Skip delaying free() through release timer if TMSI is not
239 * known, since those cannot really be reused.
240 */
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200241 if (ms_tlli(ms) == GSM_RESERVED_TMSI) {
242 talloc_free(ms);
Pau Espin Pedrol14379ef2023-04-17 20:43:26 +0200243 return;
Pau Espin Pedrol403e0482023-04-17 20:28:10 +0200244 }
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100245
Pau Espin Pedrolb53230a2023-04-20 16:42:54 +0200246 LOGPMS(ms, DMS, LOGL_INFO, "Schedule MS release in %lu secs\n", delay_rel_sec);
Pau Espin Pedrolf086fed2023-04-20 16:52:40 +0200247 osmo_timer_schedule(&ms->release_timer, delay_rel_sec, 0);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100248}
249
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200250static void ms_becomes_active(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100251{
Pau Espin Pedrolf086fed2023-04-20 16:52:40 +0200252 if (!osmo_timer_pending(&ms->release_timer))
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100253 return;
254
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200255 LOGPMS(ms, DMS, LOGL_DEBUG, "Cancel scheduled MS release\n");
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100256
Pau Espin Pedrol4673eb02023-04-20 17:18:34 +0200257 timerclear(&ms->tv_idle_start);
Pau Espin Pedrolf086fed2023-04-20 16:52:40 +0200258 osmo_timer_del(&ms->release_timer);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100259}
260
261void ms_set_mode(struct GprsMs *ms, enum mcs_kind mode)
262{
263 ms->mode = mode;
264
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100265 switch (ms->mode) {
266 case GPRS:
267 if (!mcs_is_gprs(ms->current_cs_ul)) {
268 ms->current_cs_ul = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100269 ms->bts->initial_cs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100270 if (!mcs_is_valid(ms->current_cs_ul))
271 ms->current_cs_ul = CS1;
272 }
273 if (!mcs_is_gprs(ms->current_cs_dl)) {
274 ms->current_cs_dl = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100275 ms->bts->initial_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100276 if (!mcs_is_valid(ms->current_cs_dl))
277 ms->current_cs_dl = CS1;
278 }
279 break;
280
281 case EGPRS_GMSK:
Pau Espin Pedrol7bb8cd62021-01-25 15:08:35 +0100282 if (!mcs_is_edge_gmsk(ms->current_cs_ul)) {
283 ms->current_cs_ul = mcs_get_egprs_by_num(
284 ms->bts->initial_mcs_ul);
285 if (!mcs_is_valid(ms->current_cs_ul))
286 ms->current_cs_ul = MCS1;
287 }
288 if (!mcs_is_edge_gmsk(ms->current_cs_dl)) {
289 ms->current_cs_dl = mcs_get_egprs_by_num(
290 ms->bts->initial_mcs_dl);
291 if (!mcs_is_valid(ms->current_cs_dl))
292 ms->current_cs_dl = MCS1;
293 }
294 break;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100295 case EGPRS:
296 if (!mcs_is_edge(ms->current_cs_ul)) {
297 ms->current_cs_ul = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100298 ms->bts->initial_mcs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100299 if (!mcs_is_valid(ms->current_cs_ul))
300 ms->current_cs_ul = MCS1;
301 }
302 if (!mcs_is_edge(ms->current_cs_dl)) {
303 ms->current_cs_dl = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100304 ms->bts->initial_mcs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100305 if (!mcs_is_valid(ms->current_cs_dl))
306 ms->current_cs_dl = MCS1;
307 }
308 break;
309 }
310}
311
Pau Espin Pedrol6efa3812023-04-18 17:31:02 +0200312/* If a TBF is attached to an MS, it is either in ms->{dl,ul}_tbf or in ms->old_tbfs list */
313static bool ms_tbf_is_attached(const struct GprsMs *ms, const struct gprs_rlcmac_tbf *tbf)
314{
315 const struct llist_item *pos;
316 OSMO_ASSERT(ms);
317 OSMO_ASSERT(tbf);
318 OSMO_ASSERT(tbf_ms(tbf) == ms);
319
320 if (tbf == ul_tbf_as_tbf_const(ms->ul_tbf))
321 return true;
322
323 if (tbf == dl_tbf_as_tbf_const(ms->dl_tbf))
324 return true;
325
326 llist_for_each_entry(pos, &ms->old_tbfs, list) {
327 const struct gprs_rlcmac_tbf *tmp_tbf = (struct gprs_rlcmac_tbf *)pos->entry;
328 if (tmp_tbf == tbf)
329 return true;
330 }
331 return false;
332}
333
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100334static void ms_attach_ul_tbf(struct GprsMs *ms, struct gprs_rlcmac_ul_tbf *tbf)
335{
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200336 LOGPMS(ms, DMS, LOGL_INFO, "Attaching UL TBF: %s\n", tbf_name((struct gprs_rlcmac_tbf *)tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100337
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100338 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200339 llist_add_tail(tbf_ms_list(ul_tbf_as_tbf(ms->ul_tbf)), &ms->old_tbfs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100340
341 ms->ul_tbf = tbf;
342
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200343 ms_ref(ms, MS_USE_TBF);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100344}
345
346static void ms_attach_dl_tbf(struct GprsMs *ms, struct gprs_rlcmac_dl_tbf *tbf)
347{
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200348 LOGPMS(ms, DMS, LOGL_INFO, "Attaching DL TBF: %s\n", tbf_name((struct gprs_rlcmac_tbf *)tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100349
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100350 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200351 llist_add_tail(tbf_ms_list(dl_tbf_as_tbf(ms->dl_tbf)), &ms->old_tbfs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100352
353 ms->dl_tbf = tbf;
354
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200355 ms_ref(ms, MS_USE_TBF);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100356}
357
358void ms_attach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
359{
Pau Espin Pedrol3ea467d2023-04-18 16:33:34 +0200360 OSMO_ASSERT(ms);
361 OSMO_ASSERT(tbf);
Pau Espin Pedrol6efa3812023-04-18 17:31:02 +0200362 OSMO_ASSERT(!ms_tbf_is_attached(ms, tbf));
Pau Espin Pedrol3ea467d2023-04-18 16:33:34 +0200363
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200364 if (tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF)
Pau Espin Pedrolcc30b052022-10-27 15:25:55 +0200365 ms_attach_dl_tbf(ms, tbf_as_dl_tbf(tbf));
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200366 else
Pau Espin Pedrolcc30b052022-10-27 15:25:55 +0200367 ms_attach_ul_tbf(ms, tbf_as_ul_tbf(tbf));
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200368
369 ms_becomes_active(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100370}
371
372void ms_detach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
373{
Pau Espin Pedrol6efa3812023-04-18 17:31:02 +0200374 OSMO_ASSERT(tbf_ms(tbf) == ms);
375
376 /* In general this should not happen, but it can happen if during TBF
377 * allocation something fails before tbf->setup() called ms_attach_tbf(). */
378 if (!ms_tbf_is_attached(ms, tbf))
379 return;
380
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200381 LOGPMS(ms, DMS, LOGL_INFO, "Detaching TBF: %s\n",
Pau Espin Pedroldf6684f2023-04-18 17:50:34 +0200382 tbf_name(tbf));
383
Pau Espin Pedrol6efa3812023-04-18 17:31:02 +0200384 if (tbf == ul_tbf_as_tbf(ms->ul_tbf)) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100385 ms->ul_tbf = NULL;
Pau Espin Pedrol6efa3812023-04-18 17:31:02 +0200386 } else if (tbf == dl_tbf_as_tbf(ms->dl_tbf)) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100387 ms->dl_tbf = NULL;
388 } else {
Pau Espin Pedrol6efa3812023-04-18 17:31:02 +0200389 /* We know from ms_tbf_is_attached()==true check above that tbf
390 * is in ms->old_tbfs, no need to look it up again. */
391 llist_del(tbf_ms_list(tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100392 }
393
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200394 ms_unref(ms, MS_USE_TBF);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100395}
396
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200397/* Cleans up old MS being merged into a new one. Should be called with a
398ms_ref() taken to avoid use-after-free. */
Pau Espin Pedrol5faedf32023-04-28 14:29:40 +0200399static void ms_reset(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100400{
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200401 LOGPMS(ms, DMS, LOGL_INFO, "Clearing MS object\n");
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200402 struct llist_item *pos;
403 struct gprs_rlcmac_tbf *tbf;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100404
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200405 tbf = ul_tbf_as_tbf(ms_ul_tbf(ms));
406 if (tbf && !tbf_timers_pending(tbf, T_MAX))
407 tbf_free(tbf);
408 tbf = dl_tbf_as_tbf(ms_dl_tbf(ms));
409 if (tbf && !tbf_timers_pending(tbf, T_MAX))
410 tbf_free(tbf);
411
Pau Espin Pedrol7a491b92023-04-28 14:45:39 +0200412 while ((pos = llist_first_entry_or_null(&ms->old_tbfs, struct llist_item, list))) {
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200413 tbf = (struct gprs_rlcmac_tbf *)pos->entry;
414 if (!tbf_timers_pending(tbf, T_MAX))
415 tbf_free(tbf);
416 }
417
418 /* Flag it with invalid data so that it cannot be looked up anymore and
Pau Espin Pedrolb53230a2023-04-20 16:42:54 +0200419 * shows up specially if listed in VTY. Furthermore, it will also trigger
420 * immediate free() when it becomes idle: */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100421 ms->tlli = GSM_RESERVED_TMSI;
422 ms->new_dl_tlli = ms->tlli;
423 ms->new_ul_tlli = ms->tlli;
424 ms->imsi[0] = '\0';
425}
426
Pau Espin Pedrol8abe13c2022-10-21 18:49:48 +0200427/* This function should be called on the MS object of a TBF each time an RLCMAC
428 * block is received for it with TLLI information.
429 * Besides updating the TLLI field on the MS object, it also seeks for other MS
430 * objects in the store and merges them into the current MS object. The MS
431 * duplication happened because we don't learn the TLLI of the created TBF until
432 * a later point. */
433void ms_update_announced_tlli(struct GprsMs *ms, uint32_t tlli)
434{
435 struct GprsMs *old_ms = NULL;
436
437 if (tlli == GSM_RESERVED_TMSI)
438 return;
439
440 /* When the TLLI does not match the ms, check if there is another
441 * MS object that belongs to that TLLI and if yes make sure one of them
442 * gets deleted. */
443 if (!ms_check_tlli(ms, tlli))
Pau Espin Pedroleb0a0522023-04-17 16:33:35 +0200444 old_ms = bts_get_ms_by_tlli(ms->bts, tlli, GSM_RESERVED_TMSI);
Pau Espin Pedrol8abe13c2022-10-21 18:49:48 +0200445
446 ms_set_tlli(ms, tlli);
447
448 if (old_ms)
449 ms_merge_and_clear_ms(ms, old_ms);
450 /* old_ms may no longer be available here */
451}
452
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100453/* Merge 'old_ms' object into 'ms' object.
454 * 'old_ms' may be freed during the call to this function, don't use the pointer to it afterwards */
455void ms_merge_and_clear_ms(struct GprsMs *ms, struct GprsMs *old_ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100456{
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100457 char old_ms_name[128];
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100458 OSMO_ASSERT(old_ms != ms);
Pau Espin Pedrol403e0482023-04-17 20:28:10 +0200459 ms_ref(old_ms, __func__);
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100460
461 ms_name_buf(old_ms, old_ms_name, sizeof(old_ms_name));
462
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200463 LOGPMS(ms, DMS, LOGL_INFO, "Merge MS: %s\n", old_ms_name);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100464
465 if (strlen(ms_imsi(ms)) == 0 && strlen(ms_imsi(old_ms)) != 0)
466 osmo_strlcpy(ms->imsi, ms_imsi(old_ms), sizeof(ms->imsi));
467
468 if (!ms_ms_class(ms) && ms_ms_class(old_ms))
469 ms_set_ms_class(ms, ms_ms_class(old_ms));
470
471 if (!ms_egprs_ms_class(ms) && ms_egprs_ms_class(old_ms))
472 ms_set_egprs_ms_class(ms, ms_egprs_ms_class(old_ms));
473
474 llc_queue_move_and_merge(&ms->llc_queue, &old_ms->llc_queue);
475
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100476 /* Clean up the old MS object */
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100477 ms_reset(old_ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100478
Pau Espin Pedrol403e0482023-04-17 20:28:10 +0200479 ms_unref(old_ms, __func__);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100480}
481
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100482/* Apply changes to the TLLI directly, used interally by functions below: */
483static void ms_apply_tlli_change(struct GprsMs *ms, uint32_t tlli)
484{
485 ms->tlli = tlli;
486 ms->new_dl_tlli = GSM_RESERVED_TMSI;
487 ms->new_ul_tlli = GSM_RESERVED_TMSI;
488
489 /* Update TBF FSM names: */
490 if (ms->ul_tbf)
491 tbf_update_state_fsm_name(ul_tbf_as_tbf(ms->ul_tbf));
492 if (ms->dl_tbf)
493 tbf_update_state_fsm_name(dl_tbf_as_tbf(ms->dl_tbf));
494}
495
Pau Espin Pedrol0b5997e2022-10-21 14:10:41 +0200496/* Set/update the MS object TLLI based on knowledge gained from the MS side (Uplink direction) */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100497void ms_set_tlli(struct GprsMs *ms, uint32_t tlli)
498{
499 if (tlli == ms->tlli || tlli == ms->new_ul_tlli)
500 return;
501
502 if (tlli != ms->new_dl_tlli) {
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200503 LOGP(DMS, LOGL_INFO,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100504 "Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
505 "not yet confirmed\n",
506 ms_tlli(ms), tlli);
507 ms->new_ul_tlli = tlli;
508 return;
509 }
510
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200511 LOGP(DMS, LOGL_INFO,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100512 "Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
513 "already confirmed partly\n",
514 ms->tlli, tlli);
515
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100516 ms_apply_tlli_change(ms, tlli);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100517}
518
Pau Espin Pedrol0b5997e2022-10-21 14:10:41 +0200519/* Set/update the MS object TLLI based on knowledge gained from the SGSN side (Downlink direction) */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100520bool ms_confirm_tlli(struct GprsMs *ms, uint32_t tlli)
521{
522 if (tlli == ms->tlli || tlli == ms->new_dl_tlli)
523 return false;
524
525 if (tlli != ms->new_ul_tlli) {
526 /* The MS has not sent a message with the new TLLI, which may
527 * happen according to the spec [TODO: add reference]. */
528
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200529 LOGP(DMS, LOGL_INFO,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100530 "The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
531 "partly confirmed\n", tlli);
532 /* Use the network's idea of TLLI as candidate, this does not
533 * change the result value of tlli() */
534 ms->new_dl_tlli = tlli;
535 return false;
536 }
537
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200538 LOGP(DMS, LOGL_INFO,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100539 "Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
540
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100541 ms_apply_tlli_change(ms, tlli);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100542
543 return true;
544}
545
546void ms_set_imsi(struct GprsMs *ms, const char *imsi)
547{
548 if (!imsi) {
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200549 LOGP(DMS, LOGL_ERROR, "Expected IMSI!\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100550 return;
551 }
552
553 if (imsi[0] && strlen(imsi) < 3) {
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200554 LOGP(DMS, LOGL_ERROR, "No valid IMSI '%s'!\n",
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100555 imsi);
556 return;
557 }
558
559 if (strcmp(imsi, ms->imsi) == 0)
560 return;
561
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200562 LOGP(DMS, LOGL_INFO,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100563 "Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
564 ms_tlli(ms), ms->imsi, imsi);
565
Pau Espin Pedrolcde18c52023-04-17 18:16:48 +0200566 struct GprsMs *old_ms = bts_get_ms_by_imsi(ms->bts, imsi);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100567 /* Check if we are going to store a different MS object with already
568 existing IMSI. This is probably a bug in code calling this function,
569 since it should take care of this explicitly */
570 if (old_ms) {
571 /* We cannot find ms->ms by IMSI since we know that it has a
572 * different IMSI */
573 OSMO_ASSERT(old_ms != ms);
574
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200575 LOGPMS(ms, DMS, LOGL_NOTICE,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100576 "IMSI '%s' was already assigned to another "
577 "MS object: TLLI = 0x%08x, that IMSI will be removed\n",
578 imsi, ms_tlli(old_ms));
579
580 ms_merge_and_clear_ms(ms, old_ms);
Pau Espin Pedrolb8ff74b2022-10-31 12:58:46 +0100581 /* old_ms may no longer be available here */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100582 }
583
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100584 /* Store the new IMSI: */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100585 osmo_strlcpy(ms->imsi, imsi, sizeof(ms->imsi));
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100586
587 /* Update TBF FSM names: */
588 if (ms->ul_tbf)
589 tbf_update_state_fsm_name(ul_tbf_as_tbf(ms->ul_tbf));
590 if (ms->dl_tbf)
591 tbf_update_state_fsm_name(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100592}
593
594void ms_set_ta(struct GprsMs *ms, uint8_t ta_)
595{
596 if (ta_ == ms->ta)
597 return;
598
599 if (gsm48_ta_is_valid(ta_)) {
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200600 LOGP(DMS, LOGL_INFO,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100601 "Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
602 ms_tlli(ms), ms->ta, ta_);
603 ms->ta = ta_;
604 } else
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200605 LOGP(DMS, LOGL_NOTICE,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100606 "MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
607 "value %d kept)\n", ms_tlli(ms), ta_, ms->ta);
608}
609
610void ms_set_ms_class(struct GprsMs *ms, uint8_t ms_class_)
611{
612 if (ms_class_ == ms->ms_class)
613 return;
614
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200615 LOGP(DMS, LOGL_INFO,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100616 "Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
617 ms_tlli(ms), ms->ms_class, ms_class_);
618
619 ms->ms_class = ms_class_;
620}
621
622void ms_set_egprs_ms_class(struct GprsMs *ms, uint8_t ms_class_)
623{
624 if (ms_class_ == ms->egprs_ms_class)
625 return;
626
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200627 LOGP(DMS, LOGL_INFO,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100628 "Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
629 ms_tlli(ms), ms->egprs_ms_class, ms_class_);
630
631 ms->egprs_ms_class = ms_class_;
632
633 if (!bts_max_mcs_ul(ms->bts) || !bts_max_mcs_dl(ms->bts)) {
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200634 LOGPMS(ms, DMS, LOGL_DEBUG,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100635 "Avoid enabling EGPRS because use of MCS is disabled: ul=%u dl=%u\n",
636 bts_max_mcs_ul(ms->bts), bts_max_mcs_dl(ms->bts));
637 return;
638 }
639
640 if (mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts))) &&
641 mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts))) &&
642 ms_mode(ms) != EGPRS)
643 {
644 ms_set_mode(ms, EGPRS_GMSK);
645 } else {
646 ms_set_mode(ms, EGPRS);
647 }
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200648 LOGPMS(ms, DMS, LOGL_INFO, "Enabled EGPRS, mode %s\n", mode_name(ms_mode(ms)));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100649}
650
651void ms_update_error_rate(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, int error_rate)
652{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100653 int64_t now;
654 enum CodingScheme max_cs_dl = ms_max_cs_dl(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100655 OSMO_ASSERT(max_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100656
657 if (error_rate < 0)
658 return;
659
660 now = now_msec();
661
662 /* TODO: Check for TBF direction */
663 /* TODO: Support different CS values for UL and DL */
664
665 ms->nack_rate_dl = error_rate;
666
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100667 if (error_rate > the_pcu->vty.cs_adj_upper_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100668 if (mcs_chan_code(ms->current_cs_dl) > 0) {
669 mcs_dec_kind(&ms->current_cs_dl, ms_mode(ms));
670 LOGP(DRLCMACDL, LOGL_INFO,
671 "MS (IMSI %s): High error rate %d%%, "
672 "reducing CS level to %s\n",
673 ms_imsi(ms), error_rate, mcs_name(ms->current_cs_dl));
674 ms->last_cs_not_low = now;
675 }
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100676 } else if (error_rate < the_pcu->vty.cs_adj_lower_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100677 if (ms->current_cs_dl < max_cs_dl) {
678 if (now - ms->last_cs_not_low > 1000) {
679 mcs_inc_kind(&ms->current_cs_dl, ms_mode(ms));
680
681 LOGP(DRLCMACDL, LOGL_INFO,
682 "MS (IMSI %s): Low error rate %d%%, "
683 "increasing DL CS level to %s\n",
684 ms_imsi(ms), error_rate,
685 mcs_name(ms->current_cs_dl));
686 ms->last_cs_not_low = now;
687 } else {
688 LOGP(DRLCMACDL, LOGL_DEBUG,
689 "MS (IMSI %s): Low error rate %d%%, "
690 "ignored (within blocking period)\n",
691 ms_imsi(ms), error_rate);
692 }
693 }
694 } else {
695 LOGP(DRLCMACDL, LOGL_DEBUG,
696 "MS (IMSI %s): Medium error rate %d%%, ignored\n",
697 ms_imsi(ms), error_rate);
698 ms->last_cs_not_low = now;
699 }
700}
701
702enum CodingScheme ms_max_cs_ul(const struct GprsMs *ms)
703{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100704 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100705 OSMO_ASSERT(ms->bts != NULL);
706
707 if (mcs_is_gprs(ms->current_cs_ul)) {
708 if (!bts_max_cs_ul(ms->bts)) {
709 return CS4;
710 }
711
712 return mcs_get_gprs_by_num(bts_max_cs_ul(ms->bts));
713 }
714
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100715 cs = mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts));
716 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
717 cs = MCS4;
718 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100719}
720
721void ms_set_current_cs_dl(struct GprsMs *ms, enum CodingScheme scheme)
722{
723 ms->current_cs_dl = scheme;
724}
725
726enum CodingScheme ms_max_cs_dl(const struct GprsMs *ms)
727{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100728 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100729 OSMO_ASSERT(ms->bts != NULL);
730
731 if (mcs_is_gprs(ms->current_cs_dl)) {
732 if (!bts_max_cs_dl(ms->bts)) {
733 return CS4;
734 }
735
736 return mcs_get_gprs_by_num(bts_max_cs_dl(ms->bts));
737 }
738
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100739 cs = mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts));
740 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
741 cs = MCS4;
742 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100743}
744
745void ms_update_cs_ul(struct GprsMs *ms, const struct pcu_l1_meas *meas)
746{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100747 enum CodingScheme max_cs_ul = ms_max_cs_ul(ms);
748
749 int old_link_qual;
750 int low;
751 int high;
752 enum CodingScheme new_cs_ul = ms->current_cs_ul;
753 uint8_t current_cs = mcs_chan_code(ms->current_cs_ul);
754
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100755 if (!max_cs_ul) {
756 LOGP(DRLCMACMEAS, LOGL_ERROR,
757 "max_cs_ul cannot be derived (current UL CS: %s)\n",
758 mcs_name(ms->current_cs_ul));
759 return;
760 }
761
762 if (!ms->current_cs_ul) {
763 LOGP(DRLCMACMEAS, LOGL_ERROR,
764 "Unable to update UL (M)CS because it's not set: %s\n",
765 mcs_name(ms->current_cs_ul));
766 return;
767 }
768
769 if (!meas->have_link_qual) {
770 LOGP(DRLCMACMEAS, LOGL_ERROR,
771 "Unable to update UL (M)CS %s because we don't have link quality measurements.\n",
772 mcs_name(ms->current_cs_ul));
773 return;
774 }
775
776 if (mcs_is_gprs(ms->current_cs_ul)) {
777 if (current_cs >= MAX_GPRS_CS)
778 current_cs = MAX_GPRS_CS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100779 low = the_pcu->vty.cs_lqual_ranges[current_cs].low;
780 high = the_pcu->vty.cs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100781 } else if (mcs_is_edge(ms->current_cs_ul)) {
782 if (current_cs >= MAX_EDGE_MCS)
783 current_cs = MAX_EDGE_MCS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100784 low = the_pcu->vty.mcs_lqual_ranges[current_cs].low;
785 high = the_pcu->vty.mcs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100786 } else {
787 LOGP(DRLCMACMEAS, LOGL_ERROR,
788 "Unable to update UL (M)CS because it's neither GPRS nor EDGE: %s\n",
789 mcs_name(ms->current_cs_ul));
790 return;
791 }
792
793 /* To avoid rapid changes of the coding scheme, we also take
794 * the old link quality value into account (if present). */
795 if (ms->l1_meas.have_link_qual)
796 old_link_qual = ms->l1_meas.link_qual;
797 else
798 old_link_qual = meas->link_qual;
799
800 if (meas->link_qual < low && old_link_qual < low)
801 mcs_dec_kind(&new_cs_ul, ms_mode(ms));
802 else if (meas->link_qual > high && old_link_qual > high &&
803 ms->current_cs_ul < max_cs_ul)
804 mcs_inc_kind(&new_cs_ul, ms_mode(ms));
805
806 if (ms->current_cs_ul != new_cs_ul) {
807 LOGPMS(ms, DRLCMACMEAS, LOGL_INFO,
808 "Link quality %ddB (old %ddB) left window [%d, %d], "
809 "modifying uplink CS level: %s -> %s\n",
810 meas->link_qual, old_link_qual,
811 low, high,
812 mcs_name(ms->current_cs_ul), mcs_name(new_cs_ul));
813
814 ms->current_cs_ul = new_cs_ul;
815 }
816}
817
818void ms_update_l1_meas(struct GprsMs *ms, const struct pcu_l1_meas *meas)
819{
820 unsigned i;
821
822 ms_update_cs_ul(ms, meas);
823
824 if (meas->have_rssi)
825 pcu_l1_meas_set_rssi(&ms->l1_meas, meas->rssi);
826 if (meas->have_bto)
827 pcu_l1_meas_set_bto(&ms->l1_meas, meas->bto);
828 if (meas->have_ber)
829 pcu_l1_meas_set_ber(&ms->l1_meas, meas->ber);
830 if (meas->have_link_qual)
831 pcu_l1_meas_set_link_qual(&ms->l1_meas, meas->link_qual);
832
833 if (meas->have_ms_rx_qual)
834 pcu_l1_meas_set_ms_rx_qual(&ms->l1_meas, meas->ms_rx_qual);
835 if (meas->have_ms_c_value)
836 pcu_l1_meas_set_ms_c_value(&ms->l1_meas, meas->ms_c_value);
837 if (meas->have_ms_sign_var)
838 pcu_l1_meas_set_ms_sign_var(&ms->l1_meas, meas->ms_sign_var);
839
840 if (meas->have_ms_i_level) {
841 for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
842 if (meas->ts[i].have_ms_i_level)
843 pcu_l1_meas_set_ms_i_level(&ms->l1_meas, i, meas->ts[i].ms_i_level);
844 else
845 ms->l1_meas.ts[i].have_ms_i_level = 0;
846 }
847 }
848}
849
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100850/* req_mcs_kind acts as a set filter, where EGPRS means any and GPRS is the most restrictive */
851enum CodingScheme ms_current_cs_dl(const struct GprsMs *ms, enum mcs_kind req_mcs_kind)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100852{
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100853 enum CodingScheme orig_cs = ms->current_cs_dl;
854 struct gprs_rlcmac_bts *bts = ms->bts;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100855 size_t unencoded_octets;
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100856 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100857
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100858 /* It could be that a TBF requests a GPRS CS despite the MS currently
859 being upgraded to EGPRS (hence reporting MCS). That could happen
860 because the TBF was created early in the process where we didn't have
861 yet enough information about the MS, and only AFTER it was created we
862 upgraded the MS to be EGPRS capable.
863 As a result, when the MS is queried for the target CS here, we could be
864 returning an MCS despite the current TBF being established as GPRS,
865 but we rather stick to the TBF type we assigned to the MS rather than
866 magically sending EGPRS data blocks to a GPRS TBF.
867 It could also be that the caller requests specific MCS kind
868 explicitly too due to scheduling restrictions (GPRS+EGPRS multiplexing). */
869 if (req_mcs_kind == EGPRS_GMSK && mcs_is_edge(orig_cs) && orig_cs > MCS4) {
870 cs = bts_cs_dl_is_supported(bts, MCS4) ? MCS4 :
871 bts_cs_dl_is_supported(bts, MCS3) ? MCS3 :
872 bts_cs_dl_is_supported(bts, MCS2) ? MCS2 :
873 MCS1;
874 } else if (req_mcs_kind == GPRS && mcs_is_edge(orig_cs)) { /* GPRS */
875 int i;
876 cs = orig_cs > MCS4 ? MCS4 : orig_cs;
877 cs -= (MCS1 - CS1); /* MCSx -> CSx */
878 /* Find suitable CS starting from equivalent MCS which is supported by BTS: */
879 for (i = mcs_chan_code(cs); !bts_cs_dl_is_supported(bts, CS1 + i); i--);
880 OSMO_ASSERT(i >= 0 && i <= 3); /* CS1 is always supported */
881 cs = CS1 + i;
882 } else {
883 cs = orig_cs;
884 }
885
886 if (orig_cs != cs)
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200887 LOGPMS(ms, DMS, LOGL_INFO, "MS (mode=%s) suggests transmitting "
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100888 "DL %s, downgrade to %s in order to match TBF & scheduler requirements\n",
889 mode_name(ms_mode(ms)), mcs_name(orig_cs), mcs_name(cs));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100890
891 unencoded_octets = llc_queue_octets(&ms->llc_queue);
892
893 /* If the DL TBF is active, add number of unencoded chunk octets */
894 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200895 unencoded_octets += llc_chunk_size(tbf_llc(dl_tbf_as_tbf(ms->dl_tbf)));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100896
897 /* There are many unencoded octets, don't reduce */
Pau Espin Pedrolad79b852021-01-14 13:20:55 +0100898 if (unencoded_octets >= the_pcu->vty.cs_downgrade_threshold)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100899 return cs;
900
901 /* RF conditions are good, don't reduce */
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100902 if (ms->nack_rate_dl < the_pcu->vty.cs_adj_lower_limit)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100903 return cs;
904
905 /* The throughput would probably be better if the CS level was reduced */
906 mcs_dec_kind(&cs, ms_mode(ms));
907
908 /* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
909 if (cs == CS2)
910 mcs_dec_kind(&cs, ms_mode(ms));
911
912 return cs;
913}
914
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100915struct gprs_rlcmac_pdch *ms_first_common_ts(const struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100916{
Pau Espin Pedrol345d9ad2022-12-12 19:22:44 +0100917 return ms->first_common_ts;
918}
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100919
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100920void ms_set_first_common_ts(struct GprsMs *ms, struct gprs_rlcmac_pdch *pdch)
Pau Espin Pedrol345d9ad2022-12-12 19:22:44 +0100921{
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100922 OSMO_ASSERT(pdch);
923 ms->first_common_ts = pdch;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100924}
925
926uint8_t ms_dl_slots(const struct GprsMs *ms)
927{
928 uint8_t slots = 0;
929
930 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200931 slots |= tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100932
933 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200934 slots |= tbf_dl_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100935
936 return slots;
937}
938
939uint8_t ms_ul_slots(const struct GprsMs *ms)
940{
941 uint8_t slots = 0;
942
943 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200944 slots |= tbf_ul_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100945
946 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200947 slots |= tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100948
949 return slots;
950}
951
952uint8_t ms_current_pacch_slots(const struct GprsMs *ms)
953{
954 uint8_t slots = 0;
955
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200956 bool is_dl_active = ms->dl_tbf && tbf_is_tfi_assigned(dl_tbf_as_tbf(ms->dl_tbf));
957 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 +0100958
959 if (!is_dl_active && !is_ul_active)
960 return 0;
961
962 /* see TS 44.060, 8.1.1.2.2 */
963 if (is_dl_active && !is_ul_active)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200964 slots = tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100965 else if (!is_dl_active && is_ul_active)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200966 slots = tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100967 else
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200968 slots = tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf)) &
969 tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100970
971 /* Assume a multislot class 1 device */
972 /* TODO: For class 2 devices, this could be removed */
973 slots = pcu_lsb(slots);
974
975 return slots;
976}
977
978void ms_set_reserved_slots(struct GprsMs *ms, struct gprs_rlcmac_trx *trx,
979 uint8_t ul_slots, uint8_t dl_slots)
980{
981 if (ms->current_trx) {
982 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
983 ms->reserved_dl_slots);
984 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
985 ms->reserved_ul_slots);
986 ms->reserved_dl_slots = 0;
987 ms->reserved_ul_slots = 0;
988 }
989 ms->current_trx = trx;
990 if (trx) {
991 ms->reserved_dl_slots = dl_slots;
992 ms->reserved_ul_slots = ul_slots;
993 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
994 ms->reserved_dl_slots);
995 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
996 ms->reserved_ul_slots);
997 }
998}
999
1000struct gprs_rlcmac_tbf *ms_tbf(const struct GprsMs *ms, enum gprs_rlcmac_tbf_direction dir)
1001{
1002 switch (dir) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +02001003 case GPRS_RLCMAC_DL_TBF: return dl_tbf_as_tbf(ms->dl_tbf);
1004 case GPRS_RLCMAC_UL_TBF: return ul_tbf_as_tbf(ms->ul_tbf);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +01001005 }
1006
1007 return NULL;
1008}
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001009
Pau Espin Pedrol3547d642022-10-21 15:00:08 +02001010const char *ms_name(const struct GprsMs *ms)
1011{
1012 static char _ms_name_buf[128];
1013 return ms_name_buf(ms, _ms_name_buf, sizeof(_ms_name_buf));
1014}
1015
1016char *ms_name_buf(const struct GprsMs *ms, char *buf, unsigned int buf_size)
1017{
Pau Espin Pedrol94f82582022-11-03 14:16:17 +01001018 struct osmo_strbuf sb = { .buf = buf, .len = buf_size };
1019 uint32_t tlli = ms_tlli(ms);
1020
1021 OSMO_STRBUF_PRINTF(sb, "MS(");
1022 if (ms_imsi_is_valid(ms))
1023 OSMO_STRBUF_PRINTF(sb, "IMSI-%s:", ms_imsi(ms));
1024 if (tlli != GSM_RESERVED_TMSI)
1025 OSMO_STRBUF_PRINTF(sb, "TLLI-0x%08x:", tlli);
1026 OSMO_STRBUF_PRINTF(sb, "TA-%" PRIu8 ":MSCLS-%" PRIu8 "-%" PRIu8,
1027 ms_ta(ms), ms_ms_class(ms), ms_egprs_ms_class(ms));
1028 if (ms->ul_tbf)
1029 OSMO_STRBUF_PRINTF(sb, ":UL");
1030 if (ms->dl_tbf)
1031 OSMO_STRBUF_PRINTF(sb, ":DL");
1032
1033 OSMO_STRBUF_PRINTF(sb, ")");
Pau Espin Pedrol3547d642022-10-21 15:00:08 +02001034 return buf;
1035}
1036
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001037int ms_nacc_start(struct GprsMs *ms, Packet_Cell_Change_Notification_t *notif)
1038{
1039 if (!ms->nacc)
1040 ms->nacc = nacc_fsm_alloc(ms);
1041 if (!ms->nacc)
1042 return -EINVAL;
1043 return osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_RX_CELL_CHG_NOTIFICATION, notif);
1044}
1045
1046bool ms_nacc_rts(const struct GprsMs *ms)
1047{
1048 if (!ms->nacc)
1049 return false;
1050 if (ms->nacc->fi->state == NACC_ST_TX_NEIGHBOUR_DATA ||
1051 ms->nacc->fi->state == NACC_ST_TX_CELL_CHG_CONTINUE)
1052 return true;
1053 return false;
1054}
1055
Pau Espin Pedrol5ba3ef92022-12-12 18:02:25 +01001056struct msgb *ms_nacc_create_rlcmac_msg(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf,
1057 const struct gprs_rlcmac_pdch *pdch, uint32_t fn)
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001058{
1059 int rc;
1060 struct nacc_ev_create_rlcmac_msg_ctx data_ctx;
1061
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001062 data_ctx = (struct nacc_ev_create_rlcmac_msg_ctx) {
1063 .tbf = tbf,
Pau Espin Pedrol5ba3ef92022-12-12 18:02:25 +01001064 .pdch = pdch,
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001065 .fn = fn,
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001066 .msg = NULL,
1067 };
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001068
1069 rc = osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_CREATE_RLCMAC_MSG, &data_ctx);
1070 if (rc != 0 || !data_ctx.msg)
1071 return NULL;
1072 return data_ctx.msg;
1073}
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001074
1075static void ms_start_llc_timer(struct GprsMs *ms)
1076{
1077 if (the_pcu->vty.llc_idle_ack_csec > 0) {
1078 struct timespec tv;
1079 csecs_to_timespec(the_pcu->vty.llc_idle_ack_csec, &tv);
1080 osmo_timer_schedule(&ms->llc_timer, tv.tv_sec, tv.tv_nsec / 1000);
1081 }
1082}
1083
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001084/* Can we get to send a DL TBF ass to the MS? */
1085static bool ms_is_reachable_for_dl_ass(const struct GprsMs *ms)
1086{
1087 struct gprs_rlcmac_ul_tbf *ul_tbf = ms_ul_tbf(ms);
1088
1089 /* This function assumes it is called when no DL TBF is present */
1090 OSMO_ASSERT(!ms_dl_tbf(ms));
1091
1092 /* 3GPP TS 44.060 sec 7.1.3.1 Initiation of the Packet resource request procedure:
1093 * "Furthermore, the mobile station shall not respond to PACKET DOWNLINK ASSIGNMENT
1094 * or MULTIPLE TBF DOWNLINK ASSIGNMENT messages before contention resolution is
1095 * completed on the mobile station side." */
1096 /* The possible uplink TBF is used to trigger downlink assignment:
1097 * - If there is no uplink TBF the MS is potentially in packet idle mode
1098 * and hence assignment will be done over CCCH (PCH)
1099 * - If there's an uplink TBF but it is finished (waiting for last PKT
1100 * CTRL ACK after sending last Pkt UL ACK/NACK with FINAL_ACK=1, then we
1101 * have no ways to contact the MS right now. Assignment will be delayed
1102 * until PKT CTRL ACK is received and the TBF is released at the MS side
1103 * (then assignment goes through PCH).
1104 */
1105 if (!ul_tbf)
1106 return true;
1107 if (ul_tbf_contention_resolution_done(ul_tbf) &&
1108 !tbf_ul_ack_waiting_cnf_final_ack(ul_tbf))
1109 return true;
1110
1111 return false;
1112
1113}
1114
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001115/* Alloc a UL TBF to be assigned over PACCH. Called when an MS requests to
1116 * create a new UL TBF during the end of life of a previous UL TBF (or an SBA).
1117 * In summary, this TBF is allocated as a consequence of receiving a "Pkt
1118 * Resource Req" or "Pkt Ctrl Ack" from the MS.
1119 * See TS 44.060 9.3.2.4.2 "Non-extended uplink TBF mode".
1120 */
1121struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_pacch(struct GprsMs *ms, int8_t use_trx)
1122{
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001123 struct gprs_rlcmac_ul_tbf *ul_tbf;
Pau Espin Pedrol4d363912023-04-21 19:32:16 +02001124 const struct alloc_resources_req req = {
1125 .bts = ms->bts,
1126 .ms = ms,
1127 .direction = GPRS_RLCMAC_UL_TBF,
1128 .single = false,
1129 .use_trx = use_trx,
1130 };
1131 struct alloc_resources_res res = {};
1132 int rc;
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001133
Pau Espin Pedrol4d363912023-04-21 19:32:16 +02001134 rc = the_pcu->alloc_algorithm(&req, &res);
1135 if (rc < 0) {
1136 LOGPMS(ms, DTBF, LOGL_NOTICE,
1137 "Timeslot Allocation failed: trx = %d, single_slot = %d\n",
1138 req.use_trx, req.single);
1139 bts_do_rate_ctr_inc(ms->bts, CTR_TBF_ALLOC_FAIL);
1140 return NULL;
1141 }
1142
1143 ul_tbf = ul_tbf_alloc(ms->bts, ms);
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001144 if (!ul_tbf) {
Pau Espin Pedrol4d363912023-04-21 19:32:16 +02001145 LOGPMS(ms, DTBF, LOGL_NOTICE, "ul_tbf_alloc() failed\n");
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001146 /* Caller will most probably send a Imm Ass Reject after return */
1147 return NULL;
1148 }
Pau Espin Pedrol4d363912023-04-21 19:32:16 +02001149
1150 /* Update MS, really allocate the resources */
1151 if (res.reserved_ul_slots != ms_reserved_ul_slots(ms) ||
1152 res.reserved_dl_slots != ms_reserved_dl_slots(ms)) {
1153 /* The reserved slots have changed, update the MS */
1154 ms_set_reserved_slots(ms, res.trx, res.reserved_ul_slots, res.reserved_dl_slots);
1155 }
1156 ms_set_first_common_ts(ms, res.first_common_ts);
1157
1158 /* Apply allocated resources to TBF: */
1159 ul_tbf_apply_allocated_resources(ul_tbf, &res);
1160
1161 ms_attach_tbf(ms, ul_tbf_as_tbf(ul_tbf));
1162
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001163 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_PACCH, NULL);
1164 /* Contention resolution is considered to be done since TLLI is known in MS */
1165 return ul_tbf;
1166}
1167
1168/* Alloc a UL TBF to be assigned over AGCH. Used by request of a "One phase
1169 * packet access", where MS requested only 1 PDCH TS (TS 44.018 Table 9.1.8.1). */
1170struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_agch(struct GprsMs *ms)
1171{
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001172 struct gprs_rlcmac_ul_tbf *ul_tbf;
Pau Espin Pedrol4d363912023-04-21 19:32:16 +02001173 const struct alloc_resources_req req = {
1174 .bts = ms->bts,
1175 .ms = ms,
1176 .direction = GPRS_RLCMAC_UL_TBF,
1177 .single = true,
1178 .use_trx = -1,
1179 };
1180 struct alloc_resources_res res = {};
1181 int rc;
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001182
Pau Espin Pedrol4d363912023-04-21 19:32:16 +02001183 rc = the_pcu->alloc_algorithm(&req, &res);
1184 if (rc < 0) {
1185 LOGPMS(ms, DTBF, LOGL_NOTICE,
1186 "Timeslot Allocation failed: trx = %d, single_slot = %d\n",
1187 req.use_trx, req.single);
1188 bts_do_rate_ctr_inc(ms->bts, CTR_TBF_ALLOC_FAIL);
1189 return NULL;
1190 }
1191
1192 ul_tbf = ul_tbf_alloc(ms->bts, ms);
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001193 if (!ul_tbf) {
Pau Espin Pedrol4d363912023-04-21 19:32:16 +02001194 LOGPMS(ms, DTBF, LOGL_NOTICE, "ul_tbf_alloc() failed\n");
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001195 /* Caller will most probably send a Imm Ass Reject after return */
1196 return NULL;
1197 }
Pau Espin Pedrol4d363912023-04-21 19:32:16 +02001198
1199 /* Update MS, really allocate the resources */
1200 if (res.reserved_ul_slots != ms_reserved_ul_slots(ms) ||
1201 res.reserved_dl_slots != ms_reserved_dl_slots(ms)) {
1202 /* The reserved slots have changed, update the MS */
1203 ms_set_reserved_slots(ms, res.trx, res.reserved_ul_slots, res.reserved_dl_slots);
1204 }
1205 ms_set_first_common_ts(ms, res.first_common_ts);
1206
1207 /* Apply allocated resources to TBF: */
1208 ul_tbf_apply_allocated_resources(ul_tbf, &res);
1209
1210 ms_attach_tbf(ms, ul_tbf_as_tbf(ul_tbf));
1211
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001212 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_CCCH, NULL);
1213 return ul_tbf;
1214}
1215
Pau Espin Pedrola621d592022-12-13 17:35:04 +01001216/* Create a temporary dummy TBF to Tx a ImmAssReject if allocating a new one during
1217 * packet resource Request failed. This is similar as ul_tbf_alloc() but without
Pau Espin Pedrol4d363912023-04-21 19:32:16 +02001218 * calling alloc_algo (in charge of TFI/USF allocation), and reusing resources
Pau Espin Pedrola621d592022-12-13 17:35:04 +01001219 * from Packet Resource Request we received. See TS 44.060 sec 7.1.3.2.1 */
1220struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_rejected_pacch(struct GprsMs *ms, struct gprs_rlcmac_pdch *pdch)
1221{
1222 struct gprs_rlcmac_ul_tbf *ul_tbf;
Pau Espin Pedrol4d363912023-04-21 19:32:16 +02001223 struct alloc_resources_res fake_res = {
1224 .trx = pdch->trx,
1225 .first_common_ts = pdch,
1226 .reserved_ul_slots = 0,
1227 .reserved_dl_slots = 0,
1228 .ass_slots_mask = 0,
1229 .upgrade_to_multislot = false,
1230 .tfi = TBF_TFI_UNSET,
1231 .usf = {0},
1232 };
1233 ul_tbf = ul_tbf_alloc(ms->bts, ms);
Pau Espin Pedrola621d592022-12-13 17:35:04 +01001234 if (!ul_tbf)
1235 return NULL;
Pau Espin Pedrol4d363912023-04-21 19:32:16 +02001236
1237 /* The only one TS is the common, control TS */
1238 ms_set_first_common_ts(ms, pdch);
1239
1240 /* Apply fake resources to TBF, to attach it to the proper TRX/PDCH: */
1241 ul_tbf_apply_allocated_resources(ul_tbf, &fake_res);
1242
1243 ms_attach_tbf(ms, ul_tbf_as_tbf(ul_tbf));
1244
Pau Espin Pedrola621d592022-12-13 17:35:04 +01001245 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_PACCH, NULL);
1246 osmo_fsm_inst_dispatch(tbf_ul_ass_fi(ul_tbf_as_tbf(ul_tbf)), TBF_UL_ASS_EV_SCHED_ASS_REJ, NULL);
1247
1248 return ul_tbf;
1249}
1250
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001251/* A new DL-TBF is allocated and assigned through PACCH using "tbf".
1252 * "tbf" may be either a UL-TBF or a DL-TBF.
1253 * Note: This should be called only when MS is reachable, see ms_is_reachable_for_dl_ass().
1254 */
1255int ms_new_dl_tbf_assigned_on_pacch(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001256{
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001257 OSMO_ASSERT(tbf);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001258 struct gprs_rlcmac_dl_tbf *dl_tbf;
Pau Espin Pedrol4d363912023-04-21 19:32:16 +02001259 const struct alloc_resources_req req = {
1260 .bts = ms->bts,
1261 .ms = ms,
1262 .direction = GPRS_RLCMAC_DL_TBF,
1263 .single = false,
1264 .use_trx = tbf_get_trx(tbf)->trx_no,
1265 };
1266 struct alloc_resources_res res = {};
1267 int rc;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001268
Pau Espin Pedrol4d363912023-04-21 19:32:16 +02001269 rc = the_pcu->alloc_algorithm(&req, &res);
1270 if (rc < 0) {
1271 LOGPMS(ms, DTBF, LOGL_NOTICE,
1272 "Timeslot Allocation failed: trx = %d, single_slot = %d\n",
1273 req.use_trx, req.single);
1274 bts_do_rate_ctr_inc(ms->bts, CTR_TBF_ALLOC_FAIL);
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001275 return -EBUSY;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001276 }
1277
Pau Espin Pedrol4d363912023-04-21 19:32:16 +02001278 dl_tbf = dl_tbf_alloc(ms->bts, ms);
1279 if (!dl_tbf) {
1280 LOGPMS(ms, DTBF, LOGL_NOTICE, "dl_tbf_alloc() failed\n");
1281 return -1;
1282 }
1283
1284 /* Update MS, really allocate the resources */
1285 if (res.reserved_ul_slots != ms_reserved_ul_slots(ms) ||
1286 res.reserved_dl_slots != ms_reserved_dl_slots(ms)) {
1287 /* The reserved slots have changed, update the MS */
1288 ms_set_reserved_slots(ms, res.trx, res.reserved_ul_slots, res.reserved_dl_slots);
1289 }
1290 ms_set_first_common_ts(ms, res.first_common_ts);
1291
1292 /* Apply allocated resources to TBF: */
1293 dl_tbf_apply_allocated_resources(dl_tbf, &res);
1294
1295 ms_attach_tbf(ms, dl_tbf_as_tbf(dl_tbf));
1296
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001297 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START (PACCH)\n");
1298 dl_tbf_trigger_ass_on_pacch(dl_tbf, tbf);
1299 return 0;
1300}
1301
1302/* A new DL-TBF is allocated and assigned through PCH.
1303 * Note: This should be called only when MS is reachable, see ms_is_reachable_for_dl_ass().
1304 */
1305int ms_new_dl_tbf_assigned_on_pch(struct GprsMs *ms)
1306{
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001307 struct gprs_rlcmac_dl_tbf *dl_tbf;
Pau Espin Pedrol4d363912023-04-21 19:32:16 +02001308 const struct alloc_resources_req req = {
1309 .bts = ms->bts,
1310 .ms = ms,
1311 .direction = GPRS_RLCMAC_DL_TBF,
1312 .single = true,
1313 .use_trx = -1,
1314 };
1315 struct alloc_resources_res res = {};
1316 int rc;
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001317
Pau Espin Pedrol4d363912023-04-21 19:32:16 +02001318 rc = the_pcu->alloc_algorithm(&req, &res);
1319 if (rc < 0) {
1320 LOGPMS(ms, DTBF, LOGL_NOTICE,
1321 "Timeslot Allocation failed: trx = %d, single_slot = %d\n",
1322 req.use_trx, req.single);
1323 bts_do_rate_ctr_inc(ms->bts, CTR_TBF_ALLOC_FAIL);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001324 return -EBUSY;
1325 }
1326
Pau Espin Pedrol4d363912023-04-21 19:32:16 +02001327 dl_tbf = dl_tbf_alloc(ms->bts, ms);
1328 if (!dl_tbf) {
1329 LOGPMS(ms, DTBF, LOGL_NOTICE, "dl_tbf_alloc() failed\n");
1330 return -1;
1331 }
1332
1333 /* Update MS, really allocate the resources */
1334 if (res.reserved_ul_slots != ms_reserved_ul_slots(ms) ||
1335 res.reserved_dl_slots != ms_reserved_dl_slots(ms)) {
1336 /* The reserved slots have changed, update the MS */
1337 ms_set_reserved_slots(ms, res.trx, res.reserved_ul_slots, res.reserved_dl_slots);
1338 }
1339 ms_set_first_common_ts(ms, res.first_common_ts);
1340
1341 /* Apply allocated resources to TBF: */
1342 dl_tbf_apply_allocated_resources(dl_tbf, &res);
1343
1344 ms_attach_tbf(ms, dl_tbf_as_tbf(dl_tbf));
1345
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001346 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START (PCH)\n");
1347 dl_tbf_trigger_ass_on_pch(dl_tbf);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001348 return 0;
1349}
1350
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001351int ms_append_llc_dl_data(struct GprsMs *ms, uint16_t pdu_delay_csec, const uint8_t *data, uint16_t len)
1352{
1353 struct timespec expire_time;
1354 struct gprs_rlcmac_dl_tbf *dl_tbf;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001355 int rc = 0;
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001356
1357 LOGPMS(ms, DTBFDL, LOGL_DEBUG, "appending %u bytes to DL LLC queue\n", len);
1358
1359 struct msgb *llc_msg = msgb_alloc(len, "llc_pdu_queue");
1360 if (!llc_msg)
1361 return -ENOMEM;
1362
1363 llc_queue_calc_pdu_lifetime(ms->bts, pdu_delay_csec, &expire_time);
1364 memcpy(msgb_put(llc_msg, len), data, len);
1365 llc_queue_enqueue(ms_llc_queue(ms), llc_msg, &expire_time);
1366 ms_start_llc_timer(ms);
1367
1368 dl_tbf = ms_dl_tbf(ms);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001369 if (dl_tbf) {
1370 if (tbf_state(dl_tbf_as_tbf_const(dl_tbf)) == TBF_ST_WAIT_RELEASE) {
1371 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "in WAIT RELEASE state (T3193), so reuse TBF\n");
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001372 rc = ms_new_dl_tbf_assigned_on_pacch(ms, dl_tbf_as_tbf(dl_tbf));
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001373 }
1374 } else {
1375 /* Check if we can create a DL TBF to start sending the enqueued
1376 * data. Otherwise it will be triggered later when it is reachable
Pau Espin Pedrol84b0ebc2023-04-19 18:45:21 +02001377 * again. */
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001378 if (ms_is_reachable_for_dl_ass(ms)) {
1379 if (ms_ul_tbf(ms))
1380 rc = ms_new_dl_tbf_assigned_on_pacch(ms, ul_tbf_as_tbf(ms_ul_tbf(ms)));
1381 else
1382 rc = ms_new_dl_tbf_assigned_on_pch(ms);
1383 }
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001384 }
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001385 return rc;
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001386}