2014-08-08 10:14:12 +00:00
|
|
|
/* Copied from tbf.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 <bts.h>
|
|
|
|
#include <tbf.h>
|
2019-09-25 15:47:02 +00:00
|
|
|
#include <tbf_dl.h>
|
|
|
|
#include <tbf_ul.h>
|
2014-08-08 10:14:12 +00:00
|
|
|
#include <rlc.h>
|
|
|
|
#include <gprs_rlcmac.h>
|
|
|
|
#include <gprs_debug.h>
|
|
|
|
#include <gprs_bssgp_pcu.h>
|
llc: Use CoDel to drop packages from the LLC queue
Currently packets are only dropped if they have reached their maximum
life time. This leads to LLC queues being constantly filled under
load, increasing the latency up to the maximum life time. This kind
of bufferbloat hinders TCP's congestion avoidance algorithms. To keep
the queues short, the CoDel active queue management algorithm can be
used.
This commit changes to llc_dequeue method to apply the CoDel
algorithm to selectively drop LLC frames before they passed to the
TBF layer to be encoded in BSNs. This feature is currently disabled
by default.
The CoDel state is managed per MS since the LLC queues are also kept
in the MS objects.
Note that there is still some buffering in the TBF objects, in the
worst case (CS4) 3.5kByte + LLC-MTU octets are stored there. The
resulting additional packet delay is not (yet) taken into account for
CoDel.
Also note that configuration changes are applied to new MS objects
only.
The following VTY commands are added to the 'pcu' node:
- queue codel activates CoDel, the interval is selected by
the implementation
- queue codel interval <1-1000>
activates CoDel with a fixed interval given
in centiseconds (10ms-10s)
- no queue codel deactivates CoDel
Which interval value to use is still an open issue. For high speed
links (e.g. Ethernet), CoDel suggests 100ms. For slower links, the
expected RTT is recommended. The current implementation uses a
default value of 2000ms.
Measurements:
Note that the following measurements depend on several other factors,
most notably the interaction with the SGSN's flow control. They are
just examples to give an idea how CoDel might influence some
parameters.
The measurements have been done with a single E71, first with a
running ping only (Idle), then with an additional TCP download
of a 360k file (Busy). The CoDel interval was set to 1s.
- Idle :
ping ~400ms, avg queue delay 0ms, dropped 0
- Busy, No CoDel:
ping ~6s, avg queue delay 4-6s,
dropped 0, scheduled 948, duration 54s
- Busy, CoDel:
ping 500-1500ms, avg queue delay ~600ms,
dropped 77, scheduled 1040, duration 60s
More measurements with two MS downloading in parallel (two
independant measurements per case).
- Busy, No CoDel:
dropped 0, scheduled 1883, duration 121s
dropped 19, scheduled 2003, duration 133s
- Busy, CoDel:
dropped 22, scheduled 1926, duration 116s
dropped 22, scheduled 1955, duration 108s
Sponsored-by: On-Waves ehf
2015-07-17 14:39:09 +00:00
|
|
|
#include <gprs_codel.h>
|
2014-08-08 10:14:12 +00:00
|
|
|
#include <decoding.h>
|
2016-01-11 15:15:45 +00:00
|
|
|
#include <encoding.h>
|
2018-01-26 12:31:42 +00:00
|
|
|
#include <gprs_ms.h>
|
|
|
|
#include <gprs_ms_storage.h>
|
|
|
|
#include <llc.h>
|
2015-03-19 10:22:38 +00:00
|
|
|
#include "pcu_utils.h"
|
|
|
|
|
2014-08-08 10:14:12 +00:00
|
|
|
extern "C" {
|
|
|
|
#include <osmocom/core/msgb.h>
|
|
|
|
#include <osmocom/core/talloc.h>
|
2016-05-21 20:33:27 +00:00
|
|
|
#include <osmocom/gprs/gprs_bssgp_bss.h>
|
2018-01-26 12:31:42 +00:00
|
|
|
#include <osmocom/core/bitvec.h>
|
|
|
|
#include <osmocom/core/linuxlist.h>
|
|
|
|
#include <osmocom/core/logging.h>
|
|
|
|
#include <osmocom/core/rate_ctr.h>
|
2020-10-23 20:37:52 +00:00
|
|
|
#include <osmocom/core/stats.h>
|
2018-01-26 12:31:42 +00:00
|
|
|
#include <osmocom/core/timer.h>
|
|
|
|
#include <osmocom/core/utils.h>
|
|
|
|
#include <osmocom/gsm/gsm_utils.h>
|
|
|
|
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
2019-03-05 13:59:03 +00:00
|
|
|
#include "coding_scheme.h"
|
2014-08-08 10:14:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
/* After sending these frames, we poll for ack/nack. */
|
|
|
|
#define POLL_ACK_AFTER_FRAMES 20
|
|
|
|
|
2020-10-23 20:37:52 +00:00
|
|
|
extern void *tall_pcu_ctx;
|
|
|
|
|
|
|
|
static const struct rate_ctr_desc tbf_dl_gprs_ctr_description[] = {
|
|
|
|
{ "gprs:downlink:cs1", "CS1 " },
|
|
|
|
{ "gprs:downlink:cs2", "CS2 " },
|
|
|
|
{ "gprs:downlink:cs3", "CS3 " },
|
|
|
|
{ "gprs:downlink:cs4", "CS4 " },
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct rate_ctr_desc tbf_dl_egprs_ctr_description[] = {
|
|
|
|
{ "egprs:downlink:mcs1", "MCS1 " },
|
|
|
|
{ "egprs:downlink:mcs2", "MCS2 " },
|
|
|
|
{ "egprs:downlink:mcs3", "MCS3 " },
|
|
|
|
{ "egprs:downlink:mcs4", "MCS4 " },
|
|
|
|
{ "egprs:downlink:mcs5", "MCS5 " },
|
|
|
|
{ "egprs:downlink:mcs6", "MCS6 " },
|
|
|
|
{ "egprs:downlink:mcs7", "MCS7 " },
|
|
|
|
{ "egprs:downlink:mcs8", "MCS8 " },
|
|
|
|
{ "egprs:downlink:mcs9", "MCS9 " },
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct rate_ctr_group_desc tbf_dl_gprs_ctrg_desc = {
|
|
|
|
"tbf:gprs",
|
|
|
|
"Data Blocks",
|
|
|
|
OSMO_STATS_CLASS_SUBSCRIBER,
|
|
|
|
ARRAY_SIZE(tbf_dl_gprs_ctr_description),
|
|
|
|
tbf_dl_gprs_ctr_description,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct rate_ctr_group_desc tbf_dl_egprs_ctrg_desc = {
|
|
|
|
"tbf:egprs",
|
|
|
|
"Data Blocks",
|
|
|
|
OSMO_STATS_CLASS_SUBSCRIBER,
|
|
|
|
ARRAY_SIZE(tbf_dl_egprs_ctr_description),
|
|
|
|
tbf_dl_egprs_ctr_description,
|
|
|
|
};
|
|
|
|
|
2015-04-02 11:58:09 +00:00
|
|
|
static void llc_timer_cb(void *_tbf)
|
|
|
|
{
|
|
|
|
struct gprs_rlcmac_dl_tbf *tbf = (struct gprs_rlcmac_dl_tbf *)_tbf;
|
|
|
|
|
2021-05-14 10:50:46 +00:00
|
|
|
if (tbf->state_is_not(TBF_ST_FLOW))
|
2015-04-02 11:58:09 +00:00
|
|
|
return;
|
|
|
|
|
2017-12-15 16:36:45 +00:00
|
|
|
LOGPTBFDL(tbf, LOGL_DEBUG, "LLC receive timeout, requesting DL ACK\n");
|
2015-04-02 11:58:09 +00:00
|
|
|
|
|
|
|
tbf->request_dl_ack();
|
|
|
|
}
|
|
|
|
|
2020-10-23 20:17:01 +00:00
|
|
|
gprs_rlcmac_dl_tbf::BandWidth::BandWidth() :
|
|
|
|
dl_bw_octets(0),
|
|
|
|
dl_throughput(0),
|
|
|
|
dl_loss_lost(0),
|
|
|
|
dl_loss_received(0)
|
|
|
|
{
|
|
|
|
timespecclear(&dl_bw_tv);
|
|
|
|
timespecclear(&dl_loss_tv);
|
|
|
|
}
|
|
|
|
|
2020-10-23 20:37:52 +00:00
|
|
|
static int dl_tbf_dtor(struct gprs_rlcmac_dl_tbf *tbf)
|
|
|
|
{
|
|
|
|
tbf->~gprs_rlcmac_dl_tbf();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct gprs_rlcmac_dl_tbf *tbf_alloc_dl_tbf(struct gprs_rlcmac_bts *bts, GprsMs *ms, int8_t use_trx, bool single_slot)
|
|
|
|
{
|
|
|
|
struct gprs_rlcmac_dl_tbf *tbf;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
OSMO_ASSERT(ms != NULL);
|
|
|
|
|
2021-02-25 16:57:37 +00:00
|
|
|
LOGPMS(ms, DTBF, LOGL_DEBUG, "********** DL-TBF starts here **********\n");
|
|
|
|
LOGPMS(ms, DTBF, LOGL_INFO, "Allocating DL TBF\n");
|
2020-10-23 20:37:52 +00:00
|
|
|
|
|
|
|
tbf = talloc(tall_pcu_ctx, struct gprs_rlcmac_dl_tbf);
|
|
|
|
|
|
|
|
if (!tbf)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
talloc_set_destructor(tbf, dl_tbf_dtor);
|
2021-01-14 15:48:38 +00:00
|
|
|
new (tbf) gprs_rlcmac_dl_tbf(bts, ms);
|
2020-10-23 20:37:52 +00:00
|
|
|
|
|
|
|
rc = tbf->setup(use_trx, single_slot);
|
|
|
|
/* if no resource */
|
|
|
|
if (rc < 0) {
|
|
|
|
talloc_free(tbf);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tbf->is_egprs_enabled()) {
|
|
|
|
tbf->set_window_size();
|
|
|
|
tbf->m_dl_egprs_ctrs = rate_ctr_group_alloc(tbf,
|
|
|
|
&tbf_dl_egprs_ctrg_desc,
|
|
|
|
tbf->m_ctrs->idx);
|
|
|
|
if (!tbf->m_dl_egprs_ctrs) {
|
|
|
|
LOGPTBF(tbf, LOGL_ERROR, "Couldn't allocate EGPRS DL counters\n");
|
|
|
|
talloc_free(tbf);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
tbf->m_dl_gprs_ctrs = rate_ctr_group_alloc(tbf,
|
|
|
|
&tbf_dl_gprs_ctrg_desc,
|
|
|
|
tbf->m_ctrs->idx);
|
|
|
|
if (!tbf->m_dl_gprs_ctrs) {
|
|
|
|
LOGPTBF(tbf, LOGL_ERROR, "Couldn't allocate GPRS DL counters\n");
|
|
|
|
talloc_free(tbf);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-13 16:39:36 +00:00
|
|
|
llist_add(tbf_trx_list((struct gprs_rlcmac_tbf *)tbf), &tbf->trx->dl_tbfs);
|
2021-01-14 15:48:38 +00:00
|
|
|
bts_do_rate_ctr_inc(tbf->bts, CTR_TBF_DL_ALLOCATED);
|
2020-10-23 20:37:52 +00:00
|
|
|
|
|
|
|
osmo_clock_gettime(CLOCK_MONOTONIC, &tbf->m_bw.dl_bw_tv);
|
|
|
|
osmo_clock_gettime(CLOCK_MONOTONIC, &tbf->m_bw.dl_loss_tv);
|
|
|
|
|
|
|
|
return tbf;
|
|
|
|
}
|
|
|
|
|
2021-07-26 15:27:51 +00:00
|
|
|
gprs_rlcmac_dl_tbf::~gprs_rlcmac_dl_tbf()
|
|
|
|
{
|
|
|
|
osmo_timer_del(&m_llc_timer);
|
2021-07-26 16:24:14 +00:00
|
|
|
if (is_egprs_enabled()) {
|
|
|
|
rate_ctr_group_free(m_dl_egprs_ctrs);
|
|
|
|
} else {
|
|
|
|
rate_ctr_group_free(m_dl_gprs_ctrs);
|
|
|
|
}
|
|
|
|
/* ~gprs_rlcmac_tbf() is called automatically upon return */
|
2021-07-26 15:27:51 +00:00
|
|
|
}
|
|
|
|
|
2021-01-14 15:48:38 +00:00
|
|
|
gprs_rlcmac_dl_tbf::gprs_rlcmac_dl_tbf(struct gprs_rlcmac_bts *bts_, GprsMs *ms) :
|
2020-10-23 16:41:40 +00:00
|
|
|
gprs_rlcmac_tbf(bts_, ms, GPRS_RLCMAC_DL_TBF),
|
2019-09-26 12:41:18 +00:00
|
|
|
m_tx_counter(0),
|
|
|
|
m_wait_confirm(0),
|
|
|
|
m_dl_ack_requested(false),
|
2021-03-02 12:07:05 +00:00
|
|
|
m_last_dl_poll_fn(-1),
|
|
|
|
m_last_dl_drained_fn(-1),
|
2019-09-26 12:41:18 +00:00
|
|
|
m_dl_gprs_ctrs(NULL),
|
|
|
|
m_dl_egprs_ctrs(NULL)
|
|
|
|
{
|
|
|
|
memset(&m_llc_timer, 0, sizeof(m_llc_timer));
|
2019-09-26 12:42:41 +00:00
|
|
|
osmo_timer_setup(&m_llc_timer, llc_timer_cb, this);
|
2019-09-26 12:41:18 +00:00
|
|
|
}
|
|
|
|
|
2015-04-02 11:58:09 +00:00
|
|
|
void gprs_rlcmac_dl_tbf::start_llc_timer()
|
|
|
|
{
|
2021-01-14 13:45:14 +00:00
|
|
|
if (the_pcu->vty.llc_idle_ack_csec > 0) {
|
2020-03-11 13:04:52 +00:00
|
|
|
struct timespec tv;
|
2021-01-14 13:45:14 +00:00
|
|
|
csecs_to_timespec(the_pcu->vty.llc_idle_ack_csec, &tv);
|
2020-03-11 13:04:52 +00:00
|
|
|
osmo_timer_schedule(&m_llc_timer, tv.tv_sec, tv.tv_nsec / 1000);
|
2015-04-02 11:58:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-28 18:58:17 +00:00
|
|
|
int gprs_rlcmac_dl_tbf::append_data(uint16_t pdu_delay_csec,
|
|
|
|
const uint8_t *data, uint16_t len)
|
2014-08-08 10:14:12 +00:00
|
|
|
{
|
2020-03-11 13:04:52 +00:00
|
|
|
struct timespec expire_time;
|
2020-02-26 19:05:33 +00:00
|
|
|
|
2017-12-15 16:36:45 +00:00
|
|
|
LOGPTBFDL(this, LOGL_DEBUG, "appending %u bytes\n", len);
|
2020-02-26 19:05:33 +00:00
|
|
|
|
2015-06-15 12:32:33 +00:00
|
|
|
struct msgb *llc_msg = msgb_alloc(len, "llc_pdu_queue");
|
2015-06-12 12:06:09 +00:00
|
|
|
if (!llc_msg)
|
|
|
|
return -ENOMEM;
|
2015-06-15 12:32:33 +00:00
|
|
|
|
2020-02-26 19:05:33 +00:00
|
|
|
gprs_llc_queue::calc_pdu_lifetime(bts, pdu_delay_csec, &expire_time);
|
2015-06-12 12:06:09 +00:00
|
|
|
memcpy(msgb_put(llc_msg, len), data, len);
|
2020-02-26 19:05:33 +00:00
|
|
|
llc_queue()->enqueue(llc_msg, &expire_time);
|
2015-06-12 12:06:09 +00:00
|
|
|
start_llc_timer();
|
|
|
|
|
2021-05-14 10:50:46 +00:00
|
|
|
if (state_is(TBF_ST_WAIT_RELEASE)) {
|
2017-12-15 16:36:45 +00:00
|
|
|
LOGPTBFDL(this, LOGL_DEBUG, "in WAIT RELEASE state (T3193), so reuse TBF\n");
|
2015-09-01 09:20:29 +00:00
|
|
|
establish_dl_tbf_on_pacch();
|
2014-08-08 10:14:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-10-26 11:40:11 +00:00
|
|
|
static int tbf_new_dl_assignment(struct gprs_rlcmac_bts *bts, GprsMs *ms,
|
|
|
|
struct gprs_rlcmac_dl_tbf **tbf)
|
2014-08-08 10:14:12 +00:00
|
|
|
{
|
2017-09-28 13:56:05 +00:00
|
|
|
bool ss;
|
2014-08-08 10:14:12 +00:00
|
|
|
int8_t use_trx;
|
2015-05-13 11:33:12 +00:00
|
|
|
struct gprs_rlcmac_ul_tbf *ul_tbf = NULL, *old_ul_tbf;
|
2015-04-09 17:18:59 +00:00
|
|
|
struct gprs_rlcmac_dl_tbf *dl_tbf = NULL;
|
2020-05-08 16:15:59 +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
|
|
|
ul_tbf = ms_ul_tbf(ms);
|
2020-05-08 16:15:59 +00:00
|
|
|
|
2021-05-10 16:54:52 +00:00
|
|
|
/* 3GPP TS 44.060 sec 7.1.3.1 Initiation of the Packet resource request procedure:
|
|
|
|
* "Furthermore, the mobile station shall not respond to PACKET DOWNLINK ASSIGNMENT
|
|
|
|
* or MULTIPLE TBF DOWNLINK ASSIGNMENT messages before contention resolution is
|
|
|
|
* completed on the mobile station side." */
|
2014-08-08 10:14:12 +00:00
|
|
|
if (ul_tbf && ul_tbf->m_contention_resolution_done
|
|
|
|
&& !ul_tbf->m_final_ack_sent) {
|
|
|
|
use_trx = ul_tbf->trx->trx_no;
|
2017-09-28 13:56:05 +00:00
|
|
|
ss = false;
|
2014-08-08 10:14:12 +00:00
|
|
|
old_ul_tbf = ul_tbf;
|
|
|
|
} else {
|
|
|
|
use_trx = -1;
|
2017-09-28 13:56:05 +00:00
|
|
|
ss = true; /* PCH assignment only allows one timeslot */
|
2014-08-08 10:14:12 +00:00
|
|
|
old_ul_tbf = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create new TBF (any TRX)
|
2017-09-01 12:36:44 +00:00
|
|
|
/* FIXME: Copy and paste with alloc_ul_tbf */
|
2015-07-10 08:41:36 +00:00
|
|
|
/* set number of downlink slots according to multislot class */
|
2020-05-08 16:15:59 +00:00
|
|
|
dl_tbf = tbf_alloc_dl_tbf(bts, ms, use_trx, ss);
|
2015-04-09 17:18:59 +00:00
|
|
|
|
2014-08-08 10:14:12 +00:00
|
|
|
if (!dl_tbf) {
|
2021-02-25 16:57:37 +00:00
|
|
|
LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
|
2014-08-08 10:14:12 +00:00
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
2017-12-15 16:36:45 +00:00
|
|
|
LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START\n");
|
2014-08-08 10:14:12 +00:00
|
|
|
|
|
|
|
/* trigger downlink assignment and set state to ASSIGN.
|
|
|
|
* we don't use old_downlink, so the possible uplink is used
|
|
|
|
* to trigger downlink assignment. if there is no uplink,
|
|
|
|
* AGCH is used. */
|
2017-07-07 16:25:41 +00:00
|
|
|
dl_tbf->trigger_ass(old_ul_tbf);
|
2015-06-12 12:06:09 +00:00
|
|
|
*tbf = dl_tbf;
|
2014-08-08 10:14:12 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* TODO: split into unit test-able parts...
|
|
|
|
*/
|
2021-01-18 16:14:14 +00:00
|
|
|
int dl_tbf_handle(struct gprs_rlcmac_bts *bts,
|
|
|
|
const uint32_t tlli, const uint32_t tlli_old, const char *imsi,
|
|
|
|
uint8_t ms_class, uint8_t egprs_ms_class,
|
|
|
|
const uint16_t delay_csec,
|
|
|
|
const uint8_t *data, const uint16_t len)
|
2014-08-08 10:14:12 +00:00
|
|
|
{
|
2015-08-14 10:50:54 +00:00
|
|
|
struct gprs_rlcmac_dl_tbf *dl_tbf = NULL;
|
2015-06-12 12:06:09 +00:00
|
|
|
int rc;
|
2015-08-14 10:50:54 +00:00
|
|
|
GprsMs *ms, *ms_old;
|
2014-08-08 10:14:12 +00:00
|
|
|
|
|
|
|
/* check for existing TBF */
|
2021-01-14 15:48:38 +00:00
|
|
|
ms = bts_ms_store(bts)->get_ms(tlli, tlli_old, imsi);
|
2016-01-07 15:47:34 +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
|
|
|
if (ms && strlen(ms_imsi(ms)) == 0) {
|
2021-01-14 15:48:38 +00:00
|
|
|
ms_old = bts_ms_store(bts)->get_ms(0, 0, imsi);
|
2015-08-14 10:50:54 +00:00
|
|
|
if (ms_old && ms_old != ms) {
|
|
|
|
/* The TLLI has changed (RAU), so there are two MS
|
|
|
|
* objects for the same MS */
|
2018-01-19 17:22:25 +00:00
|
|
|
LOGP(DTBF, LOGL_NOTICE,
|
|
|
|
"There is a new MS object for the same MS: (0x%08x, '%s') -> (0x%08x, '%s')\n",
|
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_tlli(ms_old), ms_imsi(ms_old), ms_tlli(ms), ms_imsi(ms));
|
2015-08-14 10:50:54 +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
|
|
|
ms_ref(ms_old);
|
2015-08-14 10:50:54 +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
|
|
|
if (!ms_dl_tbf(ms) && ms_dl_tbf(ms_old)) {
|
2018-01-19 17:22:25 +00:00
|
|
|
LOGP(DTBF, LOGL_NOTICE,
|
|
|
|
"IMSI %s, old TBF %s: moving DL TBF to new MS object\n",
|
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
|
|
|
imsi, ms_dl_tbf(ms_old)->name());
|
|
|
|
dl_tbf = ms_dl_tbf(ms_old);
|
2015-08-14 10:50:54 +00:00
|
|
|
/* Move the DL TBF to the new MS */
|
|
|
|
dl_tbf->set_ms(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_merge_and_clear_ms(ms, ms_old);
|
|
|
|
|
|
|
|
ms_unref(ms_old);
|
2015-08-14 10:50:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-26 11:40:11 +00:00
|
|
|
if (!ms)
|
2021-01-14 15:48:38 +00:00
|
|
|
ms = bts_alloc_ms(bts, ms_class, egprs_ms_class);
|
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_imsi(ms, imsi);
|
|
|
|
ms_confirm_tlli(ms, tlli);
|
|
|
|
if (!ms_ms_class(ms) && ms_class) {
|
|
|
|
ms_set_ms_class(ms, ms_class);
|
2020-10-28 18:58:17 +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
|
|
|
if (!ms_egprs_ms_class(ms) && egprs_ms_class) {
|
|
|
|
ms_set_egprs_ms_class(ms, egprs_ms_class);
|
2020-10-28 18:58:17 +00:00
|
|
|
}
|
2020-10-26 11:40:11 +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_tbf = ms_dl_tbf(ms);
|
2015-06-12 12:06:09 +00:00
|
|
|
if (!dl_tbf) {
|
2020-10-26 11:40:11 +00:00
|
|
|
rc = tbf_new_dl_assignment(bts, ms, &dl_tbf);
|
2015-06-12 12:06:09 +00:00
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
}
|
2015-05-15 13:50:43 +00:00
|
|
|
|
2020-10-28 18:58:17 +00:00
|
|
|
rc = dl_tbf->append_data(delay_csec, data, len);
|
2015-06-12 12:06:09 +00:00
|
|
|
|
|
|
|
return rc;
|
2014-08-08 10:14:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct msgb *gprs_rlcmac_dl_tbf::llc_dequeue(bssgp_bvc_ctx *bctx)
|
|
|
|
{
|
|
|
|
struct msgb *msg;
|
2020-03-11 13:04:52 +00:00
|
|
|
struct timespec tv_now, tv_now2;
|
2014-08-08 10:14:12 +00:00
|
|
|
uint32_t octets = 0, frames = 0;
|
2020-03-11 13:04:52 +00:00
|
|
|
struct timespec hyst_delta = {0, 0};
|
2015-03-20 11:02:42 +00:00
|
|
|
const unsigned keep_small_thresh = 60;
|
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
|
|
|
const MetaInfo *info;
|
2015-03-20 11:02:42 +00:00
|
|
|
|
2021-01-14 13:45:14 +00:00
|
|
|
if (the_pcu->vty.llc_discard_csec)
|
|
|
|
csecs_to_timespec(the_pcu->vty.llc_discard_csec, &hyst_delta);
|
2014-08-08 10:14:12 +00:00
|
|
|
|
2020-03-11 13:04:52 +00:00
|
|
|
osmo_clock_gettime(CLOCK_MONOTONIC, &tv_now);
|
|
|
|
timespecadd(&tv_now, &hyst_delta, &tv_now2);
|
2014-08-08 10:14:12 +00:00
|
|
|
|
2015-06-15 12:32:33 +00:00
|
|
|
while ((msg = llc_queue()->dequeue(&info))) {
|
2020-03-11 13:04:52 +00:00
|
|
|
const struct timespec *tv_disc = &info->expire_time;
|
|
|
|
const struct timespec *tv_recv = &info->recv_time;
|
2015-05-06 12:24:36 +00:00
|
|
|
|
|
|
|
gprs_bssgp_update_queue_delay(tv_recv, &tv_now);
|
2014-08-08 10:14:12 +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
|
|
|
if (ms() && ms_codel_state(ms())) {
|
|
|
|
int bytes = llc_queue_octets(llc_queue());
|
|
|
|
if (gprs_codel_control(ms_codel_state(ms()),
|
llc: Use CoDel to drop packages from the LLC queue
Currently packets are only dropped if they have reached their maximum
life time. This leads to LLC queues being constantly filled under
load, increasing the latency up to the maximum life time. This kind
of bufferbloat hinders TCP's congestion avoidance algorithms. To keep
the queues short, the CoDel active queue management algorithm can be
used.
This commit changes to llc_dequeue method to apply the CoDel
algorithm to selectively drop LLC frames before they passed to the
TBF layer to be encoded in BSNs. This feature is currently disabled
by default.
The CoDel state is managed per MS since the LLC queues are also kept
in the MS objects.
Note that there is still some buffering in the TBF objects, in the
worst case (CS4) 3.5kByte + LLC-MTU octets are stored there. The
resulting additional packet delay is not (yet) taken into account for
CoDel.
Also note that configuration changes are applied to new MS objects
only.
The following VTY commands are added to the 'pcu' node:
- queue codel activates CoDel, the interval is selected by
the implementation
- queue codel interval <1-1000>
activates CoDel with a fixed interval given
in centiseconds (10ms-10s)
- no queue codel deactivates CoDel
Which interval value to use is still an open issue. For high speed
links (e.g. Ethernet), CoDel suggests 100ms. For slower links, the
expected RTT is recommended. The current implementation uses a
default value of 2000ms.
Measurements:
Note that the following measurements depend on several other factors,
most notably the interaction with the SGSN's flow control. They are
just examples to give an idea how CoDel might influence some
parameters.
The measurements have been done with a single E71, first with a
running ping only (Idle), then with an additional TCP download
of a 360k file (Busy). The CoDel interval was set to 1s.
- Idle :
ping ~400ms, avg queue delay 0ms, dropped 0
- Busy, No CoDel:
ping ~6s, avg queue delay 4-6s,
dropped 0, scheduled 948, duration 54s
- Busy, CoDel:
ping 500-1500ms, avg queue delay ~600ms,
dropped 77, scheduled 1040, duration 60s
More measurements with two MS downloading in parallel (two
independant measurements per case).
- Busy, No CoDel:
dropped 0, scheduled 1883, duration 121s
dropped 19, scheduled 2003, duration 133s
- Busy, CoDel:
dropped 22, scheduled 1926, duration 116s
dropped 22, scheduled 1955, duration 108s
Sponsored-by: On-Waves ehf
2015-07-17 14:39:09 +00:00
|
|
|
tv_recv, &tv_now, bytes))
|
|
|
|
goto drop_frame;
|
|
|
|
}
|
|
|
|
|
2015-03-20 11:02:42 +00:00
|
|
|
/* Is the age below the low water mark? */
|
2015-05-29 08:37:09 +00:00
|
|
|
if (!gprs_llc_queue::is_frame_expired(&tv_now2, tv_disc))
|
2015-03-20 11:02:42 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
/* Is the age below the high water mark */
|
2015-05-29 08:37:09 +00:00
|
|
|
if (!gprs_llc_queue::is_frame_expired(&tv_now, tv_disc)) {
|
2015-03-20 11:02:42 +00:00
|
|
|
/* Has the previous message not been dropped? */
|
|
|
|
if (frames == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Hysteresis mode, try to discard LLC messages until
|
|
|
|
* the low water mark has been reached */
|
|
|
|
|
|
|
|
/* Check whether to abort the hysteresis mode */
|
|
|
|
|
|
|
|
/* Is the frame small, perhaps only a TCP ACK? */
|
|
|
|
if (msg->len <= keep_small_thresh)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Is it a GMM message? */
|
|
|
|
if (!gprs_llc::is_user_data_frame(msg->data, msg->len))
|
|
|
|
break;
|
2014-08-08 10:14:12 +00:00
|
|
|
}
|
2015-03-20 11:02:42 +00:00
|
|
|
|
2021-01-14 15:48:38 +00:00
|
|
|
bts_do_rate_ctr_inc(bts, CTR_LLC_FRAME_TIMEDOUT);
|
llc: Use CoDel to drop packages from the LLC queue
Currently packets are only dropped if they have reached their maximum
life time. This leads to LLC queues being constantly filled under
load, increasing the latency up to the maximum life time. This kind
of bufferbloat hinders TCP's congestion avoidance algorithms. To keep
the queues short, the CoDel active queue management algorithm can be
used.
This commit changes to llc_dequeue method to apply the CoDel
algorithm to selectively drop LLC frames before they passed to the
TBF layer to be encoded in BSNs. This feature is currently disabled
by default.
The CoDel state is managed per MS since the LLC queues are also kept
in the MS objects.
Note that there is still some buffering in the TBF objects, in the
worst case (CS4) 3.5kByte + LLC-MTU octets are stored there. The
resulting additional packet delay is not (yet) taken into account for
CoDel.
Also note that configuration changes are applied to new MS objects
only.
The following VTY commands are added to the 'pcu' node:
- queue codel activates CoDel, the interval is selected by
the implementation
- queue codel interval <1-1000>
activates CoDel with a fixed interval given
in centiseconds (10ms-10s)
- no queue codel deactivates CoDel
Which interval value to use is still an open issue. For high speed
links (e.g. Ethernet), CoDel suggests 100ms. For slower links, the
expected RTT is recommended. The current implementation uses a
default value of 2000ms.
Measurements:
Note that the following measurements depend on several other factors,
most notably the interaction with the SGSN's flow control. They are
just examples to give an idea how CoDel might influence some
parameters.
The measurements have been done with a single E71, first with a
running ping only (Idle), then with an additional TCP download
of a 360k file (Busy). The CoDel interval was set to 1s.
- Idle :
ping ~400ms, avg queue delay 0ms, dropped 0
- Busy, No CoDel:
ping ~6s, avg queue delay 4-6s,
dropped 0, scheduled 948, duration 54s
- Busy, CoDel:
ping 500-1500ms, avg queue delay ~600ms,
dropped 77, scheduled 1040, duration 60s
More measurements with two MS downloading in parallel (two
independant measurements per case).
- Busy, No CoDel:
dropped 0, scheduled 1883, duration 121s
dropped 19, scheduled 2003, duration 133s
- Busy, CoDel:
dropped 22, scheduled 1926, duration 116s
dropped 22, scheduled 1955, duration 108s
Sponsored-by: On-Waves ehf
2015-07-17 14:39:09 +00:00
|
|
|
drop_frame:
|
2015-03-20 11:02:42 +00:00
|
|
|
frames++;
|
|
|
|
octets += msg->len;
|
|
|
|
msgb_free(msg);
|
2021-01-14 15:48:38 +00:00
|
|
|
bts_do_rate_ctr_inc(bts, CTR_LLC_FRAME_DROPPED);
|
2015-03-20 11:02:42 +00:00
|
|
|
continue;
|
2014-08-08 10:14:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (frames) {
|
2017-12-15 16:36:45 +00:00
|
|
|
LOGPTBFDL(this, LOGL_NOTICE, "Discarding LLC PDU "
|
2015-03-20 11:02:42 +00:00
|
|
|
"because lifetime limit reached, "
|
|
|
|
"count=%u new_queue_size=%zu\n",
|
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
|
|
|
frames, llc_queue_size(llc_queue()));
|
2014-08-08 10:14:12 +00:00
|
|
|
if (frames > 0xff)
|
|
|
|
frames = 0xff;
|
|
|
|
if (octets > 0xffffff)
|
|
|
|
octets = 0xffffff;
|
2015-08-21 13:10:23 +00:00
|
|
|
if (bctx)
|
|
|
|
bssgp_tx_llc_discarded(bctx, tlli(), frames, octets);
|
2014-08-08 10:14:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
edge: Send a second BSN block in an RLC message if possible
Currently only one BSN block is encoded in each RLC data message,
even if MSC7-9 are used, which transport two independant (except for a
max BSN delta of 512) BSN blocks. In that case, the same block is
just put twice into the same message.
The current create_dl_acked_block(fn, ts) method handles
block selection (resend, new BSN, dummy block, ...) and restart
handling in one method and is too complex to extend it accordingly.
Therefore this commit move the block selection/creation handling into
a new method (take_next_bsn) which delivers the next BSN along with a
hint, whether it may be combined with another block. In that case,
the function can be called a second time (this time with a valid
previous BSN, that's the one returned by the first call) to get the
second BSN. The real block generation method
create_dl_acked_block(fn, ts, index, index2) is then called with both
BSNs as indices (the second must be -1, if there is only one BSN).
Note that every BSN returned by take_next_bsn should be passed to
create_dl_acked_block to avoid state inconsistencies.
Sponsored-by: On-Waves ehf
2016-02-02 17:12:46 +00:00
|
|
|
bool gprs_rlcmac_dl_tbf::restart_bsn_cycle()
|
2014-08-08 10:14:12 +00:00
|
|
|
{
|
edge: Send a second BSN block in an RLC message if possible
Currently only one BSN block is encoded in each RLC data message,
even if MSC7-9 are used, which transport two independant (except for a
max BSN delta of 512) BSN blocks. In that case, the same block is
just put twice into the same message.
The current create_dl_acked_block(fn, ts) method handles
block selection (resend, new BSN, dummy block, ...) and restart
handling in one method and is too complex to extend it accordingly.
Therefore this commit move the block selection/creation handling into
a new method (take_next_bsn) which delivers the next BSN along with a
hint, whether it may be combined with another block. In that case,
the function can be called a second time (this time with a valid
previous BSN, that's the one returned by the first call) to get the
second BSN. The real block generation method
create_dl_acked_block(fn, ts, index, index2) is then called with both
BSNs as indices (the second must be -1, if there is only one BSN).
Note that every BSN returned by take_next_bsn should be passed to
create_dl_acked_block to avoid state inconsistencies.
Sponsored-by: On-Waves ehf
2016-02-02 17:12:46 +00:00
|
|
|
/* If V(S) == V(A) and finished state, we would have received
|
|
|
|
* acknowledgement of all transmitted block. In this case we would
|
|
|
|
* have transmitted the final block, and received ack from MS. But in
|
|
|
|
* this case we did not receive the final ack indication from MS. This
|
|
|
|
* should never happen if MS works correctly.
|
|
|
|
*/
|
|
|
|
if (m_window.window_empty()) {
|
2018-01-19 17:22:25 +00:00
|
|
|
LOGPTBFDL(this, LOGL_DEBUG, "MS acked all blocks\n");
|
edge: Send a second BSN block in an RLC message if possible
Currently only one BSN block is encoded in each RLC data message,
even if MSC7-9 are used, which transport two independant (except for a
max BSN delta of 512) BSN blocks. In that case, the same block is
just put twice into the same message.
The current create_dl_acked_block(fn, ts) method handles
block selection (resend, new BSN, dummy block, ...) and restart
handling in one method and is too complex to extend it accordingly.
Therefore this commit move the block selection/creation handling into
a new method (take_next_bsn) which delivers the next BSN along with a
hint, whether it may be combined with another block. In that case,
the function can be called a second time (this time with a valid
previous BSN, that's the one returned by the first call) to get the
second BSN. The real block generation method
create_dl_acked_block(fn, ts, index, index2) is then called with both
BSNs as indices (the second must be -1, if there is only one BSN).
Note that every BSN returned by take_next_bsn should be passed to
create_dl_acked_block to avoid state inconsistencies.
Sponsored-by: On-Waves ehf
2016-02-02 17:12:46 +00:00
|
|
|
return false;
|
|
|
|
}
|
2014-08-08 10:14:12 +00:00
|
|
|
|
edge: Send a second BSN block in an RLC message if possible
Currently only one BSN block is encoded in each RLC data message,
even if MSC7-9 are used, which transport two independant (except for a
max BSN delta of 512) BSN blocks. In that case, the same block is
just put twice into the same message.
The current create_dl_acked_block(fn, ts) method handles
block selection (resend, new BSN, dummy block, ...) and restart
handling in one method and is too complex to extend it accordingly.
Therefore this commit move the block selection/creation handling into
a new method (take_next_bsn) which delivers the next BSN along with a
hint, whether it may be combined with another block. In that case,
the function can be called a second time (this time with a valid
previous BSN, that's the one returned by the first call) to get the
second BSN. The real block generation method
create_dl_acked_block(fn, ts, index, index2) is then called with both
BSNs as indices (the second must be -1, if there is only one BSN).
Note that every BSN returned by take_next_bsn should be passed to
create_dl_acked_block to avoid state inconsistencies.
Sponsored-by: On-Waves ehf
2016-02-02 17:12:46 +00:00
|
|
|
/* cycle through all unacked blocks */
|
|
|
|
int resend = m_window.mark_for_resend();
|
|
|
|
|
|
|
|
/* At this point there should be at least one unacked block
|
|
|
|
* to be resent. If not, this is an software error. */
|
|
|
|
if (resend == 0) {
|
2018-01-19 17:22:25 +00:00
|
|
|
LOGPTBFDL(this, LOGL_ERROR,
|
|
|
|
"FIXME: Software error: There are no unacknowledged blocks, but V(A) != V(S). PLEASE FIX!\n");
|
edge: Send a second BSN block in an RLC message if possible
Currently only one BSN block is encoded in each RLC data message,
even if MSC7-9 are used, which transport two independant (except for a
max BSN delta of 512) BSN blocks. In that case, the same block is
just put twice into the same message.
The current create_dl_acked_block(fn, ts) method handles
block selection (resend, new BSN, dummy block, ...) and restart
handling in one method and is too complex to extend it accordingly.
Therefore this commit move the block selection/creation handling into
a new method (take_next_bsn) which delivers the next BSN along with a
hint, whether it may be combined with another block. In that case,
the function can be called a second time (this time with a valid
previous BSN, that's the one returned by the first call) to get the
second BSN. The real block generation method
create_dl_acked_block(fn, ts, index, index2) is then called with both
BSNs as indices (the second must be -1, if there is only one BSN).
Note that every BSN returned by take_next_bsn should be passed to
create_dl_acked_block to avoid state inconsistencies.
Sponsored-by: On-Waves ehf
2016-02-02 17:12:46 +00:00
|
|
|
return false;
|
2014-08-08 10:14:12 +00:00
|
|
|
}
|
|
|
|
|
edge: Send a second BSN block in an RLC message if possible
Currently only one BSN block is encoded in each RLC data message,
even if MSC7-9 are used, which transport two independant (except for a
max BSN delta of 512) BSN blocks. In that case, the same block is
just put twice into the same message.
The current create_dl_acked_block(fn, ts) method handles
block selection (resend, new BSN, dummy block, ...) and restart
handling in one method and is too complex to extend it accordingly.
Therefore this commit move the block selection/creation handling into
a new method (take_next_bsn) which delivers the next BSN along with a
hint, whether it may be combined with another block. In that case,
the function can be called a second time (this time with a valid
previous BSN, that's the one returned by the first call) to get the
second BSN. The real block generation method
create_dl_acked_block(fn, ts, index, index2) is then called with both
BSNs as indices (the second must be -1, if there is only one BSN).
Note that every BSN returned by take_next_bsn should be passed to
create_dl_acked_block to avoid state inconsistencies.
Sponsored-by: On-Waves ehf
2016-02-02 17:12:46 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
int gprs_rlcmac_dl_tbf::take_next_bsn(uint32_t fn,
|
Implement downgrade to DL MCS1-4 when USF for GPRS_only MS
In previous status, if USF for GPRS-only MS was selected, then EGPRS
TBFs were skipped and either a GPRS TBF was selected or a Dummy Block
was sent. That means the behavior was unfair towards EGPRS TBFs, because
sometimes they were skipped in favor of GPRS ones.
This patch imporves the situation in the above mentioned USF scenario, by
first, under specific conditions, allowing selection of an EGPRS TBF and
then forcing it to transmit in EGPRS-GMSK (MCS1-4) so that the
USF-targeted MS can still decode the USF, while at the same time
providing more fairness by allowing the EGPRS TBF to transmit data.
The specific conditions mentioned above are, mainly, related to the fact
that once a DL data block has been sent, and hence a BSN was assigned to
it, it cannot be retransmitted later using another MCS, since lower
MCS1-4 wouldn't be able to contain higher MCS RLC payload.
The set of conditions could be expanded in the future by also selecting
the EGPRS TBF if retransmition is required and the block to be
retransmitted was originally transmitted as MCS1-4.
Related: OS#4544
Change-Id: I9af23e175435fe9ae7b0e4119ad52fcd4707b9ca
2020-11-16 17:49:39 +00:00
|
|
|
int previous_bsn, enum mcs_kind req_mcs_kind, bool *may_combine)
|
edge: Send a second BSN block in an RLC message if possible
Currently only one BSN block is encoded in each RLC data message,
even if MSC7-9 are used, which transport two independant (except for a
max BSN delta of 512) BSN blocks. In that case, the same block is
just put twice into the same message.
The current create_dl_acked_block(fn, ts) method handles
block selection (resend, new BSN, dummy block, ...) and restart
handling in one method and is too complex to extend it accordingly.
Therefore this commit move the block selection/creation handling into
a new method (take_next_bsn) which delivers the next BSN along with a
hint, whether it may be combined with another block. In that case,
the function can be called a second time (this time with a valid
previous BSN, that's the one returned by the first call) to get the
second BSN. The real block generation method
create_dl_acked_block(fn, ts, index, index2) is then called with both
BSNs as indices (the second must be -1, if there is only one BSN).
Note that every BSN returned by take_next_bsn should be passed to
create_dl_acked_block to avoid state inconsistencies.
Sponsored-by: On-Waves ehf
2016-02-02 17:12:46 +00:00
|
|
|
{
|
|
|
|
int bsn;
|
2016-02-04 14:16:47 +00:00
|
|
|
int data_len2, force_data_len = -1;
|
2021-01-25 11:05:32 +00:00
|
|
|
enum CodingScheme tx_cs;
|
|
|
|
|
|
|
|
/* Scheduler may be fine with sending any kind of data, but if
|
|
|
|
the selected TBF is GPRS-only, then let's filter out EGPRS
|
|
|
|
here */
|
|
|
|
if (!is_egprs_enabled())
|
|
|
|
req_mcs_kind = GPRS;
|
edge: Send a second BSN block in an RLC message if possible
Currently only one BSN block is encoded in each RLC data message,
even if MSC7-9 are used, which transport two independant (except for a
max BSN delta of 512) BSN blocks. In that case, the same block is
just put twice into the same message.
The current create_dl_acked_block(fn, ts) method handles
block selection (resend, new BSN, dummy block, ...) and restart
handling in one method and is too complex to extend it accordingly.
Therefore this commit move the block selection/creation handling into
a new method (take_next_bsn) which delivers the next BSN along with a
hint, whether it may be combined with another block. In that case,
the function can be called a second time (this time with a valid
previous BSN, that's the one returned by the first call) to get the
second BSN. The real block generation method
create_dl_acked_block(fn, ts, index, index2) is then called with both
BSNs as indices (the second must be -1, if there is only one BSN).
Note that every BSN returned by take_next_bsn should be passed to
create_dl_acked_block to avoid state inconsistencies.
Sponsored-by: On-Waves ehf
2016-02-02 17:12:46 +00:00
|
|
|
|
2019-09-10 13:34:49 +00:00
|
|
|
/* search for a nacked or resend marked bsn */
|
edge: Send a second BSN block in an RLC message if possible
Currently only one BSN block is encoded in each RLC data message,
even if MSC7-9 are used, which transport two independant (except for a
max BSN delta of 512) BSN blocks. In that case, the same block is
just put twice into the same message.
The current create_dl_acked_block(fn, ts) method handles
block selection (resend, new BSN, dummy block, ...) and restart
handling in one method and is too complex to extend it accordingly.
Therefore this commit move the block selection/creation handling into
a new method (take_next_bsn) which delivers the next BSN along with a
hint, whether it may be combined with another block. In that case,
the function can be called a second time (this time with a valid
previous BSN, that's the one returned by the first call) to get the
second BSN. The real block generation method
create_dl_acked_block(fn, ts, index, index2) is then called with both
BSNs as indices (the second must be -1, if there is only one BSN).
Note that every BSN returned by take_next_bsn should be passed to
create_dl_acked_block to avoid state inconsistencies.
Sponsored-by: On-Waves ehf
2016-02-02 17:12:46 +00:00
|
|
|
bsn = m_window.resend_needed();
|
|
|
|
|
|
|
|
if (previous_bsn >= 0) {
|
2021-01-25 11:05:32 +00:00
|
|
|
tx_cs = m_rlc.block(previous_bsn)->cs_current_trans;
|
|
|
|
if (!mcs_is_edge(tx_cs))
|
edge: Send a second BSN block in an RLC message if possible
Currently only one BSN block is encoded in each RLC data message,
even if MSC7-9 are used, which transport two independant (except for a
max BSN delta of 512) BSN blocks. In that case, the same block is
just put twice into the same message.
The current create_dl_acked_block(fn, ts) method handles
block selection (resend, new BSN, dummy block, ...) and restart
handling in one method and is too complex to extend it accordingly.
Therefore this commit move the block selection/creation handling into
a new method (take_next_bsn) which delivers the next BSN along with a
hint, whether it may be combined with another block. In that case,
the function can be called a second time (this time with a valid
previous BSN, that's the one returned by the first call) to get the
second BSN. The real block generation method
create_dl_acked_block(fn, ts, index, index2) is then called with both
BSNs as indices (the second must be -1, if there is only one BSN).
Note that every BSN returned by take_next_bsn should be passed to
create_dl_acked_block to avoid state inconsistencies.
Sponsored-by: On-Waves ehf
2016-02-02 17:12:46 +00:00
|
|
|
return -1;
|
2016-02-04 14:16:47 +00:00
|
|
|
force_data_len = m_rlc.block(previous_bsn)->len;
|
2021-01-25 11:05:32 +00:00
|
|
|
} else {
|
|
|
|
tx_cs = ms_current_cs_dl(ms(), req_mcs_kind);
|
edge: Send a second BSN block in an RLC message if possible
Currently only one BSN block is encoded in each RLC data message,
even if MSC7-9 are used, which transport two independant (except for a
max BSN delta of 512) BSN blocks. In that case, the same block is
just put twice into the same message.
The current create_dl_acked_block(fn, ts) method handles
block selection (resend, new BSN, dummy block, ...) and restart
handling in one method and is too complex to extend it accordingly.
Therefore this commit move the block selection/creation handling into
a new method (take_next_bsn) which delivers the next BSN along with a
hint, whether it may be combined with another block. In that case,
the function can be called a second time (this time with a valid
previous BSN, that's the one returned by the first call) to get the
second BSN. The real block generation method
create_dl_acked_block(fn, ts, index, index2) is then called with both
BSNs as indices (the second must be -1, if there is only one BSN).
Note that every BSN returned by take_next_bsn should be passed to
create_dl_acked_block to avoid state inconsistencies.
Sponsored-by: On-Waves ehf
2016-02-02 17:12:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (bsn >= 0) {
|
2019-09-10 13:34:49 +00:00
|
|
|
/* resend an unacked bsn or resend bsn. */
|
edge: Send a second BSN block in an RLC message if possible
Currently only one BSN block is encoded in each RLC data message,
even if MSC7-9 are used, which transport two independant (except for a
max BSN delta of 512) BSN blocks. In that case, the same block is
just put twice into the same message.
The current create_dl_acked_block(fn, ts) method handles
block selection (resend, new BSN, dummy block, ...) and restart
handling in one method and is too complex to extend it accordingly.
Therefore this commit move the block selection/creation handling into
a new method (take_next_bsn) which delivers the next BSN along with a
hint, whether it may be combined with another block. In that case,
the function can be called a second time (this time with a valid
previous BSN, that's the one returned by the first call) to get the
second BSN. The real block generation method
create_dl_acked_block(fn, ts, index, index2) is then called with both
BSNs as indices (the second must be -1, if there is only one BSN).
Note that every BSN returned by take_next_bsn should be passed to
create_dl_acked_block to avoid state inconsistencies.
Sponsored-by: On-Waves ehf
2016-02-02 17:12:46 +00:00
|
|
|
if (previous_bsn == bsn)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (previous_bsn >= 0 &&
|
|
|
|
m_window.mod_sns(bsn - previous_bsn) > RLC_EGPRS_MAX_BSN_DELTA)
|
|
|
|
return -1;
|
|
|
|
|
2016-07-13 13:16:17 +00:00
|
|
|
if (is_egprs_enabled()) {
|
2016-08-22 11:51:10 +00:00
|
|
|
/* Table 8.1.1.2 and Table 8.1.1.1 of 44.060 */
|
2021-01-25 11:05:32 +00:00
|
|
|
m_rlc.block(bsn)->cs_current_trans = get_retx_mcs(m_rlc.block(bsn)->cs_init, tx_cs,
|
2021-01-14 12:08:02 +00:00
|
|
|
bts->pcu->vty.dl_arq_type == EGPRS_ARQ1);
|
2016-07-13 13:16:17 +00:00
|
|
|
|
2018-01-19 17:22:25 +00:00
|
|
|
LOGPTBFDL(this, LOGL_DEBUG,
|
2019-03-06 17:03:58 +00:00
|
|
|
"initial_cs_dl(%s) last_mcs(%s) demanded_mcs(%s) cs_trans(%s) arq_type(%d) bsn(%d)\n",
|
|
|
|
mcs_name(m_rlc.block(bsn)->cs_init),
|
|
|
|
mcs_name(m_rlc.block(bsn)->cs_last),
|
2021-01-25 11:05:32 +00:00
|
|
|
mcs_name(tx_cs),
|
2019-03-06 17:03:58 +00:00
|
|
|
mcs_name(m_rlc.block(bsn)->cs_current_trans),
|
2021-01-14 12:08:02 +00:00
|
|
|
the_pcu->vty.dl_arq_type, bsn);
|
2016-07-13 13:16:17 +00:00
|
|
|
|
|
|
|
/* TODO: Need to remove this check when MCS-8 -> MCS-6
|
|
|
|
* transistion is handled.
|
|
|
|
* Refer commit be881c028fc4da00c4046ecd9296727975c206a3
|
|
|
|
*/
|
2019-03-06 16:04:59 +00:00
|
|
|
if (m_rlc.block(bsn)->cs_init == MCS8)
|
2016-07-13 13:16:17 +00:00
|
|
|
m_rlc.block(bsn)->cs_current_trans =
|
2019-03-06 16:04:59 +00:00
|
|
|
MCS8;
|
2019-09-10 13:34:49 +00:00
|
|
|
} else {
|
|
|
|
/* gprs */
|
2016-07-13 13:16:17 +00:00
|
|
|
m_rlc.block(bsn)->cs_current_trans =
|
|
|
|
m_rlc.block(bsn)->cs_last;
|
2019-09-10 13:34:49 +00:00
|
|
|
}
|
2016-07-13 13:16:17 +00:00
|
|
|
|
2016-02-04 14:16:47 +00:00
|
|
|
data_len2 = m_rlc.block(bsn)->len;
|
|
|
|
if (force_data_len > 0 && force_data_len != data_len2)
|
edge: Send a second BSN block in an RLC message if possible
Currently only one BSN block is encoded in each RLC data message,
even if MSC7-9 are used, which transport two independant (except for a
max BSN delta of 512) BSN blocks. In that case, the same block is
just put twice into the same message.
The current create_dl_acked_block(fn, ts) method handles
block selection (resend, new BSN, dummy block, ...) and restart
handling in one method and is too complex to extend it accordingly.
Therefore this commit move the block selection/creation handling into
a new method (take_next_bsn) which delivers the next BSN along with a
hint, whether it may be combined with another block. In that case,
the function can be called a second time (this time with a valid
previous BSN, that's the one returned by the first call) to get the
second BSN. The real block generation method
create_dl_acked_block(fn, ts, index, index2) is then called with both
BSNs as indices (the second must be -1, if there is only one BSN).
Note that every BSN returned by take_next_bsn should be passed to
create_dl_acked_block to avoid state inconsistencies.
Sponsored-by: On-Waves ehf
2016-02-02 17:12:46 +00:00
|
|
|
return -1;
|
2018-01-19 17:22:25 +00:00
|
|
|
LOGPTBFDL(this, LOGL_DEBUG, "Resending BSN %d\n", bsn);
|
edge: Send a second BSN block in an RLC message if possible
Currently only one BSN block is encoded in each RLC data message,
even if MSC7-9 are used, which transport two independant (except for a
max BSN delta of 512) BSN blocks. In that case, the same block is
just put twice into the same message.
The current create_dl_acked_block(fn, ts) method handles
block selection (resend, new BSN, dummy block, ...) and restart
handling in one method and is too complex to extend it accordingly.
Therefore this commit move the block selection/creation handling into
a new method (take_next_bsn) which delivers the next BSN along with a
hint, whether it may be combined with another block. In that case,
the function can be called a second time (this time with a valid
previous BSN, that's the one returned by the first call) to get the
second BSN. The real block generation method
create_dl_acked_block(fn, ts, index, index2) is then called with both
BSNs as indices (the second must be -1, if there is only one BSN).
Note that every BSN returned by take_next_bsn should be passed to
create_dl_acked_block to avoid state inconsistencies.
Sponsored-by: On-Waves ehf
2016-02-02 17:12:46 +00:00
|
|
|
/* re-send block with negative aknowlegement */
|
|
|
|
m_window.m_v_b.mark_unacked(bsn);
|
2021-01-14 15:48:38 +00:00
|
|
|
bts_do_rate_ctr_inc(bts, CTR_RLC_RESENT);
|
2021-05-14 10:50:46 +00:00
|
|
|
} else if (state_is(TBF_ST_FINISHED)) {
|
2019-09-10 13:34:49 +00:00
|
|
|
/* If the TBF is in finished, we already sent all packages at least once.
|
|
|
|
* If any packages could have been sent (because of unacked) it should have
|
|
|
|
* been catched up by the upper if(bsn >= 0) */
|
2018-01-19 17:22:25 +00:00
|
|
|
LOGPTBFDL(this, LOGL_DEBUG,
|
|
|
|
"Restarting at BSN %d, because all blocks have been transmitted.\n",
|
|
|
|
m_window.v_a());
|
2021-01-14 15:48:38 +00:00
|
|
|
bts_do_rate_ctr_inc(bts, CTR_RLC_RESTARTED);
|
edge: Send a second BSN block in an RLC message if possible
Currently only one BSN block is encoded in each RLC data message,
even if MSC7-9 are used, which transport two independant (except for a
max BSN delta of 512) BSN blocks. In that case, the same block is
just put twice into the same message.
The current create_dl_acked_block(fn, ts) method handles
block selection (resend, new BSN, dummy block, ...) and restart
handling in one method and is too complex to extend it accordingly.
Therefore this commit move the block selection/creation handling into
a new method (take_next_bsn) which delivers the next BSN along with a
hint, whether it may be combined with another block. In that case,
the function can be called a second time (this time with a valid
previous BSN, that's the one returned by the first call) to get the
second BSN. The real block generation method
create_dl_acked_block(fn, ts, index, index2) is then called with both
BSNs as indices (the second must be -1, if there is only one BSN).
Note that every BSN returned by take_next_bsn should be passed to
create_dl_acked_block to avoid state inconsistencies.
Sponsored-by: On-Waves ehf
2016-02-02 17:12:46 +00:00
|
|
|
if (restart_bsn_cycle())
|
Implement downgrade to DL MCS1-4 when USF for GPRS_only MS
In previous status, if USF for GPRS-only MS was selected, then EGPRS
TBFs were skipped and either a GPRS TBF was selected or a Dummy Block
was sent. That means the behavior was unfair towards EGPRS TBFs, because
sometimes they were skipped in favor of GPRS ones.
This patch imporves the situation in the above mentioned USF scenario, by
first, under specific conditions, allowing selection of an EGPRS TBF and
then forcing it to transmit in EGPRS-GMSK (MCS1-4) so that the
USF-targeted MS can still decode the USF, while at the same time
providing more fairness by allowing the EGPRS TBF to transmit data.
The specific conditions mentioned above are, mainly, related to the fact
that once a DL data block has been sent, and hence a BSN was assigned to
it, it cannot be retransmitted later using another MCS, since lower
MCS1-4 wouldn't be able to contain higher MCS RLC payload.
The set of conditions could be expanded in the future by also selecting
the EGPRS TBF if retransmition is required and the block to be
retransmitted was originally transmitted as MCS1-4.
Related: OS#4544
Change-Id: I9af23e175435fe9ae7b0e4119ad52fcd4707b9ca
2020-11-16 17:49:39 +00:00
|
|
|
return take_next_bsn(fn, previous_bsn, req_mcs_kind, may_combine);
|
2015-03-19 12:22:07 +00:00
|
|
|
} else if (dl_window_stalled()) {
|
2019-09-10 13:34:49 +00:00
|
|
|
/* There are no more packages to send, but the window is stalled.
|
|
|
|
* Restart the bsn_cycle to resend all unacked messages */
|
2018-01-19 17:22:25 +00:00
|
|
|
LOGPTBFDL(this, LOGL_NOTICE,
|
|
|
|
"Restarting at BSN %d, because the window is stalled.\n",
|
|
|
|
m_window.v_a());
|
2021-01-14 15:48:38 +00:00
|
|
|
bts_do_rate_ctr_inc(bts, CTR_RLC_STALLED);
|
edge: Send a second BSN block in an RLC message if possible
Currently only one BSN block is encoded in each RLC data message,
even if MSC7-9 are used, which transport two independant (except for a
max BSN delta of 512) BSN blocks. In that case, the same block is
just put twice into the same message.
The current create_dl_acked_block(fn, ts) method handles
block selection (resend, new BSN, dummy block, ...) and restart
handling in one method and is too complex to extend it accordingly.
Therefore this commit move the block selection/creation handling into
a new method (take_next_bsn) which delivers the next BSN along with a
hint, whether it may be combined with another block. In that case,
the function can be called a second time (this time with a valid
previous BSN, that's the one returned by the first call) to get the
second BSN. The real block generation method
create_dl_acked_block(fn, ts, index, index2) is then called with both
BSNs as indices (the second must be -1, if there is only one BSN).
Note that every BSN returned by take_next_bsn should be passed to
create_dl_acked_block to avoid state inconsistencies.
Sponsored-by: On-Waves ehf
2016-02-02 17:12:46 +00:00
|
|
|
if (restart_bsn_cycle())
|
Implement downgrade to DL MCS1-4 when USF for GPRS_only MS
In previous status, if USF for GPRS-only MS was selected, then EGPRS
TBFs were skipped and either a GPRS TBF was selected or a Dummy Block
was sent. That means the behavior was unfair towards EGPRS TBFs, because
sometimes they were skipped in favor of GPRS ones.
This patch imporves the situation in the above mentioned USF scenario, by
first, under specific conditions, allowing selection of an EGPRS TBF and
then forcing it to transmit in EGPRS-GMSK (MCS1-4) so that the
USF-targeted MS can still decode the USF, while at the same time
providing more fairness by allowing the EGPRS TBF to transmit data.
The specific conditions mentioned above are, mainly, related to the fact
that once a DL data block has been sent, and hence a BSN was assigned to
it, it cannot be retransmitted later using another MCS, since lower
MCS1-4 wouldn't be able to contain higher MCS RLC payload.
The set of conditions could be expanded in the future by also selecting
the EGPRS TBF if retransmition is required and the block to be
retransmitted was originally transmitted as MCS1-4.
Related: OS#4544
Change-Id: I9af23e175435fe9ae7b0e4119ad52fcd4707b9ca
2020-11-16 17:49:39 +00:00
|
|
|
return take_next_bsn(fn, previous_bsn, req_mcs_kind, may_combine);
|
2015-03-19 13:21:33 +00:00
|
|
|
} else if (have_data()) {
|
2019-09-10 13:34:49 +00:00
|
|
|
/* The window has space left, generate new bsn */
|
2018-01-19 17:22:25 +00:00
|
|
|
LOGPTBFDL(this, LOGL_DEBUG,
|
Implement downgrade to DL MCS1-4 when USF for GPRS_only MS
In previous status, if USF for GPRS-only MS was selected, then EGPRS
TBFs were skipped and either a GPRS TBF was selected or a Dummy Block
was sent. That means the behavior was unfair towards EGPRS TBFs, because
sometimes they were skipped in favor of GPRS ones.
This patch imporves the situation in the above mentioned USF scenario, by
first, under specific conditions, allowing selection of an EGPRS TBF and
then forcing it to transmit in EGPRS-GMSK (MCS1-4) so that the
USF-targeted MS can still decode the USF, while at the same time
providing more fairness by allowing the EGPRS TBF to transmit data.
The specific conditions mentioned above are, mainly, related to the fact
that once a DL data block has been sent, and hence a BSN was assigned to
it, it cannot be retransmitted later using another MCS, since lower
MCS1-4 wouldn't be able to contain higher MCS RLC payload.
The set of conditions could be expanded in the future by also selecting
the EGPRS TBF if retransmition is required and the block to be
retransmitted was originally transmitted as MCS1-4.
Related: OS#4544
Change-Id: I9af23e175435fe9ae7b0e4119ad52fcd4707b9ca
2020-11-16 17:49:39 +00:00
|
|
|
"Sending new block at BSN %d, CS=%s%s\n",
|
2021-01-25 11:05:32 +00:00
|
|
|
m_window.v_s(), mcs_name(tx_cs),
|
|
|
|
force_data_len != -1 ? " (forced)" : "");
|
edge: Send a second BSN block in an RLC message if possible
Currently only one BSN block is encoded in each RLC data message,
even if MSC7-9 are used, which transport two independant (except for a
max BSN delta of 512) BSN blocks. In that case, the same block is
just put twice into the same message.
The current create_dl_acked_block(fn, ts) method handles
block selection (resend, new BSN, dummy block, ...) and restart
handling in one method and is too complex to extend it accordingly.
Therefore this commit move the block selection/creation handling into
a new method (take_next_bsn) which delivers the next BSN along with a
hint, whether it may be combined with another block. In that case,
the function can be called a second time (this time with a valid
previous BSN, that's the one returned by the first call) to get the
second BSN. The real block generation method
create_dl_acked_block(fn, ts, index, index2) is then called with both
BSNs as indices (the second must be -1, if there is only one BSN).
Note that every BSN returned by take_next_bsn should be passed to
create_dl_acked_block to avoid state inconsistencies.
Sponsored-by: On-Waves ehf
2016-02-02 17:12:46 +00:00
|
|
|
|
2021-01-25 11:05:32 +00:00
|
|
|
bsn = create_new_bsn(fn, tx_cs);
|
2021-01-14 11:56:58 +00:00
|
|
|
} else if (bts->pcu->vty.dl_tbf_preemptive_retransmission && !m_window.window_empty()) {
|
2019-09-10 13:34:49 +00:00
|
|
|
/* The window contains unacked packages, but not acked.
|
|
|
|
* Mark unacked bsns as RESEND */
|
2018-01-19 17:22:25 +00:00
|
|
|
LOGPTBFDL(this, LOGL_DEBUG,
|
|
|
|
"Restarting at BSN %d, because all blocks have been transmitted (FLOW).\n",
|
|
|
|
m_window.v_a());
|
2021-01-14 15:48:38 +00:00
|
|
|
bts_do_rate_ctr_inc(bts, CTR_RLC_RESTARTED);
|
edge: Send a second BSN block in an RLC message if possible
Currently only one BSN block is encoded in each RLC data message,
even if MSC7-9 are used, which transport two independant (except for a
max BSN delta of 512) BSN blocks. In that case, the same block is
just put twice into the same message.
The current create_dl_acked_block(fn, ts) method handles
block selection (resend, new BSN, dummy block, ...) and restart
handling in one method and is too complex to extend it accordingly.
Therefore this commit move the block selection/creation handling into
a new method (take_next_bsn) which delivers the next BSN along with a
hint, whether it may be combined with another block. In that case,
the function can be called a second time (this time with a valid
previous BSN, that's the one returned by the first call) to get the
second BSN. The real block generation method
create_dl_acked_block(fn, ts, index, index2) is then called with both
BSNs as indices (the second must be -1, if there is only one BSN).
Note that every BSN returned by take_next_bsn should be passed to
create_dl_acked_block to avoid state inconsistencies.
Sponsored-by: On-Waves ehf
2016-02-02 17:12:46 +00:00
|
|
|
if (restart_bsn_cycle())
|
Implement downgrade to DL MCS1-4 when USF for GPRS_only MS
In previous status, if USF for GPRS-only MS was selected, then EGPRS
TBFs were skipped and either a GPRS TBF was selected or a Dummy Block
was sent. That means the behavior was unfair towards EGPRS TBFs, because
sometimes they were skipped in favor of GPRS ones.
This patch imporves the situation in the above mentioned USF scenario, by
first, under specific conditions, allowing selection of an EGPRS TBF and
then forcing it to transmit in EGPRS-GMSK (MCS1-4) so that the
USF-targeted MS can still decode the USF, while at the same time
providing more fairness by allowing the EGPRS TBF to transmit data.
The specific conditions mentioned above are, mainly, related to the fact
that once a DL data block has been sent, and hence a BSN was assigned to
it, it cannot be retransmitted later using another MCS, since lower
MCS1-4 wouldn't be able to contain higher MCS RLC payload.
The set of conditions could be expanded in the future by also selecting
the EGPRS TBF if retransmition is required and the block to be
retransmitted was originally transmitted as MCS1-4.
Related: OS#4544
Change-Id: I9af23e175435fe9ae7b0e4119ad52fcd4707b9ca
2020-11-16 17:49:39 +00:00
|
|
|
return take_next_bsn(fn, previous_bsn, req_mcs_kind, may_combine);
|
2015-03-19 12:22:07 +00:00
|
|
|
} else {
|
2015-03-19 13:21:33 +00:00
|
|
|
/* Nothing left to send, create dummy LLC commands */
|
2018-01-19 17:22:25 +00:00
|
|
|
LOGPTBFDL(this, LOGL_DEBUG,
|
|
|
|
"Sending new dummy block at BSN %d, CS=%s\n",
|
2021-01-25 11:05:32 +00:00
|
|
|
m_window.v_s(), mcs_name(tx_cs));
|
|
|
|
bsn = create_new_bsn(fn, tx_cs);
|
2016-07-13 13:16:17 +00:00
|
|
|
/* Don't send a second block, so don't set cs_current_trans */
|
2014-08-08 10:14:12 +00:00
|
|
|
}
|
|
|
|
|
edge: Send a second BSN block in an RLC message if possible
Currently only one BSN block is encoded in each RLC data message,
even if MSC7-9 are used, which transport two independant (except for a
max BSN delta of 512) BSN blocks. In that case, the same block is
just put twice into the same message.
The current create_dl_acked_block(fn, ts) method handles
block selection (resend, new BSN, dummy block, ...) and restart
handling in one method and is too complex to extend it accordingly.
Therefore this commit move the block selection/creation handling into
a new method (take_next_bsn) which delivers the next BSN along with a
hint, whether it may be combined with another block. In that case,
the function can be called a second time (this time with a valid
previous BSN, that's the one returned by the first call) to get the
second BSN. The real block generation method
create_dl_acked_block(fn, ts, index, index2) is then called with both
BSNs as indices (the second must be -1, if there is only one BSN).
Note that every BSN returned by take_next_bsn should be passed to
create_dl_acked_block to avoid state inconsistencies.
Sponsored-by: On-Waves ehf
2016-02-02 17:12:46 +00:00
|
|
|
if (bsn < 0) {
|
2015-03-19 12:22:07 +00:00
|
|
|
/* we just send final block again */
|
2018-01-19 17:22:25 +00:00
|
|
|
LOGPTBFDL(this, LOGL_DEBUG,
|
|
|
|
"Nothing else to send, Re-transmit final block!\n");
|
edge: Send a second BSN block in an RLC message if possible
Currently only one BSN block is encoded in each RLC data message,
even if MSC7-9 are used, which transport two independant (except for a
max BSN delta of 512) BSN blocks. In that case, the same block is
just put twice into the same message.
The current create_dl_acked_block(fn, ts) method handles
block selection (resend, new BSN, dummy block, ...) and restart
handling in one method and is too complex to extend it accordingly.
Therefore this commit move the block selection/creation handling into
a new method (take_next_bsn) which delivers the next BSN along with a
hint, whether it may be combined with another block. In that case,
the function can be called a second time (this time with a valid
previous BSN, that's the one returned by the first call) to get the
second BSN. The real block generation method
create_dl_acked_block(fn, ts, index, index2) is then called with both
BSNs as indices (the second must be -1, if there is only one BSN).
Note that every BSN returned by take_next_bsn should be passed to
create_dl_acked_block to avoid state inconsistencies.
Sponsored-by: On-Waves ehf
2016-02-02 17:12:46 +00:00
|
|
|
bsn = m_window.v_s_mod(-1);
|
2021-01-14 15:48:38 +00:00
|
|
|
bts_do_rate_ctr_inc(bts, CTR_RLC_FINAL_BLOCK_RESENT);
|
|
|
|
bts_do_rate_ctr_inc(bts, CTR_RLC_RESENT);
|
2015-03-19 12:22:07 +00:00
|
|
|
}
|
|
|
|
|
2020-05-18 09:35:35 +00:00
|
|
|
*may_combine = num_data_blocks(mcs_header_type(m_rlc.block(bsn)->cs_current_trans)) > 1;
|
2015-03-19 12:22:07 +00:00
|
|
|
|
edge: Send a second BSN block in an RLC message if possible
Currently only one BSN block is encoded in each RLC data message,
even if MSC7-9 are used, which transport two independant (except for a
max BSN delta of 512) BSN blocks. In that case, the same block is
just put twice into the same message.
The current create_dl_acked_block(fn, ts) method handles
block selection (resend, new BSN, dummy block, ...) and restart
handling in one method and is too complex to extend it accordingly.
Therefore this commit move the block selection/creation handling into
a new method (take_next_bsn) which delivers the next BSN along with a
hint, whether it may be combined with another block. In that case,
the function can be called a second time (this time with a valid
previous BSN, that's the one returned by the first call) to get the
second BSN. The real block generation method
create_dl_acked_block(fn, ts, index, index2) is then called with both
BSNs as indices (the second must be -1, if there is only one BSN).
Note that every BSN returned by take_next_bsn should be passed to
create_dl_acked_block to avoid state inconsistencies.
Sponsored-by: On-Waves ehf
2016-02-02 17:12:46 +00:00
|
|
|
return bsn;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create DL data block
|
|
|
|
* The messages are fragmented and forwarded as data blocks.
|
|
|
|
*/
|
Implement downgrade to DL MCS1-4 when USF for GPRS_only MS
In previous status, if USF for GPRS-only MS was selected, then EGPRS
TBFs were skipped and either a GPRS TBF was selected or a Dummy Block
was sent. That means the behavior was unfair towards EGPRS TBFs, because
sometimes they were skipped in favor of GPRS ones.
This patch imporves the situation in the above mentioned USF scenario, by
first, under specific conditions, allowing selection of an EGPRS TBF and
then forcing it to transmit in EGPRS-GMSK (MCS1-4) so that the
USF-targeted MS can still decode the USF, while at the same time
providing more fairness by allowing the EGPRS TBF to transmit data.
The specific conditions mentioned above are, mainly, related to the fact
that once a DL data block has been sent, and hence a BSN was assigned to
it, it cannot be retransmitted later using another MCS, since lower
MCS1-4 wouldn't be able to contain higher MCS RLC payload.
The set of conditions could be expanded in the future by also selecting
the EGPRS TBF if retransmition is required and the block to be
retransmitted was originally transmitted as MCS1-4.
Related: OS#4544
Change-Id: I9af23e175435fe9ae7b0e4119ad52fcd4707b9ca
2020-11-16 17:49:39 +00:00
|
|
|
struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(uint32_t fn, uint8_t ts, enum mcs_kind req_mcs_kind)
|
edge: Send a second BSN block in an RLC message if possible
Currently only one BSN block is encoded in each RLC data message,
even if MSC7-9 are used, which transport two independant (except for a
max BSN delta of 512) BSN blocks. In that case, the same block is
just put twice into the same message.
The current create_dl_acked_block(fn, ts) method handles
block selection (resend, new BSN, dummy block, ...) and restart
handling in one method and is too complex to extend it accordingly.
Therefore this commit move the block selection/creation handling into
a new method (take_next_bsn) which delivers the next BSN along with a
hint, whether it may be combined with another block. In that case,
the function can be called a second time (this time with a valid
previous BSN, that's the one returned by the first call) to get the
second BSN. The real block generation method
create_dl_acked_block(fn, ts, index, index2) is then called with both
BSNs as indices (the second must be -1, if there is only one BSN).
Note that every BSN returned by take_next_bsn should be passed to
create_dl_acked_block to avoid state inconsistencies.
Sponsored-by: On-Waves ehf
2016-02-02 17:12:46 +00:00
|
|
|
{
|
|
|
|
int bsn, bsn2 = -1;
|
2016-02-04 14:16:47 +00:00
|
|
|
bool may_combine;
|
edge: Send a second BSN block in an RLC message if possible
Currently only one BSN block is encoded in each RLC data message,
even if MSC7-9 are used, which transport two independant (except for a
max BSN delta of 512) BSN blocks. In that case, the same block is
just put twice into the same message.
The current create_dl_acked_block(fn, ts) method handles
block selection (resend, new BSN, dummy block, ...) and restart
handling in one method and is too complex to extend it accordingly.
Therefore this commit move the block selection/creation handling into
a new method (take_next_bsn) which delivers the next BSN along with a
hint, whether it may be combined with another block. In that case,
the function can be called a second time (this time with a valid
previous BSN, that's the one returned by the first call) to get the
second BSN. The real block generation method
create_dl_acked_block(fn, ts, index, index2) is then called with both
BSNs as indices (the second must be -1, if there is only one BSN).
Note that every BSN returned by take_next_bsn should be passed to
create_dl_acked_block to avoid state inconsistencies.
Sponsored-by: On-Waves ehf
2016-02-02 17:12:46 +00:00
|
|
|
|
Implement downgrade to DL MCS1-4 when USF for GPRS_only MS
In previous status, if USF for GPRS-only MS was selected, then EGPRS
TBFs were skipped and either a GPRS TBF was selected or a Dummy Block
was sent. That means the behavior was unfair towards EGPRS TBFs, because
sometimes they were skipped in favor of GPRS ones.
This patch imporves the situation in the above mentioned USF scenario, by
first, under specific conditions, allowing selection of an EGPRS TBF and
then forcing it to transmit in EGPRS-GMSK (MCS1-4) so that the
USF-targeted MS can still decode the USF, while at the same time
providing more fairness by allowing the EGPRS TBF to transmit data.
The specific conditions mentioned above are, mainly, related to the fact
that once a DL data block has been sent, and hence a BSN was assigned to
it, it cannot be retransmitted later using another MCS, since lower
MCS1-4 wouldn't be able to contain higher MCS RLC payload.
The set of conditions could be expanded in the future by also selecting
the EGPRS TBF if retransmition is required and the block to be
retransmitted was originally transmitted as MCS1-4.
Related: OS#4544
Change-Id: I9af23e175435fe9ae7b0e4119ad52fcd4707b9ca
2020-11-16 17:49:39 +00:00
|
|
|
LOGPTBFDL(this, LOGL_DEBUG, "downlink (V(A)==%d .. V(S)==%d) mcs_mode_restrict=%s\n",
|
|
|
|
m_window.v_a(), m_window.v_s(), mode_name(req_mcs_kind));
|
edge: Send a second BSN block in an RLC message if possible
Currently only one BSN block is encoded in each RLC data message,
even if MSC7-9 are used, which transport two independant (except for a
max BSN delta of 512) BSN blocks. In that case, the same block is
just put twice into the same message.
The current create_dl_acked_block(fn, ts) method handles
block selection (resend, new BSN, dummy block, ...) and restart
handling in one method and is too complex to extend it accordingly.
Therefore this commit move the block selection/creation handling into
a new method (take_next_bsn) which delivers the next BSN along with a
hint, whether it may be combined with another block. In that case,
the function can be called a second time (this time with a valid
previous BSN, that's the one returned by the first call) to get the
second BSN. The real block generation method
create_dl_acked_block(fn, ts, index, index2) is then called with both
BSNs as indices (the second must be -1, if there is only one BSN).
Note that every BSN returned by take_next_bsn should be passed to
create_dl_acked_block to avoid state inconsistencies.
Sponsored-by: On-Waves ehf
2016-02-02 17:12:46 +00:00
|
|
|
|
Implement downgrade to DL MCS1-4 when USF for GPRS_only MS
In previous status, if USF for GPRS-only MS was selected, then EGPRS
TBFs were skipped and either a GPRS TBF was selected or a Dummy Block
was sent. That means the behavior was unfair towards EGPRS TBFs, because
sometimes they were skipped in favor of GPRS ones.
This patch imporves the situation in the above mentioned USF scenario, by
first, under specific conditions, allowing selection of an EGPRS TBF and
then forcing it to transmit in EGPRS-GMSK (MCS1-4) so that the
USF-targeted MS can still decode the USF, while at the same time
providing more fairness by allowing the EGPRS TBF to transmit data.
The specific conditions mentioned above are, mainly, related to the fact
that once a DL data block has been sent, and hence a BSN was assigned to
it, it cannot be retransmitted later using another MCS, since lower
MCS1-4 wouldn't be able to contain higher MCS RLC payload.
The set of conditions could be expanded in the future by also selecting
the EGPRS TBF if retransmition is required and the block to be
retransmitted was originally transmitted as MCS1-4.
Related: OS#4544
Change-Id: I9af23e175435fe9ae7b0e4119ad52fcd4707b9ca
2020-11-16 17:49:39 +00:00
|
|
|
bsn = take_next_bsn(fn, -1, req_mcs_kind, &may_combine);
|
edge: Send a second BSN block in an RLC message if possible
Currently only one BSN block is encoded in each RLC data message,
even if MSC7-9 are used, which transport two independant (except for a
max BSN delta of 512) BSN blocks. In that case, the same block is
just put twice into the same message.
The current create_dl_acked_block(fn, ts) method handles
block selection (resend, new BSN, dummy block, ...) and restart
handling in one method and is too complex to extend it accordingly.
Therefore this commit move the block selection/creation handling into
a new method (take_next_bsn) which delivers the next BSN along with a
hint, whether it may be combined with another block. In that case,
the function can be called a second time (this time with a valid
previous BSN, that's the one returned by the first call) to get the
second BSN. The real block generation method
create_dl_acked_block(fn, ts, index, index2) is then called with both
BSNs as indices (the second must be -1, if there is only one BSN).
Note that every BSN returned by take_next_bsn should be passed to
create_dl_acked_block to avoid state inconsistencies.
Sponsored-by: On-Waves ehf
2016-02-02 17:12:46 +00:00
|
|
|
if (bsn < 0)
|
|
|
|
return NULL;
|
|
|
|
|
2016-02-04 14:16:47 +00:00
|
|
|
if (may_combine)
|
Implement downgrade to DL MCS1-4 when USF for GPRS_only MS
In previous status, if USF for GPRS-only MS was selected, then EGPRS
TBFs were skipped and either a GPRS TBF was selected or a Dummy Block
was sent. That means the behavior was unfair towards EGPRS TBFs, because
sometimes they were skipped in favor of GPRS ones.
This patch imporves the situation in the above mentioned USF scenario, by
first, under specific conditions, allowing selection of an EGPRS TBF and
then forcing it to transmit in EGPRS-GMSK (MCS1-4) so that the
USF-targeted MS can still decode the USF, while at the same time
providing more fairness by allowing the EGPRS TBF to transmit data.
The specific conditions mentioned above are, mainly, related to the fact
that once a DL data block has been sent, and hence a BSN was assigned to
it, it cannot be retransmitted later using another MCS, since lower
MCS1-4 wouldn't be able to contain higher MCS RLC payload.
The set of conditions could be expanded in the future by also selecting
the EGPRS TBF if retransmition is required and the block to be
retransmitted was originally transmitted as MCS1-4.
Related: OS#4544
Change-Id: I9af23e175435fe9ae7b0e4119ad52fcd4707b9ca
2020-11-16 17:49:39 +00:00
|
|
|
bsn2 = take_next_bsn(fn, bsn, req_mcs_kind, &may_combine);
|
edge: Send a second BSN block in an RLC message if possible
Currently only one BSN block is encoded in each RLC data message,
even if MSC7-9 are used, which transport two independant (except for a
max BSN delta of 512) BSN blocks. In that case, the same block is
just put twice into the same message.
The current create_dl_acked_block(fn, ts) method handles
block selection (resend, new BSN, dummy block, ...) and restart
handling in one method and is too complex to extend it accordingly.
Therefore this commit move the block selection/creation handling into
a new method (take_next_bsn) which delivers the next BSN along with a
hint, whether it may be combined with another block. In that case,
the function can be called a second time (this time with a valid
previous BSN, that's the one returned by the first call) to get the
second BSN. The real block generation method
create_dl_acked_block(fn, ts, index, index2) is then called with both
BSNs as indices (the second must be -1, if there is only one BSN).
Note that every BSN returned by take_next_bsn should be passed to
create_dl_acked_block to avoid state inconsistencies.
Sponsored-by: On-Waves ehf
2016-02-02 17:12:46 +00:00
|
|
|
|
|
|
|
return create_dl_acked_block(fn, ts, bsn, bsn2);
|
2014-08-08 10:14:12 +00:00
|
|
|
}
|
|
|
|
|
2017-07-07 16:25:41 +00:00
|
|
|
/* depending on the current TBF, we assign on PACCH or AGCH */
|
|
|
|
void gprs_rlcmac_dl_tbf::trigger_ass(struct gprs_rlcmac_tbf *old_tbf)
|
|
|
|
{
|
2019-12-23 11:41:34 +00:00
|
|
|
uint16_t pgroup;
|
2017-07-07 16:25:41 +00:00
|
|
|
/* stop pending timer */
|
2017-12-20 16:31:13 +00:00
|
|
|
stop_timers("assignment (DL-TBF)");
|
2017-07-07 16:25:41 +00:00
|
|
|
|
|
|
|
/* check for downlink tbf: */
|
|
|
|
if (old_tbf) {
|
2017-12-15 16:36:45 +00:00
|
|
|
LOGPTBFDL(this, LOGL_DEBUG, "Send dowlink assignment on PACCH, because %s exists\n", old_tbf->name());
|
2018-01-23 19:09:06 +00:00
|
|
|
TBF_SET_ASS_STATE_DL(old_tbf, GPRS_RLCMAC_DL_ASS_SEND_ASS);
|
2021-05-14 10:50:46 +00:00
|
|
|
old_tbf->was_releasing = old_tbf->state_is(TBF_ST_WAIT_RELEASE);
|
2017-07-07 16:25:41 +00:00
|
|
|
|
|
|
|
/* change state */
|
2021-07-22 17:20:50 +00:00
|
|
|
osmo_fsm_inst_dispatch(this->state_fsm.fi, TBF_EV_ASSIGN_ADD_PACCH, NULL);
|
2017-07-07 16:25:41 +00:00
|
|
|
|
2021-04-26 16:49:33 +00:00
|
|
|
/* Start timer, expiry in gprs_rlcmac_tbf::handle_timeout tbf_free()s the TBF */
|
2019-09-05 12:48:35 +00:00
|
|
|
T_START(this, T0, -2001, "assignment (PACCH)", true);
|
2017-07-07 16:25:41 +00:00
|
|
|
} else {
|
2017-12-15 16:36:45 +00:00
|
|
|
LOGPTBFDL(this, LOGL_DEBUG, "Send dowlink assignment on PCH, no TBF exist (IMSI=%s)\n",
|
|
|
|
imsi());
|
2021-05-14 10:50:46 +00:00
|
|
|
was_releasing = state_is(TBF_ST_WAIT_RELEASE);
|
2017-07-07 16:25:41 +00:00
|
|
|
|
|
|
|
/* change state */
|
2021-07-22 17:20:50 +00:00
|
|
|
osmo_fsm_inst_dispatch(this->state_fsm.fi, TBF_EV_ASSIGN_ADD_CCCH, NULL);
|
2017-07-07 16:25:41 +00:00
|
|
|
|
|
|
|
/* send immediate assignment */
|
2019-12-23 11:41:34 +00:00
|
|
|
if ((pgroup = imsi2paging_group(imsi())) > 999)
|
|
|
|
LOGPTBFDL(this, LOGL_ERROR, "IMSI to paging group failed! (%s)\n", imsi());
|
2021-03-29 16:40:18 +00:00
|
|
|
bts_snd_dl_ass(bts, this, pgroup);
|
2017-07-07 16:25:41 +00:00
|
|
|
m_wait_confirm = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-12 12:06:09 +00:00
|
|
|
void gprs_rlcmac_dl_tbf::schedule_next_frame()
|
|
|
|
{
|
|
|
|
struct msgb *msg;
|
|
|
|
|
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 (llc_frame_length(&m_llc) != 0)
|
2015-06-12 12:06:09 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
/* dequeue next LLC frame, if any */
|
2021-01-21 17:02:40 +00:00
|
|
|
msg = llc_dequeue(bts->pcu->bssgp.bctx);
|
2015-06-12 12:06:09 +00:00
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
|
2018-01-30 15:03:10 +00:00
|
|
|
LOGPTBFDL(this, LOGL_DEBUG, "Dequeue next LLC (len=%d)\n", msg->len);
|
2015-06-12 12:06:09 +00:00
|
|
|
|
|
|
|
m_llc.put_frame(msg->data, msg->len);
|
2021-01-14 15:48:38 +00:00
|
|
|
bts_do_rate_ctr_inc(bts, CTR_LLC_FRAME_SCHED);
|
2015-06-12 12:06:09 +00:00
|
|
|
msgb_free(msg);
|
|
|
|
m_last_dl_drained_fn = -1;
|
|
|
|
}
|
|
|
|
|
2020-05-18 09:35:35 +00:00
|
|
|
int gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, enum CodingScheme cs)
|
2014-08-08 10:14:12 +00:00
|
|
|
{
|
2016-01-12 16:02:49 +00:00
|
|
|
uint8_t *data;
|
2014-08-08 10:14:12 +00:00
|
|
|
gprs_rlc_data *rlc_data;
|
|
|
|
const uint16_t bsn = m_window.v_s();
|
2016-01-11 15:15:45 +00:00
|
|
|
gprs_rlc_data_block_info *rdbi;
|
2016-01-12 16:02:49 +00:00
|
|
|
int num_chunks = 0;
|
|
|
|
int write_offset = 0;
|
|
|
|
Encoding::AppendResult ar;
|
2014-08-08 10:14:12 +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
|
|
|
if (llc_frame_length(&m_llc) == 0)
|
2015-06-12 12:06:09 +00:00
|
|
|
schedule_next_frame();
|
|
|
|
|
2020-05-18 09:35:35 +00:00
|
|
|
OSMO_ASSERT(mcs_is_valid(cs));
|
2015-06-02 14:00:41 +00:00
|
|
|
|
2016-01-11 15:15:45 +00:00
|
|
|
/* length of usable data block (single data unit w/o header) */
|
2020-05-18 09:35:35 +00:00
|
|
|
const uint8_t block_data_len = mcs_max_data_block_bytes(cs);
|
2014-08-08 10:14:12 +00:00
|
|
|
|
|
|
|
/* now we still have untransmitted LLC data, so we fill mac block */
|
|
|
|
rlc_data = m_rlc.block(bsn);
|
2020-05-18 09:02:39 +00:00
|
|
|
data = prepare(rlc_data, block_data_len);
|
2016-07-12 08:47:12 +00:00
|
|
|
rlc_data->cs_last = cs;
|
2016-07-13 13:16:17 +00:00
|
|
|
rlc_data->cs_current_trans = cs;
|
2016-08-22 11:51:10 +00:00
|
|
|
|
|
|
|
/* Initialise the variable related to DL SPB */
|
|
|
|
rlc_data->spb_status.block_status_dl = EGPRS_RESEG_DL_DEFAULT;
|
|
|
|
rlc_data->cs_init = cs;
|
|
|
|
|
2016-01-11 15:15:45 +00:00
|
|
|
rlc_data->len = block_data_len;
|
2014-08-08 10:14:12 +00:00
|
|
|
|
2016-01-11 15:15:45 +00:00
|
|
|
rdbi = &(rlc_data->block_info);
|
|
|
|
memset(rdbi, 0, sizeof(*rdbi));
|
|
|
|
rdbi->data_len = block_data_len;
|
|
|
|
|
|
|
|
rdbi->cv = 15; /* Final Block Indicator, set late, if true */
|
|
|
|
rdbi->bsn = bsn; /* Block Sequence Number */
|
|
|
|
rdbi->e = 1; /* Extension bit, maybe set later (1: no extension) */
|
|
|
|
|
2016-01-12 16:02:49 +00:00
|
|
|
do {
|
|
|
|
bool is_final;
|
2016-05-21 17:45:23 +00:00
|
|
|
int payload_written = 0;
|
2016-01-12 16:02:49 +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
|
|
|
if (llc_frame_length(&m_llc) == 0) {
|
2021-03-02 12:19:39 +00:00
|
|
|
/* The data just drained, store the current fn */
|
|
|
|
if (m_last_dl_drained_fn < 0)
|
|
|
|
m_last_dl_drained_fn = fn;
|
|
|
|
|
2020-11-27 13:51:27 +00:00
|
|
|
/* It is not clear, when the next real data will
|
|
|
|
* arrive, so request a DL ack/nack now */
|
|
|
|
request_dl_ack();
|
2016-05-24 14:55:30 +00:00
|
|
|
|
2016-01-12 16:02:49 +00:00
|
|
|
int space = block_data_len - write_offset;
|
2020-11-27 13:51:27 +00:00
|
|
|
|
|
|
|
if (num_chunks != 0) {
|
|
|
|
/* Nothing to send, and we already put some data in
|
|
|
|
* rlcmac data block, we are done */
|
|
|
|
LOGPTBFDL(this, LOGL_DEBUG,
|
|
|
|
"LLC queue completely drained and there's "
|
|
|
|
"still %d free bytes in rlcmac data block\n", space);
|
2021-03-02 13:15:17 +00:00
|
|
|
|
|
|
|
/* We may need to update fbi in header here
|
|
|
|
* since m_last_dl_drained_fn was updated above
|
|
|
|
* Specially important when X2031 is 0. */
|
|
|
|
is_final = llc_queue_size(llc_queue()) == 0 && !keep_open(fn);
|
|
|
|
if (is_final) {
|
|
|
|
rdbi->cv = 0;
|
2021-07-23 16:27:57 +00:00
|
|
|
osmo_fsm_inst_dispatch(this->state_fsm.fi, TBF_EV_LAST_DL_DATA_SENT, NULL);
|
2021-03-02 13:15:17 +00:00
|
|
|
}
|
|
|
|
|
2020-11-27 13:51:27 +00:00
|
|
|
if (mcs_is_edge(cs)) {
|
|
|
|
/* in EGPRS there's no M bit, so we need
|
|
|
|
* to flag padding with LI=127 */
|
|
|
|
Encoding::rlc_data_to_dl_append_egprs_li_padding(rdbi,
|
|
|
|
&write_offset,
|
|
|
|
&num_chunks,
|
|
|
|
data);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Nothing to send from upper layers (LLC), but still
|
|
|
|
* requested to send something to MS to delay the
|
|
|
|
* release of the TBF. See 3GPP TS 44.060 9.3.1a
|
|
|
|
* "Delayed release of downlink Temporary Block Flow" */
|
2015-03-25 11:21:55 +00:00
|
|
|
/* A header will need to by added, so we just need
|
|
|
|
* space-1 octets */
|
|
|
|
m_llc.put_dummy_frame(space - 1);
|
|
|
|
|
2018-01-19 17:22:25 +00:00
|
|
|
LOGPTBFDL(this, LOGL_DEBUG,
|
|
|
|
"Empty chunk, added LLC dummy command of size %d, drained_since=%d\n",
|
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
|
|
|
llc_frame_length(&m_llc), frames_since_last_drain(fn));
|
2015-03-25 11:21:55 +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
|
|
|
is_final = llc_queue_size(llc_queue()) == 0 && !keep_open(fn);
|
2016-01-12 16:02:49 +00:00
|
|
|
|
2016-01-13 09:51:25 +00:00
|
|
|
ar = Encoding::rlc_data_to_dl_append(rdbi, cs,
|
2016-05-21 17:45:23 +00:00
|
|
|
&m_llc, &write_offset, &num_chunks, data, is_final, &payload_written);
|
|
|
|
|
|
|
|
if (payload_written > 0)
|
2021-01-14 15:48:38 +00:00
|
|
|
bts_do_rate_ctr_add(bts, CTR_RLC_DL_PAYLOAD_BYTES, payload_written);
|
2016-01-12 16:02:49 +00:00
|
|
|
|
|
|
|
if (ar == Encoding::AR_NEED_MORE_BLOCKS)
|
2014-08-08 10:14:12 +00:00
|
|
|
break;
|
2015-03-19 10:22: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
|
|
|
LOGPTBFDL(this, LOGL_DEBUG, "Complete DL frame, len=%d\n", llc_frame_length(&m_llc));
|
|
|
|
gprs_rlcmac_dl_bw(this, llc_frame_length(&m_llc));
|
2021-01-14 15:48:38 +00:00
|
|
|
bts_do_rate_ctr_add(bts, CTR_LLC_DL_BYTES, llc_frame_length(&m_llc));
|
2014-08-08 10:14:12 +00:00
|
|
|
m_llc.reset();
|
2015-03-19 10:22:38 +00:00
|
|
|
|
2016-01-12 16:02:49 +00:00
|
|
|
if (is_final) {
|
2015-03-12 11:08:54 +00:00
|
|
|
request_dl_ack();
|
2021-07-23 16:27:57 +00:00
|
|
|
osmo_fsm_inst_dispatch(this->state_fsm.fi, TBF_EV_LAST_DL_DATA_SENT, NULL);
|
2014-08-08 10:14:12 +00:00
|
|
|
}
|
2016-01-12 16:02:49 +00:00
|
|
|
|
|
|
|
/* dequeue next LLC frame, if any */
|
|
|
|
schedule_next_frame();
|
|
|
|
} while (ar == Encoding::AR_COMPLETED_SPACE_LEFT);
|
|
|
|
|
2018-01-19 17:22:25 +00:00
|
|
|
LOGPTBFDL(this, LOGL_DEBUG, "data block (BSN %d, %s): %s\n",
|
2019-03-05 13:59:03 +00:00
|
|
|
bsn, mcs_name(rlc_data->cs_last),
|
2018-01-19 17:22:25 +00:00
|
|
|
osmo_hexdump(rlc_data->block, block_data_len));
|
2014-08-08 10:14:12 +00:00
|
|
|
/* raise send state and set ack state array */
|
|
|
|
m_window.m_v_b.mark_unacked(bsn);
|
|
|
|
m_window.increment_send();
|
|
|
|
|
edge: Send a second BSN block in an RLC message if possible
Currently only one BSN block is encoded in each RLC data message,
even if MSC7-9 are used, which transport two independant (except for a
max BSN delta of 512) BSN blocks. In that case, the same block is
just put twice into the same message.
The current create_dl_acked_block(fn, ts) method handles
block selection (resend, new BSN, dummy block, ...) and restart
handling in one method and is too complex to extend it accordingly.
Therefore this commit move the block selection/creation handling into
a new method (take_next_bsn) which delivers the next BSN along with a
hint, whether it may be combined with another block. In that case,
the function can be called a second time (this time with a valid
previous BSN, that's the one returned by the first call) to get the
second BSN. The real block generation method
create_dl_acked_block(fn, ts, index, index2) is then called with both
BSNs as indices (the second must be -1, if there is only one BSN).
Note that every BSN returned by take_next_bsn should be passed to
create_dl_acked_block to avoid state inconsistencies.
Sponsored-by: On-Waves ehf
2016-02-02 17:12:46 +00:00
|
|
|
return bsn;
|
2015-03-12 11:08:54 +00:00
|
|
|
}
|
|
|
|
|
2017-07-07 11:49:29 +00:00
|
|
|
bool gprs_rlcmac_dl_tbf::handle_ack_nack()
|
|
|
|
{
|
|
|
|
bool ack_recovered = false;
|
|
|
|
|
2021-07-22 17:20:50 +00:00
|
|
|
state_fsm.state_flags |= (1 << GPRS_RLCMAC_FLAG_DL_ACK);
|
2018-01-02 13:17:04 +00:00
|
|
|
if (check_n_clear(GPRS_RLCMAC_FLAG_TO_DL_ACK)) {
|
2017-07-07 11:49:29 +00:00
|
|
|
ack_recovered = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* reset N3105 */
|
2018-02-20 17:16:11 +00:00
|
|
|
n_reset(N3105);
|
2017-12-20 16:31:13 +00:00
|
|
|
t_stop(T3191, "ACK/NACK received");
|
2017-07-07 11:49:29 +00:00
|
|
|
|
|
|
|
return ack_recovered;
|
|
|
|
}
|
|
|
|
|
2014-08-08 10:14:12 +00:00
|
|
|
struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(
|
|
|
|
const uint32_t fn, const uint8_t ts,
|
2016-02-02 14:32:10 +00:00
|
|
|
int index, int index2)
|
2014-08-08 10:14:12 +00:00
|
|
|
{
|
2016-02-02 14:32:10 +00:00
|
|
|
uint8_t *msg_data;
|
2014-08-08 10:14:12 +00:00
|
|
|
struct msgb *dl_msg;
|
2016-01-11 15:15:45 +00:00
|
|
|
unsigned msg_len;
|
2014-08-25 14:20:23 +00:00
|
|
|
bool need_poll;
|
2016-01-11 15:15:45 +00:00
|
|
|
/* TODO: support MCS-7 - MCS-9, where data_block_idx can be 1 */
|
2017-12-21 11:11:33 +00:00
|
|
|
uint8_t data_block_idx = 0;
|
2016-01-26 20:46:26 +00:00
|
|
|
unsigned int rrbp;
|
|
|
|
uint32_t new_poll_fn;
|
|
|
|
int rc;
|
2016-02-02 14:32:10 +00:00
|
|
|
bool is_final = false;
|
2016-01-11 15:15:45 +00:00
|
|
|
gprs_rlc_data_info rlc;
|
2020-05-18 09:35:35 +00:00
|
|
|
enum CodingScheme cs;
|
2016-02-02 14:32:10 +00:00
|
|
|
int bsns[ARRAY_SIZE(rlc.block_info)];
|
|
|
|
unsigned num_bsns;
|
|
|
|
bool need_padding = false;
|
2016-08-22 11:51:10 +00:00
|
|
|
enum egprs_rlcmac_dl_spb spb = EGPRS_RLCMAC_DL_NO_RETX;
|
|
|
|
unsigned int spb_status = get_egprs_dl_spb_status(index);
|
2017-02-08 15:49:20 +00:00
|
|
|
|
|
|
|
enum egprs_puncturing_values punct[2] = {
|
|
|
|
EGPRS_PS_INVALID, EGPRS_PS_INVALID
|
|
|
|
};
|
|
|
|
osmo_static_assert(ARRAY_SIZE(rlc.block_info) == 2,
|
|
|
|
rlc_block_info_size_is_two);
|
|
|
|
|
2016-02-02 14:32:10 +00:00
|
|
|
/*
|
|
|
|
* TODO: This is an experimental work-around to put 2 BSN into
|
|
|
|
* MSC-7 to MCS-9 encoded messages. It just sends the same BSN
|
|
|
|
* twice in the block. The cs should be derived from the TBF's
|
|
|
|
* current CS such that both BSNs (that must be compatible) can
|
|
|
|
* be put into the data area, even if the resulting CS is higher than
|
|
|
|
* the current limit.
|
|
|
|
*/
|
2016-07-13 13:16:17 +00:00
|
|
|
cs = m_rlc.block(index)->cs_current_trans;
|
2019-03-13 17:44:20 +00:00
|
|
|
enum CodingScheme cs_init = m_rlc.block(index)->cs_init;
|
2016-02-02 14:32:10 +00:00
|
|
|
bsns[0] = index;
|
|
|
|
num_bsns = 1;
|
2014-08-08 10:14:12 +00:00
|
|
|
|
2016-02-02 14:32:10 +00:00
|
|
|
if (index2 >= 0) {
|
|
|
|
bsns[num_bsns] = index2;
|
|
|
|
num_bsns += 1;
|
|
|
|
}
|
2016-01-11 15:15:45 +00:00
|
|
|
|
2016-11-10 12:46:30 +00:00
|
|
|
update_coding_scheme_counter_dl(cs);
|
2016-08-22 11:51:10 +00:00
|
|
|
/*
|
|
|
|
* if the intial mcs is 8 and retransmission mcs is either 6 or 3
|
|
|
|
* we have to include the padding of 6 octets in first segment
|
|
|
|
*/
|
2019-03-13 17:44:20 +00:00
|
|
|
if ((cs_init == MCS8) &&
|
|
|
|
(cs == MCS6 || cs == MCS3)) {
|
2016-08-22 11:51:10 +00:00
|
|
|
if (spb_status == EGPRS_RESEG_DL_DEFAULT ||
|
2019-03-13 17:44:20 +00:00
|
|
|
spb_status == EGPRS_RESEG_SECOND_SEG_SENT)
|
2016-08-22 11:51:10 +00:00
|
|
|
need_padding = true;
|
|
|
|
} else if (num_bsns == 1) {
|
2016-02-03 19:44:46 +00:00
|
|
|
/* TODO: remove the conditional when MCS-6 padding isn't
|
|
|
|
* failing to be decoded by MEs anymore */
|
2016-07-13 13:16:17 +00:00
|
|
|
/* TODO: support of MCS-8 -> MCS-6 transition should be
|
|
|
|
* handled
|
|
|
|
* Refer commit be881c028fc4da00c4046ecd9296727975c206a3
|
|
|
|
* dated 2016-02-07 23:45:40 (UTC)
|
|
|
|
*/
|
2020-05-18 09:35:35 +00:00
|
|
|
if (cs != MCS8)
|
|
|
|
mcs_dec_to_single_block(&cs, &need_padding);
|
2016-02-03 19:44:46 +00:00
|
|
|
}
|
2016-01-11 15:15:45 +00:00
|
|
|
|
2016-08-22 11:51:10 +00:00
|
|
|
spb = get_egprs_dl_spb(index);
|
|
|
|
|
2018-01-19 17:22:25 +00:00
|
|
|
LOGPTBFDL(this, LOGL_DEBUG, "need_padding %d spb_status %d spb %d (BSN1 %d BSN2 %d)\n",
|
|
|
|
need_padding, spb_status, spb, index, index2);
|
2016-08-22 11:51:10 +00:00
|
|
|
|
|
|
|
gprs_rlc_data_info_init_dl(&rlc, cs, need_padding, spb);
|
2016-01-11 15:15:45 +00:00
|
|
|
|
|
|
|
rlc.usf = 7; /* will be set at scheduler */
|
|
|
|
rlc.pr = 0; /* FIXME: power reduction */
|
|
|
|
rlc.tfi = m_tfi; /* TFI */
|
|
|
|
|
2016-02-02 14:32:10 +00:00
|
|
|
/* return data block(s) as message */
|
2020-05-18 09:35:35 +00:00
|
|
|
msg_len = mcs_size_dl(cs);
|
2016-02-02 14:32:10 +00:00
|
|
|
dl_msg = msgb_alloc(msg_len, "rlcmac_dl_data");
|
|
|
|
if (!dl_msg)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
msg_data = msgb_put(dl_msg, msg_len);
|
|
|
|
|
2016-08-26 12:43:10 +00:00
|
|
|
OSMO_ASSERT(rlc.num_data_blocks <= ARRAY_SIZE(rlc.block_info));
|
|
|
|
OSMO_ASSERT(rlc.num_data_blocks > 0);
|
|
|
|
|
2018-01-19 17:22:25 +00:00
|
|
|
LOGPTBFDL(this, LOGL_DEBUG, "Copying %u RLC blocks, %u BSNs\n", rlc.num_data_blocks, num_bsns);
|
2017-09-08 10:23:12 +00:00
|
|
|
|
2017-12-21 11:11:33 +00:00
|
|
|
/* Copy block(s) to RLC message: the num_data_blocks cannot be more than 2 - see assert above */
|
|
|
|
for (data_block_idx = 0; data_block_idx < OSMO_MIN(rlc.num_data_blocks, 2);
|
2016-02-02 14:32:10 +00:00
|
|
|
data_block_idx++)
|
|
|
|
{
|
|
|
|
int bsn;
|
|
|
|
uint8_t *block_data;
|
|
|
|
gprs_rlc_data_block_info *rdbi, *block_info;
|
2016-08-22 11:51:10 +00:00
|
|
|
enum egprs_rlc_dl_reseg_bsn_state reseg_status;
|
2016-02-02 14:32:10 +00:00
|
|
|
|
|
|
|
/* Check if there are more blocks than BSNs */
|
|
|
|
if (data_block_idx < num_bsns)
|
|
|
|
bsn = bsns[data_block_idx];
|
|
|
|
else
|
|
|
|
bsn = bsns[0];
|
|
|
|
|
2016-03-23 12:59:47 +00:00
|
|
|
/* Get current puncturing scheme from block */
|
2016-08-22 11:51:10 +00:00
|
|
|
|
|
|
|
m_rlc.block(bsn)->next_ps = gprs_get_punct_scheme(
|
2016-03-23 12:59:47 +00:00
|
|
|
m_rlc.block(bsn)->next_ps,
|
2016-08-22 11:51:10 +00:00
|
|
|
m_rlc.block(bsn)->cs_last, cs, spb);
|
2016-03-23 12:59:47 +00:00
|
|
|
|
2019-03-25 15:32:50 +00:00
|
|
|
if (mcs_is_edge(cs)) {
|
2016-08-22 11:51:10 +00:00
|
|
|
OSMO_ASSERT(m_rlc.block(bsn)->next_ps >= EGPRS_PS_1);
|
|
|
|
OSMO_ASSERT(m_rlc.block(bsn)->next_ps <= EGPRS_PS_3);
|
2016-03-23 12:59:47 +00:00
|
|
|
}
|
2017-12-21 11:11:33 +00:00
|
|
|
|
2016-08-22 11:51:10 +00:00
|
|
|
punct[data_block_idx] = m_rlc.block(bsn)->next_ps;
|
2016-02-02 14:32:10 +00:00
|
|
|
|
|
|
|
rdbi = &rlc.block_info[data_block_idx];
|
|
|
|
block_info = &m_rlc.block(bsn)->block_info;
|
|
|
|
|
2016-08-22 11:51:10 +00:00
|
|
|
/*
|
|
|
|
* get data and header from current block
|
|
|
|
* function returns the reseg status
|
|
|
|
*/
|
|
|
|
reseg_status = egprs_dl_get_data(bsn, &block_data);
|
|
|
|
m_rlc.block(bsn)->spb_status.block_status_dl = reseg_status;
|
2016-03-23 12:59:47 +00:00
|
|
|
|
2016-08-22 11:51:10 +00:00
|
|
|
/*
|
|
|
|
* If it is first segment of the split block set the state of
|
|
|
|
* bsn to nacked. If it is the first segment dont update the
|
|
|
|
* next ps value of bsn. since next segment also needs same cps
|
2016-03-23 12:59:47 +00:00
|
|
|
*/
|
2016-08-22 11:51:10 +00:00
|
|
|
if (spb == EGPRS_RLCMAC_DL_FIRST_SEG)
|
|
|
|
m_window.m_v_b.mark_nacked(bsn);
|
|
|
|
else {
|
|
|
|
/*
|
|
|
|
* TODO: Need to handle 2 same bsns
|
|
|
|
* in header type 1
|
|
|
|
*/
|
|
|
|
gprs_update_punct_scheme(&m_rlc.block(bsn)->next_ps,
|
|
|
|
cs);
|
|
|
|
}
|
2016-03-23 12:59:47 +00:00
|
|
|
|
2016-07-13 13:16:17 +00:00
|
|
|
m_rlc.block(bsn)->cs_last = cs;
|
2016-02-02 14:32:10 +00:00
|
|
|
rdbi->e = block_info->e;
|
|
|
|
rdbi->cv = block_info->cv;
|
|
|
|
rdbi->bsn = bsn;
|
|
|
|
is_final = is_final || rdbi->cv == 0;
|
|
|
|
|
2018-01-19 17:22:25 +00:00
|
|
|
LOGPTBFDL(this, LOGL_DEBUG, "Copying data unit %d (BSN %d)\n",
|
|
|
|
data_block_idx, bsn);
|
2016-02-02 14:32:10 +00:00
|
|
|
|
|
|
|
Encoding::rlc_copy_from_aligned_buffer(&rlc, data_block_idx,
|
|
|
|
msg_data, block_data);
|
|
|
|
}
|
|
|
|
|
2017-02-08 14:47:19 +00:00
|
|
|
/* Calculate CPS only for EGPRS case */
|
2019-03-25 15:32:50 +00:00
|
|
|
if (mcs_is_edge(cs))
|
2017-02-08 14:47:19 +00:00
|
|
|
rlc.cps = gprs_rlc_mcs_cps(cs, punct[0], punct[1], need_padding);
|
2014-08-08 10:14:12 +00:00
|
|
|
|
2015-03-20 13:53:54 +00:00
|
|
|
/* If the TBF has just started, relate frames_since_last_poll to the
|
|
|
|
* current fn */
|
|
|
|
if (m_last_dl_poll_fn < 0)
|
|
|
|
m_last_dl_poll_fn = fn;
|
|
|
|
|
2021-07-22 17:20:50 +00:00
|
|
|
need_poll = state_fsm.state_flags & (1 << GPRS_RLCMAC_FLAG_TO_DL_ACK);
|
2016-01-11 15:15:45 +00:00
|
|
|
|
2014-08-08 10:14:12 +00:00
|
|
|
/* poll after POLL_ACK_AFTER_FRAMES frames, or when final block is tx.
|
|
|
|
*/
|
2015-03-12 11:08:54 +00:00
|
|
|
if (m_tx_counter >= POLL_ACK_AFTER_FRAMES || m_dl_ack_requested ||
|
2014-08-25 14:20:23 +00:00
|
|
|
need_poll) {
|
2015-03-12 11:08:54 +00:00
|
|
|
if (m_dl_ack_requested) {
|
2018-01-19 17:22:25 +00:00
|
|
|
LOGPTBFDL(this, LOGL_DEBUG,
|
2019-12-04 17:07:49 +00:00
|
|
|
"Scheduling Ack/Nack polling, because it was requested explicitly "
|
2018-01-19 17:22:25 +00:00
|
|
|
"(e.g. first final block sent).\n");
|
2014-08-25 14:20:23 +00:00
|
|
|
} else if (need_poll) {
|
2018-01-19 17:22:25 +00:00
|
|
|
LOGPTBFDL(this, LOGL_DEBUG,
|
|
|
|
"Scheduling Ack/Nack polling, because polling timed out.\n");
|
2014-08-08 10:14:12 +00:00
|
|
|
} else {
|
2018-01-19 17:22:25 +00:00
|
|
|
LOGPTBFDL(this, LOGL_DEBUG,
|
|
|
|
"Scheduling Ack/Nack polling, because %d blocks sent.\n",
|
2014-08-08 10:14:12 +00:00
|
|
|
POLL_ACK_AFTER_FRAMES);
|
|
|
|
}
|
2016-01-26 20:46:26 +00:00
|
|
|
|
|
|
|
rc = check_polling(fn, ts, &new_poll_fn, &rrbp);
|
|
|
|
if (rc >= 0) {
|
2021-03-29 16:15:30 +00:00
|
|
|
set_polling(new_poll_fn, ts, PDCH_ULC_POLL_DL_ACK);
|
2016-01-26 20:46:26 +00:00
|
|
|
|
2014-08-08 10:14:12 +00:00
|
|
|
m_tx_counter = 0;
|
|
|
|
/* start timer whenever we send the final block */
|
2016-02-02 14:32:10 +00:00
|
|
|
if (is_final)
|
2019-09-05 12:48:35 +00:00
|
|
|
T_START(this, T3191, 3191, "final block (DL-TBF)", true);
|
2014-08-08 10:14:12 +00:00
|
|
|
|
2021-07-22 17:20:50 +00:00
|
|
|
state_fsm.state_flags &= ~(1 << GPRS_RLCMAC_FLAG_TO_DL_ACK); /* clear poll timeout flag */
|
2014-08-25 14:20:23 +00:00
|
|
|
|
2015-03-12 11:08:54 +00:00
|
|
|
/* Clear request flag */
|
|
|
|
m_dl_ack_requested = false;
|
|
|
|
|
2014-08-08 10:14:12 +00:00
|
|
|
/* set polling in header */
|
2016-01-26 20:46:26 +00:00
|
|
|
rlc.rrbp = rrbp;
|
2016-01-11 15:15:45 +00:00
|
|
|
rlc.es_p = 1; /* Polling */
|
2015-03-20 13:53:54 +00:00
|
|
|
|
2021-03-29 17:01:13 +00:00
|
|
|
m_last_dl_poll_fn = new_poll_fn;
|
2015-08-24 11:30:39 +00:00
|
|
|
|
2018-01-19 17:22:25 +00:00
|
|
|
LOGPTBFDL(this, LOGL_INFO,
|
|
|
|
"Scheduled Ack/Nack polling on FN=%d, TS=%d\n",
|
2021-03-29 17:10:19 +00:00
|
|
|
new_poll_fn, ts);
|
2014-08-08 10:14:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-11 15:15:45 +00:00
|
|
|
Encoding::rlc_write_dl_data_header(&rlc, msg_data);
|
|
|
|
|
2018-01-19 17:22:25 +00:00
|
|
|
LOGPTBFDL(this, LOGL_DEBUG, "msg block (BSN %d, %s%s): %s\n",
|
2019-03-05 13:59:03 +00:00
|
|
|
index, mcs_name(cs),
|
2018-01-19 17:22:25 +00:00
|
|
|
need_padding ? ", padded" : "",
|
|
|
|
msgb_hexdump(dl_msg));
|
2016-01-11 15:15:45 +00:00
|
|
|
|
2014-08-08 10:14:12 +00:00
|
|
|
/* Increment TX-counter */
|
|
|
|
m_tx_counter++;
|
|
|
|
|
|
|
|
return dl_msg;
|
|
|
|
}
|
|
|
|
|
2015-12-23 15:29:07 +00:00
|
|
|
static uint16_t bitnum_to_bsn(int bitnum, uint16_t ssn)
|
2015-06-04 10:12:32 +00:00
|
|
|
{
|
2015-12-23 15:29:07 +00:00
|
|
|
return ssn - 1 - bitnum;
|
2015-06-04 10:12:32 +00:00
|
|
|
}
|
|
|
|
|
2015-09-07 16:49:00 +00:00
|
|
|
int gprs_rlcmac_dl_tbf::analyse_errors(char *show_rbb, uint8_t ssn,
|
|
|
|
ana_result *res)
|
2015-06-04 10:12:32 +00:00
|
|
|
{
|
|
|
|
gprs_rlc_data *rlc_data;
|
|
|
|
uint16_t lost = 0, received = 0, skipped = 0;
|
2016-02-05 16:13:24 +00:00
|
|
|
char info[RLC_MAX_WS + 1];
|
|
|
|
memset(info, '.', m_window.ws());
|
|
|
|
info[m_window.ws()] = 0;
|
2015-06-04 14:52:23 +00:00
|
|
|
uint16_t bsn = 0;
|
2015-09-07 16:49:00 +00:00
|
|
|
unsigned received_bytes = 0, lost_bytes = 0;
|
|
|
|
unsigned received_packets = 0, lost_packets = 0;
|
2016-02-05 16:13:24 +00:00
|
|
|
unsigned num_blocks = strlen(show_rbb);
|
2015-06-04 10:12:32 +00:00
|
|
|
|
2016-11-02 10:18:00 +00:00
|
|
|
unsigned distance = m_window.distance();
|
|
|
|
|
|
|
|
num_blocks = num_blocks > distance
|
|
|
|
? distance : num_blocks;
|
|
|
|
|
2015-06-04 10:12:32 +00:00
|
|
|
/* SSN - 1 is in range V(A)..V(S)-1 */
|
2016-02-05 16:13:24 +00:00
|
|
|
for (unsigned int bitpos = 0; bitpos < num_blocks; bitpos++) {
|
|
|
|
bool is_received;
|
|
|
|
int index = num_blocks - 1 - bitpos;
|
|
|
|
|
|
|
|
is_received = (index >= 0 && show_rbb[index] == 'R');
|
2015-09-07 16:49:00 +00:00
|
|
|
|
2015-12-23 15:29:07 +00:00
|
|
|
bsn = m_window.mod_sns(bitnum_to_bsn(bitpos, ssn));
|
2015-06-04 10:12:32 +00:00
|
|
|
|
2015-12-23 15:29:07 +00:00
|
|
|
if (bsn == m_window.mod_sns(m_window.v_a() - 1)) {
|
2015-06-04 14:52:23 +00:00
|
|
|
info[bitpos] = '$';
|
2015-06-04 10:12:32 +00:00
|
|
|
break;
|
2015-06-04 14:52:23 +00:00
|
|
|
}
|
2015-06-04 10:12:32 +00:00
|
|
|
|
|
|
|
rlc_data = m_rlc.block(bsn);
|
2015-06-04 14:52:23 +00:00
|
|
|
if (!rlc_data) {
|
|
|
|
info[bitpos] = '0';
|
2015-06-04 10:12:32 +00:00
|
|
|
continue;
|
2015-06-04 14:52:23 +00:00
|
|
|
}
|
2015-06-04 10:12:32 +00:00
|
|
|
|
2015-09-07 16:49:00 +00:00
|
|
|
/* Get general statistics */
|
|
|
|
if (is_received && !m_window.m_v_b.is_acked(bsn)) {
|
|
|
|
received_packets += 1;
|
|
|
|
received_bytes += rlc_data->len;
|
|
|
|
} else if (!is_received && !m_window.m_v_b.is_nacked(bsn)) {
|
|
|
|
lost_packets += 1;
|
|
|
|
lost_bytes += rlc_data->len;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get statistics for current CS */
|
|
|
|
|
2016-07-12 08:47:12 +00:00
|
|
|
if (rlc_data->cs_last != current_cs()) {
|
2015-06-04 10:12:32 +00:00
|
|
|
/* This block has already been encoded with a different
|
|
|
|
* CS, so it doesn't help us to decide, whether the
|
|
|
|
* current CS is ok. Ignore it. */
|
2015-06-04 14:52:23 +00:00
|
|
|
info[bitpos] = 'x';
|
2015-06-04 10:12:32 +00:00
|
|
|
skipped += 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-09-07 16:49:00 +00:00
|
|
|
if (is_received) {
|
2015-06-04 14:52:23 +00:00
|
|
|
if (!m_window.m_v_b.is_acked(bsn)) {
|
2015-06-04 10:12:32 +00:00
|
|
|
received += 1;
|
2015-06-04 14:52:23 +00:00
|
|
|
info[bitpos] = 'R';
|
|
|
|
} else {
|
|
|
|
info[bitpos] = 'r';
|
|
|
|
}
|
2015-06-04 10:12:32 +00:00
|
|
|
} else {
|
2015-06-04 14:52:23 +00:00
|
|
|
info[bitpos] = 'L';
|
2015-06-04 10:12:32 +00:00
|
|
|
lost += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-19 17:22:25 +00:00
|
|
|
LOGPTBFDL(this, LOGL_DEBUG,
|
|
|
|
"DL analysis, range=%d:%d, lost=%d, recv=%d, skipped=%d, bsn=%d, info='%s'\n",
|
|
|
|
m_window.v_a(), m_window.v_s(), lost, received, skipped, bsn, info);
|
2015-06-04 10:12:32 +00:00
|
|
|
|
2015-09-07 16:49:00 +00:00
|
|
|
res->received_packets = received_packets;
|
|
|
|
res->lost_packets = lost_packets;
|
|
|
|
res->received_bytes = received_bytes;
|
|
|
|
res->lost_bytes = lost_bytes;
|
|
|
|
|
2015-06-04 14:51:44 +00:00
|
|
|
if (lost + received <= 1)
|
2015-06-04 10:12:32 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
return lost * 100 / (lost + received);
|
|
|
|
}
|
|
|
|
|
2020-10-23 19:00:23 +00:00
|
|
|
gprs_rlc_window *gprs_rlcmac_dl_tbf::window()
|
2018-01-04 14:13:27 +00:00
|
|
|
{
|
|
|
|
return &m_window;
|
|
|
|
}
|
|
|
|
|
2016-02-05 16:07:12 +00:00
|
|
|
int gprs_rlcmac_dl_tbf::update_window(unsigned first_bsn,
|
|
|
|
const struct bitvec *rbb)
|
|
|
|
{
|
2016-11-02 10:18:00 +00:00
|
|
|
unsigned dist;
|
2016-02-05 16:07:12 +00:00
|
|
|
uint16_t lost = 0, received = 0;
|
|
|
|
char show_v_b[RLC_MAX_SNS + 1];
|
|
|
|
char show_rbb[RLC_MAX_SNS + 1];
|
|
|
|
int error_rate;
|
|
|
|
struct ana_result ana_res;
|
2016-11-02 10:18:00 +00:00
|
|
|
dist = m_window.distance();
|
|
|
|
unsigned num_blocks = rbb->cur_bit > dist
|
|
|
|
? dist : rbb->cur_bit;
|
2016-02-05 16:07:12 +00:00
|
|
|
unsigned behind_last_bsn = m_window.mod_sns(first_bsn + num_blocks);
|
|
|
|
|
|
|
|
Decoding::extract_rbb(rbb, show_rbb);
|
|
|
|
/* show received array in debug */
|
2018-01-19 17:22:25 +00:00
|
|
|
LOGPTBFDL(this, LOGL_DEBUG,
|
|
|
|
"ack: (BSN=%d)\"%s\"(BSN=%d) R=ACK I=NACK\n",
|
|
|
|
first_bsn, show_rbb, m_window.mod_sns(behind_last_bsn - 1));
|
2016-02-05 16:07:12 +00:00
|
|
|
|
|
|
|
error_rate = analyse_errors(show_rbb, behind_last_bsn, &ana_res);
|
|
|
|
|
2021-01-14 12:17:01 +00:00
|
|
|
if (the_pcu->vty.cs_adj_enabled && 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_update_error_rate(ms(), this, error_rate);
|
2016-02-05 16:07:12 +00:00
|
|
|
|
|
|
|
m_window.update(bts, rbb, first_bsn, &lost, &received);
|
2021-06-04 15:05:51 +00:00
|
|
|
rate_ctr_add(rate_ctr_group_get_ctr(m_ctrs, TBF_CTR_RLC_NACKED), lost);
|
2016-02-05 16:07:12 +00:00
|
|
|
|
|
|
|
/* report lost and received packets */
|
|
|
|
gprs_rlcmac_received_lost(this, received, lost);
|
|
|
|
|
|
|
|
/* Used to measure the leak rate */
|
|
|
|
gprs_bssgp_update_bytes_received(ana_res.received_bytes,
|
|
|
|
ana_res.received_packets + ana_res.lost_packets);
|
|
|
|
|
|
|
|
/* raise V(A), if possible */
|
|
|
|
m_window.raise(m_window.move_window());
|
|
|
|
|
|
|
|
/* show receive state array in debug (V(A)..V(S)-1) */
|
|
|
|
m_window.show_state(show_v_b);
|
2018-01-19 17:22:25 +00:00
|
|
|
LOGPTBFDL(this, LOGL_DEBUG,
|
|
|
|
"V(B): (V(A)=%d)\"%s\"(V(S)-1=%d) A=Acked N=Nacked U=Unacked X=Resend-Unacked I=Invalid\n",
|
|
|
|
m_window.v_a(), show_v_b, m_window.v_s_mod(-1));
|
2016-02-05 16:07:12 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2015-06-04 10:12:32 +00:00
|
|
|
|
2014-08-08 10:14:12 +00:00
|
|
|
int gprs_rlcmac_dl_tbf::update_window(const uint8_t ssn, const uint8_t *rbb)
|
|
|
|
{
|
|
|
|
int16_t dist; /* must be signed */
|
|
|
|
uint16_t lost = 0, received = 0;
|
|
|
|
char show_rbb[65];
|
|
|
|
char show_v_b[RLC_MAX_SNS + 1];
|
2015-06-04 10:12:32 +00:00
|
|
|
int error_rate;
|
2015-09-07 16:49:00 +00:00
|
|
|
struct ana_result ana_res;
|
2014-08-08 10:14:12 +00:00
|
|
|
|
|
|
|
Decoding::extract_rbb(rbb, show_rbb);
|
|
|
|
/* show received array in debug (bit 64..1) */
|
2018-01-19 17:22:25 +00:00
|
|
|
LOGPTBFDL(this, LOGL_DEBUG,
|
|
|
|
"ack: (BSN=%d)\"%s\"(BSN=%d) R=ACK I=NACK\n",
|
|
|
|
m_window.mod_sns(ssn - 64), show_rbb, m_window.mod_sns(ssn - 1));
|
2014-08-08 10:14:12 +00:00
|
|
|
|
|
|
|
/* apply received array to receive state (SSN-64..SSN-1) */
|
|
|
|
/* calculate distance of ssn from V(S) */
|
2015-12-23 15:29:07 +00:00
|
|
|
dist = m_window.mod_sns(m_window.v_s() - ssn);
|
2014-08-08 10:14:12 +00:00
|
|
|
/* check if distance is less than distance V(A)..V(S) */
|
|
|
|
if (dist >= m_window.distance()) {
|
|
|
|
/* this might happpen, if the downlink assignment
|
|
|
|
* was not received by ms and the ack refers
|
|
|
|
* to previous TBF
|
|
|
|
* FIXME: we should implement polling for
|
|
|
|
* control ack!*/
|
2017-12-15 16:36:45 +00:00
|
|
|
LOGPTBFDL(this, LOGL_NOTICE, "ack range is out of V(A)..V(S) range - Free TBF!\n");
|
2014-08-08 10:14:12 +00:00
|
|
|
return 1; /* indicate to free TBF */
|
|
|
|
}
|
|
|
|
|
2015-09-07 16:49:00 +00:00
|
|
|
error_rate = analyse_errors(show_rbb, ssn, &ana_res);
|
|
|
|
|
2021-01-14 12:17:01 +00:00
|
|
|
if (the_pcu->vty.cs_adj_enabled && 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_update_error_rate(ms(), this, error_rate);
|
2015-06-04 10:12:32 +00:00
|
|
|
|
2014-08-08 10:14:12 +00:00
|
|
|
m_window.update(bts, show_rbb, ssn,
|
|
|
|
&lost, &received);
|
2021-06-04 15:05:51 +00:00
|
|
|
rate_ctr_add(rate_ctr_group_get_ctr(m_ctrs, TBF_CTR_RLC_NACKED), lost);
|
2014-08-08 10:14:12 +00:00
|
|
|
|
|
|
|
/* report lost and received packets */
|
|
|
|
gprs_rlcmac_received_lost(this, received, lost);
|
|
|
|
|
2015-09-07 16:49:00 +00:00
|
|
|
/* Used to measure the leak rate */
|
|
|
|
gprs_bssgp_update_bytes_received(ana_res.received_bytes,
|
|
|
|
ana_res.received_packets + ana_res.lost_packets);
|
|
|
|
|
2014-08-08 10:14:12 +00:00
|
|
|
/* raise V(A), if possible */
|
|
|
|
m_window.raise(m_window.move_window());
|
|
|
|
|
|
|
|
/* show receive state array in debug (V(A)..V(S)-1) */
|
|
|
|
m_window.show_state(show_v_b);
|
2018-01-19 17:22:25 +00:00
|
|
|
LOGPTBFDL(this, LOGL_DEBUG,
|
|
|
|
"V(B): (V(A)=%d)\"%s\"(V(S)-1=%d) A=Acked N=Nacked U=Unacked X=Resend-Unacked I=Invalid\n",
|
|
|
|
m_window.v_a(), show_v_b, m_window.v_s_mod(-1));
|
2014-08-08 10:14:12 +00:00
|
|
|
|
2021-05-14 10:50:46 +00:00
|
|
|
if (state_is(TBF_ST_FINISHED) && m_window.window_empty()) {
|
2018-01-19 17:22:25 +00:00
|
|
|
LOGPTBFDL(this, LOGL_NOTICE,
|
|
|
|
"Received acknowledge of all blocks, but without final ack inidcation (don't worry)\n");
|
2014-08-08 10:14:12 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-07-26 10:33:39 +00:00
|
|
|
int gprs_rlcmac_dl_tbf::rcvd_dl_final_ack()
|
2015-09-01 09:20:29 +00:00
|
|
|
{
|
2021-07-26 10:33:39 +00:00
|
|
|
osmo_fsm_inst_dispatch(this->state_fsm.fi, TBF_EV_FINAL_ACK_RECVD, NULL);
|
2015-09-01 09:20:29 +00:00
|
|
|
release();
|
|
|
|
|
|
|
|
/* check for LLC PDU in the LLC Queue */
|
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 (llc_queue_size(llc_queue()) > 0)
|
2015-09-01 09:20:29 +00:00
|
|
|
/* we have more data so we will re-use this tbf */
|
|
|
|
establish_dl_tbf_on_pacch();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int gprs_rlcmac_dl_tbf::release()
|
2014-08-08 10:14:12 +00:00
|
|
|
{
|
|
|
|
uint16_t received;
|
|
|
|
|
|
|
|
/* range V(A)..V(S)-1 */
|
|
|
|
received = m_window.count_unacked();
|
|
|
|
|
|
|
|
/* report all outstanding packets as received */
|
|
|
|
gprs_rlcmac_received_lost(this, received, 0);
|
|
|
|
|
2015-08-13 16:36:56 +00:00
|
|
|
/* start T3193 */
|
2019-09-05 12:48:35 +00:00
|
|
|
T_START(this, T3193, 3193, "release (DL-TBF)", true);
|
2015-08-13 16:36:56 +00:00
|
|
|
|
2015-09-01 09:20:29 +00:00
|
|
|
/* reset rlc states */
|
|
|
|
m_tx_counter = 0;
|
|
|
|
m_wait_confirm = 0;
|
|
|
|
m_window.reset();
|
|
|
|
|
2021-07-22 17:20:50 +00:00
|
|
|
osmo_fsm_inst_dispatch(this->state_fsm.fi, TBF_EV_ASSIGN_DEL_CCCH, NULL);
|
2014-08-08 10:14:12 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-12-15 10:14:30 +00:00
|
|
|
int gprs_rlcmac_dl_tbf::rcvd_dl_ack(bool final_ack, unsigned first_bsn,
|
2016-02-05 16:07:12 +00:00
|
|
|
struct bitvec *rbb)
|
|
|
|
{
|
2016-01-15 12:38:31 +00:00
|
|
|
int rc;
|
2017-12-15 16:36:45 +00:00
|
|
|
LOGPTBFDL(this, LOGL_DEBUG, "downlink acknowledge\n");
|
2016-02-05 16:07:12 +00:00
|
|
|
|
2016-01-15 12:38:31 +00:00
|
|
|
rc = update_window(first_bsn, rbb);
|
2016-02-05 16:07:12 +00:00
|
|
|
|
2016-01-15 12:38:31 +00:00
|
|
|
if (final_ack) {
|
2018-01-19 17:22:25 +00:00
|
|
|
LOGPTBFDL(this, LOGL_DEBUG, "Final ACK received.\n");
|
2021-07-26 10:33:39 +00:00
|
|
|
rc = rcvd_dl_final_ack();
|
2021-05-14 10:50:46 +00:00
|
|
|
} else if (state_is(TBF_ST_FINISHED) && m_window.window_empty()) {
|
2018-01-19 17:22:25 +00:00
|
|
|
LOGPTBFDL(this, LOGL_NOTICE,
|
|
|
|
"Received acknowledge of all blocks, but without final ack indication (don't worry)\n");
|
2016-01-15 12:38:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
2016-02-05 16:07:12 +00:00
|
|
|
}
|
|
|
|
|
2017-12-15 10:14:30 +00:00
|
|
|
int gprs_rlcmac_dl_tbf::rcvd_dl_ack(bool final_ack, uint8_t ssn, uint8_t *rbb)
|
2014-08-08 10:14:12 +00:00
|
|
|
{
|
2017-12-15 16:36:45 +00:00
|
|
|
LOGPTBFDL(this, LOGL_DEBUG, "downlink acknowledge\n");
|
2014-08-08 10:14:12 +00:00
|
|
|
|
|
|
|
if (!final_ack)
|
|
|
|
return update_window(ssn, rbb);
|
|
|
|
|
2017-12-15 16:36:45 +00:00
|
|
|
LOGPTBFDL(this, LOGL_DEBUG, "Final ACK received.\n");
|
2021-07-26 10:33:39 +00:00
|
|
|
return rcvd_dl_final_ack();
|
2014-08-08 10:14:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool gprs_rlcmac_dl_tbf::dl_window_stalled() const
|
|
|
|
{
|
|
|
|
return m_window.window_stalled();
|
|
|
|
}
|
|
|
|
|
2015-03-20 13:41:50 +00:00
|
|
|
void gprs_rlcmac_dl_tbf::request_dl_ack()
|
|
|
|
{
|
|
|
|
m_dl_ack_requested = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool gprs_rlcmac_dl_tbf::need_control_ts() const
|
|
|
|
{
|
2021-07-22 17:20:50 +00:00
|
|
|
return state_fsm.state_flags & (1 << GPRS_RLCMAC_FLAG_TO_DL_ACK) ||
|
2015-03-20 13:41:50 +00:00
|
|
|
m_tx_counter >= POLL_ACK_AFTER_FRAMES ||
|
|
|
|
m_dl_ack_requested;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool gprs_rlcmac_dl_tbf::have_data() const
|
|
|
|
{
|
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
|
|
|
return llc_chunk_size(&m_llc) > 0 ||
|
|
|
|
(llc_queue_size(llc_queue()) > 0);
|
2015-03-20 13:41:50 +00:00
|
|
|
}
|
2015-03-20 13:53:54 +00:00
|
|
|
|
2017-05-16 14:10:45 +00:00
|
|
|
static inline int frames_since_last(int32_t last, unsigned fn)
|
2015-03-20 13:53:54 +00:00
|
|
|
{
|
2017-05-16 14:10:45 +00:00
|
|
|
unsigned wrapped = (fn + GSM_MAX_FN - last) % GSM_MAX_FN;
|
|
|
|
|
|
|
|
if (last < 0)
|
2015-03-20 13:53:54 +00:00
|
|
|
return -1;
|
|
|
|
|
2017-05-16 14:10:45 +00:00
|
|
|
if (wrapped < GSM_MAX_FN/2)
|
2015-03-20 13:53:54 +00:00
|
|
|
return wrapped;
|
2017-05-16 14:10:45 +00:00
|
|
|
|
|
|
|
return wrapped - GSM_MAX_FN;
|
2015-03-20 13:53:54 +00:00
|
|
|
}
|
|
|
|
|
2017-05-16 14:10:45 +00:00
|
|
|
int gprs_rlcmac_dl_tbf::frames_since_last_poll(unsigned fn) const
|
2015-03-19 10:22:38 +00:00
|
|
|
{
|
2017-05-16 14:10:45 +00:00
|
|
|
return frames_since_last(m_last_dl_poll_fn, fn);
|
|
|
|
}
|
2015-03-19 10:22:38 +00:00
|
|
|
|
2017-05-16 14:10:45 +00:00
|
|
|
int gprs_rlcmac_dl_tbf::frames_since_last_drain(unsigned fn) const
|
|
|
|
{
|
|
|
|
return frames_since_last(m_last_dl_drained_fn, fn);
|
2015-03-19 10:22:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool gprs_rlcmac_dl_tbf::keep_open(unsigned fn) const
|
|
|
|
{
|
|
|
|
int keep_time_frames;
|
2019-09-09 11:41:00 +00:00
|
|
|
unsigned long dl_tbf_idle_msec;
|
2021-03-02 11:53:58 +00:00
|
|
|
int since_last_drain;
|
|
|
|
bool keep;
|
2015-03-19 10:22:38 +00:00
|
|
|
|
2021-01-14 11:01:42 +00:00
|
|
|
dl_tbf_idle_msec = osmo_tdef_get(the_pcu->T_defs, -2031, OSMO_TDEF_MS, -1);
|
2019-09-09 11:41:00 +00:00
|
|
|
if (dl_tbf_idle_msec == 0)
|
2015-03-19 10:22:38 +00:00
|
|
|
return false;
|
|
|
|
|
2019-09-09 11:41:00 +00:00
|
|
|
keep_time_frames = msecs_to_frames(dl_tbf_idle_msec);
|
2021-03-02 11:53:58 +00:00
|
|
|
since_last_drain = frames_since_last_drain(fn);
|
|
|
|
keep = since_last_drain <= keep_time_frames;
|
|
|
|
|
|
|
|
if (since_last_drain >= 0)
|
|
|
|
LOGPTBFDL(this, LOGL_DEBUG, "Keep idle TBF open: %d/%d -> %s\n",
|
|
|
|
since_last_drain, keep_time_frames, keep ? "yes" : "no");
|
|
|
|
return keep;
|
2015-03-19 10:22:38 +00:00
|
|
|
}
|
2016-08-22 11:51:10 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* This function returns the pointer to data which needs
|
|
|
|
* to be copied. Also updates the status of the block related to
|
|
|
|
* Split block handling in the RLC/MAC block.
|
|
|
|
*/
|
|
|
|
enum egprs_rlc_dl_reseg_bsn_state
|
|
|
|
gprs_rlcmac_dl_tbf::egprs_dl_get_data(int bsn, uint8_t **block_data)
|
|
|
|
{
|
|
|
|
gprs_rlc_data *rlc_data = m_rlc.block(bsn);
|
|
|
|
egprs_rlc_dl_reseg_bsn_state *block_status_dl =
|
|
|
|
&rlc_data->spb_status.block_status_dl;
|
|
|
|
|
2020-05-18 09:35:35 +00:00
|
|
|
enum CodingScheme cs_init = rlc_data->cs_init;
|
|
|
|
enum CodingScheme cs_current_trans = rlc_data->cs_current_trans;
|
2019-03-13 17:44:20 +00:00
|
|
|
|
2020-05-18 09:35:35 +00:00
|
|
|
enum HeaderType ht_cs_init = mcs_header_type(rlc_data->cs_init);
|
|
|
|
enum HeaderType ht_cs_current_trans = mcs_header_type(rlc_data->cs_current_trans);
|
2019-03-13 17:44:20 +00:00
|
|
|
|
2016-08-22 11:51:10 +00:00
|
|
|
*block_data = &rlc_data->block[0];
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Table 10.3a.0.1 of 44.060
|
|
|
|
* MCS6,9: second segment starts at 74/2 = 37
|
|
|
|
* MCS5,7: second segment starts at 56/2 = 28
|
|
|
|
* MCS8: second segment starts at 31
|
|
|
|
* MCS4: second segment starts at 44/2 = 22
|
|
|
|
*/
|
2019-03-13 17:44:20 +00:00
|
|
|
if (ht_cs_current_trans == HEADER_EGPRS_DATA_TYPE_3) {
|
2016-08-22 11:51:10 +00:00
|
|
|
if (*block_status_dl == EGPRS_RESEG_FIRST_SEG_SENT) {
|
2019-03-13 17:44:20 +00:00
|
|
|
switch (cs_init) {
|
2019-03-06 16:04:59 +00:00
|
|
|
case MCS6 :
|
|
|
|
case MCS9 :
|
2016-08-22 11:51:10 +00:00
|
|
|
*block_data = &rlc_data->block[37];
|
|
|
|
break;
|
2019-03-06 16:04:59 +00:00
|
|
|
case MCS7 :
|
|
|
|
case MCS5 :
|
2016-08-22 11:51:10 +00:00
|
|
|
*block_data = &rlc_data->block[28];
|
|
|
|
break;
|
2019-03-06 16:04:59 +00:00
|
|
|
case MCS8 :
|
2016-08-22 11:51:10 +00:00
|
|
|
*block_data = &rlc_data->block[31];
|
|
|
|
break;
|
2019-03-06 16:04:59 +00:00
|
|
|
case MCS4 :
|
2016-08-22 11:51:10 +00:00
|
|
|
*block_data = &rlc_data->block[22];
|
|
|
|
break;
|
|
|
|
default:
|
2018-01-19 17:22:25 +00:00
|
|
|
LOGPTBFDL(this, LOGL_ERROR,
|
|
|
|
"FIXME: Software error: hit invalid condition. "
|
|
|
|
"headerType(%d) blockstatus(%d) cs(%s) PLEASE FIX!\n",
|
2019-03-13 17:44:20 +00:00
|
|
|
ht_cs_current_trans,
|
2019-03-05 13:59:03 +00:00
|
|
|
*block_status_dl, mcs_name(cs_init));
|
2016-08-22 11:51:10 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
return EGPRS_RESEG_SECOND_SEG_SENT;
|
2019-03-13 17:44:20 +00:00
|
|
|
} else if ((ht_cs_init == HEADER_EGPRS_DATA_TYPE_1) ||
|
|
|
|
(ht_cs_init == HEADER_EGPRS_DATA_TYPE_2)) {
|
2016-08-22 11:51:10 +00:00
|
|
|
return EGPRS_RESEG_FIRST_SEG_SENT;
|
2019-03-13 17:44:20 +00:00
|
|
|
} else if ((cs_init == MCS4) &&
|
|
|
|
(cs_current_trans == MCS1)) {
|
2016-08-22 11:51:10 +00:00
|
|
|
return EGPRS_RESEG_FIRST_SEG_SENT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return EGPRS_RESEG_DL_DEFAULT;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This function returns the status of split block
|
|
|
|
* for RLC/MAC block.
|
|
|
|
*/
|
|
|
|
unsigned int gprs_rlcmac_dl_tbf::get_egprs_dl_spb_status(const int bsn)
|
|
|
|
{
|
|
|
|
const gprs_rlc_data *rlc_data = m_rlc.block(bsn);
|
|
|
|
|
|
|
|
return rlc_data->spb_status.block_status_dl;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This function returns the spb value to be sent OTA
|
|
|
|
* for RLC/MAC block.
|
|
|
|
*/
|
|
|
|
enum egprs_rlcmac_dl_spb gprs_rlcmac_dl_tbf::get_egprs_dl_spb(const int bsn)
|
|
|
|
{
|
|
|
|
struct gprs_rlc_data *rlc_data = m_rlc.block(bsn);
|
2019-03-13 17:44:20 +00:00
|
|
|
egprs_rlc_dl_reseg_bsn_state block_status_dl = rlc_data->spb_status.block_status_dl;
|
2016-08-22 11:51:10 +00:00
|
|
|
|
2020-05-18 09:35:35 +00:00
|
|
|
enum CodingScheme cs_init = rlc_data->cs_init;
|
|
|
|
enum CodingScheme cs_current_trans = rlc_data->cs_current_trans;
|
2019-03-13 17:44:20 +00:00
|
|
|
|
2020-05-18 09:35:35 +00:00
|
|
|
enum HeaderType ht_cs_init = mcs_header_type(rlc_data->cs_init);
|
|
|
|
enum HeaderType ht_cs_current_trans = mcs_header_type(rlc_data->cs_current_trans);
|
2016-08-22 11:51:10 +00:00
|
|
|
|
|
|
|
/* Table 10.4.8b.1 of 44.060 */
|
2019-03-13 17:44:20 +00:00
|
|
|
if (ht_cs_current_trans == HEADER_EGPRS_DATA_TYPE_3) {
|
|
|
|
/*
|
|
|
|
* if we are sending the second segment the spb should be 3
|
|
|
|
* otherwise it should be 2
|
|
|
|
*/
|
2016-08-22 11:51:10 +00:00
|
|
|
if (block_status_dl == EGPRS_RESEG_FIRST_SEG_SENT) {
|
2016-12-16 07:27:18 +00:00
|
|
|
/* statistics */
|
2021-01-14 15:48:38 +00:00
|
|
|
bts_do_rate_ctr_inc(bts, CTR_SPB_DL_SECOND_SEGMENT);
|
2016-08-22 11:51:10 +00:00
|
|
|
return EGPRS_RLCMAC_DL_SEC_SEG;
|
2019-03-13 17:44:20 +00:00
|
|
|
} else if ((ht_cs_init == HEADER_EGPRS_DATA_TYPE_1) ||
|
|
|
|
(ht_cs_init == HEADER_EGPRS_DATA_TYPE_2)) {
|
2021-01-14 15:48:38 +00:00
|
|
|
bts_do_rate_ctr_inc(bts, CTR_SPB_DL_FIRST_SEGMENT);
|
2016-08-22 11:51:10 +00:00
|
|
|
return EGPRS_RLCMAC_DL_FIRST_SEG;
|
2019-03-13 17:44:20 +00:00
|
|
|
} else if ((cs_init == MCS4) &&
|
|
|
|
(cs_current_trans == MCS1)) {
|
2021-01-14 15:48:38 +00:00
|
|
|
bts_do_rate_ctr_inc(bts, CTR_SPB_DL_FIRST_SEGMENT);
|
2016-08-22 11:51:10 +00:00
|
|
|
return EGPRS_RLCMAC_DL_FIRST_SEG;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Non SPB cases 0 is reurned */
|
|
|
|
return EGPRS_RLCMAC_DL_NO_RETX;
|
|
|
|
}
|
2016-09-15 12:21:16 +00:00
|
|
|
|
2017-12-14 14:02:33 +00:00
|
|
|
void gprs_rlcmac_dl_tbf::set_window_size()
|
2016-09-15 12:21:16 +00:00
|
|
|
{
|
2021-01-14 15:48:38 +00:00
|
|
|
const struct gprs_rlcmac_bts *b = bts;
|
2018-12-11 15:47:30 +00:00
|
|
|
uint16_t ws = egprs_window_size(b, dl_slots());
|
|
|
|
|
2018-01-19 17:22:25 +00:00
|
|
|
LOGPTBFDL(this, LOGL_INFO, "setting EGPRS DL window size to %u, base(%u) slots(%u) ws_pdch(%u)\n",
|
2021-01-14 13:30:03 +00:00
|
|
|
ws, bts->pcu->vty.ws_base, pcu_bitcount(dl_slots()), bts->pcu->vty.ws_pdch);
|
2016-09-15 12:21:16 +00:00
|
|
|
m_window.set_ws(ws);
|
|
|
|
}
|
2016-11-10 12:46:30 +00:00
|
|
|
|
2019-03-13 17:40:19 +00:00
|
|
|
void gprs_rlcmac_dl_tbf::update_coding_scheme_counter_dl(enum CodingScheme cs)
|
2016-11-10 12:46:30 +00:00
|
|
|
{
|
2019-03-13 17:40:19 +00:00
|
|
|
switch (cs) {
|
|
|
|
case CS1:
|
2021-01-14 15:48:38 +00:00
|
|
|
bts_do_rate_ctr_inc(bts, CTR_GPRS_DL_CS1);
|
2021-06-04 15:05:51 +00:00
|
|
|
rate_ctr_inc(rate_ctr_group_get_ctr(m_dl_gprs_ctrs, TBF_CTR_GPRS_DL_CS1));
|
2019-03-13 17:40:19 +00:00
|
|
|
break;
|
|
|
|
case CS2:
|
2021-01-14 15:48:38 +00:00
|
|
|
bts_do_rate_ctr_inc(bts, CTR_GPRS_DL_CS2);
|
2021-06-04 15:05:51 +00:00
|
|
|
rate_ctr_inc(rate_ctr_group_get_ctr(m_dl_gprs_ctrs, TBF_CTR_GPRS_DL_CS2));
|
2019-03-13 17:40:19 +00:00
|
|
|
break;
|
|
|
|
case CS3:
|
2021-01-14 15:48:38 +00:00
|
|
|
bts_do_rate_ctr_inc(bts, CTR_GPRS_DL_CS3);
|
2021-06-04 15:05:51 +00:00
|
|
|
rate_ctr_inc(rate_ctr_group_get_ctr(m_dl_gprs_ctrs, TBF_CTR_GPRS_DL_CS3));
|
2019-03-13 17:40:19 +00:00
|
|
|
break;
|
|
|
|
case CS4:
|
2021-01-14 15:48:38 +00:00
|
|
|
bts_do_rate_ctr_inc(bts, CTR_GPRS_DL_CS4);
|
2021-06-04 15:05:51 +00:00
|
|
|
rate_ctr_inc(rate_ctr_group_get_ctr(m_dl_gprs_ctrs, TBF_CTR_GPRS_DL_CS4));
|
2019-03-13 17:40:19 +00:00
|
|
|
break;
|
|
|
|
case MCS1:
|
2021-01-14 15:48:38 +00:00
|
|
|
bts_do_rate_ctr_inc(bts, CTR_EGPRS_DL_MCS1);
|
2021-06-04 15:05:51 +00:00
|
|
|
rate_ctr_inc(rate_ctr_group_get_ctr(m_dl_egprs_ctrs, TBF_CTR_EGPRS_DL_MCS1));
|
2019-03-13 17:40:19 +00:00
|
|
|
break;
|
|
|
|
case MCS2:
|
2021-01-14 15:48:38 +00:00
|
|
|
bts_do_rate_ctr_inc(bts, CTR_EGPRS_DL_MCS2);
|
2021-06-04 15:05:51 +00:00
|
|
|
rate_ctr_inc(rate_ctr_group_get_ctr(m_dl_egprs_ctrs, TBF_CTR_EGPRS_DL_MCS2));
|
2019-03-13 17:40:19 +00:00
|
|
|
break;
|
|
|
|
case MCS3:
|
2021-01-14 15:48:38 +00:00
|
|
|
bts_do_rate_ctr_inc(bts, CTR_EGPRS_DL_MCS3);
|
2021-06-04 15:05:51 +00:00
|
|
|
rate_ctr_inc(rate_ctr_group_get_ctr(m_dl_egprs_ctrs, TBF_CTR_EGPRS_DL_MCS3));
|
2019-03-13 17:40:19 +00:00
|
|
|
break;
|
|
|
|
case MCS4:
|
2021-01-14 15:48:38 +00:00
|
|
|
bts_do_rate_ctr_inc(bts, CTR_EGPRS_DL_MCS4);
|
2021-06-04 15:05:51 +00:00
|
|
|
rate_ctr_inc(rate_ctr_group_get_ctr(m_dl_egprs_ctrs, TBF_CTR_EGPRS_DL_MCS4));
|
2019-03-13 17:40:19 +00:00
|
|
|
break;
|
|
|
|
case MCS5:
|
2021-01-14 15:48:38 +00:00
|
|
|
bts_do_rate_ctr_inc(bts, CTR_EGPRS_DL_MCS5);
|
2021-06-04 15:05:51 +00:00
|
|
|
rate_ctr_inc(rate_ctr_group_get_ctr(m_dl_egprs_ctrs, TBF_CTR_EGPRS_DL_MCS5));
|
2019-03-13 17:40:19 +00:00
|
|
|
break;
|
|
|
|
case MCS6:
|
2021-01-14 15:48:38 +00:00
|
|
|
bts_do_rate_ctr_inc(bts, CTR_EGPRS_DL_MCS6);
|
2021-06-04 15:05:51 +00:00
|
|
|
rate_ctr_inc(rate_ctr_group_get_ctr(m_dl_egprs_ctrs, TBF_CTR_EGPRS_DL_MCS6));
|
2019-03-13 17:40:19 +00:00
|
|
|
break;
|
|
|
|
case MCS7:
|
2021-01-14 15:48:38 +00:00
|
|
|
bts_do_rate_ctr_inc(bts, CTR_EGPRS_DL_MCS7);
|
2021-06-04 15:05:51 +00:00
|
|
|
rate_ctr_inc(rate_ctr_group_get_ctr(m_dl_egprs_ctrs, TBF_CTR_EGPRS_DL_MCS7));
|
2019-03-13 17:40:19 +00:00
|
|
|
break;
|
|
|
|
case MCS8:
|
2021-01-14 15:48:38 +00:00
|
|
|
bts_do_rate_ctr_inc(bts, CTR_EGPRS_DL_MCS8);
|
2021-06-04 15:05:51 +00:00
|
|
|
rate_ctr_inc(rate_ctr_group_get_ctr(m_dl_egprs_ctrs, TBF_CTR_EGPRS_DL_MCS8));
|
2019-03-13 17:40:19 +00:00
|
|
|
break;
|
|
|
|
case MCS9:
|
2021-01-14 15:48:38 +00:00
|
|
|
bts_do_rate_ctr_inc(bts, CTR_EGPRS_DL_MCS9);
|
2021-06-04 15:05:51 +00:00
|
|
|
rate_ctr_inc(rate_ctr_group_get_ctr(m_dl_egprs_ctrs, TBF_CTR_EGPRS_DL_MCS9));
|
2019-03-13 17:40:19 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
LOGPTBFDL(this, LOGL_ERROR, "attempting to update rate counters for unsupported (M)CS %s\n",
|
|
|
|
mcs_name(cs));
|
2016-11-10 12:46:30 +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
|
|
|
|
|
|
|
struct gprs_rlcmac_dl_tbf *as_dl_tbf(struct gprs_rlcmac_tbf *tbf)
|
|
|
|
{
|
|
|
|
if (tbf && tbf->direction == GPRS_RLCMAC_DL_TBF)
|
|
|
|
return static_cast<gprs_rlcmac_dl_tbf *>(tbf);
|
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
}
|