Vcore TLS desc in procdata (XCC)
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 31 Jul 2013 18:47:14 +0000 (11:47 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 31 Jul 2013 18:47:14 +0000 (11:47 -0700)
Userspace now tells the kernel what TLS descriptor to use for each vcore.  The
main reason is that when making a new context, the kernel would zero the TLS
base, and then userspace would quickly set it to the vcore's TLS.  On amd64,
this incurs an extra syscall (for now), and on i686, it's an extra 70ns for the
lousy-LDT mangling.  This change allows the kernel to quickly set the TLS base
(and optionally not change it if it is already correct).

Note that only amd64 uses this option in the kernel; riscv and i686 still
handle this in userspace, but also expose their tlsdesc to the kernel.

The other benefit to this is debugging - it's often helpful for me to have
access to this from the kernel.

As a historic note, this isn't the first arch-dependent tidbit in procdata.
The transition_stack (vcore's stack) was added to procdata since sparc couldn't
handle not having a stack (x86 was just loading %sp in assembly before _start).

12 files changed:
kern/arch/riscv/process.c
kern/arch/x86/process32.c
kern/arch/x86/process64.c
kern/include/process.h
kern/include/ros/event.h
kern/src/elf.c
kern/src/process.c
tests/microb_test.c
tools/compilers/gcc-glibc/glibc-2.14.1-ros/sysdeps/ros/start.c
user/parlib/include/vcore.h
user/parlib/uthread.c
user/parlib/vcore.c

index f528a6f..bdedee3 100644 (file)
@@ -20,11 +20,20 @@ void proc_pop_ctx(struct user_context *ctx)
 
 /* TODO: consider using a SW context */
 void proc_init_ctx(struct user_context *ctx, uint32_t vcoreid, uintptr_t entryp,
-                   uintptr_t stack_top)
+                   uintptr_t stack_top, uintptr_t tls_desc)
 {
        struct hw_trapframe *tf = &ctx->tf.hw_tf;
        ctx->type = ROS_HW_CTX;
 
+       /* TODO: If you'd like, take tls_desc and save it in the ctx somehow, so
+        * that proc_pop_ctx will set up that TLS before launching.  If you do this,
+        * you can change _start.c to not reset the TLS in userspace.
+        *
+        * This is a bigger deal on amd64, where we take a (fast) syscall to change
+        * the TLS desc, right after the kernel just 0'd out the TLS desc.  If you
+        * can change your HW TLS desc with negligible overhead, then feel free to
+        * do whatever.  Long term, it might be better to do whatever amd64 does. */
+
        memset(tf, 0, sizeof(*tf));
 
        tf->gpr[GPR_SP] = stack_top-64;
index d2f7859..de969c4 100644 (file)
@@ -87,7 +87,7 @@ void proc_pop_ctx(struct user_context *ctx)
 
 /* TODO: consider using a SW context */
 void proc_init_ctx(struct user_context *ctx, uint32_t vcoreid, uintptr_t entryp,
-                   uintptr_t stack_top)
+                   uintptr_t stack_top, uintptr_t tls_desc)
 {
        struct hw_trapframe *tf = &ctx->tf.hw_tf;
        ctx->type = ROS_HW_CTX;
@@ -112,6 +112,8 @@ void proc_init_ctx(struct user_context *ctx, uint32_t vcoreid, uintptr_t entryp,
        /* Coupled closely with user's entry.S.  id is the vcoreid, which entry.S
         * uses to determine what to do.  vcoreid == 0 is the main core/context. */
        tf->tf_regs.reg_eax = vcoreid;
+       /* Note we don't pass the tlsdesc.  32 bit TLS is pretty jacked up, so we
+        * let userspace deal with it. TODO: (TLSV) */
 }
 
 /* TODO: handle both HW and SW contexts */
index 9bff07e..3cd2592 100644 (file)
@@ -68,9 +68,17 @@ void proc_pop_ctx(struct user_context *ctx)
        panic("Unknown context type!\n");
 }
 
+/* Helper: if *addr isn't a canonical user address, poison it.  Use this when
+ * you need a canonical address (like MSR_FS_BASE) */
+static void enforce_user_canon(uintptr_t *addr)
+{
+       if (*addr >> 47 != 0)
+               *addr = 0x5a5a5a5a;
+}
+
 /* TODO: consider using a SW context */
 void proc_init_ctx(struct user_context *ctx, uint32_t vcoreid, uintptr_t entryp,
-                   uintptr_t stack_top)
+                   uintptr_t stack_top, uintptr_t tls_desc)
 {
        struct hw_trapframe *tf = &ctx->tf.hw_tf;
        ctx->type = ROS_HW_CTX;
@@ -94,14 +102,8 @@ void proc_init_ctx(struct user_context *ctx, uint32_t vcoreid, uintptr_t entryp,
        /* Coupled closely with user's entry.S.  id is the vcoreid, which entry.S
         * uses to determine what to do.  vcoreid == 0 is the main core/context. */
        tf->tf_rax = vcoreid;
-}
-
-/* Helper: if *addr isn't a canonical user address, poison it.  Use this when
- * you need a canonical address (like MSR_FS_BASE) */
-static void enforce_user_canon(uintptr_t *addr)
-{
-       if (*addr >> 47 != 0)
-               *addr = 0x5a5a5a5a;
+       tf->tf_fsbase = tls_desc;
+       enforce_user_canon(&tf->tf_fsbase);
 }
 
 void proc_secure_ctx(struct user_context *ctx)
index b8da41e..5675a65 100644 (file)
@@ -140,7 +140,7 @@ void __tlbshootdown(uint32_t srcid, long a0, long a1, long a2);
 /* Arch Specific */
 void proc_pop_ctx(struct user_context *ctx) __attribute__((noreturn));
 void proc_init_ctx(struct user_context *ctx, uint32_t vcoreid, uintptr_t entryp,
-                   uintptr_t stack_top);
+                   uintptr_t stack_top, uintptr_t tls_desc);
 void proc_secure_ctx(struct user_context *ctx);
 void __abandon_core(void);
 
index 125d181..44327af 100644 (file)
@@ -114,6 +114,7 @@ struct preempt_data {
        struct ancillary_state          preempt_anc;
        struct user_context                     uthread_ctx;            /* for preempts or notifs */
        uintptr_t                                       transition_stack;       /* advertised by the user */
+       uintptr_t                                       vcore_tls_desc;         /* advertised by the user */
        atomic_t                                        flags;
        int                                                     rflags;                         /* racy flags */
        bool                                            notif_disabled;         /* vcore unwilling to recv*/
index 67ab74f..d8fc0df 100644 (file)
@@ -259,7 +259,7 @@ int load_elf(struct proc* p, struct file* f)
        memcpy(p->procinfo->argp+auxp_pos,auxp,sizeof(auxp));
 
        uintptr_t core0_entry = ei.dynamic ? interp_ei.entry : ei.entry;
-       proc_init_ctx(&p->scp_ctx, 0, core0_entry, USTACKTOP);
+       proc_init_ctx(&p->scp_ctx, 0, core0_entry, USTACKTOP, 0);
        p->env_entry = ei.entry;
 
        int flags = MAP_FIXED | MAP_ANONYMOUS;
index a12ee01..7c35eb3 100644 (file)
@@ -504,7 +504,7 @@ void proc_run_s(struct proc *p)
                                pcpui->cur_ctx = &pcpui->actual_ctx;
                                memset(pcpui->cur_ctx, 0, sizeof(struct user_context));
                                proc_init_ctx(pcpui->cur_ctx, 0, p->env_entry,
-                                             vcpd->transition_stack);
+                                             vcpd->transition_stack, vcpd->vcore_tls_desc);
                        } else {
                                /* If they have no transition stack, then they can't receive
                                 * events.  The most they are getting is a wakeup from the
@@ -1760,7 +1760,7 @@ static void __set_curctx_to_vcoreid(struct proc *p, uint32_t vcoreid,
        } else { /* not restarting from a preemption, use a fresh vcore */
                assert(vcpd->transition_stack);
                proc_init_ctx(&pcpui->actual_ctx, vcoreid, p->env_entry,
-                             vcpd->transition_stack);
+                             vcpd->transition_stack, vcpd->vcore_tls_desc);
                /* Disable/mask active notifications for fresh vcores */
                vcpd->notif_disabled = TRUE;
        }
@@ -1990,7 +1990,7 @@ void __notify(uint32_t srcid, long a0, long a1, long a2)
        vcpd->uthread_ctx = *pcpui->cur_ctx;
        memset(pcpui->cur_ctx, 0, sizeof(struct user_context));
        proc_init_ctx(pcpui->cur_ctx, vcoreid, p->env_entry,
-                     vcpd->transition_stack);
+                     vcpd->transition_stack, vcpd->vcore_tls_desc);
        /* this cur_ctx will get run when the kernel returns / idles */
 }
 
index fac8789..1b3e6f4 100644 (file)
@@ -56,10 +56,9 @@ static uint32_t __get_pcoreid(void)
 void set_tlsdesc_test(unsigned long nr_loops)
 {
 #ifdef __i386__
-       extern void** vcore_thread_control_blocks;
        uint32_t vcoreid = vcore_id();
        void *mytls = get_tls_desc(vcoreid);
-       void *vctls = vcore_thread_control_blocks[vcoreid];
+       void *vctls = get_vcpd_tls_desc(vcoreid);
        segdesc_t tmp = SEG(STA_W, (uint32_t)vctls, 0xffffffff, 3);
        uint32_t gs = (vcoreid << 3) | 0x07;
     for (int i = 0; i < nr_loops; i++) {
index 19ab23c..448f587 100644 (file)
@@ -7,9 +7,6 @@
 #include <unistd.h>
 #include <vcore-tls.c>
 
-void** __vcore_thread_control_blocks = NULL;
-weak_alias(__vcore_thread_control_blocks,vcore_thread_control_blocks)
-
 __thread int __vcoreid = 0;
 __thread bool __vcore_context = FALSE;
 
@@ -62,7 +59,18 @@ _start(void)
        // acquire a TCB.
        if(init || (id != 0))
        {
-               set_tls_desc(__vcore_thread_control_blocks[id],id);
+               /* The kernel sets the TLS desc for us, based on whatever is in VCPD.
+                *
+                * x86 32-bit TLS is pretty jacked up, so the kernel doesn't set the TLS
+                * desc for us.  it's a little more expensive to do it here, esp for
+                * amd64.  Can remove this when/if we overhaul 32 bit TLS.
+                *
+                * AFAIK, riscv's TLS changes are really cheap, and they don't do it in
+                * the kernel (yet/ever), so they can set their TLS here too. */
+               #ifndef __x86_64__
+               set_tls_desc((void*)__procdata.vcore_preempt_data[id].vcore_tls_desc,
+                            id);
+               #endif
                __vcoreid = id;
                __vcore_context = TRUE;
                vcore_entry();
index db5cba4..408f3c8 100644 (file)
@@ -50,6 +50,8 @@ static inline bool vcore_is_preempted(uint32_t vcoreid);
 static inline struct preempt_data *vcpd_of(uint32_t vcoreid);
 static inline bool preempt_is_pending(uint32_t vcoreid);
 static inline bool __preempt_is_pending(uint32_t vcoreid);
+static inline void *get_vcpd_tls_desc(uint32_t vcoreid);
+static inline void set_vcpd_tls_desc(uint32_t vcoreid, void *tls_desc);
 void vcore_init(void);
 void vcore_event_init(void);
 void vcore_change_to_m(void);
@@ -143,6 +145,17 @@ static inline bool __preempt_is_pending(uint32_t vcoreid)
        return __procinfo.vcoremap[vcoreid].preempt_pending;
 }
 
+/* The kernel interface uses uintptr_t, but we have a lot of older code that
+ * uses void *, hence the casting. */
+static inline void *get_vcpd_tls_desc(uint32_t vcoreid)
+{
+       return (void*)__procdata.vcore_preempt_data[vcoreid].vcore_tls_desc;
+}
+
+static inline void set_vcpd_tls_desc(uint32_t vcoreid, void *tls_desc)
+{
+       __procdata.vcore_preempt_data[vcoreid].vcore_tls_desc = (uintptr_t)tls_desc;
+}
 
 #ifndef __PIC__
 
@@ -245,9 +258,8 @@ static inline bool __preempt_is_pending(uint32_t vcoreid)
 
 #define vcore_set_tls_var(name, val)                                           \
 ({                                                                             \
-       extern void** vcore_thread_control_blocks;                                 \
        typeof(val) __val = val;                                                   \
-       begin_access_tls_vars(vcore_thread_control_blocks[vcoreid]);               \
+       begin_access_tls_vars(get_vcpd_tls_desc(vcoreid));                         \
        name = __val;                                                              \
        end_access_tls_vars();                                                     \
 })
@@ -255,7 +267,7 @@ static inline bool __preempt_is_pending(uint32_t vcoreid)
 #define vcore_get_tls_var(name)                                                \
 ({                                                                             \
        typeof(name) val;                                                          \
-       begin_access_tls_vars(vcore_tls_descs[vcoreid]);                           \
+       begin_access_tls_vars(get_vcpd_tls_desc(vcoreid));                         \
        val = name;                                                                \
        end_access_tls_vars();                                                     \
        val;                                                                       \
index 772f6ce..c146360 100644 (file)
@@ -53,8 +53,7 @@ static void uthread_manage_thread0(struct uthread *uthread)
         * issue is that vcore0's transition-TLS isn't TLS_INITed yet.  Until it is
         * (right before vcore_entry(), don't try and take the address of any of
         * its TLS vars. */
-       extern void** vcore_thread_control_blocks;
-       set_tls_desc(vcore_thread_control_blocks[0], 0);
+       set_tls_desc(get_vcpd_tls_desc(0), 0);
        /* We might have a basic uthread already installed (from slim_init), so
         * free it before installing the new one. */
        if (current_uthread)
@@ -314,8 +313,7 @@ void uthread_yield(bool save_state, void (*yield_func)(struct uthread*, void*),
        }
        /* Change to the transition context (both TLS (if applicable) and stack). */
        if (__uthread_has_tls(uthread)) {
-               extern void **vcore_thread_control_blocks;
-               set_tls_desc(vcore_thread_control_blocks[vcoreid], vcoreid);
+               set_tls_desc(get_vcpd_tls_desc(vcoreid), vcoreid);
                assert(current_uthread == uthread);
                assert(in_vcore_context());
        } else {
@@ -773,7 +771,6 @@ void handle_vc_preempt(struct event_msg *ev_msg, unsigned int ev_type)
        struct preempt_data *vcpd = vcpd_of(vcoreid);
        uint32_t rem_vcoreid = ev_msg->ev_arg2;
        struct preempt_data *rem_vcpd = vcpd_of(rem_vcoreid);
-       extern void **vcore_thread_control_blocks;
        struct uthread *uthread_to_steal = 0;
        bool cant_migrate = FALSE;
 
@@ -836,7 +833,7 @@ void handle_vc_preempt(struct event_msg *ev_msg, unsigned int ev_type)
        vcoreid = vcore_id();   /* need to copy this out to our stack var */
        /* We want to minimize the time we're in the remote vcore's TLS, so we peak
         * and make the minimum changes we need, and deal with everything later. */
-       set_tls_desc(vcore_thread_control_blocks[rem_vcoreid], vcoreid);
+       set_tls_desc(get_vcpd_tls_desc(rem_vcoreid), vcoreid);
        if (current_uthread) {
                if (current_uthread->flags & UTHREAD_DONT_MIGRATE) {
                        cant_migrate = TRUE;
@@ -845,7 +842,7 @@ void handle_vc_preempt(struct event_msg *ev_msg, unsigned int ev_type)
                        current_uthread = 0;
                }
        }
-       set_tls_desc(vcore_thread_control_blocks[vcoreid], vcoreid);
+       set_tls_desc(get_vcpd_tls_desc(vcoreid), vcoreid);
        /* Extremely rare: they have a uthread, but it can't migrate.  So we'll need
         * to change to them. */
        if (cant_migrate) {
index 6d7860c..0b94ec2 100644 (file)
@@ -19,7 +19,6 @@ static size_t _max_vcores_ever_wanted = 1;
 atomic_t nr_new_vcores_wanted;
 atomic_t vc_req_being_handled;
 
-extern void** vcore_thread_control_blocks;
 bool vc_initialized = FALSE;
 __thread struct syscall __vcore_one_sysc = {.flags = (atomic_t)SC_DONE, 0};
 
@@ -29,10 +28,11 @@ static __thread void (*__vcore_reentry_func)(void) = NULL;
 /* TODO: probably don't want to dealloc.  Considering caching */
 static void free_transition_tls(int id)
 {
-       if(vcore_thread_control_blocks[id])
-       {
-               free_tls(vcore_thread_control_blocks[id]);
-               vcore_thread_control_blocks[id] = NULL;
+       if (get_vcpd_tls_desc(id)) {
+               /* Note we briefly have no TLS desc in VCPD.  This is fine so long as
+                * that vcore doesn't get started fresh before we put in a new desc */
+               free_tls(get_vcpd_tls_desc(id));
+               set_vcpd_tls_desc(id, NULL);
        }
 }
 
@@ -46,11 +46,11 @@ static int allocate_transition_tls(int id)
        free_transition_tls(id);
 
        void *tcb = allocate_tls();
-
-       if ((vcore_thread_control_blocks[id] = tcb) == NULL) {
+       if (!tcb) {
                errno = ENOMEM;
                return -1;
        }
+       set_vcpd_tls_desc(id, tcb);
        return 0;
 }
 
@@ -83,16 +83,11 @@ void vcore_init(void)
        /* Note this is racy, but okay.  The first time through, we are _S */
        init_once_racy(return);
 
-       vcore_thread_control_blocks = (void**)calloc(max_vcores(),sizeof(void*));
-
-       if(!vcore_thread_control_blocks)
-               goto vcore_init_fail;
-
        /* Need to alloc vcore0's transition stuff here (technically, just the TLS)
         * so that schedulers can use vcore0's transition TLS before it comes up in
         * vcore_entry() */
        if(allocate_transition_stack(0) || allocate_transition_tls(0))
-               goto vcore_init_tls_fail;
+               goto vcore_init_fail;
 
        /* Initialize our VCPD event queues' ucqs, two pages per ucq, 4 per vcore */
        mmap_block = (uintptr_t)mmap(0, PGSIZE * 4 * max_vcores(),
@@ -120,8 +115,6 @@ void vcore_init(void)
         * _M) */
        vc_initialized = TRUE;
        return;
-vcore_init_tls_fail:
-       free(vcore_thread_control_blocks);
 vcore_init_fail:
        assert(0);
 }