2013-09-29 05:37:40 +00:00
|
|
|
/* gprs_rlcmac.cpp
|
|
|
|
*
|
|
|
|
* Copyright (C) 2012 Ivan Klyuchnikov
|
|
|
|
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
|
|
|
|
* Copyright (C) 2013 by Holger Hans Peter Freyther
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <gprs_rlcmac.h>
|
|
|
|
#include <gprs_debug.h>
|
2013-10-19 19:10:38 +00:00
|
|
|
#include <bts.h>
|
2013-09-29 05:37:40 +00:00
|
|
|
#include <tbf.h>
|
2019-09-25 15:47:02 +00:00
|
|
|
#include <tbf_ul.h>
|
2018-02-19 16:17:28 +00:00
|
|
|
#include <pdch.h>
|
2015-06-18 15:16:26 +00:00
|
|
|
#include <gprs_ms.h>
|
2016-01-19 14:48:03 +00:00
|
|
|
#include <pcu_utils.h>
|
2013-09-29 05:37:40 +00:00
|
|
|
|
|
|
|
#include <errno.h>
|
2015-06-19 14:35:38 +00:00
|
|
|
#include <values.h>
|
2013-09-29 05:37:40 +00:00
|
|
|
|
2017-11-01 17:11:24 +00:00
|
|
|
extern "C" {
|
|
|
|
#include "mslot_class.h"
|
2018-01-26 12:31:42 +00:00
|
|
|
#include <osmocom/core/linuxlist.h>
|
|
|
|
#include <osmocom/core/logging.h>
|
|
|
|
#include <osmocom/core/utils.h>
|
2017-11-01 17:11:24 +00:00
|
|
|
}
|
|
|
|
|
2015-07-16 16:33:46 +00:00
|
|
|
/* Consider a PDCH as idle if has at most this number of TBFs assigned to it */
|
|
|
|
#define PDCH_IDLE_TBF_THRESH 1
|
|
|
|
|
2018-02-19 17:43:01 +00:00
|
|
|
#define LOGPSL(tbf, level, fmt, args...) LOGP(DRLCMAC, level, "[%s] " fmt, \
|
|
|
|
(tbf->direction == GPRS_RLCMAC_DL_TBF) ? "DL" : "UL", ## args)
|
|
|
|
|
|
|
|
#define LOGPAL(tbf, kind, single, trx_n, level, fmt, args...) LOGPSL(tbf, level, \
|
|
|
|
"algo %s <%s> (suggested TRX: %d): " fmt, \
|
|
|
|
kind, single ? "single" : "multi", trx_n, ## args)
|
|
|
|
|
2015-06-22 14:14:23 +00:00
|
|
|
static char *set_flag_chars(char *buf, uint8_t val, char set_char, char unset_char = 0)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < 8; i += 1, val = val >> 1) {
|
|
|
|
if (val & 1)
|
|
|
|
buf[i] = set_char;
|
|
|
|
else if (unset_char)
|
|
|
|
buf[i] = unset_char;
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
2018-02-05 15:15:30 +00:00
|
|
|
static uint8_t find_possible_pdchs(const struct gprs_rlcmac_trx *trx, uint8_t max_slots, uint8_t mask,
|
|
|
|
const char *mask_reason = NULL)
|
2013-09-29 06:18:17 +00:00
|
|
|
{
|
2015-06-19 14:35:38 +00:00
|
|
|
unsigned ts;
|
2018-02-05 15:15:30 +00:00
|
|
|
uint8_t valid_ts_set = 0;
|
2015-06-30 07:44:05 +00:00
|
|
|
int8_t last_tsc = -1; /* must be signed */
|
2015-06-19 14:35:38 +00:00
|
|
|
|
|
|
|
for (ts = 0; ts < ARRAY_SIZE(trx->pdch); ts++) {
|
2018-01-26 10:09:16 +00:00
|
|
|
const struct gprs_rlcmac_pdch *pdch;
|
2013-09-29 06:18:17 +00:00
|
|
|
|
|
|
|
pdch = &trx->pdch[ts];
|
2013-10-19 15:37:48 +00:00
|
|
|
if (!pdch->is_enabled()) {
|
2013-09-29 06:18:17 +00:00
|
|
|
LOGP(DRLCMAC, LOGL_DEBUG, "- Skipping TS %d, because "
|
|
|
|
"not enabled\n", ts);
|
|
|
|
continue;
|
|
|
|
}
|
2015-06-19 14:35:38 +00:00
|
|
|
|
|
|
|
if (((1 << ts) & mask) == 0) {
|
|
|
|
if (mask_reason)
|
|
|
|
LOGP(DRLCMAC, LOGL_DEBUG,
|
|
|
|
"- Skipping TS %d, because %s\n",
|
|
|
|
ts, mask_reason);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-06-30 07:44:05 +00:00
|
|
|
if (max_slots > 1) {
|
|
|
|
/* check if TSC changes, see TS 45.002, 6.4.2 */
|
|
|
|
if (last_tsc < 0)
|
|
|
|
last_tsc = pdch->tsc;
|
|
|
|
else if (last_tsc != pdch->tsc) {
|
|
|
|
LOGP(DRLCMAC, LOGL_ERROR,
|
|
|
|
"Skipping TS %d of TRX=%d, because it "
|
|
|
|
"has different TSC than lower TS of TRX. "
|
|
|
|
"In order to allow multislot, all "
|
|
|
|
"slots must be configured with the same "
|
|
|
|
"TSC!\n", ts, trx->trx_no);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-19 14:35:38 +00:00
|
|
|
valid_ts_set |= 1 << ts;
|
2013-09-29 06:18:17 +00:00
|
|
|
}
|
|
|
|
|
2015-06-19 14:35:38 +00:00
|
|
|
return valid_ts_set;
|
|
|
|
}
|
|
|
|
|
2018-01-26 10:09:16 +00:00
|
|
|
static int compute_usage_by_num_tbfs(const struct gprs_rlcmac_pdch *pdch, enum gprs_rlcmac_tbf_direction dir)
|
2015-06-19 14:35:38 +00:00
|
|
|
{
|
2015-07-09 11:44:18 +00:00
|
|
|
return pdch->num_tbfs(dir);
|
|
|
|
}
|
2015-06-19 14:35:38 +00:00
|
|
|
|
2018-01-26 10:09:16 +00:00
|
|
|
static int compute_usage_by_reservation(const struct gprs_rlcmac_pdch *pdch, enum gprs_rlcmac_tbf_direction)
|
2015-07-09 11:44:18 +00:00
|
|
|
{
|
|
|
|
return
|
|
|
|
pdch->num_reserved(GPRS_RLCMAC_DL_TBF) +
|
|
|
|
pdch->num_reserved(GPRS_RLCMAC_UL_TBF);
|
2013-09-29 06:18:17 +00:00
|
|
|
}
|
2013-09-29 05:37:40 +00:00
|
|
|
|
2018-01-26 10:09:16 +00:00
|
|
|
static int compute_usage_for_algo_a(const struct gprs_rlcmac_pdch *pdch, enum gprs_rlcmac_tbf_direction dir)
|
2015-07-16 13:04:07 +00:00
|
|
|
{
|
|
|
|
int usage =
|
|
|
|
pdch->num_tbfs(GPRS_RLCMAC_DL_TBF) +
|
|
|
|
pdch->num_tbfs(GPRS_RLCMAC_UL_TBF) +
|
|
|
|
compute_usage_by_reservation(pdch, dir);
|
|
|
|
|
2017-09-20 15:55:28 +00:00
|
|
|
if (pdch->assigned_tfi(reverse(dir)) == NO_FREE_TFI)
|
2015-07-16 13:04:07 +00:00
|
|
|
/* No TFI in the opposite direction, avoid it */
|
|
|
|
usage += 32;
|
|
|
|
|
|
|
|
return usage;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-01-26 10:09:16 +00:00
|
|
|
/*! Return the TS which corresponds to least busy PDCH
|
|
|
|
*
|
|
|
|
* \param[in] trx Pointer to TRX object
|
|
|
|
* \param[in] dir TBF direction
|
|
|
|
* \param[in] mask set of available timeslots
|
|
|
|
* \param[in] fn Function pointer to function which computes number of associated TBFs
|
|
|
|
* \param[out] free_tfi Free TFI
|
|
|
|
* \param[out] free_usf Free USF
|
|
|
|
* \returns TS number or -1 if unable to find
|
|
|
|
*/
|
|
|
|
static int find_least_busy_pdch(const struct gprs_rlcmac_trx *trx, enum gprs_rlcmac_tbf_direction dir, uint8_t mask,
|
|
|
|
int (*fn)(const struct gprs_rlcmac_pdch *, enum gprs_rlcmac_tbf_direction dir),
|
2021-02-19 15:49:16 +00:00
|
|
|
int *free_tfi = NULL, int *free_usf = NULL)
|
2015-07-02 13:48:25 +00:00
|
|
|
{
|
|
|
|
unsigned ts;
|
|
|
|
int min_used = INT_MAX;
|
|
|
|
int min_ts = -1;
|
2015-07-10 10:25:25 +00:00
|
|
|
int min_tfi = -1;
|
2015-07-02 13:48:25 +00:00
|
|
|
int min_usf = -1;
|
|
|
|
|
|
|
|
for (ts = 0; ts < ARRAY_SIZE(trx->pdch); ts++) {
|
2018-01-26 10:09:16 +00:00
|
|
|
const struct gprs_rlcmac_pdch *pdch = &trx->pdch[ts];
|
2015-07-02 13:48:25 +00:00
|
|
|
int num_tbfs;
|
|
|
|
int usf = -1; /* must be signed */
|
2015-07-10 10:25:25 +00:00
|
|
|
int tfi = -1;
|
2015-07-02 13:48:25 +00:00
|
|
|
|
|
|
|
if (((1 << ts) & mask) == 0)
|
|
|
|
continue;
|
|
|
|
|
2015-07-09 11:44:18 +00:00
|
|
|
num_tbfs = fn(pdch, dir);
|
2015-07-02 13:48:25 +00:00
|
|
|
|
|
|
|
if (num_tbfs < min_used) {
|
|
|
|
/* We have found a candidate */
|
2015-07-10 10:25:25 +00:00
|
|
|
/* Make sure that a TFI is available */
|
|
|
|
if (free_tfi) {
|
2018-02-05 15:11:36 +00:00
|
|
|
tfi = find_free_tfi(pdch->assigned_tfi(dir));
|
2015-07-10 10:25:25 +00:00
|
|
|
if (tfi < 0) {
|
|
|
|
LOGP(DRLCMAC, LOGL_DEBUG,
|
|
|
|
"- Skipping TS %d, because "
|
|
|
|
"no TFI available\n", ts);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2015-07-02 13:48:25 +00:00
|
|
|
/* Make sure that an USF is available */
|
|
|
|
if (dir == GPRS_RLCMAC_UL_TBF) {
|
2018-01-31 14:22:36 +00:00
|
|
|
usf = find_free_usf(pdch->assigned_usf());
|
2015-07-02 13:48:25 +00:00
|
|
|
if (usf < 0) {
|
|
|
|
LOGP(DRLCMAC, LOGL_DEBUG,
|
|
|
|
"- Skipping TS %d, because "
|
|
|
|
"no USF available\n", ts);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (min_ts >= 0)
|
|
|
|
LOGP(DRLCMAC, LOGL_DEBUG,
|
|
|
|
"- Skipping TS %d, because "
|
|
|
|
"num TBFs %d > %d\n",
|
|
|
|
min_ts, min_used, num_tbfs);
|
|
|
|
min_used = num_tbfs;
|
|
|
|
min_ts = ts;
|
2015-07-10 10:25:25 +00:00
|
|
|
min_tfi = tfi;
|
2015-07-02 13:48:25 +00:00
|
|
|
min_usf = usf;
|
|
|
|
} else {
|
|
|
|
LOGP(DRLCMAC, LOGL_DEBUG,
|
|
|
|
"- Skipping TS %d, because "
|
|
|
|
"num TBFs %d >= %d\n",
|
|
|
|
ts, num_tbfs, min_used);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (min_ts < 0)
|
|
|
|
return -1;
|
|
|
|
|
2015-07-10 10:25:25 +00:00
|
|
|
if (free_tfi)
|
|
|
|
*free_tfi = min_tfi;
|
2015-07-02 13:48:25 +00:00
|
|
|
if (free_usf)
|
|
|
|
*free_usf = min_usf;
|
|
|
|
|
|
|
|
return min_ts;
|
|
|
|
}
|
|
|
|
|
2015-06-29 11:45:05 +00:00
|
|
|
static void attach_tbf_to_pdch(struct gprs_rlcmac_pdch *pdch,
|
|
|
|
struct gprs_rlcmac_tbf *tbf)
|
|
|
|
{
|
|
|
|
if (tbf->pdch[pdch->ts_no])
|
|
|
|
tbf->pdch[pdch->ts_no]->detach_tbf(tbf);
|
|
|
|
|
|
|
|
tbf->pdch[pdch->ts_no] = pdch;
|
|
|
|
pdch->attach_tbf(tbf);
|
|
|
|
}
|
|
|
|
|
2018-01-26 10:09:16 +00:00
|
|
|
static void assign_uplink_tbf_usf(struct gprs_rlcmac_pdch *pdch, struct gprs_rlcmac_ul_tbf *tbf, uint8_t tfi, int8_t usf)
|
2013-09-29 05:37:40 +00:00
|
|
|
{
|
2015-07-10 10:25:25 +00:00
|
|
|
tbf->m_tfi = tfi;
|
2014-08-07 13:49:21 +00:00
|
|
|
tbf->m_usf[pdch->ts_no] = usf;
|
2015-06-29 11:45:05 +00:00
|
|
|
attach_tbf_to_pdch(pdch, tbf);
|
2013-09-29 05:37:40 +00:00
|
|
|
}
|
|
|
|
|
2018-01-26 10:09:16 +00:00
|
|
|
static void assign_dlink_tbf(struct gprs_rlcmac_pdch *pdch, struct gprs_rlcmac_dl_tbf *tbf, uint8_t tfi)
|
2013-09-29 06:08:28 +00:00
|
|
|
{
|
2015-07-10 10:25:25 +00:00
|
|
|
tbf->m_tfi = tfi;
|
2015-06-29 11:45:05 +00:00
|
|
|
attach_tbf_to_pdch(pdch, tbf);
|
2013-09-29 06:08:28 +00:00
|
|
|
}
|
|
|
|
|
2021-01-18 11:53:54 +00:00
|
|
|
static int find_trx(const struct gprs_rlcmac_bts *bts, const GprsMs *ms, int8_t use_trx)
|
2015-07-10 10:25:25 +00:00
|
|
|
{
|
|
|
|
unsigned trx_no;
|
|
|
|
unsigned ts;
|
|
|
|
|
|
|
|
/* We must use the TRX currently actively used by an MS */
|
Convert GprsMS and helpers classes to C
As we integrate osmo-pcu more and more with libosmocore features, it
becomes really hard to use them since libosmocore relies heavily on C
specific compilation features, which are not available in old C++
compilers (such as designated initializers for complex types in FSMs).
GprsMs is right now a quite simple object since initial design of
osmo-pcu made it optional and most of the logic was placed and stored
duplicated in TBF objects. However, that's changing as we introduce more
features, with the GprsMS class getting more weight. Hence, let's move
it now to be a C struct in order to be able to easily use libosmocore
features there, such as FSMs.
Some helper classes which GprsMs uses are also mostly move to C since
they are mostly structs with methods, so there's no point in having
duplicated APIs for C++ and C for such simple cases.
For some more complex classes, like (ul_,dl_)tbf, C API bindings are
added where needed so that GprsMs can use functionalitites from that
class. Most of those APIs can be kept afterwards and drop the C++ ones
since they provide no benefit in general.
Change-Id: I0b50e3367aaad9dcada76da97b438e452c8b230c
2020-12-16 14:59:45 +00:00
|
|
|
if (ms && ms_current_trx(ms))
|
|
|
|
return ms_current_trx(ms)->trx_no;
|
2015-07-10 10:25:25 +00:00
|
|
|
|
|
|
|
if (use_trx >= 0 && use_trx < 8)
|
|
|
|
return use_trx;
|
|
|
|
|
|
|
|
/* Find the first TRX that has a PDCH with a free UL and DL TFI */
|
2021-01-18 11:53:54 +00:00
|
|
|
for (trx_no = 0; trx_no < ARRAY_SIZE(bts->trx); trx_no += 1) {
|
|
|
|
const struct gprs_rlcmac_trx *trx = &bts->trx[trx_no];
|
2015-07-10 10:25:25 +00:00
|
|
|
for (ts = 0; ts < ARRAY_SIZE(trx->pdch); ts++) {
|
2018-01-26 10:09:16 +00:00
|
|
|
const struct gprs_rlcmac_pdch *pdch = &trx->pdch[ts];
|
2015-07-10 10:25:25 +00:00
|
|
|
if (!pdch->is_enabled())
|
|
|
|
continue;
|
|
|
|
|
2017-09-20 15:55:28 +00:00
|
|
|
if (pdch->assigned_tfi(GPRS_RLCMAC_UL_TBF) == NO_FREE_TFI)
|
2015-07-10 10:25:25 +00:00
|
|
|
continue;
|
|
|
|
|
2017-09-20 15:55:28 +00:00
|
|
|
if (pdch->assigned_tfi(GPRS_RLCMAC_DL_TBF) == NO_FREE_TFI)
|
2015-07-10 10:25:25 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
return trx_no;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
2021-01-18 11:53:54 +00:00
|
|
|
static bool idle_pdch_avail(const struct gprs_rlcmac_bts *bts)
|
2015-07-16 16:33:46 +00:00
|
|
|
{
|
|
|
|
unsigned trx_no;
|
|
|
|
unsigned ts;
|
|
|
|
|
|
|
|
/* Find the first PDCH with an unused DL TS */
|
2021-01-18 11:53:54 +00:00
|
|
|
for (trx_no = 0; trx_no < ARRAY_SIZE(bts->trx); trx_no += 1) {
|
|
|
|
const struct gprs_rlcmac_trx *trx = &bts->trx[trx_no];
|
2015-07-16 16:33:46 +00:00
|
|
|
for (ts = 0; ts < ARRAY_SIZE(trx->pdch); ts++) {
|
2018-01-26 10:09:16 +00:00
|
|
|
const struct gprs_rlcmac_pdch *pdch = &trx->pdch[ts];
|
2015-07-16 16:33:46 +00:00
|
|
|
if (!pdch->is_enabled())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (pdch->num_tbfs(GPRS_RLCMAC_DL_TBF) > PDCH_IDLE_TBF_THRESH)
|
|
|
|
continue;
|
|
|
|
|
2018-01-26 10:09:16 +00:00
|
|
|
return true;
|
2015-07-16 16:33:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-26 10:09:16 +00:00
|
|
|
return false;
|
2015-07-16 16:33:46 +00:00
|
|
|
}
|
|
|
|
|
2018-01-26 10:09:16 +00:00
|
|
|
/*! Return free TFI
|
|
|
|
*
|
|
|
|
* \param[in] bts Pointer to BTS struct
|
|
|
|
* \param[in] ms Pointer to MS object
|
|
|
|
* \param[in] dir DL or UL direction
|
|
|
|
* \param[in] use_trx which TRX to use or -1 if it should be selected based on what MS uses
|
|
|
|
* \param[out] trx_no_ TRX number on which TFI was found
|
|
|
|
* \returns negative error code or 0 on success
|
|
|
|
*/
|
2021-11-09 15:52:31 +00:00
|
|
|
static int tfi_find_free(const struct gprs_rlcmac_bts *bts, const GprsMs *ms,
|
2018-01-26 10:09:16 +00:00
|
|
|
enum gprs_rlcmac_tbf_direction dir, int8_t use_trx, uint8_t *trx_no_)
|
2015-07-10 08:41:36 +00:00
|
|
|
{
|
2021-11-09 15:52:31 +00:00
|
|
|
const struct gprs_rlcmac_trx *trx;
|
2015-07-10 08:41:36 +00:00
|
|
|
int tfi;
|
|
|
|
uint8_t trx_no;
|
|
|
|
|
2021-11-09 15:52:31 +00:00
|
|
|
/* If MS is already doing stuff on a TRX, set use_trx to it: */
|
|
|
|
if ((trx = ms_current_trx(ms))) {
|
2017-09-28 14:41:24 +00:00
|
|
|
if (use_trx >= 0 && use_trx != trx->trx_no) {
|
|
|
|
LOGP(DRLCMAC, LOGL_ERROR, "- Requested incompatible TRX %d (current is %d)\n",
|
|
|
|
use_trx, trx->trx_no);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
use_trx = trx->trx_no;
|
|
|
|
}
|
|
|
|
|
2021-01-14 15:48:38 +00:00
|
|
|
tfi = bts_tfi_find_free(bts, dir, &trx_no, use_trx);
|
2015-07-10 08:41:36 +00:00
|
|
|
if (tfi < 0)
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
if (trx_no_)
|
|
|
|
*trx_no_ = trx_no;
|
|
|
|
|
|
|
|
return tfi;
|
|
|
|
}
|
2013-09-29 05:37:40 +00:00
|
|
|
|
2017-09-28 13:56:05 +00:00
|
|
|
/*! Slot Allocation: Algorithm A
|
2013-09-29 05:37:40 +00:00
|
|
|
*
|
|
|
|
* Assign single slot for uplink and downlink
|
2017-09-28 13:56:05 +00:00
|
|
|
*
|
|
|
|
* \param[in,out] bts Pointer to BTS struct
|
2021-10-18 12:00:24 +00:00
|
|
|
* \param[in,out] tbf Pointer to TBF struct
|
2017-09-28 13:56:05 +00:00
|
|
|
* \param[in] single flag indicating if we should force single-slot allocation
|
|
|
|
* \param[in] use_trx which TRX to use or -1 if it should be selected during allocation
|
|
|
|
* \returns negative error code or 0 on success
|
2013-09-29 05:37:40 +00:00
|
|
|
*/
|
2021-10-18 12:00:24 +00:00
|
|
|
int alloc_algorithm_a(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf, bool single,
|
2017-09-28 13:56:05 +00:00
|
|
|
int8_t use_trx)
|
2013-09-29 05:37:40 +00:00
|
|
|
{
|
|
|
|
struct gprs_rlcmac_pdch *pdch;
|
2015-06-19 14:35:38 +00:00
|
|
|
int ts = -1;
|
2015-06-30 08:24:37 +00:00
|
|
|
uint8_t ul_slots, dl_slots;
|
2015-07-10 08:41:36 +00:00
|
|
|
int trx_no;
|
2015-07-10 10:25:25 +00:00
|
|
|
int tfi = -1;
|
2015-06-19 14:35:38 +00:00
|
|
|
int usf = -1;
|
2018-02-05 15:15:30 +00:00
|
|
|
uint8_t mask = 0xff;
|
2015-06-19 14:35:38 +00:00
|
|
|
const char *mask_reason = NULL;
|
2021-10-18 12:00:24 +00:00
|
|
|
struct GprsMs *ms = tbf->ms();
|
Convert GprsMS and helpers classes to C
As we integrate osmo-pcu more and more with libosmocore features, it
becomes really hard to use them since libosmocore relies heavily on C
specific compilation features, which are not available in old C++
compilers (such as designated initializers for complex types in FSMs).
GprsMs is right now a quite simple object since initial design of
osmo-pcu made it optional and most of the logic was placed and stored
duplicated in TBF objects. However, that's changing as we introduce more
features, with the GprsMS class getting more weight. Hence, let's move
it now to be a C struct in order to be able to easily use libosmocore
features there, such as FSMs.
Some helper classes which GprsMs uses are also mostly move to C since
they are mostly structs with methods, so there's no point in having
duplicated APIs for C++ and C for such simple cases.
For some more complex classes, like (ul_,dl_)tbf, C API bindings are
added where needed so that GprsMs can use functionalitites from that
class. Most of those APIs can be kept afterwards and drop the C++ ones
since they provide no benefit in general.
Change-Id: I0b50e3367aaad9dcada76da97b438e452c8b230c
2020-12-16 14:59:45 +00:00
|
|
|
gprs_rlcmac_trx *trx = ms_current_trx(ms);
|
2013-09-29 05:37:40 +00:00
|
|
|
|
2020-10-23 15:07:10 +00:00
|
|
|
LOGPAL(tbf, "A", single, use_trx, LOGL_DEBUG, "Alloc start\n");
|
2013-09-29 05:37:40 +00:00
|
|
|
|
2018-01-26 10:09:16 +00:00
|
|
|
trx_no = find_trx(bts, ms, use_trx);
|
2015-07-10 10:25:25 +00:00
|
|
|
if (trx_no < 0) {
|
2020-10-23 15:11:00 +00:00
|
|
|
LOGPAL(tbf, "A", single, use_trx, LOGL_NOTICE,
|
2018-02-19 17:43:01 +00:00
|
|
|
"failed to find a usable TRX (TFI exhausted)\n");
|
2015-07-10 10:25:25 +00:00
|
|
|
return trx_no;
|
2015-07-10 08:41:36 +00:00
|
|
|
}
|
2015-07-14 09:35:21 +00:00
|
|
|
if (!trx)
|
|
|
|
trx = &bts->trx[trx_no];
|
2015-07-10 08:41:36 +00:00
|
|
|
|
Convert GprsMS and helpers classes to C
As we integrate osmo-pcu more and more with libosmocore features, it
becomes really hard to use them since libosmocore relies heavily on C
specific compilation features, which are not available in old C++
compilers (such as designated initializers for complex types in FSMs).
GprsMs is right now a quite simple object since initial design of
osmo-pcu made it optional and most of the logic was placed and stored
duplicated in TBF objects. However, that's changing as we introduce more
features, with the GprsMS class getting more weight. Hence, let's move
it now to be a C struct in order to be able to easily use libosmocore
features there, such as FSMs.
Some helper classes which GprsMs uses are also mostly move to C since
they are mostly structs with methods, so there's no point in having
duplicated APIs for C++ and C for such simple cases.
For some more complex classes, like (ul_,dl_)tbf, C API bindings are
added where needed so that GprsMs can use functionalitites from that
class. Most of those APIs can be kept afterwards and drop the C++ ones
since they provide no benefit in general.
Change-Id: I0b50e3367aaad9dcada76da97b438e452c8b230c
2020-12-16 14:59:45 +00:00
|
|
|
dl_slots = ms_reserved_dl_slots(ms);
|
|
|
|
ul_slots = ms_reserved_ul_slots(ms);
|
2015-06-19 14:35:38 +00:00
|
|
|
|
Convert GprsMS and helpers classes to C
As we integrate osmo-pcu more and more with libosmocore features, it
becomes really hard to use them since libosmocore relies heavily on C
specific compilation features, which are not available in old C++
compilers (such as designated initializers for complex types in FSMs).
GprsMs is right now a quite simple object since initial design of
osmo-pcu made it optional and most of the logic was placed and stored
duplicated in TBF objects. However, that's changing as we introduce more
features, with the GprsMS class getting more weight. Hence, let's move
it now to be a C struct in order to be able to easily use libosmocore
features there, such as FSMs.
Some helper classes which GprsMs uses are also mostly move to C since
they are mostly structs with methods, so there's no point in having
duplicated APIs for C++ and C for such simple cases.
For some more complex classes, like (ul_,dl_)tbf, C API bindings are
added where needed so that GprsMs can use functionalitites from that
class. Most of those APIs can be kept afterwards and drop the C++ ones
since they provide no benefit in general.
Change-Id: I0b50e3367aaad9dcada76da97b438e452c8b230c
2020-12-16 14:59:45 +00:00
|
|
|
ts = ms_first_common_ts(ms);
|
2015-06-19 14:35:38 +00:00
|
|
|
|
2015-06-30 08:24:37 +00:00
|
|
|
if (ts >= 0) {
|
|
|
|
mask_reason = "need to reuse TS";
|
2015-06-19 14:35:38 +00:00
|
|
|
mask = 1 << ts;
|
2015-06-30 08:24:37 +00:00
|
|
|
} else if (dl_slots || ul_slots) {
|
|
|
|
mask_reason = "need to use a reserved common TS";
|
|
|
|
mask = dl_slots & ul_slots;
|
|
|
|
}
|
2015-06-19 14:35:38 +00:00
|
|
|
|
2015-07-14 09:35:21 +00:00
|
|
|
mask = find_possible_pdchs(trx, 1, mask, mask_reason);
|
2015-06-19 14:35:38 +00:00
|
|
|
if (!mask)
|
2013-09-29 05:37:40 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2015-07-14 09:35:21 +00:00
|
|
|
ts = find_least_busy_pdch(trx, tbf->direction, mask,
|
2015-07-16 13:04:07 +00:00
|
|
|
compute_usage_for_algo_a,
|
2015-07-10 10:25:25 +00:00
|
|
|
&tfi, &usf);
|
2015-06-19 14:35:38 +00:00
|
|
|
|
2015-07-14 09:35:21 +00:00
|
|
|
if (tbf->direction == GPRS_RLCMAC_UL_TBF && usf < 0) {
|
2020-10-23 15:11:00 +00:00
|
|
|
LOGPAL(tbf, "A", single, use_trx, LOGL_NOTICE,
|
2018-02-19 17:43:01 +00:00
|
|
|
"failed to allocate a TS, no USF available\n");
|
2015-06-19 14:35:38 +00:00
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
2015-07-14 09:35:21 +00:00
|
|
|
if (ts < 0) {
|
2020-10-23 15:11:00 +00:00
|
|
|
LOGPAL(tbf, "A", single, use_trx, LOGL_NOTICE,
|
2018-02-19 17:43:01 +00:00
|
|
|
"failed to allocate a TS, no TFI available\n");
|
2015-07-14 09:35:21 +00:00
|
|
|
return -EBUSY;
|
|
|
|
}
|
2015-06-19 14:35:38 +00:00
|
|
|
|
2015-07-14 09:35:21 +00:00
|
|
|
pdch = &trx->pdch[ts];
|
2015-06-19 14:35:38 +00:00
|
|
|
|
2021-10-18 12:00:24 +00:00
|
|
|
/* The allocation will be successful, so the system state and tbf/ms
|
2015-07-14 09:35:21 +00:00
|
|
|
* may be modified from now on. */
|
2021-08-23 14:58:04 +00:00
|
|
|
if (tbf->direction == GPRS_RLCMAC_UL_TBF) {
|
2021-10-18 12:00:24 +00:00
|
|
|
struct gprs_rlcmac_ul_tbf *ul_tbf = as_ul_tbf(tbf);
|
2018-02-19 17:43:01 +00:00
|
|
|
LOGPSL(tbf, LOGL_DEBUG, "Assign uplink TS=%d TFI=%d USF=%d\n", ts, tfi, usf);
|
2015-07-10 10:25:25 +00:00
|
|
|
assign_uplink_tbf_usf(pdch, ul_tbf, tfi, usf);
|
2021-08-23 14:58:04 +00:00
|
|
|
} else {
|
2021-10-18 12:00:24 +00:00
|
|
|
struct gprs_rlcmac_dl_tbf *dl_tbf = as_dl_tbf(tbf);
|
2018-02-19 17:43:01 +00:00
|
|
|
LOGPSL(tbf, LOGL_DEBUG, "Assign downlink TS=%d TFI=%d\n", ts, tfi);
|
2015-07-10 10:25:25 +00:00
|
|
|
assign_dlink_tbf(pdch, dl_tbf, tfi);
|
2013-09-29 05:37:40 +00:00
|
|
|
}
|
2015-07-14 09:35:21 +00:00
|
|
|
|
2021-10-18 12:00:24 +00:00
|
|
|
tbf->trx = trx;
|
2013-09-29 05:37:40 +00:00
|
|
|
/* the only one TS is the common TS */
|
2021-10-18 12:00:24 +00:00
|
|
|
tbf->first_ts = tbf->first_common_ts = ts;
|
2021-02-25 17:08:10 +00:00
|
|
|
ms_set_reserved_slots(ms, trx, 1 << ts, 1 << ts);
|
2013-09-29 05:37:40 +00:00
|
|
|
|
2021-10-18 12:00:24 +00:00
|
|
|
tbf->upgrade_to_multislot = false;
|
2021-01-14 15:48:38 +00:00
|
|
|
bts_do_rate_ctr_inc(bts, CTR_TBF_ALLOC_ALGO_A);
|
2013-09-29 05:37:40 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-01-31 14:22:36 +00:00
|
|
|
/*! Compute capacity of a given TRX
|
|
|
|
*
|
|
|
|
* \param[in] trx Pointer to TRX object
|
|
|
|
* \param[in] rx_window Receive window
|
|
|
|
* \param[in] tx_window Transmit window
|
|
|
|
* \returns non-negative capacity
|
|
|
|
*/
|
|
|
|
static inline unsigned compute_capacity(const struct gprs_rlcmac_trx *trx, int rx_window, int tx_window)
|
|
|
|
{
|
|
|
|
const struct gprs_rlcmac_pdch *pdch;
|
|
|
|
unsigned ts, capacity = 0;
|
|
|
|
|
|
|
|
for (ts = 0; ts < ARRAY_SIZE(trx->pdch); ts++) {
|
|
|
|
pdch = &trx->pdch[ts];
|
|
|
|
if (rx_window & (1 << ts))
|
|
|
|
capacity += OSMO_MAX(32 - pdch->num_reserved(GPRS_RLCMAC_DL_TBF), 1);
|
|
|
|
|
|
|
|
/* Only consider common slots for UL */
|
|
|
|
if (tx_window & rx_window & (1 << ts)) {
|
|
|
|
if (find_free_usf(pdch->assigned_usf()) >= 0)
|
|
|
|
capacity += OSMO_MAX(32 - pdch->num_reserved(GPRS_RLCMAC_UL_TBF), 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return capacity;
|
|
|
|
}
|
|
|
|
|
2018-02-05 15:15:30 +00:00
|
|
|
/*! Decide if a given slot should be skipped by multislot allocator
|
|
|
|
*
|
|
|
|
* \param[in] ms_class Pointer to MS Class object
|
|
|
|
* \param[in] check_tr Flag indicating whether we should check for Tra or Tta parameters for a given MS class
|
|
|
|
* \param[in] rx_window Receive window
|
|
|
|
* \param[in] tx_window Transmit window
|
|
|
|
* \param[in,out] checked_rx array with already checked RX timeslots
|
|
|
|
* \returns true if the slot should be skipped, false otherwise
|
|
|
|
*/
|
|
|
|
static bool skip_slot(uint8_t mslot_class, bool check_tr,
|
|
|
|
int16_t rx_window, int16_t tx_window,
|
|
|
|
uint32_t *checked_rx)
|
|
|
|
{
|
|
|
|
uint8_t common_slot_count, req_common_slots,
|
|
|
|
rx_slot_count = pcu_bitcount(rx_window),
|
|
|
|
tx_slot_count = pcu_bitcount(tx_window);
|
|
|
|
|
|
|
|
/* Check compliance with TS 45.002, table 6.4.2.2.1 */
|
|
|
|
/* Whether to skip this round doesn not only depend on the bit
|
|
|
|
* sets but also on check_tr. Therefore this check must be done
|
|
|
|
* before doing the mslot_test_and_set_bit shortcut. */
|
|
|
|
if (mslot_class_get_type(mslot_class) == 1) {
|
|
|
|
uint16_t slot_sum = rx_slot_count + tx_slot_count;
|
|
|
|
/* Assume down + up / dynamic.
|
|
|
|
* TODO: For ext-dynamic, down only, up only add more cases.
|
|
|
|
*/
|
|
|
|
if (slot_sum <= 6 && tx_slot_count < 3) {
|
|
|
|
if (!check_tr)
|
|
|
|
return true; /* Skip Tta */
|
|
|
|
} else if (slot_sum > 6 && tx_slot_count < 3) {
|
|
|
|
if (check_tr)
|
|
|
|
return true; /* Skip Tra */
|
|
|
|
} else
|
|
|
|
return true; /* No supported row in TS 45.002, table 6.4.2.2.1. */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Avoid repeated RX combination check */
|
|
|
|
if (mslot_test_and_set_bit(checked_rx, rx_window))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
/* Check number of common slots according to TS 45.002, §6.4.2.2 */
|
|
|
|
common_slot_count = pcu_bitcount(tx_window & rx_window);
|
|
|
|
req_common_slots = OSMO_MIN(tx_slot_count, rx_slot_count);
|
|
|
|
if (mslot_class_get_type(mslot_class) == 1)
|
|
|
|
req_common_slots = OSMO_MIN(req_common_slots, 2);
|
|
|
|
|
|
|
|
if (req_common_slots != common_slot_count)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-01-26 10:09:16 +00:00
|
|
|
/*! Find set of slots available for allocation while taking MS class into account
|
|
|
|
*
|
|
|
|
* \param[in] trx Pointer to TRX object
|
|
|
|
* \param[in] mslot_class The multislot class
|
|
|
|
* \param[in,out] ul_slots set of UL timeslots
|
|
|
|
* \param[in,out] dl_slots set of DL timeslots
|
|
|
|
* \returns negative error code or 0 on success
|
|
|
|
*/
|
2017-11-01 18:22:25 +00:00
|
|
|
int find_multi_slots(struct gprs_rlcmac_trx *trx, uint8_t mslot_class, uint8_t *ul_slots, uint8_t *dl_slots)
|
2013-12-25 19:34:26 +00:00
|
|
|
{
|
2021-02-19 16:33:21 +00:00
|
|
|
const uint8_t Rx = mslot_class_get_rx(mslot_class), /* Max number of Rx slots */
|
|
|
|
Tx = mslot_class_get_tx(mslot_class), /* Max number of Tx slots */
|
2021-02-19 16:35:11 +00:00
|
|
|
Sum = mslot_class_get_sum(mslot_class), /* Max number of Tx + Rx slots */
|
|
|
|
Type = mslot_class_get_type(mslot_class);
|
2021-02-19 16:33:21 +00:00
|
|
|
uint8_t max_slots, num_rx, num_tx, mask_sel, pdch_slots, ul_ts, dl_ts;
|
2018-02-05 15:15:30 +00:00
|
|
|
int16_t rx_window, tx_window;
|
2015-06-22 14:14:23 +00:00
|
|
|
char slot_info[9] = {0};
|
2018-01-31 14:28:53 +00:00
|
|
|
int max_capacity = -1;
|
|
|
|
uint8_t max_ul_slots = 0, max_dl_slots = 0;
|
2013-12-25 19:34:26 +00:00
|
|
|
|
2017-11-01 17:11:24 +00:00
|
|
|
if (mslot_class)
|
|
|
|
LOGP(DRLCMAC, LOGL_DEBUG, "Slot Allocation (Algorithm B) for class %d\n",
|
|
|
|
mslot_class);
|
|
|
|
|
|
|
|
if (Tx == MS_NA) {
|
|
|
|
LOGP(DRLCMAC, LOGL_NOTICE, "Multislot class %d not applicable.\n",
|
|
|
|
mslot_class);
|
2015-06-22 14:14:23 +00:00
|
|
|
return -EINVAL;
|
2013-12-25 19:34:26 +00:00
|
|
|
}
|
|
|
|
|
2021-02-19 16:31:24 +00:00
|
|
|
max_slots = OSMO_MAX(Rx, Tx);
|
2015-06-18 15:16:26 +00:00
|
|
|
|
2015-06-22 14:14:23 +00:00
|
|
|
if (*dl_slots == 0)
|
|
|
|
*dl_slots = 0xff;
|
2013-12-25 19:53:53 +00:00
|
|
|
|
2015-06-22 14:14:23 +00:00
|
|
|
if (*ul_slots == 0)
|
|
|
|
*ul_slots = 0xff;
|
2013-12-25 19:53:53 +00:00
|
|
|
|
2015-06-22 14:14:23 +00:00
|
|
|
pdch_slots = find_possible_pdchs(trx, max_slots, 0xff);
|
2013-12-25 19:53:53 +00:00
|
|
|
|
2015-06-22 14:14:23 +00:00
|
|
|
*dl_slots &= pdch_slots;
|
|
|
|
*ul_slots &= pdch_slots;
|
2013-12-25 19:53:53 +00:00
|
|
|
|
2015-06-22 14:14:23 +00:00
|
|
|
LOGP(DRLCMAC, LOGL_DEBUG, "- Possible DL/UL slots: (TS=0)\"%s\"(TS=7)\n",
|
|
|
|
set_flag_chars(set_flag_chars(set_flag_chars(slot_info,
|
|
|
|
*dl_slots, 'D', '.'),
|
|
|
|
*ul_slots, 'U'),
|
|
|
|
*ul_slots & *dl_slots, 'C'));
|
2013-12-25 19:53:53 +00:00
|
|
|
|
2015-06-22 14:14:23 +00:00
|
|
|
/* Check for each UL (TX) slot */
|
2014-01-04 14:42:38 +00:00
|
|
|
|
2015-06-22 14:14:23 +00:00
|
|
|
/* Iterate through possible numbers of TX slots */
|
2021-02-19 15:56:36 +00:00
|
|
|
for (num_tx = 1; num_tx <= Tx; num_tx += 1) {
|
2015-06-22 14:14:23 +00:00
|
|
|
uint16_t tx_valid_win = (1 << num_tx) - 1;
|
2018-01-31 14:28:53 +00:00
|
|
|
uint8_t rx_mask[MASK_TR + 1];
|
2013-12-25 20:03:42 +00:00
|
|
|
|
2018-01-31 14:28:53 +00:00
|
|
|
mslot_fill_rx_mask(mslot_class, num_tx, rx_mask);
|
2013-12-25 20:03:42 +00:00
|
|
|
|
2020-09-22 16:03:56 +00:00
|
|
|
/* Rotate group of TX slots: UUU-----, -UUU----, ..., UU-----U */
|
|
|
|
for (ul_ts = 0; ul_ts < 8; ul_ts += 1, tx_valid_win <<= 1) {
|
|
|
|
uint16_t rx_valid_win;
|
|
|
|
uint32_t checked_rx[256/32] = {0};
|
2013-12-25 20:03:42 +00:00
|
|
|
|
2020-09-22 16:03:56 +00:00
|
|
|
/* Wrap valid window */
|
|
|
|
tx_valid_win = mslot_wrap_window(tx_valid_win);
|
2013-12-25 20:11:20 +00:00
|
|
|
|
2021-01-12 18:33:53 +00:00
|
|
|
/* for multislot type 1: don't split the window to wrap around.
|
|
|
|
* E.g. 'UU-----U' is invalid for a 4 TN window. Except 8 TN window.
|
|
|
|
* See 45.002 B.1 */
|
2021-02-19 16:35:11 +00:00
|
|
|
if (Type == 1 && num_tx < 8 &&
|
2021-01-12 18:33:53 +00:00
|
|
|
tx_valid_win & (1 << 0) && tx_valid_win & (1 << 7))
|
|
|
|
continue;
|
|
|
|
|
2020-09-22 16:03:56 +00:00
|
|
|
tx_window = tx_valid_win;
|
2013-12-25 19:53:53 +00:00
|
|
|
|
2020-09-22 16:03:56 +00:00
|
|
|
/* Filter out unavailable slots */
|
|
|
|
tx_window &= *ul_slots;
|
2013-12-26 08:49:05 +00:00
|
|
|
|
2020-09-22 16:03:56 +00:00
|
|
|
/* Skip if the the first TS (ul_ts) is not in the set */
|
|
|
|
if ((tx_window & (1 << ul_ts)) == 0)
|
|
|
|
continue;
|
2013-12-26 08:49:05 +00:00
|
|
|
|
2020-09-22 16:03:56 +00:00
|
|
|
/* Skip if the the last TS (ul_ts+num_tx-1) is not in the set */
|
|
|
|
if ((tx_window & (1 << ((ul_ts+num_tx-1) % 8))) == 0)
|
|
|
|
continue;
|
2015-06-22 14:14:23 +00:00
|
|
|
|
2021-02-19 16:31:24 +00:00
|
|
|
num_rx = OSMO_MIN(Rx, Sum - num_tx);
|
2021-01-12 18:33:53 +00:00
|
|
|
rx_valid_win = (1 << num_rx) - 1;
|
2015-06-22 14:14:23 +00:00
|
|
|
|
2020-09-22 16:03:56 +00:00
|
|
|
/* Rotate group of RX slots: DDD-----, -DDD----, ..., DD-----D */
|
|
|
|
for (dl_ts = 0; dl_ts < 8; dl_ts += 1, rx_valid_win <<= 1) {
|
|
|
|
/* Wrap valid window */
|
|
|
|
rx_valid_win = (rx_valid_win | rx_valid_win >> 8) & 0xff;
|
2015-06-22 14:14:23 +00:00
|
|
|
|
2021-01-12 18:33:53 +00:00
|
|
|
/* for multislot type 1: don't split the window to wrap around.
|
|
|
|
* E.g. 'DD-----D' is invalid for a 4 TN window. Except 8 TN window.
|
|
|
|
* See 45.002 B.1 */
|
2021-02-19 16:35:11 +00:00
|
|
|
if (Type == 1 && num_rx < 8 &&
|
2021-01-12 18:33:53 +00:00
|
|
|
(rx_valid_win & (1 << 0)) && (rx_valid_win & (1 << 7)))
|
|
|
|
continue;
|
|
|
|
|
2020-09-22 16:03:56 +00:00
|
|
|
/* Validate with both Tta/Ttb/Trb and Ttb/Tra/Trb */
|
|
|
|
for (mask_sel = MASK_TT; mask_sel <= MASK_TR; mask_sel += 1) {
|
|
|
|
int capacity;
|
2015-06-22 14:14:23 +00:00
|
|
|
|
2020-09-22 16:03:56 +00:00
|
|
|
rx_window = mslot_filter_bad(rx_mask[mask_sel], ul_ts, *dl_slots, rx_valid_win);
|
|
|
|
if (rx_window < 0)
|
|
|
|
continue;
|
2013-12-26 08:49:05 +00:00
|
|
|
|
2020-09-22 16:03:56 +00:00
|
|
|
if (skip_slot(mslot_class, mask_sel != MASK_TT, rx_window, tx_window, checked_rx))
|
|
|
|
continue;
|
2013-12-26 08:49:05 +00:00
|
|
|
|
2020-09-22 16:03:56 +00:00
|
|
|
/* Compute capacity */
|
|
|
|
capacity = compute_capacity(trx, rx_window, tx_window);
|
2013-12-26 08:49:05 +00:00
|
|
|
|
2015-06-30 12:48:13 +00:00
|
|
|
#ifdef ENABLE_TS_ALLOC_DEBUG
|
2020-09-22 16:03:56 +00:00
|
|
|
LOGP(DRLCMAC, LOGL_DEBUG,
|
|
|
|
"- Considering DL/UL slots: (TS=0)\"%s\"(TS=7), "
|
|
|
|
"capacity = %d\n",
|
|
|
|
set_flag_chars(set_flag_chars(set_flag_chars(set_flag_chars(
|
|
|
|
slot_info,
|
|
|
|
rx_bad, 'x', '.'),
|
|
|
|
rx_window, 'D'),
|
|
|
|
tx_window, 'U'),
|
|
|
|
rx_window & tx_window, 'C'),
|
|
|
|
capacity);
|
2015-06-30 12:48:13 +00:00
|
|
|
#endif
|
2015-06-22 14:14:23 +00:00
|
|
|
|
2020-09-22 16:03:56 +00:00
|
|
|
if (capacity <= max_capacity)
|
|
|
|
continue;
|
2013-12-26 08:49:05 +00:00
|
|
|
|
2020-09-22 16:03:56 +00:00
|
|
|
max_capacity = capacity;
|
|
|
|
max_ul_slots = tx_window;
|
|
|
|
max_dl_slots = rx_window;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-01-31 14:22:36 +00:00
|
|
|
}
|
2013-12-26 08:49:05 +00:00
|
|
|
|
2015-06-22 14:14:23 +00:00
|
|
|
if (!max_ul_slots || !max_dl_slots) {
|
|
|
|
LOGP(DRLCMAC, LOGL_NOTICE,
|
|
|
|
"No valid UL/DL slot combination found\n");
|
2021-02-25 17:30:33 +00:00
|
|
|
bts_do_rate_ctr_inc(trx->bts, CTR_TBF_ALLOC_FAIL_NO_SLOT_COMBI);
|
2015-06-22 14:14:23 +00:00
|
|
|
return -EINVAL;
|
2013-12-26 08:31:31 +00:00
|
|
|
}
|
|
|
|
|
2015-06-22 14:14:23 +00:00
|
|
|
*ul_slots = max_ul_slots;
|
|
|
|
*dl_slots = max_dl_slots;
|
|
|
|
|
|
|
|
return 0;
|
2013-12-26 08:31:31 +00:00
|
|
|
}
|
|
|
|
|
2018-01-31 16:00:06 +00:00
|
|
|
/*! Count used bits in slots and reserved_slots bitmasks
|
|
|
|
*
|
|
|
|
* \param[in] slots Timeslots in use
|
|
|
|
* \param[in] reserved_slots Reserved timeslots
|
|
|
|
* \param[out] slotcount Number of TS in use
|
|
|
|
* \param[out] avail_count Number of reserved TS
|
|
|
|
*/
|
2021-11-09 16:22:58 +00:00
|
|
|
static void count_slots(uint8_t slots, uint8_t reserved_slots, uint8_t *slotcount, uint8_t *avail_count)
|
2018-01-31 16:00:06 +00:00
|
|
|
{
|
|
|
|
(*slotcount) = pcu_bitcount(slots);
|
|
|
|
(*avail_count) = pcu_bitcount(reserved_slots);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! Return slot mask with single TS from a given UL/DL set according to TBF's direction, ts pointer is set to that TS
|
|
|
|
* number or to negative value on error
|
|
|
|
*
|
|
|
|
* \param[in] trx Pointer to TRX object
|
|
|
|
* \param[in] tbf Pointer to TBF object
|
|
|
|
* \param[in] dl_slots set of DL timeslots
|
|
|
|
* \param[in] ul_slots set of UL timeslots
|
|
|
|
* \param[in] ts corresponding TS or -1 for autoselection
|
|
|
|
* \returns slot mask with single UL or DL timeslot number if possible
|
|
|
|
*/
|
|
|
|
static uint8_t get_single_ts(const gprs_rlcmac_trx *trx, const gprs_rlcmac_tbf *tbf, uint8_t dl_slots, uint8_t ul_slots,
|
|
|
|
int ts)
|
|
|
|
{
|
|
|
|
uint8_t ret = dl_slots & ul_slots; /* Make sure to consider the first common slot only */
|
|
|
|
|
|
|
|
if (ts < 0)
|
|
|
|
ts = find_least_busy_pdch(trx, tbf->direction, ret, compute_usage_by_num_tbfs, NULL, NULL);
|
|
|
|
|
|
|
|
if (ts < 0)
|
|
|
|
return ffs(ret);
|
|
|
|
|
|
|
|
return ret & (1 << ts);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! Find set of timeslots available for allocation
|
|
|
|
*
|
|
|
|
* \param[in] trx Pointer to TRX object
|
|
|
|
* \param[in] tbf Pointer to TBF object
|
|
|
|
* \param[in] single Flag to force the single TS allocation
|
|
|
|
* \param[in] ul_slots set of UL timeslots
|
|
|
|
* \param[in] dl_slots set of DL timeslots
|
|
|
|
* \param[in] reserved_ul_slots set of reserved UL timeslots
|
|
|
|
* \param[in] reserved_dl_slots set of reserved DL timeslots
|
|
|
|
* \param[in] first_common_ts First TS common for both UL and DL or -1 if unknown
|
|
|
|
* \returns negative error code or selected TS on success
|
|
|
|
*/
|
|
|
|
static int tbf_select_slot_set(const gprs_rlcmac_tbf *tbf, const gprs_rlcmac_trx *trx, bool single,
|
|
|
|
uint8_t ul_slots, uint8_t dl_slots,
|
|
|
|
uint8_t reserved_ul_slots, uint8_t reserved_dl_slots,
|
|
|
|
int8_t first_common_ts)
|
|
|
|
{
|
2021-05-10 09:21:50 +00:00
|
|
|
bool is_ul = tbf->direction == GPRS_RLCMAC_UL_TBF;
|
|
|
|
uint8_t sl = is_ul ? ul_slots : dl_slots;
|
2018-01-31 16:00:06 +00:00
|
|
|
char slot_info[9] = { 0 };
|
|
|
|
|
|
|
|
if (single)
|
|
|
|
sl = get_single_ts(trx, tbf, dl_slots, ul_slots, first_common_ts);
|
|
|
|
|
|
|
|
if (!sl) {
|
|
|
|
LOGP(DRLCMAC, LOGL_NOTICE, "No %s slots available\n",
|
2021-05-10 09:21:50 +00:00
|
|
|
is_ul ? "uplink" : "downlink");
|
2021-02-25 17:30:33 +00:00
|
|
|
bts_do_rate_ctr_inc(trx->bts, CTR_TBF_ALLOC_FAIL_NO_SLOT_AVAIL);
|
2018-01-31 16:00:06 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2021-05-10 09:21:50 +00:00
|
|
|
if (is_ul) {
|
2018-01-31 16:00:06 +00:00
|
|
|
snprintf(slot_info, 9, OSMO_BIT_SPEC, OSMO_BIT_PRINT_EX(reserved_ul_slots, 'u'));
|
|
|
|
masked_override_with(slot_info, sl, 'U');
|
|
|
|
} else {
|
|
|
|
snprintf(slot_info, 9, OSMO_BIT_SPEC, OSMO_BIT_PRINT_EX(reserved_dl_slots, 'd'));
|
|
|
|
masked_override_with(slot_info, sl, 'D');
|
|
|
|
}
|
|
|
|
|
2021-05-10 09:21:50 +00:00
|
|
|
LOGPC(DRLCMAC, LOGL_DEBUG, "Selected %s slots: (TS=0)\"%s\"(TS=7), %s\n",
|
|
|
|
is_ul ? "UL" : "DL",
|
|
|
|
slot_info, single ? "single" : "multi");
|
2018-01-31 16:00:06 +00:00
|
|
|
|
|
|
|
return sl;
|
|
|
|
}
|
|
|
|
|
2018-01-31 16:21:21 +00:00
|
|
|
/*! Allocate USF according to a given UL TS mapping
|
|
|
|
*
|
|
|
|
* \param[in] trx Pointer to TRX object
|
|
|
|
* \param[in] selected_ul_slots set of UL timeslots selected for allocation
|
|
|
|
* \param[in] dl_slots set of DL timeslots
|
|
|
|
* \param[out] usf array for allocated USF
|
Support uplink multi-slot allocations
Before this patch, allocate_usf() was implemented to only allocate 1 USF
per TBF, regardless of the available ul_slot mask.
As a result, only 1 slot at max was allocated to any TBF. That's a pity
because usual multislot classes like 12 support up to 2 UL slots per TBF
(in common TS with DL).
This patch reworks allocate_usf() to allocate as many UL multislots as
possible (given mslot class, current USF availability, TFI availability,
related DL TBF slots for the same MS, etc.).
As a result, it can be seen that AllocTest results change substantially
and maximum concurrent TBF allocation drops under some conditions.
That happens due to more USFs being reserved (because each TBF has now
more UL slots reserved). Hence now USF exhaustion becomes the usual
limitation factor as per the number of concurrent TBFs than can be
handled per TRX (as opposed to TFIs previously).
Some of the biggest limitations in test appear though because really
high end multislot classes are used, which can consume high volumes of
UL slots (USFs), and which are probably not the most extended devices in
the field.
Moreover, in general the curren timeslot allocator for a given
multislot class will in general try to optimize the DL side gathering
most of the possible timeslots there. That means, for instance on ms
class 12 (4 Tx, 4Rx, 5 Sum), 4 DL slots and 1 UL slot will still be
selected. But in the case where only 3 PDCHs are available, then with
this new multi-slot UL support a TBF will reserve 3 DL slots and 2 UL
slots, while before this patch it would only taken 1 UL slot instead of
2.
This USF exhaustion situation can be improved in the future by
parametrizing (VTY command?) the maximum amount of UL slots that a TBF
can reserve, making for instance a default value of 2, meaning usual
classes can gather up 2 UL timelosts at a time while forbidding high-end
hungry classes to gather up to 8 UL timeslots.
Another approach would be to dynamically limit the amount of allowed
reservable UL timeslots based on current USF reservation load.
Related: OS#2282
Change-Id: Id97cc6e3b769511b591b1694549e0dac55227c43
2021-02-22 16:20:15 +00:00
|
|
|
* \returns updated UL TS mask or negative on error
|
2018-01-31 16:21:21 +00:00
|
|
|
*/
|
Support uplink multi-slot allocations
Before this patch, allocate_usf() was implemented to only allocate 1 USF
per TBF, regardless of the available ul_slot mask.
As a result, only 1 slot at max was allocated to any TBF. That's a pity
because usual multislot classes like 12 support up to 2 UL slots per TBF
(in common TS with DL).
This patch reworks allocate_usf() to allocate as many UL multislots as
possible (given mslot class, current USF availability, TFI availability,
related DL TBF slots for the same MS, etc.).
As a result, it can be seen that AllocTest results change substantially
and maximum concurrent TBF allocation drops under some conditions.
That happens due to more USFs being reserved (because each TBF has now
more UL slots reserved). Hence now USF exhaustion becomes the usual
limitation factor as per the number of concurrent TBFs than can be
handled per TRX (as opposed to TFIs previously).
Some of the biggest limitations in test appear though because really
high end multislot classes are used, which can consume high volumes of
UL slots (USFs), and which are probably not the most extended devices in
the field.
Moreover, in general the curren timeslot allocator for a given
multislot class will in general try to optimize the DL side gathering
most of the possible timeslots there. That means, for instance on ms
class 12 (4 Tx, 4Rx, 5 Sum), 4 DL slots and 1 UL slot will still be
selected. But in the case where only 3 PDCHs are available, then with
this new multi-slot UL support a TBF will reserve 3 DL slots and 2 UL
slots, while before this patch it would only taken 1 UL slot instead of
2.
This USF exhaustion situation can be improved in the future by
parametrizing (VTY command?) the maximum amount of UL slots that a TBF
can reserve, making for instance a default value of 2, meaning usual
classes can gather up 2 UL timelosts at a time while forbidding high-end
hungry classes to gather up to 8 UL timeslots.
Another approach would be to dynamically limit the amount of allowed
reservable UL timeslots based on current USF reservation load.
Related: OS#2282
Change-Id: Id97cc6e3b769511b591b1694549e0dac55227c43
2021-02-22 16:20:15 +00:00
|
|
|
static int allocate_usf(const gprs_rlcmac_trx *trx, uint8_t selected_ul_slots, uint8_t dl_slots,
|
|
|
|
int *usf_list)
|
2018-01-31 16:21:21 +00:00
|
|
|
{
|
Support uplink multi-slot allocations
Before this patch, allocate_usf() was implemented to only allocate 1 USF
per TBF, regardless of the available ul_slot mask.
As a result, only 1 slot at max was allocated to any TBF. That's a pity
because usual multislot classes like 12 support up to 2 UL slots per TBF
(in common TS with DL).
This patch reworks allocate_usf() to allocate as many UL multislots as
possible (given mslot class, current USF availability, TFI availability,
related DL TBF slots for the same MS, etc.).
As a result, it can be seen that AllocTest results change substantially
and maximum concurrent TBF allocation drops under some conditions.
That happens due to more USFs being reserved (because each TBF has now
more UL slots reserved). Hence now USF exhaustion becomes the usual
limitation factor as per the number of concurrent TBFs than can be
handled per TRX (as opposed to TFIs previously).
Some of the biggest limitations in test appear though because really
high end multislot classes are used, which can consume high volumes of
UL slots (USFs), and which are probably not the most extended devices in
the field.
Moreover, in general the curren timeslot allocator for a given
multislot class will in general try to optimize the DL side gathering
most of the possible timeslots there. That means, for instance on ms
class 12 (4 Tx, 4Rx, 5 Sum), 4 DL slots and 1 UL slot will still be
selected. But in the case where only 3 PDCHs are available, then with
this new multi-slot UL support a TBF will reserve 3 DL slots and 2 UL
slots, while before this patch it would only taken 1 UL slot instead of
2.
This USF exhaustion situation can be improved in the future by
parametrizing (VTY command?) the maximum amount of UL slots that a TBF
can reserve, making for instance a default value of 2, meaning usual
classes can gather up 2 UL timelosts at a time while forbidding high-end
hungry classes to gather up to 8 UL timeslots.
Another approach would be to dynamically limit the amount of allowed
reservable UL timeslots based on current USF reservation load.
Related: OS#2282
Change-Id: Id97cc6e3b769511b591b1694549e0dac55227c43
2021-02-22 16:20:15 +00:00
|
|
|
uint8_t ul_slots = selected_ul_slots & dl_slots;
|
|
|
|
unsigned int ts;
|
|
|
|
|
|
|
|
for (ts = 0; ts < ARRAY_SIZE(trx->pdch); ts++) {
|
|
|
|
const struct gprs_rlcmac_pdch *pdch = &trx->pdch[ts];
|
|
|
|
int8_t free_usf;
|
2018-01-31 16:21:21 +00:00
|
|
|
|
Support uplink multi-slot allocations
Before this patch, allocate_usf() was implemented to only allocate 1 USF
per TBF, regardless of the available ul_slot mask.
As a result, only 1 slot at max was allocated to any TBF. That's a pity
because usual multislot classes like 12 support up to 2 UL slots per TBF
(in common TS with DL).
This patch reworks allocate_usf() to allocate as many UL multislots as
possible (given mslot class, current USF availability, TFI availability,
related DL TBF slots for the same MS, etc.).
As a result, it can be seen that AllocTest results change substantially
and maximum concurrent TBF allocation drops under some conditions.
That happens due to more USFs being reserved (because each TBF has now
more UL slots reserved). Hence now USF exhaustion becomes the usual
limitation factor as per the number of concurrent TBFs than can be
handled per TRX (as opposed to TFIs previously).
Some of the biggest limitations in test appear though because really
high end multislot classes are used, which can consume high volumes of
UL slots (USFs), and which are probably not the most extended devices in
the field.
Moreover, in general the curren timeslot allocator for a given
multislot class will in general try to optimize the DL side gathering
most of the possible timeslots there. That means, for instance on ms
class 12 (4 Tx, 4Rx, 5 Sum), 4 DL slots and 1 UL slot will still be
selected. But in the case where only 3 PDCHs are available, then with
this new multi-slot UL support a TBF will reserve 3 DL slots and 2 UL
slots, while before this patch it would only taken 1 UL slot instead of
2.
This USF exhaustion situation can be improved in the future by
parametrizing (VTY command?) the maximum amount of UL slots that a TBF
can reserve, making for instance a default value of 2, meaning usual
classes can gather up 2 UL timelosts at a time while forbidding high-end
hungry classes to gather up to 8 UL timeslots.
Another approach would be to dynamically limit the amount of allowed
reservable UL timeslots based on current USF reservation load.
Related: OS#2282
Change-Id: Id97cc6e3b769511b591b1694549e0dac55227c43
2021-02-22 16:20:15 +00:00
|
|
|
if (((1 << ts) & ul_slots) == 0)
|
|
|
|
continue;
|
2018-01-31 16:21:21 +00:00
|
|
|
|
Support uplink multi-slot allocations
Before this patch, allocate_usf() was implemented to only allocate 1 USF
per TBF, regardless of the available ul_slot mask.
As a result, only 1 slot at max was allocated to any TBF. That's a pity
because usual multislot classes like 12 support up to 2 UL slots per TBF
(in common TS with DL).
This patch reworks allocate_usf() to allocate as many UL multislots as
possible (given mslot class, current USF availability, TFI availability,
related DL TBF slots for the same MS, etc.).
As a result, it can be seen that AllocTest results change substantially
and maximum concurrent TBF allocation drops under some conditions.
That happens due to more USFs being reserved (because each TBF has now
more UL slots reserved). Hence now USF exhaustion becomes the usual
limitation factor as per the number of concurrent TBFs than can be
handled per TRX (as opposed to TFIs previously).
Some of the biggest limitations in test appear though because really
high end multislot classes are used, which can consume high volumes of
UL slots (USFs), and which are probably not the most extended devices in
the field.
Moreover, in general the curren timeslot allocator for a given
multislot class will in general try to optimize the DL side gathering
most of the possible timeslots there. That means, for instance on ms
class 12 (4 Tx, 4Rx, 5 Sum), 4 DL slots and 1 UL slot will still be
selected. But in the case where only 3 PDCHs are available, then with
this new multi-slot UL support a TBF will reserve 3 DL slots and 2 UL
slots, while before this patch it would only taken 1 UL slot instead of
2.
This USF exhaustion situation can be improved in the future by
parametrizing (VTY command?) the maximum amount of UL slots that a TBF
can reserve, making for instance a default value of 2, meaning usual
classes can gather up 2 UL timelosts at a time while forbidding high-end
hungry classes to gather up to 8 UL timeslots.
Another approach would be to dynamically limit the amount of allowed
reservable UL timeslots based on current USF reservation load.
Related: OS#2282
Change-Id: Id97cc6e3b769511b591b1694549e0dac55227c43
2021-02-22 16:20:15 +00:00
|
|
|
free_usf = find_free_usf(pdch->assigned_usf());
|
|
|
|
if (free_usf < 0) {
|
|
|
|
LOGP(DRLCMAC, LOGL_DEBUG,
|
|
|
|
"- Skipping TS %d, because "
|
|
|
|
"no USF available\n", ts);
|
|
|
|
ul_slots &= (~(1 << ts)) & 0xff;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
usf_list[ts] = free_usf;
|
|
|
|
}
|
2018-01-31 16:21:21 +00:00
|
|
|
|
Support uplink multi-slot allocations
Before this patch, allocate_usf() was implemented to only allocate 1 USF
per TBF, regardless of the available ul_slot mask.
As a result, only 1 slot at max was allocated to any TBF. That's a pity
because usual multislot classes like 12 support up to 2 UL slots per TBF
(in common TS with DL).
This patch reworks allocate_usf() to allocate as many UL multislots as
possible (given mslot class, current USF availability, TFI availability,
related DL TBF slots for the same MS, etc.).
As a result, it can be seen that AllocTest results change substantially
and maximum concurrent TBF allocation drops under some conditions.
That happens due to more USFs being reserved (because each TBF has now
more UL slots reserved). Hence now USF exhaustion becomes the usual
limitation factor as per the number of concurrent TBFs than can be
handled per TRX (as opposed to TFIs previously).
Some of the biggest limitations in test appear though because really
high end multislot classes are used, which can consume high volumes of
UL slots (USFs), and which are probably not the most extended devices in
the field.
Moreover, in general the curren timeslot allocator for a given
multislot class will in general try to optimize the DL side gathering
most of the possible timeslots there. That means, for instance on ms
class 12 (4 Tx, 4Rx, 5 Sum), 4 DL slots and 1 UL slot will still be
selected. But in the case where only 3 PDCHs are available, then with
this new multi-slot UL support a TBF will reserve 3 DL slots and 2 UL
slots, while before this patch it would only taken 1 UL slot instead of
2.
This USF exhaustion situation can be improved in the future by
parametrizing (VTY command?) the maximum amount of UL slots that a TBF
can reserve, making for instance a default value of 2, meaning usual
classes can gather up 2 UL timelosts at a time while forbidding high-end
hungry classes to gather up to 8 UL timeslots.
Another approach would be to dynamically limit the amount of allowed
reservable UL timeslots based on current USF reservation load.
Related: OS#2282
Change-Id: Id97cc6e3b769511b591b1694549e0dac55227c43
2021-02-22 16:20:15 +00:00
|
|
|
if (!ul_slots) {
|
2018-01-31 16:21:21 +00:00
|
|
|
LOGP(DRLCMAC, LOGL_NOTICE, "No USF available\n");
|
2021-02-25 17:30:33 +00:00
|
|
|
bts_do_rate_ctr_inc(trx->bts, CTR_TBF_ALLOC_FAIL_NO_USF);
|
2018-01-31 16:21:21 +00:00
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ul_slots;
|
|
|
|
}
|
|
|
|
|
2018-02-19 17:00:38 +00:00
|
|
|
/*! Update MS' reserved timeslots
|
|
|
|
*
|
|
|
|
* \param[in,out] trx Pointer to TRX struct
|
|
|
|
* \param[in,out] ms_ Pointer to MS object
|
|
|
|
* \param[in] tbf_ Pointer to TBF struct
|
|
|
|
* \param[in] res_ul_slots Newly reserved UL slots
|
|
|
|
* \param[in] res_dl_slots Newly reserved DL slots
|
|
|
|
* \param[in] ul_slots available UL slots (for logging only)
|
|
|
|
* \param[in] dl_slots available DL slots (for logging only)
|
|
|
|
*/
|
|
|
|
static void update_ms_reserved_slots(gprs_rlcmac_trx *trx, GprsMs *ms, uint8_t res_ul_slots, uint8_t res_dl_slots,
|
|
|
|
uint8_t ul_slots, uint8_t dl_slots)
|
|
|
|
{
|
|
|
|
char slot_info[9] = { 0 };
|
|
|
|
|
Convert GprsMS and helpers classes to C
As we integrate osmo-pcu more and more with libosmocore features, it
becomes really hard to use them since libosmocore relies heavily on C
specific compilation features, which are not available in old C++
compilers (such as designated initializers for complex types in FSMs).
GprsMs is right now a quite simple object since initial design of
osmo-pcu made it optional and most of the logic was placed and stored
duplicated in TBF objects. However, that's changing as we introduce more
features, with the GprsMS class getting more weight. Hence, let's move
it now to be a C struct in order to be able to easily use libosmocore
features there, such as FSMs.
Some helper classes which GprsMs uses are also mostly move to C since
they are mostly structs with methods, so there's no point in having
duplicated APIs for C++ and C for such simple cases.
For some more complex classes, like (ul_,dl_)tbf, C API bindings are
added where needed so that GprsMs can use functionalitites from that
class. Most of those APIs can be kept afterwards and drop the C++ ones
since they provide no benefit in general.
Change-Id: I0b50e3367aaad9dcada76da97b438e452c8b230c
2020-12-16 14:59:45 +00:00
|
|
|
if (res_ul_slots == ms_reserved_ul_slots(ms) && res_dl_slots == ms_reserved_dl_slots(ms))
|
2018-02-19 17:00:38 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
/* The reserved slots have changed, update the MS */
|
Convert GprsMS and helpers classes to C
As we integrate osmo-pcu more and more with libosmocore features, it
becomes really hard to use them since libosmocore relies heavily on C
specific compilation features, which are not available in old C++
compilers (such as designated initializers for complex types in FSMs).
GprsMs is right now a quite simple object since initial design of
osmo-pcu made it optional and most of the logic was placed and stored
duplicated in TBF objects. However, that's changing as we introduce more
features, with the GprsMS class getting more weight. Hence, let's move
it now to be a C struct in order to be able to easily use libosmocore
features there, such as FSMs.
Some helper classes which GprsMs uses are also mostly move to C since
they are mostly structs with methods, so there's no point in having
duplicated APIs for C++ and C for such simple cases.
For some more complex classes, like (ul_,dl_)tbf, C API bindings are
added where needed so that GprsMs can use functionalitites from that
class. Most of those APIs can be kept afterwards and drop the C++ ones
since they provide no benefit in general.
Change-Id: I0b50e3367aaad9dcada76da97b438e452c8b230c
2020-12-16 14:59:45 +00:00
|
|
|
ms_set_reserved_slots(ms, trx, res_ul_slots, res_dl_slots);
|
2018-02-19 17:00:38 +00:00
|
|
|
|
|
|
|
ts_format(slot_info, dl_slots, ul_slots);
|
|
|
|
LOGP(DRLCMAC, LOGL_DEBUG, "- Reserved DL/UL slots: (TS=0)\"%s\"(TS=7)\n", slot_info);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! Assign given UL timeslots to UL TBF
|
|
|
|
*
|
|
|
|
* \param[in,out] ul_tbf Pointer to UL TBF struct
|
|
|
|
* \param[in,out] trx Pointer to TRX object
|
|
|
|
* \param[in] ul_slots Set of slots to be assigned
|
|
|
|
* \param[in] tfi selected TFI
|
|
|
|
* \param[in] usf selected USF
|
|
|
|
*/
|
|
|
|
static void assign_ul_tbf_slots(struct gprs_rlcmac_ul_tbf *ul_tbf, gprs_rlcmac_trx *trx, uint8_t ul_slots, int tfi,
|
|
|
|
int *usf)
|
|
|
|
{
|
|
|
|
uint8_t ts;
|
|
|
|
|
|
|
|
for (ts = 0; ts < 8; ts++) {
|
|
|
|
if (!(ul_slots & (1 << ts)))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
OSMO_ASSERT(usf[ts] >= 0);
|
|
|
|
|
|
|
|
LOGP(DRLCMAC, LOGL_DEBUG, "- Assigning UL TS %u\n", ts);
|
|
|
|
assign_uplink_tbf_usf(&trx->pdch[ts], ul_tbf, tfi, usf[ts]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! Assign given DL timeslots to DL TBF
|
|
|
|
*
|
|
|
|
* \param[in,out] dl_tbf Pointer to DL TBF struct
|
|
|
|
* \param[in,out] trx Pointer to TRX object
|
|
|
|
* \param[in] ul_slots Set of slots to be assigned
|
|
|
|
* \param[in] tfi selected TFI
|
|
|
|
*/
|
|
|
|
static void assign_dl_tbf_slots(struct gprs_rlcmac_dl_tbf *dl_tbf, gprs_rlcmac_trx *trx, uint8_t dl_slots, int tfi)
|
|
|
|
{
|
|
|
|
uint8_t ts;
|
|
|
|
|
|
|
|
for (ts = 0; ts < 8; ts++) {
|
|
|
|
if (!(dl_slots & (1 << ts)))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
LOGP(DRLCMAC, LOGL_DEBUG, "- Assigning DL TS %u\n", ts);
|
|
|
|
assign_dlink_tbf(&trx->pdch[ts], dl_tbf, tfi);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-28 13:56:05 +00:00
|
|
|
/*! Slot Allocation: Algorithm B
|
2013-09-29 05:37:40 +00:00
|
|
|
*
|
|
|
|
* Assign as many downlink slots as possible.
|
|
|
|
* Assign one uplink slot. (With free USF)
|
|
|
|
*
|
2017-09-28 13:56:05 +00:00
|
|
|
* \param[in,out] bts Pointer to BTS struct
|
2021-10-18 12:00:24 +00:00
|
|
|
* \param[in,out] tbf Pointer to TBF struct
|
2017-09-28 13:56:05 +00:00
|
|
|
* \param[in] single flag indicating if we should force single-slot allocation
|
|
|
|
* \param[in] use_trx which TRX to use or -1 if it should be selected during allocation
|
|
|
|
* \returns negative error code or 0 on success
|
2013-09-29 05:37:40 +00:00
|
|
|
*/
|
2021-10-18 12:00:24 +00:00
|
|
|
int alloc_algorithm_b(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf, bool single,
|
2017-09-28 13:56:05 +00:00
|
|
|
int8_t use_trx)
|
2013-09-29 05:37:40 +00:00
|
|
|
{
|
2015-07-14 09:35:21 +00:00
|
|
|
uint8_t dl_slots;
|
|
|
|
uint8_t ul_slots;
|
|
|
|
uint8_t reserved_dl_slots;
|
|
|
|
uint8_t reserved_ul_slots;
|
2015-06-22 14:14:23 +00:00
|
|
|
int8_t first_common_ts;
|
2013-09-29 05:37:40 +00:00
|
|
|
uint8_t slotcount = 0;
|
2018-01-26 10:09:16 +00:00
|
|
|
uint8_t avail_count = 0, trx_no;
|
2015-07-14 09:35:21 +00:00
|
|
|
int first_ts = -1;
|
|
|
|
int usf[8] = {-1, -1, -1, -1, -1, -1, -1, -1};
|
2015-06-22 14:14:23 +00:00
|
|
|
int rc;
|
2015-07-14 09:35:21 +00:00
|
|
|
int tfi;
|
2021-10-18 12:00:24 +00:00
|
|
|
struct GprsMs *ms = tbf->ms();
|
2015-07-14 09:35:21 +00:00
|
|
|
gprs_rlcmac_trx *trx;
|
2015-07-10 08:41:36 +00:00
|
|
|
|
2020-10-23 15:07:10 +00:00
|
|
|
LOGPAL(tbf, "B", single, use_trx, LOGL_DEBUG, "Alloc start\n");
|
|
|
|
|
2015-07-14 09:35:21 +00:00
|
|
|
/* Step 1: Get current state from the MS object */
|
2013-09-29 05:37:40 +00:00
|
|
|
|
2021-05-10 10:25:20 +00:00
|
|
|
reserved_dl_slots = ms_reserved_dl_slots(ms);
|
|
|
|
reserved_ul_slots = ms_reserved_ul_slots(ms);
|
Convert GprsMS and helpers classes to C
As we integrate osmo-pcu more and more with libosmocore features, it
becomes really hard to use them since libosmocore relies heavily on C
specific compilation features, which are not available in old C++
compilers (such as designated initializers for complex types in FSMs).
GprsMs is right now a quite simple object since initial design of
osmo-pcu made it optional and most of the logic was placed and stored
duplicated in TBF objects. However, that's changing as we introduce more
features, with the GprsMS class getting more weight. Hence, let's move
it now to be a C struct in order to be able to easily use libosmocore
features there, such as FSMs.
Some helper classes which GprsMs uses are also mostly move to C since
they are mostly structs with methods, so there's no point in having
duplicated APIs for C++ and C for such simple cases.
For some more complex classes, like (ul_,dl_)tbf, C API bindings are
added where needed so that GprsMs can use functionalitites from that
class. Most of those APIs can be kept afterwards and drop the C++ ones
since they provide no benefit in general.
Change-Id: I0b50e3367aaad9dcada76da97b438e452c8b230c
2020-12-16 14:59:45 +00:00
|
|
|
first_common_ts = ms_first_common_ts(ms);
|
2015-07-14 09:35:21 +00:00
|
|
|
|
|
|
|
/* Step 2a: Find usable TRX and TFI */
|
2021-11-09 15:52:31 +00:00
|
|
|
tfi = tfi_find_free(bts, ms, tbf->direction, use_trx, &trx_no);
|
2015-07-14 09:35:21 +00:00
|
|
|
if (tfi < 0) {
|
2018-02-19 17:43:01 +00:00
|
|
|
LOGPAL(tbf, "B", single, use_trx, LOGL_NOTICE, "failed to allocate a TFI\n");
|
2015-07-14 09:35:21 +00:00
|
|
|
return tfi;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Step 2b: Reserve slots on the TRX for the MS */
|
2021-11-09 15:52:31 +00:00
|
|
|
trx = &bts->trx[trx_no];
|
2013-09-29 05:37:40 +00:00
|
|
|
|
2021-05-10 10:25:20 +00:00
|
|
|
if (!reserved_dl_slots || !reserved_ul_slots) {
|
|
|
|
rc = find_multi_slots(trx, ms_ms_class(ms), &reserved_ul_slots, &reserved_dl_slots);
|
2015-06-22 14:14:23 +00:00
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
2013-09-29 05:37:40 +00:00
|
|
|
}
|
2021-05-10 10:25:20 +00:00
|
|
|
dl_slots = reserved_dl_slots;
|
|
|
|
ul_slots = reserved_ul_slots;
|
2017-09-28 14:25:25 +00:00
|
|
|
|
2018-01-31 16:00:06 +00:00
|
|
|
/* Step 3a: Derive the slot set for the current TBF */
|
|
|
|
rc = tbf_select_slot_set(tbf, trx, single, ul_slots, dl_slots, reserved_ul_slots, reserved_dl_slots,
|
|
|
|
first_common_ts);
|
|
|
|
if (rc < 0)
|
2015-06-22 14:14:23 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2018-01-31 16:00:06 +00:00
|
|
|
/* Step 3b: Derive the slot set for a given direction */
|
2013-09-29 05:37:40 +00:00
|
|
|
if (tbf->direction == GPRS_RLCMAC_DL_TBF) {
|
2018-01-31 16:00:06 +00:00
|
|
|
dl_slots = rc;
|
2021-11-09 16:22:58 +00:00
|
|
|
count_slots(dl_slots, reserved_dl_slots, &slotcount, &avail_count);
|
2013-09-29 05:37:40 +00:00
|
|
|
} else {
|
Support uplink multi-slot allocations
Before this patch, allocate_usf() was implemented to only allocate 1 USF
per TBF, regardless of the available ul_slot mask.
As a result, only 1 slot at max was allocated to any TBF. That's a pity
because usual multislot classes like 12 support up to 2 UL slots per TBF
(in common TS with DL).
This patch reworks allocate_usf() to allocate as many UL multislots as
possible (given mslot class, current USF availability, TFI availability,
related DL TBF slots for the same MS, etc.).
As a result, it can be seen that AllocTest results change substantially
and maximum concurrent TBF allocation drops under some conditions.
That happens due to more USFs being reserved (because each TBF has now
more UL slots reserved). Hence now USF exhaustion becomes the usual
limitation factor as per the number of concurrent TBFs than can be
handled per TRX (as opposed to TFIs previously).
Some of the biggest limitations in test appear though because really
high end multislot classes are used, which can consume high volumes of
UL slots (USFs), and which are probably not the most extended devices in
the field.
Moreover, in general the curren timeslot allocator for a given
multislot class will in general try to optimize the DL side gathering
most of the possible timeslots there. That means, for instance on ms
class 12 (4 Tx, 4Rx, 5 Sum), 4 DL slots and 1 UL slot will still be
selected. But in the case where only 3 PDCHs are available, then with
this new multi-slot UL support a TBF will reserve 3 DL slots and 2 UL
slots, while before this patch it would only taken 1 UL slot instead of
2.
This USF exhaustion situation can be improved in the future by
parametrizing (VTY command?) the maximum amount of UL slots that a TBF
can reserve, making for instance a default value of 2, meaning usual
classes can gather up 2 UL timelosts at a time while forbidding high-end
hungry classes to gather up to 8 UL timeslots.
Another approach would be to dynamically limit the amount of allowed
reservable UL timeslots based on current USF reservation load.
Related: OS#2282
Change-Id: Id97cc6e3b769511b591b1694549e0dac55227c43
2021-02-22 16:20:15 +00:00
|
|
|
rc = allocate_usf(trx, rc, dl_slots, usf);
|
2018-01-31 16:21:21 +00:00
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
2015-07-14 09:35:21 +00:00
|
|
|
|
2018-01-31 16:21:21 +00:00
|
|
|
ul_slots = rc;
|
2015-07-14 09:35:21 +00:00
|
|
|
reserved_ul_slots = ul_slots;
|
2015-07-01 11:10:41 +00:00
|
|
|
|
2021-11-09 16:22:58 +00:00
|
|
|
count_slots(ul_slots, reserved_ul_slots, &slotcount, &avail_count);
|
2013-09-29 05:37:40 +00:00
|
|
|
}
|
2014-05-30 15:58:01 +00:00
|
|
|
|
2020-09-22 18:08:18 +00:00
|
|
|
first_ts = ffs(rc) - 1;
|
2015-07-14 09:35:21 +00:00
|
|
|
first_common_ts = ffs(dl_slots & ul_slots) - 1;
|
|
|
|
|
|
|
|
if (first_common_ts < 0) {
|
2018-02-19 17:43:01 +00:00
|
|
|
LOGPAL(tbf, "B", single, use_trx, LOGL_NOTICE, "first common slot unavailable\n");
|
2015-07-14 09:35:21 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2018-02-19 17:43:01 +00:00
|
|
|
|
2015-07-14 09:35:21 +00:00
|
|
|
if (first_ts < 0) {
|
2018-02-19 17:43:01 +00:00
|
|
|
LOGPAL(tbf, "B", single, use_trx, LOGL_NOTICE, "first slot unavailable\n");
|
2015-07-14 09:35:21 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2015-06-22 14:14:23 +00:00
|
|
|
if (single && slotcount) {
|
2021-10-18 12:00:24 +00:00
|
|
|
tbf->upgrade_to_multislot = (avail_count > slotcount);
|
2018-02-19 17:43:01 +00:00
|
|
|
LOGPAL(tbf, "B", single, use_trx, LOGL_INFO, "using single slot at TS %d\n", first_ts);
|
2013-09-29 05:37:40 +00:00
|
|
|
} else {
|
2021-10-18 12:00:24 +00:00
|
|
|
tbf->upgrade_to_multislot = false;
|
2018-02-19 17:43:01 +00:00
|
|
|
LOGPAL(tbf, "B", single, use_trx, LOGL_INFO, "using %d slots\n", slotcount);
|
2013-09-29 05:37:40 +00:00
|
|
|
}
|
2015-06-22 14:14:23 +00:00
|
|
|
|
2021-10-18 12:00:24 +00:00
|
|
|
/* The allocation will be successful, so the system state and tbf/ms
|
2015-07-14 09:35:21 +00:00
|
|
|
* may be modified from now on. */
|
2015-06-22 14:14:23 +00:00
|
|
|
|
2015-07-14 09:35:21 +00:00
|
|
|
/* Step 4: Update MS and TBF and really allocate the resources */
|
|
|
|
|
2021-02-25 17:08:10 +00:00
|
|
|
update_ms_reserved_slots(trx, ms, reserved_ul_slots, reserved_dl_slots, ul_slots, dl_slots);
|
2013-09-29 05:37:40 +00:00
|
|
|
|
2021-10-18 12:00:24 +00:00
|
|
|
tbf->trx = trx;
|
|
|
|
tbf->first_common_ts = first_common_ts;
|
|
|
|
tbf->first_ts = first_ts;
|
2015-07-14 09:35:21 +00:00
|
|
|
|
2021-08-23 14:58:04 +00:00
|
|
|
if (tbf->direction == GPRS_RLCMAC_DL_TBF)
|
2021-10-18 12:00:24 +00:00
|
|
|
assign_dl_tbf_slots(as_dl_tbf(tbf), trx, dl_slots, tfi);
|
2021-08-23 14:58:04 +00:00
|
|
|
else
|
2021-10-18 12:00:24 +00:00
|
|
|
assign_ul_tbf_slots(as_ul_tbf(tbf), trx, ul_slots, tfi, usf);
|
2013-09-29 05:37:40 +00:00
|
|
|
|
2021-01-14 15:48:38 +00:00
|
|
|
bts_do_rate_ctr_inc(bts, CTR_TBF_ALLOC_ALGO_B);
|
2015-07-14 12:02:41 +00:00
|
|
|
|
2013-09-29 05:37:40 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2015-07-14 11:31:48 +00:00
|
|
|
|
2017-09-28 13:56:05 +00:00
|
|
|
/*! Slot Allocation: Algorithm dynamic
|
2015-07-14 11:31:48 +00:00
|
|
|
*
|
|
|
|
* This meta algorithm automatically selects on of the other algorithms based
|
|
|
|
* on the current system state.
|
|
|
|
*
|
|
|
|
* The goal is to support as many MS and TBF as possible. On low usage, the
|
|
|
|
* goal is to provide the highest possible bandwidth per MS.
|
|
|
|
*
|
2017-09-28 13:56:05 +00:00
|
|
|
* \param[in,out] bts Pointer to BTS struct
|
2021-10-18 12:00:24 +00:00
|
|
|
* \param[in,out] tbf Pointer to TBF struct
|
2017-09-28 13:56:05 +00:00
|
|
|
* \param[in] single flag indicating if we should force single-slot allocation
|
|
|
|
* \param[in] use_trx which TRX to use or -1 if it should be selected during allocation
|
|
|
|
* \returns negative error code or 0 on success
|
2015-07-14 11:31:48 +00:00
|
|
|
*/
|
2021-10-18 12:00:24 +00:00
|
|
|
int alloc_algorithm_dynamic(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf, bool single,
|
2017-09-28 13:56:05 +00:00
|
|
|
int8_t use_trx)
|
2015-07-14 11:31:48 +00:00
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
2015-07-16 16:33:46 +00:00
|
|
|
/* Reset load_is_high if there is at least one idle PDCH */
|
|
|
|
if (bts->multislot_disabled) {
|
2018-01-26 10:09:16 +00:00
|
|
|
bts->multislot_disabled = !idle_pdch_avail(bts);
|
2015-07-16 16:33:46 +00:00
|
|
|
if (!bts->multislot_disabled)
|
|
|
|
LOGP(DRLCMAC, LOGL_DEBUG, "Enabling algorithm B\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!bts->multislot_disabled) {
|
2021-10-18 12:00:24 +00:00
|
|
|
rc = alloc_algorithm_b(bts, tbf, single, use_trx);
|
2015-07-16 16:33:46 +00:00
|
|
|
if (rc >= 0)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
if (!bts->multislot_disabled)
|
|
|
|
LOGP(DRLCMAC, LOGL_DEBUG, "Disabling algorithm B\n");
|
|
|
|
bts->multislot_disabled = 1;
|
|
|
|
}
|
2015-07-14 11:31:48 +00:00
|
|
|
|
2021-10-18 12:00:24 +00:00
|
|
|
return alloc_algorithm_a(bts, tbf, single, use_trx);
|
2015-07-14 11:31:48 +00:00
|
|
|
}
|
2015-07-17 09:38:49 +00:00
|
|
|
|
2018-01-31 17:03:49 +00:00
|
|
|
int gprs_alloc_max_dl_slots_per_ms(const struct gprs_rlcmac_bts *bts, uint8_t ms_class)
|
2015-07-17 09:38:49 +00:00
|
|
|
{
|
2017-11-01 17:11:24 +00:00
|
|
|
int rx = mslot_class_get_rx(ms_class);
|
2015-07-17 09:38:49 +00:00
|
|
|
|
|
|
|
if (rx == MS_NA)
|
|
|
|
rx = 4;
|
|
|
|
|
Split PCU global PCU object from BTS object
Currently the BTS object (and gprs_rlcmac_bts struct) are used to hold
both PCU global fields and BTS specific fields, all mangled together.
The BTS is even accessed in lots of places by means of a singleton.
This patch introduces a new struct gprs_pcu object aimed at holding all
global state, and several fields are already moved from BTS to it. The
new object can be accessed as global variable "the_pcu", reusing and
including an already exisitng "the_pcu" global variable only used for
bssgp related purposes so far.
This is only a first step towards having a complete split global pcu and
BTS, some fields are still kept in BTS and will be moved over follow-up
smaller patches in the future (since this patch is already quite big).
So far, the code still only supports one BTS, which can be accessed
using the_pcu->bts. In the future that field will be replaced with a
list, and the BTS singletons will be removed.
The cur_fn output changes in TbfTest are actually a side effect fix,
since the singleton main_bts() now points internally to the_pcu->bts,
hence the same we allocate and assign in the test. Beforehand, "the_bts"
was allocated in the stack while main_bts() still returned an unrelated
singleton BTS object instance.
Related: OS#4935
Change-Id: I88e3c6471b80245ce3798223f1a61190f14aa840
2021-01-13 17:54:38 +00:00
|
|
|
if (the_pcu->alloc_algorithm == alloc_algorithm_a)
|
2015-07-17 09:38:49 +00:00
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (bts->multislot_disabled)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return rx;
|
|
|
|
}
|