WIP-pop-3000
[akaros.git] / user / parlib / vcore.c
index 8b7b2c5..3898aed 100644 (file)
@@ -15,6 +15,8 @@
 #include <ros/arch/membar.h>
 #include <parlib/printf-ext.h>
 #include <parlib/poke.h>
+#include <parlib/assert.h>
+#include <parlib/stdio.h>
 
 __thread int __vcoreid = 0;
 __thread bool __vcore_context = FALSE;
@@ -90,9 +92,15 @@ static int allocate_vcore_stack(int id)
        if (vcpd->vcore_stack)
                return 0; // reuse old stack
 
+// XXX consider adding a guard page.  yeah, it fucks with the VMR map
+//             at least for debugging
+//     force_a_page_fault = ACCESS_ONCE(*(int*)(pt->stacktop - sizeof(int)));
+//                     also, can change this in pth code to use the syscall_async (faster)
+//     syscall_async(&uthread->local_sysc, SYS_populate_va, aux, 1);
+
        void* stackbot = mmap(0, TRANSITION_STACK_SIZE,
-                             PROT_READ|PROT_WRITE|PROT_EXEC,
-                             MAP_POPULATE|MAP_ANONYMOUS, -1, 0);
+                             PROT_READ | PROT_WRITE | PROT_EXEC,
+                             MAP_POPULATE | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
 
        if(stackbot == MAP_FAILED)
                return -1; // errno set by mmap
@@ -102,37 +110,61 @@ static int allocate_vcore_stack(int id)
        return 0;
 }
 
-/* Helper, initializes a new vcore.  Do not call directly. */
-static int __prep_new_vcore(int vcoreid)
-{
-       if (allocate_vcore_stack(vcoreid))
-               goto error_vc_stack;
-       if (allocate_transition_tls(vcoreid))
-               goto error_tls;
-       return 0;
-
-error_tls:
-       free_vcore_stack(vcoreid);
-error_vc_stack:
-       return -1;
-}
-
-/* Initializes vcores before they are used, up to nr_total_vcores.
+/* Helper: prepares a vcore for use.  Takes a block of pages for the UCQs.
  *
  * Vcores need certain things, such as a stack and TLS.  These are determined by
  * userspace.  Every vcore needs these set up before we drop into vcore context
  * on that vcore.  This means we need to prep before asking the kernel for those
- * vcores. */
-static int prep_new_vcores(int nr_total_vcores)
+ * vcores.
+ *
+ * We could have this function do its own mmap, at the expense of O(n) syscalls
+ * when we prepare the extra vcores. */
+static void __prep_vcore(int vcoreid, uintptr_t mmap_block)
 {
-       static int _max_vcores_ever_wanted = 0;
+       struct preempt_data *vcpd = vcpd_of(vcoreid);
+       int ret;
 
-       for (int i = _max_vcores_ever_wanted; i < nr_total_vcores; i++) {
-               if (__prep_new_vcore(i))
-                       return -1;
-               _max_vcores_ever_wanted++;
-       }
-       return 0;
+       ret = allocate_vcore_stack(vcoreid);
+               assert(!ret);
+       ret = allocate_transition_tls(vcoreid);
+               assert(!ret);
+
+       vcpd->ev_mbox_public.type = EV_MBOX_UCQ;
+       ucq_init_raw(&vcpd->ev_mbox_public.ucq,
+                    mmap_block + 0 * PGSIZE,
+                    mmap_block + 1 * PGSIZE);
+       vcpd->ev_mbox_private.type = EV_MBOX_UCQ;
+       ucq_init_raw(&vcpd->ev_mbox_private.ucq,
+                    mmap_block + 2 * PGSIZE,
+                    mmap_block + 3 * PGSIZE);
+
+       /* Set the lowest level entry point for each vcore. */
+       vcpd->vcore_entry = (uintptr_t)__kernel_vcore_entry;
+}
+
+static void prep_vcore_0(void)
+{
+       uintptr_t mmap_block;
+
+       mmap_block = (uintptr_t)mmap(0, PGSIZE * 4,
+                                    PROT_WRITE | PROT_READ,
+                                    MAP_POPULATE | MAP_ANONYMOUS | MAP_PRIVATE,
+                                    -1, 0);
+       assert((void*)mmap_block != MAP_FAILED);
+       __prep_vcore(0, mmap_block);
+}
+
+static void prep_remaining_vcores(void)
+{
+       uintptr_t mmap_block;
+
+       mmap_block = (uintptr_t)mmap(0, PGSIZE * 4 * (max_vcores() - 1),
+                                    PROT_WRITE | PROT_READ,
+                                    MAP_POPULATE | MAP_ANONYMOUS | MAP_PRIVATE,
+                                    -1, 0);
+       assert((void*)mmap_block != MAP_FAILED);
+       for (int i = 1; i < max_vcores(); i++)
+               __prep_vcore(i, mmap_block + 4 * (i - 1) * PGSIZE);
 }
 
 /* Run libc specific early setup code. */
@@ -144,50 +176,32 @@ static void vcore_libc_init(void)
         * program. */
 }
 
-void __attribute__((constructor)) vcore_lib_init(void)
+/* We need to separate the guts of vcore_lib_ctor() into a separate function,
+ * since the uthread ctor depends on this ctor running first.
+ *
+ * Also note that if you make a global ctor (not static, like this used to be),
+ * any shared objects that you load when the binary is built with -rdynamic will
+ * run the global ctor from the binary, not the one from the .so. */
+void vcore_lib_init(void)
 {
-       uintptr_t mmap_block;
-
        /* 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);
-
+       parlib_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 (prep_new_vcores(1))
-               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(),
-                                    PROT_WRITE | PROT_READ,
-                                    MAP_POPULATE | MAP_ANONYMOUS, -1, 0);
-       /* Yeah, this doesn't fit in the error-handling scheme, but this whole
-        * system doesn't really handle failure, and needs a rewrite involving less
-        * mmaps/munmaps. */
-       assert(mmap_block);
-       /* Note we may end up doing vcore 0's elsewhere, for _Ss, or else have a
-        * separate ev_q for that. */
-       for (int i = 0; i < max_vcores(); i++) {
-               /* four pages total for both ucqs from the big block (2 pages each) */
-               vcpd_of(i)->ev_mbox_public.type = EV_MBOX_UCQ;
-               ucq_init_raw(&vcpd_of(i)->ev_mbox_public.ucq,
-                            mmap_block + (4 * i    ) * PGSIZE,
-                            mmap_block + (4 * i + 1) * PGSIZE);
-               vcpd_of(i)->ev_mbox_private.type = EV_MBOX_UCQ;
-               ucq_init_raw(&vcpd_of(i)->ev_mbox_private.ucq,
-                            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;
-       }
+       prep_vcore_0();
        assert(!in_vcore_context());
        vcore_libc_init();
-       return;
-vcore_lib_init_fail:
-       assert(0);
+}
+
+static void __attribute__((constructor)) vcore_lib_ctor(void)
+{
+       if (__in_fake_parlib())
+               return;
+       vcore_lib_init();
 }
 
 /* Helper functions used to reenter at the top of a vcore's stack for an
@@ -214,6 +228,8 @@ void vcore_reenter(void (*entry_func)(void))
 void vcore_change_to_m(void)
 {
        int ret;
+
+       prep_remaining_vcores();
        __procdata.res_req[RES_CORES].amt_wanted = 1;
        __procdata.res_req[RES_CORES].amt_wanted_min = 1;       /* whatever */
        assert(!in_multi_mode());
@@ -228,8 +244,9 @@ static void __vc_req_poke(void *nr_vc_wanted)
 {
        long nr_vcores_wanted = *(long*)nr_vc_wanted;
 
-       if (prep_new_vcores(nr_vcores_wanted))
-               panic("Unable to prep up to %d vcores!", nr_vcores_wanted);
+       /* We init'd up to max_vcores() VCs during init.  This assumes the kernel
+        * doesn't magically change that value (which it should not do). */
+       nr_vcores_wanted = MIN(nr_vcores_wanted, max_vcores());
        if (nr_vcores_wanted > __procdata.res_req[RES_CORES].amt_wanted)
                __procdata.res_req[RES_CORES].amt_wanted = nr_vcores_wanted;
        if (nr_vcores_wanted > num_vcores())
@@ -253,6 +270,8 @@ void vcore_request_total(long nr_vcores_wanted)
 {
        static long nr_vc_wanted;
 
+       if (parlib_never_vc_request || !parlib_wants_to_be_mcp)
+               return;
        if (nr_vcores_wanted == __procdata.res_req[RES_CORES].amt_wanted)
                return;
 
@@ -274,13 +293,10 @@ void vcore_request_total(long nr_vcores_wanted)
  *
  * So for now, this will keep the older behavior (one more than I have).  This
  * is all quite racy, so we can just guess and request a total number of vcores.
- *
- * Returns 0 always, probably panics on error.  This does not return the number
- * of cores actually granted (though some parts of the kernel do internally). */
-int vcore_request_more(long nr_new_vcores)
+ */
+void vcore_request_more(long nr_new_vcores)
 {
        vcore_request_total(nr_new_vcores + num_vcores());
-       return 0;
 }
 
 /* This can return, if you failed to yield due to a concurrent event.  Note
@@ -292,6 +308,9 @@ void vcore_yield(bool preempt_pending)
        unsigned long old_nr;
        uint32_t vcoreid = vcore_id();
        struct preempt_data *vcpd = vcpd_of(vcoreid);
+
+       if (!preempt_pending && parlib_never_yield)
+               return;
        __sync_fetch_and_and(&vcpd->flags, ~VC_CAN_RCV_MSG);
        /* no wrmb() necessary, handle_events() has an mb() if it is checking */
        /* Clears notif pending and tries to handle events.  This is an optimization
@@ -448,17 +467,24 @@ void ensure_vcore_runs(uint32_t vcoreid)
 }
 
 #define NR_RELAX_SPINS 1000
-/* If you are spinning in vcore context and it is likely that you don't know who
- * you are waiting on, call this.  It will spin for a bit before firing up the
- * potentially expensive __ensure_all_run().  Don't call this from uthread
- * context.  sys_change_vcore will probably mess you up. */
-void cpu_relax_vc(uint32_t vcoreid)
+/* If you are spinning and waiting on another vcore, call this.  Pass in the
+ * vcoreid of the core you are waiting on, or your own vcoreid if you don't
+ * know.  It will spin for a bit before firing up the potentially expensive
+ * __ensure_all_run(). */
+void cpu_relax_vc(uint32_t other_vcoreid)
 {
        static __thread unsigned int __vc_relax_spun = 0;
-       assert(in_vcore_context());
+
+       /* Uthreads with notifs enabled can just spin normally.  This actually
+        * depends on the 2LS preemption policy.  Currently, we receive notifs
+        * whenever another core is preempted, so we don't need to poll. */
+       if (notif_is_enabled(vcore_id())) {
+               cpu_relax();
+               return;
+       }
        if (__vc_relax_spun++ >= NR_RELAX_SPINS) {
-               /* if vcoreid == vcore_id(), this might be expensive */
-               ensure_vcore_runs(vcoreid);
+               /* if other_vcoreid == vcore_id(), this might be expensive */
+               ensure_vcore_runs(other_vcoreid);
                __vc_relax_spun = 0;
        }
        cpu_relax();
@@ -501,3 +527,14 @@ void __attribute__((noreturn)) vcore_yield_or_restart(void)
        set_stack_pointer((void*)vcpd->vcore_stack);
        vcore_entry();
 }
+
+void vcore_wake(uint32_t vcoreid, bool force_ipi)
+{
+       struct preempt_data *vcpd = vcpd_of(vcoreid);
+
+       vcpd->notif_pending = true;
+       if (vcoreid == vcore_id())
+               return;
+       if (force_ipi || !arch_has_mwait())
+               sys_self_notify(vcoreid, EV_NONE, 0, true);
+}