lib,rlc_am_nr: add pdu_retx_queue_base, rlc_ringbuffer_base as interfaces

that are independent from templated capacity.
This commit is contained in:
Robert Falkenberg 2022-03-11 10:34:41 +01:00
parent 11ac8c2529
commit 47c1845cdc
5 changed files with 159 additions and 91 deletions

View File

@ -80,7 +80,7 @@ inline const char* enum_to_text(const char* const array[], uint32_t nof_types, u
}
template <class ItemType>
ItemType enum_to_number(ItemType* array, uint32_t nof_types, uint32_t enum_val)
constexpr ItemType enum_to_number(ItemType* array, uint32_t nof_types, uint32_t enum_val)
{
return enum_val >= nof_types ? -1 : array[enum_val];
}

View File

@ -64,9 +64,9 @@ inline std::string to_string(const rlc_am_nr_sn_size_t& sn_size)
constexpr static const char* options[] = {"12 bits", "18 bits"};
return enum_to_text(options, (uint32_t)rlc_mode_t::nulltype, (uint32_t)sn_size);
}
inline uint16_t to_number(const rlc_am_nr_sn_size_t& sn_size)
constexpr uint16_t to_number(const rlc_am_nr_sn_size_t& sn_size)
{
constexpr static uint16_t options[] = {12, 18};
constexpr uint16_t options[] = {12, 18};
return enum_to_number(options, (uint32_t)rlc_mode_t::nulltype, (uint32_t)sn_size);
}
/**
@ -74,7 +74,7 @@ inline uint16_t to_number(const rlc_am_nr_sn_size_t& sn_size)
* @param sn_size Length of the serial number field in bits
* @return cardianlity
*/
inline uint32_t cardinality(const rlc_am_nr_sn_size_t& sn_size)
constexpr uint32_t cardinality(const rlc_am_nr_sn_size_t& sn_size)
{
return (1 << to_number(sn_size));
}

View File

@ -195,25 +195,39 @@ public:
const_iterator end() const { return list.end(); }
};
template <class T>
struct rlc_ringbuffer_base {
virtual ~rlc_ringbuffer_base() = default;
virtual T& add_pdu(size_t sn) = 0;
virtual void remove_pdu(size_t sn) = 0;
virtual T& operator[](size_t sn) = 0;
virtual size_t size() const = 0;
virtual bool empty() const = 0;
virtual void clear() = 0;
virtual bool has_sn(uint32_t sn) const = 0;
};
template <class T, std::size_t WINDOW_SIZE>
struct rlc_ringbuffer_t {
T& add_pdu(size_t sn)
struct rlc_ringbuffer_t : public rlc_ringbuffer_base<T> {
~rlc_ringbuffer_t() = default;
T& add_pdu(size_t sn) override
{
srsran_expect(not has_sn(sn), "The same SN=%zd should not be added twice", sn);
window.overwrite(sn, T(sn));
return window[sn];
}
void remove_pdu(size_t sn)
void remove_pdu(size_t sn) override
{
srsran_expect(has_sn(sn), "The removed SN=%zd is not in the window", sn);
window.erase(sn);
}
T& operator[](size_t sn) { return window[sn]; }
size_t size() const { return window.size(); }
bool empty() const { return window.empty(); }
void clear() { window.clear(); }
T& operator[](size_t sn) override { return window[sn]; }
size_t size() const override { return window.size(); }
bool empty() const override { return window.empty(); }
void clear() override { window.clear(); }
bool has_sn(uint32_t sn) const { return window.contains(sn); }
bool has_sn(uint32_t sn) const override { return window.contains(sn); }
// Return the sum data bytes of all active PDUs (check PDU is non-null)
uint32_t get_buffered_bytes()
@ -316,11 +330,28 @@ struct rlc_amd_retx_nr_t {
uint32_t current_so;
};
template <class T, std::size_t WINDOW_SIZE>
class pdu_retx_queue
template <class T>
class pdu_retx_queue_base
{
public:
T& push()
virtual ~pdu_retx_queue_base() = default;
virtual T& push() = 0;
virtual void pop() = 0;
virtual T& front() = 0;
virtual void clear() = 0;
virtual bool has_sn(uint32_t sn) const = 0;
virtual size_t size() const = 0;
virtual bool empty() const = 0;
virtual bool full() const = 0;
};
template <class T, std::size_t WINDOW_SIZE>
class pdu_retx_queue : public pdu_retx_queue_base<T>
{
public:
~pdu_retx_queue() = default;
T& push() override
{
assert(not full());
T& p = buffer[wpos];
@ -328,21 +359,21 @@ public:
return p;
}
void pop() { rpos = (rpos + 1) % WINDOW_SIZE; }
void pop() override { rpos = (rpos + 1) % WINDOW_SIZE; }
T& front()
T& front() override
{
assert(not empty());
return buffer[rpos];
}
void clear()
void clear() override
{
wpos = 0;
rpos = 0;
}
bool has_sn(uint32_t sn) const
bool has_sn(uint32_t sn) const override
{
for (size_t i = rpos; i != wpos; i = (i + 1) % WINDOW_SIZE) {
if (buffer[i].sn == sn) {
@ -352,9 +383,9 @@ public:
return false;
}
size_t size() const { return (wpos >= rpos) ? wpos - rpos : WINDOW_SIZE + wpos - rpos; }
bool empty() const { return wpos == rpos; }
bool full() const { return size() == WINDOW_SIZE - 1; }
size_t size() const override { return (wpos >= rpos) ? wpos - rpos : WINDOW_SIZE + wpos - rpos; }
bool empty() const override { return wpos == rpos; }
bool full() const override { return size() == WINDOW_SIZE - 1; }
private:
std::array<T, WINDOW_SIZE> buffer;

View File

@ -132,7 +132,7 @@ private:
rlc_am* parent = nullptr;
rlc_am_nr_rx* rx = nullptr;
uint32_t mod_nr = 4096;
uint32_t mod_nr = cardinality(rlc_am_nr_sn_size_t());
inline uint32_t tx_mod_base_nr(uint32_t sn) const;
void check_sn_reached_max_retx(uint32_t sn);
@ -147,10 +147,14 @@ private:
* Ref: 3GPP TS 38.322 version 16.2.0 Section 7.1
***************************************************************************/
struct rlc_am_nr_tx_state_t st = {};
rlc_ringbuffer_t<rlc_amd_tx_pdu_nr, RLC_AM_NR_WINDOW_SIZE> tx_window;
std::unique_ptr<rlc_ringbuffer_base<rlc_amd_tx_pdu_nr> > tx_window =
std::unique_ptr<rlc_ringbuffer_base<rlc_amd_tx_pdu_nr> >(
new rlc_ringbuffer_t<rlc_amd_tx_pdu_nr, cardinality(rlc_am_nr_sn_size_t()) / 2>);
// Queues and buffers
pdu_retx_queue<rlc_amd_retx_nr_t, RLC_AM_NR_WINDOW_SIZE> retx_queue;
std::unique_ptr<pdu_retx_queue_base<rlc_amd_retx_nr_t> > retx_queue =
std::unique_ptr<pdu_retx_queue_base<rlc_amd_retx_nr_t> >(
new pdu_retx_queue<rlc_amd_retx_nr_t, cardinality(rlc_am_nr_sn_size_t()) / 2>);
uint32_t sdu_under_segmentation_sn = INVALID_RLC_SN; // SN of the SDU currently being segmented.
pdcp_sn_vector_t notify_info_vec;
@ -169,7 +173,7 @@ public:
// Getters/Setters
void set_tx_state(const rlc_am_nr_tx_state_t& st_) { st = st_; } // This should only be used for testing.
rlc_am_nr_tx_state_t get_tx_state() { return st; } // This should only be used for testing.
uint32_t get_tx_window_size() { return tx_window.size(); } // This should only be used for testing.
uint32_t get_tx_window_size() { return tx_window->size(); } // This should only be used for testing.
// Debug Helper
void debug_state() const;
@ -250,7 +254,9 @@ private:
uint32_t rx_mod_base_nr(uint32_t sn) const;
// RX Window
rlc_ringbuffer_t<rlc_amd_rx_sdu_nr_t, RLC_AM_NR_WINDOW_SIZE> rx_window;
std::unique_ptr<rlc_ringbuffer_base<rlc_amd_rx_sdu_nr_t> > rx_window =
std::unique_ptr<rlc_ringbuffer_base<rlc_amd_rx_sdu_nr_t> >(
new rlc_ringbuffer_t<rlc_amd_rx_sdu_nr_t, cardinality(rlc_am_nr_sn_size_t()) / 2>);
// Mutexes
std::mutex mutex;
@ -284,7 +290,7 @@ public:
// Getters/Setters
void set_rx_state(const rlc_am_nr_rx_state_t& st_) { st = st_; } // This should only be used for testing.
rlc_am_nr_rx_state_t get_rx_state() { return st; } // This should only be used for testing.
uint32_t get_rx_window_size() { return rx_window.size(); } // This should only be used for testing.
uint32_t get_rx_window_size() { return rx_window->size(); } // This should only be used for testing.
};
} // namespace srsran

View File

@ -47,8 +47,26 @@ bool rlc_am_nr_tx::configure(const rlc_config_t& cfg_)
}
mod_nr = cardinality(cfg.tx_sn_field_length);
switch (cfg.tx_sn_field_length) {
case rlc_am_nr_sn_size_t::size12bits:
min_hdr_size = 2;
tx_window = std::unique_ptr<rlc_ringbuffer_base<rlc_amd_tx_pdu_nr> >(
new rlc_ringbuffer_t<rlc_amd_tx_pdu_nr, cardinality(rlc_am_nr_sn_size_t::size12bits) / 2>);
retx_queue = std::unique_ptr<pdu_retx_queue_base<rlc_amd_retx_nr_t> >(
new pdu_retx_queue<rlc_amd_retx_nr_t, cardinality(rlc_am_nr_sn_size_t::size12bits) / 2>);
break;
case rlc_am_nr_sn_size_t::size18bits:
min_hdr_size = 3;
tx_window = std::unique_ptr<rlc_ringbuffer_base<rlc_amd_tx_pdu_nr> >(
new rlc_ringbuffer_t<rlc_amd_tx_pdu_nr, cardinality(rlc_am_nr_sn_size_t::size18bits) / 2>);
retx_queue = std::unique_ptr<pdu_retx_queue_base<rlc_amd_retx_nr_t> >(
new pdu_retx_queue<rlc_amd_retx_nr_t, cardinality(rlc_am_nr_sn_size_t::size18bits) / 2>);
break;
default:
RlcError("attempt to configure unsupported tx_sn_field_length %s", to_string(cfg.tx_sn_field_length));
return false;
}
min_hdr_size = cfg.tx_sn_field_length == rlc_am_nr_sn_size_t::size12bits ? 2 : 3;
max_hdr_size = min_hdr_size + so_size;
tx_enabled = true;
@ -83,7 +101,7 @@ uint32_t rlc_am_nr_tx::read_pdu(uint8_t* payload, uint32_t nof_bytes)
RlcDebug("RLC entity not active. Not generating PDU.");
return 0;
}
RlcDebug("MAC opportunity - bytes=%d, tx_window size=%zu PDUs", nof_bytes, tx_window.size());
RlcDebug("MAC opportunity - bytes=%d, tx_window size=%zu PDUs", nof_bytes, tx_window->size());
// Tx STATUS if requested
if (do_status()) {
@ -99,20 +117,20 @@ uint32_t rlc_am_nr_tx::read_pdu(uint8_t* payload, uint32_t nof_bytes)
}
// Retransmit if required
if (not retx_queue.empty()) {
RlcInfo("Re-transmission required. Retransmission queue size: %d", retx_queue.size());
if (not retx_queue->empty()) {
RlcInfo("Re-transmission required. Retransmission queue size: %d", retx_queue->size());
return build_retx_pdu(payload, nof_bytes);
}
// Send remaining segment, if it exists
if (sdu_under_segmentation_sn != INVALID_RLC_SN) {
if (not tx_window.has_sn(sdu_under_segmentation_sn)) {
if (not tx_window->has_sn(sdu_under_segmentation_sn)) {
sdu_under_segmentation_sn = INVALID_RLC_SN;
RlcError("SDU currently being segmented does not exist in tx_window. Aborting segmentation SN=%d",
sdu_under_segmentation_sn);
return 0;
}
return build_continuation_sdu_segment(tx_window[sdu_under_segmentation_sn], payload, nof_bytes);
return build_continuation_sdu_segment((*tx_window)[sdu_under_segmentation_sn], payload, nof_bytes);
}
// Check whether there is something to TX
@ -156,7 +174,7 @@ uint32_t rlc_am_nr_tx::build_new_pdu(uint8_t* payload, uint32_t nof_bytes)
// insert newly assigned SN into window and use reference for in-place operations
// NOTE: from now on, we can't return from this function anymore before increasing tx_next
rlc_amd_tx_pdu_nr& tx_pdu = tx_window.add_pdu(st.tx_next);
rlc_amd_tx_pdu_nr& tx_pdu = tx_window->add_pdu(st.tx_next);
tx_pdu.pdcp_sn = tx_sdu->md.pdcp_sn;
tx_pdu.sdu_buf = srsran::make_byte_buffer();
if (tx_pdu.sdu_buf == nullptr) {
@ -392,19 +410,19 @@ uint32_t rlc_am_nr_tx::build_continuation_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu,
uint32_t rlc_am_nr_tx::build_retx_pdu(uint8_t* payload, uint32_t nof_bytes)
{
// Check there is at least 1 element before calling front()
if (retx_queue.empty()) {
if (retx_queue->empty()) {
RlcError("in build_retx_pdu(): retx_queue is empty");
return 0;
}
rlc_amd_retx_nr_t& retx = retx_queue.front();
rlc_amd_retx_nr_t& retx = retx_queue->front();
// Sanity check - drop any retx SNs not present in tx_window
while (not tx_window.has_sn(retx.sn)) {
while (not tx_window->has_sn(retx.sn)) {
RlcWarning("SN=%d not in tx window. Ignoring retx.", retx.sn);
retx_queue.pop();
if (!retx_queue.empty()) {
retx = retx_queue.front();
retx_queue->pop();
if (!retx_queue->empty()) {
retx = retx_queue->front();
} else {
RlcWarning("empty retx queue, cannot provide retx PDU");
return 0;
@ -443,18 +461,18 @@ uint32_t rlc_am_nr_tx::build_retx_pdu(uint8_t* payload, uint32_t nof_bytes)
uint32_t
rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t& retx, uint8_t* payload, uint32_t nof_bytes)
{
srsran_assert(tx_window.has_sn(retx.sn), "Called %s without checking retx SN", __FUNCTION__);
srsran_assert(tx_window->has_sn(retx.sn), "Called %s without checking retx SN", __FUNCTION__);
srsran_assert(not is_retx_segmentation_required(retx, nof_bytes),
"Called %s without checking if segmentation was required",
__FUNCTION__);
// Get tx_pdu info from tx_window
rlc_amd_tx_pdu_nr& tx_pdu = tx_window[retx.sn];
rlc_amd_tx_pdu_nr& tx_pdu = (*tx_window)[retx.sn];
// Get expected header and payload len
uint32_t expected_hdr_len = get_retx_expected_hdr_len(retx);
uint32_t retx_payload_len =
retx.is_segment ? (retx.so_start + retx.segment_length - retx.current_so) : tx_window[retx.sn].sdu_buf->N_bytes;
uint32_t retx_payload_len = retx.is_segment ? (retx.so_start + retx.segment_length - retx.current_so)
: (*tx_window)[retx.sn].sdu_buf->N_bytes;
srsran_assert(nof_bytes >= (expected_hdr_len + retx_payload_len),
"Called %s but segmentation is required. nof_bytes=%d, expeced_hdr_len=%d, retx_payload_len=%d",
__FUNCTION__,
@ -496,7 +514,7 @@ rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t& retx, uint8
uint32_t retx_pdu_payload_size = 0;
if (not retx.is_segment) {
// RETX full SDU
retx_pdu_payload_size = tx_window[retx.sn].sdu_buf->N_bytes;
retx_pdu_payload_size = (*tx_window)[retx.sn].sdu_buf->N_bytes;
} else {
// RETX SDU segment
retx_pdu_payload_size = (retx.so_start + retx.segment_length - retx.current_so);
@ -506,13 +524,13 @@ rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t& retx, uint8
memcpy(&payload[hdr_len], &tx_pdu.sdu_buf->msg[retx.current_so], retx_pdu_payload_size);
// Update RETX queue and log
retx_queue.pop();
RlcHexInfo(tx_window[retx.sn].sdu_buf->msg,
tx_window[retx.sn].sdu_buf->N_bytes,
retx_queue->pop();
RlcHexInfo((*tx_window)[retx.sn].sdu_buf->msg,
(*tx_window)[retx.sn].sdu_buf->N_bytes,
"Original SDU SN=%d (%d B) (attempt %d/%d)",
retx.sn,
tx_window[retx.sn].sdu_buf->N_bytes,
tx_window[retx.sn].retx_count + 1,
(*tx_window)[retx.sn].sdu_buf->N_bytes,
(*tx_window)[retx.sn].retx_count + 1,
cfg.max_retx_thresh);
RlcHexInfo(payload, pdu_bytes, "RETX PDU SN=%d (%d B)", retx.sn, pdu_bytes);
log_rlc_am_nr_pdu_header_to_string(logger.debug, new_header, rb_name);
@ -534,12 +552,12 @@ rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t& retx, uint8
uint32_t rlc_am_nr_tx::build_retx_pdu_with_segmentation(rlc_amd_retx_nr_t& retx, uint8_t* payload, uint32_t nof_bytes)
{
// Get tx_pdu info from tx_window
srsran_assert(tx_window.has_sn(retx.sn), "Called %s without checking retx SN", __FUNCTION__);
srsran_assert(tx_window->has_sn(retx.sn), "Called %s without checking retx SN", __FUNCTION__);
srsran_assert(is_retx_segmentation_required(retx, nof_bytes),
"Called %s without checking if segmentation was not required",
__FUNCTION__);
rlc_amd_tx_pdu_nr& tx_pdu = tx_window[retx.sn];
rlc_amd_tx_pdu_nr& tx_pdu = (*tx_window)[retx.sn];
// Is this an SDU segment or a full SDU?
if (not retx.is_segment) {
@ -638,7 +656,7 @@ bool rlc_am_nr_tx::is_retx_segmentation_required(const rlc_amd_retx_nr_t& retx,
segmentation_required = true;
}
} else {
if (nof_bytes < (tx_window[retx.sn].sdu_buf->N_bytes + min_hdr_size)) {
if (nof_bytes < ((*tx_window)[retx.sn].sdu_buf->N_bytes + min_hdr_size)) {
RlcInfo("Segmentation required for RETX. SN=%d", retx.sn);
segmentation_required = true;
}
@ -701,9 +719,9 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes)
return;
}
for (uint32_t sn = st.tx_next_ack; sn < stop_sn; sn++) {
if (tx_window.has_sn(sn)) {
notify_info_vec.push_back(tx_window[sn].pdcp_sn);
tx_window.remove_pdu(sn);
if (tx_window->has_sn(sn)) {
notify_info_vec.push_back((*tx_window)[sn].pdcp_sn);
tx_window->remove_pdu(sn);
st.tx_next_ack = (sn + 1) % mod_nr;
} else {
RlcError("Missing ACKed SN from TX window");
@ -729,8 +747,8 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes)
if (st.tx_next_ack <= status.nacks[nack_idx].nack_sn && status.nacks[nack_idx].nack_sn <= st.tx_next) {
auto nack = status.nacks[nack_idx];
uint32_t nack_sn = nack.nack_sn;
if (tx_window.has_sn(nack_sn)) {
auto& pdu = tx_window[nack_sn];
if (tx_window->has_sn(nack_sn)) {
auto& pdu = (*tx_window)[nack_sn];
if (nack.has_so) {
// NACK'ing missing bytes in SDU segment.
@ -743,7 +761,7 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes)
segm++) {
if (segm->so >= nack.so_start && segm->so <= nack.so_end) {
// TODO: Check if this segment is not already queued for retransmission
rlc_amd_retx_nr_t& retx = retx_queue.push();
rlc_amd_retx_nr_t& retx = retx_queue->push();
retx.sn = nack_sn;
retx.is_segment = true;
retx.so_start = segm->so;
@ -759,8 +777,8 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes)
} else {
// NACK'ing full SDU.
// add to retx queue if it's not already there
if (not retx_queue.has_sn(nack_sn)) {
rlc_amd_retx_nr_t& retx = retx_queue.push();
if (not retx_queue->has_sn(nack_sn)) {
rlc_amd_retx_nr_t& retx = retx_queue->push();
retx.sn = nack_sn;
retx.is_segment = false;
retx.so_start = 0;
@ -776,7 +794,7 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes)
// Process retx_count and inform upper layers if needed
for (uint32_t retx_sn : retx_sn_set) {
auto& pdu = tx_window[retx_sn];
auto& pdu = (*tx_window)[retx_sn];
// Increment retx_count
if (pdu.retx_count == RETX_COUNT_NOT_STARTED) {
// Set retx_count = 0 on first RE-transmission of associated SDU (38.322 Sec. 5.3.2)
@ -811,11 +829,11 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes)
*/
void rlc_am_nr_tx::check_sn_reached_max_retx(uint32_t sn)
{
if (tx_window[sn].retx_count == cfg.max_retx_thresh) {
RlcWarning("Signaling max number of reTx=%d for SN=%d", tx_window[sn].retx_count, sn);
if ((*tx_window)[sn].retx_count == cfg.max_retx_thresh) {
RlcWarning("Signaling max number of reTx=%d for SN=%d", (*tx_window)[sn].retx_count, sn);
parent->rrc->max_retx_attempted();
srsran::pdcp_sn_vector_t pdcp_sns;
pdcp_sns.push_back(tx_window[sn].pdcp_sn);
pdcp_sns.push_back((*tx_window)[sn].pdcp_sn);
parent->pdcp->notify_failure(parent->lcid, pdcp_sns);
std::lock_guard<std::mutex> lock(parent->metrics_mutex);
@ -843,19 +861,19 @@ void rlc_am_nr_tx::get_buffer_state(uint32_t& n_bytes_new, uint32_t& n_bytes_pri
}
// Bytes needed for retx
if (not retx_queue.empty()) {
rlc_amd_retx_nr_t& retx = retx_queue.front();
if (not retx_queue->empty()) {
rlc_amd_retx_nr_t& retx = retx_queue->front();
RlcDebug("buffer state - retx - SN=%d, Segment: %s, %d:%d",
retx.sn,
retx.is_segment ? "true" : "false",
retx.so_start,
retx.so_start + retx.segment_length - 1);
if (tx_window.has_sn(retx.sn)) {
if (tx_window->has_sn(retx.sn)) {
int req_bytes = retx.segment_length;
int hdr_req_bytes = retx.is_segment ? max_hdr_size : min_hdr_size; // Segmentation not supported yet
if (req_bytes <= 0) {
RlcError("in get_buffer_state(): Removing retx with SN=%d from queue", retx.sn);
retx_queue.pop();
retx_queue->pop();
} else {
n_bytes_prio += (req_bytes + hdr_req_bytes);
RlcDebug("buffer state - retx: %d bytes", n_bytes_prio);
@ -969,6 +987,19 @@ bool rlc_am_nr_rx::configure(const rlc_config_t& cfg_)
}
mod_nr = cardinality(cfg.rx_sn_field_length);
switch (cfg.rx_sn_field_length) {
case rlc_am_nr_sn_size_t::size12bits:
rx_window = std::unique_ptr<rlc_ringbuffer_base<rlc_amd_rx_sdu_nr_t> >(
new rlc_ringbuffer_t<rlc_amd_rx_sdu_nr_t, cardinality(rlc_am_nr_sn_size_t::size12bits) / 2>);
break;
case rlc_am_nr_sn_size_t::size18bits:
rx_window = std::unique_ptr<rlc_ringbuffer_base<rlc_amd_rx_sdu_nr_t> >(
new rlc_ringbuffer_t<rlc_amd_rx_sdu_nr_t, cardinality(rlc_am_nr_sn_size_t::size18bits) / 2>);
break;
default:
RlcError("attempt to configure unsupported rx_sn_field_length %s", to_string(cfg.rx_sn_field_length));
return false;
}
RlcDebug("RLC AM NR configured rx entity.");
@ -998,7 +1029,7 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes)
}
// Section 5.2.3.2.2, discard duplicate PDUs
if (rx_window.has_sn(header.sn) && rx_window[header.sn].fully_received) {
if (rx_window->has_sn(header.sn) && (*rx_window)[header.sn].fully_received) {
RlcInfo("discarding duplicate SN=%d", header.sn);
return;
}
@ -1041,8 +1072,8 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes)
uint32_t sn_upd = 0;
uint32_t window_top = st.rx_next + RLC_AM_NR_WINDOW_SIZE;
for (sn_upd = st.rx_highest_status; sn_upd < window_top; ++sn_upd) {
if (rx_window.has_sn(sn_upd)) {
if (not rx_window[sn_upd].fully_received) {
if (rx_window->has_sn(sn_upd)) {
if (not(*rx_window)[sn_upd].fully_received) {
break; // first SDU not fully received
}
} else {
@ -1063,13 +1094,13 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes)
uint32_t sn_upd = 0;
uint32_t window_top = st.rx_next + RLC_AM_NR_WINDOW_SIZE;
for (sn_upd = st.rx_next; sn_upd < window_top; ++sn_upd) {
if (rx_window.has_sn(sn_upd)) {
if (not rx_window[sn_upd].fully_received) {
if (rx_window->has_sn(sn_upd)) {
if (not(*rx_window)[sn_upd].fully_received) {
break; // first SDU not fully received
}
// RX_Next serves as the lower edge of the receiving window
// As such, we remove any SDU from the window if we update this value
rx_window.remove_pdu(sn_upd);
rx_window->remove_pdu(sn_upd);
} else {
break; // first SDU not fully received
}
@ -1101,8 +1132,8 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes)
if (st.rx_next_highest > st.rx_next + 1) {
restart_reassembly_timer = true;
}
if (st.rx_next_highest == st.rx_next + 1 && rx_window.has_sn(st.rx_next + 1) &&
not rx_window[st.rx_next + 1].fully_received) {
if (st.rx_next_highest == st.rx_next + 1 && rx_window->has_sn(st.rx_next + 1) &&
not(*rx_window)[st.rx_next + 1].fully_received) {
restart_reassembly_timer = true;
}
if (restart_reassembly_timer) {
@ -1119,11 +1150,11 @@ int rlc_am_nr_rx::handle_full_data_sdu(const rlc_am_nr_pdu_header_t& header, con
{
uint32_t hdr_len = rlc_am_nr_packed_length(header);
// Full SDU received. Add SDU to Rx Window and copy full PDU into SDU buffer.
rlc_amd_rx_sdu_nr_t& rx_sdu = rx_window.add_pdu(header.sn);
rlc_amd_rx_sdu_nr_t& rx_sdu = rx_window->add_pdu(header.sn);
rx_sdu.buf = srsran::make_byte_buffer();
if (rx_sdu.buf == nullptr) {
RlcError("fatal error. Couldn't allocate PDU in %s.", __FUNCTION__);
rx_window.remove_pdu(header.sn);
rx_window->remove_pdu(header.sn);
return SRSRAN_ERROR;
}
rx_sdu.buf->set_timestamp();
@ -1131,13 +1162,13 @@ int rlc_am_nr_rx::handle_full_data_sdu(const rlc_am_nr_pdu_header_t& header, con
// check available space for payload
if (nof_bytes > rx_sdu.buf->get_tailroom()) {
RlcError("discarding SN=%d of size %d B (available space %d B)", header.sn, nof_bytes, rx_sdu.buf->get_tailroom());
rx_window.remove_pdu(header.sn);
rx_window->remove_pdu(header.sn);
return SRSRAN_ERROR;
}
memcpy(rx_sdu.buf->msg, payload + hdr_len, nof_bytes - hdr_len); // Don't copy header
rx_sdu.buf->N_bytes = nof_bytes - hdr_len;
rx_sdu.fully_received = true;
write_to_upper_layers(parent->lcid, std::move(rx_window[header.sn].buf));
write_to_upper_layers(parent->lcid, std::move((*rx_window)[header.sn].buf));
return SRSRAN_SUCCESS;
}
@ -1162,7 +1193,7 @@ int rlc_am_nr_rx::handle_segment_data_sdu(const rlc_am_nr_pdu_header_t& header,
}
// Add a new SDU to the RX window if necessary
rlc_amd_rx_sdu_nr_t& rx_sdu = rx_window.has_sn(header.sn) ? rx_window[header.sn] : rx_window.add_pdu(header.sn);
rlc_amd_rx_sdu_nr_t& rx_sdu = rx_window->has_sn(header.sn) ? (*rx_window)[header.sn] : rx_window->add_pdu(header.sn);
// Create PDU segment info, to be stored later
rlc_amd_rx_pdu_nr pdu_segment = {};
@ -1185,14 +1216,14 @@ int rlc_am_nr_rx::handle_segment_data_sdu(const rlc_am_nr_pdu_header_t& header,
rx_sdu.buf = srsran::make_byte_buffer();
if (rx_sdu.buf == nullptr) {
RlcError("fatal error. Couldn't allocate PDU in %s.", __FUNCTION__);
rx_window.remove_pdu(header.sn);
rx_window->remove_pdu(header.sn);
return SRSRAN_ERROR;
}
for (const auto& it : rx_sdu.segments) {
memcpy(&rx_sdu.buf->msg[rx_sdu.buf->N_bytes], it.buf->msg, it.buf->N_bytes);
rx_sdu.buf->N_bytes += it.buf->N_bytes;
}
write_to_upper_layers(parent->lcid, std::move(rx_window[header.sn].buf));
write_to_upper_layers(parent->lcid, std::move((*rx_window)[header.sn].buf));
}
return SRSRAN_SUCCESS;
}
@ -1213,20 +1244,20 @@ uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t m
uint32_t i = status->ack_sn;
while (rx_mod_base_nr(i) <= rx_mod_base_nr(st.rx_highest_status)) {
if ((rx_window.has_sn(i) && rx_window[i].fully_received) || i == st.rx_highest_status) {
if ((rx_window->has_sn(i) && (*rx_window)[i].fully_received) || i == st.rx_highest_status) {
// only update ACK_SN if this SN has been fully received, or if we reached the maximum possible SN
status->ack_sn = i;
} else {
if (not rx_window.has_sn(i)) {
if (not rx_window->has_sn(i)) {
// No segment received, NACK the whole SDU
status->nacks[status->N_nack].nack_sn = i;
status->N_nack++;
} else if (not rx_window[i].fully_received) {
} else if (not(*rx_window)[i].fully_received) {
// Some segments were received, but not all.
// NACK non consecutive missing bytes
uint32_t last_so = 0;
bool last_segment_rx = false;
for (auto segm = rx_window[i].segments.begin(); segm != rx_window[i].segments.end(); segm++) {
for (auto segm = (*rx_window)[i].segments.begin(); segm != (*rx_window)[i].segments.end(); segm++) {
if (segm->header.so != last_so) {
// Some bytes were not received
status->nacks[status->N_nack].nack_sn = i;
@ -1301,7 +1332,7 @@ void rlc_am_nr_rx::timer_expired(uint32_t timeout_id)
*/
for (uint32_t tmp_sn = st.rx_next_status_trigger; tmp_sn < st.rx_next_status_trigger + RLC_AM_NR_WINDOW_SIZE;
tmp_sn++) {
if (not rx_window.has_sn(tmp_sn)) {
if (not rx_window->has_sn(tmp_sn)) {
st.rx_highest_status = tmp_sn;
break;
}
@ -1310,7 +1341,7 @@ void rlc_am_nr_rx::timer_expired(uint32_t timeout_id)
if (st.rx_next_highest > st.rx_highest_status + 1) {
restart_reassembly_timer = true;
}
if (st.rx_next_highest == st.rx_highest_status + 1 && not rx_window[st.rx_next_highest].fully_received) {
if (st.rx_next_highest == st.rx_highest_status + 1 && not(*rx_window)[st.rx_next_highest].fully_received) {
restart_reassembly_timer = true;
}
if (restart_reassembly_timer) {