diff --git a/lib/include/srsran/rlc/rlc_am_data_structs.h b/lib/include/srsran/rlc/rlc_am_data_structs.h index 499b54626..7f56ba431 100644 --- a/lib/include/srsran/rlc/rlc_am_data_structs.h +++ b/lib/include/srsran/rlc/rlc_am_data_structs.h @@ -203,6 +203,7 @@ struct rlc_ringbuffer_base { virtual T& operator[](size_t sn) = 0; virtual size_t size() const = 0; virtual bool empty() const = 0; + virtual bool full() const = 0; virtual void clear() = 0; virtual bool has_sn(uint32_t sn) const = 0; }; @@ -224,6 +225,7 @@ struct rlc_ringbuffer_t : public rlc_ringbuffer_base { } T& operator[](size_t sn) override { return window[sn]; } size_t size() const override { return window.size(); } + bool full() const override { return window.full(); } bool empty() const override { return window.empty(); } void clear() override { window.clear(); } @@ -354,8 +356,8 @@ public: T& push() override { assert(not full()); - T& p = buffer[wpos]; - wpos = (wpos + 1) % WINDOW_SIZE; + T& p = buffer[wpos]; + wpos = (wpos + 1) % WINDOW_SIZE; return p; } diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 155f46787..047227622 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -493,6 +493,7 @@ rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t& retx, uint8 retx.segment_length); // Update & write header + uint32_t retx_sn = retx.sn; uint32_t current_so = 0; rlc_nr_si_field_t si = rlc_nr_si_field_t::full_sdu; if (retx.is_segment) { @@ -506,10 +507,8 @@ rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t& retx, uint8 current_so = retx.current_so; } rlc_am_nr_pdu_header_t new_header = tx_pdu.header; - new_header.p = get_pdu_poll(true, 0); new_header.si = si; new_header.so = current_so; - uint32_t hdr_len = rlc_am_nr_write_data_pdu_header(new_header, payload); // Write payload into PDU uint32_t retx_pdu_payload_size = 0; @@ -520,9 +519,12 @@ rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t& retx, uint8 // RETX SDU segment retx_pdu_payload_size = (retx.so_start + retx.segment_length - retx.current_so); } + retx_queue->pop(); + new_header.p = get_pdu_poll(true, 0); + uint32_t hdr_len = rlc_am_nr_write_data_pdu_header(new_header, payload); uint32_t pdu_bytes = hdr_len + retx_pdu_payload_size; srsran_assert(pdu_bytes <= nof_bytes, "Error calculating hdr_len and pdu_payload_len"); - memcpy(&payload[hdr_len], &tx_pdu.sdu_buf->msg[retx.current_so], retx_pdu_payload_size); + memcpy(&payload[hdr_len], &tx_pdu.sdu_buf->msg[current_so], retx_pdu_payload_size); // Update RETX queue and log retx_queue->pop(); @@ -533,7 +535,7 @@ rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t& retx, uint8 (*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); + 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); debug_state(); @@ -927,13 +929,16 @@ uint8_t rlc_am_nr_tx::get_pdu_poll(bool is_retx, uint32_t sdu_bytes) poll = 1; } } + /* - * - if both the transmission buffer and the retransmission buffer becomes empty (excluding transmitted RLC SDUs -or RLC SDU segments awaiting acknowledgements) after the transmission of the AMD PDU; or + * - if both the transmission buffer and the retransmission buffer becomes empty + * (excluding transmitted RLC SDUs or RLC SDU segments awaiting acknowledgements) + * after the transmission of the AMD PDU; or * - if no new RLC SDU can be transmitted after the transmission of the AMD PDU (e.g. due to window stalling); * - include a poll in the AMD PDU as described below. */ - if (tx_sdu_queue.is_empty() && retx_queue->empty()) { + + if ((tx_sdu_queue.is_empty() && retx_queue->empty()) || tx_window->full()) { poll = 1; } diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index 81cde558f..c2f959460 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -411,7 +411,21 @@ int lost_pdu_test(rlc_am_nr_sn_size_t sn_size) rlc2.write_pdu(retx_buf.msg, retx_buf.N_bytes); + TESTASSERT_EQ(3, rlc2.get_buffer_state()); // Status report shoud be required, as the TX buffers are now empty. + } + { + // Double check status report + byte_buffer_t status_buf; + int len = rlc2.read_pdu(status_buf.msg, 3); + status_buf.N_bytes = len; + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check = {}; + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); + TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. + TESTASSERT_EQ(0, status_check.N_nack); // All PDUs are acked now } // Check statistics @@ -420,8 +434,9 @@ int lost_pdu_test(rlc_am_nr_sn_size_t sn_size) uint32_t total_tx_pdu_bytes1 = (NBUFS + 1) * data_pdu_size; // (NBUFS + 1 RETX) * PDU size uint32_t total_rx_pdu_bytes1 = 2 * status_pdu_ack_size + status_pdu_nack_size; // Two status PDU (one with a NACK) - uint32_t total_tx_pdu_bytes2 = total_rx_pdu_bytes1; // Two status PDU (one with a NACK) - uint32_t total_rx_pdu_bytes2 = (NBUFS)*data_pdu_size; // (NBUFS - 1 Lost + 1 RETX) * PDU size + uint32_t total_tx_pdu_bytes2 = + 3 * status_pdu_ack_size + status_pdu_nack_size; // Three status PDU (one with a NACK, two without) + uint32_t total_rx_pdu_bytes2 = (NBUFS)*data_pdu_size; // (NBUFS - 1 Lost + 1 RETX) * PDU size // SDU metrics TESTASSERT_EQ(5, metrics1.num_tx_sdus); @@ -443,9 +458,9 @@ int lost_pdu_test(rlc_am_nr_sn_size_t sn_size) TESTASSERT_EQ(5, metrics2.num_rx_sdu_bytes); TESTASSERT_EQ(0, metrics2.num_lost_sdus); // SDU metrics - TESTASSERT_EQ(2, metrics2.num_tx_pdus); // Two status PDUs + TESTASSERT_EQ(3, metrics2.num_tx_pdus); // Three status PDUs TESTASSERT_EQ(5, metrics2.num_rx_pdus); // 5 PDUs (6 tx'ed, but one was lost) - TESTASSERT_EQ(total_tx_pdu_bytes2, metrics2.num_tx_pdu_bytes); // Two status PDU (one with a NACK) + TESTASSERT_EQ(total_tx_pdu_bytes2, metrics2.num_tx_pdu_bytes); // Three status PDU (one with a NACK, two without) TESTASSERT_EQ(total_rx_pdu_bytes2, metrics2.num_rx_pdu_bytes); // (NBUFS - 1 Lost + 1 RETX) * PDU size TESTASSERT_EQ(0, metrics2.num_lost_sdus); // No lost SDUs return SRSRAN_SUCCESS; @@ -1421,6 +1436,7 @@ int poll_test_poll_retx() } return SRSRAN_SUCCESS; } + int main() { // Setup the log message spy to intercept error and warning log entries from RLC @@ -1463,10 +1479,5 @@ int main() TESTASSERT(poll_test_poll_pdu() == SRSRAN_SUCCESS); TESTASSERT(poll_test_poll_byte() == SRSRAN_SUCCESS); TESTASSERT(poll_test_poll_retx() == SRSRAN_SUCCESS); - // Test p bit *not* set on RETX with PollPDU - // Test p bit *not* set on RETX with PollBYTE - // Test p bit set on empty TX queue and empty retx queue - // Test p bit *not* set on empty TX queue and empty retx queue - // Test p bit set on window stall return SRSRAN_SUCCESS; }