Allow ev_qs to not have INDIR throttling (XCC)
[akaros.git] / kern / src / process.c
index 751f935..448e3d2 100644 (file)
@@ -111,7 +111,9 @@ int __proc_set_state(struct proc *p, uint32_t state)
         * RBS -> RGS
         * RGS -> RBS
         * RGS -> W
+        * RGM -> W
         * W   -> RBS
+        * W   -> RBM
         * RGS -> RBM
         * RBM -> RGM
         * RGM -> RBM
@@ -139,7 +141,7 @@ int __proc_set_state(struct proc *p, uint32_t state)
                                panic("Invalid State Transition! PROC_RUNNING_S to %02x", state);
                        break;
                case PROC_WAITING:
-                       if (state != PROC_RUNNABLE_S)
+                       if (!(state & (PROC_RUNNABLE_S | PROC_RUNNABLE_M)))
                                panic("Invalid State Transition! PROC_WAITING to %02x", state);
                        break;
                case PROC_DYING:
@@ -151,7 +153,8 @@ int __proc_set_state(struct proc *p, uint32_t state)
                                panic("Invalid State Transition! PROC_RUNNABLE_M to %02x", state);
                        break;
                case PROC_RUNNING_M:
-                       if (!(state & (PROC_RUNNABLE_S | PROC_RUNNABLE_M | PROC_DYING)))
+                       if (!(state & (PROC_RUNNABLE_S | PROC_RUNNABLE_M | PROC_WAITING |
+                                      PROC_DYING)))
                                panic("Invalid State Transition! PROC_RUNNING_M to %02x", state);
                        break;
        }
@@ -306,12 +309,14 @@ 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 */
        memset(&p->resources, 0, sizeof(p->resources));
        memset(&p->env_ancillary_state, 0, sizeof(p->env_ancillary_state));
        memset(&p->env_tf, 0, sizeof(p->env_tf));
+       spinlock_init(&p->mm_lock);
        TAILQ_INIT(&p->vm_regions); /* could init this in the slab */
        /* Initialize the vcore lists, we'll build the inactive list so that it includes
         * all vcores when we initialize procinfo.  Do this before initing procinfo. */
@@ -781,6 +786,7 @@ void proc_yield(struct proc *SAFE p, bool being_nice)
 {
        uint32_t vcoreid = get_vcoreid(p, core_id());
        struct vcore *vc = vcoreid2vcore(p, vcoreid);
+       struct preempt_data *vcpd = &p->procdata->vcore_preempt_data[vcoreid];
 
        /* no reason to be nice, return */
        if (being_nice && !vc->preempt_pending)
@@ -798,7 +804,17 @@ void proc_yield(struct proc *SAFE p, bool being_nice)
        /* no need to preempt later, since we are yielding (nice or otherwise) */
        if (vc->preempt_pending)
                vc->preempt_pending = 0;
-
+       /* Don't let them yield if they are missing a notification.  Userspace must
+        * not leave vcore context without dealing with notif_pending.  pop_ros_tf()
+        * handles leaving via uthread context.  This handles leaving via a yield.
+        *
+        * This early check is an optimization.  The real check is below when it
+        * works with the online_vcs list (syncing with event.c and INDIR/IPI
+        * posting). */
+       if (vcpd->notif_pending) {
+               spin_unlock(&p->proc_lock);
+               return;
+       }
        switch (p->state) {
                case (PROC_RUNNING_S):
                        __proc_yield_s(p, current_tf);  /* current_tf 0'd in abandon core */
@@ -812,11 +828,23 @@ void proc_yield(struct proc *SAFE p, bool being_nice)
                                spin_unlock(&p->proc_lock);
                                return;
                        }
-                       __seq_start_write(&p->procinfo->coremap_seqctr);
                        /* Remove from the online list, add to the yielded list, and unmap
                         * the vcore, which gives up the core. */
                        TAILQ_REMOVE(&p->online_vcs, vc, list);
+                       /* Now that we're off the online list, check to see if an alert made
+                        * it through (event.c sets this) */
+                       cmb();
+                       if (vcpd->notif_pending) {
+                               /* We lost, put it back on the list and abort the yield */
+                               TAILQ_INSERT_TAIL(&p->online_vcs, vc, list); /* could go HEAD */
+                               spin_unlock(&p->proc_lock);
+                               return;
+                       }
+                       /* We won the race with event sending, we can safely yield */
                        TAILQ_INSERT_HEAD(&p->inactive_vcs, vc, list);
+                       /* Note this protects stuff userspace should look at, which doesn't
+                        * include the TAILQs. */
+                       __seq_start_write(&p->procinfo->coremap_seqctr);
                        __unmap_vcore(p, vcoreid);
                        /* Adjust implied resource desires */
                        p->resources[RES_CORES].amt_granted = --(p->procinfo->num_vcores);
@@ -884,6 +912,27 @@ void proc_notify(struct proc *p, uint32_t vcoreid)
        }
 }
 
+/* Hold the lock before calling this.  If the process is WAITING, it will wake
+ * it up and schedule it. */
+void __proc_wakeup(struct proc *p)
+{
+       if (p->state != PROC_WAITING)
+               return;
+       if (__proc_is_mcp(p))
+               __proc_set_state(p, PROC_RUNNABLE_M);
+       else
+               __proc_set_state(p, PROC_RUNNABLE_S);
+       schedule_proc(p);
+}
+
+/* Is the process in multi_mode / is an MCP or not?  */
+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;
+}
+
 /************************  Preemption Functions  ******************************
  * Don't rely on these much - I'll be sure to change them up a bit.
  *
@@ -1371,15 +1420,16 @@ void switch_back(struct proc *new_p, struct proc *old_proc)
  * shootdown and batching our messages.  Should do the sanity about rounding up
  * and down in this function too.
  *
- * Hold the proc_lock before calling this.
- *
  * Would be nice to have a broadcast kmsg at this point.  Note this may send a
  * message to the calling core (interrupting it, possibly while holding the
  * proc_lock).  We don't need to process routine messages since it's an
  * immediate message. */
-void __proc_tlbshootdown(struct proc *p, uintptr_t start, uintptr_t end)
+void proc_tlbshootdown(struct proc *p, uintptr_t start, uintptr_t end)
 {
        struct vcore *vc_i;
+       /* TODO: we might be able to avoid locking here in the future (we must hit
+        * all online, and we can check __mapped).  it'll be complicated. */
+       spin_lock(&p->proc_lock);
        switch (p->state) {
                case (PROC_RUNNING_S):
                        tlbflush();
@@ -1400,6 +1450,7 @@ void __proc_tlbshootdown(struct proc *p, uintptr_t start, uintptr_t end)
                        warn("Unexpected case %s in %s", procstate2str(p->state),
                             __FUNCTION__);
        }
+       spin_unlock(&p->proc_lock);
 }
 
 /* Kernel message handler to start a process's context on this core.  Tightly
@@ -1446,6 +1497,7 @@ void __startcore(struct trapframe *tf, uint32_t srcid, long a0, long a1, long a2
                        proc_secure_trapframe(&local_tf);
                }
        } else { /* not restarting from a preemption, use a fresh vcore */
+               assert(vcpd->transition_stack);
                proc_init_trapframe(&local_tf, vcoreid, p_to_run->env_entry,
                                    vcpd->transition_stack);
                /* Disable/mask active notifications for fresh vcores */
@@ -1588,7 +1640,7 @@ void print_proc_info(pid_t pid)
        printk("struct proc: %p\n", p);
        printk("PID: %d\n", p->pid);
        printk("PPID: %d\n", p->ppid);
-       printk("State: 0x%08x\n", p->state);
+       printk("State: 0x%08x (%s)\n", p->state, p->is_mcp ? "M" : "S");
        printk("Refcnt: %d\n", atomic_read(&p->p_kref.refcount) - 1);
        printk("Flags: 0x%08x\n", p->env_flags);
        printk("CR3(phys): 0x%08x\n", p->env_cr3);