lib,rlc_am_lte: fix checkers for the TX window full

This commit is contained in:
Pedro Alvarez 2022-07-15 12:22:00 +01:00
parent 72220aa811
commit b001d6c10e
3 changed files with 191 additions and 6 deletions

View File

@ -86,6 +86,7 @@ private:
void retransmit_pdu(uint32_t sn);
// Helpers
bool window_full();
bool poll_required();
bool do_status();
void check_sn_reached_max_retx(uint32_t sn);

View File

@ -242,7 +242,7 @@ void rlc_am_lte_tx::get_buffer_state_nolock(uint32_t& n_bytes_newtx, uint32_t& n
}
// Bytes needed for tx SDUs
if (tx_window.size() < 1024) {
if (not window_full()) {
n_sdus = tx_sdu_queue.get_n_sdus();
n_bytes_newtx += tx_sdu_queue.size_bytes();
if (tx_sdu != NULL) {
@ -290,7 +290,7 @@ uint32_t rlc_am_lte_tx::read_pdu(uint8_t* payload, uint32_t nof_bytes)
}
// Section 5.2.2.3 in TS 36.311, if tx_window is full and retx_queue empty, retransmit PDU
if (tx_window.size() >= RLC_AM_WINDOW_SIZE && retx_queue.empty()) {
if (window_full() && retx_queue.empty()) {
retransmit_pdu(vt_a);
}
@ -314,7 +314,7 @@ void rlc_am_lte_tx::timer_expired(uint32_t timeout_id)
// Section 5.2.2.3 in TS 36.322, schedule PDU for retransmission if
// (a) both tx and retx buffer are empty (excluding tx'ed PDU waiting for ack), or
// (b) no new data PDU can be transmitted (tx window is full)
if ((retx_queue.empty() && tx_sdu_queue.size() == 0) || tx_window.size() >= RLC_AM_WINDOW_SIZE) {
if ((retx_queue.empty() && tx_sdu_queue.size() == 0) || window_full()) {
retransmit_pdu(vt_a); // TODO: TS says to send vt_s - 1 here
}
} else if (status_prohibit_timer.is_valid() && status_prohibit_timer.id() == timeout_id) {
@ -359,6 +359,14 @@ void rlc_am_lte_tx::retransmit_pdu(uint32_t sn)
* Helper functions
***************************************************************************/
bool rlc_am_lte_tx::window_full()
{
if ((vt_s - vt_a) >= RLC_AM_WINDOW_SIZE) {
return true;
}
return false;
};
/**
* Called when building a RLC PDU for checking whether the poll bit needs
* to be set.
@ -385,7 +393,7 @@ bool rlc_am_lte_tx::poll_required()
return true;
}
if (tx_window.size() >= RLC_AM_WINDOW_SIZE) {
if (window_full()) {
RlcDebug("Poll required. Cause: TX window full.");
return true;
}
@ -690,7 +698,7 @@ int rlc_am_lte_tx::build_data_pdu(uint8_t* payload, uint32_t nof_bytes)
}
// do not build any more PDU if window is already full
if (tx_window.size() >= RLC_AM_WINDOW_SIZE) {
if (window_full()) {
RlcInfo("Cannot build data PDU - Tx window full.");
return 0;
}
@ -1156,7 +1164,8 @@ rlc_am_lte_rx::rlc_am_lte_rx(rlc_am* parent_) :
pool(byte_buffer_pool::get_instance()),
reordering_timer(parent_->timers->get_unique_timer()),
rlc_am_base_rx(parent_, parent_->logger)
{}
{
}
bool rlc_am_lte_rx::configure(const rlc_config_t& cfg_)
{

View File

@ -3757,6 +3757,176 @@ bool poll_retx_expiry_test()
return SRSRAN_SUCCESS;
}
bool full_window_check_test()
{
rlc_config_t config = rlc_config_t::default_rlc_am_config();
// [I] SRB1 configured: t_poll_retx=65, poll_pdu=-1, poll_byte=-1, max_retx_thresh=6, t_reordering=55,
// t_status_prohibit=0
config.am.t_poll_retx = 65;
config.am.poll_pdu = -1;
config.am.poll_byte = -1;
config.am.max_retx_thresh = 6;
config.am.t_reordering = 55;
config.am.t_status_prohibit = 55;
#if HAVE_PCAP
rlc_pcap pcap;
pcap.open("rlc_am_poll_rext_expiry_test.pcap", config);
rlc_am_tester tester(true, &pcap);
#else
rlc_am_tester tester(true, NULL);
#endif
srsran::timer_handler timers(8);
rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
rlc_am rlc2(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers);
srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100);
srslog::fetch_basic_logger("RLC_AM_2").set_hex_dump_max_size(100);
srslog::fetch_basic_logger("RLC").set_hex_dump_max_size(100);
if (not rlc1.configure(config)) {
return -1;
}
if (not rlc2.configure(config)) {
return -1;
}
{
// Initial Tx
uint32_t num_tx_pdus = 512;
for (uint32_t i = 0; i < num_tx_pdus; ++i) {
// Write SDU
unique_byte_buffer_t sdu = srsran::make_byte_buffer();
TESTASSERT(sdu != nullptr);
sdu->N_bytes = 1;
sdu->msg[0] = i;
sdu->md.pdcp_sn = i;
rlc1.write_sdu(std::move(sdu));
unique_byte_buffer_t pdu = srsran::make_byte_buffer();
TESTASSERT(pdu != nullptr);
pdu->N_bytes = 1;
pdu->msg[0] = i;
pdu->md.pdcp_sn = i;
pdu->N_bytes = rlc1.read_pdu(pdu->msg, 3);
TESTASSERT(pdu->N_bytes == 3);
}
}
{
// Tx one more to check the window is full
unique_byte_buffer_t sdu = srsran::make_byte_buffer();
TESTASSERT(sdu != nullptr);
sdu->N_bytes = 1;
sdu->msg[0] = 0;
sdu->md.pdcp_sn = 512;
rlc1.write_sdu(std::move(sdu));
unique_byte_buffer_t pdu = srsran::make_byte_buffer();
TESTASSERT(pdu != nullptr);
pdu->N_bytes = rlc1.read_pdu(pdu->msg, 3);
TESTASSERT(pdu->N_bytes == 3);
// If the TX window is full, we should RETX SN=0
rlc_amd_pdu_header_t header = {};
rlc_am_read_data_pdu_header(&pdu->msg, &pdu->N_bytes, &header);
TESTASSERT_EQ(header.sn, 0);
TESTASSERT_EQ(header.N_li, 0);
TESTASSERT_EQ(header.fi, 0);
}
// Ack one SN in the middle of the TX window.
// This is done to make sure the full window check is correct
// even if PDUs in the middle of the window are ACKed.
// ACK_SN=3, NACK_SN=0
{
rlc_status_pdu_t status = {};
status.ack_sn = 3;
status.N_nack = 1;
status.nacks[0].nack_sn = 0;
unique_byte_buffer_t status_buf = srsran::make_byte_buffer();
TESTASSERT(status_buf != nullptr);
rlc_am_write_status_pdu(&status, status_buf.get());
rlc1.write_pdu(status_buf->msg, status_buf->N_bytes);
// Read RETX for SN=0 from NACK
unique_byte_buffer_t pdu = srsran::make_byte_buffer();
TESTASSERT(pdu != nullptr);
pdu->N_bytes = rlc1.read_pdu(pdu->msg, 3);
TESTASSERT(pdu->N_bytes == 3);
// Check RETX SN=0
rlc_amd_pdu_header_t header = {};
rlc_am_read_data_pdu_header(&pdu->msg, &pdu->N_bytes, &header);
TESTASSERT_EQ(header.sn, 0);
TESTASSERT_EQ(header.N_li, 0);
TESTASSERT_EQ(header.fi, 0);
TESTASSERT_EQ(0, rlc1.get_buffer_state());
}
{
// Tx more PDUs to check the window is still full
uint32_t num_tx_pdus = 2;
for (uint32_t i = 0; i < num_tx_pdus; ++i) {
// Write SDU
unique_byte_buffer_t sdu = srsran::make_byte_buffer();
TESTASSERT(sdu != nullptr);
sdu->N_bytes = 1;
sdu->msg[0] = i;
sdu->md.pdcp_sn = i;
rlc1.write_sdu(std::move(sdu));
unique_byte_buffer_t pdu = srsran::make_byte_buffer();
TESTASSERT(pdu != nullptr);
pdu->N_bytes = 1;
pdu->msg[0] = i;
pdu->md.pdcp_sn = i;
pdu->N_bytes = rlc1.read_pdu(pdu->msg, 3);
TESTASSERT(pdu->N_bytes == 3);
// If the TX window is full, we should RETX SN=0
rlc_amd_pdu_header_t header = {};
rlc_am_read_data_pdu_header(&pdu->msg, &pdu->N_bytes, &header);
TESTASSERT_EQ(header.sn, 0);
TESTASSERT_EQ(header.N_li, 0);
TESTASSERT_EQ(header.fi, 0);
}
}
// ACK more PDUs and advance VT(A).
// New PDUs should be available to read now.
{
rlc_status_pdu_t status = {};
status.ack_sn = 5;
status.N_nack = 0;
unique_byte_buffer_t status_buf = srsran::make_byte_buffer();
TESTASSERT(status_buf != nullptr);
rlc_am_write_status_pdu(&status, status_buf.get());
rlc1.write_pdu(status_buf->msg, status_buf->N_bytes);
// Read new PDU
unique_byte_buffer_t pdu = srsran::make_byte_buffer();
TESTASSERT(pdu != nullptr);
pdu->N_bytes = rlc1.read_pdu(pdu->msg, 3);
TESTASSERT(pdu->N_bytes == 3);
// If the TX window is no longer full, we should TX a new SN (SN=512)
rlc_amd_pdu_header_t header = {};
rlc_am_read_data_pdu_header(&pdu->msg, &pdu->N_bytes, &header);
TESTASSERT_EQ(header.sn, 512);
TESTASSERT_EQ(header.N_li, 0);
TESTASSERT_EQ(header.fi, 0);
}
#if HAVE_PCAP
pcap.close();
#endif
return SRSRAN_SUCCESS;
}
int main(int argc, char** argv)
{
// Setup the log message spy to intercept error and warning log entries from RLC
@ -3966,5 +4136,10 @@ int main(int argc, char** argv)
printf("poll_retx_expiry_test failed\n");
exit(-1);
};
if (full_window_check_test()) {
printf("full_window_check_test failed\n");
exit(-1);
};
return SRSRAN_SUCCESS;
}