- Added ability to the PDCP LTE entity to send PDCP Status report.

- Added handling of PDCP status report to the PDCP LTE entity.
- Added test to the generation and handling of PDCP status report.
This commit is contained in:
Pedro Alvarez 2021-02-01 19:09:52 +00:00
parent 929b8650a3
commit 9bc08af880
6 changed files with 362 additions and 4 deletions

View File

@ -42,6 +42,18 @@ enum pdcp_dc_field_t {
};
static const char* pdcp_dc_field_text[PDCP_DC_FIELD_N_ITEMS] = {"Control PDU", "Data PDU"};
enum pdcp_pdu_type_t {
PDCP_PDU_TYPE_STATUS_REPORT = 0,
PDCP_PDU_TYPE_INTERSPERSED_ROHC_FEEDBACK_PACKET,
PDCP_PDU_TYPE_LWA_STATUS_REPORT,
PDCP_PDU_TYPE_LWA_END_MARKER_PACKET,
PDCP_PDU_TYPE_N_ITEMS,
};
static const char* pdcp_pdu_type_text[PDCP_PDU_TYPE_N_ITEMS] = {"PDCP Report PDU",
"Interspersed ROCH Feedback Packet",
"LWA Status Report",
"LWA End-marker Packet"};
// Taken from PDCP-Config (TS 38.331 version 15.2.1)
enum class pdcp_t_reordering_t {
ms0 = 0,

View File

@ -64,6 +64,10 @@ public:
// Getter for unacknowledged PDUs. Used for handover
std::map<uint32_t, srslte::unique_byte_buffer_t> get_buffered_pdus() override;
// Status report helper(s)
bool send_status_report();
void handle_status_report_pdu(srslte::unique_byte_buffer_t pdu);
// Internal state getters/setters
void get_bearer_state(pdcp_lte_state_t* state) override;
void set_bearer_state(const pdcp_lte_state_t& state) override;
@ -89,6 +93,7 @@ private:
// TX Queue
std::map<uint32_t, unique_byte_buffer_t> undelivered_sdus_queue;
void handle_control_pdu(srslte::unique_byte_buffer_t pdu);
void handle_srb_pdu(srslte::unique_byte_buffer_t pdu);
void handle_um_drb_pdu(srslte::unique_byte_buffer_t pdu);
void handle_am_drb_pdu(srslte::unique_byte_buffer_t pdu);

View File

@ -11,7 +11,9 @@
*/
#include "srslte/upper/pdcp_entity_lte.h"
#include "srslte/common/int_helpers.h"
#include "srslte/common/security.h"
#include <bitset>
namespace srslte {
@ -181,9 +183,10 @@ void pdcp_entity_lte::write_sdu(unique_byte_buffer_t sdu, int upper_sn)
// RLC interface
void pdcp_entity_lte::write_pdu(unique_byte_buffer_t pdu)
{
// drop control PDUs
// Handle control PDUs
if (is_drb() && is_control_pdu(pdu)) {
logger.info("Dropping PDCP control PDU");
logger.info("Handling PDCP control PDU");
handle_control_pdu(std::move(pdu));
return;
}
@ -223,8 +226,22 @@ void pdcp_entity_lte::write_pdu(unique_byte_buffer_t pdu)
}
}
// Handle control PDU
void pdcp_entity_lte::handle_control_pdu(unique_byte_buffer_t pdu)
{
switch ((pdu->msg[0] >> 4) & 0x03) {
case PDCP_PDU_TYPE_STATUS_REPORT:
handle_status_report_pdu(std::move(pdu));
break;
default:
logger.warning("Unhandled control PDU");
return;
}
return;
}
/****************************************************************************
* Rx data/control handler functions
* Rx data handler functions
* Ref: 3GPP TS 36.323 v10.1.0 Section 5.1.2
***************************************************************************/
// SRBs (5.1.2.2)
@ -333,7 +350,7 @@ void pdcp_entity_lte::handle_am_drb_pdu(srslte::unique_byte_buffer_t pdu)
sn_diff_last_submit,
last_submit_diff_sn,
reordering_window);
return; // Discard
return;
}
if ((int32_t)(st.next_pdcp_rx_sn - sn) > (int32_t)reordering_window) {
@ -368,6 +385,127 @@ void pdcp_entity_lte::handle_am_drb_pdu(srslte::unique_byte_buffer_t pdu)
gw->write_pdu(lcid, std::move(pdu));
}
/****************************************************************************
* Control handler functions (Status Report)
* Ref: 3GPP TS 36.323 v10.1.0 Section 5.1.3
***************************************************************************/
// Section 5.3.1 transmit operation
bool pdcp_entity_lte::send_status_report()
{
// Check wether RCL AM is being used.
if (rlc->rb_is_um(lcid)) {
logger.error("Trying to send PDCP Status Report and RLC is not AM");
return false;
}
// Get First Missing Segment (FMS)
uint32_t fms = 0;
if (undelivered_sdus_queue.empty()) {
fms = st.next_pdcp_tx_sn;
} else {
fms = undelivered_sdus_queue.begin()->first;
}
logger.debug("PDCP Status report: FMS=%d", fms);
// Allocate Status Report PDU
unique_byte_buffer_t pdu = make_byte_buffer();
// 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);
// Set FMS
switch (cfg.sn_len) {
case PDCP_SN_LEN_12:
pdu->msg[0] = pdu->msg[0] | (0x0F & (fms >> 8));
pdu->msg[1] = 0xFF & fms;
pdu->N_bytes = 2;
break;
case PDCP_SN_LEN_18:
pdu->msg[0] = pdu->msg[0] | (0x03 & (fms >> 16));
pdu->msg[1] = 0xFF & (fms >> 8);
pdu->msg[2] = 0xFF & fms;
pdu->N_bytes = 3;
break;
default:
logger.error("Unsupported SN length for Status Report.");
return false;
}
// Add bitmap of missing PDUs, if necessary
if (not undelivered_sdus_queue.empty()) {
uint32_t byte_offset = 0;
for (auto it = undelivered_sdus_queue.begin(); it != undelivered_sdus_queue.end(); it++) {
uint32_t offset = it->first - fms;
uint32_t bit_offset = offset % 8;
byte_offset = offset / 8;
pdu->msg[pdu->N_bytes + byte_offset] |= 1 << (7 - bit_offset);
}
pdu->N_bytes += (byte_offset + 1);
}
// Write PDU to RLC
rlc->write_sdu(lcid, std::move(pdu));
return true;
}
// Section 5.3.2 receive operation
void pdcp_entity_lte::handle_status_report_pdu(unique_byte_buffer_t pdu)
{
uint32_t fms;
std::vector<uint32_t> acked_sns;
uint32_t bitmap_offset;
// Get FMS
switch (cfg.sn_len) {
case PDCP_SN_LEN_12: {
uint16_t tmp16;
uint8_to_uint16(pdu->msg, &tmp16);
fms = tmp16 & 0x0FFF;
bitmap_offset = 2;
break;
}
case PDCP_SN_LEN_18: {
uint8_to_uint24(pdu->msg, &fms);
fms = fms & 0x3FFF;
bitmap_offset = 3;
break;
}
default:
logger.error("Unsupported SN length for Status Report.");
return;
}
// Remove all SDUs with SN smaller than FMS
for (auto it = undelivered_sdus_queue.begin(); it != undelivered_sdus_queue.end();) {
if (it->first < fms) {
discard_timers_map.erase(it->first);
it = undelivered_sdus_queue.erase(it);
} else {
++it;
}
}
// Get acked SNs from bitmap
for (uint32_t i = 0; (i + bitmap_offset) < pdu->N_bytes; i++) {
std::bitset<8> bset{pdu->msg[bitmap_offset + i]};
for (uint8_t j = 0; j < 8; j++) {
if (bset[8 - j]) {
uint32_t acked_sn = fms + i * 8 + j;
acked_sns.push_back(acked_sn);
}
}
}
// Discard ACK'ed SDUs
for (uint32_t sn : acked_sns) {
logger.debug("Status report ACKed SN=%d.", sn);
undelivered_sdus_queue.erase(sn);
discard_timers_map.erase(sn);
}
}
/****************************************************************************
* TX PDUs Queue Helper
***************************************************************************/

View File

@ -77,6 +77,10 @@ add_executable(pdcp_lte_test_discard_sdu pdcp_lte_test_discard_sdu.cc)
target_link_libraries(pdcp_lte_test_discard_sdu srslte_upper srslte_common)
add_test(pdcp_lte_test_discard_sdu pdcp_lte_test_discard_sdu)
add_executable(pdcp_lte_test_status_report pdcp_lte_test_status_report.cc)
target_link_libraries(pdcp_lte_test_status_report srslte_upper srslte_common)
add_test(pdcp_lte_test_status_report pdcp_lte_test_status_report)
########################################################################
# Option to run command after build (useful for remote builds)
########################################################################

View File

@ -139,6 +139,8 @@ int run_all_tests()
int main()
{
srslog::init();
if (run_all_tests() != SRSLTE_SUCCESS) {
fprintf(stderr, "pdcp_lte_tests() failed\n");
return SRSLTE_ERROR;

View File

@ -0,0 +1,197 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#include "pdcp_lte_test.h"
#include <numeric>
/*
* Test correct transmission of FMS and Bitmap in status report
*/
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};
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;
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 0001 0000 0001
for (uint32_t i = 0; i < 257; i++) {
srslte::unique_byte_buffer_t sdu = srslte::make_byte_buffer();
sdu->append_bytes(sdu1, sizeof(sdu1));
pdcp->write_sdu(std::move(sdu));
pdcp->notify_delivery({i});
}
// Check undelivered SDUs queue size
TESTASSERT(pdcp->nof_discard_timers() == 0); // 0 timers should be running
// Generate 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
/*
* | D/C | TYPE | FMS | -> | 0 | 0 | 0001 |
* | FMS | -> | 00000001 |
*/
TESTASSERT(out_pdu->msg[0] == 1);
TESTASSERT(out_pdu->msg[1] == 1);
TESTASSERT(out_pdu->N_bytes == 2);
// 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();
sdu->append_bytes(sdu1, sizeof(sdu1));
pdcp->write_sdu(std::move(sdu));
if (i != 257 && i != 258 && i != 271 && i != 272) {
pdcp->notify_delivery({i});
}
}
// Check undelivered SDUs queue size
TESTASSERT(pdcp->nof_discard_timers() == 4);
// Generate 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
/*
* | D/C | TYPE | FMS | -> | 0 | 0 | 0001 |
* | FMS | -> | 00000001 |
* | bitmap | -> | 11000000 |
* | bitmap (cont.) | -> | 00000011 |
*/
TESTASSERT(out_pdu->N_bytes == 4);
TESTASSERT(out_pdu->msg[0] == 0b00000001);
TESTASSERT(out_pdu->msg[1] == 0b00000001);
TESTASSERT(out_pdu->msg[2] == 0b11000000);
TESTASSERT(out_pdu->msg[3] == 0b00000011);
return 0;
}
/*
* Test reception of status report
*/
int test_rx_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};
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;
pdcp_hlp.set_pdcp_initial_state(init_state);
srslte::unique_byte_buffer_t status_pdu = srslte::make_byte_buffer();
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();
sdu->append_bytes(sdu1, sizeof(sdu1));
pdcp->write_sdu(std::move(sdu));
pdcp->notify_delivery({i});
}
// 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();
sdu->append_bytes(sdu1, sizeof(sdu1));
pdcp->write_sdu(std::move(sdu));
if (i != 257 && i != 258 && i != 271 && i != 272) {
pdcp->notify_delivery({i});
}
}
// Check undelivered SDUs queue size
TESTASSERT(pdcp->nof_discard_timers() == 4);
// Set status PDU
// Notify up to 270 wit FMS
// Notify 272 with bitmap
/*
* | D/C | TYPE | FMS | -> | 0 | 0 | 0001 |
* | FMS | -> | 00001110 |
* | bitmap | -> | 01000000 |
*/
status_pdu->N_bytes = 3;
status_pdu->msg[0] = 0b00000001;
status_pdu->msg[1] = 0b00001110;
status_pdu->msg[2] = 0b01000000;
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;
}
// Setup all tests
int run_all_tests()
{
// Setup log
srslog::basic_logger& logger = srslog::fetch_basic_logger("PDCP LTE Test Status Report", false);
logger.set_level(srslog::basic_levels::debug);
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 = {};
TESTASSERT(test_tx_status_report(normal_init_state, logger) == 0);
TESTASSERT(test_rx_status_report(normal_init_state, logger) == 0);
return 0;
}
int main()
{
srslog::init();
if (run_all_tests() != SRSLTE_SUCCESS) {
fprintf(stderr, "pdcp_lte_tests() failed\n");
return SRSLTE_ERROR;
}
return SRSLTE_SUCCESS;
}