kernel: add k_thread_create() API

Unline k_thread_spawn(), the struct k_thread can live anywhere and not
in the thread's stack region. This will be useful for memory protection
scenarios where private kernel structures for a thread are not
accessible by that thread, or we want to allow the thread to use all the
stack space we gave it.

This requires a change to the internal _new_thread() API as we need to
provide a separate pointer for the k_thread.

By default, we still create internal threads with the k_thread in stack
memory. Forthcoming patches will change this, but we first need to make
it easier to define k_thread memory of variable size depending on
whether we need to store coprocessor state or not.

Change-Id: I533bbcf317833ba67a771b356b6bbc6596bf60f5
Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
This commit is contained in:
Andrew Boie 2017-03-30 13:07:02 -07:00 committed by Anas Nashif
commit d26cf2dc33
16 changed files with 147 additions and 100 deletions

View file

@ -54,7 +54,7 @@ struct init_stack_frame {
*
* @return N/A
*/
void _new_thread(char *pStackMem, size_t stackSize,
void _new_thread(struct k_thread *thread, char *pStackMem, size_t stackSize,
_thread_entry_t pEntry,
void *parameter1, void *parameter2, void *parameter3,
int priority, unsigned int options)
@ -64,12 +64,9 @@ void _new_thread(char *pStackMem, size_t stackSize,
char *stackEnd = pStackMem + stackSize;
struct init_stack_frame *pInitCtx;
struct k_thread *thread;
thread = _new_thread_init(pStackMem, stackSize, priority, options);
_new_thread_init(thread, pStackMem, stackSize, priority, options);
/* carve the thread entry struct from the "base" of the stack */
pInitCtx = (struct init_stack_frame *)(STACK_ROUND_DOWN(stackEnd) -
sizeof(struct init_stack_frame));

View file

@ -49,7 +49,7 @@
* @return N/A
*/
void _new_thread(char *pStackMem, size_t stackSize,
void _new_thread(struct k_thread *thread, char *pStackMem, size_t stackSize,
_thread_entry_t pEntry,
void *parameter1, void *parameter2, void *parameter3,
int priority, unsigned int options)
@ -62,9 +62,8 @@ void _new_thread(char *pStackMem, size_t stackSize,
char *stackEnd = pStackMem + stackSize;
struct __esf *pInitCtx;
struct k_thread *thread = (struct k_thread *) pStackMem;
thread = _new_thread_init(pStackMem, stackSize, priority, options);
_new_thread_init(thread, pStackMem, stackSize, priority, options);
/* carve the thread entry struct from the "base" of the stack */

View file

@ -38,8 +38,8 @@ static ALWAYS_INLINE void kernel_arch_init(void)
}
static ALWAYS_INLINE void
_arch_switch_to_main_thread(char *main_stack, size_t main_stack_size,
_thread_entry_t _main)
_arch_switch_to_main_thread(struct k_thread *main_thread, char *main_stack,
size_t main_stack_size, _thread_entry_t _main)
{
/* get high address of the stack, i.e. its start (stack grows down) */
char *start_of_main_stack;
@ -47,7 +47,7 @@ _arch_switch_to_main_thread(char *main_stack, size_t main_stack_size,
start_of_main_stack = main_stack + main_stack_size;
start_of_main_stack = (void *)STACK_ROUND_DOWN(start_of_main_stack);
_current = (void *)main_stack;
_current = main_thread;
/* the ready queue cache already contains the main thread */

View file

@ -31,17 +31,16 @@ struct init_stack_frame {
};
void _new_thread(char *stack_memory, size_t stack_size,
void _new_thread(struct k_thread *thread, char *stack_memory, size_t stack_size,
_thread_entry_t thread_func,
void *arg1, void *arg2, void *arg3,
int priority, unsigned int options)
{
_ASSERT_VALID_PRIO(priority, thread_func);
struct k_thread *thread;
struct init_stack_frame *iframe;
thread = _new_thread_init(stack_memory, stack_size, priority, options);
_new_thread_init(thread, stack_memory, stack_size, priority, options);
/* Initial stack frame data, stored at the base of the stack */
iframe = (struct init_stack_frame *)

View file

@ -15,17 +15,16 @@ void _thread_entry_wrapper(_thread_entry_t thread,
void *arg2,
void *arg3);
void _new_thread(char *stack_memory, size_t stack_size,
_thread_entry_t thread_func,
void _new_thread(struct k_thread *thread, char *stack_memory,
size_t stack_size, _thread_entry_t thread_func,
void *arg1, void *arg2, void *arg3,
int priority, unsigned int options)
{
_ASSERT_VALID_PRIO(priority, thread_func);
struct k_thread *thread;
struct __esf *stack_init;
thread = _new_thread_init(stack_memory, stack_size, priority, options);
_new_thread_init(thread, stack_memory, stack_size, priority, options);
/* Initial stack frame for thread */
stack_init = (struct __esf *)

View file

@ -39,6 +39,7 @@ void _thread_entry_wrapper(_thread_entry_t, void *,
*
* This function is called by _new_thread() to initialize tasks.
*
* @param thread pointer to thread struct memory
* @param pStackMem pointer to thread stack memory
* @param stackSize size of a stack in bytes
* @param priority thread priority
@ -180,9 +181,8 @@ __asm__("\t.globl _thread_entry\n"
* This function is utilized to create execution threads for both fiber
* threads and kernel tasks.
*
* The k_thread structure is carved from the "end" of the specified
* thread stack memory.
*
* @param thread pointer to thread struct memory, including any space needed
* for extra coprocessor context
* @param pStackmem the pointer to aligned stack memory
* @param stackSize the stack size in bytes
* @param pEntry thread entry point routine
@ -195,7 +195,7 @@ __asm__("\t.globl _thread_entry\n"
*
* @return opaque pointer to initialized k_thread structure
*/
void _new_thread(char *pStackMem, size_t stackSize,
void _new_thread(struct k_thread *thread, char *pStackMem, size_t stackSize,
_thread_entry_t pEntry,
void *parameter1, void *parameter2, void *parameter3,
int priority, unsigned int options)
@ -203,9 +203,8 @@ void _new_thread(char *pStackMem, size_t stackSize,
_ASSERT_VALID_PRIO(priority, pEntry);
unsigned long *pInitialThread;
struct k_thread *thread;
thread = _new_thread_init(pStackMem, stackSize, priority, options);
_new_thread_init(thread, pStackMem, stackSize, priority, options);
/* carve the thread entry struct from the "base" of the stack */

View file

@ -16,9 +16,9 @@
extern void _xt_user_exit(void);
/*
* @brief Initialize a new thread from its stack space
* @brief Initialize a new thread
*
* The struct k_thread is put at the lower address of the stack. An
* Any coprocessor context data is put at the lower address of the stack. An
* initial context, to be "restored" by __return_from_coop(), is put at
* the other end of the stack, and thus reusable by the stack when not
* needed anymore.
@ -29,6 +29,7 @@ extern void _xt_user_exit(void);
*
* <options> is currently unused.
*
* @param thread pointer to k_thread memory
* @param pStackmem the pointer to aligned stack memory
* @param stackSize the stack size in bytes
* @param pEntry thread entry point routine
@ -41,23 +42,19 @@ extern void _xt_user_exit(void);
* @return N/A
*/
void _new_thread(char *pStack, size_t stackSize,
void _new_thread(struct k_thread *thread, char *pStack, size_t stackSize,
void (*pEntry)(void *, void *, void *),
void *p1, void *p2, void *p3,
int priority, unsigned int options)
{
/* Align stack end to maximum alignment requirement. */
char *stackEnd = (char *)ROUND_DOWN(pStack + stackSize, 16);
/* k_thread is located at top of stack while frames are located at end
* of it
*/
struct k_thread *thread;
#if XCHAL_CP_NUM > 0
u32_t *cpSA;
char *cpStack;
#endif
thread = _new_thread_init(pStack, stackSize, priority, options);
_new_thread_init(thread, pStack, stackSize, priority, options);
#ifdef CONFIG_DEBUG
printk("\nstackPtr = %p, stackSize = %d\n", pStack, stackSize);

View file

@ -100,15 +100,15 @@ tagged as an SSE user, or if the application wants to avoid the exception
handling overhead involved in auto-tagging threads, it is possible to
pre-tag a thread using one of the techniques listed below.
* A statically-spawned x86 thread can be pre-tagged by passing the
* A statically-created x86 thread can be pre-tagged by passing the
:c:macro:`K_FP_REGS` or :c:macro:`K_SSE_REGS` option to
:c:macro:`K_THREAD_DEFINE`.
* A dynamically-spawned x86 thread can be pre-tagged by passing the
* A dynamically-created x86 thread can be pre-tagged by passing the
:c:macro:`K_FP_REGS` or :c:macro:`K_SSE_REGS` option to
:cpp:func:`k_thread_spawn()`.
:cpp:func:`k_thread_create()`.
* An already-spawned x86 thread can pre-tag itself once it has started
* An already-created x86 thread can pre-tag itself once it has started
by passing the :c:macro:`K_FP_REGS` or :c:macro:`K_SSE_REGS` option to
:cpp:func:`k_float_enable()`.

View file

@ -18,11 +18,13 @@ referenced by a :dfn:`thread id` that is assigned when the thread is spawned.
A thread has the following key properties:
* A **stack area**, which is a region of memory used for the thread's
control block (of type :c:type:`struct k_thread`) and for its stack.
* A **stack area**, which is a region of memory used for the thread's stack.
The **size** of the stack area can be tailored to conform to the actual needs
of the thread's processing.
* A **thread control block** for private kernel bookkeeping of the thread's
metadata. This is an instance of type :c:type:`struct k_thread`.
* An **entry point function**, which is invoked when the thread is started.
Up to 3 **argument values** can be passed to this function.
@ -38,13 +40,12 @@ A thread has the following key properties:
.. _spawning_thread:
Thread Spawning
Thread Creation
===============
A thread must be spawned before it can be used. The kernel initializes
the control block portion of the thread's stack area, as well as one
end of the stack portion. The remainder of the thread's stack is typically
left uninitialized.
A thread must be created before it can be used. The kernel initializes
the thread control block as well as one end of the stack portion. The remainder
of the thread's stack is typically left uninitialized.
Specifying a start delay of :c:macro:`K_NO_WAIT` instructs the kernel
to start thread execution immediately. Alternatively, the kernel can be
@ -146,11 +147,9 @@ Implementation
Spawning a Thread
=================
A thread is spawned by defining its stack area and then calling
:cpp:func:`k_thread_spawn()`. The stack area is an array of bytes
whose size must equal :c:macro:`K_THREAD_SIZEOF` plus the size
of the thread's stack. The stack area must be defined using the
:c:macro:`__stack` attribute to ensure it is properly aligned.
A thread is spawned by defining its stack area and its thread control block,
and then calling :cpp:func:`k_thread_create()`. The stack area must be defined
using the :c:macro:`__stack` attribute to ensure it is properly aligned.
The thread spawning function returns its thread id, which can be used
to reference the thread.
@ -165,14 +164,16 @@ The following code spawns a thread that starts immediately.
extern void my_entry_point(void *, void *, void *);
char __noinit __stack my_stack_area[MY_STACK_SIZE];
struct k_thread my_thread_data;
k_tid_t my_tid = k_thread_spawn(my_stack_area, MY_STACK_SIZE,
my_entry_point, NULL, NULL, NULL,
MY_PRIORITY, 0, K_NO_WAIT);
k_tid_t my_tid = k_thread_create(&my_thread_data, my_stack_area,
MY_STACK_SIZE, my_entry_point,
NULL, NULL, NULL,
MY_PRIORITY, 0, K_NO_WAIT);
Alternatively, a thread can be spawned at compile time by calling
:c:macro:`K_THREAD_DEFINE`. Observe that the macro defines
the stack area and thread id variables automatically.
the stack area, control block, and thread id variables automatically.
The following code has the same effect as the code segment above.
@ -231,7 +232,7 @@ APIs
The following thread APIs are provided by :file:`kernel.h`:
* :c:macro:`K_THREAD_DEFINE`
* :cpp:func:`k_thread_spawn()`
* :cpp:func:`k_thread_create()`
* :cpp:func:`k_thread_cancel()`
* :cpp:func:`k_thread_abort()`
* :cpp:func:`k_thread_suspend()`

View file

@ -149,16 +149,14 @@ Defining a Workqueue
A workqueue is defined using a variable of type :c:type:`struct k_work_q`.
The workqueue is initialized by defining the stack area used by its thread
and then calling :cpp:func:`k_work_q_start()`. The stack area is an array
of bytes whose size must equal :c:macro:`K_THREAD_SIZEOF` plus the size
of the thread's stack. The stack area must be defined using the
:c:macro:`__stack` attribute to ensure it is properly aligned.
and then calling :cpp:func:`k_work_q_start()`. The stack area must be defined
using the :c:macro:`__stack` attribute to ensure it is properly aligned.
The following code defines and initializes a workqueue.
.. code-block:: c
#define MY_STACK_SIZE (K_THREAD_SIZEOF + 500)
#define MY_STACK_SIZE 512
#define MY_PRIORITY 5
char __noinit __stack my_stack_area[MY_STACK_SIZE];

View file

@ -355,6 +355,12 @@ typedef void (*k_thread_entry_t)(void *p1, void *p2, void *p3);
* scheduler may preempt the current thread to allow the new thread to
* execute.
*
* Kernel data structures for bookkeeping and context storage for this thread
* will be placed at the beginning of the thread's stack memory region and may
* become corrupted if too much of the stack is used. This function has been
* deprecated in favor of k_thread_create() to give the user more control on
* where these data structures reside.
*
* Thread options are architecture-specific, and can include K_ESSENTIAL,
* K_FP_REGS, and K_SSE_REGS. Multiple options may be specified by separating
* them using "|" (the logical OR operator).
@ -371,11 +377,49 @@ typedef void (*k_thread_entry_t)(void *p1, void *p2, void *p3);
*
* @return ID of new thread.
*/
extern k_tid_t k_thread_spawn(char *stack, size_t stack_size,
extern __deprecated k_tid_t k_thread_spawn(char *stack, size_t stack_size,
k_thread_entry_t entry,
void *p1, void *p2, void *p3,
int prio, u32_t options, s32_t delay);
/**
* @brief Create a thread.
*
* This routine initializes a thread, then schedules it for execution.
*
* The new thread may be scheduled for immediate execution or a delayed start.
* If the newly spawned thread does not have a delayed start the kernel
* scheduler may preempt the current thread to allow the new thread to
* execute.
*
* Thread options are architecture-specific, and can include K_ESSENTIAL,
* K_FP_REGS, and K_SSE_REGS. Multiple options may be specified by separating
* them using "|" (the logical OR operator).
*
* Historically, users often would use the beginning of the stack memory region
* to store the struct k_thread data, although corruption will occur if the
* stack overflows this region and stack protection features may not detect this
* situation.
*
* @param new_thread Pointer to uninitialized struct k_thread
* @param stack Pointer to the stack space.
* @param stack_size Stack size in bytes.
* @param entry Thread entry function.
* @param p1 1st entry point parameter.
* @param p2 2nd entry point parameter.
* @param p3 3rd entry point parameter.
* @param prio Thread priority.
* @param options Thread options.
* @param delay Scheduling delay (in milliseconds), or K_NO_WAIT (for no delay).
*
* @return ID of new thread.
*/
extern k_tid_t k_thread_create(struct k_thread *new_thread, char *stack,
size_t stack_size,
void (*entry)(void *, void *, void*),
void *p1, void *p2, void *p3,
int prio, u32_t options, s32_t delay);
/**
* @brief Put the current thread to sleep.
*
@ -469,10 +513,8 @@ extern void k_thread_abort(k_tid_t thread);
#define _INACTIVE (-1)
struct _static_thread_data {
union {
char *init_stack;
struct k_thread *thread;
};
struct k_thread *init_thread;
char *init_stack;
unsigned int init_stack_size;
void (*init_entry)(void *, void *, void *);
void *init_p1;
@ -485,11 +527,12 @@ struct _static_thread_data {
u32_t init_groups;
};
#define _THREAD_INITIALIZER(stack, stack_size, \
#define _THREAD_INITIALIZER(thread, stack, stack_size, \
entry, p1, p2, p3, \
prio, options, delay, abort, groups) \
{ \
{.init_stack = (stack)}, \
.init_thread = (thread), \
.init_stack = (stack), \
.init_stack_size = (stack_size), \
.init_entry = (void (*)(void *, void *, void *))entry, \
.init_p1 = (void *)p1, \
@ -536,13 +579,15 @@ struct _static_thread_data {
#define K_THREAD_DEFINE(name, stack_size, \
entry, p1, p2, p3, \
prio, options, delay) \
char __noinit __stack _k_thread_obj_##name[stack_size]; \
char __noinit __stack _k_thread_stack_##name[stack_size]; \
struct k_thread _k_thread_obj_##name; \
struct _static_thread_data _k_thread_data_##name __aligned(4) \
__in_section(_static_thread_data, static, name) = \
_THREAD_INITIALIZER(_k_thread_obj_##name, stack_size, \
_THREAD_INITIALIZER(&_k_thread_obj_##name, \
_k_thread_stack_##name, stack_size, \
entry, p1, p2, p3, prio, options, delay, \
NULL, 0); \
const k_tid_t name = (k_tid_t)_k_thread_obj_##name
NULL, 0); \
const k_tid_t name = (k_tid_t)&_k_thread_obj_##name
/**
* @brief Get a thread's priority.
@ -1795,6 +1840,7 @@ typedef void (*k_work_handler_t)(struct k_work *work);
struct k_work_q {
struct k_fifo fifo;
struct k_thread thread;
};
enum {

View file

@ -142,20 +142,19 @@ extern void _init_thread_base(struct _thread_base *thread_base,
int priority, u32_t initial_state,
unsigned int options);
static ALWAYS_INLINE struct k_thread *_new_thread_init(char *pStack,
size_t stackSize,
int prio,
unsigned int options)
static ALWAYS_INLINE void _new_thread_init(struct k_thread *thread,
char *pStack, size_t stackSize,
int prio, unsigned int options)
{
struct k_thread *thread;
#if !defined(CONFIG_INIT_STACKS) && !defined(CONFIG_THREAD_STACK_INFO)
ARG_UNUSED(pStack);
ARG_UNUSED(stackSize);
#endif
#ifdef CONFIG_INIT_STACKS
memset(pStack, 0xaa, stackSize);
#endif
/* Initialize various struct k_thread members */
thread = (struct k_thread *)pStack;
_init_thread_base(&thread->base, prio, _THREAD_PRESTART, options);
/* static threads overwrite it afterwards with real value */
@ -171,8 +170,6 @@ static ALWAYS_INLINE struct k_thread *_new_thread_init(char *pStack,
thread->stack_info.start = (u32_t)pStack;
thread->stack_info.size = (u32_t)stackSize;
#endif /* CONFIG_THREAD_STACK_INFO */
return thread;
}
#if defined(CONFIG_THREAD_MONITOR)

View file

@ -43,7 +43,7 @@ FUNC_NORETURN void _Cstart(void);
extern void _thread_entry(void (*)(void *, void *, void *),
void *, void *, void *);
extern void _new_thread(char *pStack, size_t stackSize,
extern void _new_thread(struct k_thread *thread, char *pStack, size_t stackSize,
void (*pEntry)(void *, void *, void *),
void *p1, void *p2, void *p3,
int prio, unsigned int options);

View file

@ -81,8 +81,11 @@ u64_t __noinit __end_tick_tsc;
char __noinit __stack _main_stack[MAIN_STACK_SIZE];
char __noinit __stack _idle_stack[IDLE_STACK_SIZE];
k_tid_t const _main_thread = (k_tid_t)_main_stack;
k_tid_t const _idle_thread = (k_tid_t)_idle_stack;
static struct k_thread _main_thread_s;
static struct k_thread _idle_thread_s;
k_tid_t const _main_thread = (k_tid_t)&_main_thread_s;
k_tid_t const _idle_thread = (k_tid_t)&_idle_thread_s;
/*
* storage space for the interrupt stack
@ -271,15 +274,15 @@ static void prepare_multithreading(struct k_thread *dummy_thread)
*/
_ready_q.cache = _main_thread;
_new_thread(_main_stack, MAIN_STACK_SIZE,
_main, NULL, NULL, NULL,
_new_thread(_main_thread, _main_stack,
MAIN_STACK_SIZE, _main, NULL, NULL, NULL,
CONFIG_MAIN_THREAD_PRIORITY, K_ESSENTIAL);
_mark_thread_as_started(_main_thread);
_add_thread_to_ready_q(_main_thread);
#ifdef CONFIG_MULTITHREADING
_new_thread(_idle_stack, IDLE_STACK_SIZE,
idle, NULL, NULL, NULL,
_new_thread(_idle_thread, _idle_stack,
IDLE_STACK_SIZE, idle, NULL, NULL, NULL,
K_LOWEST_THREAD_PRIO, K_ESSENTIAL);
_mark_thread_as_started(_idle_thread);
_add_thread_to_ready_q(_idle_thread);
@ -295,7 +298,8 @@ static void prepare_multithreading(struct k_thread *dummy_thread)
static void switch_to_main_thread(void)
{
#ifdef CONFIG_ARCH_HAS_CUSTOM_SWAP_TO_MAIN
_arch_switch_to_main_thread(_main_stack, MAIN_STACK_SIZE, _main);
_arch_switch_to_main_thread(_main_thread, _main_stack, MAIN_STACK_SIZE,
_main);
#else
/*
* Context switch to main task (entry function is _main()): the

View file

@ -208,21 +208,32 @@ static void schedule_new_thread(struct k_thread *thread, s32_t delay)
#endif
#ifdef CONFIG_MULTITHREADING
k_tid_t k_thread_create(struct k_thread *new_thread, char *stack,
size_t stack_size, void (*entry)(void *, void *, void*),
void *p1, void *p2, void *p3,
int prio, u32_t options, s32_t delay)
{
__ASSERT(!_is_in_isr(), "Threads may not be created in ISRs");
_new_thread(new_thread, stack, stack_size, entry, p1, p2, p3, prio,
options);
schedule_new_thread(new_thread, delay);
return new_thread;
}
k_tid_t k_thread_spawn(char *stack, size_t stack_size,
void (*entry)(void *, void *, void*),
void *p1, void *p2, void *p3,
int prio, u32_t options, s32_t delay)
{
__ASSERT(!_is_in_isr(), "");
struct k_thread *new_thread = (struct k_thread *)stack;
_new_thread(stack, stack_size, entry, p1, p2, p3, prio, options);
schedule_new_thread(new_thread, delay);
return new_thread;
return k_thread_create(new_thread, stack, stack_size, entry, p1, p2,
p3, prio, options, delay);
}
#endif
int k_thread_cancel(k_tid_t tid)
@ -264,7 +275,7 @@ void _k_thread_group_op(u32_t groups, void (*func)(struct k_thread *))
_FOREACH_STATIC_THREAD(thread_data) {
if (is_in_any_group(thread_data, groups)) {
key = irq_lock();
func(thread_data->thread);
func(thread_data->init_thread);
irq_unlock(key);
}
}
@ -359,6 +370,7 @@ void _init_static_threads(void)
_FOREACH_STATIC_THREAD(thread_data) {
_new_thread(
thread_data->init_thread,
thread_data->init_stack,
thread_data->init_stack_size,
thread_data->init_entry,
@ -368,7 +380,7 @@ void _init_static_threads(void)
thread_data->init_prio,
thread_data->init_options);
thread_data->thread->init_data = thread_data;
thread_data->init_thread->init_data = thread_data;
}
_sched_lock();
@ -385,7 +397,7 @@ void _init_static_threads(void)
key = irq_lock();
_FOREACH_STATIC_THREAD(thread_data) {
if (thread_data->init_delay != K_FOREVER) {
schedule_new_thread(thread_data->thread,
schedule_new_thread(thread_data->init_thread,
thread_data->init_delay);
}
}

View file

@ -48,9 +48,8 @@ void k_work_q_start(struct k_work_q *work_q, char *stack,
{
k_fifo_init(&work_q->fifo);
k_thread_spawn(stack, stack_size,
work_q_main, work_q, 0, 0,
prio, 0, 0);
k_thread_create(&work_q->thread, stack, stack_size, work_q_main,
work_q, 0, 0, prio, 0, 0);
}
#ifdef CONFIG_SYS_CLOCK_EXISTS