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; bool configure(const rlc_config_t& cfg_) final;
uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes) 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_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 reestablish() final;
void stop() final; void stop() final;

@ -828,43 +828,83 @@ 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) 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++) { for (uint32_t nack_idx = 0; nack_idx < status.nacks.size(); nack_idx++) {
if (status.nacks[nack_idx].has_nack_range) { 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", 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_range,
status.nacks[nack_idx].nack_sn); status.nacks[nack_idx].nack_sn);
continue; 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;
}
handle_nack(nack, retx_sn_set);
}
} else {
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) {
auto& pdu = (*tx_window)[retx_sn];
// Increment retx_count
if (pdu.retx_count == RETX_COUNT_NOT_STARTED) {
// Set retx_count = 0 on first RE-transmission of associated SDU (38.322 Sec. 5.3.2)
pdu.retx_count = 0;
} else {
// Increment otherwise
pdu.retx_count++;
} }
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)) { // Inform upper layers if needed
RlcDebug("Handling NACK for SN=%d", status.nacks[nack_idx].nack_sn); check_sn_reached_max_retx(retx_sn);
auto nack = status.nacks[nack_idx]; }
uint32_t nack_sn = nack.nack_sn;
if (tx_window->has_sn(nack_sn)) { // Notify PDCP
auto& pdu = (*tx_window)[nack_sn]; if (not notify_info_vec.empty()) {
parent->pdcp->notify_delivery(parent->lcid, notify_info_vec);
}
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) { if (nack.has_so) {
// NACK'ing missing bytes in SDU segment. // NACK'ing missing bytes in SDU segment.
// Retransmit all SDU segments within those missing bytes. // Retransmit all SDU segments within those missing bytes.
if (pdu.segment_list.empty()) { if (pdu.segment_list.empty()) {
RlcError("Received NACK with SO, but there is no segment information. SN=%d", nack_sn); RlcError("Received NACK with SO, but there is no segment information. SN=%d", nack.nack_sn);
} }
bool segment_found = false; bool segment_found = false;
for (const rlc_amd_tx_pdu_nr::pdu_segment& segm : pdu.segment_list) { 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 (segm.so >= nack.so_start && segm.so <= nack.so_end) {
if (not retx_queue->has_sn(nack_sn, segm.so)) { if (not retx_queue->has_sn(nack.nack_sn, segm.so)) {
rlc_amd_retx_nr_t& retx = retx_queue->push(); rlc_amd_retx_nr_t& retx = retx_queue->push();
retx.sn = nack_sn; retx.sn = nack.nack_sn;
retx.is_segment = true; retx.is_segment = true;
retx.so_start = segm.so; retx.so_start = segm.so;
retx.current_so = segm.so; retx.current_so = segm.so;
retx.segment_length = segm.payload_len; retx.segment_length = segm.payload_len;
retx_sn_set.insert(nack_sn); retx_sn_set.insert(nack.nack_sn);
RlcInfo("Scheduled RETX of SDU segment SN=%d, so_start=%d, segment_length=%d", RlcInfo("Scheduled RETX of SDU segment SN=%d, so_start=%d, segment_length=%d",
retx.sn, retx.sn,
retx.so_start, retx.so_start,
retx.segment_length); retx.segment_length);
} else { } else {
RlcInfo("Skip already scheduled RETX of SDU segment SN=%d, so_start=%d, segment_length=%d", RlcInfo("Skip already scheduled RETX of SDU segment SN=%d, so_start=%d, segment_length=%d",
nack_sn, nack.nack_sn,
segm.so, segm.so,
segm.payload_len); segm.payload_len);
} }
@ -873,34 +913,33 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes)
} }
if (!segment_found) { if (!segment_found) {
RlcWarning("Could not find segment for NACK_SN=%d. SO_start=%d, SO_end=%d", RlcWarning("Could not find segment for NACK_SN=%d. SO_start=%d, SO_end=%d",
status.nacks[nack_idx].nack_sn, nack.nack_sn,
nack.so_start, nack.so_start,
nack.so_end); nack.so_end);
for (const rlc_amd_tx_pdu_nr::pdu_segment& segm : pdu.segment_list) { for (const rlc_amd_tx_pdu_nr::pdu_segment& segm : pdu.segment_list) {
RlcDebug( RlcDebug("Segments for SN=%d. SO=%d, SO_end=%d", nack.nack_sn, segm.so, segm.payload_len);
"Segments for SN=%d. SO=%d, SO_end=%d", status.nacks[nack_idx].nack_sn, segm.so, segm.payload_len);
} }
} }
} else { } else {
// NACK'ing full SDU. // NACK'ing full SDU.
// add to retx queue if it's not already there // add to retx queue if it's not already there
if (not retx_queue->has_sn(nack_sn)) { if (not retx_queue->has_sn(nack.nack_sn)) {
// Have we segmented the SDU already? // Have we segmented the SDU already?
if ((*tx_window)[nack_sn].segment_list.empty()) { if ((*tx_window)[nack.nack_sn].segment_list.empty()) {
rlc_amd_retx_nr_t& retx = retx_queue->push(); rlc_amd_retx_nr_t& retx = retx_queue->push();
retx.sn = nack_sn; retx.sn = nack.nack_sn;
retx.is_segment = false; retx.is_segment = false;
retx.so_start = 0; retx.so_start = 0;
retx.current_so = 0; retx.current_so = 0;
retx.segment_length = pdu.sdu_buf->N_bytes; retx.segment_length = pdu.sdu_buf->N_bytes;
retx_sn_set.insert(nack_sn); retx_sn_set.insert(nack.nack_sn);
RlcInfo("Scheduled RETX of SDU SN=%d", retx.sn); RlcInfo("Scheduled RETX of SDU SN=%d", retx.sn);
} else { } else {
RlcInfo("Scheduled RETX of SDU SN=%d", nack_sn); RlcInfo("Scheduled RETX of SDU SN=%d", nack.nack_sn);
retx_sn_set.insert(nack_sn); retx_sn_set.insert(nack.nack_sn);
for (auto segm : (*tx_window)[nack_sn].segment_list) { for (auto segm : (*tx_window)[nack.nack_sn].segment_list) {
rlc_amd_retx_nr_t& retx = retx_queue->push(); rlc_amd_retx_nr_t& retx = retx_queue->push();
retx.sn = nack_sn; retx.sn = nack.nack_sn;
retx.is_segment = true; retx.is_segment = true;
retx.so_start = segm.so; retx.so_start = segm.so;
retx.current_so = segm.so; retx.current_so = segm.so;
@ -910,48 +949,22 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes)
} }
} else { } else {
RlcInfo("RETX queue already has NACK_SN. SDU SN=%d, Tx_Next_Ack=%d, Tx_Next=%d", RlcInfo("RETX queue already has NACK_SN. SDU SN=%d, Tx_Next_Ack=%d, Tx_Next=%d",
status.nacks[nack_idx].nack_sn, nack.nack_sn,
st.tx_next_ack, st.tx_next_ack,
st.tx_next); st.tx_next);
} }
} }
} else { } else {
RlcInfo("TX window does not contain NACK_SN. SDU SN=%d, Tx_Next_Ack=%d, Tx_Next=%d", RlcInfo("TX window does not contain NACK_SN. SDU SN=%d, Tx_Next_Ack=%d, Tx_Next=%d",
status.nacks[nack_idx].nack_sn, nack.nack_sn,
st.tx_next_ack, st.tx_next_ack,
st.tx_next); st.tx_next);
} // TX window containts NACK SN } // TX window containts NACK SN
} else { } else {
RlcInfo("RETX not in expected range. SDU SN=%d, Tx_Next_Ack=%d, Tx_Next=%d", RlcInfo(
status.nacks[nack_idx].nack_sn, "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);
st.tx_next_ack,
st.tx_next);
} // NACK SN within expected range } // NACK SN within expected range
} // NACK loop
// Process retx_count and inform upper layers if needed
for (uint32_t retx_sn : retx_sn_set) {
auto& pdu = (*tx_window)[retx_sn];
// Increment retx_count
if (pdu.retx_count == RETX_COUNT_NOT_STARTED) {
// Set retx_count = 0 on first RE-transmission of associated SDU (38.322 Sec. 5.3.2)
pdu.retx_count = 0;
} else {
// Increment otherwise
pdu.retx_count++;
}
// Inform upper layers if needed
check_sn_reached_max_retx(retx_sn);
}
// Notify PDCP
if (not notify_info_vec.empty()) {
parent->pdcp->notify_delivery(parent->lcid, notify_info_vec);
} }
notify_info_vec.clear();
}
/** /**
* Helper to check if a SN has reached the max reTx threshold * Helper to check if a SN has reached the max reTx threshold
* *

Loading…
Cancel
Save