Skip to content

Commit 16f0555

Browse files
robertfalkenbergcodebot
authored andcommitted
rlc_tx_am: no ReTx of unsent bytes for NACK'ed SDU under segmentation
This prevents the ReTx overtaking the actual transmission of an SDU under segmentation, as ReTx has priority. If such ReTx is ACK'ed by the peer, the original SDU under segmentation falls out of the tx_window.
1 parent 3b3c8d9 commit 16f0555

File tree

2 files changed

+72
-7
lines changed

2 files changed

+72
-7
lines changed

lib/rlc/rlc_tx_am_entity.cpp

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,8 @@ size_t rlc_tx_am_entity::pull_pdu(span<uint8_t> rlc_pdu_buf)
147147
pcap.push_pdu(pcap_context, rlc_pdu_buf.subspan(0, pdu_len));
148148
return pdu_len;
149149
}
150-
sn_under_segmentation = INVALID_RLC_SN;
151150
logger.log_error("SDU under segmentation does not exist in tx_window. sn={}", sn_under_segmentation);
151+
sn_under_segmentation = INVALID_RLC_SN;
152152
// attempt to send next SDU
153153
}
154154

@@ -745,16 +745,27 @@ bool rlc_tx_am_entity::handle_nack(rlc_am_status_nack nack)
745745
return false;
746746
}
747747

748-
uint32_t sdu_length = (*tx_window)[nack.nack_sn].sdu.length();
748+
const rlc_tx_am_sdu_info& sdu_info = (*tx_window)[nack.nack_sn];
749+
uint32_t sdu_length = sdu_info.sdu.length();
749750

750751
// Convert NACK for full SDUs into NACK with segment offset and length
751752
if (!nack.has_so) {
752753
nack.so_start = 0;
753754
nack.so_end = sdu_length - 1;
754755
}
755-
// Replace "end"-mark with actual SDU length
756756
if (nack.so_end == rlc_am_status_nack::so_end_of_sdu) {
757-
nack.so_end = sdu_length - 1;
757+
if (nack.nack_sn != sn_under_segmentation) {
758+
// Replace "end"-mark with actual SDU length
759+
nack.so_end = sdu_length - 1;
760+
} else {
761+
// The SDU is still under segmentation; RETX only what was already sent; avoid RETX overtaking the original
762+
if (sdu_info.next_so == 0) {
763+
logger.log_error(
764+
"Cannot RETX sn_under_segmentation={} with invalid next_so={}", sn_under_segmentation, sdu_info.next_so);
765+
return false;
766+
}
767+
nack.so_end = sdu_info.next_so - 1;
768+
}
758769
}
759770
// Sanity checks
760771
if (nack.so_start > nack.so_end) {

tests/unittests/rlc/rlc_tx_am_test.cpp

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,12 @@ class rlc_tx_am_test : public ::testing::Test, public ::testing::WithParamInterf
191191
/// \param[in] pdu_size Maximum size of each PDU that is read from RLC AM entity
192192
/// \param[in] n_sdus Number of SDUs to push into RLC AM entity
193193
/// \param[in] sdu_size Size of each SDU that is pushed into RLC AM entity
194-
void
195-
tx_segmented_pdus(byte_buffer_chain* out_pdus, uint32_t n_pdus, uint32_t pdu_size, uint32_t n_sdus, uint32_t sdu_size)
194+
void tx_segmented_pdus(byte_buffer_chain* out_pdus,
195+
uint32_t n_pdus,
196+
uint32_t pdu_size,
197+
uint32_t n_sdus,
198+
uint32_t sdu_size,
199+
uint32_t expect_remaining_bytes = 0)
196200
{
197201
uint32_t header_min_size = sn_size == rlc_am_sn_size::size12bits ? 2 : 3;
198202
uint32_t header_so_size = 2;
@@ -278,7 +282,7 @@ class rlc_tx_am_test : public ::testing::Test, public ::testing::WithParamInterf
278282
sdu_so += out_pdus[i].length() - header_size;
279283
}
280284
}
281-
EXPECT_EQ(rlc->get_buffer_state(), 0);
285+
EXPECT_EQ(rlc->get_buffer_state(), expect_remaining_bytes);
282286
EXPECT_EQ(tester->bsr, expect_buffer_state); // pull_pdu does not push BSR to lower layer
283287
EXPECT_EQ(tester->bsr_count, n_bsr);
284288
}
@@ -947,6 +951,56 @@ TEST_P(rlc_tx_am_test, retx_pdu_last_segment_without_segmentation)
947951
EXPECT_EQ(tester->highest_delivered_pdcp_sn_list.front(), 2);
948952
}
949953

954+
TEST_P(rlc_tx_am_test, retx_sn_under_segmentation)
955+
{
956+
const uint32_t n_sdus = 5;
957+
const uint32_t sdu_size = 9;
958+
959+
const uint32_t n_splits = 3;
960+
961+
const uint32_t n_pdus = n_sdus * n_splits - 1;
962+
const uint32_t header_size = (sn_size == rlc_am_sn_size::size12bits ? 2 : 3);
963+
const uint32_t so_size = 2;
964+
const uint32_t pdu_size = header_size + so_size + (sdu_size / n_splits);
965+
byte_buffer_chain pdus[n_pdus];
966+
967+
const uint32_t unsent_sdu_bytes = sdu_size / n_splits - so_size; // subtract 2 extra bytes from 1st segment (no SO)
968+
tx_segmented_pdus(pdus, n_pdus, pdu_size, n_sdus, sdu_size, unsent_sdu_bytes + header_size + so_size);
969+
970+
// NACK SN=4 2:65535 => this SN is currently under segmentation; ReTx only the parts that haven't been sent yet.
971+
rlc_am_status_nack nack = {};
972+
nack.nack_sn = 4;
973+
nack.has_so = true;
974+
nack.so_start = so_size + (sdu_size / n_splits); // assume 1st segment is received properly
975+
nack.so_end = nack.so_end_of_sdu; // the rest is missing
976+
{
977+
rlc_am_status_pdu status_pdu(sn_size);
978+
status_pdu.ack_sn = n_sdus;
979+
status_pdu.push_nack(nack);
980+
rlc->on_status_pdu(std::move(status_pdu));
981+
}
982+
pcell_worker.run_pending_tasks();
983+
984+
const uint32_t retx_bytes = sdu_size - nack.so_start - unsent_sdu_bytes;
985+
EXPECT_EQ(rlc->get_buffer_state(), unsent_sdu_bytes + header_size + so_size + header_size + so_size + retx_bytes);
986+
987+
// read the ReTx segment which shall only include bytes that were already sent
988+
std::vector<uint8_t> pdu_buf;
989+
size_t pdu_len;
990+
pdu_buf.resize(header_size + so_size + retx_bytes);
991+
pdu_len = rlc->pull_pdu(pdu_buf);
992+
pdu_buf.resize(pdu_len);
993+
EXPECT_EQ(pdu_len, header_size + so_size + retx_bytes);
994+
995+
// read the rest of the SDU under segmentation that was not yet sent
996+
EXPECT_EQ(rlc->get_buffer_state(), unsent_sdu_bytes + header_size + so_size);
997+
pdu_buf.resize(unsent_sdu_bytes + header_size + so_size);
998+
pdu_len = rlc->pull_pdu(pdu_buf);
999+
pdu_buf.resize(pdu_len);
1000+
1001+
EXPECT_EQ(rlc->get_buffer_state(), 0);
1002+
}
1003+
9501004
TEST_P(rlc_tx_am_test, retx_pdu_segment_invalid_so_start_and_so_end)
9511005
{
9521006
const uint32_t sdu_size = 3;

0 commit comments

Comments
 (0)