srsLTE/lib/src/rlc/rlc_um_base.cc

351 lines
8.9 KiB
C++

/**
* Copyright 2013-2022 Software Radio Systems Limited
*
* This file is part of srsRAN.
*
* srsRAN 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.
*
* srsRAN 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 "srsran/rlc/rlc_um_base.h"
#include "srsran/interfaces/ue_rrc_interfaces.h"
#include <sstream>
namespace srsran {
rlc_um_base::rlc_um_base(srslog::basic_logger& logger,
uint32_t lcid_,
srsue::pdcp_interface_rlc* pdcp_,
srsue::rrc_interface_rlc* rrc_,
srsran::timer_handler* timers_) :
rlc_common(logger), lcid(lcid_), pdcp(pdcp_), rrc(rrc_), timers(timers_), pool(byte_buffer_pool::get_instance())
{}
rlc_um_base::~rlc_um_base() {}
void rlc_um_base::stop()
{
if (tx) {
tx->stop();
}
if (rx) {
rx->stop();
}
}
rlc_mode_t rlc_um_base::get_mode()
{
return rlc_mode_t::um;
}
uint32_t rlc_um_base::get_lcid()
{
return lcid;
}
bool rlc_um_base::is_mrb()
{
return cfg.um.is_mrb;
}
void rlc_um_base::reestablish()
{
tx_enabled = false;
if (tx) {
tx->reestablish(); // calls stop and enables tx again
}
if (rx) {
rx->reestablish(); // nothing else needed
}
tx_enabled = true;
}
void rlc_um_base::empty_queue()
{
// Drop all messages in TX SDU queue
if (tx) {
tx->empty_queue();
}
}
/****************************************************************************
* PDCP interface
***************************************************************************/
void rlc_um_base::write_sdu(unique_byte_buffer_t sdu)
{
if (not tx_enabled || not tx) {
RlcDebug("RB is currently deactivated. Dropping SDU (%d B)", sdu->N_bytes);
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics.num_lost_sdus++;
return;
}
int sdu_bytes = sdu->N_bytes; //< Store SDU length for book-keeping
if (tx->try_write_sdu(std::move(sdu)) == SRSRAN_SUCCESS) {
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics.num_tx_sdus++;
metrics.num_tx_sdu_bytes += sdu_bytes;
} else {
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics.num_lost_sdus++;
}
}
void rlc_um_base::discard_sdu(uint32_t discard_sn)
{
if (not tx_enabled || not tx) {
RlcDebug("RB is currently deactivated. Ignoring SDU discard (SN=%u)", discard_sn);
return;
}
tx->discard_sdu(discard_sn);
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics.num_lost_sdus++;
}
bool rlc_um_base::sdu_queue_is_full()
{
return tx->sdu_queue_is_full();
}
/****************************************************************************
* MAC interface
***************************************************************************/
bool rlc_um_base::has_data()
{
if (tx) {
return tx->has_data();
}
return false;
}
uint32_t rlc_um_base::get_buffer_state()
{
uint32_t newtx_queue = 0;
uint32_t prio_tx_queue = 0;
get_buffer_state(newtx_queue, prio_tx_queue);
return newtx_queue + prio_tx_queue;
}
void rlc_um_base::get_buffer_state(uint32_t& newtx_queue, uint32_t& prio_tx_queue)
{
newtx_queue = 0;
prio_tx_queue = 0;
if (tx) {
newtx_queue = tx->get_buffer_state();
}
}
uint32_t rlc_um_base::read_pdu(uint8_t* payload, uint32_t nof_bytes)
{
if (tx && tx_enabled) {
uint32_t len = tx->build_data_pdu(payload, nof_bytes);
if (len > 0) {
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics.num_tx_pdu_bytes += len;
metrics.num_tx_pdus++;
}
return len;
}
return 0;
}
void rlc_um_base::write_pdu(uint8_t* payload, uint32_t nof_bytes)
{
if (rx && rx_enabled) {
{
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics.num_rx_pdus++;
metrics.num_rx_pdu_bytes += nof_bytes;
}
rx->handle_data_pdu(payload, nof_bytes);
}
}
rlc_bearer_metrics_t rlc_um_base::get_metrics()
{
std::lock_guard<std::mutex> lock(metrics_mutex);
return metrics;
}
void rlc_um_base::reset_metrics()
{
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics = {};
}
void rlc_um_base::set_bsr_callback(bsr_callback_t callback)
{
tx->set_bsr_callback(std::move(callback));
}
/****************************************************************************
* Helper functions
***************************************************************************/
std::string rlc_um_base::get_rb_name(srsue::rrc_interface_rlc* rrc, uint32_t lcid, bool is_mrb)
{
if (is_mrb) {
std::stringstream ss;
ss << "MRB" << lcid;
return ss.str();
} else {
return rrc->get_rb_name(lcid);
}
}
/****************************************************************************
* Rx subclass implementation (base)
***************************************************************************/
rlc_um_base::rlc_um_base_rx::rlc_um_base_rx(rlc_um_base* parent_) :
pool(parent_->pool),
logger(parent_->logger),
timers(parent_->timers),
pdcp(parent_->pdcp),
rrc(parent_->rrc),
cfg(parent_->cfg),
metrics(parent_->metrics),
lcid(parent_->lcid)
{}
rlc_um_base::rlc_um_base_rx::~rlc_um_base_rx() {}
/****************************************************************************
* Tx subclass implementation (base)
***************************************************************************/
rlc_um_base::rlc_um_base_tx::rlc_um_base_tx(rlc_um_base* parent_) :
logger(parent_->logger), pool(parent_->pool), parent(parent_)
{}
rlc_um_base::rlc_um_base_tx::~rlc_um_base_tx() {}
void rlc_um_base::rlc_um_base_tx::stop()
{
empty_queue();
reset();
}
void rlc_um_base::rlc_um_base_tx::reestablish()
{
stop();
// bearer is enabled in base class
}
void rlc_um_base::rlc_um_base_tx::empty_queue()
{
std::lock_guard<std::mutex> lock(mutex);
// deallocate all SDUs in transmit queue
while (not tx_sdu_queue.is_empty()) {
unique_byte_buffer_t buf = tx_sdu_queue.read();
}
// deallocate SDU that is currently processed
tx_sdu.reset();
}
bool rlc_um_base::rlc_um_base_tx::has_data()
{
return (tx_sdu != nullptr || !tx_sdu_queue.is_empty());
}
void rlc_um_base::rlc_um_base_tx::set_bsr_callback(bsr_callback_t callback)
{
bsr_callback = callback;
}
void rlc_um_base::rlc_um_base_tx::write_sdu(unique_byte_buffer_t sdu)
{
if (sdu) {
RlcHexInfo(sdu->msg, sdu->N_bytes, "Tx SDU (%d B, tx_sdu_queue_len=%d)", sdu->N_bytes, tx_sdu_queue.size());
tx_sdu_queue.write(std::move(sdu));
} else {
RlcWarning("NULL SDU pointer in write_sdu()");
}
}
int rlc_um_base::rlc_um_base_tx::try_write_sdu(unique_byte_buffer_t sdu)
{
if (sdu) {
uint8_t* msg_ptr = sdu->msg;
uint32_t nof_bytes = sdu->N_bytes;
srsran::error_type<unique_byte_buffer_t> ret = tx_sdu_queue.try_write(std::move(sdu));
if (ret) {
RlcHexInfo(msg_ptr, nof_bytes, "Tx SDU (%d B, tx_sdu_queue_len=%d)", nof_bytes, tx_sdu_queue.size());
return SRSRAN_SUCCESS;
} else {
RlcHexWarning(ret.error()->msg,
ret.error()->N_bytes,
"[Dropped SDU] %s Tx SDU (%d B, tx_sdu_queue_len=%d)",
rb_name.c_str(),
ret.error()->N_bytes,
tx_sdu_queue.size());
}
} else {
RlcWarning("NULL SDU pointer in write_sdu()");
}
return SRSRAN_ERROR;
}
void rlc_um_base::rlc_um_base_tx::discard_sdu(uint32_t discard_sn)
{
std::lock_guard<std::mutex> lock(mutex);
bool discarded = tx_sdu_queue.apply_first([&discard_sn, this](unique_byte_buffer_t& sdu) {
if (sdu != nullptr && sdu->md.pdcp_sn == discard_sn) {
tx_sdu_queue.queue.pop_func(sdu);
sdu = nullptr;
}
return false;
});
// Discard fails when the PDCP PDU is already in Tx window.
RlcInfo("%s PDU with PDCP_SN=%d", discarded ? "Discarding" : "Couldn't discard", discard_sn);
}
bool rlc_um_base::rlc_um_base_tx::sdu_queue_is_full()
{
return tx_sdu_queue.is_full();
}
uint32_t rlc_um_base::rlc_um_base_tx::build_data_pdu(uint8_t* payload, uint32_t nof_bytes)
{
unique_byte_buffer_t pdu;
{
std::lock_guard<std::mutex> lock(mutex);
RlcDebug("MAC opportunity - %d bytes", nof_bytes);
if (tx_sdu == nullptr && tx_sdu_queue.is_empty()) {
RlcInfo("No data available to be sent");
return 0;
}
pdu = make_byte_buffer();
if (!pdu || pdu->N_bytes != 0) {
RlcError("Failed to allocate PDU buffer");
return 0;
}
}
return build_data_pdu(std::move(pdu), payload, nof_bytes);
}
} // namespace srsran