CPU state tracking
[akaros.git] / kern / src / schedule.c
index 3aa3218..bbda959 100644 (file)
@@ -97,14 +97,20 @@ static void set_ksched_alarm(void)
        set_alarm(&per_cpu_info[core_id()].tchain, &ksched_waiter);
 }
 
-/* Kmsg, to run the scheduler tick (not in interrupt context) and reset the
+/* Need a kmsg to just run the sched, but not to rearm */
+static void __just_sched(uint32_t srcid, long a0, long a1, long a2)
+{
+       run_scheduler();
+}
+
+/* RKM alarm, to run the scheduler tick (not in interrupt context) and reset the
  * alarm.  Note that interrupts will be disabled, but this is not the same as
  * interrupt context.  We're a routine kmsg, which means the core is in a
  * quiescent state. */
-static void __ksched_tick(uint32_t srcid, long a0, long a1, long a2)
+static void __ksched_tick(struct alarm_waiter *waiter)
 {
        /* TODO: imagine doing some accounting here */
-       schedule();
+       run_scheduler();
        /* Set our alarm to go off, incrementing from our last tick (instead of
         * setting it relative to now, since some time has passed since the alarm
         * first went off.  Note, this may be now or in the past! */
@@ -112,13 +118,6 @@ static void __ksched_tick(uint32_t srcid, long a0, long a1, long a2)
        set_alarm(&per_cpu_info[core_id()].tchain, &ksched_waiter);
 }
 
-/* Interrupt/alarm handler: tells our core to run the scheduler (out of
- * interrupt context). */
-static void __kalarm(struct alarm_waiter *waiter)
-{
-       send_kernel_message(core_id(), __ksched_tick, 0, 0, 0, KMSG_ROUTINE);
-}
-
 void schedule_init(void)
 {
        spin_lock(&sched_lock);
@@ -126,20 +125,20 @@ void schedule_init(void)
        all_pcores = kmalloc(sizeof(struct sched_pcore) * num_cpus, 0);
        memset(all_pcores, 0, sizeof(struct sched_pcore) * num_cpus);
        assert(!core_id());             /* want the alarm on core0 for now */
-       init_awaiter(&ksched_waiter, __kalarm);
+       init_awaiter(&ksched_waiter, __ksched_tick);
        set_ksched_alarm();
        /* init the idlecore list.  if they turned off hyperthreading, give them the
         * odds from 1..max-1.  otherwise, give them everything by 0 (default mgmt
         * core).  TODO: (CG/LL) better LL/CG mgmt */
-#ifndef __CONFIG_DISABLE_SMT__
+#ifndef CONFIG_DISABLE_SMT
        for (int i = 1; i < num_cpus; i++)
                TAILQ_INSERT_TAIL(&idlecores, pcoreid2spc(i), alloc_next);
 #else
        assert(!(num_cpus % 2));
        for (int i = 1; i < num_cpus; i += 2)
                TAILQ_INSERT_TAIL(&idlecores, pcoreid2spc(i), alloc_next);
-#endif /* __CONFIG_DISABLE_SMT__ */
-#ifdef __CONFIG_ARSC_SERVER__
+#endif /* CONFIG_DISABLE_SMT */
+#ifdef CONFIG_ARSC_SERVER
        struct sched_pcore *a_core = TAILQ_FIRST(&idlecores);
        assert(a_core);
        TAILQ_REMOVE(&idlecores, a_core, alloc_next);
@@ -147,7 +146,7 @@ void schedule_init(void)
                            KMSG_ROUTINE);
        warn("Using core %d for the ARSCs - there are probably issues with this.",
             spc2pcoreid(a_core));
-#endif /* __CONFIG_ARSC_SERVER__ */
+#endif /* CONFIG_ARSC_SERVER */
        spin_unlock(&sched_lock);
        return;
 }
@@ -287,10 +286,7 @@ void __sched_mcp_wakeup(struct proc *p)
                spin_unlock(&sched_lock);
                return;
        }
-       /* could try and prioritize p somehow (move it to the front of the list).
-        * for now, just help them out a bit (mild help here, can remove this) */
-       if (!p->procdata->res_req[RES_CORES].amt_wanted)
-               p->procdata->res_req[RES_CORES].amt_wanted = 1;
+       /* could try and prioritize p somehow (move it to the front of the list). */
        spin_unlock(&sched_lock);
        /* note they could be dying at this point too. */
        poke(&ksched_poker, p);
@@ -308,6 +304,23 @@ void __sched_scp_wakeup(struct proc *p)
        remove_from_any_list(p);
        add_to_list(p, &runnable_scps);
        spin_unlock(&sched_lock);
+       /* we could be on a CG core, and all the mgmt cores could be halted.  if we
+        * don't tell one of them about the new proc, they will sleep until the
+        * timer tick goes off. */
+       if (!management_core()) {
+               /* TODO: pick a better core and only send if halted.
+                *
+                * FYI, a POKE on x86 might lose a rare race with halt code, since the
+                * poke handler does not abort halts.  if this happens, the next timer
+                * IRQ would wake up the core.
+                *
+                * ideally, we'd know if a specific mgmt core is sleeping and wake it
+                * up.  o/w, we could interrupt an already-running mgmt core that won't
+                * get to our new proc anytime soon.  also, by poking core 0, a
+                * different mgmt core could remain idle (and this process would sleep)
+                * until its tick goes off */
+               send_ipi(0, I_POKE_CORE);
+       }
 }
 
 /* Callback to return a core to the ksched, which tracks it as idle and
@@ -366,13 +379,25 @@ static bool __schedule_scp(void)
                disable_irqsave(&state);
                /* someone is currently running, dequeue them */
                if (pcpui->owning_proc) {
+                       spin_lock(&pcpui->owning_proc->proc_lock);
+                       /* process might be dying, with a KMSG to clean it up waiting on
+                        * this core.  can't do much, so we'll attempt to restart */
+                       if (pcpui->owning_proc->state == PROC_DYING) {
+                               send_kernel_message(core_id(), __just_sched, 0, 0, 0,
+                                                   KMSG_ROUTINE);
+                               spin_unlock(&pcpui->owning_proc->proc_lock);
+                               enable_irqsave(&state);
+                               return FALSE;
+                       }
                        printd("Descheduled %d in favor of %d\n", pcpui->owning_proc->pid,
                               p->pid);
-                       /* locking just to be safe */
-                       spin_lock(&p->proc_lock);
                        __proc_set_state(pcpui->owning_proc, PROC_RUNNABLE_S);
+                       /* Saving FP state aggressively.  Odds are, the SCP was hit by an
+                        * IRQ and has a HW ctx, in which case we must save. */
+                       __proc_save_fpu_s(pcpui->owning_proc);
                        __proc_save_context_s(pcpui->owning_proc, pcpui->cur_ctx);
-                       spin_unlock(&p->proc_lock);
+                       vcore_account_offline(pcpui->owning_proc, 0); /* VC# */
+                       spin_unlock(&pcpui->owning_proc->proc_lock);
                        /* round-robin the SCPs (inserts at the end of the queue) */
                        switch_lists(pcpui->owning_proc, &unrunnable_scps, &runnable_scps);
                        clear_owning_proc(pcoreid);
@@ -402,6 +427,18 @@ static uint32_t get_cores_needed(struct proc *p)
        /* Help them out - if they ask for something impossible, give them 1 so they
         * can make some progress. (this is racy, and unnecessary). */
        if (amt_wanted > p->procinfo->max_vcores) {
+               printk("[kernel] proc %d wanted more than max, wanted %d\n", p->pid,
+                      amt_wanted);
+               p->procdata->res_req[RES_CORES].amt_wanted = 1;
+               amt_wanted = 1;
+       }
+       /* There are a few cases where amt_wanted is 0, but they are still RUNNABLE
+        * (involving yields, events, and preemptions).  In these cases, give them
+        * at least 1, so they can make progress and yield properly.  If they are
+        * not WAITING, they did not yield and may have missed a message. */
+       if (!amt_wanted) {
+               /* could ++, but there could be a race and we don't want to give them
+                * more than they ever asked for (in case they haven't prepped) */
                p->procdata->res_req[RES_CORES].amt_wanted = 1;
                amt_wanted = 1;
        }
@@ -484,8 +521,11 @@ static void __run_mcp_ksched(void *arg)
 /* Something has changed, and for whatever reason the scheduler should
  * reevaluate things. 
  *
+ * Don't call this if you are processing a syscall or otherwise care about your
+ * kthread variables, cur_proc/owning_proc, etc.
+ *
  * Don't call this from interrupt context (grabs proclocks). */
-void schedule(void)
+void run_scheduler(void)
 {
        /* MCP scheduling: post work, then poke.  for now, i just want the func to
         * run again, so merely a poke is sufficient. */
@@ -500,7 +540,7 @@ void schedule(void)
 /* A process is asking the ksched to look at its resource desires.  The
  * scheduler is free to ignore this, for its own reasons, so long as it
  * eventually gets around to looking at resource desires. */
-void poke_ksched(struct proc *p, int res_type)
+void poke_ksched(struct proc *p, unsigned int res_type)
 {
        /* ignoring res_type for now.  could post that if we wanted (would need some
         * other structs/flags) */
@@ -544,11 +584,11 @@ void avail_res_changed(int res_type, long change)
 uint32_t max_vcores(struct proc *p)
 {
 /* TODO: (CG/LL) */
-#ifdef __CONFIG_DISABLE_SMT__
+#ifdef CONFIG_DISABLE_SMT
        return num_cpus >> 1;
 #else
        return num_cpus - 1;    /* reserving core 0 */
-#endif /* __CONFIG_DISABLE_SMT__ */
+#endif /* CONFIG_DISABLE_SMT */
 }
 
 /* This deals with a request for more cores.  The amt of new cores needed is
@@ -822,7 +862,7 @@ void print_idlecoremap(void)
        /* not locking, so we can look at this without deadlocking. */
        printk("Idle cores (unlocked!):\n");
        TAILQ_FOREACH(spc_i, &idlecores, alloc_next)
-               printk("Core %d, prov to %d (%08p)\n", spc2pcoreid(spc_i),
+               printk("Core %d, prov to %d (%p)\n", spc2pcoreid(spc_i),
                       spc_i->prov_proc ? spc_i->prov_proc->pid : 0, spc_i->prov_proc);
 }
 
@@ -855,7 +895,7 @@ void print_prov_map(void)
        printk("Which cores are provisioned to which procs:\n------------------\n");
        for (int i = 0; i < num_cpus; i++) {
                spc_i = pcoreid2spc(i);
-               printk("Core %02d, prov: %d(%08p) alloc: %d(%08p)\n", i,
+               printk("Core %02d, prov: %d(%p) alloc: %d(%p)\n", i,
                       spc_i->prov_proc ? spc_i->prov_proc->pid : 0, spc_i->prov_proc,
                       spc_i->alloc_proc ? spc_i->alloc_proc->pid : 0,
                       spc_i->alloc_proc);
@@ -867,12 +907,12 @@ void print_proc_prov(struct proc *p)
        struct sched_pcore *spc_i;
        if (!p)
                return;
-       printk("Prov cores alloced to proc %d (%08p)\n----------\n", p->pid, p);
+       printk("Prov cores alloced to proc %d (%p)\n----------\n", p->pid, p);
        TAILQ_FOREACH(spc_i, &p->ksched_data.prov_alloc_me, prov_next)
                printk("Pcore %d\n", spc2pcoreid(spc_i));
-       printk("Prov cores not alloced to proc %d (%08p)\n----------\n", p->pid, p);
+       printk("Prov cores not alloced to proc %d (%p)\n----------\n", p->pid, p);
        TAILQ_FOREACH(spc_i, &p->ksched_data.prov_not_alloc_me, prov_next)
-               printk("Pcore %d (alloced to %d (%08p))\n", spc2pcoreid(spc_i),
+               printk("Pcore %d (alloced to %d (%p))\n", spc2pcoreid(spc_i),
                       spc_i->alloc_proc ? spc_i->alloc_proc->pid : 0,
                       spc_i->alloc_proc);
 }