mirror of https://github.com/PentHertz/srsLTE.git
Merge branch 'rlc_am_subclass' into next
This commit is contained in:
commit
07e42c1964
|
@ -75,7 +75,8 @@ option(ENABLE_HARDSIM "Enable support for SIM cards" ON)
|
||||||
|
|
||||||
option(BUILD_STATIC "Attempt to statically link external deps" OFF)
|
option(BUILD_STATIC "Attempt to statically link external deps" OFF)
|
||||||
option(RPATH "Enable RPATH" OFF)
|
option(RPATH "Enable RPATH" OFF)
|
||||||
option(ENABLE_ASAN "Enable gcc address sanitizer" OFF)
|
option(ENABLE_ASAN "Enable gcc/clang address sanitizer" OFF)
|
||||||
|
option(ENABLE_MSAN "Enable clang memory sanitizer" OFF)
|
||||||
|
|
||||||
option(USE_LTE_RATES "Use standard LTE sampling rates" OFF)
|
option(USE_LTE_RATES "Use standard LTE sampling rates" OFF)
|
||||||
|
|
||||||
|
@ -315,7 +316,6 @@ if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||||
endif(HAVE_SSE)
|
endif(HAVE_SSE)
|
||||||
endif(NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug")
|
endif(NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug")
|
||||||
|
|
||||||
|
|
||||||
if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm")
|
if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm")
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon -march=native -DIS_ARM -DHAVE_NEON")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon -march=native -DIS_ARM -DHAVE_NEON")
|
||||||
message(STATUS "have ARM")
|
message(STATUS "have ARM")
|
||||||
|
@ -332,10 +332,21 @@ if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||||
if(NOT WIN32)
|
if(NOT WIN32)
|
||||||
ADD_CXX_COMPILER_FLAG_IF_AVAILABLE(-fvisibility=hidden HAVE_VISIBILITY_HIDDEN)
|
ADD_CXX_COMPILER_FLAG_IF_AVAILABLE(-fvisibility=hidden HAVE_VISIBILITY_HIDDEN)
|
||||||
endif(NOT WIN32)
|
endif(NOT WIN32)
|
||||||
if (ENABLE_ASAN)
|
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
if (ENABLE_ASAN AND ENABLE_MSAN)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
message(FATAL_ERROR "ASAN and MSAN cannot be enabled at the same time.")
|
||||||
endif (ENABLE_ASAN)
|
endif (ENABLE_ASAN AND ENABLE_MSAN)
|
||||||
|
|
||||||
|
if (ENABLE_ASAN)
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
||||||
|
endif (ENABLE_ASAN)
|
||||||
|
|
||||||
|
if (ENABLE_MSAN AND CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=memory -fno-omit-frame-pointer -fPIE -pie")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=memory -fno-omit-frame-pointer -fPIE -pie")
|
||||||
|
endif (ENABLE_MSAN AND CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||||
|
|
||||||
endif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang")
|
endif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||||
|
|
||||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||||
|
@ -359,7 +370,7 @@ if(NOT CLANG_TIDY_BIN)
|
||||||
message(STATUS "clang-tidy not found.")
|
message(STATUS "clang-tidy not found.")
|
||||||
else()
|
else()
|
||||||
message(STATUS "clang-tidy found: ${CLANG_TIDY_BIN}")
|
message(STATUS "clang-tidy found: ${CLANG_TIDY_BIN}")
|
||||||
set(DO_CLANG_TIDY "${CLANG_TIDY_BIN}" "-checks=*,-clang-analyzer-alpha.*")
|
set(DO_CLANG_TIDY "${CLANG_TIDY_BIN}" "-checks=*,-clang-analyzer-alpha.*,-modernize-*,-cppcoreguidelines-pro-type-vararg,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-cppcoreguidelines-pro-bounds-constant-array-index")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,7 @@ public:
|
||||||
log_filter();
|
log_filter();
|
||||||
log_filter(std::string layer);
|
log_filter(std::string layer);
|
||||||
log_filter(std::string layer, logger *logger_, bool tti=false);
|
log_filter(std::string layer, logger *logger_, bool tti=false);
|
||||||
|
~log_filter();
|
||||||
|
|
||||||
void init(std::string layer, logger *logger_, bool tti=false);
|
void init(std::string layer, logger *logger_, bool tti=false);
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ public:
|
||||||
log *rlc_log_,
|
log *rlc_log_,
|
||||||
mac_interface_timers *mac_timers_,
|
mac_interface_timers *mac_timers_,
|
||||||
uint32_t lcid_,
|
uint32_t lcid_,
|
||||||
int buffer_size = -1); // -1 to use default buffer sizes
|
int buffer_size_ = -1); // -1 to use default buffer sizes
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
void get_metrics(rlc_metrics_t &m);
|
void get_metrics(rlc_metrics_t &m);
|
||||||
|
@ -88,6 +88,7 @@ public:
|
||||||
void add_bearer(uint32_t lcid, srslte_rlc_config_t cnfg);
|
void add_bearer(uint32_t lcid, srslte_rlc_config_t cnfg);
|
||||||
void add_bearer_mrb(uint32_t lcid);
|
void add_bearer_mrb(uint32_t lcid);
|
||||||
void del_bearer(uint32_t lcid);
|
void del_bearer(uint32_t lcid);
|
||||||
|
void del_bearer_mrb(uint32_t lcid);
|
||||||
void change_lcid(uint32_t old_lcid, uint32_t new_lcid);
|
void change_lcid(uint32_t old_lcid, uint32_t new_lcid);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -69,14 +69,14 @@ struct rlc_amd_retx_t{
|
||||||
class rlc_am : public rlc_common
|
class rlc_am : public rlc_common
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
rlc_am(uint32_t queue_len = 16);
|
rlc_am(uint32_t queue_len = 128);
|
||||||
~rlc_am();
|
~rlc_am();
|
||||||
void init(log *rlc_entity_log_,
|
void init(log *log_,
|
||||||
uint32_t lcid_,
|
uint32_t lcid_,
|
||||||
srsue::pdcp_interface_rlc *pdcp_,
|
srsue::pdcp_interface_rlc *pdcp_,
|
||||||
srsue::rrc_interface_rlc *rrc_,
|
srsue::rrc_interface_rlc *rrc_,
|
||||||
mac_interface_timers *mac_timers);
|
mac_interface_timers *mac_timers_);
|
||||||
bool configure(srslte_rlc_config_t cnfg);
|
bool configure(srslte_rlc_config_t cfg_);
|
||||||
void reestablish();
|
void reestablish();
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ private:
|
||||||
class rlc_am_tx : public timer_callback
|
class rlc_am_tx : public timer_callback
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
rlc_am_tx(rlc_am *parent_, uint32_t queue_len);
|
rlc_am_tx(rlc_am *parent_, uint32_t queue_len_);
|
||||||
~rlc_am_tx();
|
~rlc_am_tx();
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
|
@ -139,6 +139,7 @@ private:
|
||||||
|
|
||||||
bool retx_queue_has_sn(uint32_t sn);
|
bool retx_queue_has_sn(uint32_t sn);
|
||||||
int required_buffer_size(rlc_amd_retx_t retx);
|
int required_buffer_size(rlc_amd_retx_t retx);
|
||||||
|
void retransmit_random_pdu();
|
||||||
|
|
||||||
// Timer checks
|
// Timer checks
|
||||||
bool status_prohibited;
|
bool status_prohibited;
|
||||||
|
|
|
@ -48,7 +48,7 @@ class rlc_um
|
||||||
:public rlc_common
|
:public rlc_common
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
rlc_um(uint32_t queue_len = 32);
|
rlc_um(uint32_t queue_len = 128);
|
||||||
~rlc_um();
|
~rlc_um();
|
||||||
void init(log *rlc_entity_log_,
|
void init(log *rlc_entity_log_,
|
||||||
uint32_t lcid_,
|
uint32_t lcid_,
|
||||||
|
|
|
@ -59,6 +59,10 @@ log_filter::log_filter(std::string layer, logger *logger_, bool tti)
|
||||||
init(layer, logger_, tti);
|
init(layer, logger_, tti);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log_filter::~log_filter()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void log_filter::init(std::string layer, logger *logger_, bool tti)
|
void log_filter::init(std::string layer, logger *logger_, bool tti)
|
||||||
{
|
{
|
||||||
service_name = layer;
|
service_name = layer;
|
||||||
|
|
|
@ -22,3 +22,8 @@ file(GLOB SOURCES "*.cc")
|
||||||
add_library(srslte_upper STATIC ${SOURCES})
|
add_library(srslte_upper STATIC ${SOURCES})
|
||||||
target_link_libraries(srslte_upper srslte_common srslte_asn1)
|
target_link_libraries(srslte_upper srslte_common srslte_asn1)
|
||||||
install(TARGETS srslte_upper DESTINATION ${LIBRARY_DIR})
|
install(TARGETS srslte_upper DESTINATION ${LIBRARY_DIR})
|
||||||
|
|
||||||
|
# Run clang-tidy if available
|
||||||
|
if(CLANG_TIDY_BIN)
|
||||||
|
set_target_properties(srslte_upper PROPERTIES CXX_CLANG_TIDY "${DO_CLANG_TIDY}")
|
||||||
|
endif()
|
|
@ -42,6 +42,7 @@ rlc::rlc()
|
||||||
mac_timers = NULL;
|
mac_timers = NULL;
|
||||||
ue = NULL;
|
ue = NULL;
|
||||||
default_lcid = 0;
|
default_lcid = 0;
|
||||||
|
buffer_size = 0;
|
||||||
bzero(metrics_time, sizeof(metrics_time));
|
bzero(metrics_time, sizeof(metrics_time));
|
||||||
pthread_rwlock_init(&rwlock, NULL);
|
pthread_rwlock_init(&rwlock, NULL);
|
||||||
}
|
}
|
||||||
|
@ -123,8 +124,8 @@ void rlc::get_metrics(rlc_metrics_t &m)
|
||||||
m.ul_tput_mbps[it->first] = (it->second->get_num_tx_bytes()*8/static_cast<double>(1e6))/secs;
|
m.ul_tput_mbps[it->first] = (it->second->get_num_tx_bytes()*8/static_cast<double>(1e6))/secs;
|
||||||
rlc_log->info("LCID=%d, RX throughput: %4.6f Mbps. TX throughput: %4.6f Mbps.\n",
|
rlc_log->info("LCID=%d, RX throughput: %4.6f Mbps. TX throughput: %4.6f Mbps.\n",
|
||||||
it->first,
|
it->first,
|
||||||
(it->second->get_num_rx_bytes()*8/(double)1e6)/secs,
|
(it->second->get_num_rx_bytes()*8/static_cast<double>(1e6))/secs,
|
||||||
(it->second->get_num_tx_bytes()*8/(double)1e6)/secs);
|
(it->second->get_num_tx_bytes()*8/static_cast<double>(1e6))/secs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add multicast metrics
|
// Add multicast metrics
|
||||||
|
@ -279,7 +280,7 @@ uint32_t rlc::get_total_mch_buffer_state(uint32_t lcid)
|
||||||
uint32_t ret = 0;
|
uint32_t ret = 0;
|
||||||
|
|
||||||
pthread_rwlock_rdlock(&rwlock);
|
pthread_rwlock_rdlock(&rwlock);
|
||||||
if (valid_lcid(lcid)) {
|
if (valid_lcid_mrb(lcid)) {
|
||||||
ret = rlc_array_mrb.at(lcid)->get_total_buffer_state();
|
ret = rlc_array_mrb.at(lcid)->get_total_buffer_state();
|
||||||
}
|
}
|
||||||
pthread_rwlock_unlock(&rwlock);
|
pthread_rwlock_unlock(&rwlock);
|
||||||
|
@ -305,7 +306,7 @@ int rlc::read_pdu_mch(uint32_t lcid, uint8_t *payload, uint32_t nof_bytes)
|
||||||
uint32_t ret = 0;
|
uint32_t ret = 0;
|
||||||
|
|
||||||
pthread_rwlock_rdlock(&rwlock);
|
pthread_rwlock_rdlock(&rwlock);
|
||||||
if (valid_lcid(lcid)) {
|
if (valid_lcid_mrb(lcid)) {
|
||||||
ret = rlc_array_mrb.at(lcid)->read_pdu(payload, nof_bytes);
|
ret = rlc_array_mrb.at(lcid)->read_pdu(payload, nof_bytes);
|
||||||
}
|
}
|
||||||
pthread_rwlock_unlock(&rwlock);
|
pthread_rwlock_unlock(&rwlock);
|
||||||
|
@ -327,7 +328,7 @@ void rlc::write_pdu_bcch_bch(uint8_t *payload, uint32_t nof_bytes)
|
||||||
{
|
{
|
||||||
rlc_log->info_hex(payload, nof_bytes, "BCCH BCH message received.");
|
rlc_log->info_hex(payload, nof_bytes, "BCCH BCH message received.");
|
||||||
byte_buffer_t *buf = pool_allocate;
|
byte_buffer_t *buf = pool_allocate;
|
||||||
if (buf) {
|
if (buf != NULL) {
|
||||||
memcpy(buf->msg, payload, nof_bytes);
|
memcpy(buf->msg, payload, nof_bytes);
|
||||||
buf->N_bytes = nof_bytes;
|
buf->N_bytes = nof_bytes;
|
||||||
buf->set_timestamp();
|
buf->set_timestamp();
|
||||||
|
@ -342,7 +343,7 @@ void rlc::write_pdu_bcch_dlsch(uint8_t *payload, uint32_t nof_bytes)
|
||||||
{
|
{
|
||||||
rlc_log->info_hex(payload, nof_bytes, "BCCH TXSCH message received.");
|
rlc_log->info_hex(payload, nof_bytes, "BCCH TXSCH message received.");
|
||||||
byte_buffer_t *buf = pool_allocate;
|
byte_buffer_t *buf = pool_allocate;
|
||||||
if (buf) {
|
if (buf != NULL) {
|
||||||
memcpy(buf->msg, payload, nof_bytes);
|
memcpy(buf->msg, payload, nof_bytes);
|
||||||
buf->N_bytes = nof_bytes;
|
buf->N_bytes = nof_bytes;
|
||||||
buf->set_timestamp();
|
buf->set_timestamp();
|
||||||
|
@ -357,7 +358,7 @@ void rlc::write_pdu_pcch(uint8_t *payload, uint32_t nof_bytes)
|
||||||
{
|
{
|
||||||
rlc_log->info_hex(payload, nof_bytes, "PCCH message received.");
|
rlc_log->info_hex(payload, nof_bytes, "PCCH message received.");
|
||||||
byte_buffer_t *buf = pool_allocate;
|
byte_buffer_t *buf = pool_allocate;
|
||||||
if (buf) {
|
if (buf != NULL) {
|
||||||
memcpy(buf->msg, payload, nof_bytes);
|
memcpy(buf->msg, payload, nof_bytes);
|
||||||
buf->N_bytes = nof_bytes;
|
buf->N_bytes = nof_bytes;
|
||||||
buf->set_timestamp();
|
buf->set_timestamp();
|
||||||
|
@ -394,7 +395,7 @@ void rlc::add_bearer(uint32_t lcid)
|
||||||
add_bearer(lcid, srslte_rlc_config_t());
|
add_bearer(lcid, srslte_rlc_config_t());
|
||||||
} else {
|
} else {
|
||||||
// SRB1 and SRB2 are AM
|
// SRB1 and SRB2 are AM
|
||||||
LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg;
|
LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg = {};
|
||||||
cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM;
|
cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM;
|
||||||
cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS45;
|
cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS45;
|
||||||
cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_INFINITY;
|
cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_INFINITY;
|
||||||
|
@ -472,10 +473,10 @@ void rlc::add_bearer_mrb(uint32_t lcid)
|
||||||
|
|
||||||
if (not valid_lcid_mrb(lcid)) {
|
if (not valid_lcid_mrb(lcid)) {
|
||||||
rlc_entity = new rlc_um();
|
rlc_entity = new rlc_um();
|
||||||
if (rlc_entity) {
|
if (rlc_entity != NULL) {
|
||||||
// configure and add to array
|
// configure and add to array
|
||||||
rlc_entity->init(rlc_log, lcid, pdcp, rrc, mac_timers);
|
rlc_entity->init(rlc_log, lcid, pdcp, rrc, mac_timers);
|
||||||
if (rlc_entity->configure(srslte_rlc_config_t::mch_config()) == false) {
|
if (not rlc_entity->configure(srslte_rlc_config_t::mch_config())) {
|
||||||
rlc_log->error("Error configuring RLC entity\n.");
|
rlc_log->error("Error configuring RLC entity\n.");
|
||||||
goto delete_and_exit;
|
goto delete_and_exit;
|
||||||
}
|
}
|
||||||
|
@ -494,7 +495,7 @@ void rlc::add_bearer_mrb(uint32_t lcid)
|
||||||
}
|
}
|
||||||
|
|
||||||
delete_and_exit:
|
delete_and_exit:
|
||||||
if (rlc_entity) {
|
if (rlc_entity != NULL) {
|
||||||
delete(rlc_entity);
|
delete(rlc_entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -507,7 +508,7 @@ void rlc::del_bearer(uint32_t lcid)
|
||||||
{
|
{
|
||||||
pthread_rwlock_wrlock(&rwlock);
|
pthread_rwlock_wrlock(&rwlock);
|
||||||
|
|
||||||
if (valid_lcid_mrb(lcid)) {
|
if (valid_lcid(lcid)) {
|
||||||
rlc_map_t::iterator it = rlc_array.find(lcid);
|
rlc_map_t::iterator it = rlc_array.find(lcid);
|
||||||
it->second->stop();
|
it->second->stop();
|
||||||
delete(it->second);
|
delete(it->second);
|
||||||
|
@ -521,6 +522,24 @@ void rlc::del_bearer(uint32_t lcid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void rlc::del_bearer_mrb(uint32_t lcid)
|
||||||
|
{
|
||||||
|
pthread_rwlock_wrlock(&rwlock);
|
||||||
|
|
||||||
|
if (valid_lcid_mrb(lcid)) {
|
||||||
|
rlc_map_t::iterator it = rlc_array_mrb.find(lcid);
|
||||||
|
it->second->stop();
|
||||||
|
delete(it->second);
|
||||||
|
rlc_array_mrb.erase(it);
|
||||||
|
rlc_log->warning("Deleted RLC MRB bearer %s\n", rrc->get_rb_name(lcid).c_str());
|
||||||
|
} else {
|
||||||
|
rlc_log->error("Can't delete bearer %s. Bearer doesn't exist.\n", rrc->get_rb_name(lcid).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_rwlock_unlock(&rwlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void rlc::change_lcid(uint32_t old_lcid, uint32_t new_lcid)
|
void rlc::change_lcid(uint32_t old_lcid, uint32_t new_lcid)
|
||||||
{
|
{
|
||||||
pthread_rwlock_wrlock(&rwlock);
|
pthread_rwlock_wrlock(&rwlock);
|
||||||
|
@ -577,4 +596,4 @@ bool rlc::valid_lcid_mrb(uint32_t lcid)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace srsue
|
} // namespace srslte
|
||||||
|
|
|
@ -31,8 +31,8 @@
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#define MOD 1024
|
#define MOD 1024
|
||||||
#define RX_MOD_BASE(x) ((x-vr_r)%1024)
|
#define RX_MOD_BASE(x) (((x)-vr_r)%1024)
|
||||||
#define TX_MOD_BASE(x) ((x-vt_a)%1024)
|
#define TX_MOD_BASE(x) (((x)-vt_a)%1024)
|
||||||
#define LCID (parent->lcid)
|
#define LCID (parent->lcid)
|
||||||
#define RB_NAME (parent->rb_name.c_str())
|
#define RB_NAME (parent->rb_name.c_str())
|
||||||
|
|
||||||
|
@ -198,6 +198,7 @@ rlc_am::rlc_am_tx::rlc_am_tx(rlc_am* parent_, uint32_t queue_len_)
|
||||||
,tx_enabled(false)
|
,tx_enabled(false)
|
||||||
{
|
{
|
||||||
pthread_mutex_init(&mutex, NULL);
|
pthread_mutex_init(&mutex, NULL);
|
||||||
|
ZERO_OBJECT(tx_status);
|
||||||
}
|
}
|
||||||
|
|
||||||
rlc_am::rlc_am_tx::~rlc_am_tx()
|
rlc_am::rlc_am_tx::~rlc_am_tx()
|
||||||
|
@ -209,7 +210,7 @@ void rlc_am::rlc_am_tx::init()
|
||||||
{
|
{
|
||||||
log = parent->log;
|
log = parent->log;
|
||||||
|
|
||||||
if (parent->mac_timers) {
|
if (parent->mac_timers != NULL) {
|
||||||
poll_retx_timer_id = parent->mac_timers->timer_get_unique_id();
|
poll_retx_timer_id = parent->mac_timers->timer_get_unique_id();
|
||||||
poll_retx_timer = parent->mac_timers->timer_get(poll_retx_timer_id);
|
poll_retx_timer = parent->mac_timers->timer_get(poll_retx_timer_id);
|
||||||
|
|
||||||
|
@ -224,10 +225,19 @@ bool rlc_am::rlc_am_tx::configure(srslte_rlc_am_config_t cfg_)
|
||||||
cfg = cfg_;
|
cfg = cfg_;
|
||||||
|
|
||||||
// check timers
|
// check timers
|
||||||
if (not poll_retx_timer or not status_prohibit_timer) {
|
if (poll_retx_timer == NULL or status_prohibit_timer == NULL) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// configure timers
|
||||||
|
if (cfg.t_status_prohibit > 0) {
|
||||||
|
status_prohibit_timer->set(this, static_cast<uint32_t>(cfg.t_status_prohibit));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cfg.t_poll_retx > 0) {
|
||||||
|
poll_retx_timer->set(this, static_cast<uint32_t>(cfg.t_poll_retx));
|
||||||
|
}
|
||||||
|
|
||||||
tx_enabled = true;
|
tx_enabled = true;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -239,13 +249,15 @@ void rlc_am::rlc_am_tx::stop()
|
||||||
|
|
||||||
pthread_mutex_lock(&mutex);
|
pthread_mutex_lock(&mutex);
|
||||||
|
|
||||||
if (parent->mac_timers && poll_retx_timer) {
|
tx_enabled = false;
|
||||||
|
|
||||||
|
if (parent->mac_timers != NULL && poll_retx_timer != NULL) {
|
||||||
poll_retx_timer->stop();
|
poll_retx_timer->stop();
|
||||||
parent->mac_timers->timer_release_id(poll_retx_timer_id);
|
parent->mac_timers->timer_release_id(poll_retx_timer_id);
|
||||||
poll_retx_timer = NULL;
|
poll_retx_timer = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parent->mac_timers && status_prohibit_timer) {
|
if (parent->mac_timers != NULL && status_prohibit_timer != NULL) {
|
||||||
status_prohibit_timer->stop();
|
status_prohibit_timer->stop();
|
||||||
parent->mac_timers->timer_release_id(status_prohibit_timer_id);
|
parent->mac_timers->timer_release_id(status_prohibit_timer_id);
|
||||||
status_prohibit_timer = NULL;
|
status_prohibit_timer = NULL;
|
||||||
|
@ -283,7 +295,7 @@ void rlc_am::rlc_am_tx::empty_queue()
|
||||||
}
|
}
|
||||||
|
|
||||||
// deallocate SDU that is currently processed
|
// deallocate SDU that is currently processed
|
||||||
if(tx_sdu) {
|
if (tx_sdu != NULL) {
|
||||||
pool->deallocate(tx_sdu);
|
pool->deallocate(tx_sdu);
|
||||||
tx_sdu = NULL;
|
tx_sdu = NULL;
|
||||||
}
|
}
|
||||||
|
@ -316,7 +328,7 @@ uint32_t rlc_am::rlc_am_tx::get_buffer_state()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bytes needed for retx
|
// Bytes needed for retx
|
||||||
if (retx_queue.size() > 0) {
|
if (not retx_queue.empty()) {
|
||||||
rlc_amd_retx_t retx = retx_queue.front();
|
rlc_amd_retx_t retx = retx_queue.front();
|
||||||
log->debug("Buffer state - retx - SN: %d, Segment: %s, %d:%d\n", retx.sn, retx.is_segment ? "true" : "false", retx.so_start, retx.so_end);
|
log->debug("Buffer state - retx - SN: %d, Segment: %s, %d:%d\n", retx.sn, retx.is_segment ? "true" : "false", retx.so_start, retx.so_end);
|
||||||
if(tx_window.end() != tx_window.find(retx.sn)) {
|
if(tx_window.end() != tx_window.find(retx.sn)) {
|
||||||
|
@ -326,7 +338,7 @@ uint32_t rlc_am::rlc_am_tx::get_buffer_state()
|
||||||
retx_queue.pop_front();
|
retx_queue.pop_front();
|
||||||
goto unlock_and_return;
|
goto unlock_and_return;
|
||||||
}
|
}
|
||||||
n_bytes = (uint32_t) req_bytes;
|
n_bytes = static_cast<uint32_t>(req_bytes);
|
||||||
log->debug("Buffer state - retx: %d bytes\n", n_bytes);
|
log->debug("Buffer state - retx: %d bytes\n", n_bytes);
|
||||||
goto unlock_and_return;
|
goto unlock_and_return;
|
||||||
}
|
}
|
||||||
|
@ -336,7 +348,7 @@ uint32_t rlc_am::rlc_am_tx::get_buffer_state()
|
||||||
if (tx_window.size() < 1024) {
|
if (tx_window.size() < 1024) {
|
||||||
n_sdus = tx_sdu_queue.size();
|
n_sdus = tx_sdu_queue.size();
|
||||||
n_bytes = tx_sdu_queue.size_bytes();
|
n_bytes = tx_sdu_queue.size_bytes();
|
||||||
if (tx_sdu) {
|
if (tx_sdu != NULL) {
|
||||||
n_sdus++;
|
n_sdus++;
|
||||||
n_bytes += tx_sdu->N_bytes;
|
n_bytes += tx_sdu->N_bytes;
|
||||||
}
|
}
|
||||||
|
@ -371,7 +383,7 @@ uint32_t rlc_am::rlc_am_tx::get_total_buffer_state()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bytes needed for retx
|
// Bytes needed for retx
|
||||||
if(retx_queue.size() > 0) {
|
if(not retx_queue.empty()) {
|
||||||
rlc_amd_retx_t retx = retx_queue.front();
|
rlc_amd_retx_t retx = retx_queue.front();
|
||||||
log->debug("Buffer state - retx - SN: %d, Segment: %s, %d:%d\n", retx.sn, retx.is_segment ? "true" : "false", retx.so_start, retx.so_end);
|
log->debug("Buffer state - retx - SN: %d, Segment: %s, %d:%d\n", retx.sn, retx.is_segment ? "true" : "false", retx.so_start, retx.so_end);
|
||||||
if(tx_window.end() != tx_window.find(retx.sn)) {
|
if(tx_window.end() != tx_window.find(retx.sn)) {
|
||||||
|
@ -390,19 +402,19 @@ uint32_t rlc_am::rlc_am_tx::get_total_buffer_state()
|
||||||
if(tx_window.size() < 1024) {
|
if(tx_window.size() < 1024) {
|
||||||
n_sdus = tx_sdu_queue.size();
|
n_sdus = tx_sdu_queue.size();
|
||||||
n_bytes += tx_sdu_queue.size_bytes();
|
n_bytes += tx_sdu_queue.size_bytes();
|
||||||
if(tx_sdu)
|
if (tx_sdu != NULL) {
|
||||||
{
|
|
||||||
n_sdus++;
|
n_sdus++;
|
||||||
n_bytes += tx_sdu->N_bytes;
|
n_bytes += tx_sdu->N_bytes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Room needed for header extensions? (integer rounding)
|
// Room needed for header extensions? (integer rounding)
|
||||||
if(n_sdus > 1)
|
if (n_sdus > 1) {
|
||||||
n_bytes += ((n_sdus-1)*1.5)+0.5;
|
n_bytes += ((n_sdus-1)*1.5)+0.5;
|
||||||
|
}
|
||||||
|
|
||||||
// Room needed for fixed header?
|
// Room needed for fixed header?
|
||||||
if(n_bytes > 0) {
|
if (n_bytes > 0) {
|
||||||
n_bytes += 3;
|
n_bytes += 3;
|
||||||
log->debug("Buffer state - tx SDUs: %d bytes\n", n_bytes);
|
log->debug("Buffer state - tx SDUs: %d bytes\n", n_bytes);
|
||||||
}
|
}
|
||||||
|
@ -417,7 +429,8 @@ void rlc_am::rlc_am_tx::write_sdu(byte_buffer_t *sdu, bool blocking)
|
||||||
byte_buffer_pool::get_instance()->deallocate(sdu);
|
byte_buffer_pool::get_instance()->deallocate(sdu);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (sdu) {
|
|
||||||
|
if (sdu != NULL) {
|
||||||
if (blocking) {
|
if (blocking) {
|
||||||
// block on write to queue
|
// block on write to queue
|
||||||
tx_sdu_queue.write(sdu);
|
tx_sdu_queue.write(sdu);
|
||||||
|
@ -427,7 +440,7 @@ void rlc_am::rlc_am_tx::write_sdu(byte_buffer_t *sdu, bool blocking)
|
||||||
if (tx_sdu_queue.try_write(sdu)) {
|
if (tx_sdu_queue.try_write(sdu)) {
|
||||||
log->info_hex(sdu->msg, sdu->N_bytes, "%s Tx SDU (%d B, tx_sdu_queue_len=%d)", RB_NAME, sdu->N_bytes, tx_sdu_queue.size());
|
log->info_hex(sdu->msg, sdu->N_bytes, "%s Tx SDU (%d B, tx_sdu_queue_len=%d)", RB_NAME, sdu->N_bytes, tx_sdu_queue.size());
|
||||||
} else {
|
} else {
|
||||||
log->info_hex(sdu->msg, sdu->N_bytes, "[Dropped SDU] %s Tx SDU (%d B, tx_sdu_queue_len=%d)", RB_NAME, sdu->N_bytes, tx_sdu_queue.size());
|
log->debug_hex(sdu->msg, sdu->N_bytes, "[Dropped SDU] %s Tx SDU (%d B, tx_sdu_queue_len=%d)", RB_NAME, sdu->N_bytes, tx_sdu_queue.size());
|
||||||
pool->deallocate(sdu);
|
pool->deallocate(sdu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -439,34 +452,30 @@ void rlc_am::rlc_am_tx::write_sdu(byte_buffer_t *sdu, bool blocking)
|
||||||
int rlc_am::rlc_am_tx::read_pdu(uint8_t *payload, uint32_t nof_bytes)
|
int rlc_am::rlc_am_tx::read_pdu(uint8_t *payload, uint32_t nof_bytes)
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(&mutex);
|
pthread_mutex_lock(&mutex);
|
||||||
|
|
||||||
int pdu_size = 0;
|
int pdu_size = 0;
|
||||||
|
|
||||||
log->debug("MAC opportunity - %d bytes\n", nof_bytes);
|
log->debug("MAC opportunity - %d bytes\n", nof_bytes);
|
||||||
log->debug("tx_window size - %zu PDUs\n", tx_window.size());
|
log->debug("tx_window size - %zu PDUs\n", tx_window.size());
|
||||||
|
|
||||||
|
if (not tx_enabled) {
|
||||||
|
log->debug("RLC entity not active. Not generating PDU.\n");
|
||||||
|
goto unlock_and_exit;
|
||||||
|
}
|
||||||
|
|
||||||
// Tx STATUS if requested
|
// Tx STATUS if requested
|
||||||
if(do_status() && not status_prohibited) {
|
if(do_status() && not status_prohibited) {
|
||||||
pdu_size = build_status_pdu(payload, nof_bytes);
|
pdu_size = build_status_pdu(payload, nof_bytes);
|
||||||
goto unlock_and_exit;
|
goto unlock_and_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if tx_window is full and retx_queue empty, retransmit next PDU to be ack'ed
|
// Section 5.2.2.3 in TS 36.311, if tx_window is full and retx_queue empty, retransmit random PDU
|
||||||
if (tx_window.size() >= RLC_AM_WINDOW_SIZE && retx_queue.size() == 0) {
|
if (tx_window.size() >= RLC_AM_WINDOW_SIZE && retx_queue.empty()) {
|
||||||
if (tx_window[vt_a].buf != NULL) {
|
retransmit_random_pdu();
|
||||||
log->warning("Full Tx window, ReTx'ing first outstanding PDU\n");
|
|
||||||
rlc_amd_retx_t retx;
|
|
||||||
retx.is_segment = false;
|
|
||||||
retx.so_start = 0;
|
|
||||||
retx.so_end = tx_window[vt_a].buf->N_bytes;
|
|
||||||
retx.sn = vt_a;
|
|
||||||
retx_queue.push_back(retx);
|
|
||||||
} else {
|
|
||||||
log->error("Found invalid PDU in tx_window.\n");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RETX if required
|
// RETX if required
|
||||||
if(retx_queue.size() > 0) {
|
if (not retx_queue.empty()) {
|
||||||
pdu_size = build_retx_pdu(payload, nof_bytes);
|
pdu_size = build_retx_pdu(payload, nof_bytes);
|
||||||
if (pdu_size > 0) {
|
if (pdu_size > 0) {
|
||||||
goto unlock_and_exit;
|
goto unlock_and_exit;
|
||||||
|
@ -485,31 +494,34 @@ unlock_and_exit:
|
||||||
void rlc_am::rlc_am_tx::timer_expired(uint32_t timeout_id)
|
void rlc_am::rlc_am_tx::timer_expired(uint32_t timeout_id)
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(&mutex);
|
pthread_mutex_lock(&mutex);
|
||||||
if (poll_retx_timer && poll_retx_timer_id == timeout_id) {
|
if (poll_retx_timer != NULL && poll_retx_timer_id == timeout_id) {
|
||||||
// if both tx and retx buffer are empty, retransmit next PDU to be ack'ed (Section 5.2.2.3 in TS 36.322)
|
log->debug("Poll reTx timer expired for LCID=%d after %d ms\n", parent->lcid, poll_retx_timer->get_timeout());
|
||||||
log->debug("Poll reTx timer expired (lcid=%d)\n", parent->lcid);
|
// Section 5.2.2.3 in TS 36.311, if tx_window is full and retx_queue empty, retransmit random PDU
|
||||||
if ((tx_window.size() > 0 && retx_queue.size() == 0 && tx_sdu_queue.size() == 0)) {
|
if ((tx_window.size() >= RLC_AM_WINDOW_SIZE && retx_queue.empty() && tx_sdu_queue.size() == 0)) {
|
||||||
std::map<uint32_t, rlc_amd_tx_pdu_t>::iterator it = tx_window.find(vt_s - 1);
|
retransmit_random_pdu();
|
||||||
if (it != tx_window.end()) {
|
|
||||||
log->info("Schedule last PDU (SN=%d) for reTx.\n", vt_s - 1);
|
|
||||||
rlc_amd_retx_t retx;
|
|
||||||
retx.is_segment = false;
|
|
||||||
retx.so_start = 0;
|
|
||||||
retx.so_end = tx_window[vt_s - 1].buf->N_bytes;
|
|
||||||
retx.sn = vt_s - 1;
|
|
||||||
retx_queue.push_back(retx);
|
|
||||||
} else {
|
|
||||||
log->error("Found invalid PDU in tx_window.\n");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
if (status_prohibit_timer && status_prohibit_timer_id == timeout_id) {
|
if (status_prohibit_timer != NULL && status_prohibit_timer_id == timeout_id) {
|
||||||
status_prohibited = true;
|
status_prohibited = false;
|
||||||
status_prohibit_timer->reset();
|
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&mutex);
|
pthread_mutex_unlock(&mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void rlc_am::rlc_am_tx::retransmit_random_pdu()
|
||||||
|
{
|
||||||
|
// randomly select PDU in tx window for retransmission
|
||||||
|
std::map<uint32_t, rlc_amd_tx_pdu_t>::iterator it = tx_window.begin();
|
||||||
|
std::advance(it, rand() % tx_window.size());
|
||||||
|
|
||||||
|
log->info("Schedule SN=%d for reTx.\n", it->first);
|
||||||
|
rlc_amd_retx_t retx = {};
|
||||||
|
retx.is_segment = false;
|
||||||
|
retx.so_start = 0;
|
||||||
|
retx.so_end = it->second.buf->N_bytes;
|
||||||
|
retx.sn = it->first;
|
||||||
|
retx_queue.push_back(retx);
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t rlc_am::rlc_am_tx::get_num_tx_bytes()
|
uint32_t rlc_am::rlc_am_tx::get_num_tx_bytes()
|
||||||
{
|
{
|
||||||
return num_tx_bytes;
|
return num_tx_bytes;
|
||||||
|
@ -528,25 +540,28 @@ void rlc_am::rlc_am_tx::reset_metrics()
|
||||||
|
|
||||||
bool rlc_am::rlc_am_tx::poll_required()
|
bool rlc_am::rlc_am_tx::poll_required()
|
||||||
{
|
{
|
||||||
if (cfg.poll_pdu > 0 && pdu_without_poll > (uint32_t)cfg.poll_pdu) {
|
if (cfg.poll_pdu > 0 && pdu_without_poll > static_cast<uint32_t>(cfg.poll_pdu)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cfg.poll_byte > 0 && byte_without_poll > (uint32_t)cfg.poll_byte) {
|
if (cfg.poll_byte > 0 && byte_without_poll > static_cast<uint32_t>(cfg.poll_byte)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (poll_retx_timer) {
|
if (poll_retx_timer != NULL) {
|
||||||
if (poll_retx_timer->is_expired()) {
|
if (poll_retx_timer->is_expired()) {
|
||||||
// re-arm timer
|
// re-arm timer (will be stopped when status PDU is received)
|
||||||
poll_retx_timer->reset();
|
poll_retx_timer->reset();
|
||||||
poll_retx_timer->set(this, cfg.t_poll_retx);
|
|
||||||
poll_retx_timer->run();
|
poll_retx_timer->run();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tx_sdu_queue.size() == 0 && retx_queue.size() == 0) {
|
if (tx_window.size() >= RLC_AM_WINDOW_SIZE) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tx_sdu_queue.size() == 0 && retx_queue.empty()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -565,25 +580,28 @@ bool rlc_am::rlc_am_tx::poll_required()
|
||||||
int rlc_am::rlc_am_tx::build_status_pdu(uint8_t *payload, uint32_t nof_bytes)
|
int rlc_am::rlc_am_tx::build_status_pdu(uint8_t *payload, uint32_t nof_bytes)
|
||||||
{
|
{
|
||||||
int pdu_len = parent->rx.get_status(&tx_status);
|
int pdu_len = parent->rx.get_status(&tx_status);
|
||||||
if (pdu_len > 0 && nof_bytes >= (uint32_t)pdu_len)
|
if (pdu_len > 0 && nof_bytes >= static_cast<uint32_t>(pdu_len)) {
|
||||||
{
|
|
||||||
log->info("%s Tx status PDU - %s\n",
|
log->info("%s Tx status PDU - %s\n",
|
||||||
RB_NAME, rlc_am_to_string(&tx_status).c_str());
|
RB_NAME, rlc_am_to_string(&tx_status).c_str());
|
||||||
|
|
||||||
parent->rx.reset_status();
|
parent->rx.reset_status();
|
||||||
|
|
||||||
if(cfg.t_status_prohibit > 0 && status_prohibit_timer) {
|
if (cfg.t_status_prohibit > 0 && status_prohibit_timer != NULL) {
|
||||||
status_prohibited = false;
|
status_prohibited = true;
|
||||||
status_prohibit_timer->set(this, cfg.t_status_prohibit);
|
|
||||||
|
// re-arm timer
|
||||||
|
status_prohibit_timer->reset();
|
||||||
status_prohibit_timer->run();
|
status_prohibit_timer->run();
|
||||||
}
|
}
|
||||||
debug_state();
|
debug_state();
|
||||||
return rlc_am_write_status_pdu(&tx_status, payload);
|
pdu_len = rlc_am_write_status_pdu(&tx_status, payload);
|
||||||
}else{
|
} else{
|
||||||
log->warning("%s Cannot tx status PDU - %d bytes available, %d bytes required\n",
|
log->warning("%s Cannot tx status PDU - %d bytes available, %d bytes required\n",
|
||||||
RB_NAME, nof_bytes, pdu_len);
|
RB_NAME, nof_bytes, pdu_len);
|
||||||
return 0;
|
pdu_len = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return pdu_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
int rlc_am::rlc_am_tx::build_retx_pdu(uint8_t *payload, uint32_t nof_bytes)
|
int rlc_am::rlc_am_tx::build_retx_pdu(uint8_t *payload, uint32_t nof_bytes)
|
||||||
|
@ -614,7 +632,8 @@ int rlc_am::rlc_am_tx::build_retx_pdu(uint8_t *payload, uint32_t nof_bytes)
|
||||||
retx_queue.pop_front();
|
retx_queue.pop_front();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if(retx.is_segment || req_size > (int)nof_bytes) {
|
|
||||||
|
if (retx.is_segment || req_size > static_cast<int>(nof_bytes)) {
|
||||||
log->debug("%s build_retx_pdu - resegmentation required\n", RB_NAME);
|
log->debug("%s build_retx_pdu - resegmentation required\n", RB_NAME);
|
||||||
return build_segment(payload, nof_bytes, retx);
|
return build_segment(payload, nof_bytes, retx);
|
||||||
}
|
}
|
||||||
|
@ -633,8 +652,8 @@ int rlc_am::rlc_am_tx::build_retx_pdu(uint8_t *payload, uint32_t nof_bytes)
|
||||||
poll_sn = vt_s;
|
poll_sn = vt_s;
|
||||||
pdu_without_poll = 0;
|
pdu_without_poll = 0;
|
||||||
byte_without_poll = 0;
|
byte_without_poll = 0;
|
||||||
if (poll_retx_timer) {
|
if (poll_retx_timer != NULL) {
|
||||||
poll_retx_timer->set(this, cfg.t_poll_retx);
|
poll_retx_timer->reset();
|
||||||
poll_retx_timer->run();
|
poll_retx_timer->run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -646,6 +665,8 @@ int rlc_am::rlc_am_tx::build_retx_pdu(uint8_t *payload, uint32_t nof_bytes)
|
||||||
retx_queue.pop_front();
|
retx_queue.pop_front();
|
||||||
tx_window[retx.sn].retx_count++;
|
tx_window[retx.sn].retx_count++;
|
||||||
if (tx_window[retx.sn].retx_count >= cfg.max_retx_thresh) {
|
if (tx_window[retx.sn].retx_count >= cfg.max_retx_thresh) {
|
||||||
|
log->warning("%s Signaling max number of reTx=%d for for PDU %d\n",
|
||||||
|
RB_NAME, tx_window[retx.sn].retx_count, retx.sn);
|
||||||
parent->rrc->max_retx_attempted();
|
parent->rrc->max_retx_attempted();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -656,15 +677,14 @@ int rlc_am::rlc_am_tx::build_retx_pdu(uint8_t *payload, uint32_t nof_bytes)
|
||||||
return (ptr-payload) + tx_window[retx.sn].buf->N_bytes;
|
return (ptr-payload) + tx_window[retx.sn].buf->N_bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
int rlc_am::rlc_am_tx::build_segment(uint8_t *payload, uint32_t nof_bytes, rlc_amd_retx_t retx)
|
int rlc_am::rlc_am_tx::build_segment(uint8_t *payload, uint32_t nof_bytes, rlc_amd_retx_t retx) {
|
||||||
{
|
if (tx_window[retx.sn].buf == NULL) {
|
||||||
if (!tx_window[retx.sn].buf) {
|
|
||||||
log->error("In build_segment: retx.sn=%d has null buffer\n", retx.sn);
|
log->error("In build_segment: retx.sn=%d has null buffer\n", retx.sn);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if(!retx.is_segment){
|
if (!retx.is_segment) {
|
||||||
retx.so_start = 0;
|
retx.so_start = 0;
|
||||||
retx.so_end = tx_window[retx.sn].buf->N_bytes;
|
retx.so_end = tx_window[retx.sn].buf->N_bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct new header
|
// Construct new header
|
||||||
|
@ -676,28 +696,27 @@ int rlc_am::rlc_am_tx::build_segment(uint8_t *payload, uint32_t nof_bytes, rlc_a
|
||||||
log->info("%s pdu_without_poll: %d\n", RB_NAME, pdu_without_poll);
|
log->info("%s pdu_without_poll: %d\n", RB_NAME, pdu_without_poll);
|
||||||
log->info("%s byte_without_poll: %d\n", RB_NAME, byte_without_poll);
|
log->info("%s byte_without_poll: %d\n", RB_NAME, byte_without_poll);
|
||||||
|
|
||||||
new_header.dc = RLC_DC_FIELD_DATA_PDU;
|
new_header.dc = RLC_DC_FIELD_DATA_PDU;
|
||||||
new_header.rf = 1;
|
new_header.rf = 1;
|
||||||
new_header.fi = RLC_FI_FIELD_NOT_START_OR_END_ALIGNED;
|
new_header.fi = RLC_FI_FIELD_NOT_START_OR_END_ALIGNED;
|
||||||
new_header.sn = old_header.sn;
|
new_header.sn = old_header.sn;
|
||||||
new_header.lsf = 0;
|
new_header.lsf = 0;
|
||||||
new_header.so = retx.so_start;
|
new_header.so = retx.so_start;
|
||||||
new_header.N_li = 0;
|
new_header.N_li = 0;
|
||||||
new_header.p = 0;
|
new_header.p = 0;
|
||||||
if(poll_required())
|
if (poll_required()) {
|
||||||
{
|
|
||||||
log->debug("%s setting poll bit to request status\n", RB_NAME);
|
log->debug("%s setting poll bit to request status\n", RB_NAME);
|
||||||
new_header.p = 1;
|
new_header.p = 1;
|
||||||
poll_sn = vt_s;
|
poll_sn = vt_s;
|
||||||
pdu_without_poll = 0;
|
pdu_without_poll = 0;
|
||||||
byte_without_poll = 0;
|
byte_without_poll = 0;
|
||||||
if (poll_retx_timer) {
|
if (poll_retx_timer != NULL) {
|
||||||
poll_retx_timer->set(this, cfg.t_poll_retx);
|
poll_retx_timer->reset();
|
||||||
poll_retx_timer->run();
|
poll_retx_timer->run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t head_len = 0;
|
uint32_t head_len = 0;
|
||||||
uint32_t pdu_space = 0;
|
uint32_t pdu_space = 0;
|
||||||
|
|
||||||
head_len = rlc_am_packed_length(&new_header);
|
head_len = rlc_am_packed_length(&new_header);
|
||||||
|
@ -706,48 +725,55 @@ int rlc_am::rlc_am_tx::build_segment(uint8_t *payload, uint32_t nof_bytes, rlc_a
|
||||||
head_len += 2;
|
head_len += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(nof_bytes <= head_len)
|
if (nof_bytes <= head_len) {
|
||||||
{
|
|
||||||
log->warning("%s Cannot build a PDU segment - %d bytes available, %d bytes required for header\n",
|
log->warning("%s Cannot build a PDU segment - %d bytes available, %d bytes required for header\n",
|
||||||
RB_NAME, nof_bytes, head_len);
|
RB_NAME, nof_bytes, head_len);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pdu_space = nof_bytes-head_len;
|
pdu_space = nof_bytes - head_len;
|
||||||
if(pdu_space < (retx.so_end-retx.so_start))
|
if (pdu_space < (retx.so_end - retx.so_start)) {
|
||||||
retx.so_end = retx.so_start+pdu_space;
|
retx.so_end = retx.so_start + pdu_space;
|
||||||
|
}
|
||||||
|
|
||||||
// Need to rebuild the li table & update fi based on so_start and so_end
|
// Need to rebuild the li table & update fi based on so_start and so_end
|
||||||
if(retx.so_start == 0 && rlc_am_start_aligned(old_header.fi))
|
if (retx.so_start == 0 && rlc_am_start_aligned(old_header.fi)) {
|
||||||
new_header.fi &= RLC_FI_FIELD_NOT_END_ALIGNED; // segment is start aligned
|
new_header.fi &= RLC_FI_FIELD_NOT_END_ALIGNED; // segment is start aligned
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t lower = 0;
|
uint32_t lower = 0;
|
||||||
uint32_t upper = 0;
|
uint32_t upper = 0;
|
||||||
uint32_t li = 0;
|
uint32_t li = 0;
|
||||||
|
|
||||||
for(uint32_t i=0; i<old_header.N_li; i++) {
|
for(uint32_t i=0; i<old_header.N_li; i++) {
|
||||||
if(lower >= retx.so_end)
|
if (lower >= retx.so_end) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if(pdu_space <= 2)
|
if (pdu_space <= 2) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
upper += old_header.li[i];
|
upper += old_header.li[i];
|
||||||
|
|
||||||
head_len = rlc_am_packed_length(&new_header);
|
head_len = rlc_am_packed_length(&new_header);
|
||||||
pdu_space = nof_bytes-head_len;
|
pdu_space = nof_bytes-head_len;
|
||||||
if(pdu_space < (retx.so_end-retx.so_start))
|
if(pdu_space < (retx.so_end-retx.so_start)) {
|
||||||
retx.so_end = retx.so_start+pdu_space;
|
retx.so_end = retx.so_start + pdu_space;
|
||||||
|
}
|
||||||
|
|
||||||
if(upper > retx.so_start && lower < retx.so_end) { // Current SDU is needed
|
if(upper > retx.so_start && lower < retx.so_end) { // Current SDU is needed
|
||||||
li = upper - lower;
|
li = upper - lower;
|
||||||
if(upper > retx.so_end)
|
if (upper > retx.so_end) {
|
||||||
li -= upper - retx.so_end;
|
li -= upper - retx.so_end;
|
||||||
if(lower < retx.so_start)
|
}
|
||||||
|
if (lower < retx.so_start) {
|
||||||
li -= retx.so_start - lower;
|
li -= retx.so_start - lower;
|
||||||
if(lower > 0 && lower == retx.so_start)
|
}
|
||||||
|
if (lower > 0 && lower == retx.so_start) {
|
||||||
new_header.fi &= RLC_FI_FIELD_NOT_END_ALIGNED; // segment start is aligned with this SDU
|
new_header.fi &= RLC_FI_FIELD_NOT_END_ALIGNED; // segment start is aligned with this SDU
|
||||||
if(upper == retx.so_end) {
|
}
|
||||||
|
if (upper == retx.so_end) {
|
||||||
new_header.fi &= RLC_FI_FIELD_NOT_START_ALIGNED; // segment end is aligned with this SDU
|
new_header.fi &= RLC_FI_FIELD_NOT_START_ALIGNED; // segment end is aligned with this SDU
|
||||||
}
|
}
|
||||||
new_header.li[new_header.N_li++] = li;
|
new_header.li[new_header.N_li++] = li;
|
||||||
|
@ -767,15 +793,17 @@ int rlc_am::rlc_am_tx::build_segment(uint8_t *payload, uint32_t nof_bytes, rlc_a
|
||||||
if(tx_window[retx.sn].buf->N_bytes == retx.so_end) {
|
if(tx_window[retx.sn].buf->N_bytes == retx.so_end) {
|
||||||
retx_queue.pop_front();
|
retx_queue.pop_front();
|
||||||
new_header.lsf = 1;
|
new_header.lsf = 1;
|
||||||
if(rlc_am_end_aligned(old_header.fi))
|
if(rlc_am_end_aligned(old_header.fi)) {
|
||||||
new_header.fi &= RLC_FI_FIELD_NOT_START_ALIGNED; // segment is end aligned
|
new_header.fi &= RLC_FI_FIELD_NOT_START_ALIGNED; // segment is end aligned
|
||||||
|
}
|
||||||
} else if(retx_queue.front().so_end == retx.so_end) {
|
} else if(retx_queue.front().so_end == retx.so_end) {
|
||||||
retx_queue.pop_front();
|
retx_queue.pop_front();
|
||||||
} else {
|
} else {
|
||||||
retx_queue.front().is_segment = true;
|
retx_queue.front().is_segment = true;
|
||||||
retx_queue.front().so_start = retx.so_end;
|
retx_queue.front().so_start = retx.so_end;
|
||||||
if(new_header.N_li > 0)
|
if (new_header.N_li > 0) {
|
||||||
new_header.N_li--;
|
new_header.N_li--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write header and pdu
|
// Write header and pdu
|
||||||
|
@ -790,7 +818,7 @@ int rlc_am::rlc_am_tx::build_segment(uint8_t *payload, uint32_t nof_bytes, rlc_a
|
||||||
|
|
||||||
debug_state();
|
debug_state();
|
||||||
int pdu_len = (ptr-payload) + len;
|
int pdu_len = (ptr-payload) + len;
|
||||||
if(pdu_len > (int)nof_bytes) {
|
if (pdu_len > static_cast<int>(nof_bytes)) {
|
||||||
log->error("%s Retx PDU segment length error. Available: %d, Used: %d\n",
|
log->error("%s Retx PDU segment length error. Available: %d, Used: %d\n",
|
||||||
RB_NAME, nof_bytes, pdu_len);
|
RB_NAME, nof_bytes, pdu_len);
|
||||||
log->debug("%s Retx PDU segment length error. Header len: %ld, Payload len: %d, N_li: %d\n",
|
log->debug("%s Retx PDU segment length error. Header len: %ld, Payload len: %d, N_li: %d\n",
|
||||||
|
@ -802,20 +830,20 @@ int rlc_am::rlc_am_tx::build_segment(uint8_t *payload, uint32_t nof_bytes, rlc_a
|
||||||
|
|
||||||
int rlc_am::rlc_am_tx::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
|
int rlc_am::rlc_am_tx::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
|
||||||
{
|
{
|
||||||
if(!tx_sdu && tx_sdu_queue.size() == 0)
|
if(tx_sdu == NULL && tx_sdu_queue.size() == 0)
|
||||||
{
|
{
|
||||||
log->info("No data available to be sent\n");
|
log->info("No data available to be sent\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// do not build any more PDU if window is already full
|
// do not build any more PDU if window is already full
|
||||||
if (!tx_sdu && tx_window.size() >= RLC_AM_WINDOW_SIZE) {
|
if (tx_sdu == NULL && tx_window.size() >= RLC_AM_WINDOW_SIZE) {
|
||||||
log->info("Tx window full.\n");
|
log->info("Tx window full.\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
byte_buffer_t *pdu = pool_allocate_blocking;
|
byte_buffer_t *pdu = pool_allocate_blocking;
|
||||||
if (!pdu) {
|
if (pdu == NULL) {
|
||||||
#ifdef RLC_AM_BUFFER_DEBUG
|
#ifdef RLC_AM_BUFFER_DEBUG
|
||||||
log->console("Fatal Error: Could not allocate PDU in build_data_pdu()\n");
|
log->console("Fatal Error: Could not allocate PDU in build_data_pdu()\n");
|
||||||
log->console("tx_window size: %d PDUs\n", tx_window.size());
|
log->console("tx_window size: %d PDUs\n", tx_window.size());
|
||||||
|
@ -862,8 +890,7 @@ int rlc_am::rlc_am_tx::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
|
||||||
RB_NAME, pdu_space, head_len);
|
RB_NAME, pdu_space, head_len);
|
||||||
|
|
||||||
// Check for SDU segment
|
// Check for SDU segment
|
||||||
if(tx_sdu)
|
if (tx_sdu != NULL) {
|
||||||
{
|
|
||||||
to_move = ((pdu_space-head_len) >= tx_sdu->N_bytes) ? tx_sdu->N_bytes : pdu_space-head_len;
|
to_move = ((pdu_space-head_len) >= tx_sdu->N_bytes) ? tx_sdu->N_bytes : pdu_space-head_len;
|
||||||
memcpy(pdu_ptr, tx_sdu->msg, to_move);
|
memcpy(pdu_ptr, tx_sdu->msg, to_move);
|
||||||
last_li = to_move;
|
last_li = to_move;
|
||||||
|
@ -878,10 +905,11 @@ int rlc_am::rlc_am_tx::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
|
||||||
pool->deallocate(tx_sdu);
|
pool->deallocate(tx_sdu);
|
||||||
tx_sdu = NULL;
|
tx_sdu = NULL;
|
||||||
}
|
}
|
||||||
if(pdu_space > to_move)
|
if (pdu_space > to_move) {
|
||||||
pdu_space -= to_move;
|
pdu_space -= to_move;
|
||||||
else
|
} else {
|
||||||
pdu_space = 0;
|
pdu_space = 0;
|
||||||
|
}
|
||||||
header.fi |= RLC_FI_FIELD_NOT_START_ALIGNED; // First byte does not correspond to first byte of SDU
|
header.fi |= RLC_FI_FIELD_NOT_START_ALIGNED; // First byte does not correspond to first byte of SDU
|
||||||
|
|
||||||
log->debug("%s Building PDU - added SDU segment (len:%d) - pdu_space: %d, head_len: %d \n",
|
log->debug("%s Building PDU - added SDU segment (len:%d) - pdu_space: %d, head_len: %d \n",
|
||||||
|
@ -889,12 +917,12 @@ int rlc_am::rlc_am_tx::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pull SDUs from queue
|
// Pull SDUs from queue
|
||||||
while(pdu_space > head_len + 1 && tx_sdu_queue.size() > 0)
|
while (pdu_space > head_len + 1 && tx_sdu_queue.size() > 0) {
|
||||||
{
|
if (last_li > 0) {
|
||||||
if(last_li > 0)
|
|
||||||
header.li[header.N_li++] = last_li;
|
header.li[header.N_li++] = last_li;
|
||||||
|
}
|
||||||
head_len = rlc_am_packed_length(&header);
|
head_len = rlc_am_packed_length(&header);
|
||||||
if(head_len >= pdu_space) {
|
if (head_len >= pdu_space) {
|
||||||
header.N_li--;
|
header.N_li--;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -913,10 +941,11 @@ int rlc_am::rlc_am_tx::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
|
||||||
pool->deallocate(tx_sdu);
|
pool->deallocate(tx_sdu);
|
||||||
tx_sdu = NULL;
|
tx_sdu = NULL;
|
||||||
}
|
}
|
||||||
if(pdu_space > to_move)
|
if(pdu_space > to_move) {
|
||||||
pdu_space -= to_move;
|
pdu_space -= to_move;
|
||||||
else
|
} else {
|
||||||
pdu_space = 0;
|
pdu_space = 0;
|
||||||
|
}
|
||||||
|
|
||||||
log->debug("%s Building PDU - added SDU segment (len:%d) - pdu_space: %d, head_len: %d \n",
|
log->debug("%s Building PDU - added SDU segment (len:%d) - pdu_space: %d, head_len: %d \n",
|
||||||
RB_NAME, to_move, pdu_space, head_len);
|
RB_NAME, to_move, pdu_space, head_len);
|
||||||
|
@ -928,8 +957,9 @@ int rlc_am::rlc_am_tx::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(tx_sdu)
|
if (tx_sdu != NULL) {
|
||||||
header.fi |= RLC_FI_FIELD_NOT_END_ALIGNED; // Last byte does not correspond to last byte of SDU
|
header.fi |= RLC_FI_FIELD_NOT_END_ALIGNED; // Last byte does not correspond to last byte of SDU
|
||||||
|
}
|
||||||
|
|
||||||
// Set Poll bit
|
// Set Poll bit
|
||||||
pdu_without_poll++;
|
pdu_without_poll++;
|
||||||
|
@ -943,8 +973,8 @@ int rlc_am::rlc_am_tx::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
|
||||||
poll_sn = vt_s;
|
poll_sn = vt_s;
|
||||||
pdu_without_poll = 0;
|
pdu_without_poll = 0;
|
||||||
byte_without_poll = 0;
|
byte_without_poll = 0;
|
||||||
if (poll_retx_timer) {
|
if (poll_retx_timer != NULL) {
|
||||||
poll_retx_timer->set(this, cfg.t_poll_retx);
|
poll_retx_timer->reset();
|
||||||
poll_retx_timer->run();
|
poll_retx_timer->run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -979,7 +1009,7 @@ void rlc_am::rlc_am_tx::handle_control_pdu(uint8_t *payload, uint32_t nof_bytes)
|
||||||
|
|
||||||
log->info("%s Rx Status PDU: %s\n", RB_NAME, rlc_am_to_string(&status).c_str());
|
log->info("%s Rx Status PDU: %s\n", RB_NAME, rlc_am_to_string(&status).c_str());
|
||||||
|
|
||||||
if (poll_retx_timer) {
|
if (poll_retx_timer != NULL) {
|
||||||
poll_retx_timer->reset();
|
poll_retx_timer->reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1005,7 +1035,8 @@ void rlc_am::rlc_am_tx::handle_control_pdu(uint8_t *payload, uint32_t nof_bytes)
|
||||||
if(tx_window.end() != it)
|
if(tx_window.end() != it)
|
||||||
{
|
{
|
||||||
if(!retx_queue_has_sn(i)) {
|
if(!retx_queue_has_sn(i)) {
|
||||||
rlc_amd_retx_t retx;
|
rlc_amd_retx_t retx = {};
|
||||||
|
retx.sn = i;
|
||||||
retx.is_segment = false;
|
retx.is_segment = false;
|
||||||
retx.so_start = 0;
|
retx.so_start = 0;
|
||||||
retx.so_end = it->second.buf->N_bytes;
|
retx.so_end = it->second.buf->N_bytes;
|
||||||
|
@ -1036,8 +1067,6 @@ void rlc_am::rlc_am_tx::handle_control_pdu(uint8_t *payload, uint32_t nof_bytes)
|
||||||
RB_NAME, i, status.nacks[j].so_start, status.nacks[j].so_end, it->second.buf->N_bytes);
|
RB_NAME, i, status.nacks[j].so_start, status.nacks[j].so_end, it->second.buf->N_bytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
retx.sn = i;
|
|
||||||
retx_queue.push_back(retx);
|
retx_queue.push_back(retx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1050,7 +1079,7 @@ void rlc_am::rlc_am_tx::handle_control_pdu(uint8_t *payload, uint32_t nof_bytes)
|
||||||
it = tx_window.find(i);
|
it = tx_window.find(i);
|
||||||
if (it != tx_window.end()) {
|
if (it != tx_window.end()) {
|
||||||
if(update_vt_a) {
|
if(update_vt_a) {
|
||||||
if(it->second.buf) {
|
if (it->second.buf != NULL) {
|
||||||
pool->deallocate(it->second.buf);
|
pool->deallocate(it->second.buf);
|
||||||
it->second.buf = 0;
|
it->second.buf = 0;
|
||||||
}
|
}
|
||||||
|
@ -1080,7 +1109,7 @@ void rlc_am::rlc_am_tx::debug_state()
|
||||||
int rlc_am::rlc_am_tx::required_buffer_size(rlc_amd_retx_t retx)
|
int rlc_am::rlc_am_tx::required_buffer_size(rlc_amd_retx_t retx)
|
||||||
{
|
{
|
||||||
if (!retx.is_segment) {
|
if (!retx.is_segment) {
|
||||||
if (tx_window.count(retx.sn)) {
|
if (tx_window.count(retx.sn) == 1) {
|
||||||
if (tx_window[retx.sn].buf) {
|
if (tx_window[retx.sn].buf) {
|
||||||
return rlc_am_packed_length(&tx_window[retx.sn].header) + tx_window[retx.sn].buf->N_bytes;
|
return rlc_am_packed_length(&tx_window[retx.sn].header) + tx_window[retx.sn].buf->N_bytes;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1106,33 +1135,34 @@ int rlc_am::rlc_am_tx::required_buffer_size(rlc_amd_retx_t retx)
|
||||||
new_header.so = retx.so_start;
|
new_header.so = retx.so_start;
|
||||||
new_header.N_li = 0;
|
new_header.N_li = 0;
|
||||||
|
|
||||||
uint32_t head_len = 0;
|
|
||||||
|
|
||||||
// Need to rebuild the li table & update fi based on so_start and so_end
|
// Need to rebuild the li table & update fi based on so_start and so_end
|
||||||
if(retx.so_start != 0 && rlc_am_start_aligned(old_header.fi))
|
if(retx.so_start != 0 && rlc_am_start_aligned(old_header.fi)) {
|
||||||
new_header.fi &= RLC_FI_FIELD_NOT_END_ALIGNED; // segment is start aligned
|
new_header.fi &= RLC_FI_FIELD_NOT_END_ALIGNED; // segment is start aligned
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t lower = 0;
|
uint32_t lower = 0;
|
||||||
uint32_t upper = 0;
|
uint32_t upper = 0;
|
||||||
uint32_t li = 0;
|
uint32_t li = 0;
|
||||||
|
|
||||||
for(uint32_t i=0; i<old_header.N_li; i++) {
|
for(uint32_t i=0; i<old_header.N_li; i++) {
|
||||||
if(lower >= retx.so_end)
|
if(lower >= retx.so_end) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
upper += old_header.li[i];
|
upper += old_header.li[i];
|
||||||
|
|
||||||
head_len = rlc_am_packed_length(&new_header);
|
if (upper > retx.so_start && lower < retx.so_end) { // Current SDU is needed
|
||||||
|
|
||||||
if(upper > retx.so_start && lower < retx.so_end) { // Current SDU is needed
|
|
||||||
li = upper - lower;
|
li = upper - lower;
|
||||||
if(upper > retx.so_end)
|
if (upper > retx.so_end) {
|
||||||
li -= upper - retx.so_end;
|
li -= upper - retx.so_end;
|
||||||
if(lower < retx.so_start)
|
}
|
||||||
|
if (lower < retx.so_start) {
|
||||||
li -= retx.so_start - lower;
|
li -= retx.so_start - lower;
|
||||||
if(lower > 0 && lower == retx.so_start)
|
}
|
||||||
|
if (lower > 0 && lower == retx.so_start) {
|
||||||
new_header.fi &= RLC_FI_FIELD_NOT_END_ALIGNED; // segment start is aligned with this SDU
|
new_header.fi &= RLC_FI_FIELD_NOT_END_ALIGNED; // segment start is aligned with this SDU
|
||||||
if(upper == retx.so_end) {
|
}
|
||||||
|
if (upper == retx.so_end) {
|
||||||
new_header.fi &= RLC_FI_FIELD_NOT_START_ALIGNED; // segment end is aligned with this SDU
|
new_header.fi &= RLC_FI_FIELD_NOT_START_ALIGNED; // segment end is aligned with this SDU
|
||||||
}
|
}
|
||||||
new_header.li[new_header.N_li++] = li;
|
new_header.li[new_header.N_li++] = li;
|
||||||
|
@ -1152,9 +1182,10 @@ int rlc_am::rlc_am_tx::required_buffer_size(rlc_amd_retx_t retx)
|
||||||
bool rlc_am::rlc_am_tx::retx_queue_has_sn(uint32_t sn)
|
bool rlc_am::rlc_am_tx::retx_queue_has_sn(uint32_t sn)
|
||||||
{
|
{
|
||||||
std::deque<rlc_amd_retx_t>::iterator q_it;
|
std::deque<rlc_amd_retx_t>::iterator q_it;
|
||||||
for(q_it = retx_queue.begin(); q_it != retx_queue.end(); q_it++) {
|
for (q_it = retx_queue.begin(); q_it != retx_queue.end(); ++q_it) {
|
||||||
if(q_it->sn == sn)
|
if (q_it->sn == sn) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1192,8 +1223,8 @@ rlc_am::rlc_am_rx::~rlc_am_rx()
|
||||||
|
|
||||||
void rlc_am::rlc_am_rx::init()
|
void rlc_am::rlc_am_rx::init()
|
||||||
{
|
{
|
||||||
log = parent->log;
|
log = parent->log;
|
||||||
if (parent->mac_timers) {
|
if (parent->mac_timers != NULL) {
|
||||||
reordering_timer_id = parent->mac_timers->timer_get_unique_id();
|
reordering_timer_id = parent->mac_timers->timer_get_unique_id();
|
||||||
reordering_timer = parent->mac_timers->timer_get(reordering_timer_id);
|
reordering_timer = parent->mac_timers->timer_get(reordering_timer_id);
|
||||||
}
|
}
|
||||||
|
@ -1205,10 +1236,15 @@ bool rlc_am::rlc_am_rx::configure(srslte_rlc_am_config_t cfg_)
|
||||||
cfg = cfg_;
|
cfg = cfg_;
|
||||||
|
|
||||||
// check timers
|
// check timers
|
||||||
if (not reordering_timer) {
|
if (reordering_timer == NULL) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// configure timer
|
||||||
|
if (cfg.t_reordering > 0) {
|
||||||
|
reordering_timer->set(this, static_cast<uint32_t>(cfg.t_reordering));
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1221,13 +1257,13 @@ void rlc_am::rlc_am_rx::stop()
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(&mutex);
|
pthread_mutex_lock(&mutex);
|
||||||
|
|
||||||
if (parent->mac_timers && reordering_timer) {
|
if (parent->mac_timers != NULL && reordering_timer != NULL) {
|
||||||
reordering_timer->stop();
|
reordering_timer->stop();
|
||||||
parent->mac_timers->timer_release_id(reordering_timer_id);
|
parent->mac_timers->timer_release_id(reordering_timer_id);
|
||||||
reordering_timer = NULL;
|
reordering_timer = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rx_sdu) {
|
if (rx_sdu != NULL) {
|
||||||
pool->deallocate(rx_sdu);
|
pool->deallocate(rx_sdu);
|
||||||
rx_sdu = NULL;
|
rx_sdu = NULL;
|
||||||
}
|
}
|
||||||
|
@ -1269,7 +1305,10 @@ void rlc_am::rlc_am_rx::handle_data_pdu(uint8_t *payload, uint32_t nof_bytes, rl
|
||||||
std::map<uint32_t, rlc_amd_rx_pdu_t>::iterator it;
|
std::map<uint32_t, rlc_amd_rx_pdu_t>::iterator it;
|
||||||
|
|
||||||
log->info_hex(payload, nof_bytes, "%s Rx data PDU SN: %d (%d B), %s",
|
log->info_hex(payload, nof_bytes, "%s Rx data PDU SN: %d (%d B), %s",
|
||||||
RB_NAME, header.sn, nof_bytes, rlc_fi_field_text[header.fi]);
|
RB_NAME,
|
||||||
|
header.sn,
|
||||||
|
nof_bytes,
|
||||||
|
rlc_fi_field_text[header.fi]);
|
||||||
|
|
||||||
if(!inside_rx_window(header.sn)) {
|
if(!inside_rx_window(header.sn)) {
|
||||||
if(header.p) {
|
if(header.p) {
|
||||||
|
@ -1295,7 +1334,7 @@ void rlc_am::rlc_am_rx::handle_data_pdu(uint8_t *payload, uint32_t nof_bytes, rl
|
||||||
// Write to rx window
|
// Write to rx window
|
||||||
rlc_amd_rx_pdu_t pdu;
|
rlc_amd_rx_pdu_t pdu;
|
||||||
pdu.buf = pool_allocate_blocking;
|
pdu.buf = pool_allocate_blocking;
|
||||||
if (!pdu.buf) {
|
if (pdu.buf == NULL) {
|
||||||
#ifdef RLC_AM_BUFFER_DEBUG
|
#ifdef RLC_AM_BUFFER_DEBUG
|
||||||
log->console("Fatal Error: Couldn't allocate PDU in handle_data_pdu().\n");
|
log->console("Fatal Error: Couldn't allocate PDU in handle_data_pdu().\n");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
|
@ -1331,7 +1370,7 @@ void rlc_am::rlc_am_rx::handle_data_pdu(uint8_t *payload, uint32_t nof_bytes, rl
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check poll bit
|
// Check poll bit
|
||||||
if(header.p) {
|
if (header.p) {
|
||||||
log->info("%s Status packet requested through polling bit\n", RB_NAME);
|
log->info("%s Status packet requested through polling bit\n", RB_NAME);
|
||||||
poll_received = true;
|
poll_received = true;
|
||||||
|
|
||||||
|
@ -1348,7 +1387,7 @@ void rlc_am::rlc_am_rx::handle_data_pdu(uint8_t *payload, uint32_t nof_bytes, rl
|
||||||
reassemble_rx_sdus();
|
reassemble_rx_sdus();
|
||||||
|
|
||||||
// Update reordering variables and timers (36.322 v10.0.0 Section 5.1.3.2.3)
|
// Update reordering variables and timers (36.322 v10.0.0 Section 5.1.3.2.3)
|
||||||
if (reordering_timer) {
|
if (reordering_timer != NULL) {
|
||||||
if (reordering_timer->is_running()) {
|
if (reordering_timer->is_running()) {
|
||||||
if(vr_x == vr_r || (!inside_rx_window(vr_x) && vr_x != vr_mr)) {
|
if(vr_x == vr_r || (!inside_rx_window(vr_x) && vr_x != vr_mr)) {
|
||||||
reordering_timer->reset();
|
reordering_timer->reset();
|
||||||
|
@ -1357,7 +1396,7 @@ void rlc_am::rlc_am_rx::handle_data_pdu(uint8_t *payload, uint32_t nof_bytes, rl
|
||||||
|
|
||||||
if (not reordering_timer->is_running()) {
|
if (not reordering_timer->is_running()) {
|
||||||
if(RX_MOD_BASE(vr_h) > RX_MOD_BASE(vr_r)) {
|
if(RX_MOD_BASE(vr_h) > RX_MOD_BASE(vr_r)) {
|
||||||
reordering_timer->set(this, cfg.t_reordering);
|
reordering_timer->reset();
|
||||||
reordering_timer->run();
|
reordering_timer->run();
|
||||||
vr_x = vr_h;
|
vr_x = vr_h;
|
||||||
}
|
}
|
||||||
|
@ -1388,7 +1427,7 @@ void rlc_am::rlc_am_rx::handle_data_pdu_segment(uint8_t *payload, uint32_t nof_b
|
||||||
|
|
||||||
rlc_amd_rx_pdu_t segment;
|
rlc_amd_rx_pdu_t segment;
|
||||||
segment.buf = pool_allocate_blocking;
|
segment.buf = pool_allocate_blocking;
|
||||||
if (!segment.buf) {
|
if (segment.buf == NULL) {
|
||||||
#ifdef RLC_AM_BUFFER_DEBUG
|
#ifdef RLC_AM_BUFFER_DEBUG
|
||||||
log->console("Fatal Error: Couldn't allocate PDU in handle_data_pdu_segment().\n");
|
log->console("Fatal Error: Couldn't allocate PDU in handle_data_pdu_segment().\n");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
|
@ -1429,14 +1468,13 @@ void rlc_am::rlc_am_rx::handle_data_pdu_segment(uint8_t *payload, uint32_t nof_b
|
||||||
pdu.segments.push_back(segment);
|
pdu.segments.push_back(segment);
|
||||||
rx_segments[header.sn] = pdu;
|
rx_segments[header.sn] = pdu;
|
||||||
|
|
||||||
|
|
||||||
// Update vr_h
|
// Update vr_h
|
||||||
if(RX_MOD_BASE(header.sn) >= RX_MOD_BASE(vr_h))
|
if (RX_MOD_BASE(header.sn) >= RX_MOD_BASE(vr_h)) {
|
||||||
vr_h = (header.sn + 1)%MOD;
|
vr_h = (header.sn + 1) % MOD;
|
||||||
|
}
|
||||||
|
|
||||||
// Check poll bit
|
// Check poll bit
|
||||||
if(header.p)
|
if (header.p) {
|
||||||
{
|
|
||||||
log->info("%s Status packet requested through polling bit\n", RB_NAME);
|
log->info("%s Status packet requested through polling bit\n", RB_NAME);
|
||||||
poll_received = true;
|
poll_received = true;
|
||||||
|
|
||||||
|
@ -1459,9 +1497,9 @@ void rlc_am::rlc_am_rx::handle_data_pdu_segment(uint8_t *payload, uint32_t nof_b
|
||||||
void rlc_am::rlc_am_rx::reassemble_rx_sdus()
|
void rlc_am::rlc_am_rx::reassemble_rx_sdus()
|
||||||
{
|
{
|
||||||
uint32_t len = 0;
|
uint32_t len = 0;
|
||||||
if(!rx_sdu) {
|
if (rx_sdu == NULL) {
|
||||||
rx_sdu = pool_allocate_blocking;
|
rx_sdu = pool_allocate_blocking;
|
||||||
if (!rx_sdu) {
|
if (rx_sdu == NULL) {
|
||||||
#ifdef RLC_AM_BUFFER_DEBUG
|
#ifdef RLC_AM_BUFFER_DEBUG
|
||||||
log->console("Fatal Error: Could not allocate PDU in reassemble_rx_sdus() (1)\n");
|
log->console("Fatal Error: Could not allocate PDU in reassemble_rx_sdus() (1)\n");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
|
@ -1495,7 +1533,7 @@ void rlc_am::rlc_am_rx::reassemble_rx_sdus()
|
||||||
parent->pdcp->write_pdu(parent->lcid, rx_sdu);
|
parent->pdcp->write_pdu(parent->lcid, rx_sdu);
|
||||||
|
|
||||||
rx_sdu = pool_allocate_blocking;
|
rx_sdu = pool_allocate_blocking;
|
||||||
if (!rx_sdu) {
|
if (rx_sdu == NULL) {
|
||||||
#ifdef RLC_AM_BUFFER_DEBUG
|
#ifdef RLC_AM_BUFFER_DEBUG
|
||||||
log->console("Fatal Error: Could not allocate PDU in reassemble_rx_sdus() (2)\n");
|
log->console("Fatal Error: Could not allocate PDU in reassemble_rx_sdus() (2)\n");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
|
@ -1528,12 +1566,12 @@ void rlc_am::rlc_am_rx::reassemble_rx_sdus()
|
||||||
rx_window.erase(vr_r);
|
rx_window.erase(vr_r);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(rlc_am_end_aligned(rx_window[vr_r].header.fi)) {
|
if (rlc_am_end_aligned(rx_window[vr_r].header.fi)) {
|
||||||
log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU (%d B)", RB_NAME, rx_sdu->N_bytes);
|
log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU (%d B)", RB_NAME, rx_sdu->N_bytes);
|
||||||
rx_sdu->set_timestamp();
|
rx_sdu->set_timestamp();
|
||||||
parent->pdcp->write_pdu(parent->lcid, rx_sdu);
|
parent->pdcp->write_pdu(parent->lcid, rx_sdu);
|
||||||
rx_sdu = pool_allocate_blocking;
|
rx_sdu = pool_allocate_blocking;
|
||||||
if (!rx_sdu) {
|
if (rx_sdu == NULL) {
|
||||||
#ifdef RLC_AM_BUFFER_DEBUG
|
#ifdef RLC_AM_BUFFER_DEBUG
|
||||||
log->console("Fatal Error: Could not allocate PDU in reassemble_rx_sdus() (3)\n");
|
log->console("Fatal Error: Could not allocate PDU in reassemble_rx_sdus() (3)\n");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
|
@ -1604,7 +1642,7 @@ void rlc_am::rlc_am_rx::write_pdu(uint8_t *payload, uint32_t nof_bytes)
|
||||||
void rlc_am::rlc_am_rx::timer_expired(uint32_t timeout_id)
|
void rlc_am::rlc_am_rx::timer_expired(uint32_t timeout_id)
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(&mutex);
|
pthread_mutex_lock(&mutex);
|
||||||
if (reordering_timer && reordering_timer_id == timeout_id) {
|
if (reordering_timer != NULL && reordering_timer_id == timeout_id) {
|
||||||
reordering_timer->reset();
|
reordering_timer->reset();
|
||||||
log->debug("%s reordering timeout expiry - updating vr_ms\n", RB_NAME);
|
log->debug("%s reordering timeout expiry - updating vr_ms\n", RB_NAME);
|
||||||
|
|
||||||
|
@ -1621,7 +1659,7 @@ void rlc_am::rlc_am_rx::timer_expired(uint32_t timeout_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (RX_MOD_BASE(vr_h) > RX_MOD_BASE(vr_ms)) {
|
if (RX_MOD_BASE(vr_h) > RX_MOD_BASE(vr_ms)) {
|
||||||
reordering_timer->set(this, cfg.t_reordering);
|
reordering_timer->reset();
|
||||||
reordering_timer->run();
|
reordering_timer->run();
|
||||||
vr_x = vr_h;
|
vr_x = vr_h;
|
||||||
}
|
}
|
||||||
|
@ -1640,9 +1678,10 @@ int rlc_am::rlc_am_rx::get_status(rlc_status_pdu_t* status)
|
||||||
|
|
||||||
// We don't use segment NACKs - just NACK the full PDU
|
// We don't use segment NACKs - just NACK the full PDU
|
||||||
uint32_t i = vr_r;
|
uint32_t i = vr_r;
|
||||||
while(RX_MOD_BASE(i) < RX_MOD_BASE(vr_ms)) {
|
while (RX_MOD_BASE(i) < RX_MOD_BASE(vr_ms) && status->N_nack < RLC_AM_WINDOW_SIZE) {
|
||||||
if(rx_window.find(i) == rx_window.end()) {
|
if(rx_window.find(i) == rx_window.end()) {
|
||||||
status->nacks[status->N_nack++].nack_sn = i;
|
status->nacks[status->N_nack].nack_sn = i;
|
||||||
|
status->N_nack++;
|
||||||
}
|
}
|
||||||
i = (i + 1)%MOD;
|
i = (i + 1)%MOD;
|
||||||
}
|
}
|
||||||
|
@ -1694,12 +1733,14 @@ bool rlc_am::rlc_am_rx::add_segment_and_check(rlc_amd_rx_pdu_segments_t *pdu, rl
|
||||||
uint32_t so = 0;
|
uint32_t so = 0;
|
||||||
std::list<rlc_amd_rx_pdu_t>::iterator it, tmpit;
|
std::list<rlc_amd_rx_pdu_t>::iterator it, tmpit;
|
||||||
for(it = pdu->segments.begin(); it != pdu->segments.end(); it++) {
|
for(it = pdu->segments.begin(); it != pdu->segments.end(); it++) {
|
||||||
if(so != it->header.so)
|
if (so != it->header.so) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
so += it->buf->N_bytes;
|
so += it->buf->N_bytes;
|
||||||
}
|
}
|
||||||
if(!pdu->segments.back().header.lsf)
|
if (!pdu->segments.back().header.lsf) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// We have all segments of the PDU - reconstruct and handle
|
// We have all segments of the PDU - reconstruct and handle
|
||||||
rlc_amd_pdu_header_t header;
|
rlc_amd_pdu_header_t header;
|
||||||
|
@ -1745,7 +1786,7 @@ bool rlc_am::rlc_am_rx::add_segment_and_check(rlc_amd_rx_pdu_segments_t *pdu, rl
|
||||||
|
|
||||||
// Copy data
|
// Copy data
|
||||||
byte_buffer_t *full_pdu = pool_allocate_blocking;
|
byte_buffer_t *full_pdu = pool_allocate_blocking;
|
||||||
if (!full_pdu) {
|
if (full_pdu == NULL) {
|
||||||
#ifdef RLC_AM_BUFFER_DEBUG
|
#ifdef RLC_AM_BUFFER_DEBUG
|
||||||
log->console("Fatal Error: Could not allocate PDU in add_segment_and_check()\n");
|
log->console("Fatal Error: Could not allocate PDU in add_segment_and_check()\n");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
|
@ -1801,14 +1842,14 @@ void rlc_am_read_data_pdu_header(uint8_t **payload, uint32_t *nof_bytes, rlc_amd
|
||||||
uint8_t ext;
|
uint8_t ext;
|
||||||
uint8_t *ptr = *payload;
|
uint8_t *ptr = *payload;
|
||||||
|
|
||||||
header->dc = (rlc_dc_field_t)((*ptr >> 7) & 0x01);
|
header->dc = static_cast<rlc_dc_field_t>((*ptr >> 7) & 0x01);
|
||||||
|
|
||||||
if(RLC_DC_FIELD_DATA_PDU == header->dc)
|
if(RLC_DC_FIELD_DATA_PDU == header->dc)
|
||||||
{
|
{
|
||||||
// Fixed part
|
// Fixed part
|
||||||
header->rf = ((*ptr >> 6) & 0x01);
|
header->rf = ((*ptr >> 6) & 0x01);
|
||||||
header->p = ((*ptr >> 5) & 0x01);
|
header->p = ((*ptr >> 5) & 0x01);
|
||||||
header->fi = (rlc_fi_field_t)((*ptr >> 3) & 0x03);
|
header->fi = static_cast<rlc_fi_field_t>((*ptr >> 3) & 0x03);
|
||||||
ext = ((*ptr >> 2) & 0x01);
|
ext = ((*ptr >> 2) & 0x01);
|
||||||
header->sn = (*ptr & 0x03) << 8; // 2 bits SN
|
header->sn = (*ptr & 0x03) << 8; // 2 bits SN
|
||||||
ptr++;
|
ptr++;
|
||||||
|
@ -1848,8 +1889,9 @@ void rlc_am_read_data_pdu_header(uint8_t **payload, uint32_t *nof_bytes, rlc_amd
|
||||||
}
|
}
|
||||||
|
|
||||||
// Account for padding if N_li is odd
|
// Account for padding if N_li is odd
|
||||||
if(header->N_li%2 == 1)
|
if (header->N_li%2 == 1) {
|
||||||
ptr++;
|
ptr++;
|
||||||
|
}
|
||||||
|
|
||||||
*nof_bytes -= ptr-*payload;
|
*nof_bytes -= ptr-*payload;
|
||||||
*payload = ptr;
|
*payload = ptr;
|
||||||
|
@ -1885,7 +1927,7 @@ void rlc_am_write_data_pdu_header(rlc_amd_pdu_header_t *header, uint8_t **payloa
|
||||||
ptr++;
|
ptr++;
|
||||||
|
|
||||||
// Segment part
|
// Segment part
|
||||||
if(header->rf)
|
if (header->rf)
|
||||||
{
|
{
|
||||||
*ptr = (header->lsf & 0x01) << 7;
|
*ptr = (header->lsf & 0x01) << 7;
|
||||||
*ptr |= (header->so & 0x7F00) >> 8; // 7 bits of SO
|
*ptr |= (header->so & 0x7F00) >> 8; // 7 bits of SO
|
||||||
|
@ -1916,8 +1958,9 @@ void rlc_am_write_data_pdu_header(rlc_amd_pdu_header_t *header, uint8_t **payloa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Pad if N_li is odd
|
// Pad if N_li is odd
|
||||||
if(header->N_li%2 == 1)
|
if (header->N_li%2 == 1) {
|
||||||
ptr++;
|
ptr++;
|
||||||
|
}
|
||||||
|
|
||||||
*payload = ptr;
|
*payload = ptr;
|
||||||
}
|
}
|
||||||
|
@ -1937,7 +1980,7 @@ void rlc_am_read_status_pdu(uint8_t *payload, uint32_t nof_bytes, rlc_status_pdu
|
||||||
srslte_bit_unpack_vector(payload, tmp.msg, nof_bytes*8);
|
srslte_bit_unpack_vector(payload, tmp.msg, nof_bytes*8);
|
||||||
tmp.N_bits = nof_bytes*8;
|
tmp.N_bits = nof_bytes*8;
|
||||||
|
|
||||||
rlc_dc_field_t dc = (rlc_dc_field_t)srslte_bit_pack(&ptr, 1);
|
rlc_dc_field_t dc = static_cast<rlc_dc_field_t>(srslte_bit_pack(&ptr, 1));
|
||||||
|
|
||||||
if(RLC_DC_FIELD_CONTROL_PDU == dc)
|
if(RLC_DC_FIELD_CONTROL_PDU == dc)
|
||||||
{
|
{
|
||||||
|
@ -2009,7 +2052,9 @@ int rlc_am_write_status_pdu(rlc_status_pdu_t *status, uint8_t *payload)
|
||||||
uint32_t rlc_am_packed_length(rlc_amd_pdu_header_t *header)
|
uint32_t rlc_am_packed_length(rlc_amd_pdu_header_t *header)
|
||||||
{
|
{
|
||||||
uint32_t len = 2; // Fixed part is 2 bytes
|
uint32_t len = 2; // Fixed part is 2 bytes
|
||||||
if(header->rf) len += 2; // Segment header is 2 bytes
|
if (header->rf) {
|
||||||
|
len += 2; // Segment header is 2 bytes
|
||||||
|
}
|
||||||
len += header->N_li * 1.5 + 0.5; // Extension part - integer rounding up
|
len += header->N_li * 1.5 + 0.5; // Extension part - integer rounding up
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
@ -2086,4 +2131,4 @@ bool rlc_am_not_start_aligned(const uint8_t fi)
|
||||||
return (fi == RLC_FI_FIELD_NOT_START_ALIGNED || fi == RLC_FI_FIELD_NOT_START_OR_END_ALIGNED);
|
return (fi == RLC_FI_FIELD_NOT_START_ALIGNED || fi == RLC_FI_FIELD_NOT_START_OR_END_ALIGNED);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace srsue
|
} // namespace srslte
|
||||||
|
|
|
@ -36,6 +36,11 @@ add_test(rlc_am_stress_test rlc_stress_test --mode=AM --loglevel 1 --sdu_gen_del
|
||||||
add_test(rlc_um_stress_test rlc_stress_test --mode=UM --loglevel 1)
|
add_test(rlc_um_stress_test rlc_stress_test --mode=UM --loglevel 1)
|
||||||
add_test(rlc_tm_stress_test rlc_stress_test --mode=TM --loglevel 1 --opp_sdu_ratio=1.0)
|
add_test(rlc_tm_stress_test rlc_stress_test --mode=TM --loglevel 1 --opp_sdu_ratio=1.0)
|
||||||
|
|
||||||
|
# Run clang-tidy if available
|
||||||
|
if(CLANG_TIDY_BIN)
|
||||||
|
set_target_properties(rlc_stress_test PROPERTIES CXX_CLANG_TIDY "${DO_CLANG_TIDY}")
|
||||||
|
endif()
|
||||||
|
|
||||||
add_executable(rlc_um_data_test rlc_um_data_test.cc)
|
add_executable(rlc_um_data_test rlc_um_data_test.cc)
|
||||||
target_link_libraries(rlc_um_data_test srslte_upper srslte_phy srslte_common)
|
target_link_libraries(rlc_um_data_test srslte_upper srslte_phy srslte_common)
|
||||||
add_test(rlc_um_data_test rlc_um_data_test)
|
add_test(rlc_um_data_test rlc_um_data_test)
|
||||||
|
|
|
@ -867,7 +867,6 @@ bool resegment_test_3()
|
||||||
|
|
||||||
bool resegment_test_4()
|
bool resegment_test_4()
|
||||||
{
|
{
|
||||||
|
|
||||||
// SDUs: | 10 | 10 | 10 | 10 | 10 |
|
// SDUs: | 10 | 10 | 10 | 10 | 10 |
|
||||||
// PDUs: | 5 | 5| 30 | 5 | 5|
|
// PDUs: | 5 | 5| 30 | 5 | 5|
|
||||||
// Retx PDU segments: | 15 | 15 |
|
// Retx PDU segments: | 15 | 15 |
|
||||||
|
@ -980,7 +979,6 @@ bool resegment_test_4()
|
||||||
|
|
||||||
bool resegment_test_5()
|
bool resegment_test_5()
|
||||||
{
|
{
|
||||||
|
|
||||||
// SDUs: | 10 | 10 | 10 | 10 | 10 |
|
// SDUs: | 10 | 10 | 10 | 10 | 10 |
|
||||||
// PDUs: |2|3| 40 |3|2|
|
// PDUs: |2|3| 40 |3|2|
|
||||||
// Retx PDU segments: | 20 | 20 |
|
// Retx PDU segments: | 20 | 20 |
|
||||||
|
@ -1373,6 +1371,18 @@ bool resegment_test_7()
|
||||||
timers.step_all();
|
timers.step_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read status PDU from RLC2
|
||||||
|
assert(rlc2.get_buffer_state());
|
||||||
|
status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status
|
||||||
|
|
||||||
|
// Write status PDU to RLC1
|
||||||
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
||||||
|
#if HAVE_PCAP
|
||||||
|
pcap.write_ul_am_ccch(status_buf.msg, status_buf.N_bytes);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// check status again
|
||||||
|
assert(0 == rlc1.get_buffer_state());
|
||||||
assert(0 == rlc2.get_buffer_state());
|
assert(0 == rlc2.get_buffer_state());
|
||||||
|
|
||||||
// Check number of SDUs and their content
|
// Check number of SDUs and their content
|
||||||
|
@ -1441,11 +1451,11 @@ bool resegment_test_8()
|
||||||
cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS5;
|
cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS5;
|
||||||
|
|
||||||
if (not rlc1.configure(&cnfg)) {
|
if (not rlc1.configure(&cnfg)) {
|
||||||
exit(-1);
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (not rlc2.configure(&cnfg)) {
|
if (not rlc2.configure(&cnfg)) {
|
||||||
exit(-1);
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push 2 SDUs into RLC1
|
// Push 2 SDUs into RLC1
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <stdlib.h>
|
#include <cstdlib>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include "srslte/common/log_filter.h"
|
#include "srslte/common/log_filter.h"
|
||||||
#include "srslte/common/logger_stdout.h"
|
#include "srslte/common/logger_stdout.h"
|
||||||
|
@ -34,10 +34,9 @@
|
||||||
#include "srslte/upper/rlc.h"
|
#include "srslte/upper/rlc.h"
|
||||||
#include <boost/program_options.hpp>
|
#include <boost/program_options.hpp>
|
||||||
#include <boost/program_options/parsers.hpp>
|
#include <boost/program_options/parsers.hpp>
|
||||||
#include <assert.h>
|
#include <cassert>
|
||||||
#include <srslte/upper/rlc_interface.h>
|
#include <srslte/upper/rlc_interface.h>
|
||||||
|
|
||||||
#define SDU_SIZE (1500)
|
|
||||||
#define LOG_HEX_LIMIT (-1)
|
#define LOG_HEX_LIMIT (-1)
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
@ -47,6 +46,7 @@ namespace bpo = boost::program_options;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
std::string mode;
|
std::string mode;
|
||||||
|
uint32_t sdu_size;
|
||||||
uint32_t test_duration_sec;
|
uint32_t test_duration_sec;
|
||||||
float error_rate;
|
float error_rate;
|
||||||
uint32_t sdu_gen_delay_usec;
|
uint32_t sdu_gen_delay_usec;
|
||||||
|
@ -57,6 +57,7 @@ typedef struct {
|
||||||
bool write_pcap;
|
bool write_pcap;
|
||||||
float opp_sdu_ratio;
|
float opp_sdu_ratio;
|
||||||
bool zero_seed;
|
bool zero_seed;
|
||||||
|
bool pedantic;
|
||||||
} stress_test_args_t;
|
} stress_test_args_t;
|
||||||
|
|
||||||
void parse_args(stress_test_args_t *args, int argc, char *argv[]) {
|
void parse_args(stress_test_args_t *args, int argc, char *argv[]) {
|
||||||
|
@ -73,6 +74,7 @@ void parse_args(stress_test_args_t *args, int argc, char *argv[]) {
|
||||||
common.add_options()
|
common.add_options()
|
||||||
("mode", bpo::value<std::string>(&args->mode)->default_value("AM"), "Whether to test RLC acknowledged or unacknowledged mode (AM/UM)")
|
("mode", bpo::value<std::string>(&args->mode)->default_value("AM"), "Whether to test RLC acknowledged or unacknowledged mode (AM/UM)")
|
||||||
("duration", bpo::value<uint32_t>(&args->test_duration_sec)->default_value(5), "Duration (sec)")
|
("duration", bpo::value<uint32_t>(&args->test_duration_sec)->default_value(5), "Duration (sec)")
|
||||||
|
("sdu_size", bpo::value<uint32_t>(&args->sdu_size)->default_value(1500), "Size of SDUs")
|
||||||
("sdu_gen_delay", bpo::value<uint32_t>(&args->sdu_gen_delay_usec)->default_value(0), "SDU generation delay (usec)")
|
("sdu_gen_delay", bpo::value<uint32_t>(&args->sdu_gen_delay_usec)->default_value(0), "SDU generation delay (usec)")
|
||||||
("pdu_tx_delay", bpo::value<uint32_t>(&args->pdu_tx_delay_usec)->default_value(0), "Delay in MAC for transfering PDU from tx'ing RLC to rx'ing RLC (usec)")
|
("pdu_tx_delay", bpo::value<uint32_t>(&args->pdu_tx_delay_usec)->default_value(0), "Delay in MAC for transfering PDU from tx'ing RLC to rx'ing RLC (usec)")
|
||||||
("error_rate", bpo::value<float>(&args->error_rate)->default_value(0.1), "Rate at which RLC PDUs are dropped")
|
("error_rate", bpo::value<float>(&args->error_rate)->default_value(0.1), "Rate at which RLC PDUs are dropped")
|
||||||
|
@ -81,7 +83,8 @@ void parse_args(stress_test_args_t *args, int argc, char *argv[]) {
|
||||||
("loglevel", bpo::value<uint32_t>(&args->log_level)->default_value(srslte::LOG_LEVEL_DEBUG), "Log level (1=Error,2=Warning,3=Info,4=Debug)")
|
("loglevel", bpo::value<uint32_t>(&args->log_level)->default_value(srslte::LOG_LEVEL_DEBUG), "Log level (1=Error,2=Warning,3=Info,4=Debug)")
|
||||||
("singletx", bpo::value<bool>(&args->single_tx)->default_value(false), "If set to true, only one node is generating data")
|
("singletx", bpo::value<bool>(&args->single_tx)->default_value(false), "If set to true, only one node is generating data")
|
||||||
("pcap", bpo::value<bool>(&args->write_pcap)->default_value(false), "Whether to write all RLC PDU to PCAP file")
|
("pcap", bpo::value<bool>(&args->write_pcap)->default_value(false), "Whether to write all RLC PDU to PCAP file")
|
||||||
("zeroseed", bpo::value<bool>(&args->zero_seed)->default_value(false), "Whether to initialize random seed to zero");
|
("zeroseed", bpo::value<bool>(&args->zero_seed)->default_value(false), "Whether to initialize random seed to zero")
|
||||||
|
("pedantic", bpo::value<bool>(&args->pedantic)->default_value(true), "Whether to perform strict SDU size checking at receiver");
|
||||||
|
|
||||||
// these options are allowed on the command line
|
// these options are allowed on the command line
|
||||||
bpo::options_description cmdline_options;
|
bpo::options_description cmdline_options;
|
||||||
|
@ -93,7 +96,7 @@ void parse_args(stress_test_args_t *args, int argc, char *argv[]) {
|
||||||
bpo::notify(vm);
|
bpo::notify(vm);
|
||||||
|
|
||||||
// help option was given - print usage and exit
|
// help option was given - print usage and exit
|
||||||
if (vm.count("help")) {
|
if (vm.count("help") > 0) {
|
||||||
cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << endl << endl;
|
cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << endl << endl;
|
||||||
cout << common << endl << general << endl;
|
cout << common << endl << general << endl;
|
||||||
exit(0);
|
exit(0);
|
||||||
|
@ -110,19 +113,17 @@ class mac_dummy
|
||||||
,public thread
|
,public thread
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
mac_dummy(rlc_interface_mac *rlc1_, rlc_interface_mac *rlc2_, float fail_rate_, float opp_sdu_ratio_, int32_t pdu_tx_delay_usec_, uint32_t lcid_, rlc_pcap* pcap_ = NULL)
|
mac_dummy(rlc_interface_mac *rlc1_, rlc_interface_mac *rlc2_, stress_test_args_t args_, uint32_t lcid_, rlc_pcap* pcap_ = NULL)
|
||||||
:timers(8)
|
:timers(8)
|
||||||
,run_enable(true)
|
,run_enable(true)
|
||||||
,rlc1(rlc1_)
|
,rlc1(rlc1_)
|
||||||
,rlc2(rlc2_)
|
,rlc2(rlc2_)
|
||||||
,fail_rate(fail_rate_)
|
,args(args_)
|
||||||
,opp_sdu_ratio(opp_sdu_ratio_)
|
|
||||||
,pdu_tx_delay_usec(pdu_tx_delay_usec_)
|
|
||||||
,pcap(pcap_)
|
,pcap(pcap_)
|
||||||
,lcid(lcid_)
|
,lcid(lcid_)
|
||||||
,log("MAC ")
|
,log("MAC ")
|
||||||
{
|
{
|
||||||
log.set_level(srslte::LOG_LEVEL_ERROR);
|
log.set_level(static_cast<LOG_LEVEL_ENUM>(args.log_level));
|
||||||
log.set_hex_limit(LOG_HEX_LIMIT);
|
log.set_hex_limit(LOG_HEX_LIMIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,14 +156,16 @@ private:
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
float r = opp_sdu_ratio ? opp_sdu_ratio : (float)rand()/RAND_MAX;
|
float r = args.opp_sdu_ratio ? args.opp_sdu_ratio : static_cast<float>(rand())/RAND_MAX;
|
||||||
int opp_size = r*SDU_SIZE;
|
int opp_size = r*args.sdu_size;
|
||||||
uint32_t buf_state = tx_rlc->get_buffer_state(lcid);
|
uint32_t buf_state = tx_rlc->get_buffer_state(lcid);
|
||||||
if (buf_state) {
|
if (buf_state > 0) {
|
||||||
int read = tx_rlc->read_pdu(lcid, pdu->msg, opp_size);
|
int read = tx_rlc->read_pdu(lcid, pdu->msg, opp_size);
|
||||||
pdu->N_bytes = read;
|
pdu->N_bytes = read;
|
||||||
if (pdu_tx_delay_usec) usleep(pdu_tx_delay_usec);
|
if (args.pdu_tx_delay_usec > 0) {
|
||||||
if(((float)rand()/RAND_MAX > fail_rate) && read>0) {
|
usleep(args.pdu_tx_delay_usec);
|
||||||
|
}
|
||||||
|
if(((float)rand()/RAND_MAX > args.error_rate) && read>0) {
|
||||||
rx_rlc->write_pdu(lcid, pdu->msg, pdu->N_bytes);
|
rx_rlc->write_pdu(lcid, pdu->msg, pdu->N_bytes);
|
||||||
if (is_dl) {
|
if (is_dl) {
|
||||||
pcap->write_dl_am_ccch(pdu->msg, pdu->N_bytes);
|
pcap->write_dl_am_ccch(pdu->msg, pdu->N_bytes);
|
||||||
|
@ -170,7 +173,7 @@ private:
|
||||||
pcap->write_ul_am_ccch(pdu->msg, pdu->N_bytes);
|
pcap->write_ul_am_ccch(pdu->msg, pdu->N_bytes);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.info_hex(pdu->msg, pdu->N_bytes, "Dropping RLC PDU (%d B)\n", pdu->N_bytes);
|
log.warning_hex(pdu->msg, pdu->N_bytes, "Dropping RLC PDU (%d B)\n", pdu->N_bytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
byte_buffer_pool::get_instance()->deallocate(pdu);
|
byte_buffer_pool::get_instance()->deallocate(pdu);
|
||||||
|
@ -194,9 +197,7 @@ private:
|
||||||
rlc_interface_mac *rlc2;
|
rlc_interface_mac *rlc2;
|
||||||
srslte::timers timers;
|
srslte::timers timers;
|
||||||
bool run_enable;
|
bool run_enable;
|
||||||
float fail_rate;
|
stress_test_args_t args;
|
||||||
float opp_sdu_ratio;
|
|
||||||
uint32_t pdu_tx_delay_usec;
|
|
||||||
rlc_pcap *pcap;
|
rlc_pcap *pcap;
|
||||||
uint32_t lcid;
|
uint32_t lcid;
|
||||||
srslte::log_filter log;
|
srslte::log_filter log;
|
||||||
|
@ -209,13 +210,17 @@ class rlc_tester
|
||||||
,public thread
|
,public thread
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
rlc_tester(rlc_interface_pdcp *rlc_, std::string name_, uint32_t sdu_gen_delay_usec_, uint32_t lcid_){
|
rlc_tester(rlc_interface_pdcp *rlc_, std::string name_, stress_test_args_t args_, uint32_t lcid_)
|
||||||
rlc = rlc_;
|
:log("Testr")
|
||||||
run_enable = true;
|
,rlc(rlc_)
|
||||||
rx_pdus = 0;
|
,run_enable(true)
|
||||||
name = name_;
|
,rx_pdus()
|
||||||
sdu_gen_delay_usec = sdu_gen_delay_usec_;
|
,name(name_)
|
||||||
lcid = lcid_;
|
,args(args_)
|
||||||
|
,lcid(lcid_)
|
||||||
|
{
|
||||||
|
log.set_level(srslte::LOG_LEVEL_ERROR);
|
||||||
|
log.set_hex_limit(LOG_HEX_LIMIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void stop()
|
void stop()
|
||||||
|
@ -228,13 +233,14 @@ public:
|
||||||
void write_pdu(uint32_t rx_lcid, byte_buffer_t *sdu)
|
void write_pdu(uint32_t rx_lcid, byte_buffer_t *sdu)
|
||||||
{
|
{
|
||||||
assert(rx_lcid == lcid);
|
assert(rx_lcid == lcid);
|
||||||
if (sdu->N_bytes != SDU_SIZE) {
|
if (sdu->N_bytes % args.sdu_size != 0) {
|
||||||
srslte::log_filter log1("Testr");;
|
log.error_hex(sdu->msg, sdu->N_bytes, "Received PDU with size %d, expected %d. Exiting.\n", sdu->N_bytes, args.sdu_size);
|
||||||
log1.set_level(srslte::LOG_LEVEL_ERROR);
|
// fail if SDU is not exactly what we expected
|
||||||
log1.set_hex_limit(sdu->N_bytes);
|
if (args.pedantic && sdu->N_bytes != args.sdu_size) {
|
||||||
log1.error_hex(sdu->msg, sdu->N_bytes, "Received PDU with size %d, expected %d. Exiting.\n", sdu->N_bytes, SDU_SIZE);
|
exit(-1);
|
||||||
exit(-1);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
byte_buffer_pool::get_instance()->deallocate(sdu);
|
byte_buffer_pool::get_instance()->deallocate(sdu);
|
||||||
rx_pdus++;
|
rx_pdus++;
|
||||||
}
|
}
|
||||||
|
@ -254,29 +260,32 @@ private:
|
||||||
uint8_t sn = 0;
|
uint8_t sn = 0;
|
||||||
while(run_enable) {
|
while(run_enable) {
|
||||||
byte_buffer_t *pdu = byte_buffer_pool::get_instance()->allocate("rlc_tester::run_thread");
|
byte_buffer_t *pdu = byte_buffer_pool::get_instance()->allocate("rlc_tester::run_thread");
|
||||||
if (!pdu) {
|
if (pdu == NULL) {
|
||||||
printf("Error: Could not allocate PDU in rlc_tester::run_thread\n\n\n");
|
printf("Error: Could not allocate PDU in rlc_tester::run_thread\n\n\n");
|
||||||
// backoff for a bit
|
// backoff for a bit
|
||||||
usleep(1000);
|
usleep(1000);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (uint32_t i = 0; i < SDU_SIZE; i++) {
|
for (uint32_t i = 0; i < args.sdu_size; i++) {
|
||||||
pdu->msg[i] = sn;
|
pdu->msg[i] = sn;
|
||||||
}
|
}
|
||||||
sn++;
|
sn++;
|
||||||
pdu->N_bytes = SDU_SIZE;
|
pdu->N_bytes = args.sdu_size;
|
||||||
rlc->write_sdu(lcid, pdu);
|
rlc->write_sdu(lcid, pdu);
|
||||||
if (sdu_gen_delay_usec) usleep(sdu_gen_delay_usec);
|
if (args.sdu_gen_delay_usec > 0) {
|
||||||
|
usleep(args.sdu_gen_delay_usec);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool run_enable;
|
bool run_enable;
|
||||||
long rx_pdus;
|
uint64_t rx_pdus;
|
||||||
uint32_t lcid;
|
uint32_t lcid;
|
||||||
|
srslte::log_filter log;
|
||||||
|
|
||||||
std::string name;
|
std::string name;
|
||||||
|
|
||||||
uint32_t sdu_gen_delay_usec;
|
stress_test_args_t args;
|
||||||
|
|
||||||
rlc_interface_pdcp *rlc;
|
rlc_interface_pdcp *rlc;
|
||||||
};
|
};
|
||||||
|
@ -285,8 +294,8 @@ void stress_test(stress_test_args_t args)
|
||||||
{
|
{
|
||||||
srslte::log_filter log1("RLC_1");
|
srslte::log_filter log1("RLC_1");
|
||||||
srslte::log_filter log2("RLC_2");
|
srslte::log_filter log2("RLC_2");
|
||||||
log1.set_level((LOG_LEVEL_ENUM)args.log_level);
|
log1.set_level(static_cast<LOG_LEVEL_ENUM>(args.log_level));
|
||||||
log2.set_level((LOG_LEVEL_ENUM)args.log_level);
|
log2.set_level(static_cast<LOG_LEVEL_ENUM>(args.log_level));
|
||||||
log1.set_hex_limit(LOG_HEX_LIMIT);
|
log1.set_hex_limit(LOG_HEX_LIMIT);
|
||||||
log2.set_hex_limit(LOG_HEX_LIMIT);
|
log2.set_hex_limit(LOG_HEX_LIMIT);
|
||||||
rlc_pcap pcap;
|
rlc_pcap pcap;
|
||||||
|
@ -326,9 +335,9 @@ void stress_test(stress_test_args_t args)
|
||||||
rlc rlc1;
|
rlc rlc1;
|
||||||
rlc rlc2;
|
rlc rlc2;
|
||||||
|
|
||||||
rlc_tester tester1(&rlc1, "tester1", args.sdu_gen_delay_usec, lcid);
|
rlc_tester tester1(&rlc1, "tester1", args, lcid);
|
||||||
rlc_tester tester2(&rlc2, "tester2", args.sdu_gen_delay_usec, lcid);
|
rlc_tester tester2(&rlc2, "tester2", args, lcid);
|
||||||
mac_dummy mac(&rlc1, &rlc2, args.error_rate, args.opp_sdu_ratio, args.pdu_tx_delay_usec, lcid, &pcap);
|
mac_dummy mac(&rlc1, &rlc2, args, lcid, &pcap);
|
||||||
ue_interface ue;
|
ue_interface ue;
|
||||||
|
|
||||||
rlc1.init(&tester1, &tester1, &ue, &log1, &mac, 0);
|
rlc1.init(&tester1, &tester1, &ue, &log1, &mac, 0);
|
||||||
|
@ -346,6 +355,10 @@ void stress_test(stress_test_args_t args)
|
||||||
}
|
}
|
||||||
mac.start();
|
mac.start();
|
||||||
|
|
||||||
|
if (args.test_duration_sec < 1) {
|
||||||
|
args.test_duration_sec = 1;
|
||||||
|
}
|
||||||
|
|
||||||
for (uint32_t i = 0; i < args.test_duration_sec; i++) {
|
for (uint32_t i = 0; i < args.test_duration_sec; i++) {
|
||||||
// if enabled, mimic reestablishment every second
|
// if enabled, mimic reestablishment every second
|
||||||
if (args.reestablish) {
|
if (args.reestablish) {
|
||||||
|
@ -355,39 +368,47 @@ void stress_test(stress_test_args_t args)
|
||||||
usleep(1e6);
|
usleep(1e6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
printf("Test finished, tearing down ..\n");
|
||||||
|
|
||||||
// Stop RLC instances first to release blocking writers
|
// Stop RLC instances first to release blocking writers
|
||||||
rlc1.stop();
|
rlc1.stop();
|
||||||
rlc2.stop();
|
rlc2.stop();
|
||||||
|
|
||||||
|
printf("RLC entities stopped.\n");
|
||||||
|
|
||||||
|
// Stop upper layer writers
|
||||||
tester1.stop();
|
tester1.stop();
|
||||||
tester2.stop();
|
tester2.stop();
|
||||||
|
|
||||||
|
printf("Writers stopped.\n");
|
||||||
|
|
||||||
mac.stop();
|
mac.stop();
|
||||||
if (args.write_pcap) {
|
if (args.write_pcap) {
|
||||||
pcap.close();
|
pcap.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
rlc_metrics_t metrics;
|
rlc_metrics_t metrics = {};
|
||||||
rlc1.get_metrics(metrics);
|
rlc1.get_metrics(metrics);
|
||||||
|
|
||||||
printf("RLC1 received %d SDUs in %ds (%.2f PDU/s), Throughput: DL=%4.2f Mbps, UL=%4.2f Mbps\n",
|
printf("RLC1 received %d SDUs in %ds (%.2f/s), Throughput: DL=%4.2f Mbps, UL=%4.2f Mbps\n",
|
||||||
tester1.get_nof_rx_pdus(),
|
tester1.get_nof_rx_pdus(),
|
||||||
args.test_duration_sec,
|
args.test_duration_sec,
|
||||||
(float)tester1.get_nof_rx_pdus()/args.test_duration_sec,
|
static_cast<double>(tester1.get_nof_rx_pdus()/args.test_duration_sec),
|
||||||
metrics.dl_tput_mbps[lcid],
|
metrics.dl_tput_mbps[lcid],
|
||||||
metrics.ul_tput_mbps[lcid]);
|
metrics.ul_tput_mbps[lcid]);
|
||||||
|
|
||||||
rlc2.get_metrics(metrics);
|
rlc2.get_metrics(metrics);
|
||||||
printf("RLC2 received %d SDUs in %ds (%.2f PDU/s), Throughput: DL=%4.2f Mbps, UL=%4.2f Mbps\n",
|
printf("RLC2 received %d SDUs in %ds (%.2f/s), Throughput: DL=%4.2f Mbps, UL=%4.2f Mbps\n",
|
||||||
tester2.get_nof_rx_pdus(),
|
tester2.get_nof_rx_pdus(),
|
||||||
args.test_duration_sec,
|
args.test_duration_sec,
|
||||||
(float)tester2.get_nof_rx_pdus()/args.test_duration_sec,
|
static_cast<double>(tester2.get_nof_rx_pdus()/args.test_duration_sec),
|
||||||
metrics.dl_tput_mbps[lcid],
|
metrics.dl_tput_mbps[lcid],
|
||||||
metrics.ul_tput_mbps[lcid]);
|
metrics.ul_tput_mbps[lcid]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
stress_test_args_t args;
|
stress_test_args_t args = {};
|
||||||
parse_args(&args, argc, argv);
|
parse_args(&args, argc, argv);
|
||||||
|
|
||||||
if (args.zero_seed) {
|
if (args.zero_seed) {
|
||||||
|
|
|
@ -53,6 +53,7 @@ phch_recv::phch_recv() {
|
||||||
ul_freq = -1;
|
ul_freq = -1;
|
||||||
bzero(&cell, sizeof(srslte_cell_t));
|
bzero(&cell, sizeof(srslte_cell_t));
|
||||||
bzero(&metrics, sizeof(sync_metrics_t));
|
bzero(&metrics, sizeof(sync_metrics_t));
|
||||||
|
cellsearch_earfcn_index = 0;
|
||||||
running = false;
|
running = false;
|
||||||
worker_com = NULL;
|
worker_com = NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,7 @@ phch_worker::phch_worker() : tr_exec(10240)
|
||||||
chest_loop = NULL;
|
chest_loop = NULL;
|
||||||
|
|
||||||
bzero(signal_buffer, sizeof(cf_t*)*SRSLTE_MAX_PORTS);
|
bzero(signal_buffer, sizeof(cf_t*)*SRSLTE_MAX_PORTS);
|
||||||
|
ZERO_OBJECT(cell);
|
||||||
|
|
||||||
mem_initiated = false;
|
mem_initiated = false;
|
||||||
cell_initiated = false;
|
cell_initiated = false;
|
||||||
|
|
Loading…
Reference in New Issue