Added an RLC AM base class to avoid code duplication in the RLC AM NR entity.

This class is based on a template that receives as argument the
rlc_am_*_tx/rx entities, so that those are different for LTE and NR.

Moved code from rlc_am_lte/nr entities so that they use the new base class.
master
Pedro Alvarez 4 years ago
parent e65bcd7147
commit b15f63f32f

@ -15,6 +15,8 @@
#include "srsran/common/buffer_pool.h" #include "srsran/common/buffer_pool.h"
#include "srsran/common/common.h" #include "srsran/common/common.h"
#include "srsran/common/timers.h"
#include "srsran/interfaces/ue_rrc_interfaces.h"
#include "srsran/rlc/rlc_common.h" #include "srsran/rlc/rlc_common.h"
#include "srsran/upper/byte_buffer_queue.h" #include "srsran/upper/byte_buffer_queue.h"
#include <map> #include <map>
@ -22,20 +24,187 @@
#include <pthread.h> #include <pthread.h>
#include <queue> #include <queue>
namespace srsran { namespace srsue {
class pdcp_interface_rlc;
class rrc_interface_rlc;
///< Add rlc_am_base here } // namespace srsue
namespace srsran {
bool rlc_am_is_control_pdu(uint8_t* payload); bool rlc_am_is_control_pdu(uint8_t* payload);
bool rlc_am_is_control_pdu(byte_buffer_t* pdu); bool rlc_am_is_control_pdu(byte_buffer_t* pdu);
} // namespace srsran /*******************************
* rlc_am_base class
******************************/
template <typename T_rlc_tx, typename T_rlc_rx>
class rlc_am_base : public rlc_common
{
public:
rlc_am_base(srslog::basic_logger& logger,
uint32_t lcid_,
srsue::pdcp_interface_rlc* pdcp_,
srsue::rrc_interface_rlc* rrc_,
srsran::timer_handler* timers_) :
logger(logger), rrc(rrc_), pdcp(pdcp_), timers(timers_), lcid(lcid_), tx(this), rx(this)
{}
namespace srsue { bool configure(const rlc_config_t& cfg_) final
{
// determine bearer name and configure Rx/Tx objects
rb_name = rrc->get_rb_name(lcid);
class pdcp_interface_rlc; // store config
class rrc_interface_rlc; cfg = cfg_;
} // namespace srsue if (not rx.configure(cfg)) {
logger.error("Error configuring bearer (RX)");
return false;
}
if (not tx.configure(cfg)) {
logger.error("Error configuring bearer (TX)");
return false;
}
logger.info("%s configured: t_poll_retx=%d, poll_pdu=%d, poll_byte=%d, max_retx_thresh=%d, "
"t_reordering=%d, t_status_prohibit=%d",
rb_name.c_str(),
cfg.am.t_poll_retx,
cfg.am.poll_pdu,
cfg.am.poll_byte,
cfg.am.max_retx_thresh,
cfg.am.t_reordering,
cfg.am.t_status_prohibit);
return true;
}
void reestablish() final
{
logger.debug("Reestablished bearer %s", rb_name.c_str());
tx.reestablish(); // calls stop and enables tx again
rx.reestablish(); // calls only stop
}
void stop() final
{
logger.debug("Stopped bearer %s", rb_name.c_str());
tx.stop();
rx.stop();
}
void empty_queue() final
{
// Drop all messages in TX SDU queue
tx.empty_queue();
}
rlc_mode_t get_mode() final { return rlc_mode_t::am; }
uint32_t get_bearer() final { return lcid; }
/****************************************************************************
* PDCP interface
***************************************************************************/
void write_sdu(unique_byte_buffer_t sdu) final
{
if (tx.write_sdu(std::move(sdu)) == SRSRAN_SUCCESS) {
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics.num_tx_sdus++;
}
}
void discard_sdu(uint32_t discard_sn) final
{
tx.discard_sdu(discard_sn);
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics.num_lost_sdus++;
}
bool sdu_queue_is_full() final { return tx.sdu_queue_is_full(); }
/****************************************************************************
* MAC interface
***************************************************************************/
bool has_data() final { return tx.has_data(); }
uint32_t get_buffer_state() final { return tx.get_buffer_state(); }
void get_buffer_state(uint32_t& tx_queue, uint32_t& prio_tx_queue) final
{
tx.get_buffer_state(tx_queue, prio_tx_queue);
}
uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes) final
{
uint32_t read_bytes = tx.read_pdu(payload, nof_bytes);
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics.num_tx_pdus++;
metrics.num_tx_pdu_bytes += read_bytes;
return read_bytes;
}
void write_pdu(uint8_t* payload, uint32_t nof_bytes) final
{
rx.write_pdu(payload, nof_bytes);
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics.num_rx_pdus++;
metrics.num_rx_pdu_bytes += nof_bytes;
}
/****************************************************************************
* Metrics
***************************************************************************/
rlc_bearer_metrics_t get_metrics()
{
// update values that aren't calculated on the fly
uint32_t latency = rx.get_sdu_rx_latency_ms();
uint32_t buffered_bytes = rx.get_rx_buffered_bytes();
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics.rx_latency_ms = latency;
metrics.rx_buffered_bytes = buffered_bytes;
return metrics;
}
void reset_metrics()
{
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics = {};
}
// BSR callback
void set_bsr_callback(bsr_callback_t callback) final { tx.set_bsr_callback(callback); }
private:
// Common variables needed/provided by parent class
srsue::rrc_interface_rlc* rrc = nullptr;
srslog::basic_logger& logger;
srsue::pdcp_interface_rlc* pdcp = nullptr;
srsran::timer_handler* timers = nullptr;
uint32_t lcid = 0;
rlc_config_t cfg = {};
std::string rb_name;
static const int poll_periodicity = 8; // After how many data PDUs a status PDU shall be requested
// Rx and Tx objects
friend class rlc_am_lte_tx;
friend class rlc_am_lte_rx;
friend class rlc_am_nr_tx;
friend class rlc_am_nr_rx;
T_rlc_tx tx;
T_rlc_rx rx;
std::mutex metrics_mutex;
rlc_bearer_metrics_t metrics = {};
};
} // namespace srsran
#endif // SRSRAN_RLC_AM_BASE_H #endif // SRSRAN_RLC_AM_BASE_H

@ -80,251 +80,198 @@ private:
size_t rpos = 0; size_t rpos = 0;
}; };
class rlc_am_lte : public rlc_common // Transmitter sub-class
class rlc_am_lte_tx;
class rlc_am_lte_rx;
using rlc_am_lte = rlc_am_base<rlc_am_lte_tx, rlc_am_lte_rx>;
class rlc_am_lte_tx : public timer_callback
{ {
public: public:
rlc_am_lte(srslog::basic_logger& logger, explicit rlc_am_lte_tx(rlc_am_lte* parent_);
uint32_t lcid_, ~rlc_am_lte_tx() = default;
srsue::pdcp_interface_rlc* pdcp_,
srsue::rrc_interface_rlc* rrc_,
srsran::timer_handler* timers_);
bool configure(const rlc_config_t& cfg_); bool configure(const rlc_config_t& cfg_);
void reestablish();
void stop();
void empty_queue(); void empty_queue();
void reestablish();
void stop();
rlc_mode_t get_mode(); int write_sdu(unique_byte_buffer_t sdu);
uint32_t get_bearer(); uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes);
void discard_sdu(uint32_t discard_sn);
// PDCP interface bool sdu_queue_is_full();
void write_sdu(unique_byte_buffer_t sdu);
void discard_sdu(uint32_t pdcp_sn);
bool sdu_queue_is_full();
// MAC interface
bool has_data(); bool has_data();
uint32_t get_buffer_state(); uint32_t get_buffer_state();
void get_buffer_state(uint32_t& tx_queue, uint32_t& prio_tx_queue); void get_buffer_state(uint32_t& tx_queue, uint32_t& prio_tx_queue);
uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes);
void write_pdu(uint8_t* payload, uint32_t nof_bytes);
rlc_bearer_metrics_t get_metrics(); // Timeout callback interface
void reset_metrics(); void timer_expired(uint32_t timeout_id) final;
// Interface for Rx subclass
void handle_control_pdu(uint8_t* payload, uint32_t nof_bytes);
void set_bsr_callback(bsr_callback_t callback); void set_bsr_callback(bsr_callback_t callback);
private: private:
// Transmitter sub-class void stop_nolock();
class rlc_am_lte_tx : public timer_callback
{
public:
rlc_am_lte_tx(rlc_am_lte* parent_);
~rlc_am_lte_tx();
bool configure(const rlc_config_t& cfg_);
void empty_queue(); int build_status_pdu(uint8_t* payload, uint32_t nof_bytes);
void reestablish(); int build_retx_pdu(uint8_t* payload, uint32_t nof_bytes);
void stop(); int build_segment(uint8_t* payload, uint32_t nof_bytes, rlc_amd_retx_t retx);
int build_data_pdu(uint8_t* payload, uint32_t nof_bytes);
void update_notification_ack_info(uint32_t rlc_sn);
int write_sdu(unique_byte_buffer_t sdu); void empty_queue_nolock();
uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes); void debug_state();
void discard_sdu(uint32_t discard_sn);
bool sdu_queue_is_full();
bool has_data(); int required_buffer_size(const rlc_amd_retx_t& retx);
uint32_t get_buffer_state(); void retransmit_pdu(uint32_t sn);
void get_buffer_state(uint32_t& new_tx, uint32_t& prio_tx);
// Timeout callback interface // Helpers
void timer_expired(uint32_t timeout_id); void get_buffer_state_nolock(uint32_t& new_tx, uint32_t& prio_tx);
bool poll_required();
bool do_status();
void check_sn_reached_max_retx(uint32_t sn);
// Interface for Rx subclass rlc_am_lte* parent = nullptr;
void handle_control_pdu(uint8_t* payload, uint32_t nof_bytes); byte_buffer_pool* pool = nullptr;
srslog::basic_logger& logger;
rlc_am_pdu_segment_pool segment_pool;
void set_bsr_callback(bsr_callback_t callback); /****************************************************************************
* Configurable parameters
* Ref: 3GPP TS 36.322 v10.0.0 Section 7
***************************************************************************/
private: rlc_am_config_t cfg = {};
void stop_nolock();
int build_status_pdu(uint8_t* payload, uint32_t nof_bytes); // TX SDU buffers
int build_retx_pdu(uint8_t* payload, uint32_t nof_bytes); byte_buffer_queue tx_sdu_queue;
int build_segment(uint8_t* payload, uint32_t nof_bytes, rlc_amd_retx_t retx); unique_byte_buffer_t tx_sdu;
int build_data_pdu(uint8_t* payload, uint32_t nof_bytes);
void update_notification_ack_info(uint32_t rlc_sn);
void debug_state(); bool tx_enabled = false;
void empty_queue_nolock();
int required_buffer_size(const rlc_amd_retx_t& retx); /****************************************************************************
void retransmit_pdu(uint32_t sn); * State variables and counters
* Ref: 3GPP TS 36.322 v10.0.0 Section 7
***************************************************************************/
void get_buffer_state_nolock(uint32_t& new_tx, uint32_t& prio_tx); // Tx state variables
uint32_t vt_a = 0; // ACK state. SN of next PDU in sequence to be ACKed. Low edge of tx window.
uint32_t vt_ms = RLC_AM_WINDOW_SIZE; // Max send state. High edge of tx window. vt_a + window_size.
uint32_t vt_s = 0; // Send state. SN to be assigned for next PDU.
uint32_t poll_sn = 0; // Poll send state. SN of most recent PDU txed with poll bit set.
// Helpers // Tx counters
bool poll_required(); uint32_t pdu_without_poll = 0;
bool do_status(); uint32_t byte_without_poll = 0;
void check_sn_reached_max_retx(uint32_t sn);
rlc_am_lte* parent = nullptr; rlc_status_pdu_t tx_status;
byte_buffer_pool* pool = nullptr;
srslog::basic_logger& logger;
rlc_am_pdu_segment_pool segment_pool;
/**************************************************************************** /****************************************************************************
* Configurable parameters * Timers
* Ref: 3GPP TS 36.322 v10.0.0 Section 7 * Ref: 3GPP TS 36.322 v10.0.0 Section 7
***************************************************************************/ ***************************************************************************/
rlc_am_config_t cfg = {}; srsran::timer_handler::unique_timer poll_retx_timer;
srsran::timer_handler::unique_timer status_prohibit_timer;
// TX SDU buffers // SDU info for PDCP notifications
byte_buffer_queue tx_sdu_queue; buffered_pdcp_pdu_list undelivered_sdu_info_queue;
unique_byte_buffer_t tx_sdu;
bool tx_enabled = false; // Callback function for buffer status report
bsr_callback_t bsr_callback;
/**************************************************************************** // Tx windows
* State variables and counters rlc_ringbuffer_t<rlc_amd_tx_pdu, RLC_AM_WINDOW_SIZE> tx_window;
* Ref: 3GPP TS 36.322 v10.0.0 Section 7 pdu_retx_queue retx_queue;
***************************************************************************/ pdcp_sn_vector_t notify_info_vec;
// Tx state variables // Mutexes
uint32_t vt_a = 0; // ACK state. SN of next PDU in sequence to be ACKed. Low edge of tx window. std::mutex mutex;
uint32_t vt_ms = RLC_AM_WINDOW_SIZE; // Max send state. High edge of tx window. vt_a + window_size.
uint32_t vt_s = 0; // Send state. SN to be assigned for next PDU.
uint32_t poll_sn = 0; // Poll send state. SN of most recent PDU txed with poll bit set.
// Tx counters // default to RLC SDU queue length
uint32_t pdu_without_poll = 0; const uint32_t MAX_SDUS_PER_RLC_PDU = RLC_TX_QUEUE_LEN;
uint32_t byte_without_poll = 0; };
rlc_status_pdu_t tx_status;
/****************************************************************************
* Timers
* Ref: 3GPP TS 36.322 v10.0.0 Section 7
***************************************************************************/
srsran::timer_handler::unique_timer poll_retx_timer; // Receiver sub-class
srsran::timer_handler::unique_timer status_prohibit_timer; class rlc_am_lte_rx : public timer_callback
{
public:
rlc_am_lte_rx(rlc_am_lte* parent_);
~rlc_am_lte_rx();
// SDU info for PDCP notifications bool configure(rlc_config_t cfg_);
buffered_pdcp_pdu_list undelivered_sdu_info_queue; void reestablish();
void stop();
// Callback function for buffer status report void write_pdu(uint8_t* payload, uint32_t nof_bytes);
bsr_callback_t bsr_callback;
// Tx windows uint32_t get_rx_buffered_bytes(); // returns sum of PDUs in rx_window
rlc_ringbuffer_t<rlc_amd_tx_pdu, RLC_AM_WINDOW_SIZE> tx_window; uint32_t get_sdu_rx_latency_ms();
pdu_retx_queue retx_queue;
pdcp_sn_vector_t notify_info_vec;
// Mutexes // Timeout callback interface
std::mutex mutex; void timer_expired(uint32_t timeout_id);
// default to RLC SDU queue length // Functions needed by Tx subclass to query rx state
const uint32_t MAX_SDUS_PER_RLC_PDU = RLC_TX_QUEUE_LEN; int get_status_pdu_length();
}; int get_status_pdu(rlc_status_pdu_t* status, const uint32_t nof_bytes);
bool get_do_status();
// Receiver sub-class private:
class rlc_am_lte_rx : public timer_callback void handle_data_pdu(uint8_t* payload, uint32_t nof_bytes, rlc_amd_pdu_header_t& header);
{ void handle_data_pdu_segment(uint8_t* payload, uint32_t nof_bytes, rlc_amd_pdu_header_t& header);
public: void reassemble_rx_sdus();
rlc_am_lte_rx(rlc_am_lte* parent_); bool inside_rx_window(const int16_t sn);
~rlc_am_lte_rx(); void debug_state();
void print_rx_segments();
bool configure(rlc_am_config_t cfg_); bool add_segment_and_check(rlc_amd_rx_pdu_segments_t* pdu, rlc_amd_rx_pdu* segment);
void reestablish(); void reset_status();
void stop();
rlc_am_lte* parent = nullptr;
void write_pdu(uint8_t* payload, uint32_t nof_bytes); byte_buffer_pool* pool = nullptr;
srslog::basic_logger& logger;
uint32_t get_rx_buffered_bytes(); // returns sum of PDUs in rx_window
uint32_t get_sdu_rx_latency_ms(); /****************************************************************************
* Configurable parameters
// Timeout callback interface * Ref: 3GPP TS 36.322 v10.0.0 Section 7
void timer_expired(uint32_t timeout_id); ***************************************************************************/
rlc_am_config_t cfg = {};
// Functions needed by Tx subclass to query rx state
int get_status_pdu_length(); // RX SDU buffers
int get_status_pdu(rlc_status_pdu_t* status, const uint32_t nof_bytes); unique_byte_buffer_t rx_sdu;
bool get_do_status();
/****************************************************************************
private: * State variables and counters
void handle_data_pdu(uint8_t* payload, uint32_t nof_bytes, rlc_amd_pdu_header_t& header); * Ref: 3GPP TS 36.322 v10.0.0 Section 7
void handle_data_pdu_segment(uint8_t* payload, uint32_t nof_bytes, rlc_amd_pdu_header_t& header); ***************************************************************************/
void reassemble_rx_sdus();
bool inside_rx_window(const int16_t sn); // Rx state variables
void debug_state(); uint32_t vr_r = 0; // Receive state. SN following last in-sequence received PDU. Low edge of rx window
void print_rx_segments(); uint32_t vr_mr = RLC_AM_WINDOW_SIZE; // Max acceptable receive state. High edge of rx window. vr_r + window size.
bool add_segment_and_check(rlc_amd_rx_pdu_segments_t* pdu, rlc_amd_rx_pdu* segment); uint32_t vr_x = 0; // t_reordering state. SN following PDU which triggered t_reordering.
void reset_status(); uint32_t vr_ms = 0; // Max status tx state. Highest possible value of SN for ACK_SN in status PDU.
uint32_t vr_h = 0; // Highest rx state. SN following PDU with highest SN among rxed PDUs.
rlc_am_lte* parent = nullptr;
byte_buffer_pool* pool = nullptr; // Mutex to protect members
srslog::basic_logger& logger; std::mutex mutex;
/**************************************************************************** // Rx windows
* Configurable parameters rlc_ringbuffer_t<rlc_amd_rx_pdu, RLC_AM_WINDOW_SIZE> rx_window;
* Ref: 3GPP TS 36.322 v10.0.0 Section 7 std::map<uint32_t, rlc_amd_rx_pdu_segments_t> rx_segments;
***************************************************************************/
rlc_am_config_t cfg = {}; bool poll_received = false;
std::atomic<bool> do_status = {false}; // light-weight access from Tx entity
// RX SDU buffers
unique_byte_buffer_t rx_sdu; /****************************************************************************
* Timers
/**************************************************************************** * Ref: 3GPP TS 36.322 v10.0.0 Section 7
* State variables and counters ***************************************************************************/
* Ref: 3GPP TS 36.322 v10.0.0 Section 7
***************************************************************************/ srsran::timer_handler::unique_timer reordering_timer;
// Rx state variables srsran::rolling_average<double> sdu_rx_latency_ms;
uint32_t vr_r = 0; // Receive state. SN following last in-sequence received PDU. Low edge of rx window
uint32_t vr_mr = RLC_AM_WINDOW_SIZE; // Max acceptable receive state. High edge of rx window. vr_r + window size.
uint32_t vr_x = 0; // t_reordering state. SN following PDU which triggered t_reordering.
uint32_t vr_ms = 0; // Max status tx state. Highest possible value of SN for ACK_SN in status PDU.
uint32_t vr_h = 0; // Highest rx state. SN following PDU with highest SN among rxed PDUs.
// Mutex to protect members
std::mutex mutex;
// Rx windows
rlc_ringbuffer_t<rlc_amd_rx_pdu, RLC_AM_WINDOW_SIZE> rx_window;
std::map<uint32_t, rlc_amd_rx_pdu_segments_t> rx_segments;
bool poll_received = false;
std::atomic<bool> do_status = {false}; // light-weight access from Tx entity
/****************************************************************************
* Timers
* Ref: 3GPP TS 36.322 v10.0.0 Section 7
***************************************************************************/
srsran::timer_handler::unique_timer reordering_timer;
srsran::rolling_average<double> sdu_rx_latency_ms;
};
// Common variables needed/provided by parent class
srsue::rrc_interface_rlc* rrc = nullptr;
srslog::basic_logger& logger;
srsue::pdcp_interface_rlc* pdcp = nullptr;
srsran::timer_handler* timers = nullptr;
uint32_t lcid = 0;
rlc_config_t cfg = {};
std::string rb_name;
static const int poll_periodicity = 8; // After how many data PDUs a status PDU shall be requested
// Rx and Tx objects
rlc_am_lte_tx tx;
rlc_am_lte_rx rx;
std::mutex metrics_mutex;
rlc_bearer_metrics_t metrics = {};
}; };
} // namespace srsran } // namespace srsran

@ -25,112 +25,70 @@
namespace srsran { namespace srsran {
class rlc_am_nr : public rlc_common // Transmitter sub-class
class rlc_am_nr_tx;
class rlc_am_nr_rx;
using rlc_am_nr = rlc_am_base<rlc_am_nr_tx, rlc_am_nr_rx>;
// Transmitter sub-class
class rlc_am_nr_tx
{
public:
explicit rlc_am_nr_tx(rlc_am_nr* parent_);
~rlc_am_nr_tx() = default;
bool configure(const rlc_config_t& cfg_);
void stop();
int write_sdu(unique_byte_buffer_t sdu);
uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes);
void discard_sdu(uint32_t discard_sn);
bool sdu_queue_is_full();
void reestablish();
void empty_queue();
bool has_data();
uint32_t get_buffer_state();
void get_buffer_state(uint32_t& tx_queue, uint32_t& prio_tx_queue);
void set_bsr_callback(const bsr_callback_t& callback);
private:
rlc_am_nr* parent = nullptr;
byte_buffer_pool* pool = nullptr;
srslog::basic_logger& logger;
/****************************************************************************
* Configurable parameters
* Ref: 3GPP TS 38.322 v10.0.0 Section 7.4
***************************************************************************/
rlc_am_config_t cfg = {};
};
// Receiver sub-class
class rlc_am_nr_rx
{ {
public: public:
rlc_am_nr(srslog::basic_logger& logger, explicit rlc_am_nr_rx(rlc_am_nr* parent_);
uint32_t lcid_, ~rlc_am_nr_rx() = default;
srsue::pdcp_interface_rlc* pdcp_,
srsue::rrc_interface_rlc* rrc_, bool configure(const rlc_config_t& cfg_);
srsran::timer_handler* timers_); void stop();
bool configure(const rlc_config_t& cfg_) final; void reestablish();
void stop() final;
void write_pdu(uint8_t* payload, uint32_t nof_bytes);
rlc_mode_t get_mode() final; uint32_t get_sdu_rx_latency_ms();
uint32_t get_bearer() final; uint32_t get_rx_buffered_bytes();
void reestablish() final;
void empty_queue() final;
void set_bsr_callback(bsr_callback_t callback) final;
// PDCP interface
void write_sdu(unique_byte_buffer_t sdu) final;
void discard_sdu(uint32_t pdcp_sn) final;
bool sdu_queue_is_full() final;
// MAC interface
bool has_data() final;
uint32_t get_buffer_state() final;
void get_buffer_state(uint32_t& tx_queue, uint32_t& prio_tx_queue) final;
uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes) final;
void write_pdu(uint8_t* payload, uint32_t nof_bytes) final;
rlc_bearer_metrics_t get_metrics() final;
void reset_metrics() final;
private: private:
// Transmitter sub-class rlc_am_nr* parent = nullptr;
class rlc_am_nr_tx byte_buffer_pool* pool = nullptr;
{ srslog::basic_logger& logger;
public:
explicit rlc_am_nr_tx(rlc_am_nr* parent_); /****************************************************************************
~rlc_am_nr_tx() = default; * Configurable parameters
* Ref: 3GPP TS 38.322 v10.0.0 Section 7.4
bool configure(const rlc_am_config_t& cfg_); ***************************************************************************/
void stop(); rlc_am_config_t cfg = {};
int write_sdu(unique_byte_buffer_t sdu);
uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes);
void discard_sdu(uint32_t discard_sn);
bool sdu_queue_is_full();
bool has_data();
uint32_t get_buffer_state();
rlc_am_nr* parent = nullptr;
srslog::basic_logger& logger;
private:
byte_buffer_pool* pool = nullptr;
/****************************************************************************
* Configurable parameters
* Ref: 3GPP TS 38.322 v10.0.0 Section 7.4
***************************************************************************/
rlc_am_config_t cfg = {};
};
// Receiver sub-class
class rlc_am_nr_rx
{
public:
explicit rlc_am_nr_rx(rlc_am_nr* parent_);
~rlc_am_nr_rx() = default;
bool configure(const rlc_am_config_t& cfg_);
void stop();
void write_pdu(uint8_t* payload, uint32_t nof_bytes);
rlc_am_nr* parent = nullptr;
srslog::basic_logger& logger;
private:
byte_buffer_pool* pool = nullptr;
/****************************************************************************
* Configurable parameters
* Ref: 3GPP TS 38.322 v10.0.0 Section 7.4
***************************************************************************/
rlc_am_config_t cfg = {};
};
// Common variables needed/provided by parent class
srsue::rrc_interface_rlc* rrc = nullptr;
srslog::basic_logger& logger;
srsue::pdcp_interface_rlc* pdcp = nullptr;
srsran::timer_handler* timers = nullptr;
uint32_t lcid = 0;
rlc_config_t cfg = {};
std::string rb_name;
static const int poll_periodicity = 8; // After how many data PDUs a status PDU shall be requested
// Rx and Tx objects
rlc_am_nr_tx tx;
rlc_am_nr_rx rx;
rlc_bearer_metrics_t metrics = {};
}; };
} // namespace srsran } // namespace srsran

@ -89,172 +89,11 @@ rlc_amd_tx_pdu::~rlc_amd_tx_pdu()
} }
} }
/*******************************
* rlc_am_lte class
******************************/
rlc_am_lte::rlc_am_lte(srslog::basic_logger& logger,
uint32_t lcid_,
srsue::pdcp_interface_rlc* pdcp_,
srsue::rrc_interface_rlc* rrc_,
srsran::timer_handler* timers_) :
logger(logger), rrc(rrc_), pdcp(pdcp_), timers(timers_), lcid(lcid_), tx(this), rx(this)
{}
// Applies new configuration. Must be just reestablished or initiated
bool rlc_am_lte::configure(const rlc_config_t& cfg_)
{
// determine bearer name and configure Rx/Tx objects
rb_name = rrc->get_rb_name(lcid);
// store config
cfg = cfg_;
if (not rx.configure(cfg.am)) {
logger.error("Error configuring bearer (RX)");
return false;
}
if (not tx.configure(cfg)) {
logger.error("Error configuring bearer (TX)");
return false;
}
logger.info("%s configured: t_poll_retx=%d, poll_pdu=%d, poll_byte=%d, max_retx_thresh=%d, "
"t_reordering=%d, t_status_prohibit=%d",
rb_name.c_str(),
cfg.am.t_poll_retx,
cfg.am.poll_pdu,
cfg.am.poll_byte,
cfg.am.max_retx_thresh,
cfg.am.t_reordering,
cfg.am.t_status_prohibit);
return true;
}
void rlc_am_lte::set_bsr_callback(bsr_callback_t callback)
{
tx.set_bsr_callback(callback);
}
void rlc_am_lte::empty_queue()
{
// Drop all messages in TX SDU queue
tx.empty_queue();
}
void rlc_am_lte::reestablish()
{
logger.debug("Reestablished bearer %s", rb_name.c_str());
tx.reestablish(); // calls stop and enables tx again
rx.reestablish(); // calls only stop
}
void rlc_am_lte::stop()
{
logger.debug("Stopped bearer %s", rb_name.c_str());
tx.stop();
rx.stop();
}
rlc_mode_t rlc_am_lte::get_mode()
{
return rlc_mode_t::am;
}
uint32_t rlc_am_lte::get_bearer()
{
return lcid;
}
rlc_bearer_metrics_t rlc_am_lte::get_metrics()
{
// update values that aren't calculated on the fly
uint32_t latency = rx.get_sdu_rx_latency_ms();
uint32_t buffered_bytes = rx.get_rx_buffered_bytes();
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics.rx_latency_ms = latency;
metrics.rx_buffered_bytes = buffered_bytes;
return metrics;
}
void rlc_am_lte::reset_metrics()
{
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics = {};
}
/****************************************************************************
* PDCP interface
***************************************************************************/
void rlc_am_lte::write_sdu(unique_byte_buffer_t sdu)
{
if (tx.write_sdu(std::move(sdu)) == SRSRAN_SUCCESS) {
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics.num_tx_sdus++;
}
}
void rlc_am_lte::discard_sdu(uint32_t discard_sn)
{
tx.discard_sdu(discard_sn);
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics.num_lost_sdus++;
}
bool rlc_am_lte::sdu_queue_is_full()
{
return tx.sdu_queue_is_full();
}
/****************************************************************************
* MAC interface
***************************************************************************/
bool rlc_am_lte::has_data()
{
return tx.has_data();
}
uint32_t rlc_am_lte::get_buffer_state()
{
return tx.get_buffer_state();
}
void rlc_am_lte::get_buffer_state(uint32_t& tx_queue, uint32_t& prio_tx_queue)
{
tx.get_buffer_state(tx_queue, prio_tx_queue);
}
uint32_t rlc_am_lte::read_pdu(uint8_t* payload, uint32_t nof_bytes)
{
uint32_t read_bytes = tx.read_pdu(payload, nof_bytes);
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics.num_tx_pdus++;
metrics.num_tx_pdu_bytes += read_bytes;
return read_bytes;
}
void rlc_am_lte::write_pdu(uint8_t* payload, uint32_t nof_bytes)
{
rx.write_pdu(payload, nof_bytes);
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics.num_rx_pdus++;
metrics.num_rx_pdu_bytes += nof_bytes;
}
/**************************************************************************** /****************************************************************************
* Tx subclass implementation * Tx subclass implementation
***************************************************************************/ ***************************************************************************/
rlc_am_lte::rlc_am_lte_tx::rlc_am_lte_tx(rlc_am_lte* parent_) : rlc_am_lte_tx::rlc_am_lte_tx(rlc_am_lte* parent_) :
parent(parent_), parent(parent_),
logger(parent_->logger), logger(parent_->logger),
pool(byte_buffer_pool::get_instance()), pool(byte_buffer_pool::get_instance()),
@ -262,14 +101,12 @@ rlc_am_lte::rlc_am_lte_tx::rlc_am_lte_tx(rlc_am_lte* parent_) :
status_prohibit_timer(parent_->timers->get_unique_timer()) status_prohibit_timer(parent_->timers->get_unique_timer())
{} {}
rlc_am_lte::rlc_am_lte_tx::~rlc_am_lte_tx() {} void rlc_am_lte_tx::set_bsr_callback(bsr_callback_t callback)
void rlc_am_lte::rlc_am_lte_tx::set_bsr_callback(bsr_callback_t callback)
{ {
bsr_callback = callback; bsr_callback = callback;
} }
bool rlc_am_lte::rlc_am_lte_tx::configure(const rlc_config_t& cfg_) bool rlc_am_lte_tx::configure(const rlc_config_t& cfg_)
{ {
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
if (cfg_.tx_queue_length > MAX_SDUS_PER_RLC_PDU) { if (cfg_.tx_queue_length > MAX_SDUS_PER_RLC_PDU) {
@ -307,13 +144,13 @@ bool rlc_am_lte::rlc_am_lte_tx::configure(const rlc_config_t& cfg_)
return true; return true;
} }
void rlc_am_lte::rlc_am_lte_tx::stop() void rlc_am_lte_tx::stop()
{ {
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
stop_nolock(); stop_nolock();
} }
void rlc_am_lte::rlc_am_lte_tx::stop_nolock() void rlc_am_lte_tx::stop_nolock()
{ {
empty_queue_nolock(); empty_queue_nolock();
@ -345,13 +182,13 @@ void rlc_am_lte::rlc_am_lte_tx::stop_nolock()
undelivered_sdu_info_queue.clear(); undelivered_sdu_info_queue.clear();
} }
void rlc_am_lte::rlc_am_lte_tx::empty_queue() void rlc_am_lte_tx::empty_queue()
{ {
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
empty_queue_nolock(); empty_queue_nolock();
} }
void rlc_am_lte::rlc_am_lte_tx::empty_queue_nolock() void rlc_am_lte_tx::empty_queue_nolock()
{ {
// deallocate all SDUs in transmit queue // deallocate all SDUs in transmit queue
while (tx_sdu_queue.size() > 0) { while (tx_sdu_queue.size() > 0) {
@ -365,20 +202,20 @@ void rlc_am_lte::rlc_am_lte_tx::empty_queue_nolock()
tx_sdu.reset(); tx_sdu.reset();
} }
void rlc_am_lte::rlc_am_lte_tx::reestablish() void rlc_am_lte_tx::reestablish()
{ {
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
stop_nolock(); stop_nolock();
tx_enabled = true; tx_enabled = true;
} }
bool rlc_am_lte::rlc_am_lte_tx::do_status() bool rlc_am_lte_tx::do_status()
{ {
return parent->rx.get_do_status(); return parent->rx.get_do_status();
} }
// Function is supposed to return as fast as possible // Function is supposed to return as fast as possible
bool rlc_am_lte::rlc_am_lte_tx::has_data() bool rlc_am_lte_tx::has_data()
{ {
return (((do_status() && not status_prohibit_timer.is_running())) || // if we have a status PDU to transmit return (((do_status() && not status_prohibit_timer.is_running())) || // if we have a status PDU to transmit
(not retx_queue.empty()) || // if we have a retransmission (not retx_queue.empty()) || // if we have a retransmission
@ -395,7 +232,7 @@ bool rlc_am_lte::rlc_am_lte_tx::has_data()
* *
* @param sn The SN of the PDU to check * @param sn The SN of the PDU to check
*/ */
void rlc_am_lte::rlc_am_lte_tx::check_sn_reached_max_retx(uint32_t sn) void rlc_am_lte_tx::check_sn_reached_max_retx(uint32_t sn)
{ {
if (tx_window[sn].retx_count == cfg.max_retx_thresh) { if (tx_window[sn].retx_count == cfg.max_retx_thresh) {
logger.warning("%s Signaling max number of reTx=%d for SN=%d", RB_NAME, tx_window[sn].retx_count, sn); logger.warning("%s Signaling max number of reTx=%d for SN=%d", RB_NAME, tx_window[sn].retx_count, sn);
@ -411,27 +248,27 @@ void rlc_am_lte::rlc_am_lte_tx::check_sn_reached_max_retx(uint32_t sn)
} }
} }
uint32_t rlc_am_lte::rlc_am_lte_tx::get_buffer_state() uint32_t rlc_am_lte_tx::get_buffer_state()
{ {
uint32_t new_tx_queue = 0, prio_tx_queue = 0; uint32_t new_tx_queue = 0, prio_tx_queue = 0;
get_buffer_state(new_tx_queue, prio_tx_queue); get_buffer_state(new_tx_queue, prio_tx_queue);
return new_tx_queue + prio_tx_queue; return new_tx_queue + prio_tx_queue;
} }
void rlc_am_lte::rlc_am_lte_tx::get_buffer_state(uint32_t& n_bytes_newtx, uint32_t& n_bytes_prio) void rlc_am_lte_tx::get_buffer_state(uint32_t& n_bytes_newtx, uint32_t& n_bytes_prio)
{ {
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
get_buffer_state_nolock(n_bytes_newtx, n_bytes_prio); get_buffer_state_nolock(n_bytes_newtx, n_bytes_prio);
} }
void rlc_am_lte::rlc_am_lte_tx::get_buffer_state_nolock(uint32_t& n_bytes_newtx, uint32_t& n_bytes_prio) void rlc_am_lte_tx::get_buffer_state_nolock(uint32_t& n_bytes_newtx, uint32_t& n_bytes_prio)
{ {
n_bytes_newtx = 0; n_bytes_newtx = 0;
n_bytes_prio = 0; n_bytes_prio = 0;
uint32_t n_sdus = 0; uint32_t n_sdus = 0;
logger.debug("%s Buffer state - do_status=%s, status_prohibit_running=%s (%d/%d)", logger.debug("%s Buffer state - do_status=%s, status_prohibit_running=%s (%d/%d)",
RB_NAME, parent->rb_name,
do_status() ? "yes" : "no", do_status() ? "yes" : "no",
status_prohibit_timer.is_running() ? "yes" : "no", status_prohibit_timer.is_running() ? "yes" : "no",
status_prohibit_timer.time_elapsed(), status_prohibit_timer.time_elapsed(),
@ -490,7 +327,7 @@ void rlc_am_lte::rlc_am_lte_tx::get_buffer_state_nolock(uint32_t& n_bytes_newtx,
} }
} }
int rlc_am_lte::rlc_am_lte_tx::write_sdu(unique_byte_buffer_t sdu) int rlc_am_lte_tx::write_sdu(unique_byte_buffer_t sdu)
{ {
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
@ -526,7 +363,7 @@ int rlc_am_lte::rlc_am_lte_tx::write_sdu(unique_byte_buffer_t sdu)
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }
void rlc_am_lte::rlc_am_lte_tx::discard_sdu(uint32_t discard_sn) void rlc_am_lte_tx::discard_sdu(uint32_t discard_sn)
{ {
if (!tx_enabled) { if (!tx_enabled) {
return; return;
@ -544,12 +381,12 @@ void rlc_am_lte::rlc_am_lte_tx::discard_sdu(uint32_t discard_sn)
logger.info("%s PDU with PDCP_SN=%d", discarded ? "Discarding" : "Couldn't discard", discard_sn); logger.info("%s PDU with PDCP_SN=%d", discarded ? "Discarding" : "Couldn't discard", discard_sn);
} }
bool rlc_am_lte::rlc_am_lte_tx::sdu_queue_is_full() bool rlc_am_lte_tx::sdu_queue_is_full()
{ {
return tx_sdu_queue.is_full(); return tx_sdu_queue.is_full();
} }
uint32_t rlc_am_lte::rlc_am_lte_tx::read_pdu(uint8_t* payload, uint32_t nof_bytes) uint32_t rlc_am_lte_tx::read_pdu(uint8_t* payload, uint32_t nof_bytes)
{ {
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
@ -587,7 +424,7 @@ uint32_t rlc_am_lte::rlc_am_lte_tx::read_pdu(uint8_t* payload, uint32_t nof_byte
return build_data_pdu(payload, nof_bytes); return build_data_pdu(payload, nof_bytes);
} }
void rlc_am_lte::rlc_am_lte_tx::timer_expired(uint32_t timeout_id) void rlc_am_lte_tx::timer_expired(uint32_t timeout_id)
{ {
std::unique_lock<std::mutex> lock(mutex); std::unique_lock<std::mutex> lock(mutex);
if (poll_retx_timer.is_valid() && poll_retx_timer.id() == timeout_id) { if (poll_retx_timer.is_valid() && poll_retx_timer.id() == timeout_id) {
@ -608,7 +445,7 @@ void rlc_am_lte::rlc_am_lte_tx::timer_expired(uint32_t timeout_id)
} }
} }
void rlc_am_lte::rlc_am_lte_tx::retransmit_pdu(uint32_t sn) void rlc_am_lte_tx::retransmit_pdu(uint32_t sn)
{ {
if (tx_window.empty()) { if (tx_window.empty()) {
logger.warning("%s No PDU to retransmit", RB_NAME); logger.warning("%s No PDU to retransmit", RB_NAME);
@ -647,7 +484,7 @@ void rlc_am_lte::rlc_am_lte_tx::retransmit_pdu(uint32_t sn)
* *
* @return True if a status PDU needs to be requested, false otherwise. * @return True if a status PDU needs to be requested, false otherwise.
*/ */
bool rlc_am_lte::rlc_am_lte_tx::poll_required() bool rlc_am_lte_tx::poll_required()
{ {
if (cfg.poll_pdu > 0 && pdu_without_poll > static_cast<uint32_t>(cfg.poll_pdu)) { if (cfg.poll_pdu > 0 && pdu_without_poll > static_cast<uint32_t>(cfg.poll_pdu)) {
return true; return true;
@ -675,14 +512,14 @@ bool rlc_am_lte::rlc_am_lte_tx::poll_required()
* However, it seems more appropiate to request more often if polling * However, it seems more appropiate to request more often if polling
* is disabled otherwise, e.g. every N PDUs. * is disabled otherwise, e.g. every N PDUs.
*/ */
if (cfg.poll_pdu == 0 && cfg.poll_byte == 0 && vt_s % poll_periodicity == 0) { if (cfg.poll_pdu == 0 && cfg.poll_byte == 0 && vt_s % rlc_am_lte::poll_periodicity == 0) {
return true; return true;
} }
return false; return false;
} }
int rlc_am_lte::rlc_am_lte_tx::build_status_pdu(uint8_t* payload, uint32_t nof_bytes) int rlc_am_lte_tx::build_status_pdu(uint8_t* payload, uint32_t nof_bytes)
{ {
int pdu_len = parent->rx.get_status_pdu(&tx_status, nof_bytes); int pdu_len = parent->rx.get_status_pdu(&tx_status, nof_bytes);
if (pdu_len == SRSRAN_ERROR) { if (pdu_len == SRSRAN_ERROR) {
@ -704,7 +541,7 @@ int rlc_am_lte::rlc_am_lte_tx::build_status_pdu(uint8_t* payload, uint32_t nof_b
return pdu_len; return pdu_len;
} }
int rlc_am_lte::rlc_am_lte_tx::build_retx_pdu(uint8_t* payload, uint32_t nof_bytes) int rlc_am_lte_tx::build_retx_pdu(uint8_t* payload, uint32_t nof_bytes)
{ {
// Check there is at least 1 element before calling front() // Check there is at least 1 element before calling front()
if (retx_queue.empty()) { if (retx_queue.empty()) {
@ -785,7 +622,7 @@ int rlc_am_lte::rlc_am_lte_tx::build_retx_pdu(uint8_t* payload, uint32_t nof_byt
return (ptr - payload) + tx_window[retx.sn].buf->N_bytes; return (ptr - payload) + tx_window[retx.sn].buf->N_bytes;
} }
int rlc_am_lte::rlc_am_lte_tx::build_segment(uint8_t* payload, uint32_t nof_bytes, rlc_amd_retx_t retx) int rlc_am_lte_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 == NULL) {
logger.error("In build_segment: retx.sn=%d has null buffer", retx.sn); logger.error("In build_segment: retx.sn=%d has null buffer", retx.sn);
@ -960,7 +797,7 @@ int rlc_am_lte::rlc_am_lte_tx::build_segment(uint8_t* payload, uint32_t nof_byte
return pdu_len; return pdu_len;
} }
int rlc_am_lte::rlc_am_lte_tx::build_data_pdu(uint8_t* payload, uint32_t nof_bytes) int rlc_am_lte_tx::build_data_pdu(uint8_t* payload, uint32_t nof_bytes)
{ {
if (tx_sdu == NULL && tx_sdu_queue.is_empty()) { if (tx_sdu == NULL && tx_sdu_queue.is_empty()) {
logger.info("No data available to be sent"); logger.info("No data available to be sent");
@ -1176,7 +1013,7 @@ int rlc_am_lte::rlc_am_lte_tx::build_data_pdu(uint8_t* payload, uint32_t nof_byt
return total_len; return total_len;
} }
void rlc_am_lte::rlc_am_lte_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) void rlc_am_lte_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes)
{ {
if (not tx_enabled) { if (not tx_enabled) {
return; return;
@ -1324,7 +1161,7 @@ void rlc_am_lte::rlc_am_lte_tx::handle_control_pdu(uint8_t* payload, uint32_t no
* @tx_pdu: RLC PDU that was ack'ed. * @tx_pdu: RLC PDU that was ack'ed.
* @notify_info_vec: Vector which will keep track of the PDCP PDU SNs that have been fully ack'ed. * @notify_info_vec: Vector which will keep track of the PDCP PDU SNs that have been fully ack'ed.
*/ */
void rlc_am_lte::rlc_am_lte_tx::update_notification_ack_info(uint32_t rlc_sn) void rlc_am_lte_tx::update_notification_ack_info(uint32_t rlc_sn)
{ {
logger.debug("Updating ACK info: RLC SN=%d, number of notified SDU=%ld, number of undelivered SDUs=%ld", logger.debug("Updating ACK info: RLC SN=%d, number of notified SDU=%ld, number of undelivered SDUs=%ld",
rlc_sn, rlc_sn,
@ -1361,12 +1198,12 @@ void rlc_am_lte::rlc_am_lte_tx::update_notification_ack_info(uint32_t rlc_sn)
} }
} }
void rlc_am_lte::rlc_am_lte_tx::debug_state() void rlc_am_lte_tx::debug_state()
{ {
logger.debug("%s vt_a = %d, vt_ms = %d, vt_s = %d, poll_sn = %d", RB_NAME, vt_a, vt_ms, vt_s, poll_sn); logger.debug("%s vt_a = %d, vt_ms = %d, vt_s = %d, poll_sn = %d", RB_NAME, vt_a, vt_ms, vt_s, poll_sn);
} }
int rlc_am_lte::rlc_am_lte_tx::required_buffer_size(const rlc_amd_retx_t& retx) int rlc_am_lte_tx::required_buffer_size(const rlc_amd_retx_t& retx)
{ {
if (!retx.is_segment) { if (!retx.is_segment) {
if (tx_window.has_sn(retx.sn)) { if (tx_window.has_sn(retx.sn)) {
@ -1443,19 +1280,19 @@ int rlc_am_lte::rlc_am_lte_tx::required_buffer_size(const rlc_amd_retx_t& retx)
* Rx subclass implementation * Rx subclass implementation
***************************************************************************/ ***************************************************************************/
rlc_am_lte::rlc_am_lte_rx::rlc_am_lte_rx(rlc_am_lte* parent_) : rlc_am_lte_rx::rlc_am_lte_rx(rlc_am_lte* parent_) :
parent(parent_), parent(parent_),
pool(byte_buffer_pool::get_instance()), pool(byte_buffer_pool::get_instance()),
logger(parent_->logger), logger(parent_->logger),
reordering_timer(parent_->timers->get_unique_timer()) reordering_timer(parent_->timers->get_unique_timer())
{} {}
rlc_am_lte::rlc_am_lte_rx::~rlc_am_lte_rx() {} rlc_am_lte_rx::~rlc_am_lte_rx() {}
bool rlc_am_lte::rlc_am_lte_rx::configure(rlc_am_config_t cfg_) bool rlc_am_lte_rx::configure(rlc_config_t cfg_)
{ {
// TODO: add config checks // TODO: add config checks
cfg = cfg_; cfg = cfg_.am;
// check timers // check timers
if (not reordering_timer.is_valid()) { if (not reordering_timer.is_valid()) {
@ -1471,12 +1308,12 @@ bool rlc_am_lte::rlc_am_lte_rx::configure(rlc_am_config_t cfg_)
return true; return true;
} }
void rlc_am_lte::rlc_am_lte_rx::reestablish() void rlc_am_lte_rx::reestablish()
{ {
stop(); stop();
} }
void rlc_am_lte::rlc_am_lte_rx::stop() void rlc_am_lte_rx::stop()
{ {
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
@ -1508,7 +1345,7 @@ void rlc_am_lte::rlc_am_lte_rx::stop()
* @param nof_bytes Payload length * @param nof_bytes Payload length
* @param header Reference to PDU header (unpacked by caller) * @param header Reference to PDU header (unpacked by caller)
*/ */
void rlc_am_lte::rlc_am_lte_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes, rlc_amd_pdu_header_t& header) void rlc_am_lte_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes, rlc_amd_pdu_header_t& header)
{ {
std::map<uint32_t, rlc_amd_rx_pdu>::iterator it; std::map<uint32_t, rlc_amd_rx_pdu>::iterator it;
@ -1625,9 +1462,7 @@ void rlc_am_lte::rlc_am_lte_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_b
debug_state(); debug_state();
} }
void rlc_am_lte::rlc_am_lte_rx::handle_data_pdu_segment(uint8_t* payload, void rlc_am_lte_rx::handle_data_pdu_segment(uint8_t* payload, uint32_t nof_bytes, rlc_amd_pdu_header_t& header)
uint32_t nof_bytes,
rlc_amd_pdu_header_t& header)
{ {
std::map<uint32_t, rlc_amd_rx_pdu_segments_t>::iterator it; std::map<uint32_t, rlc_amd_rx_pdu_segments_t>::iterator it;
@ -1715,7 +1550,7 @@ void rlc_am_lte::rlc_am_lte_rx::handle_data_pdu_segment(uint8_t* pa
debug_state(); debug_state();
} }
void rlc_am_lte::rlc_am_lte_rx::reassemble_rx_sdus() void rlc_am_lte_rx::reassemble_rx_sdus()
{ {
uint32_t len = 0; uint32_t len = 0;
if (rx_sdu == NULL) { if (rx_sdu == NULL) {
@ -1866,18 +1701,18 @@ void rlc_am_lte::rlc_am_lte_rx::reassemble_rx_sdus()
} }
} }
void rlc_am_lte::rlc_am_lte_rx::reset_status() void rlc_am_lte_rx::reset_status()
{ {
do_status = false; do_status = false;
poll_received = false; poll_received = false;
} }
bool rlc_am_lte::rlc_am_lte_rx::get_do_status() bool rlc_am_lte_rx::get_do_status()
{ {
return do_status.load(std::memory_order_relaxed); return do_status.load(std::memory_order_relaxed);
} }
void rlc_am_lte::rlc_am_lte_rx::write_pdu(uint8_t* payload, const uint32_t nof_bytes) void rlc_am_lte_rx::write_pdu(uint8_t* payload, const uint32_t nof_bytes)
{ {
if (nof_bytes < 1) { if (nof_bytes < 1) {
return; return;
@ -1902,13 +1737,13 @@ void rlc_am_lte::rlc_am_lte_rx::write_pdu(uint8_t* payload, const uint32_t nof_b
} }
} }
uint32_t rlc_am_lte::rlc_am_lte_rx::get_rx_buffered_bytes() uint32_t rlc_am_lte_rx::get_rx_buffered_bytes()
{ {
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
return rx_window.get_buffered_bytes(); return rx_window.get_buffered_bytes();
} }
uint32_t rlc_am_lte::rlc_am_lte_rx::get_sdu_rx_latency_ms() uint32_t rlc_am_lte_rx::get_sdu_rx_latency_ms()
{ {
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
return sdu_rx_latency_ms.value(); return sdu_rx_latency_ms.value();
@ -1919,7 +1754,7 @@ uint32_t rlc_am_lte::rlc_am_lte_rx::get_sdu_rx_latency_ms()
* *
* @param timeout_id * @param timeout_id
*/ */
void rlc_am_lte::rlc_am_lte_rx::timer_expired(uint32_t timeout_id) void rlc_am_lte_rx::timer_expired(uint32_t timeout_id)
{ {
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
if (reordering_timer.is_valid() and reordering_timer.id() == timeout_id) { if (reordering_timer.is_valid() and reordering_timer.id() == timeout_id) {
@ -1946,7 +1781,7 @@ void rlc_am_lte::rlc_am_lte_rx::timer_expired(uint32_t timeout_id)
// Called from Tx object to pack status PDU that doesn't exceed a given size // Called from Tx object to pack status PDU that doesn't exceed a given size
// If lock-acquisition fails, return -1. Otherwise it returns the length of the generated PDU. // If lock-acquisition fails, return -1. Otherwise it returns the length of the generated PDU.
int rlc_am_lte::rlc_am_lte_rx::get_status_pdu(rlc_status_pdu_t* status, const uint32_t max_pdu_size) int rlc_am_lte_rx::get_status_pdu(rlc_status_pdu_t* status, const uint32_t max_pdu_size)
{ {
std::unique_lock<std::mutex> lock(mutex, std::try_to_lock); std::unique_lock<std::mutex> lock(mutex, std::try_to_lock);
if (not lock.owns_lock()) { if (not lock.owns_lock()) {
@ -1999,7 +1834,7 @@ int rlc_am_lte::rlc_am_lte_rx::get_status_pdu(rlc_status_pdu_t* status, const ui
} }
// Called from Tx object to obtain length of the full status PDU // Called from Tx object to obtain length of the full status PDU
int rlc_am_lte::rlc_am_lte_rx::get_status_pdu_length() int rlc_am_lte_rx::get_status_pdu_length()
{ {
std::unique_lock<std::mutex> lock(mutex, std::try_to_lock); std::unique_lock<std::mutex> lock(mutex, std::try_to_lock);
if (not lock.owns_lock()) { if (not lock.owns_lock()) {
@ -2017,7 +1852,7 @@ int rlc_am_lte::rlc_am_lte_rx::get_status_pdu_length()
return rlc_am_packed_length(&status); return rlc_am_packed_length(&status);
} }
void rlc_am_lte::rlc_am_lte_rx::print_rx_segments() void rlc_am_lte_rx::print_rx_segments()
{ {
std::map<uint32_t, rlc_amd_rx_pdu_segments_t>::iterator it; std::map<uint32_t, rlc_amd_rx_pdu_segments_t>::iterator it;
std::stringstream ss; std::stringstream ss;
@ -2033,7 +1868,7 @@ void rlc_am_lte::rlc_am_lte_rx::print_rx_segments()
} }
// NOTE: Preference would be to capture by value, and then move; but header is stack allocated // NOTE: Preference would be to capture by value, and then move; but header is stack allocated
bool rlc_am_lte::rlc_am_lte_rx::add_segment_and_check(rlc_amd_rx_pdu_segments_t* pdu, rlc_amd_rx_pdu* segment) bool rlc_am_lte_rx::add_segment_and_check(rlc_amd_rx_pdu_segments_t* pdu, rlc_amd_rx_pdu* segment)
{ {
// Find segment insertion point in the list of segments // Find segment insertion point in the list of segments
auto it1 = pdu->segments.begin(); auto it1 = pdu->segments.begin();
@ -2213,7 +2048,7 @@ bool rlc_am_lte::rlc_am_lte_rx::add_segment_and_check(rlc_amd_rx_pdu_segments_t*
return true; return true;
} }
bool rlc_am_lte::rlc_am_lte_rx::inside_rx_window(const int16_t sn) bool rlc_am_lte_rx::inside_rx_window(const int16_t sn)
{ {
if (RX_MOD_BASE(sn) >= RX_MOD_BASE(static_cast<int16_t>(vr_r)) && RX_MOD_BASE(sn) < RX_MOD_BASE(vr_mr)) { if (RX_MOD_BASE(sn) >= RX_MOD_BASE(static_cast<int16_t>(vr_r)) && RX_MOD_BASE(sn) < RX_MOD_BASE(vr_mr)) {
return true; return true;
@ -2222,7 +2057,7 @@ bool rlc_am_lte::rlc_am_lte_rx::inside_rx_window(const int16_t sn)
} }
} }
void rlc_am_lte::rlc_am_lte_rx::debug_state() void rlc_am_lte_rx::debug_state()
{ {
logger.debug("%s vr_r = %d, vr_mr = %d, vr_x = %d, vr_ms = %d, vr_h = %d", RB_NAME, vr_r, vr_mr, vr_x, vr_ms, vr_h); logger.debug("%s vr_r = %d, vr_mr = %d, vr_x = %d, vr_ms = %d, vr_h = %d", RB_NAME, vr_r, vr_mr, vr_x, vr_ms, vr_h);
} }

@ -20,197 +20,98 @@
namespace srsran { namespace srsran {
/******************************* /*******************************
* RLC AM NR class * RLC AM NR
******************************/ * Tx subclass implementation
rlc_am_nr::rlc_am_nr(srslog::basic_logger& logger, ***************************************************************************/
uint32_t lcid_, rlc_am_nr_tx::rlc_am_nr_tx(rlc_am_nr* parent_) :
srsue::pdcp_interface_rlc* pdcp_, parent(parent_), logger(parent_->logger), pool(byte_buffer_pool::get_instance())
srsue::rrc_interface_rlc* rrc_,
srsran::timer_handler* timers_) :
logger(logger), rrc(rrc_), pdcp(pdcp_), timers(timers_), lcid(lcid_), tx(this), rx(this)
{} {}
// Applies new configuration. Must be just reestablished or initiated bool rlc_am_nr_tx::configure(const rlc_config_t& cfg_)
bool rlc_am_nr::configure(const rlc_config_t& cfg_)
{ {
// determine bearer name and configure Rx/Tx objects /*
rb_name = rrc->get_rb_name(lcid); if (cfg_.tx_queue_length > MAX_SDUS_PER_RLC_PDU) {
logger.error("Configuring Tx queue length of %d PDUs too big. Maximum value is %d.",
// store config cfg_.tx_queue_length,
cfg = cfg_; MAX_SDUS_PER_RLC_PDU);
return false;
if (not rx.configure(cfg.am)) { }
logger.error("Error configuring bearer (RX)"); */
return false; cfg = cfg_.am;
}
if (not tx.configure(cfg.am)) {
logger.error("Error configuring bearer (TX)");
return false;
}
logger.info("%s configured: t_poll_retx=%d, poll_pdu=%d, poll_byte=%d, max_retx_thresh=%d, "
"t_reordering=%d, t_status_prohibit=%d",
rb_name.c_str(),
cfg.am.t_poll_retx,
cfg.am.poll_pdu,
cfg.am.poll_byte,
cfg.am.max_retx_thresh,
cfg.am.t_reordering,
cfg.am.t_status_prohibit);
return true; return true;
} }
void rlc_am_nr::stop() {} bool rlc_am_nr_tx::has_data()
rlc_mode_t rlc_am_nr::get_mode()
{ {
return rlc_mode_t::am; return true;
} }
uint32_t rlc_am_nr::get_bearer() uint32_t rlc_am_nr_tx::read_pdu(uint8_t* payload, uint32_t nof_bytes)
{ {
return 0; return 0;
} }
void rlc_am_nr::reestablish() {} void rlc_am_nr_tx::reestablish()
void rlc_am_nr::empty_queue() {}
void rlc_am_nr::set_bsr_callback(bsr_callback_t callback) {}
rlc_bearer_metrics_t rlc_am_nr::get_metrics()
{ {
return {}; stop();
} }
void rlc_am_nr::reset_metrics() {} uint32_t rlc_am_nr_tx::get_buffer_state()
/****************************************************************************
* PDCP interface
***************************************************************************/
void rlc_am_nr::write_sdu(unique_byte_buffer_t sdu)
{ {
if (tx.write_sdu(std::move(sdu)) == SRSRAN_SUCCESS) { return 0;
metrics.num_tx_sdus++;
}
} }
void rlc_am_nr::discard_sdu(uint32_t pdcp_sn) void rlc_am_nr_tx::get_buffer_state(uint32_t& tx_queue, uint32_t& prio_tx_queue) {}
{
tx.discard_sdu(pdcp_sn);
metrics.num_lost_sdus++;
}
bool rlc_am_nr::sdu_queue_is_full() int rlc_am_nr_tx::write_sdu(unique_byte_buffer_t sdu)
{ {
return tx.sdu_queue_is_full(); return 0;
} }
/**************************************************************************** void rlc_am_nr_tx::discard_sdu(uint32_t discard_sn) {}
* MAC interface
***************************************************************************/
bool rlc_am_nr::has_data() bool rlc_am_nr_tx::sdu_queue_is_full()
{ {
return tx.has_data(); return false;
}
uint32_t rlc_am_nr::get_buffer_state()
{
return tx.get_buffer_state();
} }
void rlc_am_nr::get_buffer_state(uint32_t& tx_queue, uint32_t& prio_tx_queue) void rlc_am_nr_tx::empty_queue() {}
{
// TODO
tx_queue = tx.get_buffer_state();
prio_tx_queue = 0;
}
uint32_t rlc_am_nr::read_pdu(uint8_t* payload, uint32_t nof_bytes) void rlc_am_nr_tx::set_bsr_callback(const bsr_callback_t& callback) {}
{
uint32_t read_bytes = tx.read_pdu(payload, nof_bytes);
metrics.num_tx_pdus++;
metrics.num_tx_pdu_bytes += read_bytes;
return read_bytes;
}
void rlc_am_nr::write_pdu(uint8_t* payload, uint32_t nof_bytes) void rlc_am_nr_tx::stop() {}
{
rx.write_pdu(payload, nof_bytes);
metrics.num_rx_pdus++;
metrics.num_rx_pdu_bytes += nof_bytes;
}
/**************************************************************************** /****************************************************************************
* Tx subclass implementation * Rx subclass implementation
***************************************************************************/ ***************************************************************************/
rlc_am_nr::rlc_am_nr_tx::rlc_am_nr_tx(rlc_am_nr* parent_) : rlc_am_nr_rx::rlc_am_nr_rx(rlc_am_nr* parent_) :
parent(parent_), logger(parent_->logger), pool(byte_buffer_pool::get_instance()) parent(parent_), pool(byte_buffer_pool::get_instance()), logger(parent_->logger)
{} {}
bool rlc_am_nr::rlc_am_nr_tx::configure(const rlc_am_config_t& cfg_) bool rlc_am_nr_rx::configure(const rlc_config_t& cfg_)
{ {
/* cfg = cfg_.am;
if (cfg_.tx_queue_length > MAX_SDUS_PER_RLC_PDU) {
logger.error("Configuring Tx queue length of %d PDUs too big. Maximum value is %d.",
cfg_.tx_queue_length,
MAX_SDUS_PER_RLC_PDU);
return false;
}
*/
cfg = cfg_;
return true; return true;
} }
int rlc_am_nr::rlc_am_nr_tx::write_sdu(unique_byte_buffer_t sdu) void rlc_am_nr_rx::stop() {}
{
return 0;
}
void rlc_am_nr::rlc_am_nr_tx::discard_sdu(uint32_t sn) void rlc_am_nr_rx::write_pdu(uint8_t* payload, uint32_t nof_bytes) {}
{
return;
}
bool rlc_am_nr::rlc_am_nr_tx::sdu_queue_is_full() void rlc_am_nr_rx::reestablish()
{ {
return false; stop();
} }
bool rlc_am_nr::rlc_am_nr_tx::has_data() uint32_t rlc_am_nr_rx::get_sdu_rx_latency_ms()
{
return true;
}
uint32_t rlc_am_nr::rlc_am_nr_tx::read_pdu(uint8_t* payload, uint32_t nof_bytes)
{ {
return 0; return 0;
} }
uint32_t rlc_am_nr::rlc_am_nr_tx::get_buffer_state() uint32_t rlc_am_nr_rx::get_rx_buffered_bytes()
{ {
return 0; return 0;
} }
/****************************************************************************
* Rx subclass implementation
***************************************************************************/
rlc_am_nr::rlc_am_nr_rx::rlc_am_nr_rx(rlc_am_nr* parent_) :
parent(parent_), pool(byte_buffer_pool::get_instance()), logger(parent_->logger)
{}
bool rlc_am_nr::rlc_am_nr_rx::configure(const rlc_am_config_t& cfg_)
{
cfg = cfg_;
return true;
}
void rlc_am_nr::rlc_am_nr_rx::stop() {}
void rlc_am_nr::rlc_am_nr_rx::write_pdu(uint8_t* payload, uint32_t nof_bytes) {}
} // namespace srsran } // namespace srsran

Loading…
Cancel
Save