blob: 9730fa178bf48bf85e2f25c0c7ef5e8af7ede032 [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.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with it program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
20
21#include <unistd.h>
22#include <talloc.h>
23
24#include "pdch_ul_controller.h"
25#include "bts.h"
26#include "sba.h"
27#include "pdch.h"
Pau Espin Pedrol99360a32021-03-09 17:18:12 +010028#include "pcu_utils.h"
Pau Espin Pedrol0b998b12021-03-24 14:45:43 +010029#include "tbf_ul.h"
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +010030
31/* TS 44.060 Table 10.4.5.1 states maximum RRBP is N + 26. Give extra space for time diff between Tx and Rx? */
32#define MAX_FN_RESERVED (27 + 50)
33
34const struct value_string pdch_ul_node_names[] = {
35 { PDCH_ULC_NODE_TBF_USF, "USF" },
36 { PDCH_ULC_NODE_TBF_POLL, "POLL" },
37 { PDCH_ULC_NODE_SBA, "SBA" },
38 { 0, NULL }
39};
40
Pau Espin Pedrolc6e911c2021-06-07 18:13:23 +020041const struct value_string pdch_ulc_tbf_poll_reason_names[] = {
42 { PDCH_ULC_POLL_UL_ASS, "UL_ASS" },
43 { PDCH_ULC_POLL_DL_ASS, "DL_ASS" },
44 { PDCH_ULC_POLL_UL_ACK, "UL_ACK" },
45 { PDCH_ULC_POLL_DL_ACK, "DL_ACK" },
46 { PDCH_ULC_POLL_CELL_CHG_CONTINUE, "CELL_CHG_CONTINUE" },
Pau Espin Pedroldeed90d2021-05-26 13:17:01 +020047 { PDCH_ULC_POLL_MEAS_ORDER, "MEAS_ORDER" },
Pau Espin Pedrolc6e911c2021-06-07 18:13:23 +020048 { 0, NULL }
49};
50
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +010051#define GSM_MAX_FN_THRESH (GSM_MAX_FN >> 1)
52/* 0: equal, -1: fn1 BEFORE fn2, 1: fn1 AFTER fn2 */
53static inline int fn_cmp(uint32_t fn1, uint32_t fn2)
54{
55 if (fn1 == fn2)
56 return 0;
57 /* FN1 goes before FN2: */
58 if ((fn1 < fn2 && (fn2 - fn1) < GSM_MAX_FN_THRESH) ||
59 (fn1 > fn2 && (fn1 - fn2) > GSM_MAX_FN_THRESH))
60 return -1;
61 /* FN1 goes after FN2: */
62 return 1;
63}
64
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +010065struct pdch_ulc *pdch_ulc_alloc(struct gprs_rlcmac_pdch *pdch, void *ctx)
66{
67 struct pdch_ulc* ulc;
68 ulc = talloc_zero(ctx, struct pdch_ulc);
69 if (!ulc)
70 return ulc;
71
72 ulc->pdch = pdch;
73 ulc->pool_ctx = talloc_pool(ulc, sizeof(struct pdch_ulc_node) * MAX_FN_RESERVED);
74 return ulc;
75}
76
77struct pdch_ulc_node *pdch_ulc_get_node(struct pdch_ulc *ulc, uint32_t fn)
78{
Pau Espin Pedrol222f6742021-03-29 11:29:22 +020079 struct rb_node *node = ulc->tree_root.rb_node;
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +010080 struct pdch_ulc_node *it;
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +010081 int res;
Pau Espin Pedrol222f6742021-03-29 11:29:22 +020082
83 while (node) {
84 it = rb_entry(node, struct pdch_ulc_node, node);
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +010085 res = fn_cmp(it->fn, fn);
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +010086 if (res > 0) /* it->fn AFTER fn */
Pau Espin Pedrol222f6742021-03-29 11:29:22 +020087 node = node->rb_left;
88 else if (res < 0) /* it->fn BEFORE fn */
89 node = node->rb_right;
90 else /* it->fn == fn */
91 return it;
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +010092 }
93 return NULL;
94}
95struct pdch_ulc_node *pdch_ulc_pop_node(struct pdch_ulc *ulc, uint32_t fn)
96{
97 struct pdch_ulc_node *item = pdch_ulc_get_node(ulc, fn);
98 if (!item)
99 return NULL;
100 rb_erase(&item->node, &ulc->tree_root);
101 return item;
102}
103
104struct gprs_rlcmac_sba *pdch_ulc_get_sba(struct pdch_ulc *ulc, uint32_t fn)
105{
106 struct pdch_ulc_node *item = pdch_ulc_get_node(ulc, fn);
107 if (!item || item->type != PDCH_ULC_NODE_SBA)
108 return NULL;
109 return item->sba.sba;
110}
111
Pau Espin Pedrolfd1fbdb2021-03-11 16:59:50 +0100112struct gprs_rlcmac_tbf *pdch_ulc_get_tbf_poll(struct pdch_ulc *ulc, uint32_t fn)
113{
114 struct pdch_ulc_node *item = pdch_ulc_get_node(ulc, fn);
115 if (!item || item->type != PDCH_ULC_NODE_TBF_POLL)
116 return NULL;
117 return item->tbf_poll.poll_tbf;
118}
119
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100120bool pdch_ulc_fn_is_free(struct pdch_ulc *ulc, uint32_t fn)
121{
122 return !pdch_ulc_get_node(ulc, fn);
123}
124
Pau Espin Pedrol50a1ede2021-03-29 13:49:43 +0200125struct rrbp_opt {
126 uint8_t offset;
127 enum rrbp_field coding;
128};
129
Pau Espin Pedrol99360a32021-03-09 17:18:12 +0100130int pdch_ulc_get_next_free_rrbp_fn(struct pdch_ulc *ulc, uint32_t fn, uint32_t *poll_fn, unsigned int *rrbp)
131{
Pau Espin Pedrol50a1ede2021-03-29 13:49:43 +0200132 uint8_t i;
133 static const struct rrbp_opt rrbp_list[] = {
134 { 13, RRBP_N_plus_13 },
135 { 17, RRBP_N_plus_17_18 },
136 { 18, RRBP_N_plus_17_18 },
137 { 21, RRBP_N_plus_21_22 },
138 { 22, RRBP_N_plus_21_22 },
139 { 26, RRBP_N_plus_26 },
140 };
141
142 for (i = 0; i < ARRAY_SIZE(rrbp_list); i++) {
143 uint32_t new_poll_fn = next_fn(fn, rrbp_list[i].offset);
144 if (!fn_valid(new_poll_fn))
145 continue;
146 if (pdch_ulc_fn_is_free(ulc, new_poll_fn)) {
147 LOGPDCH(ulc->pdch, DRLCMAC, LOGL_DEBUG, "POLL scheduled at FN %" PRIu32
148 " + %" PRIu8 " = %" PRIu32 "\n",
149 fn, rrbp_list[i].offset, new_poll_fn);
150 *poll_fn = new_poll_fn;
151 *rrbp = (unsigned int)rrbp_list[i].coding;
152 return 0;
153 }
154 LOGPDCH(ulc->pdch, DRLCMAC, LOGL_DEBUG, "UL block already scheduled at FN %" PRIu32
155 " + %" PRIu8 " = %" PRIu32 "\n",
156 fn, rrbp_list[i].offset, new_poll_fn);
157
Pau Espin Pedrol99360a32021-03-09 17:18:12 +0100158 }
Pau Espin Pedrol50a1ede2021-03-29 13:49:43 +0200159 LOGPDCH(ulc->pdch, DRLCMAC, LOGL_ERROR, "FN=%" PRIu32 " "
160 "Failed allocating POLL, all RRBP values are already reserved!\n", fn);
161 return -EBUSY;
Pau Espin Pedrol99360a32021-03-09 17:18:12 +0100162}
163
Pau Espin Pedrolce3bd252021-03-29 12:13:13 +0200164/* Get next free (unreserved) FN which is not located in time before "start_fn" */
165uint32_t pdch_ulc_get_next_free_fn(struct pdch_ulc *ulc, uint32_t start_fn)
166{
167 struct rb_node *node;
168 struct pdch_ulc_node *it;
169 int res;
170 uint32_t check_fn = start_fn;
171
172 for (node = rb_first(&ulc->tree_root); node; node = rb_next(node)) {
173 it = container_of(node, struct pdch_ulc_node, node);
174 res = fn_cmp(it->fn, check_fn);
175 if (res > 0) { /* it->fn AFTER check_fn */
176 /* Next reserved FN is passed check_fn, hence it means check_fn is free */
177 return check_fn;
178 }
179 /* if it->fn < check_fn, simply continue iterating, we want to reach at least check_fn */
180 if (res == 0)/* it->fn == fn */
181 check_fn = fn_next_block(check_fn);
182 /* if it->fn < check_fn, simply continue iterating, we want to reach at least check_fn */
183 }
184 return check_fn;
185}
186
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100187static struct pdch_ulc_node *_alloc_node(struct pdch_ulc *ulc, uint32_t fn)
188{
189 struct pdch_ulc_node *node;
190 node = talloc_zero(ulc->pool_ctx, struct pdch_ulc_node);
191 node->fn = fn;
192 return node;
193}
194
195static int pdch_ulc_add_node(struct pdch_ulc *ulc, struct pdch_ulc_node *item)
196{
197 struct rb_node **n = &(ulc->tree_root.rb_node);
198 struct rb_node *parent = NULL;
199
200 while (*n) {
201 struct pdch_ulc_node *it;
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +0100202 int res;
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100203
204 it = container_of(*n, struct pdch_ulc_node, node);
205
206 parent = *n;
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +0100207 res = fn_cmp(item->fn, it->fn);
208 if (res < 0) { /* item->fn "BEFORE" it->fn */
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100209 n = &((*n)->rb_left);
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +0100210 } else if (res > 0) { /* item->fn "AFTER" it->fn */
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100211 n = &((*n)->rb_right);
212 } else {
213 LOGPDCH(ulc->pdch, DRLCMAC, LOGL_ERROR,
214 "Trying to reserve already reserved FN %u\n",
215 item->fn);
216 return -EEXIST;
217 }
218 }
219
220 rb_link_node(&item->node, parent, n);
221 rb_insert_color(&item->node, &ulc->tree_root);
222 return 0;
223}
224
225int pdch_ulc_reserve_tbf_usf(struct pdch_ulc *ulc, uint32_t fn, struct gprs_rlcmac_ul_tbf *ul_tbf)
226{
Pau Espin Pedrolc1f38c72021-03-24 14:06:15 +0100227 struct pdch_ulc_node *item = _alloc_node(ulc, fn);
228 item->type = PDCH_ULC_NODE_TBF_USF;
229 item->tbf_usf.ul_tbf = ul_tbf;
230 return pdch_ulc_add_node(ulc, item);
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100231}
232
Pau Espin Pedrol86580e12021-03-29 18:15:30 +0200233int 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 +0100234{
Pau Espin Pedrol99360a32021-03-09 17:18:12 +0100235 struct pdch_ulc_node *item = _alloc_node(ulc, fn);
236 item->type = PDCH_ULC_NODE_TBF_POLL;
237 item->tbf_poll.poll_tbf = tbf;
Pau Espin Pedrol86580e12021-03-29 18:15:30 +0200238 item->tbf_poll.reason = reason;
Pau Espin Pedrol99360a32021-03-09 17:18:12 +0100239 return pdch_ulc_add_node(ulc, item);
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100240}
241
242int pdch_ulc_reserve_sba(struct pdch_ulc *ulc, struct gprs_rlcmac_sba *sba)
243{
244 struct pdch_ulc_node *item = _alloc_node(ulc, sba->fn);
245 item->type = PDCH_ULC_NODE_SBA;
246 item->sba.sba = sba;
247 return pdch_ulc_add_node(ulc, item);
248}
249
Pau Espin Pedrolade9c2f2021-03-24 14:02:49 +0100250void pdch_ulc_release_node(struct pdch_ulc *ulc, struct pdch_ulc_node *item)
251{
252 rb_erase(&item->node, &ulc->tree_root);
253 talloc_free(item);
254}
255
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100256int pdch_ulc_release_fn(struct pdch_ulc *ulc, uint32_t fn)
257{
258 struct pdch_ulc_node *item = pdch_ulc_get_node(ulc, fn);
259 if (!item)
260 return -ENOKEY;
Pau Espin Pedrolade9c2f2021-03-24 14:02:49 +0100261 pdch_ulc_release_node(ulc, item);
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100262 return 0;
263}
264
Pau Espin Pedrol99360a32021-03-09 17:18:12 +0100265void pdch_ulc_release_tbf(struct pdch_ulc *ulc, const struct gprs_rlcmac_tbf *tbf)
266{
267 bool tree_modified;
268 do {
269 struct rb_node *node;
270 struct pdch_ulc_node *item;
271 const struct gprs_rlcmac_tbf *item_tbf;
272
273 tree_modified = false;
274 for (node = rb_first(&ulc->tree_root); node; node = rb_next(node)) {
275 item = container_of(node, struct pdch_ulc_node, node);
276 switch (item->type) {
277 case PDCH_ULC_NODE_SBA:
278 continue;
279 case PDCH_ULC_NODE_TBF_POLL:
280 item_tbf = item->tbf_poll.poll_tbf;
281 break;
282 case PDCH_ULC_NODE_TBF_USF:
283 item_tbf = (struct gprs_rlcmac_tbf *)item->tbf_usf.ul_tbf;
284 break;
Harald Welted7f05582021-03-20 16:17:30 +0100285 default:
286 OSMO_ASSERT(0);
Pau Espin Pedrol99360a32021-03-09 17:18:12 +0100287 }
288 if (item_tbf != tbf)
289 continue;
290 /* One entry found, remove it from tree and restart
291 * search from start (to avoid traverse continue from
292 * no-more existant node */
293 tree_modified = true;
Pau Espin Pedrolade9c2f2021-03-24 14:02:49 +0100294 pdch_ulc_release_node(ulc, item);
Pau Espin Pedrol99360a32021-03-09 17:18:12 +0100295 break;
296 }
297 } while (tree_modified);
298}
299
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100300void pdch_ulc_expire_fn(struct pdch_ulc *ulc, uint32_t fn)
301{
302 struct gprs_rlcmac_sba *sba;
303 struct pdch_ulc_node *item;
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +0100304 int res;
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100305
306 struct rb_node *first;
307 while((first = rb_first(&ulc->tree_root))) {
308 item = container_of(first, struct pdch_ulc_node, node);
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +0100309 res = fn_cmp(item->fn, fn);
310 if (res > 0) /* item->fn AFTER fn */
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100311 break;
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +0100312 if (res < 0) { /* item->fn BEFORE fn */
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100313 /* Sanity check: */
314 LOGPDCH(ulc->pdch, DRLCMAC, LOGL_ERROR,
315 "Expiring FN=%" PRIu32 " but previous FN=%" PRIu32 " is still reserved!\n",
316 fn, item->fn);
317 }
318 rb_erase(&item->node, &ulc->tree_root);
319
320 switch (item->type) {
321 case PDCH_ULC_NODE_TBF_USF:
Pau Espin Pedrolc1f38c72021-03-24 14:06:15 +0100322 LOGPDCH(ulc->pdch, DRLCMAC, LOGL_INFO,
323 "Timeout for registered USF (FN=%u): %s\n",
324 item->fn, tbf_name((struct gprs_rlcmac_tbf *)item->tbf_usf.ul_tbf));
Pau Espin Pedrol0b998b12021-03-24 14:45:43 +0100325 tbf_usf_timeout(item->tbf_usf.ul_tbf);
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100326 break;
327 case PDCH_ULC_NODE_TBF_POLL:
Pau Espin Pedrol99360a32021-03-09 17:18:12 +0100328 LOGPDCH(ulc->pdch, DRLCMAC, LOGL_NOTICE,
329 "Timeout for registered POLL (FN=%u): %s\n",
330 item->fn, tbf_name(item->tbf_poll.poll_tbf));
Pau Espin Pedrol16e16782021-03-29 19:10:19 +0200331 tbf_poll_timeout(item->tbf_poll.poll_tbf, ulc->pdch, item->fn, item->tbf_poll.reason);
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100332 break;
333 case PDCH_ULC_NODE_SBA:
334 sba = item->sba.sba;
335 LOGPDCH(sba->pdch, DRLCMAC, LOGL_NOTICE,
336 "Timeout for registered SBA (FN=%u, TA=%u)\n",
337 sba->fn, sba->ta);
338 sba_timeout(sba);
339 break;
340 }
341
342 talloc_free(item);
343 }
344}