blob: 0b062c1c18af1fbe1eb4c6a0f42f78ee0384cbea [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
Pau Espin Pedrole1a89152023-04-28 14:46:32 +0200398 * ms_ref() taken to avoid use-after-free.
399 */
Pau Espin Pedrol5faedf32023-04-28 14:29:40 +0200400static void ms_reset(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100401{
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200402 LOGPMS(ms, DMS, LOGL_INFO, "Clearing MS object\n");
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200403 struct llist_item *pos;
404 struct gprs_rlcmac_tbf *tbf;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100405
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200406 tbf = ul_tbf_as_tbf(ms_ul_tbf(ms));
407 if (tbf && !tbf_timers_pending(tbf, T_MAX))
408 tbf_free(tbf);
409 tbf = dl_tbf_as_tbf(ms_dl_tbf(ms));
410 if (tbf && !tbf_timers_pending(tbf, T_MAX))
411 tbf_free(tbf);
412
Pau Espin Pedrol7a491b92023-04-28 14:45:39 +0200413 while ((pos = llist_first_entry_or_null(&ms->old_tbfs, struct llist_item, list))) {
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200414 tbf = (struct gprs_rlcmac_tbf *)pos->entry;
415 if (!tbf_timers_pending(tbf, T_MAX))
416 tbf_free(tbf);
417 }
418
419 /* Flag it with invalid data so that it cannot be looked up anymore and
Pau Espin Pedrolb53230a2023-04-20 16:42:54 +0200420 * shows up specially if listed in VTY. Furthermore, it will also trigger
421 * immediate free() when it becomes idle: */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100422 ms->tlli = GSM_RESERVED_TMSI;
423 ms->new_dl_tlli = ms->tlli;
424 ms->new_ul_tlli = ms->tlli;
425 ms->imsi[0] = '\0';
426}
427
Pau Espin Pedrol8abe13c2022-10-21 18:49:48 +0200428/* This function should be called on the MS object of a TBF each time an RLCMAC
429 * block is received for it with TLLI information.
430 * Besides updating the TLLI field on the MS object, it also seeks for other MS
431 * objects in the store and merges them into the current MS object. The MS
432 * duplication happened because we don't learn the TLLI of the created TBF until
433 * a later point. */
434void ms_update_announced_tlli(struct GprsMs *ms, uint32_t tlli)
435{
436 struct GprsMs *old_ms = NULL;
437
438 if (tlli == GSM_RESERVED_TMSI)
439 return;
440
441 /* When the TLLI does not match the ms, check if there is another
442 * MS object that belongs to that TLLI and if yes make sure one of them
443 * gets deleted. */
444 if (!ms_check_tlli(ms, tlli))
Pau Espin Pedroleb0a0522023-04-17 16:33:35 +0200445 old_ms = bts_get_ms_by_tlli(ms->bts, tlli, GSM_RESERVED_TMSI);
Pau Espin Pedrol8abe13c2022-10-21 18:49:48 +0200446
447 ms_set_tlli(ms, tlli);
448
449 if (old_ms)
450 ms_merge_and_clear_ms(ms, old_ms);
451 /* old_ms may no longer be available here */
452}
453
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100454/* Merge 'old_ms' object into 'ms' object.
455 * 'old_ms' may be freed during the call to this function, don't use the pointer to it afterwards */
456void ms_merge_and_clear_ms(struct GprsMs *ms, struct GprsMs *old_ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100457{
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100458 char old_ms_name[128];
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100459 OSMO_ASSERT(old_ms != ms);
Pau Espin Pedrol403e0482023-04-17 20:28:10 +0200460 ms_ref(old_ms, __func__);
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100461
462 ms_name_buf(old_ms, old_ms_name, sizeof(old_ms_name));
463
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200464 LOGPMS(ms, DMS, LOGL_INFO, "Merge MS: %s\n", old_ms_name);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100465
466 if (strlen(ms_imsi(ms)) == 0 && strlen(ms_imsi(old_ms)) != 0)
467 osmo_strlcpy(ms->imsi, ms_imsi(old_ms), sizeof(ms->imsi));
468
469 if (!ms_ms_class(ms) && ms_ms_class(old_ms))
470 ms_set_ms_class(ms, ms_ms_class(old_ms));
471
472 if (!ms_egprs_ms_class(ms) && ms_egprs_ms_class(old_ms))
473 ms_set_egprs_ms_class(ms, ms_egprs_ms_class(old_ms));
474
475 llc_queue_move_and_merge(&ms->llc_queue, &old_ms->llc_queue);
476
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100477 /* Clean up the old MS object */
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100478 ms_reset(old_ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100479
Pau Espin Pedrol403e0482023-04-17 20:28:10 +0200480 ms_unref(old_ms, __func__);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100481}
482
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100483/* Apply changes to the TLLI directly, used interally by functions below: */
484static void ms_apply_tlli_change(struct GprsMs *ms, uint32_t tlli)
485{
486 ms->tlli = tlli;
487 ms->new_dl_tlli = GSM_RESERVED_TMSI;
488 ms->new_ul_tlli = GSM_RESERVED_TMSI;
489
490 /* Update TBF FSM names: */
491 if (ms->ul_tbf)
492 tbf_update_state_fsm_name(ul_tbf_as_tbf(ms->ul_tbf));
493 if (ms->dl_tbf)
494 tbf_update_state_fsm_name(dl_tbf_as_tbf(ms->dl_tbf));
495}
496
Pau Espin Pedrol0b5997e2022-10-21 14:10:41 +0200497/* Set/update the MS object TLLI based on knowledge gained from the MS side (Uplink direction) */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100498void ms_set_tlli(struct GprsMs *ms, uint32_t tlli)
499{
500 if (tlli == ms->tlli || tlli == ms->new_ul_tlli)
501 return;
502
503 if (tlli != ms->new_dl_tlli) {
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200504 LOGP(DMS, LOGL_INFO,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100505 "Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
506 "not yet confirmed\n",
507 ms_tlli(ms), tlli);
508 ms->new_ul_tlli = tlli;
509 return;
510 }
511
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200512 LOGP(DMS, LOGL_INFO,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100513 "Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
514 "already confirmed partly\n",
515 ms->tlli, tlli);
516
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100517 ms_apply_tlli_change(ms, tlli);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100518}
519
Pau Espin Pedrol0b5997e2022-10-21 14:10:41 +0200520/* Set/update the MS object TLLI based on knowledge gained from the SGSN side (Downlink direction) */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100521bool ms_confirm_tlli(struct GprsMs *ms, uint32_t tlli)
522{
523 if (tlli == ms->tlli || tlli == ms->new_dl_tlli)
524 return false;
525
526 if (tlli != ms->new_ul_tlli) {
527 /* The MS has not sent a message with the new TLLI, which may
528 * happen according to the spec [TODO: add reference]. */
529
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200530 LOGP(DMS, LOGL_INFO,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100531 "The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
532 "partly confirmed\n", tlli);
533 /* Use the network's idea of TLLI as candidate, this does not
534 * change the result value of tlli() */
535 ms->new_dl_tlli = tlli;
536 return false;
537 }
538
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200539 LOGP(DMS, LOGL_INFO,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100540 "Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
541
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100542 ms_apply_tlli_change(ms, tlli);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100543
544 return true;
545}
546
547void ms_set_imsi(struct GprsMs *ms, const char *imsi)
548{
549 if (!imsi) {
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200550 LOGP(DMS, LOGL_ERROR, "Expected IMSI!\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100551 return;
552 }
553
554 if (imsi[0] && strlen(imsi) < 3) {
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200555 LOGP(DMS, LOGL_ERROR, "No valid IMSI '%s'!\n",
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100556 imsi);
557 return;
558 }
559
560 if (strcmp(imsi, ms->imsi) == 0)
561 return;
562
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200563 LOGP(DMS, LOGL_INFO,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100564 "Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
565 ms_tlli(ms), ms->imsi, imsi);
566
Pau Espin Pedrolcde18c52023-04-17 18:16:48 +0200567 struct GprsMs *old_ms = bts_get_ms_by_imsi(ms->bts, imsi);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100568 /* Check if we are going to store a different MS object with already
569 existing IMSI. This is probably a bug in code calling this function,
570 since it should take care of this explicitly */
571 if (old_ms) {
572 /* We cannot find ms->ms by IMSI since we know that it has a
573 * different IMSI */
574 OSMO_ASSERT(old_ms != ms);
575
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200576 LOGPMS(ms, DMS, LOGL_NOTICE,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100577 "IMSI '%s' was already assigned to another "
578 "MS object: TLLI = 0x%08x, that IMSI will be removed\n",
579 imsi, ms_tlli(old_ms));
580
581 ms_merge_and_clear_ms(ms, old_ms);
Pau Espin Pedrolb8ff74b2022-10-31 12:58:46 +0100582 /* old_ms may no longer be available here */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100583 }
584
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100585 /* Store the new IMSI: */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100586 osmo_strlcpy(ms->imsi, imsi, sizeof(ms->imsi));
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100587
588 /* Update TBF FSM names: */
589 if (ms->ul_tbf)
590 tbf_update_state_fsm_name(ul_tbf_as_tbf(ms->ul_tbf));
591 if (ms->dl_tbf)
592 tbf_update_state_fsm_name(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100593}
594
595void ms_set_ta(struct GprsMs *ms, uint8_t ta_)
596{
597 if (ta_ == ms->ta)
598 return;
599
600 if (gsm48_ta_is_valid(ta_)) {
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200601 LOGP(DMS, LOGL_INFO,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100602 "Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
603 ms_tlli(ms), ms->ta, ta_);
604 ms->ta = ta_;
605 } else
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200606 LOGP(DMS, LOGL_NOTICE,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100607 "MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
608 "value %d kept)\n", ms_tlli(ms), ta_, ms->ta);
609}
610
611void ms_set_ms_class(struct GprsMs *ms, uint8_t ms_class_)
612{
613 if (ms_class_ == ms->ms_class)
614 return;
615
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200616 LOGP(DMS, LOGL_INFO,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100617 "Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
618 ms_tlli(ms), ms->ms_class, ms_class_);
619
620 ms->ms_class = ms_class_;
621}
622
623void ms_set_egprs_ms_class(struct GprsMs *ms, uint8_t ms_class_)
624{
625 if (ms_class_ == ms->egprs_ms_class)
626 return;
627
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200628 LOGP(DMS, LOGL_INFO,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100629 "Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
630 ms_tlli(ms), ms->egprs_ms_class, ms_class_);
631
632 ms->egprs_ms_class = ms_class_;
633
634 if (!bts_max_mcs_ul(ms->bts) || !bts_max_mcs_dl(ms->bts)) {
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200635 LOGPMS(ms, DMS, LOGL_DEBUG,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100636 "Avoid enabling EGPRS because use of MCS is disabled: ul=%u dl=%u\n",
637 bts_max_mcs_ul(ms->bts), bts_max_mcs_dl(ms->bts));
638 return;
639 }
640
641 if (mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts))) &&
642 mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts))) &&
643 ms_mode(ms) != EGPRS)
644 {
645 ms_set_mode(ms, EGPRS_GMSK);
646 } else {
647 ms_set_mode(ms, EGPRS);
648 }
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200649 LOGPMS(ms, DMS, LOGL_INFO, "Enabled EGPRS, mode %s\n", mode_name(ms_mode(ms)));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100650}
651
652void ms_update_error_rate(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, int error_rate)
653{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100654 int64_t now;
655 enum CodingScheme max_cs_dl = ms_max_cs_dl(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100656 OSMO_ASSERT(max_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100657
658 if (error_rate < 0)
659 return;
660
661 now = now_msec();
662
663 /* TODO: Check for TBF direction */
664 /* TODO: Support different CS values for UL and DL */
665
666 ms->nack_rate_dl = error_rate;
667
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100668 if (error_rate > the_pcu->vty.cs_adj_upper_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100669 if (mcs_chan_code(ms->current_cs_dl) > 0) {
670 mcs_dec_kind(&ms->current_cs_dl, ms_mode(ms));
671 LOGP(DRLCMACDL, LOGL_INFO,
672 "MS (IMSI %s): High error rate %d%%, "
673 "reducing CS level to %s\n",
674 ms_imsi(ms), error_rate, mcs_name(ms->current_cs_dl));
675 ms->last_cs_not_low = now;
676 }
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100677 } else if (error_rate < the_pcu->vty.cs_adj_lower_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100678 if (ms->current_cs_dl < max_cs_dl) {
679 if (now - ms->last_cs_not_low > 1000) {
680 mcs_inc_kind(&ms->current_cs_dl, ms_mode(ms));
681
682 LOGP(DRLCMACDL, LOGL_INFO,
683 "MS (IMSI %s): Low error rate %d%%, "
684 "increasing DL CS level to %s\n",
685 ms_imsi(ms), error_rate,
686 mcs_name(ms->current_cs_dl));
687 ms->last_cs_not_low = now;
688 } else {
689 LOGP(DRLCMACDL, LOGL_DEBUG,
690 "MS (IMSI %s): Low error rate %d%%, "
691 "ignored (within blocking period)\n",
692 ms_imsi(ms), error_rate);
693 }
694 }
695 } else {
696 LOGP(DRLCMACDL, LOGL_DEBUG,
697 "MS (IMSI %s): Medium error rate %d%%, ignored\n",
698 ms_imsi(ms), error_rate);
699 ms->last_cs_not_low = now;
700 }
701}
702
703enum CodingScheme ms_max_cs_ul(const struct GprsMs *ms)
704{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100705 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100706 OSMO_ASSERT(ms->bts != NULL);
707
708 if (mcs_is_gprs(ms->current_cs_ul)) {
709 if (!bts_max_cs_ul(ms->bts)) {
710 return CS4;
711 }
712
713 return mcs_get_gprs_by_num(bts_max_cs_ul(ms->bts));
714 }
715
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100716 cs = mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts));
717 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
718 cs = MCS4;
719 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100720}
721
722void ms_set_current_cs_dl(struct GprsMs *ms, enum CodingScheme scheme)
723{
724 ms->current_cs_dl = scheme;
725}
726
727enum CodingScheme ms_max_cs_dl(const struct GprsMs *ms)
728{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100729 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100730 OSMO_ASSERT(ms->bts != NULL);
731
732 if (mcs_is_gprs(ms->current_cs_dl)) {
733 if (!bts_max_cs_dl(ms->bts)) {
734 return CS4;
735 }
736
737 return mcs_get_gprs_by_num(bts_max_cs_dl(ms->bts));
738 }
739
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100740 cs = mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts));
741 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
742 cs = MCS4;
743 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100744}
745
746void ms_update_cs_ul(struct GprsMs *ms, const struct pcu_l1_meas *meas)
747{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100748 enum CodingScheme max_cs_ul = ms_max_cs_ul(ms);
749
750 int old_link_qual;
751 int low;
752 int high;
753 enum CodingScheme new_cs_ul = ms->current_cs_ul;
754 uint8_t current_cs = mcs_chan_code(ms->current_cs_ul);
755
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100756 if (!max_cs_ul) {
757 LOGP(DRLCMACMEAS, LOGL_ERROR,
758 "max_cs_ul cannot be derived (current UL CS: %s)\n",
759 mcs_name(ms->current_cs_ul));
760 return;
761 }
762
763 if (!ms->current_cs_ul) {
764 LOGP(DRLCMACMEAS, LOGL_ERROR,
765 "Unable to update UL (M)CS because it's not set: %s\n",
766 mcs_name(ms->current_cs_ul));
767 return;
768 }
769
770 if (!meas->have_link_qual) {
771 LOGP(DRLCMACMEAS, LOGL_ERROR,
772 "Unable to update UL (M)CS %s because we don't have link quality measurements.\n",
773 mcs_name(ms->current_cs_ul));
774 return;
775 }
776
777 if (mcs_is_gprs(ms->current_cs_ul)) {
778 if (current_cs >= MAX_GPRS_CS)
779 current_cs = MAX_GPRS_CS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100780 low = the_pcu->vty.cs_lqual_ranges[current_cs].low;
781 high = the_pcu->vty.cs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100782 } else if (mcs_is_edge(ms->current_cs_ul)) {
783 if (current_cs >= MAX_EDGE_MCS)
784 current_cs = MAX_EDGE_MCS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100785 low = the_pcu->vty.mcs_lqual_ranges[current_cs].low;
786 high = the_pcu->vty.mcs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100787 } else {
788 LOGP(DRLCMACMEAS, LOGL_ERROR,
789 "Unable to update UL (M)CS because it's neither GPRS nor EDGE: %s\n",
790 mcs_name(ms->current_cs_ul));
791 return;
792 }
793
794 /* To avoid rapid changes of the coding scheme, we also take
795 * the old link quality value into account (if present). */
796 if (ms->l1_meas.have_link_qual)
797 old_link_qual = ms->l1_meas.link_qual;
798 else
799 old_link_qual = meas->link_qual;
800
801 if (meas->link_qual < low && old_link_qual < low)
802 mcs_dec_kind(&new_cs_ul, ms_mode(ms));
803 else if (meas->link_qual > high && old_link_qual > high &&
804 ms->current_cs_ul < max_cs_ul)
805 mcs_inc_kind(&new_cs_ul, ms_mode(ms));
806
807 if (ms->current_cs_ul != new_cs_ul) {
808 LOGPMS(ms, DRLCMACMEAS, LOGL_INFO,
809 "Link quality %ddB (old %ddB) left window [%d, %d], "
810 "modifying uplink CS level: %s -> %s\n",
811 meas->link_qual, old_link_qual,
812 low, high,
813 mcs_name(ms->current_cs_ul), mcs_name(new_cs_ul));
814
815 ms->current_cs_ul = new_cs_ul;
816 }
817}
818
819void ms_update_l1_meas(struct GprsMs *ms, const struct pcu_l1_meas *meas)
820{
821 unsigned i;
822
823 ms_update_cs_ul(ms, meas);
824
825 if (meas->have_rssi)
826 pcu_l1_meas_set_rssi(&ms->l1_meas, meas->rssi);
827 if (meas->have_bto)
828 pcu_l1_meas_set_bto(&ms->l1_meas, meas->bto);
829 if (meas->have_ber)
830 pcu_l1_meas_set_ber(&ms->l1_meas, meas->ber);
831 if (meas->have_link_qual)
832 pcu_l1_meas_set_link_qual(&ms->l1_meas, meas->link_qual);
833
834 if (meas->have_ms_rx_qual)
835 pcu_l1_meas_set_ms_rx_qual(&ms->l1_meas, meas->ms_rx_qual);
836 if (meas->have_ms_c_value)
837 pcu_l1_meas_set_ms_c_value(&ms->l1_meas, meas->ms_c_value);
838 if (meas->have_ms_sign_var)
839 pcu_l1_meas_set_ms_sign_var(&ms->l1_meas, meas->ms_sign_var);
840
841 if (meas->have_ms_i_level) {
842 for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
843 if (meas->ts[i].have_ms_i_level)
844 pcu_l1_meas_set_ms_i_level(&ms->l1_meas, i, meas->ts[i].ms_i_level);
845 else
846 ms->l1_meas.ts[i].have_ms_i_level = 0;
847 }
848 }
849}
850
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100851/* req_mcs_kind acts as a set filter, where EGPRS means any and GPRS is the most restrictive */
852enum CodingScheme ms_current_cs_dl(const struct GprsMs *ms, enum mcs_kind req_mcs_kind)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100853{
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100854 enum CodingScheme orig_cs = ms->current_cs_dl;
855 struct gprs_rlcmac_bts *bts = ms->bts;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100856 size_t unencoded_octets;
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100857 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100858
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100859 /* It could be that a TBF requests a GPRS CS despite the MS currently
860 being upgraded to EGPRS (hence reporting MCS). That could happen
861 because the TBF was created early in the process where we didn't have
862 yet enough information about the MS, and only AFTER it was created we
863 upgraded the MS to be EGPRS capable.
864 As a result, when the MS is queried for the target CS here, we could be
865 returning an MCS despite the current TBF being established as GPRS,
866 but we rather stick to the TBF type we assigned to the MS rather than
867 magically sending EGPRS data blocks to a GPRS TBF.
868 It could also be that the caller requests specific MCS kind
869 explicitly too due to scheduling restrictions (GPRS+EGPRS multiplexing). */
870 if (req_mcs_kind == EGPRS_GMSK && mcs_is_edge(orig_cs) && orig_cs > MCS4) {
871 cs = bts_cs_dl_is_supported(bts, MCS4) ? MCS4 :
872 bts_cs_dl_is_supported(bts, MCS3) ? MCS3 :
873 bts_cs_dl_is_supported(bts, MCS2) ? MCS2 :
874 MCS1;
875 } else if (req_mcs_kind == GPRS && mcs_is_edge(orig_cs)) { /* GPRS */
876 int i;
877 cs = orig_cs > MCS4 ? MCS4 : orig_cs;
878 cs -= (MCS1 - CS1); /* MCSx -> CSx */
879 /* Find suitable CS starting from equivalent MCS which is supported by BTS: */
880 for (i = mcs_chan_code(cs); !bts_cs_dl_is_supported(bts, CS1 + i); i--);
881 OSMO_ASSERT(i >= 0 && i <= 3); /* CS1 is always supported */
882 cs = CS1 + i;
883 } else {
884 cs = orig_cs;
885 }
886
887 if (orig_cs != cs)
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200888 LOGPMS(ms, DMS, LOGL_INFO, "MS (mode=%s) suggests transmitting "
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100889 "DL %s, downgrade to %s in order to match TBF & scheduler requirements\n",
890 mode_name(ms_mode(ms)), mcs_name(orig_cs), mcs_name(cs));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100891
892 unencoded_octets = llc_queue_octets(&ms->llc_queue);
893
894 /* If the DL TBF is active, add number of unencoded chunk octets */
895 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200896 unencoded_octets += llc_chunk_size(tbf_llc(dl_tbf_as_tbf(ms->dl_tbf)));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100897
898 /* There are many unencoded octets, don't reduce */
Pau Espin Pedrolad79b852021-01-14 13:20:55 +0100899 if (unencoded_octets >= the_pcu->vty.cs_downgrade_threshold)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100900 return cs;
901
902 /* RF conditions are good, don't reduce */
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100903 if (ms->nack_rate_dl < the_pcu->vty.cs_adj_lower_limit)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100904 return cs;
905
906 /* The throughput would probably be better if the CS level was reduced */
907 mcs_dec_kind(&cs, ms_mode(ms));
908
909 /* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
910 if (cs == CS2)
911 mcs_dec_kind(&cs, ms_mode(ms));
912
913 return cs;
914}
915
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100916struct gprs_rlcmac_pdch *ms_first_common_ts(const struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100917{
Pau Espin Pedrol345d9ad2022-12-12 19:22:44 +0100918 return ms->first_common_ts;
919}
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100920
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100921void ms_set_first_common_ts(struct GprsMs *ms, struct gprs_rlcmac_pdch *pdch)
Pau Espin Pedrol345d9ad2022-12-12 19:22:44 +0100922{
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100923 OSMO_ASSERT(pdch);
924 ms->first_common_ts = pdch;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100925}
926
927uint8_t ms_dl_slots(const struct GprsMs *ms)
928{
929 uint8_t slots = 0;
930
931 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200932 slots |= tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100933
934 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200935 slots |= tbf_dl_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100936
937 return slots;
938}
939
940uint8_t ms_ul_slots(const struct GprsMs *ms)
941{
942 uint8_t slots = 0;
943
944 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200945 slots |= tbf_ul_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100946
947 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200948 slots |= tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100949
950 return slots;
951}
952
953uint8_t ms_current_pacch_slots(const struct GprsMs *ms)
954{
955 uint8_t slots = 0;
956
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200957 bool is_dl_active = ms->dl_tbf && tbf_is_tfi_assigned(dl_tbf_as_tbf(ms->dl_tbf));
958 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 +0100959
960 if (!is_dl_active && !is_ul_active)
961 return 0;
962
963 /* see TS 44.060, 8.1.1.2.2 */
964 if (is_dl_active && !is_ul_active)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200965 slots = tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100966 else if (!is_dl_active && is_ul_active)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200967 slots = tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100968 else
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200969 slots = tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf)) &
970 tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100971
972 /* Assume a multislot class 1 device */
973 /* TODO: For class 2 devices, this could be removed */
974 slots = pcu_lsb(slots);
975
976 return slots;
977}
978
979void ms_set_reserved_slots(struct GprsMs *ms, struct gprs_rlcmac_trx *trx,
980 uint8_t ul_slots, uint8_t dl_slots)
981{
982 if (ms->current_trx) {
983 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
984 ms->reserved_dl_slots);
985 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
986 ms->reserved_ul_slots);
987 ms->reserved_dl_slots = 0;
988 ms->reserved_ul_slots = 0;
989 }
990 ms->current_trx = trx;
991 if (trx) {
992 ms->reserved_dl_slots = dl_slots;
993 ms->reserved_ul_slots = ul_slots;
994 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
995 ms->reserved_dl_slots);
996 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
997 ms->reserved_ul_slots);
998 }
999}
1000
1001struct gprs_rlcmac_tbf *ms_tbf(const struct GprsMs *ms, enum gprs_rlcmac_tbf_direction dir)
1002{
1003 switch (dir) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +02001004 case GPRS_RLCMAC_DL_TBF: return dl_tbf_as_tbf(ms->dl_tbf);
1005 case GPRS_RLCMAC_UL_TBF: return ul_tbf_as_tbf(ms->ul_tbf);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +01001006 }
1007
1008 return NULL;
1009}
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001010
Pau Espin Pedrol3547d642022-10-21 15:00:08 +02001011const char *ms_name(const struct GprsMs *ms)
1012{
1013 static char _ms_name_buf[128];
1014 return ms_name_buf(ms, _ms_name_buf, sizeof(_ms_name_buf));
1015}
1016
1017char *ms_name_buf(const struct GprsMs *ms, char *buf, unsigned int buf_size)
1018{
Pau Espin Pedrol94f82582022-11-03 14:16:17 +01001019 struct osmo_strbuf sb = { .buf = buf, .len = buf_size };
1020 uint32_t tlli = ms_tlli(ms);
1021
1022 OSMO_STRBUF_PRINTF(sb, "MS(");
1023 if (ms_imsi_is_valid(ms))
1024 OSMO_STRBUF_PRINTF(sb, "IMSI-%s:", ms_imsi(ms));
1025 if (tlli != GSM_RESERVED_TMSI)
1026 OSMO_STRBUF_PRINTF(sb, "TLLI-0x%08x:", tlli);
1027 OSMO_STRBUF_PRINTF(sb, "TA-%" PRIu8 ":MSCLS-%" PRIu8 "-%" PRIu8,
1028 ms_ta(ms), ms_ms_class(ms), ms_egprs_ms_class(ms));
1029 if (ms->ul_tbf)
1030 OSMO_STRBUF_PRINTF(sb, ":UL");
1031 if (ms->dl_tbf)
1032 OSMO_STRBUF_PRINTF(sb, ":DL");
1033
1034 OSMO_STRBUF_PRINTF(sb, ")");
Pau Espin Pedrol3547d642022-10-21 15:00:08 +02001035 return buf;
1036}
1037
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001038int ms_nacc_start(struct GprsMs *ms, Packet_Cell_Change_Notification_t *notif)
1039{
1040 if (!ms->nacc)
1041 ms->nacc = nacc_fsm_alloc(ms);
1042 if (!ms->nacc)
1043 return -EINVAL;
1044 return osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_RX_CELL_CHG_NOTIFICATION, notif);
1045}
1046
1047bool ms_nacc_rts(const struct GprsMs *ms)
1048{
1049 if (!ms->nacc)
1050 return false;
1051 if (ms->nacc->fi->state == NACC_ST_TX_NEIGHBOUR_DATA ||
1052 ms->nacc->fi->state == NACC_ST_TX_CELL_CHG_CONTINUE)
1053 return true;
1054 return false;
1055}
1056
Pau Espin Pedrol5ba3ef92022-12-12 18:02:25 +01001057struct msgb *ms_nacc_create_rlcmac_msg(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf,
1058 const struct gprs_rlcmac_pdch *pdch, uint32_t fn)
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001059{
1060 int rc;
1061 struct nacc_ev_create_rlcmac_msg_ctx data_ctx;
1062
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001063 data_ctx = (struct nacc_ev_create_rlcmac_msg_ctx) {
1064 .tbf = tbf,
Pau Espin Pedrol5ba3ef92022-12-12 18:02:25 +01001065 .pdch = pdch,
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001066 .fn = fn,
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001067 .msg = NULL,
1068 };
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001069
1070 rc = osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_CREATE_RLCMAC_MSG, &data_ctx);
1071 if (rc != 0 || !data_ctx.msg)
1072 return NULL;
1073 return data_ctx.msg;
1074}
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001075
1076static void ms_start_llc_timer(struct GprsMs *ms)
1077{
1078 if (the_pcu->vty.llc_idle_ack_csec > 0) {
1079 struct timespec tv;
1080 csecs_to_timespec(the_pcu->vty.llc_idle_ack_csec, &tv);
1081 osmo_timer_schedule(&ms->llc_timer, tv.tv_sec, tv.tv_nsec / 1000);
1082 }
1083}
1084
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001085/* Can we get to send a DL TBF ass to the MS? */
1086static bool ms_is_reachable_for_dl_ass(const struct GprsMs *ms)
1087{
1088 struct gprs_rlcmac_ul_tbf *ul_tbf = ms_ul_tbf(ms);
1089
1090 /* This function assumes it is called when no DL TBF is present */
1091 OSMO_ASSERT(!ms_dl_tbf(ms));
1092
1093 /* 3GPP TS 44.060 sec 7.1.3.1 Initiation of the Packet resource request procedure:
1094 * "Furthermore, the mobile station shall not respond to PACKET DOWNLINK ASSIGNMENT
1095 * or MULTIPLE TBF DOWNLINK ASSIGNMENT messages before contention resolution is
1096 * completed on the mobile station side." */
1097 /* The possible uplink TBF is used to trigger downlink assignment:
1098 * - If there is no uplink TBF the MS is potentially in packet idle mode
1099 * and hence assignment will be done over CCCH (PCH)
1100 * - If there's an uplink TBF but it is finished (waiting for last PKT
1101 * CTRL ACK after sending last Pkt UL ACK/NACK with FINAL_ACK=1, then we
1102 * have no ways to contact the MS right now. Assignment will be delayed
1103 * until PKT CTRL ACK is received and the TBF is released at the MS side
1104 * (then assignment goes through PCH).
1105 */
1106 if (!ul_tbf)
1107 return true;
1108 if (ul_tbf_contention_resolution_done(ul_tbf) &&
1109 !tbf_ul_ack_waiting_cnf_final_ack(ul_tbf))
1110 return true;
1111
1112 return false;
1113
1114}
1115
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001116/* Alloc a UL TBF to be assigned over PACCH. Called when an MS requests to
1117 * create a new UL TBF during the end of life of a previous UL TBF (or an SBA).
1118 * In summary, this TBF is allocated as a consequence of receiving a "Pkt
1119 * Resource Req" or "Pkt Ctrl Ack" from the MS.
1120 * See TS 44.060 9.3.2.4.2 "Non-extended uplink TBF mode".
1121 */
1122struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_pacch(struct GprsMs *ms, int8_t use_trx)
1123{
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001124 struct gprs_rlcmac_ul_tbf *ul_tbf;
Pau Espin Pedrol4d363912023-04-21 19:32:16 +02001125 const struct alloc_resources_req req = {
1126 .bts = ms->bts,
1127 .ms = ms,
1128 .direction = GPRS_RLCMAC_UL_TBF,
1129 .single = false,
1130 .use_trx = use_trx,
1131 };
1132 struct alloc_resources_res res = {};
1133 int rc;
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001134
Pau Espin Pedrol4d363912023-04-21 19:32:16 +02001135 rc = the_pcu->alloc_algorithm(&req, &res);
1136 if (rc < 0) {
1137 LOGPMS(ms, DTBF, LOGL_NOTICE,
1138 "Timeslot Allocation failed: trx = %d, single_slot = %d\n",
1139 req.use_trx, req.single);
1140 bts_do_rate_ctr_inc(ms->bts, CTR_TBF_ALLOC_FAIL);
1141 return NULL;
1142 }
1143
1144 ul_tbf = ul_tbf_alloc(ms->bts, ms);
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001145 if (!ul_tbf) {
Pau Espin Pedrol4d363912023-04-21 19:32:16 +02001146 LOGPMS(ms, DTBF, LOGL_NOTICE, "ul_tbf_alloc() failed\n");
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001147 /* Caller will most probably send a Imm Ass Reject after return */
1148 return NULL;
1149 }
Pau Espin Pedrol4d363912023-04-21 19:32:16 +02001150
1151 /* Update MS, really allocate the resources */
1152 if (res.reserved_ul_slots != ms_reserved_ul_slots(ms) ||
1153 res.reserved_dl_slots != ms_reserved_dl_slots(ms)) {
1154 /* The reserved slots have changed, update the MS */
1155 ms_set_reserved_slots(ms, res.trx, res.reserved_ul_slots, res.reserved_dl_slots);
1156 }
1157 ms_set_first_common_ts(ms, res.first_common_ts);
1158
1159 /* Apply allocated resources to TBF: */
1160 ul_tbf_apply_allocated_resources(ul_tbf, &res);
1161
1162 ms_attach_tbf(ms, ul_tbf_as_tbf(ul_tbf));
1163
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001164 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_PACCH, NULL);
1165 /* Contention resolution is considered to be done since TLLI is known in MS */
1166 return ul_tbf;
1167}
1168
1169/* Alloc a UL TBF to be assigned over AGCH. Used by request of a "One phase
1170 * packet access", where MS requested only 1 PDCH TS (TS 44.018 Table 9.1.8.1). */
1171struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_agch(struct GprsMs *ms)
1172{
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001173 struct gprs_rlcmac_ul_tbf *ul_tbf;
Pau Espin Pedrol4d363912023-04-21 19:32:16 +02001174 const struct alloc_resources_req req = {
1175 .bts = ms->bts,
1176 .ms = ms,
1177 .direction = GPRS_RLCMAC_UL_TBF,
1178 .single = true,
1179 .use_trx = -1,
1180 };
1181 struct alloc_resources_res res = {};
1182 int rc;
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001183
Pau Espin Pedrol4d363912023-04-21 19:32:16 +02001184 rc = the_pcu->alloc_algorithm(&req, &res);
1185 if (rc < 0) {
1186 LOGPMS(ms, DTBF, LOGL_NOTICE,
1187 "Timeslot Allocation failed: trx = %d, single_slot = %d\n",
1188 req.use_trx, req.single);
1189 bts_do_rate_ctr_inc(ms->bts, CTR_TBF_ALLOC_FAIL);
1190 return NULL;
1191 }
1192
1193 ul_tbf = ul_tbf_alloc(ms->bts, ms);
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001194 if (!ul_tbf) {
Pau Espin Pedrol4d363912023-04-21 19:32:16 +02001195 LOGPMS(ms, DTBF, LOGL_NOTICE, "ul_tbf_alloc() failed\n");
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001196 /* Caller will most probably send a Imm Ass Reject after return */
1197 return NULL;
1198 }
Pau Espin Pedrol4d363912023-04-21 19:32:16 +02001199
1200 /* Update MS, really allocate the resources */
1201 if (res.reserved_ul_slots != ms_reserved_ul_slots(ms) ||
1202 res.reserved_dl_slots != ms_reserved_dl_slots(ms)) {
1203 /* The reserved slots have changed, update the MS */
1204 ms_set_reserved_slots(ms, res.trx, res.reserved_ul_slots, res.reserved_dl_slots);
1205 }
1206 ms_set_first_common_ts(ms, res.first_common_ts);
1207
1208 /* Apply allocated resources to TBF: */
1209 ul_tbf_apply_allocated_resources(ul_tbf, &res);
1210
1211 ms_attach_tbf(ms, ul_tbf_as_tbf(ul_tbf));
1212
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001213 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_CCCH, NULL);
1214 return ul_tbf;
1215}
1216
Pau Espin Pedrola621d592022-12-13 17:35:04 +01001217/* Create a temporary dummy TBF to Tx a ImmAssReject if allocating a new one during
1218 * packet resource Request failed. This is similar as ul_tbf_alloc() but without
Pau Espin Pedrol4d363912023-04-21 19:32:16 +02001219 * calling alloc_algo (in charge of TFI/USF allocation), and reusing resources
Pau Espin Pedrola621d592022-12-13 17:35:04 +01001220 * from Packet Resource Request we received. See TS 44.060 sec 7.1.3.2.1 */
1221struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_rejected_pacch(struct GprsMs *ms, struct gprs_rlcmac_pdch *pdch)
1222{
1223 struct gprs_rlcmac_ul_tbf *ul_tbf;
Pau Espin Pedrol4d363912023-04-21 19:32:16 +02001224 struct alloc_resources_res fake_res = {
1225 .trx = pdch->trx,
1226 .first_common_ts = pdch,
1227 .reserved_ul_slots = 0,
1228 .reserved_dl_slots = 0,
1229 .ass_slots_mask = 0,
1230 .upgrade_to_multislot = false,
1231 .tfi = TBF_TFI_UNSET,
1232 .usf = {0},
1233 };
1234 ul_tbf = ul_tbf_alloc(ms->bts, ms);
Pau Espin Pedrola621d592022-12-13 17:35:04 +01001235 if (!ul_tbf)
1236 return NULL;
Pau Espin Pedrol4d363912023-04-21 19:32:16 +02001237
1238 /* The only one TS is the common, control TS */
1239 ms_set_first_common_ts(ms, pdch);
1240
1241 /* Apply fake resources to TBF, to attach it to the proper TRX/PDCH: */
1242 ul_tbf_apply_allocated_resources(ul_tbf, &fake_res);
1243
1244 ms_attach_tbf(ms, ul_tbf_as_tbf(ul_tbf));
1245
Pau Espin Pedrola621d592022-12-13 17:35:04 +01001246 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_PACCH, NULL);
1247 osmo_fsm_inst_dispatch(tbf_ul_ass_fi(ul_tbf_as_tbf(ul_tbf)), TBF_UL_ASS_EV_SCHED_ASS_REJ, NULL);
1248
1249 return ul_tbf;
1250}
1251
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001252/* A new DL-TBF is allocated and assigned through PACCH using "tbf".
1253 * "tbf" may be either a UL-TBF or a DL-TBF.
1254 * Note: This should be called only when MS is reachable, see ms_is_reachable_for_dl_ass().
1255 */
1256int ms_new_dl_tbf_assigned_on_pacch(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001257{
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001258 OSMO_ASSERT(tbf);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001259 struct gprs_rlcmac_dl_tbf *dl_tbf;
Pau Espin Pedrol4d363912023-04-21 19:32:16 +02001260 const struct alloc_resources_req req = {
1261 .bts = ms->bts,
1262 .ms = ms,
1263 .direction = GPRS_RLCMAC_DL_TBF,
1264 .single = false,
1265 .use_trx = tbf_get_trx(tbf)->trx_no,
1266 };
1267 struct alloc_resources_res res = {};
1268 int rc;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001269
Pau Espin Pedrol4d363912023-04-21 19:32:16 +02001270 rc = the_pcu->alloc_algorithm(&req, &res);
1271 if (rc < 0) {
1272 LOGPMS(ms, DTBF, LOGL_NOTICE,
1273 "Timeslot Allocation failed: trx = %d, single_slot = %d\n",
1274 req.use_trx, req.single);
1275 bts_do_rate_ctr_inc(ms->bts, CTR_TBF_ALLOC_FAIL);
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001276 return -EBUSY;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001277 }
1278
Pau Espin Pedrol4d363912023-04-21 19:32:16 +02001279 dl_tbf = dl_tbf_alloc(ms->bts, ms);
1280 if (!dl_tbf) {
1281 LOGPMS(ms, DTBF, LOGL_NOTICE, "dl_tbf_alloc() failed\n");
1282 return -1;
1283 }
1284
1285 /* Update MS, really allocate the resources */
1286 if (res.reserved_ul_slots != ms_reserved_ul_slots(ms) ||
1287 res.reserved_dl_slots != ms_reserved_dl_slots(ms)) {
1288 /* The reserved slots have changed, update the MS */
1289 ms_set_reserved_slots(ms, res.trx, res.reserved_ul_slots, res.reserved_dl_slots);
1290 }
1291 ms_set_first_common_ts(ms, res.first_common_ts);
1292
1293 /* Apply allocated resources to TBF: */
1294 dl_tbf_apply_allocated_resources(dl_tbf, &res);
1295
1296 ms_attach_tbf(ms, dl_tbf_as_tbf(dl_tbf));
1297
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001298 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START (PACCH)\n");
1299 dl_tbf_trigger_ass_on_pacch(dl_tbf, tbf);
1300 return 0;
1301}
1302
1303/* A new DL-TBF is allocated and assigned through PCH.
1304 * Note: This should be called only when MS is reachable, see ms_is_reachable_for_dl_ass().
1305 */
1306int ms_new_dl_tbf_assigned_on_pch(struct GprsMs *ms)
1307{
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001308 struct gprs_rlcmac_dl_tbf *dl_tbf;
Pau Espin Pedrol4d363912023-04-21 19:32:16 +02001309 const struct alloc_resources_req req = {
1310 .bts = ms->bts,
1311 .ms = ms,
1312 .direction = GPRS_RLCMAC_DL_TBF,
1313 .single = true,
1314 .use_trx = -1,
1315 };
1316 struct alloc_resources_res res = {};
1317 int rc;
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001318
Pau Espin Pedrol4d363912023-04-21 19:32:16 +02001319 rc = the_pcu->alloc_algorithm(&req, &res);
1320 if (rc < 0) {
1321 LOGPMS(ms, DTBF, LOGL_NOTICE,
1322 "Timeslot Allocation failed: trx = %d, single_slot = %d\n",
1323 req.use_trx, req.single);
1324 bts_do_rate_ctr_inc(ms->bts, CTR_TBF_ALLOC_FAIL);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001325 return -EBUSY;
1326 }
1327
Pau Espin Pedrol4d363912023-04-21 19:32:16 +02001328 dl_tbf = dl_tbf_alloc(ms->bts, ms);
1329 if (!dl_tbf) {
1330 LOGPMS(ms, DTBF, LOGL_NOTICE, "dl_tbf_alloc() failed\n");
1331 return -1;
1332 }
1333
1334 /* Update MS, really allocate the resources */
1335 if (res.reserved_ul_slots != ms_reserved_ul_slots(ms) ||
1336 res.reserved_dl_slots != ms_reserved_dl_slots(ms)) {
1337 /* The reserved slots have changed, update the MS */
1338 ms_set_reserved_slots(ms, res.trx, res.reserved_ul_slots, res.reserved_dl_slots);
1339 }
1340 ms_set_first_common_ts(ms, res.first_common_ts);
1341
1342 /* Apply allocated resources to TBF: */
1343 dl_tbf_apply_allocated_resources(dl_tbf, &res);
1344
1345 ms_attach_tbf(ms, dl_tbf_as_tbf(dl_tbf));
1346
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001347 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START (PCH)\n");
1348 dl_tbf_trigger_ass_on_pch(dl_tbf);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001349 return 0;
1350}
1351
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001352int ms_append_llc_dl_data(struct GprsMs *ms, uint16_t pdu_delay_csec, const uint8_t *data, uint16_t len)
1353{
1354 struct timespec expire_time;
1355 struct gprs_rlcmac_dl_tbf *dl_tbf;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001356 int rc = 0;
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001357
1358 LOGPMS(ms, DTBFDL, LOGL_DEBUG, "appending %u bytes to DL LLC queue\n", len);
1359
1360 struct msgb *llc_msg = msgb_alloc(len, "llc_pdu_queue");
1361 if (!llc_msg)
1362 return -ENOMEM;
1363
1364 llc_queue_calc_pdu_lifetime(ms->bts, pdu_delay_csec, &expire_time);
1365 memcpy(msgb_put(llc_msg, len), data, len);
1366 llc_queue_enqueue(ms_llc_queue(ms), llc_msg, &expire_time);
1367 ms_start_llc_timer(ms);
1368
1369 dl_tbf = ms_dl_tbf(ms);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001370 if (dl_tbf) {
1371 if (tbf_state(dl_tbf_as_tbf_const(dl_tbf)) == TBF_ST_WAIT_RELEASE) {
1372 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "in WAIT RELEASE state (T3193), so reuse TBF\n");
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001373 rc = ms_new_dl_tbf_assigned_on_pacch(ms, dl_tbf_as_tbf(dl_tbf));
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001374 }
1375 } else {
1376 /* Check if we can create a DL TBF to start sending the enqueued
1377 * data. Otherwise it will be triggered later when it is reachable
Pau Espin Pedrol84b0ebc2023-04-19 18:45:21 +02001378 * again. */
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001379 if (ms_is_reachable_for_dl_ass(ms)) {
1380 if (ms_ul_tbf(ms))
1381 rc = ms_new_dl_tbf_assigned_on_pacch(ms, ul_tbf_as_tbf(ms_ul_tbf(ms)));
1382 else
1383 rc = ms_new_dl_tbf_assigned_on_pch(ms);
1384 }
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001385 }
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001386 return rc;
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001387}