[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [UNIKRAFT PATCH 3/4] lib/uksched: Thread creation callbacks
Introduces the ability for libraries to hook into thread creation and deletion process at `lib/uksched`. Main intended usage are libc's (like newlibc, musl) that can initialize TLS for each thread, even when a thread is created/deleted through the uksched API. Signed-off-by: Simon Kuenzer <simon.kuenzer@xxxxxxxxx> --- lib/uksched/Makefile.uk | 1 + lib/uksched/extra.ld | 29 +++++++++++++++ lib/uksched/include/uk/thread.h | 38 ++++++++++++++++++++ lib/uksched/thread.c | 63 +++++++++++++++++++++++++++++++-- 4 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 lib/uksched/extra.ld diff --git a/lib/uksched/Makefile.uk b/lib/uksched/Makefile.uk index 229d847b..f22a08b8 100644 --- a/lib/uksched/Makefile.uk +++ b/lib/uksched/Makefile.uk @@ -6,3 +6,4 @@ CXXINCLUDES-$(CONFIG_LIBUKSCHED) += -I$(LIBUKSCHED_BASE)/include LIBUKSCHED_SRCS-y += $(LIBUKSCHED_BASE)/sched.c LIBUKSCHED_SRCS-y += $(LIBUKSCHED_BASE)/thread.c LIBUKSCHED_SRCS-y += $(LIBUKSCHED_BASE)/thread_attr.c +LIBUKSCHED_SRCS-y += $(LIBUKSCHED_BASE)/extra.ld diff --git a/lib/uksched/extra.ld b/lib/uksched/extra.ld new file mode 100644 index 00000000..ba659d26 --- /dev/null +++ b/lib/uksched/extra.ld @@ -0,0 +1,29 @@ +SECTIONS +{ + .uk_thread_inittab : { + . = ALIGN(0x8); + PROVIDE(_uk_thread_inittab_start = .); + KEEP (*(.uk_thread_inittab0)) + KEEP (*(.uk_thread_inittab0.*)) + KEEP (*(.uk_thread_inittab1)) + KEEP (*(.uk_thread_inittab1.*)) + KEEP (*(.uk_thread_inittab2)) + KEEP (*(.uk_thread_inittab2.*)) + KEEP (*(.uk_thread_inittab3)) + KEEP (*(.uk_thread_inittab3.*)) + KEEP (*(.uk_thread_inittab4)) + KEEP (*(.uk_thread_inittab4.*)) + KEEP (*(.uk_thread_inittab5)) + KEEP (*(.uk_thread_inittab5.*)) + KEEP (*(.uk_thread_inittab6)) + KEEP (*(.uk_thread_inittab6.*)) + KEEP (*(.uk_thread_inittab7)) + KEEP (*(.uk_thread_inittab7.*)) + KEEP (*(.uk_thread_inittab8)) + KEEP (*(.uk_thread_inittab8.*)) + KEEP (*(.uk_thread_inittab9)) + KEEP (*(.uk_thread_inittab9.*)) + PROVIDE(_uk_thread_inittab_end = .); + } +} +INSERT AFTER .text; diff --git a/lib/uksched/include/uk/thread.h b/lib/uksched/include/uk/thread.h index 15490517..04173d5c 100644 --- a/lib/uksched/include/uk/thread.h +++ b/lib/uksched/include/uk/thread.h @@ -40,6 +40,7 @@ #include <uk/thread_attr.h> #include <uk/wait_types.h> #include <uk/list.h> +#include <uk/prio.h> #include <uk/essentials.h> #ifdef __cplusplus @@ -59,6 +60,8 @@ struct uk_thread { bool detached; struct uk_waitq waiting_threads; struct uk_sched *sched; + void (*entry)(void *); + void *arg; void *prv; #ifdef CONFIG_LIBNEWLIBC struct _reent reent; @@ -121,6 +124,41 @@ void uk_thread_block_timeout(struct uk_thread *thread, __nsec nsec); void uk_thread_block(struct uk_thread *thread); void uk_thread_wake(struct uk_thread *thread); +/** + * Registers a thread initialization function that is + * called during thread creation + * + * @param fn + * initialization function to be called (uk_thread_init_func_t) + * @param prio + * Priority level (0 (earliest) to 9 (latest)) + * Use the UK_PRIO_AFTER() helper macro for computing priority dependencies. + * Note: Any other value for level will be ignored + */ +typedef int (*uk_thread_init_func_t)(struct uk_thread *thread); +typedef void (*uk_thread_fini_func_t)(struct uk_thread *thread); +struct uk_thread_inittab_entry { + uk_thread_init_func_t init; + uk_thread_fini_func_t fini; +}; + +#define __UK_THREAD_INITTAB_ENTRY(init_fn, fini_fn, prio) \ + static const struct uk_thread_inittab_entry \ + __used __section(".uk_thread_inittab" # prio) __align(8) \ + __uk_thread_inittab ## prio ## _ ## entry = { \ + .init = (init_fn), \ + .fini = (fini_fn) \ + } + +#define _UK_THREAD_INITTAB_ENTRY(init_fn, fini_fn, prio) \ + __UK_THREAD_INITTAB_ENTRY(init_fn, fini_fn, prio) + +#define UK_THREAD_INIT_PRIO(init_fn, fini_fn, prio) \ + _UK_THREAD_INITTAB_ENTRY(init_fn, fini_fn, prio) + +#define UK_THREAD_INIT(init_fn, fini_fn) \ + _UK_THREAD_INITTAB_ENTRY(init_fn, fini_fn, UK_PRIO_LATEST) + #ifdef __cplusplus } #endif diff --git a/lib/uksched/thread.c b/lib/uksched/thread.c index 909e242f..c85a70f1 100644 --- a/lib/uksched/thread.c +++ b/lib/uksched/thread.c @@ -88,6 +88,25 @@ struct _reent *__getreent(void) } #endif /* CONFIG_LIBNEWLIBC */ +extern const struct uk_thread_inittab_entry _uk_thread_inittab_start[]; +extern const struct uk_thread_inittab_entry _uk_thread_inittab_end; + +#define uk_thread_inittab_foreach(itr) \ + for ((itr) = DECONST(struct uk_thread_inittab_entry*, \ + _uk_thread_inittab_start); \ + (itr) < &(_uk_thread_inittab_end); \ + (itr)++) + +#define uk_thread_inittab_foreach_reverse2(itr, start) \ + for ((itr) = (start); \ + (itr) >= _uk_thread_inittab_start; \ + (itr)--) + +#define uk_thread_inittab_foreach_reverse(itr) \ + uk_thread_inittab_foreach_reverse2((itr), \ + (DECONST(struct uk_thread_inittab_entry*, \ + (&_uk_thread_inittab_end))) - 1) + int uk_thread_init(struct uk_thread *thread, struct ukplat_ctx_callbacks *cbs, struct uk_alloc *allocator, const char *name, void *stack, void *tls, @@ -96,6 +115,7 @@ int uk_thread_init(struct uk_thread *thread, unsigned long sp; void *ctx; int ret = 0; + struct uk_thread_inittab_entry *itr; UK_ASSERT(thread != NULL); UK_ASSERT(stack != NULL); @@ -104,8 +124,6 @@ int uk_thread_init(struct uk_thread *thread, /* Save pointer to the thread on the stack to get current thread */ *((unsigned long *) stack) = (unsigned long) thread; - init_sp(&sp, stack, function, arg); - /* Allocate thread context */ ctx = uk_zalloc(allocator, ukplat_thread_ctx_size(cbs)); if (!ctx) { @@ -118,6 +136,8 @@ int uk_thread_init(struct uk_thread *thread, thread->name = name; thread->stack = stack; thread->tls = tls; + thread->entry = function; + thread->arg = arg; /* Not runnable, not exited, not sleeping */ thread->flags = 0; @@ -127,10 +147,32 @@ int uk_thread_init(struct uk_thread *thread, thread->sched = NULL; thread->prv = NULL; + /* TODO: Move newlibc reent initialization to newlib as + * thread initialization function + */ #ifdef CONFIG_LIBNEWLIBC reent_init(&thread->reent); #endif + /* Iterate over registered thread initialization functions */ + uk_thread_inittab_foreach(itr) { + if (unlikely(!itr->init)) + continue; + + uk_pr_debug("New thread %p: Call thread initialization function %p...\n", + thread, *itr->init); + ret = (itr->init)(thread); + if (ret < 0) + goto err_fini; + } + + /* Prepare stack and TLS + * NOTE: In case the function pointer was changed by a thread init + * function (e.g., encapsulation), we prepare the stack here + * with the final setup + */ + init_sp(&sp, stack, thread->entry, thread->arg); + /* Platform specific context initialization */ ukplat_thread_ctx_init(cbs, thread->ctx, sp, (uintptr_t) ukarch_tls_pointer(tls)); @@ -140,14 +182,31 @@ int uk_thread_init(struct uk_thread *thread, return 0; +err_fini: + /* Run fini functions starting from one level before the failed one + * because we expect that the failed one cleaned up. + */ + uk_thread_inittab_foreach_reverse2(itr, itr - 2) { + if (unlikely(!itr->fini)) + continue; + (itr->fini)(thread); + } + uk_free(allocator, thread->ctx); err_out: return ret; } void uk_thread_fini(struct uk_thread *thread, struct uk_alloc *allocator) { + struct uk_thread_inittab_entry *itr; + UK_ASSERT(thread != NULL); + uk_thread_inittab_foreach_reverse(itr) { + if (unlikely(!itr->fini)) + continue; + (itr->fini)(thread); + } uk_free(allocator, thread->ctx); thread->ctx = NULL; } -- 2.20.1
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |