|
|
|
@ -828,106 +828,27 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes)
|
|
|
|
|
std::set<uint32_t> 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<uint32_t>& 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
|
|
|
|
|
*
|
|
|
|
|