[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



 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.