From 71ca732f97f439d310c80be1ee5f0e12cf2e6570 Mon Sep 17 00:00:00 2001 From: Christopher Friedt Date: Mon, 27 Jun 2022 23:43:38 -0400 Subject: [PATCH] tests: kernel: threads: add a testcase for dynamic thread stacks Test that automatic thread stack allocation works for both user and kernel threads. Signed-off-by: Christopher Friedt --- .../dynamic_thread_stack/CMakeLists.txt | 8 + .../threads/dynamic_thread_stack/prj.conf | 14 ++ .../threads/dynamic_thread_stack/src/main.c | 142 ++++++++++++++++++ .../dynamic_thread_stack/testcase.yaml | 76 ++++++++++ 4 files changed, 240 insertions(+) create mode 100644 tests/kernel/threads/dynamic_thread_stack/CMakeLists.txt create mode 100644 tests/kernel/threads/dynamic_thread_stack/prj.conf create mode 100644 tests/kernel/threads/dynamic_thread_stack/src/main.c create mode 100644 tests/kernel/threads/dynamic_thread_stack/testcase.yaml diff --git a/tests/kernel/threads/dynamic_thread_stack/CMakeLists.txt b/tests/kernel/threads/dynamic_thread_stack/CMakeLists.txt new file mode 100644 index 00000000000000..871a66a4527945 --- /dev/null +++ b/tests/kernel/threads/dynamic_thread_stack/CMakeLists.txt @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr HINTS $ENV{ZEPHYR_BASE}) +project(dynamic_thread_stack) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/tests/kernel/threads/dynamic_thread_stack/prj.conf b/tests/kernel/threads/dynamic_thread_stack/prj.conf new file mode 100644 index 00000000000000..4377c2ec60b4f1 --- /dev/null +++ b/tests/kernel/threads/dynamic_thread_stack/prj.conf @@ -0,0 +1,14 @@ +CONFIG_ZTEST=y +CONFIG_ZTEST_NEW_API=y +CONFIG_INIT_STACKS=y +CONFIG_THREAD_STACK_INFO=y +CONFIG_MAX_THREAD_BYTES=5 +CONFIG_DYNAMIC_THREAD=y +CONFIG_DYNAMIC_THREAD_POOL_SIZE=2 +CONFIG_DYNAMIC_THREAD_ALLOC=y +CONFIG_HEAP_MEM_POOL_SIZE=16384 +CONFIG_ZTEST_STACK_SIZE=2048 +CONFIG_MAIN_STACK_SIZE=2048 + +CONFIG_HW_STACK_PROTECTION=n +CONFIG_TEST_HW_STACK_PROTECTION=n diff --git a/tests/kernel/threads/dynamic_thread_stack/src/main.c b/tests/kernel/threads/dynamic_thread_stack/src/main.c new file mode 100644 index 00000000000000..86848e4249ee9c --- /dev/null +++ b/tests/kernel/threads/dynamic_thread_stack/src/main.c @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2022, Meta + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#define TIMEOUT_MS 500 + +#ifdef CONFIG_USERSPACE +#define STACK_OBJ_SIZE Z_THREAD_STACK_SIZE_ADJUST(CONFIG_DYNAMIC_THREAD_STACK_SIZE) +#else +#define STACK_OBJ_SIZE Z_KERNEL_STACK_SIZE_ADJUST(CONFIG_DYNAMIC_THREAD_STACK_SIZE) +#endif + +#define MAX_HEAP_STACKS (CONFIG_HEAP_MEM_POOL_SIZE / STACK_OBJ_SIZE) + +ZTEST_DMEM bool flag[CONFIG_DYNAMIC_THREAD_POOL_SIZE]; + +static void func(void *arg1, void *arg2, void *arg3) +{ + bool *flag = (bool *)arg1; + + ARG_UNUSED(arg2); + ARG_UNUSED(arg3); + + printk("Hello, dynamic world!\n"); + + *flag = true; +} + +/** @brief Exercise the pool-based thread stack allocator */ +ZTEST(dynamic_thread_stack, test_dynamic_thread_stack_pool) +{ + static k_tid_t tid[CONFIG_DYNAMIC_THREAD_POOL_SIZE]; + static struct k_thread th[CONFIG_DYNAMIC_THREAD_POOL_SIZE]; + static k_thread_stack_t *stack[CONFIG_DYNAMIC_THREAD_POOL_SIZE]; + + if (!IS_ENABLED(CONFIG_DYNAMIC_THREAD_PREFER_POOL)) { + ztest_test_skip(); + } + + /* allocate all thread stacks from the pool */ + for (size_t i = 0; i < CONFIG_DYNAMIC_THREAD_POOL_SIZE; ++i) { + stack[i] = k_thread_stack_alloc(CONFIG_DYNAMIC_THREAD_STACK_SIZE, + IS_ENABLED(CONFIG_USERSPACE) ? K_USER : 0); + + zassert_not_null(stack[i]); + } + + if (IS_ENABLED(CONFIG_DYNAMIC_THREAD_ALLOC)) { + /* ensure 1 thread can be allocated from the heap when the pool is depleted */ + zassert_ok(k_thread_stack_free( + k_thread_stack_alloc(CONFIG_DYNAMIC_THREAD_STACK_SIZE, + IS_ENABLED(CONFIG_USERSPACE) ? K_USER : 0))); + } else { + /* ensure that no more thread stacks can be allocated from the pool */ + zassert_is_null(k_thread_stack_alloc(CONFIG_DYNAMIC_THREAD_STACK_SIZE, + IS_ENABLED(CONFIG_USERSPACE) ? K_USER : 0)); + } + + /* spawn our threads */ + for (size_t i = 0; i < CONFIG_DYNAMIC_THREAD_POOL_SIZE; ++i) { + tid[i] = k_thread_create(&th[i], stack[i], + CONFIG_DYNAMIC_THREAD_STACK_SIZE, func, + &flag[i], NULL, NULL, 0, + K_USER | K_INHERIT_PERMS, K_NO_WAIT); + } + + /* join all threads and check that flags have been set */ + for (size_t i = 0; i < CONFIG_DYNAMIC_THREAD_POOL_SIZE; ++i) { + zassert_ok(k_thread_join(tid[i], K_MSEC(TIMEOUT_MS))); + zassert_true(flag[i]); + } + + /* clean up stacks allocated from the pool */ + for (size_t i = 0; i < CONFIG_DYNAMIC_THREAD_POOL_SIZE; ++i) { + zassert_ok(k_thread_stack_free(stack[i])); + } +} + +/** @brief Exercise the heap-based thread stack allocator */ +ZTEST(dynamic_thread_stack, test_dynamic_thread_stack_alloc) +{ + size_t N; + static k_tid_t tid[MAX_HEAP_STACKS]; + static bool flag[MAX_HEAP_STACKS]; + static struct k_thread th[MAX_HEAP_STACKS]; + static k_thread_stack_t *stack[MAX_HEAP_STACKS]; + + if (!IS_ENABLED(CONFIG_DYNAMIC_THREAD_PREFER_ALLOC)) { + ztest_test_skip(); + } + + if (!IS_ENABLED(CONFIG_DYNAMIC_THREAD_ALLOC)) { + ztest_test_skip(); + } + + /* allocate all thread stacks from the heap */ + for (N = 0; N < MAX_HEAP_STACKS; ++N) { + stack[N] = k_thread_stack_alloc(CONFIG_DYNAMIC_THREAD_STACK_SIZE, + IS_ENABLED(CONFIG_USERSPACE) ? K_USER : 0); + zassert_not_null(stack[N]); + } + + if (CONFIG_DYNAMIC_THREAD_POOL_SIZE == 0) { + /* ensure that no more thread stacks can be allocated from the heap */ + zassert_is_null(k_thread_stack_alloc(CONFIG_DYNAMIC_THREAD_STACK_SIZE, + IS_ENABLED(CONFIG_USERSPACE) ? K_USER : 0)); + } + + /* spwan our threads */ + for (size_t i = 0; i < N; ++i) { + tid[i] = k_thread_create(&th[i], stack[i], 0, func, &flag[i], NULL, NULL, 0, + K_USER | K_INHERIT_PERMS, K_NO_WAIT); + } + + /* join all threads and check that flags have been set */ + for (size_t i = 0; i < N; ++i) { + zassert_ok(k_thread_join(tid[i], K_MSEC(TIMEOUT_MS))); + zassert_true(flag[i]); + } + + /* clean up stacks allocated from the heap */ + for (size_t i = 0; i < N; ++i) { + zassert_ok(k_thread_stack_free(stack[i])); + } +} + +static void *dynamic_thread_stack_setup(void) +{ +#ifdef CONFIG_USERSPACE + k_thread_system_pool_assign(k_current_get()); + /* k_thread_access_grant(k_current_get(), ... ); */ +#endif + + return NULL; +} + +ZTEST_SUITE(dynamic_thread_stack, NULL, dynamic_thread_stack_setup, NULL, NULL, NULL); diff --git a/tests/kernel/threads/dynamic_thread_stack/testcase.yaml b/tests/kernel/threads/dynamic_thread_stack/testcase.yaml new file mode 100644 index 00000000000000..3bf7d892b5797b --- /dev/null +++ b/tests/kernel/threads/dynamic_thread_stack/testcase.yaml @@ -0,0 +1,76 @@ +common: + tags: kernel security + min_ram: 32 + integration_platforms: + - qemu_x86 + - qemu_x86_nommu + - qemu_x86_64 + - qemu_cortex_a53 + - qemu_cortex_a53_smp + - qemu_cortex_m3 + - qemu_riscv32 + - qemu_riscv32e + - qemu_riscv64 + - qemu_riscv64_smp + +# Permutations of (pool | alloc | user) +tests: + kernel.threads.dynamic_thread.stack.no_pool.no_alloc.no_user: + extra_configs: + # 000 + - CONFIG_DYNAMIC_THREAD_POOL_SIZE=0 + - CONFIG_DYNAMIC_THREAD_ALLOC=n + - CONFIG_USERSPACE=n + + # kernel.threads.dynamic_thread.stack.no_pool.no_alloc.user: + # tags: userspace + # extra_configs: + # # 001 + # - CONFIG_DYNAMIC_THREAD_POOL_SIZE=0 + # - CONFIG_DYNAMIC_THREAD_ALLOC=n + # - CONFIG_USERSPACE=y + + kernel.threads.dynamic_thread.stack.no_pool.alloc.no_user: + extra_configs: + # 010 + - CONFIG_DYNAMIC_THREAD_POOL_SIZE=0 + - CONFIG_DYNAMIC_THREAD_ALLOC=y + - CONFIG_USERSPACE=n + + # kernel.threads.dynamic_thread.stack.no_pool.alloc.user: + # tags: userspace + # extra_configs: + # # 011 + # - CONFIG_DYNAMIC_THREAD_POOL_SIZE=0 + # - CONFIG_DYNAMIC_THREAD_ALLOC=y + # - CONFIG_USERSPACE=y + + kernel.threads.dynamic_thread.stack.pool.no_alloc.no_user: + extra_configs: + # 100 + - CONFIG_DYNAMIC_THREAD_POOL_SIZE=2 + - CONFIG_DYNAMIC_THREAD_ALLOC=n + - CONFIG_USERSPACE=n + + # kernel.threads.dynamic_thread.stack.pool.no_alloc.user: + # tags: userspace + # extra_configs: + # # 101 + # - CONFIG_DYNAMIC_THREAD_POOL_SIZE=2 + # - CONFIG_DYNAMIC_THREAD_ALLOC=n + # - CONFIG_USERSPACE=y + + kernel.threads.dynamic_thread.stack.pool.alloc.no_user: + extra_configs: + # 110 + - CONFIG_DYNAMIC_THREAD_POOL_SIZE=2 + - CONFIG_DYNAMIC_THREAD_ALLOC=y + - CONFIG_USERSPACE=n + +# kernel.threads.dynamic_thread.stack.pool.alloc.user: +# tags: userspace +# extra_configs: +# # 111 +# - CONFIG_DYNAMIC_THREAD_POOL_SIZE=2 +# - CONFIG_DYNAMIC_THREAD_ALLOC=y +# - CONFIG_USERSPACE=y