diff --git a/lib/include/srsran/rlc/rlc_am_nr.h b/lib/include/srsran/rlc/rlc_am_nr.h index 619a3978e..61bdd88b9 100644 --- a/lib/include/srsran/rlc/rlc_am_nr.h +++ b/lib/include/srsran/rlc/rlc_am_nr.h @@ -95,6 +95,7 @@ public: bool configure(const rlc_config_t& cfg_) final; uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes) final; void handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) final; + void handle_nack(const rlc_status_nack_t& nack, std::set& retx_sn_set); void reestablish() final; void stop() final; diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index a05c8a8e3..b8eb57479 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -828,106 +828,27 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) std::set retx_sn_set; // Set of PDU SNs added for retransmission (no duplicates) for (uint32_t nack_idx = 0; nack_idx < status.nacks.size(); nack_idx++) { if (status.nacks[nack_idx].has_nack_range) { - RlcWarning("Handling NACK ranges is not yet implemented. Ignoring NACK across %d SDU(s) starting from SN=%d", - status.nacks[nack_idx].nack_range, - status.nacks[nack_idx].nack_sn); - continue; - } - if (tx_mod_base_nr(st.tx_next_ack) <= tx_mod_base_nr(status.nacks[nack_idx].nack_sn) && - tx_mod_base_nr(status.nacks[nack_idx].nack_sn) <= tx_mod_base_nr(st.tx_next)) { - RlcDebug("Handling NACK for SN=%d", status.nacks[nack_idx].nack_sn); - 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 (nack.has_so) { - // NACK'ing missing bytes in SDU segment. - // Retransmit all SDU segments within those missing bytes. - if (pdu.segment_list.empty()) { - RlcError("Received NACK with SO, but there is no segment information. SN=%d", nack_sn); - } - bool segment_found = false; - for (const rlc_amd_tx_pdu_nr::pdu_segment& segm : pdu.segment_list) { - if (segm.so >= nack.so_start && segm.so <= nack.so_end) { - if (not retx_queue->has_sn(nack_sn, segm.so)) { - rlc_amd_retx_nr_t& retx = retx_queue->push(); - retx.sn = nack_sn; - retx.is_segment = true; - retx.so_start = segm.so; - retx.current_so = segm.so; - retx.segment_length = segm.payload_len; - retx_sn_set.insert(nack_sn); - RlcInfo("Scheduled RETX of SDU segment SN=%d, so_start=%d, segment_length=%d", - retx.sn, - retx.so_start, - retx.segment_length); - } else { - RlcInfo("Skip already scheduled RETX of SDU segment SN=%d, so_start=%d, segment_length=%d", - nack_sn, - segm.so, - segm.payload_len); - } - segment_found = true; - } - } - if (!segment_found) { - RlcWarning("Could not find segment for NACK_SN=%d. SO_start=%d, SO_end=%d", - status.nacks[nack_idx].nack_sn, - nack.so_start, - nack.so_end); - for (const rlc_amd_tx_pdu_nr::pdu_segment& segm : pdu.segment_list) { - RlcDebug( - "Segments for SN=%d. SO=%d, SO_end=%d", status.nacks[nack_idx].nack_sn, segm.so, segm.payload_len); - } - } - } else { - // NACK'ing full SDU. - // add to retx queue if it's not already there - if (not retx_queue->has_sn(nack_sn)) { - // Have we segmented the SDU already? - if ((*tx_window)[nack_sn].segment_list.empty()) { - rlc_amd_retx_nr_t& retx = retx_queue->push(); - retx.sn = nack_sn; - retx.is_segment = false; - retx.so_start = 0; - retx.current_so = 0; - retx.segment_length = pdu.sdu_buf->N_bytes; - retx_sn_set.insert(nack_sn); - RlcInfo("Scheduled RETX of SDU SN=%d", retx.sn); - } else { - RlcInfo("Scheduled RETX of SDU SN=%d", nack_sn); - retx_sn_set.insert(nack_sn); - for (auto segm : (*tx_window)[nack_sn].segment_list) { - rlc_amd_retx_nr_t& retx = retx_queue->push(); - retx.sn = nack_sn; - retx.is_segment = true; - retx.so_start = segm.so; - retx.current_so = segm.so; - retx.segment_length = segm.payload_len; - RlcInfo("Scheduled RETX of SDU Segment. SN=%d, SO=%d, len=%d", retx.sn, segm.so, segm.payload_len); - } - } - } else { - RlcInfo("RETX queue already has NACK_SN. SDU SN=%d, Tx_Next_Ack=%d, Tx_Next=%d", - status.nacks[nack_idx].nack_sn, - st.tx_next_ack, - st.tx_next); - } + RlcError("Handling NACK ranges is not yet implemented. Ignoring NACK across %d SDU(s) starting from SN=%d", + status.nacks[nack_idx].nack_range, + status.nacks[nack_idx].nack_sn); + for (uint32_t range_sn = status.nacks[nack_idx].nack_sn; + range_sn < status.nacks[nack_idx].nack_sn + status.nacks[nack_idx].nack_range; + range_sn++) { + rlc_status_nack_t nack = {}; + nack.nack_sn = range_sn; + if (range_sn == status.nacks[nack_idx].nack_sn) { + // First SN + nack.so_start = status.nacks[nack_idx].so_start; + } else if (range_sn == (status.nacks[nack_idx].nack_sn + status.nacks[nack_idx].nack_range - 1)) { + // Last SN + nack.so_end = status.nacks[nack_idx].so_end; } - } else { - RlcInfo("TX window does not contain NACK_SN. SDU SN=%d, Tx_Next_Ack=%d, Tx_Next=%d", - status.nacks[nack_idx].nack_sn, - st.tx_next_ack, - st.tx_next); - } // TX window containts NACK SN + handle_nack(nack, retx_sn_set); + } } else { - RlcInfo("RETX not in expected range. SDU SN=%d, Tx_Next_Ack=%d, Tx_Next=%d", - status.nacks[nack_idx].nack_sn, - st.tx_next_ack, - st.tx_next); - } // NACK SN within expected range - } // NACK loop + handle_nack(status.nacks[nack_idx], retx_sn_set); + } + } // Process retx_count and inform upper layers if needed for (uint32_t retx_sn : retx_sn_set) { @@ -952,6 +873,98 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) notify_info_vec.clear(); } +void rlc_am_nr_tx::handle_nack(const rlc_status_nack_t& nack, std::set& retx_sn_set) +{ + if (tx_mod_base_nr(st.tx_next_ack) <= tx_mod_base_nr(nack.nack_sn) && + tx_mod_base_nr(nack.nack_sn) <= tx_mod_base_nr(st.tx_next)) { + RlcDebug("Handling NACK for SN=%d", nack.nack_sn); + if (tx_window->has_sn(nack.nack_sn)) { + auto& pdu = (*tx_window)[nack.nack_sn]; + + if (nack.has_so) { + // NACK'ing missing bytes in SDU segment. + // Retransmit all SDU segments within those missing bytes. + if (pdu.segment_list.empty()) { + RlcError("Received NACK with SO, but there is no segment information. SN=%d", nack.nack_sn); + } + bool segment_found = false; + for (const rlc_amd_tx_pdu_nr::pdu_segment& segm : pdu.segment_list) { + if (segm.so >= nack.so_start && segm.so <= nack.so_end) { + if (not retx_queue->has_sn(nack.nack_sn, segm.so)) { + rlc_amd_retx_nr_t& retx = retx_queue->push(); + retx.sn = nack.nack_sn; + retx.is_segment = true; + retx.so_start = segm.so; + retx.current_so = segm.so; + retx.segment_length = segm.payload_len; + retx_sn_set.insert(nack.nack_sn); + RlcInfo("Scheduled RETX of SDU segment SN=%d, so_start=%d, segment_length=%d", + retx.sn, + retx.so_start, + retx.segment_length); + } else { + RlcInfo("Skip already scheduled RETX of SDU segment SN=%d, so_start=%d, segment_length=%d", + nack.nack_sn, + segm.so, + segm.payload_len); + } + segment_found = true; + } + } + if (!segment_found) { + RlcWarning("Could not find segment for NACK_SN=%d. SO_start=%d, SO_end=%d", + nack.nack_sn, + nack.so_start, + nack.so_end); + for (const rlc_amd_tx_pdu_nr::pdu_segment& segm : pdu.segment_list) { + RlcDebug("Segments for SN=%d. SO=%d, SO_end=%d", nack.nack_sn, segm.so, segm.payload_len); + } + } + } else { + // NACK'ing full SDU. + // add to retx queue if it's not already there + if (not retx_queue->has_sn(nack.nack_sn)) { + // Have we segmented the SDU already? + if ((*tx_window)[nack.nack_sn].segment_list.empty()) { + rlc_amd_retx_nr_t& retx = retx_queue->push(); + retx.sn = nack.nack_sn; + retx.is_segment = false; + retx.so_start = 0; + retx.current_so = 0; + retx.segment_length = pdu.sdu_buf->N_bytes; + retx_sn_set.insert(nack.nack_sn); + RlcInfo("Scheduled RETX of SDU SN=%d", retx.sn); + } else { + RlcInfo("Scheduled RETX of SDU SN=%d", nack.nack_sn); + retx_sn_set.insert(nack.nack_sn); + for (auto segm : (*tx_window)[nack.nack_sn].segment_list) { + rlc_amd_retx_nr_t& retx = retx_queue->push(); + retx.sn = nack.nack_sn; + retx.is_segment = true; + retx.so_start = segm.so; + retx.current_so = segm.so; + retx.segment_length = segm.payload_len; + RlcInfo("Scheduled RETX of SDU Segment. SN=%d, SO=%d, len=%d", retx.sn, segm.so, segm.payload_len); + } + } + } else { + RlcInfo("RETX queue already has NACK_SN. SDU SN=%d, Tx_Next_Ack=%d, Tx_Next=%d", + nack.nack_sn, + st.tx_next_ack, + st.tx_next); + } + } + } else { + RlcInfo("TX window does not contain NACK_SN. SDU SN=%d, Tx_Next_Ack=%d, Tx_Next=%d", + nack.nack_sn, + st.tx_next_ack, + st.tx_next); + } // TX window containts NACK SN + } else { + RlcInfo( + "RETX not in expected range. SDU SN=%d, Tx_Next_Ack=%d, Tx_Next=%d", nack.nack_sn, st.tx_next_ack, st.tx_next); + } // NACK SN within expected range +} /** * Helper to check if a SN has reached the max reTx threshold *