blob: 1ead3e906c1beeab34073d3544b5a66e35957563 [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 Pedrolc7cc4162021-03-12 18:24:57 +010041#define GSM_MAX_FN_THRESH (GSM_MAX_FN >> 1)
42/* 0: equal, -1: fn1 BEFORE fn2, 1: fn1 AFTER fn2 */
43static inline int fn_cmp(uint32_t fn1, uint32_t fn2)
44{
45 if (fn1 == fn2)
46 return 0;
47 /* FN1 goes before FN2: */
48 if ((fn1 < fn2 && (fn2 - fn1) < GSM_MAX_FN_THRESH) ||
49 (fn1 > fn2 && (fn1 - fn2) > GSM_MAX_FN_THRESH))
50 return -1;
51 /* FN1 goes after FN2: */
52 return 1;
53}
54
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +010055struct pdch_ulc *pdch_ulc_alloc(struct gprs_rlcmac_pdch *pdch, void *ctx)
56{
57 struct pdch_ulc* ulc;
58 ulc = talloc_zero(ctx, struct pdch_ulc);
59 if (!ulc)
60 return ulc;
61
62 ulc->pdch = pdch;
63 ulc->pool_ctx = talloc_pool(ulc, sizeof(struct pdch_ulc_node) * MAX_FN_RESERVED);
64 return ulc;
65}
66
67struct pdch_ulc_node *pdch_ulc_get_node(struct pdch_ulc *ulc, uint32_t fn)
68{
Pau Espin Pedrol222f6742021-03-29 11:29:22 +020069 struct rb_node *node = ulc->tree_root.rb_node;
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +010070 struct pdch_ulc_node *it;
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +010071 int res;
Pau Espin Pedrol222f6742021-03-29 11:29:22 +020072
73 while (node) {
74 it = rb_entry(node, struct pdch_ulc_node, node);
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +010075 res = fn_cmp(it->fn, fn);
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +010076 if (res > 0) /* it->fn AFTER fn */
Pau Espin Pedrol222f6742021-03-29 11:29:22 +020077 node = node->rb_left;
78 else if (res < 0) /* it->fn BEFORE fn */
79 node = node->rb_right;
80 else /* it->fn == fn */
81 return it;
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +010082 }
83 return NULL;
84}
85struct pdch_ulc_node *pdch_ulc_pop_node(struct pdch_ulc *ulc, uint32_t fn)
86{
87 struct pdch_ulc_node *item = pdch_ulc_get_node(ulc, fn);
88 if (!item)
89 return NULL;
90 rb_erase(&item->node, &ulc->tree_root);
91 return item;
92}
93
94struct gprs_rlcmac_sba *pdch_ulc_get_sba(struct pdch_ulc *ulc, uint32_t fn)
95{
96 struct pdch_ulc_node *item = pdch_ulc_get_node(ulc, fn);
97 if (!item || item->type != PDCH_ULC_NODE_SBA)
98 return NULL;
99 return item->sba.sba;
100}
101
Pau Espin Pedrolfd1fbdb2021-03-11 16:59:50 +0100102struct gprs_rlcmac_tbf *pdch_ulc_get_tbf_poll(struct pdch_ulc *ulc, uint32_t fn)
103{
104 struct pdch_ulc_node *item = pdch_ulc_get_node(ulc, fn);
105 if (!item || item->type != PDCH_ULC_NODE_TBF_POLL)
106 return NULL;
107 return item->tbf_poll.poll_tbf;
108}
109
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100110bool pdch_ulc_fn_is_free(struct pdch_ulc *ulc, uint32_t fn)
111{
112 return !pdch_ulc_get_node(ulc, fn);
113}
114
Pau Espin Pedrol99360a32021-03-09 17:18:12 +0100115int pdch_ulc_get_next_free_rrbp_fn(struct pdch_ulc *ulc, uint32_t fn, uint32_t *poll_fn, unsigned int *rrbp)
116{
117 /* TODO: support other RRBP offsets, see TS 44.060 able 10.4.5.1 */
118 uint32_t new_poll_fn = next_fn(fn, 13);
119 if (!pdch_ulc_fn_is_free(ulc, new_poll_fn)) {
120 LOGPDCH(ulc->pdch, DRLCMAC, LOGL_ERROR, "Polling is already scheduled "
121 "for single block allocation at FN=%u\n", fn);
122 return -EBUSY;
123 }
124
125 *poll_fn = new_poll_fn;
126 *rrbp = 0;
127
128 return 0;
129}
130
Pau Espin Pedrolce3bd252021-03-29 12:13:13 +0200131/* Get next free (unreserved) FN which is not located in time before "start_fn" */
132uint32_t pdch_ulc_get_next_free_fn(struct pdch_ulc *ulc, uint32_t start_fn)
133{
134 struct rb_node *node;
135 struct pdch_ulc_node *it;
136 int res;
137 uint32_t check_fn = start_fn;
138
139 for (node = rb_first(&ulc->tree_root); node; node = rb_next(node)) {
140 it = container_of(node, struct pdch_ulc_node, node);
141 res = fn_cmp(it->fn, check_fn);
142 if (res > 0) { /* it->fn AFTER check_fn */
143 /* Next reserved FN is passed check_fn, hence it means check_fn is free */
144 return check_fn;
145 }
146 /* if it->fn < check_fn, simply continue iterating, we want to reach at least check_fn */
147 if (res == 0)/* it->fn == fn */
148 check_fn = fn_next_block(check_fn);
149 /* if it->fn < check_fn, simply continue iterating, we want to reach at least check_fn */
150 }
151 return check_fn;
152}
153
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100154static struct pdch_ulc_node *_alloc_node(struct pdch_ulc *ulc, uint32_t fn)
155{
156 struct pdch_ulc_node *node;
157 node = talloc_zero(ulc->pool_ctx, struct pdch_ulc_node);
158 node->fn = fn;
159 return node;
160}
161
162static int pdch_ulc_add_node(struct pdch_ulc *ulc, struct pdch_ulc_node *item)
163{
164 struct rb_node **n = &(ulc->tree_root.rb_node);
165 struct rb_node *parent = NULL;
166
167 while (*n) {
168 struct pdch_ulc_node *it;
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +0100169 int res;
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100170
171 it = container_of(*n, struct pdch_ulc_node, node);
172
173 parent = *n;
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +0100174 res = fn_cmp(item->fn, it->fn);
175 if (res < 0) { /* item->fn "BEFORE" it->fn */
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100176 n = &((*n)->rb_left);
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +0100177 } else if (res > 0) { /* item->fn "AFTER" it->fn */
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100178 n = &((*n)->rb_right);
179 } else {
180 LOGPDCH(ulc->pdch, DRLCMAC, LOGL_ERROR,
181 "Trying to reserve already reserved FN %u\n",
182 item->fn);
183 return -EEXIST;
184 }
185 }
186
187 rb_link_node(&item->node, parent, n);
188 rb_insert_color(&item->node, &ulc->tree_root);
189 return 0;
190}
191
192int pdch_ulc_reserve_tbf_usf(struct pdch_ulc *ulc, uint32_t fn, struct gprs_rlcmac_ul_tbf *ul_tbf)
193{
Pau Espin Pedrolc1f38c72021-03-24 14:06:15 +0100194 struct pdch_ulc_node *item = _alloc_node(ulc, fn);
195 item->type = PDCH_ULC_NODE_TBF_USF;
196 item->tbf_usf.ul_tbf = ul_tbf;
197 return pdch_ulc_add_node(ulc, item);
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100198}
199
200int pdch_ulc_reserve_tbf_poll(struct pdch_ulc *ulc, uint32_t fn, struct gprs_rlcmac_tbf *tbf)
201{
Pau Espin Pedrol99360a32021-03-09 17:18:12 +0100202 struct pdch_ulc_node *item = _alloc_node(ulc, fn);
203 item->type = PDCH_ULC_NODE_TBF_POLL;
204 item->tbf_poll.poll_tbf = tbf;
205 return pdch_ulc_add_node(ulc, item);
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100206}
207
208int pdch_ulc_reserve_sba(struct pdch_ulc *ulc, struct gprs_rlcmac_sba *sba)
209{
210 struct pdch_ulc_node *item = _alloc_node(ulc, sba->fn);
211 item->type = PDCH_ULC_NODE_SBA;
212 item->sba.sba = sba;
213 return pdch_ulc_add_node(ulc, item);
214}
215
Pau Espin Pedrolade9c2f2021-03-24 14:02:49 +0100216void pdch_ulc_release_node(struct pdch_ulc *ulc, struct pdch_ulc_node *item)
217{
218 rb_erase(&item->node, &ulc->tree_root);
219 talloc_free(item);
220}
221
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100222int pdch_ulc_release_fn(struct pdch_ulc *ulc, uint32_t fn)
223{
224 struct pdch_ulc_node *item = pdch_ulc_get_node(ulc, fn);
225 if (!item)
226 return -ENOKEY;
Pau Espin Pedrolade9c2f2021-03-24 14:02:49 +0100227 pdch_ulc_release_node(ulc, item);
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100228 return 0;
229}
230
Pau Espin Pedrol99360a32021-03-09 17:18:12 +0100231void pdch_ulc_release_tbf(struct pdch_ulc *ulc, const struct gprs_rlcmac_tbf *tbf)
232{
233 bool tree_modified;
234 do {
235 struct rb_node *node;
236 struct pdch_ulc_node *item;
237 const struct gprs_rlcmac_tbf *item_tbf;
238
239 tree_modified = false;
240 for (node = rb_first(&ulc->tree_root); node; node = rb_next(node)) {
241 item = container_of(node, struct pdch_ulc_node, node);
242 switch (item->type) {
243 case PDCH_ULC_NODE_SBA:
244 continue;
245 case PDCH_ULC_NODE_TBF_POLL:
246 item_tbf = item->tbf_poll.poll_tbf;
247 break;
248 case PDCH_ULC_NODE_TBF_USF:
249 item_tbf = (struct gprs_rlcmac_tbf *)item->tbf_usf.ul_tbf;
250 break;
Harald Welted7f05582021-03-20 16:17:30 +0100251 default:
252 OSMO_ASSERT(0);
Pau Espin Pedrol99360a32021-03-09 17:18:12 +0100253 }
254 if (item_tbf != tbf)
255 continue;
256 /* One entry found, remove it from tree and restart
257 * search from start (to avoid traverse continue from
258 * no-more existant node */
259 tree_modified = true;
Pau Espin Pedrolade9c2f2021-03-24 14:02:49 +0100260 pdch_ulc_release_node(ulc, item);
Pau Espin Pedrol99360a32021-03-09 17:18:12 +0100261 break;
262 }
263 } while (tree_modified);
264}
265
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100266void pdch_ulc_expire_fn(struct pdch_ulc *ulc, uint32_t fn)
267{
268 struct gprs_rlcmac_sba *sba;
269 struct pdch_ulc_node *item;
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +0100270 int res;
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100271
272 struct rb_node *first;
273 while((first = rb_first(&ulc->tree_root))) {
274 item = container_of(first, struct pdch_ulc_node, node);
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +0100275 res = fn_cmp(item->fn, fn);
276 if (res > 0) /* item->fn AFTER fn */
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100277 break;
Pau Espin Pedrolc7cc4162021-03-12 18:24:57 +0100278 if (res < 0) { /* item->fn BEFORE fn */
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100279 /* Sanity check: */
280 LOGPDCH(ulc->pdch, DRLCMAC, LOGL_ERROR,
281 "Expiring FN=%" PRIu32 " but previous FN=%" PRIu32 " is still reserved!\n",
282 fn, item->fn);
283 }
284 rb_erase(&item->node, &ulc->tree_root);
285
286 switch (item->type) {
287 case PDCH_ULC_NODE_TBF_USF:
Pau Espin Pedrolc1f38c72021-03-24 14:06:15 +0100288 LOGPDCH(ulc->pdch, DRLCMAC, LOGL_INFO,
289 "Timeout for registered USF (FN=%u): %s\n",
290 item->fn, tbf_name((struct gprs_rlcmac_tbf *)item->tbf_usf.ul_tbf));
Pau Espin Pedrol0b998b12021-03-24 14:45:43 +0100291 tbf_usf_timeout(item->tbf_usf.ul_tbf);
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100292 break;
293 case PDCH_ULC_NODE_TBF_POLL:
Pau Espin Pedrol99360a32021-03-09 17:18:12 +0100294 LOGPDCH(ulc->pdch, DRLCMAC, LOGL_NOTICE,
295 "Timeout for registered POLL (FN=%u): %s\n",
296 item->fn, tbf_name(item->tbf_poll.poll_tbf));
297 tbf_poll_timeout(item->tbf_poll.poll_tbf);
Pau Espin Pedrol15c58ac2021-03-08 14:57:58 +0100298 break;
299 case PDCH_ULC_NODE_SBA:
300 sba = item->sba.sba;
301 LOGPDCH(sba->pdch, DRLCMAC, LOGL_NOTICE,
302 "Timeout for registered SBA (FN=%u, TA=%u)\n",
303 sba->fn, sba->ta);
304 sba_timeout(sba);
305 break;
306 }
307
308 talloc_free(item);
309 }
310}