Changed generation of status report to use rx_counts instead of

undeliverd_sdus_queue. Added a queue to store information about
rx_counts received.
Added unit test for when the SNs wrap-around in status report genaration
This commit is contained in:
Pedro Alvarez 2021-03-04 11:32:35 +00:00
parent 6159cb3817
commit 3f79cd6281
3 changed files with 157 additions and 87 deletions

View File

@ -21,6 +21,8 @@
#include "srslte/interfaces/ue_rrc_interfaces.h"
#include "srslte/upper/pdcp_entity_base.h"
#include <set>
namespace srsue {
class gw_interface_pdcp;
@ -165,9 +167,30 @@ private:
// Discard callback (discardTimer)
class discard_callback;
// TX Queue
// Tx info queue
uint32_t maximum_allocated_sns_window = 2048;
std::unique_ptr<undelivered_sdus_queue> undelivered_sdus;
// Rx info queue
uint32_t fmc = 0;
std::set<uint32_t> rx_counts_info; // Keeps the RX_COUNT for generation of the stauts report
void update_rx_counts_queue(uint32_t rx_count);
/*
* Helper function to see if an SN is larger
*/
bool is_sn_larger(uint32_t sn1, uint32_t sn2)
{
int32_t diff = sn2 - sn1;
uint32_t nof_sns = 1u << cfg.sn_len;
if (diff > (int32_t)(nof_sns / 2)) {
return false;
}
if (diff <= 0 && diff > -((int32_t)(nof_sns / 2))) {
return false;
}
return true;
}
};
// Discard callback (discardTimer)

View File

@ -404,10 +404,27 @@ void pdcp_entity_lte::handle_am_drb_pdu(srslte::unique_byte_buffer_t pdu)
// Update info on last PDU submitted to upper layers
st.last_submitted_pdcp_rx_sn = sn;
// Store Rx SN/COUNT
update_rx_counts_queue(count);
// Pass to upper layers
gw->write_pdu(lcid, std::move(pdu));
}
void pdcp_entity_lte::update_rx_counts_queue(uint32_t rx_count)
{
// The received COUNT is the first missing COUNT
if (rx_count == fmc) {
fmc++;
// Update the queue for the Status report bitmap, if NEXT_PDCP_RX_SN changed
while (not rx_counts_info.empty() && *rx_counts_info.begin() == fmc) {
rx_counts_info.erase(fmc);
fmc++;
}
} else {
rx_counts_info.insert(rx_count);
}
}
/****************************************************************************
* Control handler functions (Status Report)
* Ref: 3GPP TS 36.323 v10.1.0 Section 5.1.3
@ -434,15 +451,10 @@ void pdcp_entity_lte::send_status_report()
}
// Get First Missing Segment (FMS)
uint32_t fms = 0;
if (undelivered_sdus->empty()) {
fms = st.next_pdcp_tx_sn;
} else {
fms = undelivered_sdus->get_fms();
}
uint32_t fms = SN(fmc);
// Get Last Missing Segment
uint32_t lms = undelivered_sdus->get_lms();
uint32_t nof_sns_in_bitmap = rx_counts_info.size();
// Allocate Status Report PDU
unique_byte_buffer_t pdu = make_byte_buffer();
@ -451,7 +463,7 @@ void pdcp_entity_lte::send_status_report()
return;
}
logger.debug("Status report: FMS=%d, LMS=%d", fms, lms);
logger.debug("Status report: FMS=%d, Nof SNs in bitmap=%d", fms, nof_sns_in_bitmap);
// Set control bit and type of PDU
pdu->msg[0] = ((uint8_t)PDCP_DC_FIELD_CONTROL_PDU << 7) | ((uint8_t)PDCP_PDU_TYPE_STATUS_REPORT << 4);
@ -474,8 +486,9 @@ void pdcp_entity_lte::send_status_report()
}
// Add bitmap of missing PDUs, if necessary
if (not undelivered_sdus->empty()) {
if (not rx_counts_info.empty()) {
// First check size of bitmap
uint32_t lms = *rx_counts_info.rbegin();
int32_t diff = lms - (fms - 1);
uint32_t nof_sns = 1u << cfg.sn_len;
if (diff > (int32_t)(nof_sns / 2)) {
@ -494,13 +507,12 @@ void pdcp_entity_lte::send_status_report()
lms,
fms - 1,
bitmap_sz);
for (uint32_t offset = 0; offset < sn_diff; ++offset) {
uint32_t sn = (fms + 1 + offset) % (1u << cfg.sn_len);
if (undelivered_sdus->has_sdu(sn)) {
uint32_t bit_offset = offset % 8;
uint32_t byte_offset = offset / 8;
pdu->msg[pdu->N_bytes + byte_offset] |= 1 << (7 - bit_offset);
}
for (uint32_t rx_count : rx_counts_info) {
logger.debug("Setting bitmap for RX_COUNT=%d", rx_count);
uint32_t offset = rx_count - (fmc + 1);
uint32_t bit_offset = offset % 8;
uint32_t byte_offset = offset / 8;
pdu->msg[pdu->N_bytes + byte_offset] |= 1 << (7 - bit_offset);
}
pdu->N_bytes += bitmap_sz;
}
@ -575,6 +587,7 @@ void pdcp_entity_lte::handle_status_report_pdu(unique_byte_buffer_t pdu)
undelivered_sdus->clear_sdu(sn);
}
}
/****************************************************************************
* TX PDUs Queue Helper
***************************************************************************/

View File

@ -17,40 +17,61 @@
*/
int test_tx_status_report(const srslte::pdcp_lte_state_t& init_state, srslog::basic_logger& logger)
{
srslte::pdcp_config_t cfg = {1,
srslte::PDCP_RB_IS_DRB,
srslte::SECURITY_DIRECTION_UPLINK,
srslte::SECURITY_DIRECTION_DOWNLINK,
srslte::PDCP_SN_LEN_12,
srslte::pdcp_t_reordering_t::ms500,
srslte::pdcp_discard_timer_t::ms500,
true};
srslte::pdcp_config_t cfg_tx = {1,
srslte::PDCP_RB_IS_DRB,
srslte::SECURITY_DIRECTION_UPLINK,
srslte::SECURITY_DIRECTION_DOWNLINK,
srslte::PDCP_SN_LEN_12,
srslte::pdcp_t_reordering_t::ms500,
srslte::pdcp_discard_timer_t::ms500,
true};
pdcp_lte_test_helper pdcp_hlp(cfg, sec_cfg, logger);
srslte::pdcp_entity_lte* pdcp = &pdcp_hlp.pdcp;
rlc_dummy* rlc = &pdcp_hlp.rlc;
srsue::stack_test_dummy* stack = &pdcp_hlp.stack;
srslte::pdcp_config_t cfg_rx = {1,
srslte::PDCP_RB_IS_DRB,
srslte::SECURITY_DIRECTION_DOWNLINK,
srslte::SECURITY_DIRECTION_UPLINK,
srslte::PDCP_SN_LEN_12,
srslte::pdcp_t_reordering_t::ms500,
srslte::pdcp_discard_timer_t::ms500,
true};
pdcp_hlp.set_pdcp_initial_state(init_state);
// Setup TX
pdcp_lte_test_helper pdcp_hlp_tx(cfg_tx, sec_cfg, logger);
srslte::pdcp_entity_lte* pdcp_tx = &pdcp_hlp_tx.pdcp;
rlc_dummy* rlc_tx = &pdcp_hlp_tx.rlc;
srsue::stack_test_dummy* stack_tx = &pdcp_hlp_tx.stack;
pdcp_hlp_tx.set_pdcp_initial_state(init_state);
// Setup RX
pdcp_lte_test_helper pdcp_hlp_rx(cfg_tx, sec_cfg, logger);
srslte::pdcp_entity_lte* pdcp_rx = &pdcp_hlp_tx.pdcp;
rlc_dummy* rlc_rx = &pdcp_hlp_tx.rlc;
srsue::stack_test_dummy* stack_rx = &pdcp_hlp_tx.stack;
pdcp_hlp_rx.set_pdcp_initial_state(init_state);
// Tmp variable to hold the PDCP PDU
srslte::unique_byte_buffer_t out_pdu = srslte::make_byte_buffer();
// Write 256 SDUs and notify imediatly -> FMS 0001 0000 0001
for (uint32_t i = 0; i < 257; i++) {
srslte::unique_byte_buffer_t sdu = srslte::make_byte_buffer();
srslte::unique_byte_buffer_t pdu = srslte::make_byte_buffer();
sdu->append_bytes(sdu1, sizeof(sdu1));
pdcp->write_sdu(std::move(sdu));
pdcp->notify_delivery({i});
pdcp_tx->write_sdu(std::move(sdu));
pdcp_tx->notify_delivery({i});
rlc_tx->get_last_sdu(pdu);
pdcp_rx->write_pdu(std::move(pdu));
}
// Check undelivered SDUs queue size
TESTASSERT(pdcp->nof_discard_timers() == 0); // 0 timers should be running
TESTASSERT(pdcp_tx->nof_discard_timers() == 0); // 0 timers should be running on TX
// Generate the status report
pdcp->send_status_report();
rlc->get_last_sdu(out_pdu);
pdcp_rx->send_status_report();
rlc_rx->get_last_sdu(out_pdu);
logger.debug(out_pdu->msg, out_pdu->N_bytes, "Status PDU:");
// Check status PDU
// Check status PDU. We received up to
/*
* | D/C | TYPE | FMS | -> | 0 | 0 | 0001 |
* | FMS | -> | 00000001 |
@ -62,33 +83,36 @@ int test_tx_status_report(const srslte::pdcp_lte_state_t& init_state, srslog::ba
// Write another 16 SDUs but don't notify SN=257, SN=258, SN=271 and SN=272
for (uint32_t i = 257; i < 273; i++) {
srslte::unique_byte_buffer_t sdu = srslte::make_byte_buffer();
srslte::unique_byte_buffer_t pdu = srslte::make_byte_buffer();
sdu->append_bytes(sdu1, sizeof(sdu1));
pdcp->write_sdu(std::move(sdu));
pdcp_tx->write_sdu(std::move(sdu));
if (i != 257 && i != 258 && i != 271 && i != 272) {
pdcp->notify_delivery({i});
pdcp_tx->notify_delivery({i});
rlc_tx->get_last_sdu(pdu);
pdcp_rx->write_pdu(std::move(pdu));
}
}
// Check undelivered SDUs queue size
TESTASSERT(pdcp->nof_discard_timers() == 4);
TESTASSERT(pdcp_tx->nof_discard_timers() == 4);
// Generate the status report
pdcp->send_status_report();
rlc->get_last_sdu(out_pdu);
pdcp_tx->send_status_report();
rlc_tx->get_last_sdu(out_pdu);
logger.debug(out_pdu->msg, out_pdu->N_bytes, "Status PDU:");
// Check status PDU
/*
* | D/C | TYPE | FMS | -> | 0 | 0 | 0001 |
* | FMS | -> | 00000001 |
* | bitmap | -> | 11000000 |
* | bitmap | -> | 01111111 |
* | bitmap (cont.) | -> | 00000011 |
*/
TESTASSERT(out_pdu->N_bytes == 4);
TESTASSERT(out_pdu->msg[0] == 0b00000001);
TESTASSERT(out_pdu->msg[0] == 0b00000001); // FMS = 257
TESTASSERT(out_pdu->msg[1] == 0b00000001);
TESTASSERT(out_pdu->msg[2] == 0b10000000);
TESTASSERT(out_pdu->msg[3] == 0b00000110);
TESTASSERT(out_pdu->msg[2] == 0b01111111); // FMS + 1 is missing
TESTASSERT(out_pdu->msg[3] == 0b11111000); //
return 0;
}
@ -97,37 +121,57 @@ int test_tx_status_report(const srslte::pdcp_lte_state_t& init_state, srslog::ba
*/
int test_tx_wraparound_status_report(const srslte::pdcp_lte_state_t& init_state, srslog::basic_logger& logger)
{
srslte::pdcp_config_t cfg = {1,
srslte::PDCP_RB_IS_DRB,
srslte::SECURITY_DIRECTION_UPLINK,
srslte::SECURITY_DIRECTION_DOWNLINK,
srslte::PDCP_SN_LEN_12,
srslte::pdcp_t_reordering_t::ms500,
srslte::pdcp_discard_timer_t::ms500,
true};
srslte::pdcp_config_t cfg_tx = {1,
srslte::PDCP_RB_IS_DRB,
srslte::SECURITY_DIRECTION_UPLINK,
srslte::SECURITY_DIRECTION_DOWNLINK,
srslte::PDCP_SN_LEN_12,
srslte::pdcp_t_reordering_t::ms500,
srslte::pdcp_discard_timer_t::ms500,
true};
pdcp_lte_test_helper pdcp_hlp(cfg, sec_cfg, logger);
srslte::pdcp_entity_lte* pdcp = &pdcp_hlp.pdcp;
rlc_dummy* rlc = &pdcp_hlp.rlc;
srsue::stack_test_dummy* stack = &pdcp_hlp.stack;
srslte::pdcp_config_t cfg_rx = {1,
srslte::PDCP_RB_IS_DRB,
srslte::SECURITY_DIRECTION_DOWNLINK,
srslte::SECURITY_DIRECTION_UPLINK,
srslte::PDCP_SN_LEN_12,
srslte::pdcp_t_reordering_t::ms500,
srslte::pdcp_discard_timer_t::ms500,
true};
// Setup TX
pdcp_lte_test_helper pdcp_hlp_tx(cfg_tx, sec_cfg, logger);
srslte::pdcp_entity_lte* pdcp_tx = &pdcp_hlp_tx.pdcp;
rlc_dummy* rlc_tx = &pdcp_hlp_tx.rlc;
srsue::stack_test_dummy* stack_tx = &pdcp_hlp_tx.stack;
pdcp_hlp_tx.set_pdcp_initial_state(init_state);
// Setup RX
pdcp_lte_test_helper pdcp_hlp_rx(cfg_tx, sec_cfg, logger);
srslte::pdcp_entity_lte* pdcp_rx = &pdcp_hlp_tx.pdcp;
rlc_dummy* rlc_rx = &pdcp_hlp_tx.rlc;
srsue::stack_test_dummy* stack_rx = &pdcp_hlp_tx.stack;
pdcp_hlp_rx.set_pdcp_initial_state(init_state);
pdcp_hlp.set_pdcp_initial_state(init_state);
srslte::unique_byte_buffer_t out_pdu = srslte::make_byte_buffer();
// Write 256 SDUs and notify imediatly -> FMS 1111 1111 0000
for (uint32_t i = 0; i < 4080; i++) {
srslte::unique_byte_buffer_t sdu = srslte::make_byte_buffer();
srslte::unique_byte_buffer_t pdu = srslte::make_byte_buffer();
sdu->append_bytes(sdu1, sizeof(sdu1));
pdcp->write_sdu(std::move(sdu));
pdcp->notify_delivery({i});
pdcp_tx->write_sdu(std::move(sdu));
pdcp_tx->notify_delivery({i});
rlc_tx->get_last_sdu(pdu);
pdcp_rx->write_pdu(std::move(pdu));
}
// Check undelivered SDUs queue size
TESTASSERT(pdcp->nof_discard_timers() == 0); // 0 timers should be running
TESTASSERT(pdcp_tx->nof_discard_timers() == 0); // 0 timers should be running
// Generate the status report
pdcp->send_status_report();
rlc->get_last_sdu(out_pdu);
pdcp_rx->send_status_report();
rlc_rx->get_last_sdu(out_pdu);
logger.debug(out_pdu->msg, out_pdu->N_bytes, "Status PDU:");
// Check status PDU
@ -142,19 +186,22 @@ int test_tx_wraparound_status_report(const srslte::pdcp_lte_state_t& init_state,
// Write another 32 SDUs but don't notify SN=4080, SN=4081, SN=14 and SN=15
for (uint32_t i = 4080; i < 4112; i++) {
srslte::unique_byte_buffer_t sdu = srslte::make_byte_buffer();
srslte::unique_byte_buffer_t pdu = srslte::make_byte_buffer();
sdu->append_bytes(sdu1, sizeof(sdu1));
pdcp->write_sdu(std::move(sdu));
pdcp_tx->write_sdu(std::move(sdu));
if (i != 4080 && i != 4081 && i != 4110 && i != 4111) {
pdcp->notify_delivery({i % 4096});
pdcp_tx->notify_delivery({i % 4096});
rlc_tx->get_last_sdu(pdu);
pdcp_rx->write_pdu(std::move(pdu));
}
}
// Check undelivered SDUs queue size
TESTASSERT(pdcp->nof_discard_timers() == 4);
TESTASSERT(pdcp_tx->nof_discard_timers() == 4);
// Generate the status report
pdcp->send_status_report();
rlc->get_last_sdu(out_pdu);
pdcp_rx->send_status_report();
rlc_rx->get_last_sdu(out_pdu);
logger.debug(out_pdu->msg, out_pdu->N_bytes, "Status PDU:");
// Check status PDU
@ -167,10 +214,10 @@ int test_tx_wraparound_status_report(const srslte::pdcp_lte_state_t& init_state,
TESTASSERT(out_pdu->N_bytes == 6);
TESTASSERT(out_pdu->msg[0] == 0b00001111);
TESTASSERT(out_pdu->msg[1] == 0b11110000);
TESTASSERT(out_pdu->msg[2] == 0b10000000);
TESTASSERT(out_pdu->msg[3] == 0b00000000);
TESTASSERT(out_pdu->msg[4] == 0b00000000);
TESTASSERT(out_pdu->msg[5] == 0b00000110);
TESTASSERT(out_pdu->msg[2] == 0b01111111);
TESTASSERT(out_pdu->msg[3] == 0b11111111);
TESTASSERT(out_pdu->msg[4] == 0b11111111);
TESTASSERT(out_pdu->msg[5] == 0b11111000);
return 0;
}
/*
@ -233,20 +280,6 @@ int test_rx_status_report(const srslte::pdcp_lte_state_t& init_state, srslog::ba
pdcp->write_pdu(std::move(status_pdu));
TESTASSERT(pdcp->nof_discard_timers() == 1);
// Check if the SDUs were correctly discarded with the status report
pdcp->send_status_report();
rlc->get_last_sdu(out_pdu);
logger.debug(out_pdu->msg, out_pdu->N_bytes, "Status PDU:");
// Check status PDU, only 271 is missing => FMS = 271
/*
* | D/C | TYPE | FMS | -> | 0 | 0 | 0001 |
* | FMS | -> | 00001111 |
*/
TESTASSERT(out_pdu->N_bytes == 3);
TESTASSERT(out_pdu->msg[0] == 0b00000001);
TESTASSERT(out_pdu->msg[1] == 0b00001111);
return 0;
}
@ -259,7 +292,8 @@ int run_all_tests()
logger.set_hex_dump_max_size(128);
// This is the normal initial state. All state variables are set to zero
srslte::pdcp_lte_state_t normal_init_state = {};
srslte::pdcp_lte_state_t normal_init_state = {
.next_pdcp_tx_sn = 0, .tx_hfn = 0, .rx_hfn = 0, .next_pdcp_rx_sn = 0, .last_submitted_pdcp_rx_sn = 4095};
TESTASSERT(test_tx_status_report(normal_init_state, logger) == 0);
TESTASSERT(test_tx_wraparound_status_report(normal_init_state, logger) == 0);