Bluetooth: Host: Test L2CAP -EINPROGRESS feature

The test implementation is based on a copy of the HFC multilink test.
The test verifies that the stack respects the reference counting of SDU
buffers when the L2CAP -EINPROGRESS feature is used.

Signed-off-by: Aleksander Wasaznik <aleksander.wasaznik@nordicsemi.no>
This commit is contained in:
Aleksander Wasaznik 2024-08-01 14:25:51 +02:00 committed by Henrik Brix Andersen
commit 47325f8df5
9 changed files with 343 additions and 0 deletions

View file

@ -19,6 +19,7 @@ app=tests/bsim/bluetooth/host/l2cap/stress conf_file=prj_nofrag.conf compile
app=tests/bsim/bluetooth/host/l2cap/stress conf_file=prj_syswq.conf compile
run_in_background ${ZEPHYR_BASE}/tests/bsim/bluetooth/host/l2cap/split/compile.sh
run_in_background ${ZEPHYR_BASE}/tests/bsim/bluetooth/host/l2cap/reassembly/compile.sh
run_in_background ${ZEPHYR_BASE}/tests/bsim/bluetooth/host/l2cap/einprogress/compile.sh
run_in_background ${ZEPHYR_BASE}/tests/bsim/bluetooth/host/l2cap/ecred/compile.sh
app=tests/bsim/bluetooth/host/l2cap/credits compile
app=tests/bsim/bluetooth/host/l2cap/credits conf_file=prj_ecred.conf compile

View file

@ -0,0 +1,24 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(test_l2cap_einprogress)
add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/common/testlib testlib)
target_link_libraries(app PRIVATE testlib)
add_subdirectory(${ZEPHYR_BASE}/tests/bsim/babblekit babblekit)
target_link_libraries(app PRIVATE babblekit)
zephyr_include_directories(
${BSIM_COMPONENTS_PATH}/libUtilv1/src/
${BSIM_COMPONENTS_PATH}/libPhyComv1/src/
)
target_sources(app PRIVATE
src/main.c
src/dut.c
src/tester.c
)

View file

@ -0,0 +1,13 @@
#!/usr/bin/env bash
# Copyright 2023 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
set -eu
: "${ZEPHYR_BASE:?ZEPHYR_BASE must be defined}"
INCR_BUILD=1
source ${ZEPHYR_BASE}/tests/bsim/compile.source
app="$(guess_test_relpath)" compile
wait_for_background_jobs

View file

@ -0,0 +1,25 @@
CONFIG_LOG=y
CONFIG_ASSERT=y
CONFIG_THREAD_NAME=y
CONFIG_LOG_THREAD_ID_PREFIX=y
CONFIG_ARCH_POSIX_TRAP_ON_FATAL=y
CONFIG_BT_TESTING=y
CONFIG_BT_HCI_ACL_FLOW_CONTROL=y
CONFIG_BT=y
CONFIG_BT_CENTRAL=y
CONFIG_BT_PERIPHERAL=y
# Dependency of testlib/adv and testlib/scan.
CONFIG_BT_EXT_ADV=y
# Dynamic channel depends on SMP
CONFIG_BT_SMP=y
CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y
# Disable auto-initiated procedures so they don't
# mess with the test's execution.
CONFIG_BT_AUTO_PHY_UPDATE=n
CONFIG_BT_AUTO_DATA_LEN_UPDATE=n
CONFIG_BT_GAP_AUTO_UPDATE_CONN_PARAMS=n

View file

@ -0,0 +1,11 @@
/* Copyright (c) 2024 Nordic Semiconductor ASA
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_TESTS_BSIM_BLUETOOTH_HOST_L2CAP_EINPROGRESS_SRC_DATA_H_
#define ZEPHYR_TESTS_BSIM_BLUETOOTH_HOST_L2CAP_EINPROGRESS_SRC_DATA_H_
#define TEST_DATA_L2CAP_PSM 0x0080
#define TEST_DATA_DUT_ADDR BT_TESTLIB_ADDR_LE_RANDOM_C0_00_00_00_00_(0x01)
#endif /* ZEPHYR_TESTS_BSIM_BLUETOOTH_HOST_L2CAP_EINPROGRESS_SRC_DATA_H_ */

View file

@ -0,0 +1,124 @@
/* Copyright (c) 2024 Nordic Semiconductor ASA
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/l2cap.h>
#include <zephyr/bluetooth/testing.h>
#include <zephyr/logging/log.h>
#include <zephyr/net_buf.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/sys/atomic.h>
#include <zephyr/sys/util_macro.h>
#include <testlib/addr.h>
#include <testlib/adv.h>
#include <testlib/conn.h>
#include <testlib/scan.h>
#include <babblekit/flags.h>
#include <babblekit/testcase.h>
#include "data.h"
LOG_MODULE_REGISTER(dut, LOG_LEVEL_INF);
/** Here we keep track of the reference count in the test
* application. This allows us to notice if the stack has freed
* references that were ours.
*/
static atomic_t acl_pool_refs_held[CONFIG_BT_BUF_ACL_RX_COUNT];
BUILD_ASSERT(IS_ENABLED(CONFIG_BT_TESTING));
BUILD_ASSERT(IS_ENABLED(CONFIG_BT_HCI_ACL_FLOW_CONTROL));
void bt_testing_trace_event_acl_pool_destroy(struct net_buf *destroyed_buf)
{
int buf_id = net_buf_id(destroyed_buf);
__ASSERT_NO_MSG(0 <= buf_id && buf_id < ARRAY_SIZE(acl_pool_refs_held));
TEST_ASSERT(acl_pool_refs_held[buf_id] == 0,
"ACL buf was destroyed while tester still held a reference");
}
static void acl_pool_refs_held_add(struct net_buf *buf)
{
int buf_id = net_buf_id(buf);
__ASSERT_NO_MSG(0 <= buf_id && buf_id < CONFIG_BT_BUF_ACL_RX_COUNT);
atomic_inc(&acl_pool_refs_held[buf_id]);
}
static void acl_pool_refs_held_remove(struct net_buf *buf)
{
int buf_id = net_buf_id(buf);
__ASSERT_NO_MSG(0 <= buf_id && buf_id < ARRAY_SIZE(acl_pool_refs_held));
atomic_val_t old = atomic_dec(&acl_pool_refs_held[buf_id]);
__ASSERT(old != 0, "Tester error: releasing a reference that was not held");
}
struct k_fifo ack_todo;
static int dut_chan_recv_cb(struct bt_l2cap_chan *chan, struct net_buf *buf)
{
/* Move buf. Ownership is ours if we return -EINPROGRESS. */
acl_pool_refs_held_add(buf);
k_fifo_put(&ack_todo, buf);
return -EINPROGRESS;
}
static const struct bt_l2cap_chan_ops ops = {
.recv = dut_chan_recv_cb,
};
static struct bt_l2cap_le_chan le_chan = {
.chan.ops = &ops,
};
static int dut_server_accept_cb(struct bt_conn *conn, struct bt_l2cap_server *server,
struct bt_l2cap_chan **chan)
{
*chan = &le_chan.chan;
return 0;
}
static struct bt_l2cap_server test_l2cap_server = {
.accept = dut_server_accept_cb,
.psm = TEST_DATA_L2CAP_PSM,
};
void entrypoint_dut(void)
{
struct net_buf *ack_buf;
struct bt_conn *conn = NULL;
int err;
TEST_START("dut");
k_fifo_init(&ack_todo);
err = bt_id_create(&TEST_DATA_DUT_ADDR, NULL);
__ASSERT_NO_MSG(!err);
err = bt_enable(NULL);
__ASSERT_NO_MSG(!err);
err = bt_l2cap_server_register(&test_l2cap_server);
__ASSERT_NO_MSG(!err);
err = bt_testlib_adv_conn(&conn, BT_ID_DEFAULT, NULL);
__ASSERT_NO_MSG(!err);
ack_buf = k_fifo_get(&ack_todo, K_FOREVER);
__ASSERT_NO_MSG(ack_buf);
acl_pool_refs_held_remove(ack_buf);
err = bt_l2cap_chan_recv_complete(&le_chan.chan, ack_buf);
TEST_ASSERT(!err);
TEST_PASS_AND_EXIT("dut");
}

View file

@ -0,0 +1,47 @@
/* Copyright (c) 2024 Nordic Semiconductor ASA
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include "bstests.h"
#include "babblekit/testcase.h"
extern void entrypoint_dut(void);
extern void entrypoint_tester(void);
extern enum bst_result_t bst_result;
static void test_end_cb(void)
{
if (bst_result != Passed) {
TEST_PRINT("Test has not passed.");
}
}
static const struct bst_test_instance entrypoints[] = {
{
.test_id = "l2cap/einprogress/dut",
.test_delete_f = test_end_cb,
.test_main_f = entrypoint_dut,
},
{
.test_id = "l2cap/einprogress/tester",
.test_delete_f = test_end_cb,
.test_main_f = entrypoint_tester,
},
BSTEST_END_MARKER,
};
static struct bst_test_list *install(struct bst_test_list *tests)
{
return bst_add_tests(tests, entrypoints);
};
bst_test_install_t test_installers[] = {install, NULL};
int main(void)
{
bst_main();
return 0;
}

View file

@ -0,0 +1,72 @@
/* Copyright (c) 2024 Nordic Semiconductor ASA
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/l2cap.h>
#include <zephyr/logging/log.h>
#include <zephyr/net_buf.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/sys/atomic.h>
#include <zephyr/sys/util_macro.h>
#include <testlib/addr.h>
#include <testlib/adv.h>
#include <testlib/conn.h>
#include <testlib/scan.h>
#include <babblekit/flags.h>
#include <babblekit/testcase.h>
#include "data.h"
LOG_MODULE_REGISTER(tester, LOG_LEVEL_INF);
static int tester_chan_recv_cb(struct bt_l2cap_chan *chan, struct net_buf *buf)
{
__ASSERT(false, "Unexpected recv in tester");
return 0;
};
static struct bt_l2cap_le_chan le_chan = {
.chan.ops =
&(const struct bt_l2cap_chan_ops){
.recv = tester_chan_recv_cb,
},
};
NET_BUF_POOL_DEFINE(test_pool, 1, BT_L2CAP_SDU_BUF_SIZE(0), CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL);
void entrypoint_tester(void)
{
struct net_buf *sdu;
struct bt_conn *conn = NULL;
int err;
TEST_START("tester");
err = bt_enable(NULL);
__ASSERT_NO_MSG(!err);
err = bt_testlib_connect(&TEST_DATA_DUT_ADDR, &conn);
__ASSERT_NO_MSG(!err);
err = bt_l2cap_chan_connect(conn, &le_chan.chan, TEST_DATA_L2CAP_PSM);
__ASSERT_NO_MSG(!err);
/* Wait for async L2CAP connect */
while (!atomic_test_bit(le_chan.chan.status, BT_L2CAP_STATUS_OUT)) {
k_sleep(K_MSEC(100));
}
sdu = net_buf_alloc(&test_pool, K_NO_WAIT);
__ASSERT_NO_MSG(sdu);
net_buf_reserve(sdu, BT_L2CAP_SDU_CHAN_SEND_RESERVE);
err = bt_l2cap_chan_send(&le_chan.chan, sdu);
__ASSERT(!err, "err: %d", err);
TEST_PASS("tester");
}

View file

@ -0,0 +1,26 @@
#!/usr/bin/env bash
# Copyright (c) 2024 Nordic Semiconductor
# SPDX-License-Identifier: Apache-2.0
set -eu
source ${ZEPHYR_BASE}/tests/bsim/sh_common.source
test_name="$(guess_test_long_name)"
simulation_id=${test_name}
verbosity_level=2
EXECUTE_TIMEOUT=120
SIM_LEN_US=$((2 * 1000 * 1000))
test_exe="${BSIM_OUT_PATH}/bin/bs_${BOARD_TS}_${test_name}_prj_conf"
cd ${BSIM_OUT_PATH}/bin
Execute "${test_exe}" -v=${verbosity_level} -s=${simulation_id} -d=0 \
-testid=l2cap/einprogress/dut
Execute "${test_exe}" -v=${verbosity_level} -s=${simulation_id} -d=1 \
-testid=l2cap/einprogress/tester
Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} -D=2 -sim_length=${SIM_LEN_US} $@
wait_for_background_jobs