/* * Copyright (c) 2022, Meta * * SPDX-License-Identifier: Apache-2.0 */ #include "kernel_internal.h" #include #include #include #include #include #include #include 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 #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 #endif /* CONFIG_USERSPACE */