Add RLC suspend and resume

This commit is contained in:
Ismael Gomez 2019-07-09 22:54:04 +02:00
parent db196cc052
commit 8c41625599
13 changed files with 366 additions and 98 deletions

View File

@ -275,15 +275,16 @@ public:
class rlc_interface_rrc
{
public:
virtual void reset() = 0;
virtual void reestablish() = 0;
virtual void reestablish(uint32_t lcid) = 0;
virtual void reset() = 0;
virtual void reestablish() = 0;
virtual void reestablish(uint32_t lcid) = 0;
virtual void add_bearer(uint32_t lcid, srslte::rlc_config_t cnfg) = 0;
virtual void add_bearer_mrb(uint32_t lcid) = 0;
virtual void del_bearer(uint32_t lcid) = 0;
virtual void add_bearer_mrb(uint32_t lcid) = 0;
virtual void del_bearer(uint32_t lcid) = 0;
virtual void suspend_bearer(uint32_t lcid) = 0;
virtual void resume_bearer(uint32_t lcid) = 0;
virtual void change_lcid(uint32_t old_lcid, uint32_t new_lcid) = 0;
virtual bool has_bearer(uint32_t lcid) = 0;
virtual void change_lcid(uint32_t old_lcid, uint32_t new_lcid) = 0;
virtual bool has_bearer(uint32_t lcid) = 0;
virtual bool has_data(const uint32_t lcid) = 0;
virtual void write_sdu(uint32_t lcid, srslte::unique_byte_buffer_t sdu, bool blocking = true) = 0;
};

View File

@ -80,6 +80,7 @@ public:
void add_bearer_mrb(uint32_t lcid);
void del_bearer(uint32_t lcid);
void del_bearer_mrb(uint32_t lcid);
void suspend_bearer(uint32_t lcid);
void resume_bearer(uint32_t lcid);
void change_lcid(uint32_t old_lcid, uint32_t new_lcid);
bool has_bearer(uint32_t lcid);

View File

@ -70,7 +70,6 @@ public:
srsue::rrc_interface_rlc* rrc_,
srslte::mac_interface_timers* mac_timers_);
~rlc_am();
bool resume();
bool configure(rlc_config_t cfg_);
void reestablish();
void stop();
@ -283,7 +282,6 @@ private:
mac_interface_timers* mac_timers = nullptr;
uint32_t lcid = 0;
rlc_config_t cfg = {};
bool has_configuration = false;
std::string rb_name;
static const int poll_periodicity = 8; // After how many data PDUs a status PDU shall be requested

View File

@ -22,6 +22,8 @@
#ifndef SRSLTE_RLC_COMMON_H
#define SRSLTE_RLC_COMMON_H
#include "srslte/common/block_queue.h"
namespace srslte {
/****************************************************************************
@ -141,11 +143,43 @@ public:
const static int RLC_BUFFER_NOF_PDU = 128;
virtual ~rlc_common() {}
virtual bool configure(rlc_config_t cnfg) = 0;
virtual bool resume() = 0;
virtual void stop() = 0;
virtual void reestablish() = 0;
virtual void empty_queue() = 0;
virtual bool configure(rlc_config_t cnfg) = 0;
virtual void stop() = 0;
virtual void reestablish() = 0;
virtual void empty_queue() = 0;
bool suspend()
{
if (is_suspended) {
return false;
}
is_suspended = true;
return true;
}
// Pops all PDUs from queue and calls write_pdu() method for the bearer type
bool resume()
{
if (!is_suspended) {
return false;
}
pdu_t p;
// Do not block
while (rx_pdu_resume_queue.try_pop(&p)) {
write_pdu(p.payload, p.nof_bytes);
}
is_suspended = false;
return true;
}
void write_pdu_s(uint8_t* payload, uint32_t nof_bytes)
{
if (is_suspended) {
queue_pdu(payload, nof_bytes);
} else {
write_pdu(payload, nof_bytes);
}
}
virtual rlc_mode_t get_mode() = 0;
virtual uint32_t get_bearer() = 0;
@ -162,6 +196,27 @@ public:
virtual uint32_t get_buffer_state() = 0;
virtual int read_pdu(uint8_t *payload, uint32_t nof_bytes) = 0;
virtual void write_pdu(uint8_t *payload, uint32_t nof_bytes) = 0;
private:
bool is_suspended = false;
// Enqueues the PDU in the resume queue
void queue_pdu(uint8_t* payload, uint32_t nof_bytes)
{
pdu_t p = {payload, nof_bytes};
// Do not block ever
if (!rx_pdu_resume_queue.try_push(p)) {
fprintf(stderr, "Error dropping PDUs while bearer suspended. Queue should be unbounded\n");
return;
}
}
typedef struct {
uint8_t* payload;
uint32_t nof_bytes;
} pdu_t;
block_queue<pdu_t> rx_pdu_resume_queue;
};
} // namespace srslte

View File

@ -42,7 +42,6 @@ public:
uint32_t queue_len = 16);
~rlc_tm();
bool configure(rlc_config_t cnfg);
bool resume();
void stop();
void reestablish();
void empty_queue();

View File

@ -50,7 +50,6 @@ public:
mac_interface_timers* mac_timers_);
~rlc_um();
bool configure(rlc_config_t cnfg);
bool resume();
void reestablish();
void stop();
void empty_queue();
@ -204,7 +203,6 @@ private:
srslte::log* log = nullptr;
uint32_t lcid = 0;
rlc_config_t cfg = {};
bool has_configuration = false;
std::string rb_name;
byte_buffer_pool* pool = nullptr;

View File

@ -143,6 +143,7 @@ void rlc::reestablish(uint32_t lcid)
{
pthread_rwlock_rdlock(&rwlock);
if (valid_lcid(lcid)) {
rlc_log->info("Reestablishing LCID %d\n", lcid);
rlc_array.at(lcid)->reestablish();
} else {
rlc_log->warning("RLC LCID %d doesn't exist. Deallocating SDU\n", lcid);
@ -309,7 +310,7 @@ void rlc::write_pdu(uint32_t lcid, uint8_t *payload, uint32_t nof_bytes)
{
pthread_rwlock_rdlock(&rwlock);
if (valid_lcid(lcid)) {
rlc_array.at(lcid)->write_pdu(payload, nof_bytes);
rlc_array.at(lcid)->write_pdu_s(payload, nof_bytes);
} else {
rlc_log->warning("LCID %d doesn't exist. Dropping PDU.\n", lcid);
}
@ -381,8 +382,7 @@ void rlc::add_bearer(uint32_t lcid, rlc_config_t cnfg)
rlc_common *rlc_entity = NULL;
if (not valid_lcid(lcid)) {
switch(cnfg.rlc_mode)
{
switch (cnfg.rlc_mode) {
case rlc_mode_t::tm:
rlc_entity = new rlc_tm(rlc_log, lcid, pdcp, rrc, mac_timers);
break;
@ -397,24 +397,24 @@ void rlc::add_bearer(uint32_t lcid, rlc_config_t cnfg)
goto unlock_and_exit;
}
// configure and add to array
if (cnfg.rlc_mode != rlc_mode_t::tm) {
if (not rlc_entity->configure(cnfg)) {
rlc_log->error("Error configuring RLC entity\n.");
goto delete_and_exit;
}
}
if (not rlc_array.insert(rlc_map_pair_t(lcid, rlc_entity)).second) {
rlc_log->error("Error inserting RLC entity in to array\n.");
goto delete_and_exit;
}
rlc_log->info("Added radio bearer %s in %s\n", rrc->get_rb_name(lcid).c_str(), to_string(cnfg.rlc_mode).c_str());
goto unlock_and_exit;
} else {
rlc_log->warning("Bearer %s already created.\n", rrc->get_rb_name(lcid).c_str());
rlc_entity = NULL;
}
// configure and add to array
if (cnfg.rlc_mode != rlc_mode_t::tm) {
if (not rlc_array.at(lcid)->configure(cnfg)) {
rlc_log->error("Error configuring RLC entity\n.");
goto delete_and_exit;
}
}
rlc_log->info("Configured radio bearer %s in %s\n", rrc->get_rb_name(lcid).c_str(), to_string(cnfg.rlc_mode).c_str());
delete_and_exit:
if (rlc_entity) {
delete(rlc_entity);
@ -521,15 +521,33 @@ exit:
pthread_rwlock_unlock(&rwlock);
}
void rlc::resume_bearer(uint32_t lcid)
void rlc::suspend_bearer(uint32_t lcid)
{
pthread_rwlock_wrlock(&rwlock);
if (valid_lcid(lcid)) {
if (rlc_array.at(lcid)->suspend()) {
rlc_log->info("Suspended radio bearer %s\n", rrc->get_rb_name(lcid).c_str());
} else {
rlc_log->error("Error suspending RLC entity: bearer already suspended\n.");
}
} else {
rlc_log->error("Suspending bearer: bearer %s not configured.\n", rrc->get_rb_name(lcid).c_str());
}
pthread_rwlock_unlock(&rwlock);
}
void rlc::resume_bearer(uint32_t lcid)
{
pthread_rwlock_wrlock(&rwlock);
rlc_log->info("Resuming radio bearer %s\n", rrc->get_rb_name(lcid).c_str());
if (valid_lcid(lcid)) {
if (rlc_array.at(lcid)->resume()) {
rlc_log->info("Resumed radio bearer %s\n", rrc->get_rb_name(lcid).c_str());
} else {
rlc_log->error("Error resuming RLC entity\n.");
rlc_log->error("Error resuming RLC entity: bearer not suspended\n.");
}
} else {
rlc_log->error("Resuming bearer: bearer %s not configured.\n", rrc->get_rb_name(lcid).c_str());

View File

@ -51,12 +51,14 @@ rlc_am::~rlc_am()
{
}
bool rlc_am::resume()
// Applies new configuration. Must be just reestablished or initiated
bool rlc_am::configure(rlc_config_t cfg_)
{
if (not has_configuration) {
log->error("Error resuming bearer: no previous configuration found\n");
return false;
}
// determine bearer name and configure Rx/Tx objects
rb_name = rrc->get_rb_name(lcid);
// store config
cfg = cfg_;
if (not rx.configure(cfg.am)) {
log->error("Error resuming bearer (RX)\n");
@ -68,34 +70,18 @@ bool rlc_am::resume()
return false;
}
log->info("%s configured: t_poll_retx=%d, poll_pdu=%d, poll_byte=%d, max_retx_thresh=%d, "
"t_reordering=%d, t_status_prohibit=%d\n",
rb_name.c_str(),
cfg.am.t_poll_retx,
cfg.am.poll_pdu,
cfg.am.poll_byte,
cfg.am.max_retx_thresh,
cfg.am.t_reordering,
cfg.am.t_status_prohibit);
return true;
}
bool rlc_am::configure(rlc_config_t cfg_)
{
// determine bearer name and configure Rx/Tx objects
rb_name = rrc->get_rb_name(lcid);
// store config
cfg = cfg_;
has_configuration = true;
if (resume()) {
log->info("%s configured: t_poll_retx=%d, poll_pdu=%d, poll_byte=%d, max_retx_thresh=%d, "
"t_reordering=%d, t_status_prohibit=%d\n",
rb_name.c_str(),
cfg.am.t_poll_retx,
cfg.am.poll_pdu,
cfg.am.poll_byte,
cfg.am.max_retx_thresh,
cfg.am.t_reordering,
cfg.am.t_status_prohibit);
return true;
} else {
return false;
}
}
void rlc_am::empty_queue()
{
// Drop all messages in TX SDU queue

View File

@ -49,11 +49,6 @@ bool rlc_tm::configure(rlc_config_t cnfg)
return true;
}
bool rlc_tm::resume()
{
return true;
}
void rlc_tm::empty_queue()
{
// Drop all messages in TX queue

View File

@ -48,12 +48,13 @@ rlc_um::~rlc_um()
stop();
}
bool rlc_um::resume()
bool rlc_um::configure(rlc_config_t cnfg_)
{
if (not has_configuration) {
log->error("Error resuming bearer: no previous configuration found\n");
return false;
}
// determine bearer name and configure Rx/Tx objects
rb_name = get_rb_name(rrc, lcid, cnfg_.um.is_mrb);
// store config
cfg = cnfg_;
if (not rx.configure(cfg, rb_name)) {
return false;
@ -63,31 +64,15 @@ bool rlc_um::resume()
return false;
}
log->info("%s configured in %s: t_reordering=%d ms, rx_sn_field_length=%u bits, tx_sn_field_length=%u bits\n",
rb_name.c_str(),
srslte::to_string(cnfg_.rlc_mode).c_str(),
cfg.um.t_reordering,
srslte::to_number(cfg.um.rx_sn_field_length),
srslte::to_number(cfg.um.tx_sn_field_length));
return true;
}
bool rlc_um::configure(rlc_config_t cnfg_)
{
// determine bearer name and configure Rx/Tx objects
rb_name = get_rb_name(rrc, lcid, cnfg_.um.is_mrb);
// store config
cfg = cnfg_;
has_configuration = true;
if (resume()) {
log->info("%s configured in %s: t_reordering=%d ms, rx_sn_field_length=%u bits, tx_sn_field_length=%u bits\n",
rb_name.c_str(),
srslte::to_string(cnfg_.rlc_mode).c_str(),
cfg.um.t_reordering,
srslte::to_number(cfg.um.rx_sn_field_length),
srslte::to_number(cfg.um.tx_sn_field_length));
return true;
} else {
return false;
}
}
bool rlc_um::rlc_um_rx::configure(rlc_config_t cnfg_, std::string rb_name_)
{
cfg = cnfg_.um;

View File

@ -47,6 +47,10 @@ add_executable(rlc_um_test rlc_um_test.cc)
target_link_libraries(rlc_um_test srslte_upper srslte_phy)
add_test(rlc_um_test rlc_um_test)
add_executable(rlc_common_test rlc_common_test.cc)
target_link_libraries(rlc_common_test srslte_upper srslte_phy)
add_test(rlc_common_test rlc_common_test)
########################################################################
# Option to run command after build (useful for remote builds)
########################################################################

View File

@ -0,0 +1,224 @@
/*
* Copyright 2013-2019 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE 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 Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include "srslte/common/log_filter.h"
#include "srslte/upper/rlc.h"
#include <iostream>
#define TESTASSERT(cond) \
{ \
if (!(cond)) { \
std::cout << "[" << __FUNCTION__ << "][Line " << __LINE__ << "]: FAIL at " << (#cond) << std::endl; \
return -1; \
} \
}
#define MAX_NBUFS 100
#define NBUFS 5
using namespace srslte;
using namespace srsue;
using namespace asn1::rrc;
class mac_dummy_timers : public srslte::mac_interface_timers
{
public:
srslte::timers::timer* timer_get(uint32_t timer_id) { return &t; }
uint32_t timer_get_unique_id() { return 0; }
void step() { t.step(); }
void timer_release_id(uint32_t timer_id) {}
private:
srslte::timers::timer t;
};
class rlc_tester : public pdcp_interface_rlc, public rrc_interface_rlc
{
public:
rlc_tester()
{
bzero(sdus, sizeof(sdus));
n_sdus = 0;
expected_sdu_len = 0;
}
// PDCP interface
void write_pdu(uint32_t lcid, unique_byte_buffer_t sdu)
{
if (lcid != 3 && sdu->N_bytes != expected_sdu_len) {
printf("Received PDU with size %d, expected %d. Exiting.\n", sdu->N_bytes, expected_sdu_len);
exit(-1);
}
sdus[n_sdus++] = std::move(sdu);
}
void write_pdu_bcch_bch(unique_byte_buffer_t sdu) {}
void write_pdu_bcch_dlsch(unique_byte_buffer_t sdu) {}
void write_pdu_pcch(unique_byte_buffer_t sdu) {}
void write_pdu_mch(uint32_t lcid, srslte::unique_byte_buffer_t sdu) { sdus[n_sdus++] = std::move(sdu); }
// RRC interface
void max_retx_attempted() {}
std::string get_rb_name(uint32_t lcid) { return std::string("TestRB"); }
void set_expected_sdu_len(uint32_t len) { expected_sdu_len = len; }
unique_byte_buffer_t sdus[MAX_NBUFS];
int n_sdus;
uint32_t expected_sdu_len;
};
int basic_test()
{
srslte::log_filter log1("RLC_1");
srslte::log_filter log2("RLC_2");
log1.set_level(srslte::LOG_LEVEL_DEBUG);
log2.set_level(srslte::LOG_LEVEL_DEBUG);
log1.set_hex_limit(-1);
log2.set_hex_limit(-1);
rlc_tester tester;
mac_dummy_timers timers;
int len = 0;
rlc rlc1(&log1);
rlc rlc2(&log2);
rlc1.init(&tester, &tester, &timers, 0);
rlc2.init(&tester, &tester, &timers, 0);
rlc_config_t cnfg = rlc_config_t::default_rlc_um_config(10);
cnfg.rlc_mode = rlc_mode_t::um;
cnfg.um.t_reordering = 5;
cnfg.um.rx_sn_field_length = rlc_umd_sn_size_t::size10bits;
cnfg.um.rx_window_size = 512;
cnfg.um.rx_mod = 1024;
cnfg.um.tx_sn_field_length = rlc_umd_sn_size_t::size10bits;
cnfg.um.tx_mod = 1024;
uint32_t lcid = 1;
rlc1.add_bearer(lcid, cnfg);
rlc2.add_bearer(lcid, cnfg);
byte_buffer_pool* pool = byte_buffer_pool::get_instance();
unique_byte_buffer_t sdu_bufs[NBUFS];
tester.set_expected_sdu_len(1);
// Push 5 SDUs into RLC1
for (int i = 0; i < NBUFS; i++) {
sdu_bufs[i] = srslte::allocate_unique_buffer(*pool, true);
*sdu_bufs[i]->msg = i; // Write the index into the buffer
sdu_bufs[i]->N_bytes = 1; // Give each buffer a size of 1 byte
rlc1.write_sdu(lcid, std::move(sdu_bufs[i]));
}
TESTASSERT(14 == rlc1.get_buffer_state(lcid));
// Reestablish
rlc1.reestablish(1);
TESTASSERT(0 == rlc1.get_buffer_state(lcid));
// Push again 5 SDUs, SN should start from 0
for (int i = 0; i < NBUFS; i++) {
sdu_bufs[i] = srslte::allocate_unique_buffer(*pool, true);
*sdu_bufs[i]->msg = i; // Write the index into the buffer
sdu_bufs[i]->N_bytes = 1; // Give each buffer a size of 1 byte
rlc1.write_sdu(lcid, std::move(sdu_bufs[i]));
}
TESTASSERT(14 == rlc1.get_buffer_state(lcid));
// Read 5 PDUs from RLC1 (1 byte each)
byte_buffer_t pdu_bufs[NBUFS];
for (int i = 0; i < NBUFS; i++) {
len = rlc1.read_pdu(lcid, pdu_bufs[i].msg, 4); // 3 bytes for header + payload
pdu_bufs[i].N_bytes = len;
}
TESTASSERT(0 == rlc1.get_buffer_state(lcid));
// Write 5 PDUs into RLC2
for (int i = 0; i < NBUFS; i++) {
rlc2.write_pdu(lcid, pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
}
// Check they have been passed to PDCP
TESTASSERT(NBUFS == tester.n_sdus);
rlc2.reestablish(lcid);
tester.n_sdus = 0;
// Push again
for (int i = 0; i < NBUFS; i++) {
rlc2.write_pdu(lcid, pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
}
// Check the are again in the buffer
TESTASSERT(NBUFS == tester.n_sdus);
for (int i = 0; i < NBUFS; i++) {
TESTASSERT(tester.sdus[i]->N_bytes == 1);
TESTASSERT(*(tester.sdus[i]->msg) == i);
}
// Resume unexisting and unsuspended bearer
rlc2.resume_bearer(lcid + 1);
rlc2.resume_bearer(lcid);
// Suspend unexisting bearer and twice
rlc2.suspend_bearer(lcid + 1);
rlc2.suspend_bearer(lcid);
rlc2.suspend_bearer(lcid);
// Reestablish and push again while in suspended
rlc2.reestablish(lcid);
tester.n_sdus = 0;
// Push again
for (int i = 0; i < NBUFS; i++) {
rlc2.write_pdu(lcid, pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
}
// Check they are not being passed to PDCP
TESTASSERT(0 == tester.n_sdus);
rlc2.resume_bearer(lcid);
// Check now they are being passed
TESTASSERT(NBUFS == tester.n_sdus);
for (int i = 0; i < NBUFS; i++) {
TESTASSERT(tester.sdus[i]->N_bytes == 1);
TESTASSERT(*(tester.sdus[i]->msg) == i);
}
return 0;
}
int main(int argc, char** argv)
{
if (basic_test()) {
return -1;
}
byte_buffer_pool::get_instance()->cleanup();
}

View File

@ -1812,8 +1812,12 @@ void rrc::proc_con_restablish_request()
mac_timers->timer_get(t311)->reset();
mac_timers->timer_get(t311)->run();
// TODO: suspend all RBs except SRB0;
// is this that RLC tx queue stops accepting SDUs?
// Suspend all RB except SRB0
for (int i = 1; i < SRSLTE_N_RADIO_BEARERS; i++) {
if (rlc->has_bearer(i)) {
rlc->suspend_bearer(i);
}
}
// reset MAC;
mac->reset();
@ -3068,7 +3072,7 @@ void rrc::handle_con_reest(rrc_conn_reest_s* setup)
// Apply the Radio Resource configuration
apply_rr_config_dedicated(&setup->crit_exts.c1().rrc_conn_reest_r8().rr_cfg_ded);
// Resume SRB1 (if already configured in rr_config, this function does nothing)
// Resume SRB1
rlc->resume_bearer(1);
// Send ConnectionSetupComplete message