blob: a17a84f724ad7d8558932c37f3f4cb74dd54a1fc [file] [log] [blame]
/*
* Copyright (c) 2022, Meta
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "kernel_internal.h"
#include <zephyr/kernel.h>
#include <ksched.h>
#include <zephyr/kernel/thread_stack.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/bitarray.h>
#include <zephyr/sys/kobject.h>
#include <zephyr/internal/syscall_handler.h>
LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL);
#if CONFIG_DYNAMIC_THREAD_POOL_SIZE > 0
#define BA_SIZE CONFIG_DYNAMIC_THREAD_POOL_SIZE
#else
#define BA_SIZE 1
#endif /* CONFIG_DYNAMIC_THREAD_POOL_SIZE > 0 */
struct dyn_cb_data {
k_tid_t tid;
k_thread_stack_t *stack;
};
static K_THREAD_STACK_ARRAY_DEFINE(dynamic_stack, CONFIG_DYNAMIC_THREAD_POOL_SIZE,
CONFIG_DYNAMIC_THREAD_STACK_SIZE);
SYS_BITARRAY_DEFINE_STATIC(dynamic_ba, BA_SIZE);
static k_thread_stack_t *z_thread_stack_alloc_dyn(size_t align, size_t size)
{
return z_thread_aligned_alloc(align, size);
}
static k_thread_stack_t *z_thread_stack_alloc_pool(size_t size)
{
int rv;
size_t offset;
k_thread_stack_t *stack;
if (size > CONFIG_DYNAMIC_THREAD_STACK_SIZE) {
LOG_DBG("stack size %zu is > pool stack size %d", size,
CONFIG_DYNAMIC_THREAD_STACK_SIZE);
return NULL;
}
rv = sys_bitarray_alloc(&dynamic_ba, 1, &offset);
if (rv < 0) {
LOG_DBG("unable to allocate stack from pool");
return NULL;
}
__ASSERT_NO_MSG(offset < CONFIG_DYNAMIC_THREAD_POOL_SIZE);
stack = (k_thread_stack_t *)&dynamic_stack[offset];
return stack;
}
static k_thread_stack_t *stack_alloc_dyn(size_t size, int flags)
{
if ((flags & K_USER) == K_USER) {
#ifdef CONFIG_DYNAMIC_OBJECTS
return k_object_alloc_size(K_OBJ_THREAD_STACK_ELEMENT, size);
#else
/* Dynamic user stack needs a kobject, so if this option is not
* enabled we can't proceed.
*/
return NULL;
#endif /* CONFIG_DYNAMIC_OBJECTS */
}
return z_thread_stack_alloc_dyn(Z_KERNEL_STACK_OBJ_ALIGN,
K_KERNEL_STACK_LEN(size));
}
k_thread_stack_t *z_impl_k_thread_stack_alloc(size_t size, int flags)
{
k_thread_stack_t *stack = NULL;
if (IS_ENABLED(CONFIG_DYNAMIC_THREAD_PREFER_ALLOC)) {
stack = stack_alloc_dyn(size, flags);
if (stack == NULL && CONFIG_DYNAMIC_THREAD_POOL_SIZE > 0) {
stack = z_thread_stack_alloc_pool(size);
}
} else if (IS_ENABLED(CONFIG_DYNAMIC_THREAD_PREFER_POOL)) {
if (CONFIG_DYNAMIC_THREAD_POOL_SIZE > 0) {
stack = z_thread_stack_alloc_pool(size);
}
if ((stack == NULL) && IS_ENABLED(CONFIG_DYNAMIC_THREAD_ALLOC)) {
stack = stack_alloc_dyn(size, flags);
}
}
return stack;
}
#ifdef CONFIG_USERSPACE
static inline k_thread_stack_t *z_vrfy_k_thread_stack_alloc(size_t size, int flags)
{
return z_impl_k_thread_stack_alloc(size, flags);
}
#include <zephyr/syscalls/k_thread_stack_alloc_mrsh.c>
#endif /* CONFIG_USERSPACE */
static void dyn_cb(const struct k_thread *thread, void *user_data)
{
struct dyn_cb_data *const data = (struct dyn_cb_data *)user_data;
if (data->stack == (k_thread_stack_t *)thread->stack_info.start) {
__ASSERT(data->tid == NULL, "stack %p is associated with more than one thread!",
(void *)thread->stack_info.start);
data->tid = (k_tid_t)thread;
}
}
int z_impl_k_thread_stack_free(k_thread_stack_t *stack)
{
struct dyn_cb_data data = {.stack = stack};
/* Get a possible tid associated with stack */
k_thread_foreach(dyn_cb, &data);
if (data.tid != NULL) {
if (!(z_is_thread_state_set(data.tid, _THREAD_DUMMY) ||
z_is_thread_state_set(data.tid, _THREAD_DEAD))) {
LOG_ERR("tid %p is in use!", data.tid);
return -EBUSY;
}
}
if (CONFIG_DYNAMIC_THREAD_POOL_SIZE > 0) {
if (IS_ARRAY_ELEMENT(dynamic_stack, stack)) {
if (sys_bitarray_free(&dynamic_ba, 1, ARRAY_INDEX(dynamic_stack, stack))) {
LOG_ERR("stack %p is not allocated!", stack);
return -EINVAL;
}
return 0;
}
}
if (IS_ENABLED(CONFIG_DYNAMIC_THREAD_ALLOC)) {
#ifdef CONFIG_USERSPACE
if (k_object_find(stack)) {
k_object_free(stack);
} else {
k_free(stack);
}
#else
k_free(stack);
#endif /* CONFIG_USERSPACE */
} else {
LOG_DBG("Invalid stack %p", stack);
return -EINVAL;
}
return 0;
}
#ifdef CONFIG_USERSPACE
static inline int z_vrfy_k_thread_stack_free(k_thread_stack_t *stack)
{
/* The thread stack object must not be in initialized state.
*
* Thread stack objects are initialized when the thread is created
* and de-initialized when the thread is destroyed. Since we can't
* free a stack that is in use, we have to check that the caller
* has access to the object but that it is not in use anymore.
*/
K_OOPS(K_SYSCALL_OBJ_NEVER_INIT(stack, K_OBJ_THREAD_STACK_ELEMENT));
return z_impl_k_thread_stack_free(stack);
}
#include <zephyr/syscalls/k_thread_stack_free_mrsh.c>
#endif /* CONFIG_USERSPACE */