Removes __proc_set_allcores()
[akaros.git] / kern / src / process.c
index 9107d75..4aa0046 100644 (file)
@@ -261,6 +261,7 @@ static void proc_init_procinfo(struct proc* p)
        memset(&p->procinfo->vcoremap, 0, sizeof(p->procinfo->vcoremap));
        memset(&p->procinfo->pcoremap, 0, sizeof(p->procinfo->pcoremap));
        p->procinfo->num_vcores = 0;
+       p->procinfo->is_mcp = FALSE;
        p->procinfo->coremap_seqctr = SEQCTR_INITIALIZER;
        /* For now, we'll go up to the max num_cpus (at runtime).  In the future,
         * there may be cases where we can have more vcores than num_cpus, but for
@@ -309,7 +310,6 @@ error_t proc_alloc(struct proc **pp, struct proc *parent)
        p->exitcode = 1337;     /* so we can see processes killed by the kernel */
        p->ppid = parent ? parent->pid : 0;
        p->state = PROC_CREATED; /* shouldn't go through state machine for init */
-       p->is_mcp = FALSE;
        p->env_flags = 0;
        p->env_entry = 0; // cheating.  this really gets set later
        p->heap_top = (void*)UTEXT;     /* heap_bottom set in proc_init_procinfo */
@@ -860,6 +860,8 @@ void proc_yield(struct proc *SAFE p, bool being_nice)
        /* Note this protects stuff userspace should look at, which doesn't
         * include the TAILQs. */
        __seq_start_write(&p->procinfo->coremap_seqctr);
+       /* Next time the vcore starts, it starts fresh */
+       vcpd->notif_disabled = FALSE;
        __unmap_vcore(p, vcoreid);
        /* Adjust implied resource desires */
        p->resources[RES_CORES].amt_granted = --(p->procinfo->num_vcores);
@@ -904,8 +906,8 @@ void proc_notify(struct proc *p, uint32_t vcoreid)
 {
        struct preempt_data *vcpd = &p->procdata->vcore_preempt_data[vcoreid];
        vcpd->notif_pending = TRUE;
-       wrmb(); /* must write notif_pending before reading notif_enabled */
-       if (vcpd->notif_enabled) {
+       wrmb(); /* must write notif_pending before reading notif_disabled */
+       if (!vcpd->notif_disabled) {
                /* GIANT WARNING: we aren't using the proc-lock to protect the
                 * vcoremap.  We want to be able to use this from interrupt context,
                 * and don't want the proc_lock to be an irqsave.  Spurious
@@ -938,7 +940,7 @@ bool __proc_is_mcp(struct proc *p)
 {
        /* in lieu of using the amount of cores requested, or having a bunch of
         * states (like PROC_WAITING_M and _S), I'll just track it with a bool. */
-       return p->is_mcp;
+       return p->procinfo->is_mcp;
 }
 
 /************************  Preemption Functions  ******************************
@@ -1159,6 +1161,8 @@ static void __proc_give_a_pcore(struct proc *p, uint32_t pcore)
  * WARNING: You must hold the proc_lock before calling this! */
 void __proc_give_cores(struct proc *SAFE p, uint32_t *pcorelist, size_t num)
 {
+       /* should never happen: */
+       assert(num + p->procinfo->num_vcores <= MAX_NUM_CPUS);
        switch (p->state) {
                case (PROC_RUNNABLE_S):
                case (PROC_RUNNING_S):
@@ -1206,42 +1210,28 @@ void __proc_give_cores(struct proc *SAFE p, uint32_t *pcorelist, size_t num)
        p->resources[RES_CORES].amt_granted += num;
 }
 
-/* Makes process p's coremap look like pcorelist (add, remove, etc).  Caller
- * needs to know what cores are free after this call (removed, failed, etc).
- * This info will be returned via corelist and *num.  This will send message to
- * any cores that are getting removed.
- *
- * Before implementing this, we should probably think about when this will be
- * used.  Implies preempting for the message.  The more that I think about this,
- * the less I like it.  For now, don't use this, and think hard before
- * implementing it.
- *
- * WARNING: You must hold the proc_lock before calling this! */
-void __proc_set_allcores(struct proc *SAFE p, uint32_t *pcorelist,
-                         size_t *num, amr_t message,TV(a0t) arg0,
-                         TV(a1t) arg1, TV(a2t) arg2)
-{
-       panic("Set all cores not implemented.\n");
-}
-
 /* Helper for the take_cores calls: takes a specific vcore from p, optionally
  * sending the message (or just unmapping), gives the pcore to the idlecoremap.
  */
 static void __proc_take_a_core(struct proc *p, struct vcore *vc, amr_t message,
                                long arg0, long arg1, long arg2)
 {
+       uint32_t vcoreid = vcore2vcoreid(p, vc);
+       struct preempt_data *vcpd = &p->procdata->vcore_preempt_data[vcoreid];
        /* Change lists for the vcore.  We do this before either unmapping or
         * sending the message, so the lists represent what will be very soon
         * (before we unlock, the messages are in flight). */
        TAILQ_REMOVE(&p->online_vcs, vc, list);
        TAILQ_INSERT_HEAD(&p->inactive_vcs, vc, list);
        if (message) {
+               /* lock the vcore's state.  This is okay even if we kill the proc */
+               atomic_or(&vcpd->flags, VC_K_LOCK);
                send_kernel_message(vc->pcoreid, message, arg0, arg1, arg2,
                                    KMSG_IMMEDIATE);
        } else {
                /* if there was a msg, the vcore is unmapped on the receive side.
                 * o/w, we need to do it here. */
-               __unmap_vcore(p, vcore2vcoreid(p, vc));
+               __unmap_vcore(p, vcoreid);
        }
        /* give the pcore back to the idlecoremap */
        put_idle_core(vc->pcoreid);
@@ -1462,34 +1452,30 @@ static void __set_curtf_to_vcoreid(struct proc *p, uint32_t vcoreid)
         * vcore to its pcore, though we don't always have current loaded or
         * otherwise mess with the VCPD in those code paths. */
        vcpd->can_rcv_msg = TRUE;
+       /* Mark that this vcore as no longer preempted.  No danger of clobbering
+        * other writes, since this would get turned on in __preempt (which can't be
+        * concurrent with this function on this core), and the atomic is just
+        * toggling the one bit (a concurrent VC_K_LOCK will work) */
+       atomic_and(&vcpd->flags, ~VC_PREEMPTED);
        printd("[kernel] startcore on physical core %d for process %d's vcore %d\n",
               core_id(), p->pid, vcoreid);
-       if (seq_is_locked(vcpd->preempt_tf_valid)) {
-               __seq_end_write(&vcpd->preempt_tf_valid); /* mark tf as invalid */
+       /* If notifs are disabled, the vcore was in vcore context and we need to
+        * restart the preempt_tf.  o/w, we give them a fresh vcore (which is also
+        * what happens the first time a vcore comes online).  No matter what,
+        * they'll restart in vcore context.  It's just a matter of whether or not
+        * it is the old, interrupted vcore context. */
+       if (vcpd->notif_disabled) {
                restore_fp_state(&vcpd->preempt_anc);
-               /* notif_pending and enabled means the proc wants to receive the IPI,
-                * but might have missed it.  copy over the tf so they can restart it
-                * later, and give them a fresh vcore. */
-               if (vcpd->notif_pending && vcpd->notif_enabled) {
-                       vcpd->notif_tf = vcpd->preempt_tf; // could memset
-                       proc_init_trapframe(&pcpui->actual_tf, vcoreid, p->env_entry,
-                                           vcpd->transition_stack);
-                       if (!vcpd->transition_stack)
-                               warn("No transition stack!");
-                       vcpd->notif_enabled = FALSE;
-                       vcpd->notif_pending = FALSE;
-               } else {
-                       /* copy-in the tf we'll pop, then set all security-related fields */
-                       pcpui->actual_tf = vcpd->preempt_tf;
-                       proc_secure_trapframe(&pcpui->actual_tf);
-               }
+               /* copy-in the tf we'll pop, then set all security-related fields */
+               pcpui->actual_tf = vcpd->preempt_tf;
+               proc_secure_trapframe(&pcpui->actual_tf);
        } else { /* not restarting from a preemption, use a fresh vcore */
                assert(vcpd->transition_stack);
                /* TODO: consider 0'ing the FP state.  We're probably leaking. */
                proc_init_trapframe(&pcpui->actual_tf, vcoreid, p->env_entry,
                                    vcpd->transition_stack);
                /* Disable/mask active notifications for fresh vcores */
-               vcpd->notif_enabled = FALSE;
+               vcpd->notif_disabled = TRUE;
        }
        /* cur_tf was built above (in actual_tf), now use it */
        pcpui->cur_tf = &pcpui->actual_tf;
@@ -1535,6 +1521,11 @@ void proc_change_to_vcore(struct proc *p, uint32_t new_vcoreid,
        caller_vcoreid = get_vcoreid(p, pcoreid);
        caller_vcpd = &p->procdata->vcore_preempt_data[caller_vcoreid];
        caller_vc = vcoreid2vcore(p, caller_vcoreid);
+       /* Should only call from vcore context */
+       if (!caller_vcpd->notif_disabled) {
+               printk("[kernel] You tried to change vcores from uthread ctx\n");
+               goto out_failed;
+       }
        /* Return and take the preempt message when we enable_irqs. */
        if (caller_vc->preempt_served)
                goto out_failed;
@@ -1546,13 +1537,14 @@ void proc_change_to_vcore(struct proc *p, uint32_t new_vcoreid,
        if (enable_my_notif) {
                /* if they set this flag, then the vcore can just restart from scratch,
                 * and we don't care about either the notif_tf or the preempt_tf. */
-               caller_vcpd->notif_enabled = TRUE;
+               caller_vcpd->notif_disabled = FALSE;
        } else {
                /* need to set up the calling vcore's tf so that it'll get restarted by
                 * __startcore, to make the caller look like it was preempted. */
                caller_vcpd->preempt_tf = *current_tf;
                save_fp_state(&caller_vcpd->preempt_anc);
-               caller_vcpd->preempt_tf_valid = TRUE;
+               /* Mark our core as preempted (for userspace recovery). */
+               atomic_or(&caller_vcpd->flags, VC_PREEMPTED);
        }
        /* Either way, unmap and offline our current vcore */
        /* Move the caller from online to inactive */
@@ -1569,9 +1561,10 @@ void proc_change_to_vcore(struct proc *p, uint32_t new_vcoreid,
        __unmap_vcore(p, caller_vcoreid);
        __map_vcore(p, new_vcoreid, pcoreid);
        __seq_end_write(&p->procinfo->coremap_seqctr);
-       /* send preempt message about the calling vcore.  might as well prefer
-        * directing it to the new_vc (vcoreid) to cut down on an IPI. */
-       preempt_msg.ev_type = EV_VCORE_PREEMPT;
+       /* Send either a PREEMPT msg or a CHECK_MSGS msg.  If they said to
+        * enable_my_notif, then all userspace needs is to check messages, not a
+        * full preemption recovery. */
+       preempt_msg.ev_type = (enable_my_notif ? EV_CHECK_MSGS : EV_VCORE_PREEMPT);
        preempt_msg.ev_arg2 = caller_vcoreid;   /* arg2 is 32 bits */
        send_kernel_event(p, &preempt_msg, new_vcoreid);
        /* Change cur_tf so we'll be the new vcoreid */
@@ -1639,9 +1632,10 @@ void __notify(struct trapframe *tf, uint32_t srcid, long a0, long a1, long a2)
        printd("received active notification for proc %d's vcore %d on pcore %d\n",
               p->procinfo->pid, vcoreid, coreid);
        /* sort signals.  notifs are now masked, like an interrupt gate */
-       if (!vcpd->notif_enabled)
+       if (vcpd->notif_disabled)
                return;
-       vcpd->notif_enabled = FALSE;
+       vcpd->notif_disabled = TRUE;
+       /* This bit shouldn't be important anymore */
        vcpd->notif_pending = FALSE; // no longer pending - it made it here
        /* save the old tf in the notify slot, build and pop a new one.  Note that
         * silly state isn't our business for a notification. */
@@ -1678,14 +1672,20 @@ void __preempt(struct trapframe *tf, uint32_t srcid, long a0, long a1, long a2)
        vcpd = &p->procdata->vcore_preempt_data[vcoreid];
        printd("[kernel] received __preempt for proc %d's vcore %d on pcore %d\n",
               p->procinfo->pid, vcoreid, coreid);
-       /* save the process's tf (current_tf) in the preempt slot, save the silly
-        * state, and signal the state is a valid tf.  when it is 'written,' it is
-        * valid.  Using the seq_ctrs so userspace can tell between different valid
-        * versions.  If the TF was already valid, it will panic (if CONFIGed that
-        * way). */
-       vcpd->preempt_tf = *pcpui->cur_tf;
+       /* if notifs are disabled, the vcore is in vcore context (as far as we're
+        * concerned), and we save it in the preempt slot. o/w, we save the
+        * process's cur_tf in the notif slot, and it'll appear to the vcore when it
+        * comes back up that it just took a notification. */
+       if (vcpd->notif_disabled)
+               vcpd->preempt_tf = *pcpui->cur_tf;
+       else
+               vcpd->notif_tf = *pcpui->cur_tf;
+       /* either way, we save the silly state (FP) */
        save_fp_state(&vcpd->preempt_anc);
-       __seq_start_write(&vcpd->preempt_tf_valid);
+       /* Mark the vcore as preempted and unlock (was locked by the sender). */
+       atomic_or(&vcpd->flags, VC_PREEMPTED);
+       atomic_and(&vcpd->flags, ~VC_K_LOCK);
+       wmb();  /* make sure everything else hits before we unmap */
        __unmap_vcore(p, vcoreid);
        /* We won't restart the process later.  current gets cleared later when we
         * notice there is no owning_proc and we have nothing to do (smp_idle,