lib,rlc_am_nr: added initial logic to support receiving PDU segments.

This commit is contained in:
Pedro Alvarez 2021-12-01 17:39:59 +00:00
parent aae1d9ad10
commit 8afea13d9a
3 changed files with 116 additions and 10 deletions

View File

@ -212,6 +212,7 @@ public:
void handle_data_pdu_full(uint8_t* payload, uint32_t nof_bytes, rlc_am_nr_pdu_header_t& header); void handle_data_pdu_full(uint8_t* payload, uint32_t nof_bytes, rlc_am_nr_pdu_header_t& header);
bool inside_rx_window(uint32_t sn); bool inside_rx_window(uint32_t sn);
void write_to_upper_layers(uint32_t lcid, unique_byte_buffer_t sdu); void write_to_upper_layers(uint32_t lcid, unique_byte_buffer_t sdu);
bool have_all_segments_been_received(const std::list<rlc_amd_rx_pdu_nr>& segment_list);
// Metrics // Metrics
uint32_t get_sdu_rx_latency_ms() final; uint32_t get_sdu_rx_latency_ms() final;

View File

@ -313,12 +313,20 @@ int rlc_am_nr_tx::build_continuation_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, uint
// Copy PDU to payload // Copy PDU to payload
memcpy(&payload[hdr_len], &tx_pdu.buf->msg[last_byte], segment_payload_len); memcpy(&payload[hdr_len], &tx_pdu.buf->msg[last_byte], segment_payload_len);
// Store PDU segment info into tx_window
rlc_amd_tx_pdu_nr::pdu_segment segment_info = {};
segment_info.so = last_byte;
segment_info.payload_len = segment_payload_len;
tx_pdu.segment_list.push_back(segment_info);
if (si == rlc_nr_si_field_t::neither_first_nor_last_segment) { if (si == rlc_nr_si_field_t::neither_first_nor_last_segment) {
logger->info("Grant is not large enough for full SDU." logger->info("Grant is not large enough for full SDU."
"Storing SDU segment info"); "Storing SDU segment info");
} else { } else {
logger->info("Grant is large enough for full SDU." logger->info("Grant is large enough for full SDU."
"Removing current SDU info"); "Removing current SDU info");
current_sdu.rlc_sn = INVALID_RLC_SN;
current_sdu.buf = nullptr;
} }
return hdr_len + segment_payload_len; return hdr_len + segment_payload_len;
@ -632,7 +640,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 // Section 5.2.3.2.2, discard duplicate PDUs
if (rx_window.has_sn(header.sn)) { if (rx_window.has_sn(header.sn) && rx_window[header.sn].fully_received) {
logger->info("%s Discarding duplicate SN=%d", parent->rb_name, header.sn); logger->info("%s Discarding duplicate SN=%d", parent->rb_name, header.sn);
return; return;
} }
@ -662,12 +670,65 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes)
rx_sdu.buf->N_bytes = nof_bytes - hdr_len; rx_sdu.buf->N_bytes = nof_bytes - hdr_len;
rx_sdu.fully_received = true; 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));
} else { } else if (header.si == rlc_nr_si_field_t::first_segment) { // Check whether it's a full SDU
// Check if all bytes of the RLC SDU with SN = x are received: logger->info("Initial segment PDU. SN=%d.", header.sn);
// TODO rlc_amd_rx_sdu_nr_t& rx_sdu = rx_window.add_pdu(header.sn);
if (header.si == rlc_nr_si_field_t::first_segment) { // Check whether it's a full SDU
} else if (header.si == rlc_nr_si_field_t::last_segment) { rlc_amd_rx_pdu_nr pdu_segment = {};
pdu_segment.header = header;
pdu_segment.buf = srsran::make_byte_buffer();
if (pdu_segment.buf == nullptr) {
logger->error("Fatal Error: Couldn't allocate PDU in %s.", __FUNCTION__);
rx_window.remove_pdu(header.sn);
return;
}
memcpy(pdu_segment.buf->msg, payload + hdr_len, nof_bytes - hdr_len); // Don't copy header
pdu_segment.buf->N_bytes = nof_bytes - hdr_len;
rx_sdu.segments.push_back(std::move(pdu_segment));
} else if (header.si == rlc_nr_si_field_t::neither_first_nor_last_segment) { } else if (header.si == rlc_nr_si_field_t::neither_first_nor_last_segment) {
logger->info("Middle segment PDU. SN=%d.", 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);
rlc_amd_rx_pdu_nr pdu_segment = {};
pdu_segment.header = header;
pdu_segment.buf = srsran::make_byte_buffer();
if (pdu_segment.buf == nullptr) {
logger->error("Fatal Error: Couldn't allocate PDU in %s.", __FUNCTION__);
rx_window.remove_pdu(header.sn);
return;
}
memcpy(pdu_segment.buf->msg, payload + hdr_len, nof_bytes - hdr_len); // Don't copy header
pdu_segment.buf->N_bytes = nof_bytes - hdr_len;
rx_sdu.segments.push_back(std::move(pdu_segment));
} else if (header.si == rlc_nr_si_field_t::last_segment) {
logger->info("Final segment PDU. SN=%d.", 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);
rlc_amd_rx_pdu_nr pdu_segment = {};
pdu_segment.header = header;
pdu_segment.buf = srsran::make_byte_buffer();
if (pdu_segment.buf == nullptr) {
logger->error("Fatal Error: Couldn't allocate PDU in %s.", __FUNCTION__);
rx_window.remove_pdu(header.sn);
return;
}
memcpy(pdu_segment.buf->msg, payload + hdr_len, nof_bytes - hdr_len); // Don't copy header
pdu_segment.buf->N_bytes = nof_bytes - hdr_len;
rx_sdu.segments.push_back(std::move(pdu_segment));
rx_sdu.fully_received = have_all_segments_been_received(rx_sdu.segments);
if (rx_sdu.fully_received) {
logger->info("Fully received segmented SDU. SN=%d.", header.sn);
rx_sdu.buf = srsran::make_byte_buffer();
if (rx_sdu.buf == nullptr) {
logger->error("Fatal Error: Couldn't allocate PDU in %s.", __FUNCTION__);
rx_window.remove_pdu(header.sn);
return;
}
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));
} }
} }
@ -756,8 +817,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) { if (st.rx_next_highest > st.rx_next + 1) {
restart_reassembly_timer = true; restart_reassembly_timer = true;
} }
if (st.rx_next_highest == st.rx_next + 1 && if (st.rx_next_highest == st.rx_next + 1 && rx_window.has_sn(st.rx_next + 1) &&
rx_window[st.rx_next + 1].fully_received == false) { // TODO: does the last by need to be received? not rx_window[st.rx_next + 1].fully_received) {
restart_reassembly_timer = true; restart_reassembly_timer = true;
} }
if (restart_reassembly_timer) { if (restart_reassembly_timer) {
@ -905,6 +966,27 @@ uint32_t rlc_am_nr_rx::get_rx_buffered_bytes()
return 0; return 0;
} }
bool rlc_am_nr_rx::have_all_segments_been_received(const std::list<rlc_amd_rx_pdu_nr>& segment_list)
{
if (segment_list.empty()) {
return false;
}
// Check if we have received the last segment
if ((--segment_list.end())->header.si != rlc_nr_si_field_t::last_segment) {
return false;
}
// Check if all segments have been received
uint32_t next_byte = 0;
for (const auto& it : segment_list) {
if (it.header.so != next_byte) {
return false;
}
next_byte += it.buf->N_bytes;
}
return true;
}
/* /*
* Debug Helpers * Debug Helpers
*/ */

View File

@ -371,7 +371,8 @@ int basic_segmentation_test()
rlc1.write_sdu(std::move(sdu)); rlc1.write_sdu(std::move(sdu));
// Read 3 PDUs // Read 3 PDUs
unique_byte_buffer_t pdu_bufs[3]; constexpr uint16_t n_pdus = 3;
unique_byte_buffer_t pdu_bufs[n_pdus];
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
pdu_bufs[i] = srsran::make_byte_buffer(); pdu_bufs[i] = srsran::make_byte_buffer();
TESTASSERT(nullptr != pdu_bufs[i]); TESTASSERT(nullptr != pdu_bufs[i]);
@ -384,6 +385,28 @@ int basic_segmentation_test()
} }
} }
// Write 5 PDUs into RLC2
for (int i = 0; i < n_pdus; i++) {
rlc2.write_pdu(pdu_bufs[i]->msg, pdu_bufs[i]->N_bytes); // Don't write RLC_SN=3.
}
// Check statistics
rlc_bearer_metrics_t metrics1 = rlc1.get_metrics();
rlc_bearer_metrics_t metrics2 = rlc2.get_metrics();
// SDU metrics
TESTASSERT_EQ(0, metrics2.num_tx_sdus);
TESTASSERT_EQ(1, metrics2.num_rx_sdus);
TESTASSERT_EQ(0, metrics2.num_tx_sdu_bytes);
TESTASSERT_EQ(3, metrics2.num_rx_sdu_bytes);
TESTASSERT_EQ(0, metrics2.num_lost_sdus);
// PDU metrics
TESTASSERT_EQ(0, metrics2.num_tx_pdus);
TESTASSERT_EQ(3, metrics2.num_rx_pdus); // 5 PDUs (6 tx'ed, but one was lost)
TESTASSERT_EQ(0, metrics2.num_tx_pdu_bytes); // Two status PDU (one with a NACK)
TESTASSERT_EQ(13, metrics2.num_rx_pdu_bytes); // 1 PDU (No SO) + 2 PDUs (with SO) = 3 + 2*5
TESTASSERT_EQ(0, metrics2.num_lost_sdus); // No lost SDUs
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }