Remove the option to spawn_thread for an evq (XCC)
[akaros.git] / user / parlib / vcore.c
index 0b94ec2..b440c74 100644 (file)
@@ -1,30 +1,75 @@
-#include <arch/arch.h>
+#include <parlib/arch/arch.h>
 #include <stdbool.h>
 #include <errno.h>
-#include <vcore.h>
-#include <mcs.h>
+#include <parlib/vcore.h>
+#include <parlib/mcs.h>
 #include <sys/param.h>
-#include <parlib.h>
+#include <parlib/parlib.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <sys/mman.h>
 #include <stdio.h>
-#include <event.h>
-#include <uthread.h>
-#include <ucq.h>
+#include <parlib/event.h>
+#include <parlib/uthread.h>
+#include <parlib/ucq.h>
 #include <ros/arch/membar.h>
+#include <parlib/printf-ext.h>
 
-/* starting with 1 since we alloc vcore0's stacks and TLS in vcore_init(). */
+__thread int __vcoreid = 0;
+__thread bool __vcore_context = FALSE;
+
+/* starting with 1 since we alloc vcore0's stacks and TLS in vcore_lib_init(). */
 static size_t _max_vcores_ever_wanted = 1;
 atomic_t nr_new_vcores_wanted;
 atomic_t vc_req_being_handled;
 
-bool vc_initialized = FALSE;
 __thread struct syscall __vcore_one_sysc = {.flags = (atomic_t)SC_DONE, 0};
 
 /* Per vcore entery function used when reentering at the top of a vcore's stack */
 static __thread void (*__vcore_reentry_func)(void) = NULL;
 
+/* The default user vcore_entry function. */
+void __attribute__((noreturn)) __vcore_entry(void)
+{
+       extern void uthread_vcore_entry(void);
+       uthread_vcore_entry();
+       fprintf(stderr, "vcore_entry() should never return!\n");
+       abort();
+       __builtin_unreachable();
+}
+void vcore_entry(void) __attribute__((weak, alias ("__vcore_entry")));
+
+/* The lowest level function jumped to by the kernel on every vcore_entry.
+ * Currently, this function is only necessary so we can set the tls_desc from
+ * the vcpd for non x86_64 architectures. We should consider removing this and
+ * making it mandatory to set the tls_desc in the kernel. We wouldn't even
+ * need to pass the vcore id to user space at all if we did this.  It would
+ * already be set in the preinstalled TLS as __vcore_id. */
+static void __attribute__((noreturn)) __kernel_vcore_entry(void)
+{
+       /* 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. */
+       int id = __vcore_id_on_entry;
+       #ifndef __x86_64__
+       set_tls_desc(vcpd_of(id)->vcore_tls_desc);
+       #endif
+       /* Every time the vcore comes up, it must set that it is in vcore context.
+        * uthreads may share the same TLS as their vcore (when uthreads do not have
+        * their own TLS), and if a uthread was preempted, __vcore_context == FALSE,
+        * and that will continue to be true the next time the vcore pops up. */
+       __vcore_context = TRUE;
+       vcore_entry();
+       fprintf(stderr, "vcore_entry() should never return!\n");
+       abort();
+       __builtin_unreachable();
+}
+
 /* TODO: probably don't want to dealloc.  Considering caching */
 static void free_transition_tls(int id)
 {
@@ -38,6 +83,9 @@ static void free_transition_tls(int id)
 
 static int allocate_transition_tls(int id)
 {
+       /* Libc function to initialize TLS-based locale info for ctype functions. */
+       extern void __ctype_init(void);
+
        /* We want to free and then reallocate the tls rather than simply 
         * reinitializing it because its size may have changed.  TODO: not sure if
         * this is right.  0-ing is one thing, but freeing and reallocating can be
@@ -50,19 +98,31 @@ static int allocate_transition_tls(int id)
                errno = ENOMEM;
                return -1;
        }
+
+       /* Setup some intitial TLS data for the newly allocated transition tls. */
+       void *temp_tcb = get_tls_desc();
+       set_tls_desc(tcb);
+       begin_safe_access_tls_vars();
+       __vcoreid = id;
+       __vcore_context = TRUE;
+       __ctype_init();
+       end_safe_access_tls_vars();
+       set_tls_desc(temp_tcb);
+
+       /* Install the new tls into the vcpd. */
        set_vcpd_tls_desc(id, tcb);
        return 0;
 }
 
-static void free_transition_stack(int id)
+static void free_vcore_stack(int id)
 {
        // don't actually free stacks
 }
 
-static int allocate_transition_stack(int id)
+static int allocate_vcore_stack(int id)
 {
        struct preempt_data *vcpd = vcpd_of(id);
-       if (vcpd->transition_stack)
+       if (vcpd->vcore_stack)
                return 0; // reuse old stack
 
        void* stackbot = mmap(0, TRANSITION_STACK_SIZE,
@@ -72,22 +132,35 @@ static int allocate_transition_stack(int id)
        if(stackbot == MAP_FAILED)
                return -1; // errno set by mmap
 
-       vcpd->transition_stack = (uintptr_t)stackbot + TRANSITION_STACK_SIZE;
+       vcpd->vcore_stack = (uintptr_t)stackbot + TRANSITION_STACK_SIZE;
 
        return 0;
 }
 
-void vcore_init(void)
+/* Run libc specific early setup code. */
+static void vcore_libc_init(void)
+{
+       register_printf_specifier('r', printf_errstr, printf_errstr_info);
+       /* TODO: register for other kevents/signals and whatnot (can probably reuse
+        * the simple ev_q).  Could also do this via explicit functions from the
+        * program. */
+}
+
+void __attribute__((constructor)) vcore_lib_init(void)
 {
        uintptr_t mmap_block;
-       /* Note this is racy, but okay.  The first time through, we are _S */
+
+       /* Note this is racy, but okay.  The first time through, we are _S.
+        * Also, this is the "lowest" level constructor for now, so we don't need
+        * to call any other init functions after our run_once() call. This may
+        * change in the future. */
        init_once_racy(return);
 
        /* 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_fail;
+       if (allocate_vcore_stack(0) || allocate_transition_tls(0))
+               goto vcore_lib_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(),
@@ -107,15 +180,14 @@ void vcore_init(void)
                ucq_init_raw(&vcpd_of(i)->ev_mbox_private.ev_msgs,
                             mmap_block + (4 * i + 2) * PGSIZE,
                             mmap_block + (4 * i + 3) * PGSIZE);
+               /* Set the lowest level entry point for each vcore. */
+               vcpd_of(i)->vcore_entry = (uintptr_t)__kernel_vcore_entry;
        }
        atomic_init(&vc_req_being_handled, 0);
        assert(!in_vcore_context());
-       /* no longer need to enable notifs on vcore 0, it is set like that by
-        * default (so you drop into vcore context immediately on transtioning to
-        * _M) */
-       vc_initialized = TRUE;
+       vcore_libc_init();
        return;
-vcore_init_fail:
+vcore_lib_init_fail:
        assert(0);
 }
 
@@ -134,23 +206,11 @@ void vcore_reenter(void (*entry_func)(void))
   struct preempt_data *vcpd = vcpd_of(vcore_id());
 
   __vcore_reentry_func = entry_func;
-  set_stack_pointer((void*)vcpd->transition_stack);
+  set_stack_pointer((void*)vcpd->vcore_stack);
   cmb();
   __vcore_reenter();
 }
 
-/* This gets called in glibc before calling the programs 'main'.  Need to set
- * ourselves up so that thread0 is a uthread, and then register basic signals to
- * go to vcore 0. */
-void vcore_event_init(void)
-{
-       /* set up our thread0 as a uthread */
-       uthread_slim_init();
-       /* TODO: register for other kevents/signals and whatnot (can probably reuse
-        * the simple ev_q).  Could also do this via explicit functions from the
-        * program. */
-}
-
 /* Helper, picks some sane defaults and changes the process into an MCP */
 void vcore_change_to_m(void)
 {
@@ -214,7 +274,6 @@ int vcore_request(long nr_new_vcores)
 {
        long nr_to_prep_now, nr_vcores_wanted;
 
-       assert(vc_initialized);
        /* Early sanity checks */
        if ((nr_new_vcores < 0) || (nr_new_vcores + num_vcores() > max_vcores()))
                return -1;      /* consider ERRNO */
@@ -243,7 +302,7 @@ try_handle_it:
                nr_vcores_wanted = MIN(nr_vcores_wanted, max_vcores());
                /* Make sure all we might ask for are prepped */
                for (long i = _max_vcores_ever_wanted; i < nr_vcores_wanted; i++) {
-                       if (allocate_transition_stack(i) || allocate_transition_tls(i)) {
+                       if (allocate_vcore_stack(i) || allocate_transition_tls(i)) {
                                atomic_set(&vc_req_being_handled, 0);   /* unlock and bail out*/
                                return -1;
                        }
@@ -285,8 +344,9 @@ void vcore_yield(bool preempt_pending)
         * to avoid the yield syscall if we have an event pending.  If there is one,
         * we want to unwind and return to the 2LS loop, where we may not want to
         * yield anymore.
-        * Note that the kernel only cares about CAN_RCV_MSG for the desired vcore,
-        * not for a FALLBACK.  */
+        * Note that the kernel only cares about CAN_RCV_MSG for the desired vcore;
+        * when spamming, it relies on membership of lists within the kernel.  Look
+        * at spam_list_member() for more info (k/s/event.c). */
        if (handle_events(vcoreid)) {
                __sync_fetch_and_or(&vcpd->flags, VC_CAN_RCV_MSG);
                return;
@@ -353,17 +413,42 @@ void disable_notifs(uint32_t vcoreid)
 }
 
 /* Like smp_idle(), this will put the core in a state that it can only be woken
- * up by an IPI.  In the future, we may halt or something.  This will return if
- * an event was pending (could be the one you were waiting for). */
+ * up by an IPI.  For now, this is a halt.  Maybe an mwait in the future.
+ *
+ * This will return if an event was pending (could be the one you were waiting
+ * for) or if the halt failed for some reason, such as a concurrent RKM.  If
+ * successful, this will not return at all, and the vcore will restart from the
+ * top next time it wakes.  Any sort of IRQ will wake the core.
+ *
+ * Alternatively, I might make this so it never returns, if that's easier to
+ * work with (similar issues with yield). */
 void vcore_idle(void)
 {
        uint32_t vcoreid = vcore_id();
+       /* Once we enable notifs, the calling context will be treated like a uthread
+        * (saved into the uth slot).  We don't want to ever run it again, so we
+        * need to make sure there's no cur_uth. */
+       assert(!current_uthread);
+       /* This clears notif_pending (check, signal, check again pattern). */
        if (handle_events(vcoreid))
                return;
+       /* This enables notifs, but also checks notif pending.  At this point, any
+        * new notifs will restart the vcore from the top. */
        enable_notifs(vcoreid);
-       while (1) {
-               cpu_relax();
-       }
+       /* From now, til we get into the kernel, any notifs will permanently destroy
+        * this context and start the VC from the top.
+        *
+        * Once we're in the kernel, any messages (__notify, __preempt), will be
+        * RKMs.  halt will need to check for those atomically.  Checking for
+        * notif_pending in the kernel (sleep only if not set) is not enough, since
+        * not all reasons for the kernel to stay awak set notif_pending (e.g.,
+        * __preempts and __death).
+        *
+        * At this point, we're out of VC ctx, so anyone who sets notif_pending
+        * should also send an IPI / __notify */
+       sys_halt_core(0);
+       /* in case halt returns without actually restarting the VC ctx. */
+       disable_notifs(vcoreid);
 }
 
 /* Helper, that actually makes sure a vcore is running.  Call this is you really
@@ -415,12 +500,12 @@ void ensure_vcore_runs(uint32_t vcoreid)
  * context.  sys_change_vcore will probably mess you up. */
 void cpu_relax_vc(uint32_t vcoreid)
 {
-       unsigned int spun = 0;
+       static __thread unsigned int __vc_relax_spun = 0;
        assert(in_vcore_context());
-       if (spun++ >= NR_RELAX_SPINS) {
+       if (__vc_relax_spun++ >= NR_RELAX_SPINS) {
                /* if vcoreid == vcore_id(), this might be expensive */
                ensure_vcore_runs(vcoreid);
-               spun = 0;
+               __vc_relax_spun = 0;
        }
        cpu_relax();
 }