From c4181c9082a918a796c20679f9617d2182c035c1 Mon Sep 17 00:00:00 2001 From: Aleksander Wasaznik Date: Thu, 1 Aug 2024 14:25:51 +0200 Subject: [PATCH] [nrf fromtree] 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 (cherry picked from commit 47325f8df5649cfdfff9d19504e79b5635e35a08) --- tests/bsim/bluetooth/host/l2cap/compile.sh | 1 + .../host/l2cap/einprogress/CMakeLists.txt | 24 ++++ .../host/l2cap/einprogress/compile.sh | 13 ++ .../bluetooth/host/l2cap/einprogress/prj.conf | 25 ++++ .../host/l2cap/einprogress/src/data.h | 11 ++ .../host/l2cap/einprogress/src/dut.c | 124 ++++++++++++++++++ .../host/l2cap/einprogress/src/main.c | 47 +++++++ .../host/l2cap/einprogress/src/tester.c | 72 ++++++++++ .../l2cap/einprogress/test_scripts/run.sh | 26 ++++ 9 files changed, 343 insertions(+) create mode 100644 tests/bsim/bluetooth/host/l2cap/einprogress/CMakeLists.txt create mode 100755 tests/bsim/bluetooth/host/l2cap/einprogress/compile.sh create mode 100644 tests/bsim/bluetooth/host/l2cap/einprogress/prj.conf create mode 100644 tests/bsim/bluetooth/host/l2cap/einprogress/src/data.h create mode 100644 tests/bsim/bluetooth/host/l2cap/einprogress/src/dut.c create mode 100644 tests/bsim/bluetooth/host/l2cap/einprogress/src/main.c create mode 100644 tests/bsim/bluetooth/host/l2cap/einprogress/src/tester.c create mode 100755 tests/bsim/bluetooth/host/l2cap/einprogress/test_scripts/run.sh diff --git a/tests/bsim/bluetooth/host/l2cap/compile.sh b/tests/bsim/bluetooth/host/l2cap/compile.sh index 71d41a7f2e7..ddb7f5d14bc 100755 --- a/tests/bsim/bluetooth/host/l2cap/compile.sh +++ b/tests/bsim/bluetooth/host/l2cap/compile.sh @@ -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 diff --git a/tests/bsim/bluetooth/host/l2cap/einprogress/CMakeLists.txt b/tests/bsim/bluetooth/host/l2cap/einprogress/CMakeLists.txt new file mode 100644 index 00000000000..b6d0135f936 --- /dev/null +++ b/tests/bsim/bluetooth/host/l2cap/einprogress/CMakeLists.txt @@ -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 +) diff --git a/tests/bsim/bluetooth/host/l2cap/einprogress/compile.sh b/tests/bsim/bluetooth/host/l2cap/einprogress/compile.sh new file mode 100755 index 00000000000..e717a4b2bbe --- /dev/null +++ b/tests/bsim/bluetooth/host/l2cap/einprogress/compile.sh @@ -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 diff --git a/tests/bsim/bluetooth/host/l2cap/einprogress/prj.conf b/tests/bsim/bluetooth/host/l2cap/einprogress/prj.conf new file mode 100644 index 00000000000..e2540cd6f6a --- /dev/null +++ b/tests/bsim/bluetooth/host/l2cap/einprogress/prj.conf @@ -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 diff --git a/tests/bsim/bluetooth/host/l2cap/einprogress/src/data.h b/tests/bsim/bluetooth/host/l2cap/einprogress/src/data.h new file mode 100644 index 00000000000..4920d909d08 --- /dev/null +++ b/tests/bsim/bluetooth/host/l2cap/einprogress/src/data.h @@ -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_ */ diff --git a/tests/bsim/bluetooth/host/l2cap/einprogress/src/dut.c b/tests/bsim/bluetooth/host/l2cap/einprogress/src/dut.c new file mode 100644 index 00000000000..2c026befd20 --- /dev/null +++ b/tests/bsim/bluetooth/host/l2cap/einprogress/src/dut.c @@ -0,0 +1,124 @@ +/* Copyright (c) 2024 Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#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"); +} diff --git a/tests/bsim/bluetooth/host/l2cap/einprogress/src/main.c b/tests/bsim/bluetooth/host/l2cap/einprogress/src/main.c new file mode 100644 index 00000000000..d6611e8f8dd --- /dev/null +++ b/tests/bsim/bluetooth/host/l2cap/einprogress/src/main.c @@ -0,0 +1,47 @@ +/* Copyright (c) 2024 Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#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; +} diff --git a/tests/bsim/bluetooth/host/l2cap/einprogress/src/tester.c b/tests/bsim/bluetooth/host/l2cap/einprogress/src/tester.c new file mode 100644 index 00000000000..2e2d000aa9c --- /dev/null +++ b/tests/bsim/bluetooth/host/l2cap/einprogress/src/tester.c @@ -0,0 +1,72 @@ +/* Copyright (c) 2024 Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#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"); +} diff --git a/tests/bsim/bluetooth/host/l2cap/einprogress/test_scripts/run.sh b/tests/bsim/bluetooth/host/l2cap/einprogress/test_scripts/run.sh new file mode 100755 index 00000000000..b86db52d9df --- /dev/null +++ b/tests/bsim/bluetooth/host/l2cap/einprogress/test_scripts/run.sh @@ -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