lib,rlc_am_nr: added handling of nack ranges at RX

master
Pedro Alvarez 3 years ago
parent 31665aa4ec
commit 444783e2f2

@ -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<uint32_t>& retx_sn_set);
void reestablish() final;
void stop() final;

@ -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
*

Loading…
Cancel
Save