diff --git a/doc/kernel/memory/pools.rst b/doc/kernel/memory/pools.rst index cdd0ff96e29..c7d3b29e1f4 100644 --- a/doc/kernel/memory/pools.rst +++ b/doc/kernel/memory/pools.rst @@ -173,6 +173,25 @@ memory block is actually used to satisfy the request.) ... /* use memory block */ k_mem_pool_free(&block); +Thread Resource Pools +********************* + +Certain kernel APIs may need to make heap allocations on behalf of the +calling thread. For example, some initialization APIs for objects like +pipes and message queues may need to allocate a private kernel-side buffer, +or objects like queues may temporarily allocate kernel data structures +as items are placed in the queue. + +Such memory allocations are drawn from memory pools that are assigned to +a thread. By default, a thread in the system has no resource pool and +any allocations made on its behalf will fail. The supervisor-mode only +:cpp:func:`k_thread_resource_pool_assign()` will associate any implicit +kernel-side allocations to the target thread with the provided memory pool, +and any children of that thread will inherit this assignment. + +If a system heap exists, threads may alternatively have their resources +drawn from it using the :cpp:func:`k_thread_system_pool_assign()` API. + Suggested Uses ************** @@ -191,3 +210,5 @@ The following memory pool APIs are provided by :file:`kernel.h`: * :cpp:func:`k_mem_pool_free()` * :cpp:func:`k_mem_pool_malloc()` * :cpp:func:`k_free()` +* :cpp:func:`k_thread_resource_pool_assign()` +* :cpp:func:`k_thread_system_pool_assign()` diff --git a/include/kernel.h b/include/kernel.h index aba1d7b9553..c272efeed0c 100644 --- a/include/kernel.h +++ b/include/kernel.h @@ -524,6 +524,7 @@ struct k_thread { /* Context handle returned via _arch_switch() */ void *switch_handle; #endif + struct k_mem_pool *resource_pool; /* arch-specifics: must always be at the end */ struct _thread_arch arch; @@ -700,6 +701,41 @@ extern FUNC_NORETURN void k_thread_user_mode_enter(k_thread_entry_t entry, extern void __attribute__((sentinel)) k_thread_access_grant(struct k_thread *thread, ...); +/** + * @brief Assign a resource memory pool to a thread + * + * By default, threads have no resource pool assigned unless their parent + * thread has a resource pool, in which case it is inherited. Multiple + * threads may be assigned to the same memory pool. + * + * Changing a thread's resource pool will not migrate allocations from the + * previous pool. + * + * @param thread Target thread to assign a memory pool for resource requests, + * or NULL if the thread should no longer have a memory pool. + * @param pool Memory pool to use for resources. + */ +static inline void k_thread_resource_pool_assign(struct k_thread *thread, + struct k_mem_pool *pool) +{ + thread->resource_pool = pool; +} + +#if (CONFIG_HEAP_MEM_POOL_SIZE > 0) +/** + * @brief Assign the system heap as a thread's resource pool + * + * Similar to k_thread_resource_pool_assign(), but the thread will use + * the kernel heap to draw memory. + * + * Use with caution, as a malicious thread could perform DoS attacks on the + * kernel heap. + * + * @param thread Target thread to assign the system heap for resource requests + */ +void k_thread_system_pool_assign(struct k_thread *thread); +#endif /* (CONFIG_HEAP_MEM_POOL_SIZE > 0) */ + /** * @brief Put the current thread to sleep. * diff --git a/kernel/include/kernel_internal.h b/kernel/include/kernel_internal.h index e77f4fa2f69..4f9b44a7041 100644 --- a/kernel/include/kernel_internal.h +++ b/kernel/include/kernel_internal.h @@ -159,6 +159,19 @@ void _arch_user_mode_enter(k_thread_entry_t user_entry, void *p1, void *p2, extern FUNC_NORETURN void _arch_syscall_oops(void *ssf); #endif /* CONFIG_USERSPACE */ +/** + * @brief Allocate some memory from the current thread's resource pool + * + * Threads may be assigned a resource pool, which will be used to allocate + * memory on behalf of certain kernel and driver APIs. Memory reserved + * in this way should be freed with k_free(). + * + * @param size Memory allocation size + * @return A pointer to the allocated memory, or NULL if there is insufficient + * RAM in the pool or the thread has no resource pool assigned + */ +void *z_thread_malloc(size_t size); + /* set and clear essential thread flag */ extern void _thread_essential_set(void); diff --git a/kernel/mempool.c b/kernel/mempool.c index 97f7886a837..774f7b77b70 100644 --- a/kernel/mempool.c +++ b/kernel/mempool.c @@ -185,4 +185,22 @@ void *k_calloc(size_t nmemb, size_t size) } return ret; } + +void k_thread_system_pool_assign(struct k_thread *thread) +{ + thread->resource_pool = _HEAP_MEM_POOL; +} #endif + +void *z_thread_malloc(size_t size) +{ + void *ret; + + if (_current->resource_pool) { + ret = k_mem_pool_malloc(_current->resource_pool, size); + } else { + ret = NULL; + } + + return ret; +} diff --git a/kernel/thread.c b/kernel/thread.c index eb97219e649..2c8863f7755 100644 --- a/kernel/thread.c +++ b/kernel/thread.c @@ -295,13 +295,15 @@ void _setup_new_thread(struct k_thread *new_thread, /* Any given thread has access to itself */ k_object_access_grant(new_thread, new_thread); - +#endif #ifdef CONFIG_ARCH_HAS_CUSTOM_SWAP_TO_MAIN /* _current may be null if the dummy thread is not used */ if (!_current) { + new_thread->resource_pool = NULL; return; } #endif +#ifdef CONFIG_USERSPACE /* New threads inherit any memory domain membership by the parent */ if (_current->mem_domain_info.mem_domain) { k_mem_domain_add_thread(_current->mem_domain_info.mem_domain, @@ -312,6 +314,7 @@ void _setup_new_thread(struct k_thread *new_thread, _thread_perms_inherit(_current, new_thread); } #endif + new_thread->resource_pool = _current->resource_pool; } #ifdef CONFIG_MULTITHREADING