[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Minios-devel] [UNIKRAFT PATCH 2/4] lib/uksched: create and delete thread-local storage area



On 16.04.19 15:21, Costin Lupu wrote:
Hi Florian,

Please see my comments inline.

On 4/15/19 1:03 PM, Florian Schmidt wrote:
Most of the actual TLS setup is architecture-specific, so sched.c and
thread.c call functions from a header file provided in arch/.

Signed-off-by: Florian Schmidt <florian.schmidt@xxxxxxxxx>
---
  arch/x86/x86_64/include/uk/asm/thread.h | 64 +++++++++++++++++++++++++
  include/uk/arch/thread.h                | 40 ++++++++++++++++
  lib/uksched/include/uk/sched.h          |  3 ++
  lib/uksched/include/uk/thread.h         |  3 +-
  lib/uksched/sched.c                     | 54 ++++++++++++++-------
  lib/uksched/thread.c                    |  8 ++--
  6 files changed, 152 insertions(+), 20 deletions(-)
  create mode 100644 arch/x86/x86_64/include/uk/asm/thread.h
  create mode 100644 include/uk/arch/thread.h

diff --git a/arch/x86/x86_64/include/uk/asm/thread.h 
b/arch/x86/x86_64/include/uk/asm/thread.h
new file mode 100644
index 00000000..6e95848d
--- /dev/null
+++ b/arch/x86/x86_64/include/uk/asm/thread.h

I had a discussion with Simon (CCed) a while ago, when I was working on
the changes for preemptive scheduling, about where we should put the
arch dependent code of threads. We agreed back then to keep it in the
plat/ code because arch/ contains only code that is available to user
applications as well. This is not the case for the TLS changes since
they are private to Unikraft, so please move this to
plat/common/include/x86/ . Similarly, .c files for thread arch dependent
code should be in plat/common/x86/.

Moreover, I would rename it to tls.h and use it also in the next patch
instead of thread_switch.h (see the discussion on the next patch).


Actually, after reading this patch, I agree to put this TLS code to arch. The main criteria for arch code is that it only depends on a particular architecture but is completely independent to any platform (linuxu, xen, kvm). It is right that programs and higher-level libraries can access this defintions, but this is also intended: Since this is a compiler thing I expect that every architecture provides this kind of code, so that uksched or any libc is able to do TLS operations independent of archictecture and platform.

However, I would not call the header thread, because it implements just a subset. I would call it something along `tls.h` and also namespace the provided functions, symbols, and macros: e.g., ukarch_tls_* and UKARCH_TLS_*. Please double-check this in all headers.

@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Authors: Florian Schmidt <florian.schmidt@xxxxxxxxx>
+ *
+ * Copyright (c) 2019, NEC Europe Ltd., NEC Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY.
+ */
+
+#ifndef __UKARCH_THREAD_H__
+#error Do not include this header directly
+#endif
+
+extern char _tls_start[], _etdata[], _tls_end[];
+
+static inline unsigned long tls_area_size(void)
+{
+       /* x86_64 ABI requires that fs:%0 contains the address of itself, to
+        * allow certain optimizations. Hence, the overall size of the size of
+        * the TLS area, plus 8 bytes.
+        */
+       return _tls_end - _tls_start + 8;
+}
+
+static inline unsigned int tls_area_align(void)
+{
+       return 8;
+}

Wouldn't this as a macro be good enough?

+
+static inline void tls_copy(void *tls_area)
+{
+       unsigned int tls_len = _tls_end - _tls_start;
+       unsigned int tls_data_len = _etdata - _tls_start;
+       unsigned int tls_bss_len = _tls_end - _etdata;
+
+       memcpy(tls_area, _tls_start, tls_data_len);
+       memset(tls_area+tls_data_len, 0, tls_bss_len);
+       *((uintptr_t *)(tls_area+tls_len)) = (uintptr_t)(tls_area+tls_len);

I know this passes the checkpatch, but please use the Unikraft coding
style for the spacing of these 2 lines of code in order to avoid
propagating such examples.


Btw, is unsigned int the right data type for adresses? I also prefer that headers that define the platform or architecture API never depend on any datatype definition provided by a libc. In this example, we have here a kind of an paradox because you need memcpy and memset but I would still like minimize the dependency. So, you can just replace the Unikraft-internal datatype for `uintptr_t`: `__uptr`.

+}
diff --git a/include/uk/arch/thread.h b/include/uk/arch/thread.h
new file mode 100644
index 00000000..76fd3ad5
--- /dev/null
+++ b/include/uk/arch/thread.h

Same comments as for the location of
arch/x86/x86_64/include/uk/asm/thread.h .

@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Authors: Florian Schmidt <florian.schmidt@xxxxxxxxx>
+ *
+ * Copyright (c) 2019, NEC Europe Ltd., NEC Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY.
+ */
+
+#ifndef __UKARCH_THREAD_H__
+#define __UKARCH_THREAD_H__
+
+#include <uk/asm/thread.h>
+
+#endif /* __UKARCH_THREAD_H__ */
diff --git a/lib/uksched/include/uk/sched.h b/lib/uksched/include/uk/sched.h
index cc5fbe93..3e21d8d8 100644
--- a/lib/uksched/include/uk/sched.h
+++ b/lib/uksched/include/uk/sched.h
@@ -50,6 +50,9 @@ struct uk_sched;
struct uk_sched *uk_sched_default_init(struct uk_alloc *a); +extern char _tls_start[], _etdata[], _tls_end[];
+#define have_tls_area (_tls_end - _tls_start)

Given that this is actually a predicate and you don't use uppercase in
the name of the macro, renaming it to have_tls_area() might be more
suggestive.

+
  extern struct uk_sched *uk_sched_head;
  int uk_sched_register(struct uk_sched *s);
  struct uk_sched *uk_sched_get_default(void);
diff --git a/lib/uksched/include/uk/thread.h b/lib/uksched/include/uk/thread.h
index 71e39225..8cb3d6b6 100644
--- a/lib/uksched/include/uk/thread.h
+++ b/lib/uksched/include/uk/thread.h
@@ -50,6 +50,7 @@ struct uk_sched;
  struct uk_thread {
        const char *name;
        void *stack;
+       void *tls;
        void *ctx;
        UK_TAILQ_ENTRY(struct uk_thread) thread_list;
        uint32_t flags;
@@ -106,7 +107,7 @@ struct uk_thread *uk_thread_current(void)
int uk_thread_init(struct uk_thread *thread,
                struct ukplat_ctx_callbacks *cbs, struct uk_alloc *allocator,
-               const char *name, void *stack,
+               const char *name, void *stack, void *tls,
                void (*function)(void *), void *arg);
  void uk_thread_fini(struct uk_thread *thread,
                struct uk_alloc *allocator);
diff --git a/lib/uksched/sched.c b/lib/uksched/sched.c
index e0416736..c46db730 100644
--- a/lib/uksched/sched.c
+++ b/lib/uksched/sched.c
@@ -33,10 +33,12 @@
   */
#include <stdlib.h>
+#include <string.h>
  #include <uk/plat/config.h>
  #include <uk/plat/thread.h>
  #include <uk/alloc.h>
  #include <uk/sched.h>
+#include <uk/arch/thread.h>
  #if CONFIG_LIBUKSCHEDCOOP
  #include <uk/schedcoop.h>
  #endif
@@ -148,27 +150,42 @@ static void *create_stack(struct uk_alloc *allocator)
        return stack;
  }
+static void *uk_thread_tls_create(struct uk_alloc *allocator)
+{
+       void *tls;
+
+       if (uk_posix_memalign(allocator, &tls,
+                               tls_area_align(), tls_area_size()))

I believe it would be nice to be able to check the returned value on
errors, so please use a variable for that.

+               return NULL;
+       tls_copy(tls);
+       return tls;
+}
+
  void uk_sched_idle_init(struct uk_sched *sched,
                void *stack, void (*function)(void *))
  {
        struct uk_thread *idle;
-       int rc;

Please don't remove the rc variable. See the comments below.

+       void *tls = NULL;
UK_ASSERT(sched != NULL); - if (stack == NULL)
-               stack = create_stack(sched->allocator);
-       UK_ASSERT(stack != NULL);
+       if (!stack && !(stack = create_stack(sched->allocator)))
+               goto out_crash;

Please don't squash these 2 lines since this change makes debugging more
difficult. With the original code one can put a breakpoint on the line
with the call for create_stack() and check the returned value. I suggest
you would use the same approach for the following lines of code as well.

+       if (have_tls_area && !(tls = uk_thread_tls_create(sched->allocator)))
+               goto out_crash;
idle = &sched->idle; - rc = uk_thread_init(idle,
+       if (uk_thread_init(idle,

Please keep this line as it was because this change make debugging more
difficult. We use variables to save the returned values of functions in
order to inspect the returned value when debugging. It doesn't hurt the
performance at all, so keeping this approach is on the plus side.

                        &sched->plat_ctx_cbs, sched->allocator,
-                       "Idle", stack, function, NULL);
-       if (rc)
-               UK_CRASH("Error initializing idle thread.");
+                       "Idle", stack, tls, function, NULL))
+               goto out_crash;
idle->sched = sched;
+       return;
+
+out_crash:
+       UK_CRASH("Error initializing the idle thread.");
  }
struct uk_thread *uk_sched_thread_create(struct uk_sched *sched,
@@ -177,7 +194,7 @@ struct uk_thread *uk_sched_thread_create(struct uk_sched 
*sched,
  {
        struct uk_thread *thread = NULL;
        void *stack = NULL;
-       int rc;

Again, please don't remove the rc variable.

+       void *tls = NULL;
thread = uk_malloc(sched->allocator, sizeof(struct uk_thread));
        if (thread == NULL) {
@@ -188,18 +205,17 @@ struct uk_thread *uk_sched_thread_create(struct uk_sched 
*sched,
        /* We can't use lazy allocation here
         * since the trap handler runs on the stack
         */
-       stack = create_stack(sched->allocator);
-       if (stack == NULL)
+       if (!(stack = create_stack(sched->allocator)))
+               goto err;

Again, please keep this as it was. Same discussion as above.

+       if (have_tls_area && !(tls = uk_thread_tls_create(sched->allocator)))
                goto err;
- rc = uk_thread_init(thread,
+       if (uk_thread_init(thread,

Same here.

                        &sched->plat_ctx_cbs, sched->allocator,
-                       name, stack, function, arg);
-       if (rc)
+                       name, stack, tls, function, arg))
                goto err;
- rc = uk_sched_thread_add(sched, thread, attr);
-       if (rc)
+       if (uk_sched_thread_add(sched, thread, attr))

Same here.

                goto err_add;
return thread;
@@ -207,6 +223,8 @@ struct uk_thread *uk_sched_thread_create(struct uk_sched 
*sched,
  err_add:
        uk_thread_fini(thread, sched->allocator);
  err:
+       if (tls)
+               uk_free(sched->allocator, tls);
        if (stack)
                uk_free(sched->allocator, stack);
        if (thread)
@@ -219,11 +237,15 @@ void uk_sched_thread_destroy(struct uk_sched *sched, 
struct uk_thread *thread)
  {
        UK_ASSERT(sched != NULL);
        UK_ASSERT(thread != NULL);
+       UK_ASSERT(thread->stack != NULL);
+       UK_ASSERT(!have_tls_area || thread->tls != NULL);

When I first saw this usage of have_tls_area I thought it's a (global)
variable. But it's not, so that's why I recommend changing it to
have_tls_area().

        UK_ASSERT(is_exited(thread));
UK_TAILQ_REMOVE(&sched->exited_threads, thread, thread_list);
        uk_thread_fini(thread, sched->allocator);
        uk_pfree(sched->allocator, thread->stack, STACK_SIZE_PAGE_ORDER);
+       if (thread->tls)
+               uk_free(sched->allocator, thread->tls);
        uk_free(sched->allocator, thread);
  }
diff --git a/lib/uksched/thread.c b/lib/uksched/thread.c
index 7400baee..a97fad9e 100644
--- a/lib/uksched/thread.c
+++ b/lib/uksched/thread.c
@@ -88,13 +88,14 @@ struct _reent *__getreent(void)
int uk_thread_init(struct uk_thread *thread,
                struct ukplat_ctx_callbacks *cbs, struct uk_alloc *allocator,
-               const char *name, void *stack,
+               const char *name, void *stack, void *tls,
                void (*function)(void *), void *arg)
  {
        unsigned long sp;
UK_ASSERT(thread != NULL);
        UK_ASSERT(stack != NULL);
+       UK_ASSERT(!have_tls_area || tls != NULL);
/* Save pointer to the thread on the stack to get current thread */
        *((unsigned long *) stack) = (unsigned long) thread;
@@ -108,6 +109,7 @@ int uk_thread_init(struct uk_thread *thread,
thread->name = name;
        thread->stack = stack;
+       thread->tls = tls;
/* Not runnable, not exited, not sleeping */
        thread->flags = 0;
@@ -121,8 +123,8 @@ int uk_thread_init(struct uk_thread *thread,
        reent_init(&thread->reent);
  #endif
- uk_pr_info("Thread \"%s\": pointer: %p, stack: %p\n",
-                  name, thread, thread->stack);
+       uk_pr_warn("Thread \"%s\": pointer: %p, stack: %p, tls: %p\n",
+                  name, thread, thread->stack, thread->tls);

Since we're changing this, please change it to uk_pr_debug() instead
because:
1) It is a debug message, not a warning.
2) It's exiting counterpart message is printed only for debug.
3) This would flood the output for scenarios when lots of threads are
used (e.g. the unit tests of pthread-embedded).

return 0;
  }


_______________________________________________
Minios-devel mailing list
Minios-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/minios-devel

 


Rackspace

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