blob: 0955411c67df22e74f3a409d8aa6d1efb2dae260 [file] [log] [blame]
Holger Hans Peter Freyther02ab4a82013-09-29 07:37:40 +02001/* gprs_rlcmac.cpp
2 *
3 * Copyright (C) 2012 Ivan Klyuchnikov
4 * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
5 * Copyright (C) 2013 by Holger Hans Peter Freyther
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 */
21
22#include <gprs_rlcmac.h>
23#include <gprs_debug.h>
Holger Hans Peter Freyther34bd8bd2013-10-19 21:10:38 +020024#include <bts.h>
Holger Hans Peter Freyther02ab4a82013-09-29 07:37:40 +020025#include <tbf.h>
Jacob Erlbecke2e004e2015-06-18 17:16:26 +020026#include <gprs_ms.h>
Jacob Erlbeck8cba7e92016-01-19 15:48:03 +010027#include <pcu_utils.h>
Holger Hans Peter Freyther02ab4a82013-09-29 07:37:40 +020028
29#include <errno.h>
Jacob Erlbeckec478752015-06-19 16:35:38 +020030#include <values.h>
Holger Hans Peter Freyther02ab4a82013-09-29 07:37:40 +020031
Max842d7812017-11-01 18:11:24 +010032extern "C" {
33#include "mslot_class.h"
Max1187a772018-01-26 13:31:42 +010034#include <osmocom/core/linuxlist.h>
35#include <osmocom/core/logging.h>
36#include <osmocom/core/utils.h>
Max842d7812017-11-01 18:11:24 +010037}
38
Jacob Erlbeck77da3552015-07-16 18:33:46 +020039/* Consider a PDCH as idle if has at most this number of TBFs assigned to it */
40#define PDCH_IDLE_TBF_THRESH 1
41
Jacob Erlbeckea65c722015-06-22 16:14:23 +020042static char *set_flag_chars(char *buf, uint8_t val, char set_char, char unset_char = 0)
43{
44 int i;
45
46 for (i = 0; i < 8; i += 1, val = val >> 1) {
47 if (val & 1)
48 buf[i] = set_char;
49 else if (unset_char)
50 buf[i] = unset_char;
51 }
52
53 return buf;
54}
55
56static bool test_and_set_bit(uint32_t *bits, size_t elem)
57{
58 bool was_set = bits[elem/32] & (1 << (elem % 32));
59 bits[elem/32] |= (1 << (elem % 32));
60
61 return was_set;
62}
63
Maxa76a7d02018-01-26 11:09:16 +010064static inline int8_t find_free_tfi(const struct gprs_rlcmac_pdch *pdch, enum gprs_rlcmac_tbf_direction dir)
Jacob Erlbecke0853cd2015-07-10 12:25:25 +020065{
Maxa76a7d02018-01-26 11:09:16 +010066 uint32_t tfi_map = pdch->assigned_tfi(dir);
Jacob Erlbecke0853cd2015-07-10 12:25:25 +020067 int8_t tfi;
68
Maxd000d802017-09-20 17:55:28 +020069 if (tfi_map == NO_FREE_TFI)
Jacob Erlbecke0853cd2015-07-10 12:25:25 +020070 return -1;
71
72 /* look for USF, don't use USF=7 */
73 for (tfi = 0; tfi < 32; tfi++) {
74 if (!(tfi_map & (1 << tfi)))
75 return tfi;
76 }
77
78 return -1;
79}
80
Maxa76a7d02018-01-26 11:09:16 +010081static int find_possible_pdchs(const struct gprs_rlcmac_trx *trx, size_t max_slots, uint8_t mask,
82 const char *mask_reason = NULL)
Holger Hans Peter Freytherb0a00752013-09-29 08:18:17 +020083{
Jacob Erlbeckec478752015-06-19 16:35:38 +020084 unsigned ts;
85 int valid_ts_set = 0;
Jacob Erlbeck83426b22015-06-30 09:44:05 +020086 int8_t last_tsc = -1; /* must be signed */
Jacob Erlbeckec478752015-06-19 16:35:38 +020087
88 for (ts = 0; ts < ARRAY_SIZE(trx->pdch); ts++) {
Maxa76a7d02018-01-26 11:09:16 +010089 const struct gprs_rlcmac_pdch *pdch;
Holger Hans Peter Freytherb0a00752013-09-29 08:18:17 +020090
91 pdch = &trx->pdch[ts];
Holger Hans Peter Freyther17b0d832013-10-19 17:37:48 +020092 if (!pdch->is_enabled()) {
Holger Hans Peter Freytherb0a00752013-09-29 08:18:17 +020093 LOGP(DRLCMAC, LOGL_DEBUG, "- Skipping TS %d, because "
94 "not enabled\n", ts);
95 continue;
96 }
Jacob Erlbeckec478752015-06-19 16:35:38 +020097
98 if (((1 << ts) & mask) == 0) {
99 if (mask_reason)
100 LOGP(DRLCMAC, LOGL_DEBUG,
101 "- Skipping TS %d, because %s\n",
102 ts, mask_reason);
103 continue;
104 }
105
Jacob Erlbeck83426b22015-06-30 09:44:05 +0200106 if (max_slots > 1) {
107 /* check if TSC changes, see TS 45.002, 6.4.2 */
108 if (last_tsc < 0)
109 last_tsc = pdch->tsc;
110 else if (last_tsc != pdch->tsc) {
111 LOGP(DRLCMAC, LOGL_ERROR,
112 "Skipping TS %d of TRX=%d, because it "
113 "has different TSC than lower TS of TRX. "
114 "In order to allow multislot, all "
115 "slots must be configured with the same "
116 "TSC!\n", ts, trx->trx_no);
117 continue;
118 }
119 }
120
Jacob Erlbeckec478752015-06-19 16:35:38 +0200121 valid_ts_set |= 1 << ts;
Holger Hans Peter Freytherb0a00752013-09-29 08:18:17 +0200122 }
123
Jacob Erlbeckec478752015-06-19 16:35:38 +0200124 return valid_ts_set;
125}
126
Maxa76a7d02018-01-26 11:09:16 +0100127static int compute_usage_by_num_tbfs(const struct gprs_rlcmac_pdch *pdch, enum gprs_rlcmac_tbf_direction dir)
Jacob Erlbeckc135b872015-07-09 13:44:18 +0200128{
129 return pdch->num_tbfs(dir);
130}
131
Maxa76a7d02018-01-26 11:09:16 +0100132static int compute_usage_by_reservation(const struct gprs_rlcmac_pdch *pdch, enum gprs_rlcmac_tbf_direction)
Jacob Erlbeckc135b872015-07-09 13:44:18 +0200133{
134 return
135 pdch->num_reserved(GPRS_RLCMAC_DL_TBF) +
136 pdch->num_reserved(GPRS_RLCMAC_UL_TBF);
137}
138
Maxa76a7d02018-01-26 11:09:16 +0100139static int compute_usage_for_algo_a(const struct gprs_rlcmac_pdch *pdch, enum gprs_rlcmac_tbf_direction dir)
Jacob Erlbeck7af53e62015-07-16 15:04:07 +0200140{
141 int usage =
142 pdch->num_tbfs(GPRS_RLCMAC_DL_TBF) +
143 pdch->num_tbfs(GPRS_RLCMAC_UL_TBF) +
144 compute_usage_by_reservation(pdch, dir);
145
Maxd000d802017-09-20 17:55:28 +0200146 if (pdch->assigned_tfi(reverse(dir)) == NO_FREE_TFI)
Jacob Erlbeck7af53e62015-07-16 15:04:07 +0200147 /* No TFI in the opposite direction, avoid it */
148 usage += 32;
149
150 return usage;
151
152}
153
Maxa76a7d02018-01-26 11:09:16 +0100154/*! Return the TS which corresponds to least busy PDCH
155 *
156 * \param[in] trx Pointer to TRX object
157 * \param[in] dir TBF direction
158 * \param[in] mask set of available timeslots
159 * \param[in] fn Function pointer to function which computes number of associated TBFs
160 * \param[out] free_tfi Free TFI
161 * \param[out] free_usf Free USF
162 * \returns TS number or -1 if unable to find
163 */
164static int find_least_busy_pdch(const struct gprs_rlcmac_trx *trx, enum gprs_rlcmac_tbf_direction dir, uint8_t mask,
165 int (*fn)(const struct gprs_rlcmac_pdch *, enum gprs_rlcmac_tbf_direction dir),
166 int *free_tfi = 0, int *free_usf = 0)
Jacob Erlbeckec478752015-06-19 16:35:38 +0200167{
168 unsigned ts;
169 int min_used = INT_MAX;
170 int min_ts = -1;
Jacob Erlbecke0853cd2015-07-10 12:25:25 +0200171 int min_tfi = -1;
Jacob Erlbeckec478752015-06-19 16:35:38 +0200172 int min_usf = -1;
173
174 for (ts = 0; ts < ARRAY_SIZE(trx->pdch); ts++) {
Maxa76a7d02018-01-26 11:09:16 +0100175 const struct gprs_rlcmac_pdch *pdch = &trx->pdch[ts];
Jacob Erlbeckec478752015-06-19 16:35:38 +0200176 int num_tbfs;
177 int usf = -1; /* must be signed */
Jacob Erlbecke0853cd2015-07-10 12:25:25 +0200178 int tfi = -1;
Jacob Erlbeckec478752015-06-19 16:35:38 +0200179
180 if (((1 << ts) & mask) == 0)
181 continue;
182
Jacob Erlbeckc135b872015-07-09 13:44:18 +0200183 num_tbfs = fn(pdch, dir);
Jacob Erlbeckefe62a72015-07-02 15:48:25 +0200184
185 if (num_tbfs < min_used) {
186 /* We have found a candidate */
Jacob Erlbecke0853cd2015-07-10 12:25:25 +0200187 /* Make sure that a TFI is available */
188 if (free_tfi) {
189 tfi = find_free_tfi(pdch, dir);
190 if (tfi < 0) {
191 LOGP(DRLCMAC, LOGL_DEBUG,
192 "- Skipping TS %d, because "
193 "no TFI available\n", ts);
194 continue;
195 }
196 }
Jacob Erlbeckefe62a72015-07-02 15:48:25 +0200197 /* Make sure that an USF is available */
198 if (dir == GPRS_RLCMAC_UL_TBF) {
Maxadca67b2018-01-31 15:22:36 +0100199 usf = find_free_usf(pdch->assigned_usf());
Jacob Erlbeckefe62a72015-07-02 15:48:25 +0200200 if (usf < 0) {
201 LOGP(DRLCMAC, LOGL_DEBUG,
202 "- Skipping TS %d, because "
203 "no USF available\n", ts);
204 continue;
205 }
206 }
207 if (min_ts >= 0)
208 LOGP(DRLCMAC, LOGL_DEBUG,
209 "- Skipping TS %d, because "
210 "num TBFs %d > %d\n",
211 min_ts, min_used, num_tbfs);
212 min_used = num_tbfs;
213 min_ts = ts;
Jacob Erlbecke0853cd2015-07-10 12:25:25 +0200214 min_tfi = tfi;
Jacob Erlbeckefe62a72015-07-02 15:48:25 +0200215 min_usf = usf;
216 } else {
217 LOGP(DRLCMAC, LOGL_DEBUG,
218 "- Skipping TS %d, because "
219 "num TBFs %d >= %d\n",
220 ts, num_tbfs, min_used);
221 }
222 }
223
224 if (min_ts < 0)
225 return -1;
226
Jacob Erlbecke0853cd2015-07-10 12:25:25 +0200227 if (free_tfi)
228 *free_tfi = min_tfi;
Jacob Erlbeckefe62a72015-07-02 15:48:25 +0200229 if (free_usf)
230 *free_usf = min_usf;
231
232 return min_ts;
233}
234
Jacob Erlbeckccc34e42015-06-29 13:45:05 +0200235static void attach_tbf_to_pdch(struct gprs_rlcmac_pdch *pdch,
236 struct gprs_rlcmac_tbf *tbf)
237{
238 if (tbf->pdch[pdch->ts_no])
239 tbf->pdch[pdch->ts_no]->detach_tbf(tbf);
240
241 tbf->pdch[pdch->ts_no] = pdch;
242 pdch->attach_tbf(tbf);
243}
244
Maxa76a7d02018-01-26 11:09:16 +0100245static void assign_uplink_tbf_usf(struct gprs_rlcmac_pdch *pdch, struct gprs_rlcmac_ul_tbf *tbf, uint8_t tfi, int8_t usf)
Holger Hans Peter Freyther02ab4a82013-09-29 07:37:40 +0200246{
Jacob Erlbecke0853cd2015-07-10 12:25:25 +0200247 tbf->m_tfi = tfi;
Daniel Willmann7e994e32014-08-07 15:49:21 +0200248 tbf->m_usf[pdch->ts_no] = usf;
Jacob Erlbeckccc34e42015-06-29 13:45:05 +0200249 attach_tbf_to_pdch(pdch, tbf);
Holger Hans Peter Freyther02ab4a82013-09-29 07:37:40 +0200250}
251
Maxa76a7d02018-01-26 11:09:16 +0100252static void assign_dlink_tbf(struct gprs_rlcmac_pdch *pdch, struct gprs_rlcmac_dl_tbf *tbf, uint8_t tfi)
Holger Hans Peter Freyther8481a052013-09-29 08:08:28 +0200253{
Jacob Erlbecke0853cd2015-07-10 12:25:25 +0200254 tbf->m_tfi = tfi;
Jacob Erlbeckccc34e42015-06-29 13:45:05 +0200255 attach_tbf_to_pdch(pdch, tbf);
Holger Hans Peter Freyther8481a052013-09-29 08:08:28 +0200256}
257
Maxa76a7d02018-01-26 11:09:16 +0100258static int find_trx(const struct gprs_rlcmac_bts *bts_data, const GprsMs *ms, int8_t use_trx)
Jacob Erlbecke0853cd2015-07-10 12:25:25 +0200259{
260 unsigned trx_no;
261 unsigned ts;
Jacob Erlbecke0853cd2015-07-10 12:25:25 +0200262
263 /* We must use the TRX currently actively used by an MS */
264 if (ms && ms->current_trx())
265 return ms->current_trx()->trx_no;
266
267 if (use_trx >= 0 && use_trx < 8)
268 return use_trx;
269
270 /* Find the first TRX that has a PDCH with a free UL and DL TFI */
271 for (trx_no = 0; trx_no < ARRAY_SIZE(bts_data->trx); trx_no += 1) {
Maxa76a7d02018-01-26 11:09:16 +0100272 const struct gprs_rlcmac_trx *trx = &bts_data->trx[trx_no];
Jacob Erlbecke0853cd2015-07-10 12:25:25 +0200273 for (ts = 0; ts < ARRAY_SIZE(trx->pdch); ts++) {
Maxa76a7d02018-01-26 11:09:16 +0100274 const struct gprs_rlcmac_pdch *pdch = &trx->pdch[ts];
Jacob Erlbecke0853cd2015-07-10 12:25:25 +0200275 if (!pdch->is_enabled())
276 continue;
277
Maxd000d802017-09-20 17:55:28 +0200278 if (pdch->assigned_tfi(GPRS_RLCMAC_UL_TBF) == NO_FREE_TFI)
Jacob Erlbecke0853cd2015-07-10 12:25:25 +0200279 continue;
280
Maxd000d802017-09-20 17:55:28 +0200281 if (pdch->assigned_tfi(GPRS_RLCMAC_DL_TBF) == NO_FREE_TFI)
Jacob Erlbecke0853cd2015-07-10 12:25:25 +0200282 continue;
283
284 return trx_no;
285 }
286 }
287
288 return -EBUSY;
289}
290
Maxa76a7d02018-01-26 11:09:16 +0100291static bool idle_pdch_avail(const struct gprs_rlcmac_bts *bts_data)
Jacob Erlbeck77da3552015-07-16 18:33:46 +0200292{
293 unsigned trx_no;
294 unsigned ts;
Jacob Erlbeck77da3552015-07-16 18:33:46 +0200295
296 /* Find the first PDCH with an unused DL TS */
297 for (trx_no = 0; trx_no < ARRAY_SIZE(bts_data->trx); trx_no += 1) {
Maxa76a7d02018-01-26 11:09:16 +0100298 const struct gprs_rlcmac_trx *trx = &bts_data->trx[trx_no];
Jacob Erlbeck77da3552015-07-16 18:33:46 +0200299 for (ts = 0; ts < ARRAY_SIZE(trx->pdch); ts++) {
Maxa76a7d02018-01-26 11:09:16 +0100300 const struct gprs_rlcmac_pdch *pdch = &trx->pdch[ts];
Jacob Erlbeck77da3552015-07-16 18:33:46 +0200301 if (!pdch->is_enabled())
302 continue;
303
304 if (pdch->num_tbfs(GPRS_RLCMAC_DL_TBF) > PDCH_IDLE_TBF_THRESH)
305 continue;
306
Maxa76a7d02018-01-26 11:09:16 +0100307 return true;
Jacob Erlbeck77da3552015-07-16 18:33:46 +0200308 }
309 }
310
Maxa76a7d02018-01-26 11:09:16 +0100311 return false;
Jacob Erlbeck77da3552015-07-16 18:33:46 +0200312}
313
Maxa76a7d02018-01-26 11:09:16 +0100314/*! Return free TFI
315 *
316 * \param[in] bts Pointer to BTS struct
Max7e4921d2017-09-28 16:41:24 +0200317 * \param[in] trx Optional pointer to TRX struct
Maxa76a7d02018-01-26 11:09:16 +0100318 * \param[in] ms Pointer to MS object
319 * \param[in] dir DL or UL direction
320 * \param[in] use_trx which TRX to use or -1 if it should be selected based on what MS uses
321 * \param[out] trx_no_ TRX number on which TFI was found
322 * \returns negative error code or 0 on success
323 */
324static int tfi_find_free(const BTS *bts, const gprs_rlcmac_trx *trx, const GprsMs *ms,
325 enum gprs_rlcmac_tbf_direction dir, int8_t use_trx, uint8_t *trx_no_)
Jacob Erlbeck5879c642015-07-10 10:41:36 +0200326{
327 int tfi;
328 uint8_t trx_no;
329
Max7e4921d2017-09-28 16:41:24 +0200330 if (trx) {
331 if (use_trx >= 0 && use_trx != trx->trx_no) {
332 LOGP(DRLCMAC, LOGL_ERROR, "- Requested incompatible TRX %d (current is %d)\n",
333 use_trx, trx->trx_no);
334 return -EINVAL;
335 }
336 use_trx = trx->trx_no;
337 }
338
Jacob Erlbeck5879c642015-07-10 10:41:36 +0200339 if (use_trx == -1 && ms->current_trx())
340 use_trx = ms->current_trx()->trx_no;
341
342 tfi = bts->tfi_find_free(dir, &trx_no, use_trx);
343 if (tfi < 0)
344 return -EBUSY;
345
346 if (trx_no_)
347 *trx_no_ = trx_no;
348
349 return tfi;
350}
Holger Hans Peter Freyther02ab4a82013-09-29 07:37:40 +0200351
Maxe9fe0e32017-09-28 15:56:05 +0200352/*! Slot Allocation: Algorithm A
Holger Hans Peter Freyther02ab4a82013-09-29 07:37:40 +0200353 *
354 * Assign single slot for uplink and downlink
Maxe9fe0e32017-09-28 15:56:05 +0200355 *
356 * \param[in,out] bts Pointer to BTS struct
357 * \param[in,out] ms_ Pointer to MS object
358 * \param[in,out] tbf_ Pointer to TBF struct
359 * \param[in] single flag indicating if we should force single-slot allocation
360 * \param[in] use_trx which TRX to use or -1 if it should be selected during allocation
361 * \returns negative error code or 0 on success
Holger Hans Peter Freyther02ab4a82013-09-29 07:37:40 +0200362 */
Maxe9fe0e32017-09-28 15:56:05 +0200363int alloc_algorithm_a(struct gprs_rlcmac_bts *bts, GprsMs *ms_, struct gprs_rlcmac_tbf *tbf_, bool single,
364 int8_t use_trx)
Holger Hans Peter Freyther02ab4a82013-09-29 07:37:40 +0200365{
366 struct gprs_rlcmac_pdch *pdch;
Jacob Erlbeckec478752015-06-19 16:35:38 +0200367 int ts = -1;
Jacob Erlbeck5cd496d2015-06-30 10:24:37 +0200368 uint8_t ul_slots, dl_slots;
Jacob Erlbeck5879c642015-07-10 10:41:36 +0200369 int trx_no;
Jacob Erlbecke0853cd2015-07-10 12:25:25 +0200370 int tfi = -1;
Jacob Erlbeckec478752015-06-19 16:35:38 +0200371 int usf = -1;
372 int mask = 0xff;
373 const char *mask_reason = NULL;
Jacob Erlbeck5a2b8be2015-07-14 11:35:21 +0200374 const GprsMs *ms = ms_;
375 const gprs_rlcmac_tbf *tbf = tbf_;
376 gprs_rlcmac_trx *trx = ms->current_trx();
Holger Hans Peter Freyther02ab4a82013-09-29 07:37:40 +0200377
378 LOGP(DRLCMAC, LOGL_DEBUG, "Slot Allocation (Algorithm A) for class "
Jacob Erlbeckbefc7602015-06-02 12:33:30 +0200379 "%d\n", tbf->ms_class());
Holger Hans Peter Freyther02ab4a82013-09-29 07:37:40 +0200380
Maxa76a7d02018-01-26 11:09:16 +0100381 trx_no = find_trx(bts, ms, use_trx);
Jacob Erlbecke0853cd2015-07-10 12:25:25 +0200382 if (trx_no < 0) {
383 LOGP(DRLCMAC, LOGL_NOTICE,
384 "- Failed to find a usable TRX (TFI exhausted)\n");
385 return trx_no;
Jacob Erlbeck5879c642015-07-10 10:41:36 +0200386 }
Jacob Erlbeck5a2b8be2015-07-14 11:35:21 +0200387 if (!trx)
388 trx = &bts->trx[trx_no];
Jacob Erlbeck5879c642015-07-10 10:41:36 +0200389
Jacob Erlbeck5cd496d2015-06-30 10:24:37 +0200390 dl_slots = ms->reserved_dl_slots();
391 ul_slots = ms->reserved_ul_slots();
392
393 ts = ms->first_common_ts();
394
395 if (ts >= 0) {
Jacob Erlbeckec478752015-06-19 16:35:38 +0200396 mask_reason = "need to reuse TS";
Jacob Erlbeckec478752015-06-19 16:35:38 +0200397 mask = 1 << ts;
Jacob Erlbeck5cd496d2015-06-30 10:24:37 +0200398 } else if (dl_slots || ul_slots) {
399 mask_reason = "need to use a reserved common TS";
400 mask = dl_slots & ul_slots;
401 }
Jacob Erlbeckec478752015-06-19 16:35:38 +0200402
Jacob Erlbeck5a2b8be2015-07-14 11:35:21 +0200403 mask = find_possible_pdchs(trx, 1, mask, mask_reason);
Jacob Erlbeckec478752015-06-19 16:35:38 +0200404 if (!mask)
Holger Hans Peter Freyther02ab4a82013-09-29 07:37:40 +0200405 return -EINVAL;
406
Jacob Erlbeck5a2b8be2015-07-14 11:35:21 +0200407 ts = find_least_busy_pdch(trx, tbf->direction, mask,
Jacob Erlbeck7af53e62015-07-16 15:04:07 +0200408 compute_usage_for_algo_a,
Jacob Erlbecke0853cd2015-07-10 12:25:25 +0200409 &tfi, &usf);
Jacob Erlbeckec478752015-06-19 16:35:38 +0200410
Jacob Erlbeck5a2b8be2015-07-14 11:35:21 +0200411 if (tbf->direction == GPRS_RLCMAC_UL_TBF && usf < 0) {
Jacob Erlbeckec478752015-06-19 16:35:38 +0200412 LOGP(DRLCMAC, LOGL_NOTICE, "- Failed "
Jacob Erlbeck5a2b8be2015-07-14 11:35:21 +0200413 "to allocate a TS, no USF available\n");
Jacob Erlbeckec478752015-06-19 16:35:38 +0200414 return -EBUSY;
415 }
416
Jacob Erlbeck5a2b8be2015-07-14 11:35:21 +0200417 if (ts < 0) {
418 LOGP(DRLCMAC, LOGL_NOTICE, "- Failed "
419 "to allocate a TS, no TFI available\n");
420 return -EBUSY;
421 }
422
423 pdch = &trx->pdch[ts];
424
425 /* The allocation will be successful, so the system state and tbf_/ms_
426 * may be modified from now on. */
Holger Hans Peter Freyther02ab4a82013-09-29 07:37:40 +0200427 if (tbf->direction == GPRS_RLCMAC_UL_TBF) {
Jacob Erlbeckaa9daa12015-12-28 18:49:12 +0100428 struct gprs_rlcmac_ul_tbf *ul_tbf = as_ul_tbf(tbf_);
Jacob Erlbecke0853cd2015-07-10 12:25:25 +0200429 LOGP(DRLCMAC, LOGL_DEBUG, "- Assign uplink TS=%d TFI=%d USF=%d\n",
430 ts, tfi, usf);
431 assign_uplink_tbf_usf(pdch, ul_tbf, tfi, usf);
Holger Hans Peter Freyther02ab4a82013-09-29 07:37:40 +0200432 } else {
Jacob Erlbeckaa9daa12015-12-28 18:49:12 +0100433 struct gprs_rlcmac_dl_tbf *dl_tbf = as_dl_tbf(tbf_);
Jacob Erlbecke0853cd2015-07-10 12:25:25 +0200434 LOGP(DRLCMAC, LOGL_DEBUG, "- Assign downlink TS=%d TFI=%d\n",
435 ts, tfi);
436 assign_dlink_tbf(pdch, dl_tbf, tfi);
Holger Hans Peter Freyther02ab4a82013-09-29 07:37:40 +0200437 }
Holger Hans Peter Freyther02ab4a82013-09-29 07:37:40 +0200438
Jacob Erlbeck5a2b8be2015-07-14 11:35:21 +0200439 tbf_->trx = trx;
440 /* the only one TS is the common TS */
441 tbf_->first_ts = tbf_->first_common_ts = ts;
442 ms_->set_reserved_slots(trx, 1 << ts, 1 << ts);
443
444 tbf_->upgrade_to_multislot = 0;
Jacob Erlbeck5979fe92015-07-14 14:02:41 +0200445 bts->bts->tbf_alloc_algo_a();
Holger Hans Peter Freyther02ab4a82013-09-29 07:37:40 +0200446 return 0;
447}
448
Maxadca67b2018-01-31 15:22:36 +0100449/*! Compute capacity of a given TRX
450 *
451 * \param[in] trx Pointer to TRX object
452 * \param[in] rx_window Receive window
453 * \param[in] tx_window Transmit window
454 * \returns non-negative capacity
455 */
456static inline unsigned compute_capacity(const struct gprs_rlcmac_trx *trx, int rx_window, int tx_window)
457{
458 const struct gprs_rlcmac_pdch *pdch;
459 unsigned ts, capacity = 0;
460
461 for (ts = 0; ts < ARRAY_SIZE(trx->pdch); ts++) {
462 pdch = &trx->pdch[ts];
463 if (rx_window & (1 << ts))
464 capacity += OSMO_MAX(32 - pdch->num_reserved(GPRS_RLCMAC_DL_TBF), 1);
465
466 /* Only consider common slots for UL */
467 if (tx_window & rx_window & (1 << ts)) {
468 if (find_free_usf(pdch->assigned_usf()) >= 0)
469 capacity += OSMO_MAX(32 - pdch->num_reserved(GPRS_RLCMAC_UL_TBF), 1);
470 }
471 }
472
473 return capacity;
474}
475
Maxa76a7d02018-01-26 11:09:16 +0100476/*! Find set of slots available for allocation while taking MS class into account
477 *
478 * \param[in] trx Pointer to TRX object
479 * \param[in] mslot_class The multislot class
480 * \param[in,out] ul_slots set of UL timeslots
481 * \param[in,out] dl_slots set of DL timeslots
482 * \returns negative error code or 0 on success
483 */
Max46fbfce2017-11-01 19:22:25 +0100484int find_multi_slots(struct gprs_rlcmac_trx *trx, uint8_t mslot_class, uint8_t *ul_slots, uint8_t *dl_slots)
Holger Hans Peter Freyther02ab4a82013-09-29 07:37:40 +0200485{
Maxf633b8d2018-01-31 15:28:53 +0100486 uint8_t Tx = mslot_class_get_tx(mslot_class), /* Max number of Tx slots */
487 Sum = mslot_class_get_sum(mslot_class); /* Max number of Tx + Rx slots */
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200488 int rx_window, tx_window, pdch_slots;
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200489 char slot_info[9] = {0};
Maxf633b8d2018-01-31 15:28:53 +0100490 int max_capacity = -1;
491 uint8_t max_ul_slots = 0, max_dl_slots = 0;
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200492 unsigned max_slots;
Holger Hans Peter Freyther02ab4a82013-09-29 07:37:40 +0200493
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200494 unsigned ul_ts, dl_ts;
495 unsigned num_tx;
Jacob Erlbeck5e46a202015-07-09 11:48:23 +0200496 unsigned mask_sel;
Holger Hans Peter Freyther02ab4a82013-09-29 07:37:40 +0200497
Max842d7812017-11-01 18:11:24 +0100498 if (mslot_class)
499 LOGP(DRLCMAC, LOGL_DEBUG, "Slot Allocation (Algorithm B) for class %d\n",
500 mslot_class);
501
Max842d7812017-11-01 18:11:24 +0100502 if (Tx == MS_NA) {
503 LOGP(DRLCMAC, LOGL_NOTICE, "Multislot class %d not applicable.\n",
504 mslot_class);
Holger Hans Peter Freyther02ab4a82013-09-29 07:37:40 +0200505 return -EINVAL;
506 }
507
Max842d7812017-11-01 18:11:24 +0100508 max_slots = OSMO_MAX(mslot_class_get_rx(mslot_class), Tx);
Holger Hans Peter Freyther02ab4a82013-09-29 07:37:40 +0200509
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200510 if (*dl_slots == 0)
511 *dl_slots = 0xff;
Holger Hans Peter Freyther02ab4a82013-09-29 07:37:40 +0200512
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200513 if (*ul_slots == 0)
514 *ul_slots = 0xff;
Holger Hans Peter Freyther02ab4a82013-09-29 07:37:40 +0200515
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200516 pdch_slots = find_possible_pdchs(trx, max_slots, 0xff);
Holger Hans Peter Freyther02ab4a82013-09-29 07:37:40 +0200517
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200518 *dl_slots &= pdch_slots;
519 *ul_slots &= pdch_slots;
Holger Hans Peter Freyther02ab4a82013-09-29 07:37:40 +0200520
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200521 LOGP(DRLCMAC, LOGL_DEBUG, "- Possible DL/UL slots: (TS=0)\"%s\"(TS=7)\n",
522 set_flag_chars(set_flag_chars(set_flag_chars(slot_info,
523 *dl_slots, 'D', '.'),
524 *ul_slots, 'U'),
525 *ul_slots & *dl_slots, 'C'));
526
527 /* Check for each UL (TX) slot */
528
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200529 /* Iterate through possible numbers of TX slots */
Max842d7812017-11-01 18:11:24 +0100530 for (num_tx = 1; num_tx <= mslot_class_get_tx(mslot_class); num_tx += 1) {
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200531 uint16_t tx_valid_win = (1 << num_tx) - 1;
Maxf633b8d2018-01-31 15:28:53 +0100532 uint8_t rx_mask[MASK_TR + 1];
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200533
Maxf633b8d2018-01-31 15:28:53 +0100534 mslot_fill_rx_mask(mslot_class, num_tx, rx_mask);
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200535
536 /* Rotate group of TX slots: UUU-----, -UUU----, ..., UU-----U */
537 for (ul_ts = 0; ul_ts < 8; ul_ts += 1, tx_valid_win <<= 1) {
538 unsigned tx_slot_count;
539 int max_rx;
540 uint16_t rx_valid_win;
541 uint32_t checked_rx[256/32] = {0};
542
543 /* Wrap valid window */
544 tx_valid_win = (tx_valid_win | tx_valid_win >> 8) & 0xff;
545
546 tx_window = tx_valid_win;
547
548 /* Filter out unavailable slots */
549 tx_window &= *ul_slots;
550
Jacob Erlbecke21b79c2015-07-16 11:48:43 +0200551 /* Skip if the the first TS (ul_ts) is not in the set */
552 if ((tx_window & (1 << ul_ts)) == 0)
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200553 continue;
554
Jacob Erlbecke21b79c2015-07-16 11:48:43 +0200555 /* Skip if the the last TS (ul_ts+num_tx-1) is not in the set */
556 if ((tx_window & (1 << ((ul_ts+num_tx-1) % 8))) == 0)
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200557 continue;
558
Jacob Erlbeck8cba7e92016-01-19 15:48:03 +0100559 tx_slot_count = pcu_bitcount(tx_window);
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200560
Max842d7812017-11-01 18:11:24 +0100561 max_rx = OSMO_MIN(mslot_class_get_rx(mslot_class), Sum - num_tx);
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200562 rx_valid_win = (1 << max_rx) - 1;
563
564 /* Rotate group of RX slots: DDD-----, -DDD----, ..., DD-----D */
565 for (dl_ts = 0; dl_ts < 8; dl_ts += 1, rx_valid_win <<= 1) {
566 /* Wrap valid window */
567 rx_valid_win = (rx_valid_win | rx_valid_win >> 8) & 0xff;
568
569 /* Validate with both Tta/Ttb/Trb and Ttb/Tra/Trb */
Jacob Erlbeck5e46a202015-07-09 11:48:23 +0200570 for (mask_sel = MASK_TT; mask_sel <= MASK_TR; mask_sel += 1) {
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200571 unsigned common_slot_count;
572 unsigned req_common_slots;
573 unsigned rx_slot_count;
574 uint16_t rx_bad;
575 uint8_t rx_good;
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200576 int capacity;
577
578 /* Filter out bad slots */
Jacob Erlbeck5e46a202015-07-09 11:48:23 +0200579 rx_bad = (uint16_t)(0xff & ~rx_mask[mask_sel]) << ul_ts;
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200580 rx_bad = (rx_bad | (rx_bad >> 8)) & 0xff;
581 rx_good = *dl_slots & ~rx_bad;
582
583 /* TODO: CHECK this calculation -> separate function for unit
584 * testing */
585
586 rx_window = rx_good & rx_valid_win;
Jacob Erlbeck8cba7e92016-01-19 15:48:03 +0100587 rx_slot_count = pcu_bitcount(rx_window);
Jacob Erlbeckbae33a72015-07-06 14:55:13 +0200588
589#if 0
590 LOGP(DRLCMAC, LOGL_DEBUG, "n_tx=%d, n_rx=%d, mask_sel=%d, "
591 "tx=%02x, rx=%02x, mask=%02x, bad=%02x, good=%02x, "
592 "ul=%02x, dl=%02x\n",
593 tx_slot_count, rx_slot_count, mask_sel,
594 tx_window, rx_window, rx_mask[mask_sel], rx_bad, rx_good,
595 *ul_slots, *dl_slots);
596#endif
597
598 /* Check compliance with TS 45.002, table 6.4.2.2.1 */
599 /* Whether to skip this round doesn not only depend on the bit
600 * sets but also on mask_sel. Therefore this check must be done
601 * before doing the test_and_set_bit shortcut. */
Maxf633b8d2018-01-31 15:28:53 +0100602 if (mslot_class_get_type(mslot_class) == 1) {
Jacob Erlbeckbae33a72015-07-06 14:55:13 +0200603 unsigned slot_sum = rx_slot_count + tx_slot_count;
604 /* Assume down+up/dynamic.
605 * TODO: For ext-dynamic, down only, up only add more
606 * cases.
607 */
608 if (slot_sum <= 6 && tx_slot_count < 3) {
609 if (mask_sel != MASK_TR)
610 /* Skip Tta */
611 continue;
612 } else if (slot_sum > 6 && tx_slot_count < 3) {
613 if (mask_sel != MASK_TT)
614 /* Skip Tra */
615 continue;
616 } else {
617 /* No supported row in table 6.4.2.2.1. */
618#ifdef ENABLE_TS_ALLOC_DEBUG
619 LOGP(DRLCMAC, LOGL_DEBUG,
620 "- Skipping DL/UL slots: (TS=0)\"%s\"(TS=7), "
621 "combination not supported\n",
622 set_flag_chars(set_flag_chars(set_flag_chars(
623 slot_info,
624 rx_bad, 'x', '.'),
625 rx_window, 'D'),
626 tx_window, 'U'));
627#endif
628 continue;
629 }
630 }
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200631
632 /* Avoid repeated RX combination check */
633 if (test_and_set_bit(checked_rx, rx_window))
634 continue;
635
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200636 if (!rx_good) {
Jacob Erlbeck1653f832015-06-30 14:48:13 +0200637#ifdef ENABLE_TS_ALLOC_DEBUG
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200638 LOGP(DRLCMAC, LOGL_DEBUG,
639 "- Skipping DL/UL slots: (TS=0)\"%s\"(TS=7), "
640 "no DL slots available\n",
641 set_flag_chars(set_flag_chars(slot_info,
642 rx_bad, 'x', '.'),
643 tx_window, 'U'));
Jacob Erlbeck1653f832015-06-30 14:48:13 +0200644#endif
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200645 continue;
646 }
647
648 if (!rx_window)
649 continue;
650
651 /* Check number of common slots according to TS 54.002, 6.4.2.2 */
Jacob Erlbeck8cba7e92016-01-19 15:48:03 +0100652 common_slot_count = pcu_bitcount(tx_window & rx_window);
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200653 req_common_slots = OSMO_MIN(tx_slot_count, rx_slot_count);
Maxf633b8d2018-01-31 15:28:53 +0100654 if (mslot_class_get_type(mslot_class) == 1)
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200655 req_common_slots = OSMO_MIN(req_common_slots, 2);
656
657 if (req_common_slots != common_slot_count) {
Jacob Erlbeck1653f832015-06-30 14:48:13 +0200658#ifdef ENABLE_TS_ALLOC_DEBUG
659 LOGP(DRLCMAC, LOGL_DEBUG,
660 "- Skipping DL/UL slots: (TS=0)\"%s\"(TS=7), "
661 "invalid number of common TS: %d (expected %d)\n",
662 set_flag_chars(set_flag_chars(set_flag_chars(
663 slot_info,
664 rx_bad, 'x', '.'),
665 rx_window, 'D'),
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200666 tx_window, 'U'),
Jacob Erlbeck1653f832015-06-30 14:48:13 +0200667 common_slot_count,
668 req_common_slots);
669#endif
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200670 continue;
671 }
672
673 /* Compute capacity */
Maxadca67b2018-01-31 15:22:36 +0100674 capacity = compute_capacity(trx, rx_window, tx_window);
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200675
Jacob Erlbeck1653f832015-06-30 14:48:13 +0200676#ifdef ENABLE_TS_ALLOC_DEBUG
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200677 LOGP(DRLCMAC, LOGL_DEBUG,
678 "- Considering DL/UL slots: (TS=0)\"%s\"(TS=7), "
679 "capacity = %d\n",
680 set_flag_chars(set_flag_chars(set_flag_chars(set_flag_chars(
681 slot_info,
682 rx_bad, 'x', '.'),
683 rx_window, 'D'),
684 tx_window, 'U'),
685 rx_window & tx_window, 'C'),
686 capacity);
Jacob Erlbeck1653f832015-06-30 14:48:13 +0200687#endif
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200688
689 if (capacity <= max_capacity)
690 continue;
691
692 max_capacity = capacity;
693 max_ul_slots = tx_window;
694 max_dl_slots = rx_window;
Maxadca67b2018-01-31 15:22:36 +0100695 }
696 }
697 }
698 }
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200699
700 if (!max_ul_slots || !max_dl_slots) {
701 LOGP(DRLCMAC, LOGL_NOTICE,
702 "No valid UL/DL slot combination found\n");
703 return -EINVAL;
704 }
705
706 *ul_slots = max_ul_slots;
707 *dl_slots = max_dl_slots;
708
709 return 0;
710}
711
Max0cc72122018-01-31 17:00:06 +0100712/*! Count used bits in slots and reserved_slots bitmasks
713 *
714 * \param[in] slots Timeslots in use
715 * \param[in] reserved_slots Reserved timeslots
716 * \param[out] slotcount Number of TS in use
717 * \param[out] avail_count Number of reserved TS
718 */
719static void update_slot_counters(uint8_t slots, uint8_t reserved_slots, uint8_t *slotcount, uint8_t *avail_count)
720{
721 (*slotcount) = pcu_bitcount(slots);
722 (*avail_count) = pcu_bitcount(reserved_slots);
723}
724
725/*! Return slot mask with single TS from a given UL/DL set according to TBF's direction, ts pointer is set to that TS
726 * number or to negative value on error
727 *
728 * \param[in] trx Pointer to TRX object
729 * \param[in] tbf Pointer to TBF object
730 * \param[in] dl_slots set of DL timeslots
731 * \param[in] ul_slots set of UL timeslots
732 * \param[in] ts corresponding TS or -1 for autoselection
733 * \returns slot mask with single UL or DL timeslot number if possible
734 */
735static uint8_t get_single_ts(const gprs_rlcmac_trx *trx, const gprs_rlcmac_tbf *tbf, uint8_t dl_slots, uint8_t ul_slots,
736 int ts)
737{
738 uint8_t ret = dl_slots & ul_slots; /* Make sure to consider the first common slot only */
739
740 if (ts < 0)
741 ts = find_least_busy_pdch(trx, tbf->direction, ret, compute_usage_by_num_tbfs, NULL, NULL);
742
743 if (ts < 0)
744 return ffs(ret);
745
746 return ret & (1 << ts);
747}
748
749/*! Find set of timeslots available for allocation
750 *
751 * \param[in] trx Pointer to TRX object
752 * \param[in] tbf Pointer to TBF object
753 * \param[in] single Flag to force the single TS allocation
754 * \param[in] ul_slots set of UL timeslots
755 * \param[in] dl_slots set of DL timeslots
756 * \param[in] reserved_ul_slots set of reserved UL timeslots
757 * \param[in] reserved_dl_slots set of reserved DL timeslots
758 * \param[in] first_common_ts First TS common for both UL and DL or -1 if unknown
759 * \returns negative error code or selected TS on success
760 */
761static int tbf_select_slot_set(const gprs_rlcmac_tbf *tbf, const gprs_rlcmac_trx *trx, bool single,
762 uint8_t ul_slots, uint8_t dl_slots,
763 uint8_t reserved_ul_slots, uint8_t reserved_dl_slots,
764 int8_t first_common_ts)
765{
766 uint8_t sl = tbf->direction != GPRS_RLCMAC_DL_TBF ? ul_slots : dl_slots;
767 char slot_info[9] = { 0 };
768
769 if (single)
770 sl = get_single_ts(trx, tbf, dl_slots, ul_slots, first_common_ts);
771
772 if (!sl) {
773 LOGP(DRLCMAC, LOGL_NOTICE, "No %s slots available\n",
774 tbf->direction != GPRS_RLCMAC_DL_TBF ? "uplink" : "downlink");
775 return -EINVAL;
776 }
777
778 if (tbf->direction != GPRS_RLCMAC_DL_TBF) {
779 snprintf(slot_info, 9, OSMO_BIT_SPEC, OSMO_BIT_PRINT_EX(reserved_ul_slots, 'u'));
780 masked_override_with(slot_info, sl, 'U');
781 LOGPC(DRLCMAC, LOGL_DEBUG, "- Selected UL");
782 } else {
783 snprintf(slot_info, 9, OSMO_BIT_SPEC, OSMO_BIT_PRINT_EX(reserved_dl_slots, 'd'));
784 masked_override_with(slot_info, sl, 'D');
785 LOGPC(DRLCMAC, LOGL_DEBUG, "- Selected DL");
786 }
787
788 LOGPC(DRLCMAC, LOGL_DEBUG, " slots: (TS=0)\"%s\"(TS=7)%s\n", slot_info, single ? ", single" : "");
789
790 return sl;
791}
792
Max2afec6d2018-01-31 17:21:21 +0100793/*! Allocate USF according to a given UL TS mapping
794 *
795 * N. B: this is legacy implementation which ignores given selected_ul_slots
796 * \param[in] trx Pointer to TRX object
797 * \param[in] tbf Pointer to TBF object
798 * \param[in] first_common_ts First TS which is common to both UL and DL
799 * \param[in] selected_ul_slots set of UL timeslots selected for allocation
800 * \param[in] dl_slots set of DL timeslots
801 * \param[out] usf array for allocated USF
802 * \returns updated UL TS or negative on error
803 */
804static int allocate_usf(const gprs_rlcmac_trx *trx, int8_t first_common_ts, uint8_t selected_ul_slots, uint8_t dl_slots,
805 int *usf)
806{
807 int free_usf = -1, ts;
808 uint8_t ul_slots = selected_ul_slots;
809
810 if (first_common_ts >= 0)
811 ul_slots = 1 << first_common_ts;
812 else
813 ul_slots = ul_slots & dl_slots;
814
815 ts = find_least_busy_pdch(trx, GPRS_RLCMAC_UL_TBF, ul_slots, compute_usage_by_num_tbfs, NULL, &free_usf);
816
817 if (free_usf < 0 || ts < 0) {
818 LOGP(DRLCMAC, LOGL_NOTICE, "No USF available\n");
819 return -EBUSY;
820 }
821
822 OSMO_ASSERT(ts >= 0 && ts <= 8);
823
824 /* We will stick to that single UL slot, unreserve the others */
825 ul_slots = 1 << ts;
826 usf[ts] = free_usf;
827
828 return ul_slots;
829}
830
Maxe9fe0e32017-09-28 15:56:05 +0200831/*! Slot Allocation: Algorithm B
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200832 *
833 * Assign as many downlink slots as possible.
834 * Assign one uplink slot. (With free USF)
835 *
Maxe9fe0e32017-09-28 15:56:05 +0200836 * \param[in,out] bts Pointer to BTS struct
837 * \param[in,out] ms_ Pointer to MS object
838 * \param[in,out] tbf_ Pointer to TBF struct
839 * \param[in] single flag indicating if we should force single-slot allocation
840 * \param[in] use_trx which TRX to use or -1 if it should be selected during allocation
841 * \returns negative error code or 0 on success
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200842 */
Maxe9fe0e32017-09-28 15:56:05 +0200843int alloc_algorithm_b(struct gprs_rlcmac_bts *bts, GprsMs *ms_, struct gprs_rlcmac_tbf *tbf_, bool single,
844 int8_t use_trx)
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200845{
Jacob Erlbeck5a2b8be2015-07-14 11:35:21 +0200846 uint8_t dl_slots;
847 uint8_t ul_slots;
848 uint8_t reserved_dl_slots;
849 uint8_t reserved_ul_slots;
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200850 int8_t first_common_ts;
851 uint8_t slotcount = 0;
Maxa76a7d02018-01-26 11:09:16 +0100852 uint8_t avail_count = 0, trx_no;
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200853 char slot_info[9] = {0};
854 int ts;
Jacob Erlbeck5a2b8be2015-07-14 11:35:21 +0200855 int first_ts = -1;
856 int usf[8] = {-1, -1, -1, -1, -1, -1, -1, -1};
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200857 int rc;
Jacob Erlbeck5a2b8be2015-07-14 11:35:21 +0200858 int tfi;
Jacob Erlbeck5a2b8be2015-07-14 11:35:21 +0200859 const GprsMs *ms = ms_;
860 const gprs_rlcmac_tbf *tbf = tbf_;
861 gprs_rlcmac_trx *trx;
Jacob Erlbeck5879c642015-07-10 10:41:36 +0200862
Jacob Erlbeck5a2b8be2015-07-14 11:35:21 +0200863 /* Step 1: Get current state from the MS object */
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200864
865 if (!ms) {
866 LOGP(DRLCMAC, LOGL_ERROR, "MS not set\n");
867 return -EINVAL;
868 }
869
Max92e9c172017-09-28 16:25:25 +0200870 dl_slots = ms->reserved_dl_slots();
871 ul_slots = ms->reserved_ul_slots();
Jacob Erlbeck5a2b8be2015-07-14 11:35:21 +0200872 first_common_ts = ms->first_common_ts();
873 trx = ms->current_trx();
874
Jacob Erlbeck5a2b8be2015-07-14 11:35:21 +0200875 /* Step 2a: Find usable TRX and TFI */
Maxa76a7d02018-01-26 11:09:16 +0100876 tfi = tfi_find_free(bts->bts, trx, ms, tbf->direction, use_trx, &trx_no);
Jacob Erlbeck5a2b8be2015-07-14 11:35:21 +0200877 if (tfi < 0) {
878 LOGP(DRLCMAC, LOGL_NOTICE, "- Failed to allocate a TFI\n");
879 return tfi;
880 }
881
882 /* Step 2b: Reserve slots on the TRX for the MS */
883 if (!trx)
884 trx = &bts->trx[trx_no];
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200885
886 if (!dl_slots || !ul_slots) {
Max842d7812017-11-01 18:11:24 +0100887 rc = find_multi_slots(trx, ms->ms_class(), &ul_slots, &dl_slots);
Holger Hans Peter Freyther73193112013-12-26 09:49:05 +0100888 if (rc < 0)
889 return rc;
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200890 }
891
Max92e9c172017-09-28 16:25:25 +0200892 reserved_dl_slots = dl_slots;
893 reserved_ul_slots = ul_slots;
894
Max0cc72122018-01-31 17:00:06 +0100895 /* Step 3a: Derive the slot set for the current TBF */
896 rc = tbf_select_slot_set(tbf, trx, single, ul_slots, dl_slots, reserved_ul_slots, reserved_dl_slots,
897 first_common_ts);
898 if (rc < 0)
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200899 return -EINVAL;
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200900
Max0cc72122018-01-31 17:00:06 +0100901 first_ts = ffs(rc) - 1;
Holger Hans Peter Freyther02ab4a82013-09-29 07:37:40 +0200902
Max0cc72122018-01-31 17:00:06 +0100903 /* Step 3b: Derive the slot set for a given direction */
Holger Hans Peter Freyther02ab4a82013-09-29 07:37:40 +0200904 if (tbf->direction == GPRS_RLCMAC_DL_TBF) {
Max0cc72122018-01-31 17:00:06 +0100905 dl_slots = rc;
906 update_slot_counters(dl_slots, reserved_dl_slots, &slotcount, &avail_count);
Holger Hans Peter Freyther02ab4a82013-09-29 07:37:40 +0200907 } else {
Max2afec6d2018-01-31 17:21:21 +0100908 rc = allocate_usf(trx, first_common_ts, rc, dl_slots, usf);
909 if (rc < 0)
910 return rc;
Jacob Erlbeck5a2b8be2015-07-14 11:35:21 +0200911
Max0cc72122018-01-31 17:00:06 +0100912 /* We will stick to that single UL slot, unreserve the others */
Max2afec6d2018-01-31 17:21:21 +0100913 ul_slots = rc;
Jacob Erlbeck5a2b8be2015-07-14 11:35:21 +0200914 reserved_ul_slots = ul_slots;
Jacob Erlbeck5f494b82015-07-01 13:10:41 +0200915
Max0cc72122018-01-31 17:00:06 +0100916 update_slot_counters(ul_slots, reserved_ul_slots, &slotcount, &avail_count);
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200917 }
918
Jacob Erlbeckea65c722015-06-22 16:14:23 +0200919 first_common_ts = ffs(dl_slots & ul_slots) - 1;
920
921 if (first_common_ts < 0) {
922 LOGP(DRLCMAC, LOGL_NOTICE, "No first common slots available\n");
923 return -EINVAL;
924 }
Jacob Erlbeck5a2b8be2015-07-14 11:35:21 +0200925 if (first_ts < 0) {
926 LOGP(DRLCMAC, LOGL_NOTICE, "No first slot available\n");
927 return -EINVAL;
928 }
Holger Hans Peter Freyther02ab4a82013-09-29 07:37:40 +0200929
Jacob Erlbeck5a2b8be2015-07-14 11:35:21 +0200930 if (single && slotcount) {
931 tbf_->upgrade_to_multislot = (avail_count > slotcount);
932 LOGP(DRLCMAC, LOGL_INFO, "Using single slot at TS %d for %s\n",
933 first_ts,
934 (tbf->direction == GPRS_RLCMAC_DL_TBF) ? "DL" : "UL");
935 } else {
936 tbf_->upgrade_to_multislot = 0;
937 LOGP(DRLCMAC, LOGL_INFO, "Using %d slots for %s\n", slotcount,
938 (tbf->direction == GPRS_RLCMAC_DL_TBF) ? "DL" : "UL");
939 }
940
941 /* The allocation will be successful, so the system state and tbf_/ms_
942 * may be modified from now on. */
943
944 /* Step 4: Update MS and TBF and really allocate the resources */
945
946 /* The reserved slots have changed, update the MS */
947 if (reserved_ul_slots != ms->reserved_ul_slots() ||
948 reserved_dl_slots != ms->reserved_dl_slots())
949 {
950 ms_->set_reserved_slots(trx,
951 reserved_ul_slots, reserved_dl_slots);
952
953 LOGP(DRLCMAC, LOGL_DEBUG,
954 "- Reserved DL/UL slots: (TS=0)\"%s\"(TS=7)\n",
955 set_flag_chars(set_flag_chars(set_flag_chars(slot_info,
956 dl_slots, 'D', '.'),
957 ul_slots, 'U'),
958 ul_slots & dl_slots, 'C'));
959 }
960
961 tbf_->trx = trx;
962 tbf_->first_common_ts = first_common_ts;
963 tbf_->first_ts = first_ts;
964
965 if (tbf->direction == GPRS_RLCMAC_DL_TBF) {
Jacob Erlbeckaa9daa12015-12-28 18:49:12 +0100966 struct gprs_rlcmac_dl_tbf *dl_tbf = as_dl_tbf(tbf_);
Jacob Erlbeck5a2b8be2015-07-14 11:35:21 +0200967 for (ts = 0; ts < 8; ts++) {
968 if (!(dl_slots & (1 << ts)))
969 continue;
970
971 LOGP(DRLCMAC, LOGL_DEBUG, "- Assigning DL TS "
972 "%d\n", ts);
973 assign_dlink_tbf(&trx->pdch[ts], dl_tbf, tfi);
974 }
975 } else {
Jacob Erlbeckaa9daa12015-12-28 18:49:12 +0100976 struct gprs_rlcmac_ul_tbf *ul_tbf = as_ul_tbf(tbf_);
Jacob Erlbeck5a2b8be2015-07-14 11:35:21 +0200977
978 for (ts = 0; ts < 8; ts++) {
979 if (!(ul_slots & (1 << ts)))
980 continue;
981
982 OSMO_ASSERT(usf[ts] >= 0);
983
984 LOGP(DRLCMAC, LOGL_DEBUG, "- Assigning UL TS "
985 "%d\n", ts);
986 assign_uplink_tbf_usf(&trx->pdch[ts], ul_tbf,
987 tfi, usf[ts]);
988 }
989 }
Holger Hans Peter Freyther02ab4a82013-09-29 07:37:40 +0200990
Jacob Erlbeck5979fe92015-07-14 14:02:41 +0200991 bts->bts->tbf_alloc_algo_b();
992
Holger Hans Peter Freyther02ab4a82013-09-29 07:37:40 +0200993 return 0;
994}
Jacob Erlbeck400ec022015-07-14 13:31:48 +0200995
Maxe9fe0e32017-09-28 15:56:05 +0200996/*! Slot Allocation: Algorithm dynamic
Jacob Erlbeck400ec022015-07-14 13:31:48 +0200997 *
998 * This meta algorithm automatically selects on of the other algorithms based
999 * on the current system state.
1000 *
1001 * The goal is to support as many MS and TBF as possible. On low usage, the
1002 * goal is to provide the highest possible bandwidth per MS.
1003 *
Maxe9fe0e32017-09-28 15:56:05 +02001004 * \param[in,out] bts Pointer to BTS struct
1005 * \param[in,out] ms_ Pointer to MS object
1006 * \param[in,out] tbf_ Pointer to TBF struct
1007 * \param[in] single flag indicating if we should force single-slot allocation
1008 * \param[in] use_trx which TRX to use or -1 if it should be selected during allocation
1009 * \returns negative error code or 0 on success
Jacob Erlbeck400ec022015-07-14 13:31:48 +02001010 */
Maxe9fe0e32017-09-28 15:56:05 +02001011int alloc_algorithm_dynamic(struct gprs_rlcmac_bts *bts, GprsMs *ms_, struct gprs_rlcmac_tbf *tbf_, bool single,
1012 int8_t use_trx)
Jacob Erlbeck400ec022015-07-14 13:31:48 +02001013{
1014 int rc;
1015
Jacob Erlbeck77da3552015-07-16 18:33:46 +02001016 /* Reset load_is_high if there is at least one idle PDCH */
1017 if (bts->multislot_disabled) {
Maxa76a7d02018-01-26 11:09:16 +01001018 bts->multislot_disabled = !idle_pdch_avail(bts);
Jacob Erlbeck77da3552015-07-16 18:33:46 +02001019 if (!bts->multislot_disabled)
1020 LOGP(DRLCMAC, LOGL_DEBUG, "Enabling algorithm B\n");
1021 }
1022
1023 if (!bts->multislot_disabled) {
Maxe9fe0e32017-09-28 15:56:05 +02001024 rc = alloc_algorithm_b(bts, ms_, tbf_, single, use_trx);
Jacob Erlbeck77da3552015-07-16 18:33:46 +02001025 if (rc >= 0)
1026 return rc;
1027
1028 if (!bts->multislot_disabled)
1029 LOGP(DRLCMAC, LOGL_DEBUG, "Disabling algorithm B\n");
1030 bts->multislot_disabled = 1;
1031 }
Jacob Erlbeck400ec022015-07-14 13:31:48 +02001032
Maxe9fe0e32017-09-28 15:56:05 +02001033 return alloc_algorithm_a(bts, ms_, tbf_, single, use_trx);
Jacob Erlbeck400ec022015-07-14 13:31:48 +02001034}
Jacob Erlbeck7f79f0d2015-07-17 11:38:49 +02001035
1036int gprs_alloc_max_dl_slots_per_ms(struct gprs_rlcmac_bts *bts, uint8_t ms_class)
1037{
Max842d7812017-11-01 18:11:24 +01001038 int rx = mslot_class_get_rx(ms_class);
Jacob Erlbeck7f79f0d2015-07-17 11:38:49 +02001039
1040 if (rx == MS_NA)
1041 rx = 4;
1042
1043 if (bts->alloc_algorithm == alloc_algorithm_a)
1044 return 1;
1045
1046 if (bts->multislot_disabled)
1047 return 1;
1048
1049 return rx;
1050}