diff --git a/lib/include/srslte/upper/rlc.h b/lib/include/srslte/upper/rlc.h index 23bbbe9d2..757770ead 100644 --- a/lib/include/srslte/upper/rlc.h +++ b/lib/include/srslte/upper/rlc.h @@ -46,6 +46,11 @@ public: srsue::rrc_interface_rlc* rrc_, srslte::timer_handler* timers_, uint32_t lcid_); + void init(srsue::pdcp_interface_rlc* pdcp_, + srsue::rrc_interface_rlc* rrc_, + srslte::timer_handler* timers_, + uint32_t lcid_, + bsr_callback_t bsr_callback_); void stop(); void get_metrics(rlc_metrics_t& m); @@ -101,11 +106,16 @@ private: uint32_t default_lcid = 0; + bsr_callback_t bsr_callback = nullptr; + // Timer needed for metrics calculation struct timeval metrics_time[3] = {}; bool valid_lcid(uint32_t lcid); bool valid_lcid_mrb(uint32_t lcid); + + void update_bsr(uint32_t lcid); + void update_bsr_mch(uint32_t lcid); }; } // namespace srslte diff --git a/lib/include/srslte/upper/rlc_am_lte.h b/lib/include/srslte/upper/rlc_am_lte.h index 04bc2fcc6..a35b41db0 100644 --- a/lib/include/srslte/upper/rlc_am_lte.h +++ b/lib/include/srslte/upper/rlc_am_lte.h @@ -91,6 +91,8 @@ public: rlc_bearer_metrics_t get_metrics(); void reset_metrics(); + void set_bsr_callback(bsr_callback_t callback); + private: // Transmitter sub-class class rlc_am_lte_tx : public timer_callback @@ -120,6 +122,8 @@ private: // Interface for Rx subclass void handle_control_pdu(uint8_t* payload, uint32_t nof_bytes); + void set_bsr_callback(bsr_callback_t callback); + private: int build_status_pdu(uint8_t* payload, uint32_t nof_bytes); int build_retx_pdu(uint8_t* payload, uint32_t nof_bytes); @@ -178,6 +182,9 @@ private: srslte::timer_handler::unique_timer poll_retx_timer; srslte::timer_handler::unique_timer status_prohibit_timer; + // Callback function for buffer status report + bsr_callback_t bsr_callback; + // Tx windows std::map tx_window; std::deque retx_queue; diff --git a/lib/include/srslte/upper/rlc_common.h b/lib/include/srslte/upper/rlc_common.h index 2c873a112..d1d2c1ba9 100644 --- a/lib/include/srslte/upper/rlc_common.h +++ b/lib/include/srslte/upper/rlc_common.h @@ -195,6 +195,8 @@ typedef struct { rlc_status_nack_t nacks[RLC_AM_WINDOW_SIZE]; } rlc_am_nr_status_pdu_t; +typedef std::function bsr_callback_t; + /**************************************************************************** * RLC Common interface * Common interface for all RLC entities @@ -276,6 +278,8 @@ public: virtual int read_pdu(uint8_t* payload, uint32_t nof_bytes) = 0; virtual void write_pdu(uint8_t* payload, uint32_t nof_bytes) = 0; + virtual void set_bsr_callback(bsr_callback_t callback) = 0; + private: bool suspended = false; diff --git a/lib/include/srslte/upper/rlc_tm.h b/lib/include/srslte/upper/rlc_tm.h index e606267d2..a54cfa3d0 100644 --- a/lib/include/srslte/upper/rlc_tm.h +++ b/lib/include/srslte/upper/rlc_tm.h @@ -62,6 +62,8 @@ public: int read_pdu(uint8_t* payload, uint32_t nof_bytes) override; void write_pdu(uint8_t* payload, uint32_t nof_bytes) override; + void set_bsr_callback(bsr_callback_t callback) override {} + private: byte_buffer_pool* pool = nullptr; srslte::log_ref log; diff --git a/lib/include/srslte/upper/rlc_um_base.h b/lib/include/srslte/upper/rlc_um_base.h index 9782ffa90..af8d7f09b 100644 --- a/lib/include/srslte/upper/rlc_um_base.h +++ b/lib/include/srslte/upper/rlc_um_base.h @@ -66,6 +66,8 @@ public: rlc_bearer_metrics_t get_metrics(); void reset_metrics(); + void set_bsr_callback(bsr_callback_t callback) {} + protected: // Transmitter sub-class base class rlc_um_base_tx diff --git a/lib/src/upper/rlc.cc b/lib/src/upper/rlc.cc index 9b6e99878..217f28b8c 100644 --- a/lib/src/upper/rlc.cc +++ b/lib/src/upper/rlc.cc @@ -72,6 +72,16 @@ void rlc::init(srsue::pdcp_interface_rlc* pdcp_, add_bearer(default_lcid, rlc_config_t()); } +void rlc::init(srsue::pdcp_interface_rlc* pdcp_, + srsue::rrc_interface_rlc* rrc_, + srslte::timer_handler* timers_, + uint32_t lcid_, + bsr_callback_t bsr_callback_) +{ + init(pdcp_, rrc_, timers_, lcid_); + bsr_callback = bsr_callback_; +} + void rlc::reset_metrics() { for (rlc_map_t::iterator it = rlc_array.begin(); it != rlc_array.end(); ++it) { @@ -140,7 +150,7 @@ void rlc::reestablish(uint32_t lcid) rlc_log->info("Reestablishing LCID %d\n", lcid); rlc_array.at(lcid)->reestablish(); } else { - rlc_log->warning("RLC LCID %d doesn't exist. Deallocating SDU\n", lcid); + rlc_log->warning("RLC LCID %d doesn't exist.\n", lcid); } } @@ -185,6 +195,7 @@ void rlc::write_sdu(uint32_t lcid, unique_byte_buffer_t sdu, bool blocking) if (valid_lcid(lcid)) { rlc_array.at(lcid)->write_sdu_s(std::move(sdu), blocking); + update_bsr(lcid); } else { rlc_log->warning("RLC LCID %d doesn't exist. Deallocating SDU\n", lcid); } @@ -194,6 +205,7 @@ void rlc::write_sdu_mch(uint32_t lcid, unique_byte_buffer_t sdu) { if (valid_lcid_mrb(lcid)) { rlc_array_mrb.at(lcid)->write_sdu(std::move(sdu), false); // write in non-blocking mode by default + update_bsr_mch(lcid); } else { rlc_log->warning("RLC LCID %d doesn't exist. Deallocating SDU\n", lcid); } @@ -216,6 +228,7 @@ void rlc::discard_sdu(uint32_t lcid, uint32_t discard_sn) { if (valid_lcid(lcid)) { rlc_array.at(lcid)->discard_sdu(discard_sn); + update_bsr(lcid); } else { rlc_log->warning("RLC LCID %d doesn't exist. Ignoring discard SDU\n", lcid); } @@ -281,6 +294,7 @@ int rlc::read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) rwlock_read_guard lock(rwlock); if (valid_lcid(lcid)) { ret = rlc_array.at(lcid)->read_pdu(payload, nof_bytes); + update_bsr(lcid); } else { rlc_log->warning("LCID %d doesn't exist.\n", lcid); } @@ -295,6 +309,7 @@ int rlc::read_pdu_mch(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) rwlock_read_guard lock(rwlock); if (valid_lcid_mrb(lcid)) { ret = rlc_array_mrb.at(lcid)->read_pdu(payload, nof_bytes); + update_bsr_mch(lcid); } else { rlc_log->warning("LCID %d doesn't exist.\n", lcid); } @@ -306,6 +321,7 @@ void rlc::write_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) { if (valid_lcid(lcid)) { rlc_array.at(lcid)->write_pdu_s(payload, nof_bytes); + update_bsr(lcid); } else { rlc_log->warning("LCID %d doesn't exist. Dropping PDU.\n", lcid); } @@ -355,7 +371,7 @@ void rlc::add_bearer(uint32_t lcid, const rlc_config_t& cnfg) { rwlock_write_guard lock(rwlock); - rlc_common* rlc_entity = NULL; + rlc_common* rlc_entity = nullptr; if (not valid_lcid(lcid)) { if (cnfg.rat == srslte_rat_t::lte) { @@ -373,6 +389,9 @@ void rlc::add_bearer(uint32_t lcid, const rlc_config_t& cnfg) rlc_log->error("Cannot add RLC entity - invalid mode\n"); return; } + if (rlc_entity != nullptr) { + rlc_entity->set_bsr_callback(bsr_callback); + } #ifdef HAVE_5GNR } else if (cnfg.rat == srslte_rat_t::nr) { switch (cnfg.rlc_mode) { @@ -570,4 +589,22 @@ bool rlc::valid_lcid_mrb(uint32_t lcid) return true; } +void rlc::update_bsr(uint32_t lcid) +{ + if (bsr_callback) { + uint32_t tx_queue = get_buffer_state(lcid); + uint32_t retx_queue = 0; // todo: separate tx_queue and retx_queue + bsr_callback(lcid, tx_queue, retx_queue); + } +} + +void rlc::update_bsr_mch(uint32_t lcid) +{ + if (bsr_callback) { + uint32_t tx_queue = get_total_mch_buffer_state(lcid); + uint32_t retx_queue = 0; // todo: separate tx_queue and retx_queue + bsr_callback(lcid, tx_queue, retx_queue); + } +} + } // namespace srslte diff --git a/lib/src/upper/rlc_am_lte.cc b/lib/src/upper/rlc_am_lte.cc index 121ea4a43..3055c3a87 100644 --- a/lib/src/upper/rlc_am_lte.cc +++ b/lib/src/upper/rlc_am_lte.cc @@ -77,6 +77,11 @@ bool rlc_am_lte::configure(const rlc_config_t& cfg_) 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 @@ -175,6 +180,11 @@ rlc_am_lte::rlc_am_lte_tx::~rlc_am_lte_tx() pthread_mutex_destroy(&mutex); } +void rlc_am_lte::rlc_am_lte_tx::set_bsr_callback(bsr_callback_t callback) +{ + bsr_callback = callback; +} + bool rlc_am_lte::rlc_am_lte_tx::configure(const rlc_config_t& cfg_) { // TODO: add config checks @@ -434,6 +444,10 @@ void rlc_am_lte::rlc_am_lte_tx::timer_expired(uint32_t timeout_id) } } pthread_mutex_unlock(&mutex); + + if (bsr_callback) { + bsr_callback(parent->lcid, get_buffer_state(), 0); + } } void rlc_am_lte::rlc_am_lte_tx::retransmit_random_pdu() diff --git a/srsenb/hdr/stack/upper/rlc.h b/srsenb/hdr/stack/upper/rlc.h index fabc2fd88..a2209fddd 100644 --- a/srsenb/hdr/stack/upper/rlc.h +++ b/srsenb/hdr/stack/upper/rlc.h @@ -87,6 +87,8 @@ private: srsenb::rlc* parent; }; + void update_bsr(uint32_t rnti, uint32_t lcid, uint32_t tx_queue, uint32_t retx_queue); + pthread_rwlock_t rwlock; std::map users; diff --git a/srsenb/src/stack/upper/rlc.cc b/srsenb/src/stack/upper/rlc.cc index ea835f63e..39f6b36b2 100644 --- a/srsenb/src/stack/upper/rlc.cc +++ b/srsenb/src/stack/upper/rlc.cc @@ -57,7 +57,13 @@ void rlc::add_user(uint16_t rnti) pthread_rwlock_rdlock(&rwlock); if (users.count(rnti) == 0) { std::unique_ptr obj(new srslte::rlc(log_h->get_service_name().c_str())); - obj->init(&users[rnti], &users[rnti], timers, RB_ID_SRB0); + obj->init(&users[rnti], + &users[rnti], + timers, + RB_ID_SRB0, + [rnti, this](uint32_t lcid, uint32_t tx_queue, uint32_t retx_queue) { + update_bsr(rnti, lcid, tx_queue, retx_queue); + }); users[rnti].rnti = rnti; users[rnti].pdcp = pdcp; users[rnti].rrc = rrc; @@ -154,6 +160,14 @@ bool rlc::resume_bearer(uint16_t rnti, uint32_t lcid) return result; } +// In the eNodeB, there is no polling for buffer state from the scheduler. +// This function is called by UE RLC instance every time the tx/retx buffers are updated +void rlc::update_bsr(uint32_t rnti, uint32_t lcid, uint32_t tx_queue, uint32_t retx_queue) +{ + log_h->debug("Buffer state: rnti=0x%x, lcid=%d, tx_queue=%d\n", rnti, lcid, tx_queue); + mac->rlc_buffer_state(rnti, lcid, tx_queue, retx_queue); +} + void rlc::read_pdu_pcch(uint8_t* payload, uint32_t buffer_size) { rrc->read_pdu_pcch(payload, buffer_size); @@ -162,23 +176,14 @@ void rlc::read_pdu_pcch(uint8_t* payload, uint32_t buffer_size) int rlc::read_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) { int ret; - uint32_t tx_queue; pthread_rwlock_rdlock(&rwlock); if (users.count(rnti)) { if (rnti != SRSLTE_MRNTI) { - ret = users[rnti].rlc->read_pdu(lcid, payload, nof_bytes); - tx_queue = users[rnti].rlc->get_buffer_state(lcid); + ret = users[rnti].rlc->read_pdu(lcid, payload, nof_bytes); } else { - ret = users[rnti].rlc->read_pdu_mch(lcid, payload, nof_bytes); - tx_queue = users[rnti].rlc->get_total_mch_buffer_state(lcid); + ret = users[rnti].rlc->read_pdu_mch(lcid, payload, nof_bytes); } - // In the eNodeB, there is no polling for buffer state from the scheduler, thus - // communicate buffer state every time a PDU is read - - uint32_t retx_queue = 0; - log_h->debug("Buffer state PDCP: rnti=0x%x, lcid=%d, tx_queue=%d\n", rnti, lcid, tx_queue); - mac->rlc_buffer_state(rnti, lcid, tx_queue, retx_queue); } else { ret = SRSLTE_ERROR; } @@ -191,36 +196,19 @@ void rlc::write_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof pthread_rwlock_rdlock(&rwlock); if (users.count(rnti)) { users[rnti].rlc->write_pdu(lcid, payload, nof_bytes); - - // In the eNodeB, there is no polling for buffer state from the scheduler, thus - // communicate buffer state every time a new PDU is written - uint32_t tx_queue = users[rnti].rlc->get_buffer_state(lcid); - uint32_t retx_queue = 0; - log_h->debug("Buffer state PDCP: rnti=0x%x, lcid=%d, tx_queue=%d\n", rnti, lcid, tx_queue); - mac->rlc_buffer_state(rnti, lcid, tx_queue, retx_queue); } pthread_rwlock_unlock(&rwlock); } void rlc::write_sdu(uint16_t rnti, uint32_t lcid, srslte::unique_byte_buffer_t sdu) { - uint32_t tx_queue; - pthread_rwlock_rdlock(&rwlock); if (users.count(rnti)) { if (rnti != SRSLTE_MRNTI) { users[rnti].rlc->write_sdu(lcid, std::move(sdu), false); - tx_queue = users[rnti].rlc->get_buffer_state(lcid); } else { users[rnti].rlc->write_sdu_mch(lcid, std::move(sdu)); - tx_queue = users[rnti].rlc->get_total_mch_buffer_state(lcid); } - // In the eNodeB, there is no polling for buffer state from the scheduler, thus - // communicate buffer state every time a new SDU is written - - uint32_t retx_queue = 0; - mac->rlc_buffer_state(rnti, lcid, tx_queue, retx_queue); - log_h->info("Buffer state: rnti=0x%x, lcid=%d, tx_queue=%d\n", rnti, lcid, tx_queue); } pthread_rwlock_unlock(&rwlock); } @@ -230,13 +218,6 @@ void rlc::discard_sdu(uint16_t rnti, uint32_t lcid, uint32_t discard_sn) pthread_rwlock_rdlock(&rwlock); if (users.count(rnti)) { users[rnti].rlc->discard_sdu(lcid, discard_sn); - uint32_t tx_queue = users[rnti].rlc->get_buffer_state(lcid); - - // In the eNodeB, there is no polling for buffer state from the scheduler, thus - // communicate buffer state every time a new SDU is discarded - uint32_t retx_queue = 0; - mac->rlc_buffer_state(rnti, lcid, tx_queue, retx_queue); - log_h->info("Buffer state: rnti=0x%x, lcid=%d, tx_queue=%d\n", rnti, lcid, tx_queue); } pthread_rwlock_unlock(&rwlock); }