Ksched interface cleanup, smp_idle() cleanup
[akaros.git] / kern / src / schedule.c
index 2b07de7..903fd77 100644 (file)
@@ -16,6 +16,7 @@
 #include <atomic.h>
 #include <smp.h>
 #include <manager.h>
+#include <alarm.h>
 #include <sys/queue.h>
 
 /* Process Lists */
@@ -37,12 +38,51 @@ uint32_t num_idlecores = 0;
 uint32_t num_mgmtcores = 1;
 
 /* Helper, defined below */
-static bool core_request(struct proc *p);
+static void __core_request(struct proc *p);
+
+/* Alarm struct, for our example 'timer tick' */
+struct alarm_waiter ksched_waiter;
+
+#define TIMER_TICK_USEC 10000  /* 10msec */
+
+/* Helper: Sets up a timer tick on the calling core to go off 10 msec from now.
+ * This assumes the calling core is an LL core, etc. */
+static void set_ksched_alarm(void)
+{
+       set_awaiter_rel(&ksched_waiter, TIMER_TICK_USEC);
+       set_alarm(&per_cpu_info[core_id()].tchain, &ksched_waiter);
+}
+
+/* Kmsg, 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(struct trapframe *tf, uint32_t srcid, long a0,
+                          long a1, long a2)
+{
+       /* TODO: imagine doing some accounting here */
+       schedule();
+       /* 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! */
+       set_awaiter_inc(&ksched_waiter, TIMER_TICK_USEC);
+       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)
 {
        TAILQ_INIT(&runnable_scps);
        TAILQ_INIT(&all_mcps);
+       assert(!core_id());             /* want the alarm on core0 for now */
+       init_awaiter(&ksched_waiter, __kalarm);
+       set_ksched_alarm();
 
        /* Ghetto old idle core init */
        /* Init idle cores. Core 0 is the management core. */
@@ -139,7 +179,7 @@ void schedule(void)
                 * TODO: this is RACY too - just like with DYING. */
                if (p->state == PROC_WAITING)
                        continue;
-               core_request(p);
+               __core_request(p);
        }
        /* prune any dying SCPs at the head of the queue and maybe sched our core */
        while ((p = TAILQ_FIRST(&runnable_scps))) {
@@ -166,25 +206,43 @@ void schedule(void)
 void poke_ksched(struct proc *p, int res_type)
 {
        /* TODO: probably want something to trigger all res_types */
-       /* Consider races with core_req called from other pokes or schedule */
+       spin_lock(&sched_lock);
        switch (res_type) {
                case RES_CORES:
                        /* ignore core requests from non-mcps (note we have races if we ever
                         * allow procs to switch back). */
                        if (!__proc_is_mcp(p))
                                break;
-                       /* TODO: issues with whether or not they are RUNNING.  Need to
-                        * change core_request / give_cores. */
-                       core_request(p);
+                       __core_request(p);
                        break;
                default:
                        break;
        }
+       spin_unlock(&sched_lock);
+}
+
+/* The calling cpu/core has nothing to do and plans to idle/halt.  This is an
+ * opportunity to pick the nature of that halting (low power state, etc), or
+ * provide some other work (_Ss on LL cores).  Note that interrupts are
+ * disabled, and if you return, the core will cpu_halt(). */
+void cpu_bored(void)
+{
+       if (!management_core())
+               return;
+       /* TODO: something smart.  For now, do what smp_idle did */
+       manager();
+       assert(0);
+       /* TODO run a process, and if none exist at all and we're core 0, bring up
+        * the monitor/manager */
 }
 
 /* Helper function to return a core to the idlemap.  It causes some more lock
  * acquisitions (like in a for loop), but it's a little easier.  Plus, one day
- * we might be able to do this without locks (for the putting). */
+ * we might be able to do this without locks (for the putting).
+ *
+ * TODO: this is a trigger, telling us we have more cores.  Could/should make a
+ * scheduling decision (or at least plan to).  Note that right now, the proc's
+ * lock is still being held, so we can't do anything in this context. */
 void put_idle_core(uint32_t coreid)
 {
        spin_lock(&idle_lock);
@@ -192,6 +250,24 @@ void put_idle_core(uint32_t coreid)
        spin_unlock(&idle_lock);
 }
 
+/* Bulk interface for put_idle */
+void put_idle_cores(uint32_t *pc_arr, uint32_t num)
+{
+       spin_lock(&idle_lock);
+       for (int i = 0; i < num; i++)
+               idlecoremap[num_idlecores++] = pc_arr[i];
+       spin_unlock(&idle_lock);
+}
+
+/* Available resources changed (plus or minus).  Some parts of the kernel may
+ * call this if a particular resource that is 'quantity-based' changes.  Things
+ * like available RAM to processes, bandwidth, etc.  Cores would probably be
+ * inappropriate, since we need to know which specific core is now free. */
+void avail_res_changed(int res_type, long change)
+{
+       printk("[kernel] ksched doesn't track any resources yet!\n");
+}
+
 /* Normally it'll be the max number of CG cores ever */
 uint32_t max_vcores(struct proc *p)
 {
@@ -202,25 +278,18 @@ uint32_t max_vcores(struct proc *p)
 #endif /* __CONFIG_DISABLE_SMT__ */
 }
 
-/* Ghetto helper, just hands out the next amt_new cores, or 0 if we can't do all
- * of them. */
+/* Ghetto helper, just hands out up to 'amt_new' cores (no sense of locality or
+ * anything) */
 static uint32_t get_idle_cores(struct proc *p, uint32_t *pc_arr,
                                uint32_t amt_new)
 {
-       uint32_t num_granted;
-       /* You should do something smarter than just giving the stuff out.  Like
-        * take in to account priorities, node locations, etc */
+       uint32_t num_granted = 0;
        spin_lock(&idle_lock);
-       if (num_idlecores >= amt_new) {
-               for (int i = 0; i < amt_new; i++) {
-                       // grab the last one on the list
-                       pc_arr[i] = idlecoremap[num_idlecores - 1];
-                       num_idlecores--;
-               }
-               num_granted = amt_new;
-       } else {
-               /* In this case, you might want to preempt or do other fun things... */
-               num_granted = 0;
+       for (int i = 0; i < num_idlecores && i < amt_new; i++) {
+               /* grab the last one on the list */
+               pc_arr[i] = idlecoremap[num_idlecores - 1];
+               num_idlecores--;
+               num_granted++;
        }
        spin_unlock(&idle_lock);
        return num_granted;
@@ -228,52 +297,33 @@ static uint32_t get_idle_cores(struct proc *p, uint32_t *pc_arr,
 
 /* This deals with a request for more cores.  The request is already stored in
  * the proc's amt_wanted (it is compared to amt_granted). */
-static bool core_request(struct proc *p)
+static void __core_request(struct proc *p)
 {
-       uint32_t num_granted, amt_new, amt_wanted, amt_granted;
-       uint32_t corelist[MAX_NUM_CPUS]; /* TODO UGH, this could be huge! */
+       uint32_t num_granted, amt_wanted, amt_granted;
+       uint32_t corelist[num_cpus];
 
-       /* Currently, this is all locked, and there's a variety of races involved,
-        * esp with moving amt_wanted to procdata (TODO).  Will probably want to
-        * copy-in amt_wanted too. */
-       spin_lock(&p->proc_lock);
+       /* TODO: consider copy-in for amt_wanted too. */
        amt_wanted = p->procdata->res_req[RES_CORES].amt_wanted;
        amt_granted = p->procinfo->res_grant[RES_CORES];
 
        /* Help them out - if they ask for something impossible, give them 1 so they
-        * can make some progress. (these two are racy). */
+        * can make some progress. (this is racy). */
        if (amt_wanted > p->procinfo->max_vcores) {
                p->procdata->res_req[RES_CORES].amt_wanted = 1;
        }
-       /* TODO: sort how this works with WAITING. */
-       if (!amt_wanted) {
-               p->procdata->res_req[RES_CORES].amt_wanted = 1;
-       }
        /* if they are satisfied, we're done.  There's a slight chance they have
         * cores, but they aren't running (sched gave them cores while they were
         * yielding, and now we see them on the run queue). */
-       if (amt_wanted <= amt_granted) {
-               if (amt_granted) {
-                       spin_unlock(&p->proc_lock);
-                       return TRUE;
-               } else {
-                       spin_unlock(&p->proc_lock);
-                       return FALSE;
-               }
-       }
-       /* otherwise, see what they want.  Current models are simple - it's just a
-        * raw number of cores, and we just give out what we can. */
-       amt_new = amt_wanted - amt_granted;
-       /* TODO: Could also consider amt_min */
-
-       /* TODO: change this.  this function is really "find me amt_new cores", the
-        * nature of this info depends on how we express desires, and a lot of that
-        * info could be lost through this interface. */
-       num_granted = get_idle_cores(p, corelist, amt_new);
-
+       if (amt_wanted <= amt_granted)
+               return;
+       /* Otherwise, see what they want, and try to give out as many as possible.
+        * Current models are simple - it's just a raw number of cores, and we just
+        * give out what we can. */
+       num_granted = get_idle_cores(p, corelist, amt_wanted - amt_granted);
        /* Now, actually give them out */
        if (num_granted) {
                /* give them the cores.  this will start up the extras if RUNNING_M. */
+               spin_lock(&p->proc_lock);
                __proc_give_cores(p, corelist, num_granted);
                /* at some point after giving cores, call proc_run_m() (harmless on
                 * RUNNING_Ms).  You can give small groups of cores, then run them
@@ -281,10 +331,7 @@ static bool core_request(struct proc *p)
                 * bulk preempted processes). */
                __proc_run_m(p); /* harmless to call this on RUNNING_Ms */
                spin_unlock(&p->proc_lock);
-               return TRUE;    /* proc can run (if it isn't already) */
        }
-       spin_unlock(&p->proc_lock);
-       return FALSE;           /* Not giving them anything more */
 }
 
 /************** Debugging **************/