blob: 1cdbb984a108e87d2dfc0908b587f4a5c90e60b9 [file] [log] [blame]
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +01001/* pdch_ul_controller.c
2 *
3 * Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
4 * Author: Pau Espin Pedrol <pespin@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 Pedrol15c58ac2021-03-08 14:57:58 +010015 */
16
17#include <unistd.h>
18#include <talloc.h>
19
20#include "pdch_ul_controller.h"
21#include "bts.h"
22#include "sba.h"
23#include "pdch.h"
Pau Espin Pedrol99360a32021-03-09 17:18:12 +010024#include "pcu_utils.h"
Pau Espin Pedrol0b998b12021-03-24 14:45:43 +010025#include "tbf_ul.h"
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +010026
27/* TS 44.060 Table 10.4.5.1 states maximum RRBP is N + 26. Give extra space for time diff between Tx and Rx? */
28#define MAX_FN_RESERVED (27 + 50)
29
30const struct value_string pdch_ul_node_names[] = {
31 { PDCH_ULC_NODE_TBF_USF, "USF" },
32 { PDCH_ULC_NODE_TBF_POLL, "POLL" },
33 { PDCH_ULC_NODE_SBA, "SBA" },
34 { 0, NULL }
35};
36
Pau Espin Pedrolc6e911c2021-06-07 18:13:23 +020037const struct value_string pdch_ulc_tbf_poll_reason_names[] = {
38 { PDCH_ULC_POLL_UL_ASS, "UL_ASS" },
39 { PDCH_ULC_POLL_DL_ASS, "DL_ASS" },
40 { PDCH_ULC_POLL_UL_ACK, "UL_ACK" },
41 { PDCH_ULC_POLL_DL_ACK, "DL_ACK" },
42 { PDCH_ULC_POLL_CELL_CHG_CONTINUE, "CELL_CHG_CONTINUE" },
43 { 0, NULL }
44};
45
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +010046#define GSM_MAX_FN_THRESH (GSM_MAX_FN >> 1)
47/* 0: equal, -1: fn1 BEFORE fn2, 1: fn1 AFTER fn2 */
48static inline int fn_cmp(uint32_t fn1, uint32_t fn2)
49{
50 if (fn1 == fn2)
51 return 0;
52 /* FN1 goes before FN2: */
53 if ((fn1 < fn2 && (fn2 - fn1) < GSM_MAX_FN_THRESH) ||
54 (fn1 > fn2 && (fn1 - fn2) > GSM_MAX_FN_THRESH))
55 return -1;
56 /* FN1 goes after FN2: */
57 return 1;
58}
59
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +010060struct pdch_ulc *pdch_ulc_alloc(struct gprs_rlcmac_pdch *pdch, void *ctx)
61{
62 struct pdch_ulc* ulc;
63 ulc = talloc_zero(ctx, struct pdch_ulc);
64 if (!ulc)
65 return ulc;
66
67 ulc->pdch = pdch;
68 ulc->pool_ctx = talloc_pool(ulc, sizeof(struct pdch_ulc_node) * MAX_FN_RESERVED);
69 return ulc;
70}
71
72struct pdch_ulc_node *pdch_ulc_get_node(struct pdch_ulc *ulc, uint32_t fn)
73{
Pau Espin Pedrol222f6742021-03-29 11:29:22 +020074 struct rb_node *node = ulc->tree_root.rb_node;
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +010075 struct pdch_ulc_node *it;
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +010076 int res;
Pau Espin Pedrol222f6742021-03-29 11:29:22 +020077
78 while (node) {
79 it = rb_entry(node, struct pdch_ulc_node, node);
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +010080 res = fn_cmp(it->fn, fn);
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +010081 if (res > 0) /* it->fn AFTER fn */
Pau Espin Pedrol222f6742021-03-29 11:29:22 +020082 node = node->rb_left;
83 else if (res < 0) /* it->fn BEFORE fn */
84 node = node->rb_right;
85 else /* it->fn == fn */
86 return it;
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +010087 }
88 return NULL;
89}
90struct pdch_ulc_node *pdch_ulc_pop_node(struct pdch_ulc *ulc, uint32_t fn)
91{
92 struct pdch_ulc_node *item = pdch_ulc_get_node(ulc, fn);
93 if (!item)
94 return NULL;
95 rb_erase(&item->node, &ulc->tree_root);
96 return item;
97}
98
99struct gprs_rlcmac_sba *pdch_ulc_get_sba(struct pdch_ulc *ulc, uint32_t fn)
100{
101 struct pdch_ulc_node *item = pdch_ulc_get_node(ulc, fn);
102 if (!item || item->type != PDCH_ULC_NODE_SBA)
103 return NULL;
104 return item->sba.sba;
105}
106
Pau Espin Pedrolfd1fbdb2021-03-11 16:59:50 +0100107struct gprs_rlcmac_tbf *pdch_ulc_get_tbf_poll(struct pdch_ulc *ulc, uint32_t fn)
108{
109 struct pdch_ulc_node *item = pdch_ulc_get_node(ulc, fn);
110 if (!item || item->type != PDCH_ULC_NODE_TBF_POLL)
111 return NULL;
112 return item->tbf_poll.poll_tbf;
113}
114
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100115bool pdch_ulc_fn_is_free(struct pdch_ulc *ulc, uint32_t fn)
116{
117 return !pdch_ulc_get_node(ulc, fn);
118}
119
Pau Espin Pedrol50a1ede2021-03-29 13:49:43 +0200120struct rrbp_opt {
121 uint8_t offset;
122 enum rrbp_field coding;
123};
124
Pau Espin Pedrol99360a32021-03-09 17:18:12 +0100125int pdch_ulc_get_next_free_rrbp_fn(struct pdch_ulc *ulc, uint32_t fn, uint32_t *poll_fn, unsigned int *rrbp)
126{
Pau Espin Pedrol50a1ede2021-03-29 13:49:43 +0200127 uint8_t i;
128 static const struct rrbp_opt rrbp_list[] = {
129 { 13, RRBP_N_plus_13 },
130 { 17, RRBP_N_plus_17_18 },
131 { 18, RRBP_N_plus_17_18 },
132 { 21, RRBP_N_plus_21_22 },
133 { 22, RRBP_N_plus_21_22 },
134 { 26, RRBP_N_plus_26 },
135 };
136
137 for (i = 0; i < ARRAY_SIZE(rrbp_list); i++) {
138 uint32_t new_poll_fn = next_fn(fn, rrbp_list[i].offset);
139 if (!fn_valid(new_poll_fn))
140 continue;
141 if (pdch_ulc_fn_is_free(ulc, new_poll_fn)) {
142 LOGPDCH(ulc->pdch, DRLCMAC, LOGL_DEBUG, "POLL scheduled at FN %" PRIu32
143 " + %" PRIu8 " = %" PRIu32 "\n",
144 fn, rrbp_list[i].offset, new_poll_fn);
145 *poll_fn = new_poll_fn;
146 *rrbp = (unsigned int)rrbp_list[i].coding;
147 return 0;
148 }
149 LOGPDCH(ulc->pdch, DRLCMAC, LOGL_DEBUG, "UL block already scheduled at FN %" PRIu32
150 " + %" PRIu8 " = %" PRIu32 "\n",
151 fn, rrbp_list[i].offset, new_poll_fn);
152
Pau Espin Pedrol99360a32021-03-09 17:18:12 +0100153 }
Pau Espin Pedrol50a1ede2021-03-29 13:49:43 +0200154 LOGPDCH(ulc->pdch, DRLCMAC, LOGL_ERROR, "FN=%" PRIu32 " "
155 "Failed allocating POLL, all RRBP values are already reserved!\n", fn);
156 return -EBUSY;
Pau Espin Pedrol99360a32021-03-09 17:18:12 +0100157}
158
Pau Espin Pedrolce3bd252021-03-29 12:13:13 +0200159/* Get next free (unreserved) FN which is not located in time before "start_fn" */
160uint32_t pdch_ulc_get_next_free_fn(struct pdch_ulc *ulc, uint32_t start_fn)
161{
162 struct rb_node *node;
163 struct pdch_ulc_node *it;
164 int res;
165 uint32_t check_fn = start_fn;
166
167 for (node = rb_first(&ulc->tree_root); node; node = rb_next(node)) {
168 it = container_of(node, struct pdch_ulc_node, node);
169 res = fn_cmp(it->fn, check_fn);
170 if (res > 0) { /* it->fn AFTER check_fn */
171 /* Next reserved FN is passed check_fn, hence it means check_fn is free */
172 return check_fn;
173 }
174 /* if it->fn < check_fn, simply continue iterating, we want to reach at least check_fn */
175 if (res == 0)/* it->fn == fn */
176 check_fn = fn_next_block(check_fn);
177 /* if it->fn < check_fn, simply continue iterating, we want to reach at least check_fn */
178 }
179 return check_fn;
180}
181
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100182static struct pdch_ulc_node *_alloc_node(struct pdch_ulc *ulc, uint32_t fn)
183{
184 struct pdch_ulc_node *node;
185 node = talloc_zero(ulc->pool_ctx, struct pdch_ulc_node);
186 node->fn = fn;
187 return node;
188}
189
190static int pdch_ulc_add_node(struct pdch_ulc *ulc, struct pdch_ulc_node *item)
191{
192 struct rb_node **n = &(ulc->tree_root.rb_node);
193 struct rb_node *parent = NULL;
194
195 while (*n) {
196 struct pdch_ulc_node *it;
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +0100197 int res;
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100198
199 it = container_of(*n, struct pdch_ulc_node, node);
200
201 parent = *n;
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +0100202 res = fn_cmp(item->fn, it->fn);
203 if (res < 0) { /* item->fn "BEFORE" it->fn */
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100204 n = &((*n)->rb_left);
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +0100205 } else if (res > 0) { /* item->fn "AFTER" it->fn */
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100206 n = &((*n)->rb_right);
207 } else {
208 LOGPDCH(ulc->pdch, DRLCMAC, LOGL_ERROR,
209 "Trying to reserve already reserved FN %u\n",
210 item->fn);
211 return -EEXIST;
212 }
213 }
214
215 rb_link_node(&item->node, parent, n);
216 rb_insert_color(&item->node, &ulc->tree_root);
217 return 0;
218}
219
220int pdch_ulc_reserve_tbf_usf(struct pdch_ulc *ulc, uint32_t fn, struct gprs_rlcmac_ul_tbf *ul_tbf)
221{
Pau Espin Pedrolc1f38c72021-03-24 14:06:15 +0100222 struct pdch_ulc_node *item = _alloc_node(ulc, fn);
223 item->type = PDCH_ULC_NODE_TBF_USF;
224 item->tbf_usf.ul_tbf = ul_tbf;
225 return pdch_ulc_add_node(ulc, item);
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100226}
227
Pau Espin Pedrol86580e12021-03-29 18:15:30 +0200228int pdch_ulc_reserve_tbf_poll(struct pdch_ulc *ulc, uint32_t fn, struct gprs_rlcmac_tbf *tbf, enum pdch_ulc_tbf_poll_reason reason)
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100229{
Pau Espin Pedrol99360a32021-03-09 17:18:12 +0100230 struct pdch_ulc_node *item = _alloc_node(ulc, fn);
231 item->type = PDCH_ULC_NODE_TBF_POLL;
232 item->tbf_poll.poll_tbf = tbf;
Pau Espin Pedrol86580e12021-03-29 18:15:30 +0200233 item->tbf_poll.reason = reason;
Pau Espin Pedrol99360a32021-03-09 17:18:12 +0100234 return pdch_ulc_add_node(ulc, item);
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100235}
236
237int pdch_ulc_reserve_sba(struct pdch_ulc *ulc, struct gprs_rlcmac_sba *sba)
238{
239 struct pdch_ulc_node *item = _alloc_node(ulc, sba->fn);
240 item->type = PDCH_ULC_NODE_SBA;
241 item->sba.sba = sba;
242 return pdch_ulc_add_node(ulc, item);
243}
244
Pau Espin Pedrolade9c2f2021-03-24 14:02:49 +0100245void pdch_ulc_release_node(struct pdch_ulc *ulc, struct pdch_ulc_node *item)
246{
247 rb_erase(&item->node, &ulc->tree_root);
248 talloc_free(item);
249}
250
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100251int pdch_ulc_release_fn(struct pdch_ulc *ulc, uint32_t fn)
252{
253 struct pdch_ulc_node *item = pdch_ulc_get_node(ulc, fn);
254 if (!item)
255 return -ENOKEY;
Pau Espin Pedrolade9c2f2021-03-24 14:02:49 +0100256 pdch_ulc_release_node(ulc, item);
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100257 return 0;
258}
259
Pau Espin Pedrol99360a32021-03-09 17:18:12 +0100260void pdch_ulc_release_tbf(struct pdch_ulc *ulc, const struct gprs_rlcmac_tbf *tbf)
261{
262 bool tree_modified;
263 do {
264 struct rb_node *node;
265 struct pdch_ulc_node *item;
266 const struct gprs_rlcmac_tbf *item_tbf;
267
268 tree_modified = false;
269 for (node = rb_first(&ulc->tree_root); node; node = rb_next(node)) {
270 item = container_of(node, struct pdch_ulc_node, node);
271 switch (item->type) {
272 case PDCH_ULC_NODE_SBA:
273 continue;
274 case PDCH_ULC_NODE_TBF_POLL:
275 item_tbf = item->tbf_poll.poll_tbf;
276 break;
277 case PDCH_ULC_NODE_TBF_USF:
278 item_tbf = (struct gprs_rlcmac_tbf *)item->tbf_usf.ul_tbf;
279 break;
Harald Welted7f05582021-03-20 16:17:30 +0100280 default:
281 OSMO_ASSERT(0);
Pau Espin Pedrol99360a32021-03-09 17:18:12 +0100282 }
283 if (item_tbf != tbf)
284 continue;
285 /* One entry found, remove it from tree and restart
286 * search from start (to avoid traverse continue from
287 * no-more existant node */
288 tree_modified = true;
Pau Espin Pedrolade9c2f2021-03-24 14:02:49 +0100289 pdch_ulc_release_node(ulc, item);
Pau Espin Pedrol99360a32021-03-09 17:18:12 +0100290 break;
291 }
292 } while (tree_modified);
293}
294
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100295void pdch_ulc_expire_fn(struct pdch_ulc *ulc, uint32_t fn)
296{
297 struct gprs_rlcmac_sba *sba;
298 struct pdch_ulc_node *item;
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +0100299 int res;
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100300
301 struct rb_node *first;
Vadim Yanitskiyc2eb4b72022-10-04 02:57:00 +0700302 while ((first = rb_first(&ulc->tree_root))) {
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100303 item = container_of(first, struct pdch_ulc_node, node);
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +0100304 res = fn_cmp(item->fn, fn);
305 if (res > 0) /* item->fn AFTER fn */
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100306 break;
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +0100307 if (res < 0) { /* item->fn BEFORE fn */
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100308 /* Sanity check: */
309 LOGPDCH(ulc->pdch, DRLCMAC, LOGL_ERROR,
310 "Expiring FN=%" PRIu32 " but previous FN=%" PRIu32 " is still reserved!\n",
311 fn, item->fn);
312 }
313 rb_erase(&item->node, &ulc->tree_root);
314
315 switch (item->type) {
316 case PDCH_ULC_NODE_TBF_USF:
Pau Espin Pedrolc1f38c72021-03-24 14:06:15 +0100317 LOGPDCH(ulc->pdch, DRLCMAC, LOGL_INFO,
318 "Timeout for registered USF (FN=%u): %s\n",
319 item->fn, tbf_name((struct gprs_rlcmac_tbf *)item->tbf_usf.ul_tbf));
Pau Espin Pedrol0b998b12021-03-24 14:45:43 +0100320 tbf_usf_timeout(item->tbf_usf.ul_tbf);
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100321 break;
322 case PDCH_ULC_NODE_TBF_POLL:
Pau Espin Pedrol99360a32021-03-09 17:18:12 +0100323 LOGPDCH(ulc->pdch, DRLCMAC, LOGL_NOTICE,
Pau Espin Pedrol4a1c5612021-10-12 12:15:45 +0200324 "Timeout for registered POLL (FN=%u, reason=%s): %s\n",
325 item->fn, get_value_string(pdch_ulc_tbf_poll_reason_names, item->tbf_poll.reason),
326 tbf_name(item->tbf_poll.poll_tbf));
Pau Espin Pedrol16e16782021-03-29 19:10:19 +0200327 tbf_poll_timeout(item->tbf_poll.poll_tbf, ulc->pdch, item->fn, item->tbf_poll.reason);
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100328 break;
329 case PDCH_ULC_NODE_SBA:
330 sba = item->sba.sba;
331 LOGPDCH(sba->pdch, DRLCMAC, LOGL_NOTICE,
332 "Timeout for registered SBA (FN=%u, TA=%u)\n",
333 sba->fn, sba->ta);
334 sba_timeout(sba);
335 break;
336 }
337
338 talloc_free(item);
339 }
340}