Commit 698b5e3d authored by Bartosz Podrygajlo's avatar Bartosz Podrygajlo

A testsuite and a benchmark for RLC AM mode

parent 65d8d410
......@@ -767,8 +767,10 @@ void nr_rlc_entity_am_recv_pdu(nr_rlc_entity_t *_entity,
nr_rlc_pdu_decoder_init(&decoder, buffer, size);
dc = nr_rlc_pdu_decoder_get_bits(&decoder, 1); R(decoder);
if (dc == 0)
if (dc == 0) {
LOG_D(RLC, "RLC received control PDU\n");
return process_control_pdu(entity, buffer, size);
}
/* data PDU */
p = nr_rlc_pdu_decoder_get_bits(&decoder, 1); R(decoder);
......@@ -839,6 +841,7 @@ void nr_rlc_entity_am_recv_pdu(nr_rlc_entity_t *_entity,
entity->rx_list, pdu);
/* do reception actions (38.322 5.2.3.2.3) */
LOG_D(RLC, "RLC received PDU sn %d so %d is_first %d is_last %d data_size = %d \n", sn, so, is_first, is_last, data_size);
reception_actions(entity, pdu);
if (p) {
......@@ -852,15 +855,14 @@ void nr_rlc_entity_am_recv_pdu(nr_rlc_entity_t *_entity,
entity->status_triggered = 1;
if (!(sn_compare_rx(entity, sn, entity->rx_highest_status) < 0 ||
sn_compare_rx(entity, sn, v) >= 0)) {
LOG_D(RLC, "%s:%d:%s: warning: STATUS trigger should be delayed, according to specs\n",
__FILE__, __LINE__, __FUNCTION__);
LOG_D(RLC, "warning: STATUS triggerered but should be delayed according to specs\n");
}
}
return;
err:
LOG_W(RLC, "%s:%d:%s: error decoding PDU, discarding\n", __FILE__, __LINE__, __FUNCTION__);
LOG_W(RLC, "RX error decoding PDU, discarding\n");
goto discard;
discard:
......@@ -978,10 +980,7 @@ static nr_rlc_sdu_segment_t *resegment(nr_rlc_sdu_segment_t *sdu,
pdu_header_size = compute_pdu_header_size(entity, sdu);
next = calloc(1, sizeof(nr_rlc_sdu_segment_t));
if (next == NULL) {
LOG_E(RLC, "%s:%d:%s: out of memory\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
AssertFatal(next != NULL, "out of memory\n");
*next = *sdu;
over_size = pdu_header_size + sdu->size - pdu_size;
......@@ -994,7 +993,6 @@ static nr_rlc_sdu_segment_t *resegment(nr_rlc_sdu_segment_t *sdu,
next->size = over_size;
next->so = sdu->so + sdu->size;
next->is_first = 0;
return next;
}
......@@ -1594,6 +1592,8 @@ static int generate_retx_pdu(nr_rlc_entity_am_t *entity, char *buffer,
entity->force_poll = 0;
}
LOG_D(RLC, "RLC TX: sending sdu sn = %d is_first = %d, is_last = %d size = %d\n", sdu->sdu->sn, sdu->is_first, sdu->is_last, size);
int ret_size = serialize_sdu(entity, sdu, buffer, size, p);
entity->common.stats.txpdu_pkts++;
entity->common.stats.txpdu_bytes += ret_size;
......@@ -1612,11 +1612,14 @@ static int generate_tx_pdu(nr_rlc_entity_am_t *entity, char *buffer, int size)
int p;
/* sn out of window (that is: we have window stalling)? do nothing */
if (is_window_stalling(entity))
if (is_window_stalling(entity)) {
LOG_D(RLC, "Abort transmit due to window stall\n");
return 0;
}
if (entity->tx_list == NULL)
if (entity->tx_list == NULL) {
return 0;
}
sdu = entity->tx_list;
......@@ -1645,6 +1648,7 @@ static int generate_tx_pdu(nr_rlc_entity_am_t *entity, char *buffer, int size)
/* segment if necessary */
if (pdu_size > size) {
LOG_D(RLC, "Segmentation (header %d + data %d) / (%d)\n", pdu_header_size, size - pdu_header_size, pdu_size - pdu_header_size);
nr_rlc_sdu_segment_t *next_sdu;
next_sdu = resegment(sdu, entity, size);
/* put the second SDU back at the head of the TX list */
......@@ -1693,7 +1697,7 @@ static int generate_tx_pdu(nr_rlc_entity_am_t *entity, char *buffer, int size)
entity->common.stats.txpdu_pkts++;
entity->common.stats.txpdu_bytes += ret_size;
/* No need to 'zero' time-of-arrival;
/* No need to 'zero' time-of-arrival;
Segmented packets do need to be duplicated in time-sensitive use cases */
if (entity->common.avg_time_is_on) {
uint64_t time_now = time_average_now();
......@@ -1701,6 +1705,14 @@ static int generate_tx_pdu(nr_rlc_entity_am_t *entity, char *buffer, int size)
time_average_add(entity->common.txsdu_avg_time_to_tx, time_now, waited_time);
}
LOG_D(RLC,
"RLC TX: sending sdu sn = %d is_first = %d, is_last = %d header size = %d data size = %d\n",
sdu->sdu->sn,
sdu->is_first,
sdu->is_last,
pdu_header_size,
size - pdu_header_size);
return ret_size;
// return serialize_sdu(entity, sdu, buffer, size, p);
}
......@@ -1730,14 +1742,18 @@ int nr_rlc_entity_am_generate_pdu(nr_rlc_entity_t *_entity,
if (status_to_report(entity)) {
ret = generate_status(entity, buffer, size);
if (ret != 0)
if (ret != 0) {
LOG_D(RLC, "RLC transmit status pdu PDU\n");
return ret;
}
}
if (entity->retransmit_list != NULL) {
ret = generate_retx_pdu(entity, buffer, size);
if (ret != 0)
if (ret != 0) {
LOG_D(RLC, "RLC retransmit PDU\n");
return ret;
}
}
return generate_tx_pdu(entity, buffer, size);
......@@ -1756,11 +1772,7 @@ void nr_rlc_entity_am_recv_sdu(nr_rlc_entity_t *_entity,
entity->common.stats.rxsdu_pkts++;
if (size > NR_SDU_MAX) {
LOG_E(RLC, "%s:%d:%s: fatal: SDU size too big (%d bytes)\n",
__FILE__, __LINE__, __FUNCTION__, size);
exit(1);
}
AssertFatal(size <= NR_SDU_MAX, "Fatal: SDU size too big (%d bytes)\n", size);
/* log SDUs rejected, at most once per second */
if (entity->sdu_rejected != 0
......
......@@ -12,3 +12,13 @@ add_test(NAME nr_rlc_tests
COMMAND exec_nr_rlc_test.sh ${CMAKE_CURRENT_BINARY_DIR}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
add_executable(test_nr_rlc_am_entity test_nr_rlc_am_entity.cpp)
target_link_libraries(test_nr_rlc_am_entity PRIVATE nr_rlc_core GTest::gtest minimal_lib)
add_dependencies(tests test_nr_rlc_am_entity)
add_test(NAME test_nr_rlc_am_entity COMMAND ./test_nr_rlc_am_entity)
add_executable(benchmark_nr_rlc_am_entity benchmark_nr_rlc_am_entity.cpp)
target_link_libraries(benchmark_nr_rlc_am_entity PRIVATE benchmark::benchmark nr_rlc_core UTIL ${T_LIB} minimal_lib)
add_dependencies(tests benchmark_nr_rlc_am_entity)
add_test(NAME benchmark_nr_rlc_am_entity
COMMAND ./benchmark_nr_rlc_am_entity)
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include "benchmark/benchmark.h"
extern "C" {
#include "openair2/LAYER2/nr_rlc/nr_rlc_entity_am.h"
#include "openair2/LAYER2/nr_rlc/nr_rlc_entity.h"
#include "common/utils/LOG/log.h"
#include "common/config/config_load_configmodule.h"
extern configmodule_interface_t *uniqCfg;
}
#include <iostream>
void deliver_sdu(void *deliver_sdu_data, struct nr_rlc_entity_t *entity, char *buf, int size)
{
}
void sdu_successful_delivery(void *sdu_successful_delivery_data, struct nr_rlc_entity_t *entity, int sdu_id)
{
}
void max_retx_reached(void *max_retx_reached_data, struct nr_rlc_entity_t *entity)
{
}
static void BM_nr_rlc_am_entity(benchmark::State &state)
{
nr_rlc_entity_t *tx_entity = new_nr_rlc_entity_am(10000,
10000,
deliver_sdu,
NULL,
sdu_successful_delivery,
NULL,
max_retx_reached,
NULL,
5,
35,
0,
4,
-1,
8,
12);
nr_rlc_entity_t *rx_entity = new_nr_rlc_entity_am(10000,
10000,
deliver_sdu,
NULL,
sdu_successful_delivery,
NULL,
max_retx_reached,
NULL,
5,
35,
0,
4,
-1,
8,
12);
char message[] = "Message to the other RLC AM entity.";
for (auto _ : state) {
char pdu_buf[40];
for (int i = 0; i < state.range(0); i++) {
tx_entity->recv_sdu(tx_entity, message, sizeof(message), i);
int size = tx_entity->generate_pdu(tx_entity, pdu_buf, sizeof(pdu_buf));
if (size != 0) {
// 10% packet loss
if (i % 10 != 0)
rx_entity->recv_pdu(rx_entity, pdu_buf, size);
}
// Allow the lists to build up before sending messages in the other direction
if (i % 100 == 0) {
size = rx_entity->generate_pdu(rx_entity, pdu_buf, sizeof(pdu_buf));
if (size != 0) {
// No packet lost in the other direction
tx_entity->recv_pdu(tx_entity, pdu_buf, size);
}
}
tx_entity->set_time(tx_entity, i + 1);
rx_entity->set_time(tx_entity, i + 1);
}
}
tx_entity->delete_entity(tx_entity);
rx_entity->delete_entity(rx_entity);
}
BENCHMARK(BM_nr_rlc_am_entity)->RangeMultiplier(4)->Range(100, 20000);
int main(int argc, char **argv)
{
logInit();
uniqCfg = load_configmodule(argc, argv, CONFIG_ENABLECMDLINEONLY);
g_log->log_component[RLC].level = -1;
benchmark::Initialize(&argc, argv);
benchmark::RunSpecifiedBenchmarks();
return 0;
}
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include "gtest/gtest.h"
extern "C" {
#include "openair2/LAYER2/nr_rlc/nr_rlc_entity_am.h"
#include "openair2/LAYER2/nr_rlc/nr_rlc_entity.h"
#include "common/utils/LOG/log.h"
#include "common/config/config_load_configmodule.h"
extern configmodule_interface_t *uniqCfg;
}
#include <iostream>
int sdu_delivered_count = 0;
int sdu_acked_count = 0;
void deliver_sdu(void *deliver_sdu_data, struct nr_rlc_entity_t *entity, char *buf, int size)
{
sdu_delivered_count++;
char payload[300];
ASSERT_LE(size, sizeof(payload));
snprintf(payload, size, "%s", buf);
std::cout << "Delivered sdu: " << payload << std::endl;
}
void sdu_successful_delivery(void *sdu_successful_delivery_data, struct nr_rlc_entity_t *entity, int sdu_id)
{
sdu_acked_count++;
std::cout << "SDU " << sdu_id << " acked" << std::endl;
}
void max_retx_reached(void *max_retx_reached_data, struct nr_rlc_entity_t *entity)
{
}
TEST(nr_rlc_am_entity, test_init)
{
nr_rlc_entity_t *entity = new_nr_rlc_entity_am(100,
100,
deliver_sdu,
NULL,
sdu_successful_delivery,
NULL,
max_retx_reached,
NULL,
45,
35,
0,
-1,
-1,
8,
12);
char buf[30];
EXPECT_EQ(entity->generate_pdu(entity, buf, sizeof(buf)), 0) << "No PDCP SDU provided to RLC, expected no RLC PDUS";
entity->delete_entity(entity);
}
TEST(nr_rlc_am_entity, test_segmentation_reassembly)
{
nr_rlc_entity_t *tx_entity = new_nr_rlc_entity_am(100,
100,
deliver_sdu,
NULL,
sdu_successful_delivery,
NULL,
max_retx_reached,
NULL,
45,
35,
0,
-1,
-1,
8,
12);
nr_rlc_entity_t *rx_entity = new_nr_rlc_entity_am(100,
100,
deliver_sdu,
NULL,
sdu_successful_delivery,
NULL,
max_retx_reached,
NULL,
45,
35,
0,
-1,
-1,
8,
12);
char buf[30] = {0};
snprintf(buf, sizeof(buf), "%s", "Message");
EXPECT_EQ(tx_entity->generate_pdu(tx_entity, buf, sizeof(buf)), 0) << "No PDCP SDU provided to RLC, expected no RLC PDUS";
// Higher layer IF -> deliver PDCP SDU
tx_entity->recv_sdu(tx_entity, buf, sizeof(buf), 0);
sdu_delivered_count = 0;
for (auto i = 0; i < 10; i++) {
if (sdu_delivered_count == 1) {
break;
}
int size = tx_entity->generate_pdu(tx_entity, buf, 10);
EXPECT_GT(size, 0);
rx_entity->recv_pdu(rx_entity, buf, size);
}
EXPECT_EQ(sdu_delivered_count, 1);
sdu_acked_count = 0;
int size = rx_entity->generate_pdu(rx_entity, buf, 30);
tx_entity->recv_pdu(tx_entity, buf, size);
EXPECT_EQ(sdu_acked_count, 1);
tx_entity->delete_entity(tx_entity);
rx_entity->delete_entity(rx_entity);
}
TEST(nr_rlc_am_entity, test_ack_out_of_order)
{
nr_rlc_entity_t *tx_entity = new_nr_rlc_entity_am(100,
100,
deliver_sdu,
NULL,
sdu_successful_delivery,
NULL,
max_retx_reached,
NULL,
5,
35,
0,
4,
-1,
8,
12);
nr_rlc_entity_t *rx_entity = new_nr_rlc_entity_am(100,
100,
deliver_sdu,
NULL,
sdu_successful_delivery,
NULL,
max_retx_reached,
NULL,
5,
35,
0,
4,
-1,
8,
12);
char buf[30] = {0};
EXPECT_EQ(tx_entity->generate_pdu(tx_entity, buf, sizeof(buf)), 0) << "No PDCP SDU provided to RLC, expected no RLC PDUS";
// Higher layer IF -> deliver PDCP SDU
// Generate 4 PDCP SDUS
snprintf(buf, sizeof(buf), "%s", "Message 1");
tx_entity->recv_sdu(tx_entity, buf, sizeof(buf), 0);
snprintf(buf, sizeof(buf), "%s", "Message 2");
tx_entity->recv_sdu(tx_entity, buf, sizeof(buf), 1);
snprintf(buf, sizeof(buf), "%s", "Message 3");
tx_entity->recv_sdu(tx_entity, buf, sizeof(buf), 2);
sdu_delivered_count = 0;
sdu_acked_count = 0;
char pdu_buf[40];
uint64_t time = 0;
for (auto i = 0; i < 3; i++) {
int size = tx_entity->generate_pdu(tx_entity, pdu_buf, sizeof(pdu_buf));
EXPECT_GT(size, 0);
// Do not deliver PDU 2 to RX entity
if (i != 1) {
rx_entity->recv_pdu(rx_entity, pdu_buf, size);
}
tx_entity->set_time(tx_entity, time);
rx_entity->set_time(tx_entity, time);
time++;
}
EXPECT_EQ(sdu_delivered_count, 2) << "Expected 2 out of 3 SDUs delivered";
int size = rx_entity->generate_pdu(rx_entity, pdu_buf, sizeof(pdu_buf));
EXPECT_GT(size, 0);
tx_entity->recv_pdu(tx_entity, pdu_buf, size);
// Triggers t-poll-retransmit and retransmission of the lost PDU
for (auto i = 0; i < 5; i++) {
tx_entity->set_time(tx_entity, time);
rx_entity->set_time(tx_entity, time);
size = tx_entity->generate_pdu(tx_entity, pdu_buf, sizeof(pdu_buf));
if (size != 0)
rx_entity->recv_pdu(rx_entity, pdu_buf, size);
size = rx_entity->generate_pdu(rx_entity, pdu_buf, sizeof(pdu_buf));
if (size != 0)
tx_entity->recv_pdu(tx_entity, pdu_buf, size);
time++;
}
EXPECT_EQ(sdu_acked_count, 3);
EXPECT_EQ(sdu_delivered_count, 3);
tx_entity->delete_entity(tx_entity);
rx_entity->delete_entity(rx_entity);
}
int main(int argc, char **argv)
{
logInit();
uniqCfg = load_configmodule(argc, argv, CONFIG_ENABLECMDLINEONLY);
g_log->log_component[RLC].level = OAILOG_TRACE;
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment