Skip to content

Commit 7b1b257

Browse files
committed
kernel: support dynamic thread stack allocation
Add support for dynamic thread stack allocation Signed-off-by: Christopher Friedt <[email protected]>
1 parent 8c32950 commit 7b1b257

File tree

4 files changed

+259
-0
lines changed

4 files changed

+259
-0
lines changed

include/zephyr/kernel.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,35 @@ extern void k_thread_foreach_unlocked(
265265
/* end - thread options */
266266

267267
#if !defined(_ASMLANGUAGE)
268+
/**
269+
* @brief Dynamically allocate a thread stack.
270+
*
271+
* Relevant stack creation flags include:
272+
* - @ref K_USER allocate a userspace thread (requires `CONFIG_USERSPACE=y`)
273+
*
274+
* @param size Stack size in bytes.
275+
* @param flags Stack creation flags, or 0.
276+
*
277+
* @retval the allocated thread stack on success.
278+
* @retval NULL on failure.
279+
*
280+
* @see CONFIG_DYNAMIC_THREAD
281+
*/
282+
__syscall k_thread_stack_t *k_thread_stack_alloc(size_t size, int flags);
283+
284+
/**
285+
* @brief Free a dynamically allocated thread stack.
286+
*
287+
* @param stack Pointer to the thread stack.
288+
*
289+
* @retval 0 on success.
290+
* @retval -EBUSY if the thread stack is in use.
291+
* @retval -EINVAL if @p stack is invalid.
292+
*
293+
* @see CONFIG_DYNAMIC_THREAD
294+
*/
295+
__syscall int k_thread_stack_free(k_thread_stack_t *stack);
296+
268297
/**
269298
* @brief Create a thread.
270299
*

kernel/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,12 @@ target_sources_ifdef(
122122
userspace.c
123123
)
124124

125+
target_sources_ifdef(
126+
CONFIG_DYNAMIC_THREAD
127+
kernel PRIVATE
128+
dynamic.c
129+
)
130+
125131
target_include_directories(kernel PRIVATE
126132
${ZEPHYR_BASE}/kernel/include
127133
${ARCH_DIR}/${ARCH}/include

kernel/Kconfig

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,68 @@ config THREAD_USERSPACE_LOCAL_DATA
203203
depends on USERSPACE
204204
default y if ERRNO && !ERRNO_IN_TLS
205205

206+
config DYNAMIC_THREAD
207+
bool "Support for dynamic threads [EXPERIMENTAL]"
208+
select EXPERIMENTAL
209+
depends on THREAD_STACK_INFO
210+
select DYNAMIC_OBJECTS if USERSPACE
211+
help
212+
Enable support for dynamic threads and stacks.
213+
214+
if DYNAMIC_THREAD
215+
216+
config DYNAMIC_THREAD_STACK_SIZE
217+
int "Size of each pre-allocated thread stack"
218+
default 1024 if !64BIT
219+
default 2048 if 64BIT
220+
help
221+
Default stack size (in bytes) for dynamic threads.
222+
223+
config DYNAMIC_THREAD_ALLOC
224+
bool "Support heap-allocated thread objects and stacks"
225+
help
226+
Select this option to enable allocating thread object and
227+
thread stacks from the system heap.
228+
229+
Only use this type of allocation in situations
230+
where malloc is permitted.
231+
232+
config DYNAMIC_THREAD_POOL_SIZE
233+
int "Number of statically pre-allocated threads"
234+
default 0
235+
range 0 8192
236+
help
237+
Pre-allocate a fixed number of thread objects and
238+
stacks at build time.
239+
240+
This type of "dynamic" stack is usually suitable in
241+
situations where malloc is not permitted.
242+
243+
choice DYNAMIC_THREAD_PREFER
244+
prompt "Preferred dynamic thread allocator"
245+
default DYNAMIC_THREAD_PREFER_POOL
246+
help
247+
If both CONFIG_DYNAMIC_THREAD_ALLOC=y and
248+
CONFIG_DYNAMIC_THREAD_POOL_SIZE > 0, then the user may
249+
specify the order in which allocation is attmpted.
250+
251+
config DYNAMIC_THREAD_PREFER_ALLOC
252+
bool "Prefer heap-based allocation"
253+
depends on DYNAMIC_THREAD_ALLOC
254+
help
255+
Select this option to attempt a heap-based allocation
256+
prior to any pool-based allocation.
257+
258+
config DYNAMIC_THREAD_PREFER_POOL
259+
bool "Prefer pool-based allocation"
260+
help
261+
Select this option to attempt a pool-based allocation
262+
prior to any heap-based allocation.
263+
264+
endchoice # DYNAMIC_THREAD_PREFER
265+
266+
endif # DYNAMIC_THREADS
267+
206268
config LIBC_ERRNO
207269
bool
208270
help

kernel/dynamic.c

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/*
2+
* Copyright (c) 2022, Meta
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include "kernel_internal.h"
8+
9+
#include <zephyr/kernel.h>
10+
#include <zephyr/kernel/thread_stack.h>
11+
#include <zephyr/logging/log.h>
12+
#include <zephyr/sys/bitarray.h>
13+
14+
LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL);
15+
16+
#if CONFIG_DYNAMIC_THREAD_POOL_SIZE > 0
17+
#define BA_SIZE CONFIG_DYNAMIC_THREAD_POOL_SIZE
18+
#else
19+
#define BA_SIZE 1
20+
#endif
21+
22+
struct dyn_cb_data {
23+
k_tid_t tid;
24+
k_thread_stack_t *stack;
25+
};
26+
27+
static K_THREAD_STACK_ARRAY_DEFINE(dynamic_stack, CONFIG_DYNAMIC_THREAD_POOL_SIZE,
28+
CONFIG_DYNAMIC_THREAD_STACK_SIZE);
29+
SYS_BITARRAY_DEFINE_STATIC(dynamic_ba, BA_SIZE);
30+
31+
static k_thread_stack_t *z_thread_stack_alloc_dyn(size_t align, size_t size)
32+
{
33+
return z_thread_aligned_alloc(align, size);
34+
}
35+
36+
static k_thread_stack_t *z_thread_stack_alloc_pool(size_t size)
37+
{
38+
int rv;
39+
size_t offset;
40+
k_thread_stack_t *stack;
41+
42+
if (size > CONFIG_DYNAMIC_THREAD_STACK_SIZE) {
43+
LOG_DBG("stack size %zu is > pool stack size %d", size,
44+
CONFIG_DYNAMIC_THREAD_STACK_SIZE);
45+
return NULL;
46+
}
47+
48+
rv = sys_bitarray_alloc(&dynamic_ba, 1, &offset);
49+
if (rv < 0) {
50+
LOG_DBG("unable to allocate stack from pool");
51+
return NULL;
52+
}
53+
54+
__ASSERT_NO_MSG(offset < CONFIG_DYNAMIC_THREAD_POOL_SIZE);
55+
56+
stack = (k_thread_stack_t *)&dynamic_stack[offset];
57+
58+
return stack;
59+
}
60+
61+
k_thread_stack_t *z_impl_k_thread_stack_alloc(size_t size, int flags)
62+
{
63+
size_t align = 0;
64+
size_t obj_size = 0;
65+
k_thread_stack_t *stack = NULL;
66+
67+
#ifdef CONFIG_USERSPACE
68+
if ((flags & K_USER) != 0) {
69+
align = Z_THREAD_STACK_OBJ_ALIGN(size);
70+
obj_size = Z_THREAD_STACK_SIZE_ADJUST(size);
71+
} else
72+
#endif
73+
{
74+
align = Z_KERNEL_STACK_OBJ_ALIGN;
75+
obj_size = Z_KERNEL_STACK_SIZE_ADJUST(size);
76+
}
77+
78+
if (IS_ENABLED(CONFIG_DYNAMIC_THREAD_PREFER_ALLOC)) {
79+
stack = z_thread_stack_alloc_dyn(align, obj_size);
80+
if (stack == NULL && CONFIG_DYNAMIC_THREAD_POOL_SIZE > 0) {
81+
stack = z_thread_stack_alloc_pool(size);
82+
}
83+
} else if (IS_ENABLED(CONFIG_DYNAMIC_THREAD_PREFER_POOL) &&
84+
CONFIG_DYNAMIC_THREAD_POOL_SIZE > 0) {
85+
stack = z_thread_stack_alloc_pool(size);
86+
if (stack == NULL && IS_ENABLED(CONFIG_DYNAMIC_THREAD_ALLOC)) {
87+
stack = z_thread_stack_alloc_dyn(align, obj_size);
88+
}
89+
} else {
90+
return NULL;
91+
}
92+
93+
return stack;
94+
}
95+
96+
#ifdef CONFIG_USERSPACE
97+
static inline k_thread_stack_t *z_vrfy_k_thread_stack_alloc(size_t size, int flags)
98+
{
99+
return z_impl_k_thread_stack_alloc(size, flags);
100+
}
101+
#include <syscalls/k_thread_stack_alloc_mrsh.c>
102+
#endif
103+
104+
static void dyn_cb(const struct k_thread *thread, void *user_data)
105+
{
106+
struct dyn_cb_data *const data = (struct dyn_cb_data *)user_data;
107+
108+
if (data->stack == (k_thread_stack_t *)thread->stack_info.start) {
109+
__ASSERT(data->tid == NULL, "stack %p is associated with more than one thread!");
110+
data->tid = (k_tid_t)thread;
111+
}
112+
}
113+
114+
int z_impl_k_thread_stack_free(k_thread_stack_t *stack)
115+
{
116+
char state_buf[16] = {0};
117+
struct dyn_cb_data data = {.stack = stack};
118+
119+
/* Get a possible tid associated with stack */
120+
k_thread_foreach(dyn_cb, &data);
121+
122+
if (data.tid != NULL) {
123+
/* Check if thread is in use */
124+
if (k_thread_state_str(data.tid, state_buf, sizeof(state_buf)) != state_buf) {
125+
LOG_ERR("tid %p is invalid!", data.tid);
126+
return -EINVAL;
127+
}
128+
129+
if (!(strcmp("dummy", state_buf) == 0) || (strcmp("dead", state_buf) == 0)) {
130+
LOG_ERR("tid %p is in use!", data.tid);
131+
return -EBUSY;
132+
}
133+
}
134+
135+
if (CONFIG_DYNAMIC_THREAD_POOL_SIZE > 0) {
136+
if (IS_ARRAY_ELEMENT(dynamic_stack, stack)) {
137+
if (sys_bitarray_free(&dynamic_ba, 1, ARRAY_INDEX(dynamic_stack, stack))) {
138+
LOG_ERR("stack %p is not allocated!", stack);
139+
return -EINVAL;
140+
}
141+
142+
return 0;
143+
}
144+
}
145+
146+
if (IS_ENABLED(CONFIG_DYNAMIC_THREAD_ALLOC)) {
147+
k_free(stack);
148+
} else {
149+
LOG_ERR("Invalid stack %p", stack);
150+
return -EINVAL;
151+
}
152+
153+
return 0;
154+
}
155+
156+
#ifdef CONFIG_USERSPACE
157+
static inline int z_vrfy_k_thread_stack_free(k_thread_stack_t *stack)
158+
{
159+
return z_impl_k_thread_stack_free(stack);
160+
}
161+
#include <syscalls/k_thread_stack_free_mrsh.c>
162+
#endif

0 commit comments

Comments
 (0)