|
[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 |