Resource request calls and core request handling
authorBarret Rhoden <brho@cs.berkeley.edu>
Tue, 22 Sep 2009 21:45:41 +0000 (14:45 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Mon, 19 Oct 2009 00:20:02 +0000 (17:20 -0700)
This establishes an interface to request resources from the kernel.
Currently uses a syscall per resource request, which avoids some
complexities.  Processes can use this interface to issue requests for
cores.

This also adds internal functions for the giving and taking of cores.
Currently, this family doesn't change state, requiring the processes to
be RUNNING_M or RUNNABLE_M.  See the comments for details (in
proc_give_cores()).

18 files changed:
kern/arch/i386/process.c
kern/arch/i386/trap.c
kern/include/env.h
kern/include/process.h
kern/include/resource.h [new file with mode: 0644]
kern/include/ros/resource.h [new file with mode: 0644]
kern/include/ros/syscall.h
kern/src/Makefrag
kern/src/env.c
kern/src/manager.c
kern/src/mm.c
kern/src/monitor.c
kern/src/process.c
kern/src/resource.c [new file with mode: 0644]
kern/src/syscall.c
user/apps/roslib/mhello.c
user/roslib/inc/lib.h
user/roslib/src/syscall.c

index ca63f82..e571457 100644 (file)
@@ -35,3 +35,11 @@ void proc_set_tfcoreid(trapframe_t *tf, uint32_t id)
 {
        tf->tf_regs.reg_eax = id;
 }
+
+/* For cases that we won't return from a syscall via the normal path, and need
+ * to set the syscall return value in the registers manually.  Like in a syscall
+ * moving to RUNNING_M */
+void proc_set_syscall_retval(trapframe_t *SAFE tf, intreg_t value)
+{
+       tf->tf_regs.reg_eax = value;
+}
index b8b51e1..001c23a 100644 (file)
@@ -227,6 +227,13 @@ trap(trapframe_t *tf)
                panic("Trapframe with invalid CS!");
        }
 
+       /* If we're PROC_RUNNING_S, save the trapframe (which is vcore0's) in the
+        * proc's env_tf.  make sure silly state is sorted (HSS).  Consider
+        * extending this for PROC_RUNNING_M and vcore0. */
+       if (current->state == PROC_RUNNING_S) {
+               current->env_tf = *tf;
+               tf = &current->env_tf;
+       }
        // Dispatch based on what type of trap occurred
        trap_dispatch(tf);
 
@@ -349,6 +356,13 @@ void sysenter_init(void)
 /* This is called from sysenter's asm, with the tf on the kernel stack. */
 void sysenter_callwrapper(struct Trapframe *tf)
 {
+       /* If we're PROC_RUNNING_S, save the trapframe (which is vcore0's) in the
+        * proc's env_tf.  make sure silly state is sorted (HSS).  Consider
+        * extending this for PROC_RUNNING_M and vcore0. */
+       if (current->state == PROC_RUNNING_S) {
+               current->env_tf = *tf;
+               tf = &current->env_tf;
+       }
        // Note we pass the tf ptr along, in case syscall needs to block
        tf->tf_regs.reg_eax = (intreg_t) syscall(current, tf,
                                                 tf->tf_regs.reg_eax,
@@ -370,6 +384,7 @@ uint32_t send_active_message(uint32_t dst, amr_t pc,
                              TV(a0t) arg0, TV(a1t) arg1, TV(a2t) arg2)
 {
        error_t retval = -EBUSY;
+       assert(pc);
        spin_lock_irqsave(&per_cpu_info[dst].amsg_lock);
        size_t current_amsg = per_cpu_info[dst].amsg_current;
        // If there's a PC there, then that means it's an outstanding message
index 10abc2a..996bf03 100644 (file)
@@ -9,6 +9,7 @@
 #include <ros/sysevent.h>
 #include <ros/error.h>
 #include <ros/procdata.h>
+#include <ros/resource.h>
 #include <arch/trap.h>
 #include <ros/common.h>
 #include <arch/arch.h>
@@ -55,10 +56,14 @@ struct Env {
        uint32_t env_flags;
        uint32_t env_entry;
        /* Virtual coremap: each index is the virtual core id, the contents at that
-        * index is the physical core_id() corresponding to the vcore. */
-       uint32_t vcoremap[MAX_NUM_CPUS];
+        * index is the physical core_id() corresponding to the vcore.  -1 means it
+        * is unused */
+       int32_t vcoremap[MAX_NUM_CPUS];
        uint32_t num_vcores;
 
+       /* Info about this process's resources (granted, desired) for each type. */
+       struct resource resources[MAX_NUM_RESOURCES];
+
        // Address space
        pde_t *COUNT(NPDENTRIES) env_pgdir;                     // Kernel virtual address of page dir
        physaddr_t env_cr3;                     // Physical address of page dir
index fe3550e..2fe0e95 100644 (file)
 #include <trap.h>
 #include <atomic.h>
 
-/* Process States.  Not 100% on the names yet. */
+/* Process States.  Not 100% on the names yet.  RUNNABLE_* are waiting to go to
+ * RUNNING_*.  For instance, RUNNABLE_M is expecting to go to RUNNING_M.  It
+ * could be waiting for it's timeslice, or possibly for all the cores it asked
+ * for.  You use proc_run() to transition between these states.
+ *
+ * Difference between the _M and the _S states:
+ * - _S : legacy process mode
+ * - RUNNING_M implies *guaranteed* core(s).  You can be a single core in the
+ *   RUNNING_M state.  The guarantee is subject to time slicing, but when you
+ *   run, you get all of your cores.
+ * - The time slicing is at a coarser granularity for _M states.  This means
+ *   that when you run an _S on a core, it should be interrupted/time sliced
+ *   more often, which also means the core should be classified differently for
+ *   a while.  Possibly even using it's local APIC timer.
+ * - A process in an _M state will be informed about changes to its state, e.g.,
+ *   will have a handler run in the event of a page fault
+ */
+
 #define PROC_CREATED                   0x01
 #define PROC_RUNNABLE_S                        0x02
 #define PROC_RUNNING_S                 0x04
-#define PROC_WAITING                   0x08  // can split out to INT and UINT
+#define PROC_WAITING                   0x08 // can split out to INT and UINT
 #define PROC_DYING                             0x10
-#define PROC_RUNNABLE_M                        0x20 // ready, needs all of its resources (cores)
-#define PROC_RUNNING_M                 0x40 // running, manycore style
+#define PROC_RUNNABLE_M                        0x20
+#define PROC_RUNNING_M                 0x40
 // TODO don't use this shit for process allocation flagging
 #define ENV_FREE                               0x80
 
@@ -38,23 +55,44 @@ extern struct proc_list LCKD(&freelist_lock)proc_freelist;
 extern spinlock_t runnablelist_lock;
 extern struct proc_list LCKD(&runnablelist_lock) proc_runnablelist;
 
-
-extern spinlock_t idle_lock;
+/* Idle cores: ones able to be exclusively given to a process (worker cores). */
+extern spinlock_t idle_lock;  // never grab this before a proc_lock
 extern uint32_t LCKD(&idle_lock) (RO idlecoremap)[MAX_NUM_CPUS];
 extern uint32_t LCKD(&idle_lock) num_idlecores;
 
+/* Process management: */
 int proc_set_state(struct proc *p, uint32_t state) WRITES(p->state);
 struct proc *get_proc(unsigned pid);
 bool proc_controls(struct proc *SAFE actor, struct proc *SAFE target);
+/* Transition from RUNNABLE_* to RUNNING_*. */
 void proc_run(struct proc *SAFE p);
-// TODO: why do we need these parentheses?
-void (proc_startcore)(struct proc *SAFE p, trapframe_t *SAFE tf)
+void proc_startcore(struct proc *SAFE p, trapframe_t *SAFE tf)
      __attribute__((noreturn));
-void (proc_destroy)(struct proc *SAFE p);
 void proc_destroy(struct proc *SAFE p);
+
+/* Process core management.  Only call these if you are RUNNING_M or RUNNABLE_M.
+ * These all adjust the vcoremap and take appropriate actions (like __startcore
+ * if you were already RUNNING_M.  You could be RUNNABLE_M with no vcores when
+ * these are done (basically preempted, and waiting to get run again).
+ * All of these could modify corelist and *num to communicate info back out,
+ * which would be the list of cores that are known to be free.
+ *
+ * WARNING: YOU MUST HOLD THE PROC_LOCK BEFORE CALLING THESE! */
+/* Gives process p the additional num cores listed in corelist */
+error_t proc_give_cores(struct proc *SAFE p, uint32_t corelist[], size_t *num);
+/* Makes process p's coremap look like corelist (add, remove, etc) */
+error_t proc_set_allcores(struct proc *SAFE p, uint32_t corelist[], size_t *num,
+                          amr_t message);
+/* Takes from process p the num cores listed in corelist */
+error_t proc_take_cores(struct proc *SAFE p, uint32_t corelist[], size_t *num,
+                        amr_t message);
+error_t proc_take_allcores(struct proc *SAFE p, amr_t message);
+
+/* Arch Specific */
 void proc_init_trapframe(trapframe_t *SAFE tf);
 void proc_set_program_counter(trapframe_t *SAFE tf, uintptr_t pc);
 void proc_set_tfcoreid(trapframe_t *SAFE tf, uint32_t id);
+void proc_set_syscall_retval(trapframe_t *SAFE tf, intreg_t value);
 
 /* The reference counts are mostly to track how many cores loaded the cr3 */
 error_t proc_incref(struct proc *SAFE p);
diff --git a/kern/include/resource.h b/kern/include/resource.h
new file mode 100644 (file)
index 0000000..d8c7f09
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2009 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ *
+ * Kernel resource management.
+ */
+
+#ifndef ROS_KERN_RESOURCE_H
+#define ROS_KERN_RESOURCE_H
+
+#include <ros/resource.h>
+#include <ros/error.h>
+#include <ros/common.h>
+#include <arch/trap.h>
+#include <process.h>
+
+error_t resource_req(struct proc *p, int type, size_t amount, uint32_t flags);
+
+void print_resources(struct proc *p);
+void print_all_resources(void);
+
+#endif // !ROS_KERN_RESOURCE_H
diff --git a/kern/include/ros/resource.h b/kern/include/ros/resource.h
new file mode 100644 (file)
index 0000000..9f9b880
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2009 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ *
+ * Interface for asking for resources from the kernel.
+ */
+
+#ifndef ROS_INCLUDE_RESOURCE_H
+#define ROS_INCLUDE_RESOURCE_H
+
+#include <ros/common.h>
+
+/* A request means to set the amt_wanted to X.  Any changes result in prodding
+ * the scheduler / whatever.
+ *
+ * To make these requests, userspace uses SYS_resource_req, which currently is a
+ * syscall to make one request.
+ *
+ * Another way would be to take a ptr to a resource req and length, to batch
+ * requests together.  Individual syscalls are simpler than the batch.  For
+ * example,  servicing the core request doesn't easily return (which could lead
+ * to other requests getting ignored, or us having to worry about the
+ * order of processing).  Dealing with more than one request per type could be a
+ * pain too.  The batch one is nice, since it amortizes the overhead of the syscall,
+ * but it doesn't really matter that much, esp when there are only a few resources.
+ *
+ * A few caveats for cores:
+ * - when someone yields (esp if the wish > grant): yielding means take one
+ *   away, and set wished = current.  don't yield if you want another core still
+ * - if someone requests less cores than they currently have active, we'll set
+ *   their wish to their active and return an error code (no core allocation
+ *   changes either).
+ */
+
+/* Types of resource requests */
+#define RES_CORES                       0
+#define RES_MEMORY                      1
+#define RES_APPLE_PIES          2
+#define MAX_NUM_RESOURCES    3
+
+/* Flags */
+#define REQ_ASYNC                      0x01 // Sync by default (?)
+#define REQ_SOFT                       0x02 // just making something up
+
+struct resource {
+       int type;
+       size_t amt_wanted;
+       size_t amt_granted;
+       uint32_t flags;
+};
+
+#endif // !ROS_INCLUDE_RESOURCE_H
index bbcb352..d20a195 100644 (file)
 #define SYS_mlock
 #define SYS_msync
 */
-#define SYS_serial_write                       16
-#define SYS_serial_read                                17
+#define SYS_resource_req                       16
+/* Read and write buffers over the serial port */
+#define SYS_serial_write                       17
+#define SYS_serial_read                                18
 /* The next 3 syscalls go with the experimental network driver. These syscalls
  * are used by newlib_backend / our remote binary loader to pull data from /
  * put data into a buffer managed by the network driver.  These should go away
  * as things mature. */
-#define SYS_eth_read                           18
-#define SYS_eth_write                          19
-#define SYS_run_binary                         20
+#define SYS_eth_read                           19
+#define SYS_eth_write                          20
+#define SYS_run_binary                         21
 // forward a syscall to front-end machine
-#define SYS_frontend                           21
+#define SYS_frontend                           22
 // Keep this in sync with the last syscall number
-#define NSYSCALLS                                      21
+#define NSYSCALLS                                      22
 // syscall number starts at 1 and goes up to NSYSCALLS, without holes.
 #define INVALID_SYSCALL(syscallno) ((syscallno) > NSYSCALLS)
 
index ba6d4c4..531c032 100644 (file)
@@ -41,6 +41,7 @@ KERN_SRCFILES := $(KERN_ARCH_SRCFILES) \
                  $(KERN_SRC_DIR)/hashtable_itr.c \
                  $(KERN_SRC_DIR)/schedule.c \
                  $(KERN_SRC_DIR)/mm.c \
+                 $(KERN_SRC_DIR)/resource.c \
                  $(KERN_SRC_DIR)/testing.c
 
 # Only build files if they exist.
index 8b79f47..ec270a6 100644 (file)
@@ -280,7 +280,9 @@ env_alloc(env_t **newenv_store, envid_t parent_id)
        e->env_flags = 0;
        e->env_entry = 0; // cheating.  this really gets set in load_icode
        e->num_vcores = 0;
-       memset(&e->vcoremap, 0, sizeof(e->vcoremap));
+       for (int i = 0; i < MAX_NUM_CPUS; i++)
+               e->vcoremap[i] = -1;
+       memset(&e->resources, 0, sizeof(e->resources));
 
        memset(&e->env_ancillary_state, 0, sizeof(e->env_ancillary_state));
        memset(&e->env_tf, 0, sizeof(e->env_tf));
@@ -468,7 +470,9 @@ env_free(env_t *e)
 
        // return the environment to the free list
        e->state = ENV_FREE;
+       spin_lock(&freelist_lock);
        TAILQ_INSERT_HEAD(&proc_freelist, e, proc_link);
+       spin_unlock(&freelist_lock);
 }
 
 
index 0317ce3..79122d0 100644 (file)
@@ -32,7 +32,10 @@ void manager(void)
        static uint8_t RACY progress = 0;
 
        struct proc *envs[256];
-       struct proc *p ;
+       static struct proc *p ;
+
+       uint32_t corelist[MAX_NUM_CPUS];
+       uint32_t num = 3;
 
        // This is a bypass of the standard manager structure, for network use
        // If enabled, this spawns parlib_matrix, and allows the execution
@@ -51,12 +54,13 @@ void manager(void)
                        // Here's how to do a multicored/parallel process:
                        p = kfs_proc_create(kfs_lookup_path("roslib_mhello"));
                        // being proper and all:
+                       spin_lock_irqsave(&p->proc_lock);
                        proc_set_state(p, PROC_RUNNABLE_S);
+                       /* // uncomment this to transition to a parallel process manually
                        proc_set_state(p, PROC_RUNNING_S);
                        proc_set_state(p, PROC_RUNNABLE_M);
        
                        // set vcoremap with dispatch plan.  usually done by schedule()
-                       spin_lock_irqsave(&p->proc_lock);
                        p->num_vcores = 5; // assuming 5 are free, this is just an example
                        spin_lock(&idle_lock); // need to grab the cores
                        for (int i = 0; i < 5; i++) {
@@ -65,16 +69,26 @@ void manager(void)
                                num_idlecores--;
                        }
                        spin_unlock(&idle_lock);
+                       */
                        spin_unlock_irqsave(&p->proc_lock);
                        proc_run(p);
+                       break;
+               case 1:
+                       //panic("This is okay");
+                       udelay(10000000);
+                       printk("taking 3 cores from p\n");
+                       for (int i = 0; i < num; i++)
+                               corelist[i] = 7-i; // 7, 6, and 5
+                       spin_lock_irqsave(&p->proc_lock);
+                       proc_take_cores(p, corelist, &num, __death);
+                       spin_unlock_irqsave(&p->proc_lock);
                        udelay(5000000);
-       //              printk("Killing p\n");
-       //              proc_destroy(p);
-       //              printk("Killed p\n");
+                       printk("Killing p\n");
+                       proc_destroy(p);
+                       printk("Killed p\n");
                        udelay(1000000);
                        panic("This is okay");
-                       break;
-               case 1:
+
                        envs[0] = kfs_proc_create(kfs_lookup_path("roslib_hello"));
                        proc_set_state(envs[0], PROC_RUNNABLE_S);
                        proc_run(envs[0]);
index ce96409..ff3c9f2 100644 (file)
@@ -44,6 +44,7 @@ void *mmap(struct proc *p, uintptr_t addr, size_t len, int prot, int flags,
        spin_lock_irqsave(&p->proc_lock);
        // make sure all pages are available, and in a reasonable range
        // TODO: can probably do this better with vm_regions.
+       // can also consider not mapping to 0x00000000
        for (int i = 0; i < num_pages; i++) {
                a_pte = pgdir_walk(p->env_pgdir, (void*SNT)addr, 0);
                if (a_pte && *a_pte & PTE_P)
index cb33405..9d7541c 100644 (file)
@@ -21,6 +21,7 @@
 #include <kfs.h>
 #include <manager.h>
 #include <schedule.h>
+#include <resource.h>
 
 #include <ros/memlayout.h>
 
@@ -273,12 +274,15 @@ int mon_procinfo(int argc, char *NTS *NT COUNT(argc) argv, trapframe_t *tf)
                printk("Usage: procinfo OPTION\n");
                printk("\tidle_cores: show idle core map\n");
                printk("\trunnable: show proc_runnablelist\n");
+               printk("\tresources: show resources wanted/granted for all procs\n");
                return 1;
        }
        if (!strcmp(argv[1], "idle_cores"))
                print_idlecoremap();
        else if (!strcmp(argv[1], "runnable"))
                dump_proclist(&proc_runnablelist);
+       else if (!strcmp(argv[1], "resources"))
+               print_all_resources();
        else
                printk("Bad option\n");
        return 0;
index 9533a98..c753679 100644 (file)
@@ -13,6 +13,7 @@
 #include <atomic.h>
 #include <smp.h>
 #include <pmap.h>
+#include <trap.h>
 #include <schedule.h>
 #include <manager.h>
 #include <stdio.h>
@@ -110,11 +111,16 @@ bool proc_controls(struct proc *actor, struct proc *target)
        return target->env_parent_id == actor->env_id;
 }
 
-/*
- * Dispatches a process to run, either on the current core in the case of a
- * RUNNABLE_S, or on its partition in the case of a RUNNABLE_M.
- * This should never be called to "restart" a core.
- */
+/* Dispatches a process to run, either on the current core in the case of a
+ * RUNNABLE_S, or on its partition in the case of a RUNNABLE_M.  This should
+ * never be called to "restart" a core.  This expects that the "instructions"
+ * for which core(s) to run this on will be in the vcoremap, which needs to be
+ * set externally.
+ *
+ * When a process goes from RUNNABLE_M to RUNNING_M, its vcoremap will be
+ * "packed" (no holes in the vcore->pcore mapping), vcore0 will continue to run
+ * it's old core0 context, and the other cores will come in at the entry point.
+ * Including in the case of preemption.  */
 void proc_run(struct proc *p)
 {
        spin_lock_irqsave(&p->proc_lock);
@@ -157,7 +163,7 @@ void proc_run(struct proc *p)
 #endif
                                /* handle the others.  note the sync message will spin until
                                 * there is a free active message slot, which could lock up the
-                                * system.  think about this. (TODO) */
+                                * system.  think about this. (TODO)(AMDL) */
                                for (int i = 1; i < p->num_vcores; i++)
 #ifdef __IVY__
                                        send_active_msg_sync(p->vcoremap[i], __startcore,
@@ -284,12 +290,18 @@ void proc_destroy(struct proc *p)
 {
        // Note this code relies on this lock disabling interrupts, similar to
        // proc_run.
+       uint32_t corelist[MAX_NUM_CPUS];
+       size_t num_cores_freed;
        spin_lock_irqsave(&p->proc_lock);
        switch (p->state) {
                case PROC_DYING:
                        return; // someone else killed this already.
-               case PROC_RUNNABLE_S:
                case PROC_RUNNABLE_M:
+                       /* Need to reclaim any cores this proc might have, even though it's
+                        * not running yet. */
+                       proc_take_allcores(p, 0);
+                       // fallthrough
+               case PROC_RUNNABLE_S:
                        // Think about other lists, like WAITING, or better ways to do this
                        deschedule_proc(p);
                        break;
@@ -317,16 +329,11 @@ void proc_destroy(struct proc *p)
                         * deallocate the cores.
                         * The rule is that the vcoremap is set before proc_run, and reset
                         * within proc_destroy */
-                       spin_lock(&idle_lock);
-                       for (int i = 0; i < p->num_vcores; i++) {
+
                                send_active_msg_sync(p->vcoremap[i], __death, (void *SNT)0,
                                                     (void *SNT)0, (void *SNT)0);
-                               // give the pcore back to the idlecoremap
-                               assert(num_idlecores < num_cpus); // sanity
-                               idlecoremap[num_idlecores++] = p->vcoremap[i];
-                               p->vcoremap[i] = 0; // TODO: might need a better signal
-                       }
-                       spin_unlock(&idle_lock);
+
+                       proc_take_allcores(p, __death);
                        break;
                default:
                        // TODO: getting here if it's already dead and free (ENV_FREE).
@@ -357,12 +364,226 @@ void proc_destroy(struct proc *p)
        /* If we were running the process, we should have received an IPI by now.
         * If not, odds are interrupts are disabled, which shouldn't happen while
         * servicing syscalls. */
-       // TODO: this trips if we die in a trap that kills a process, like a page
-       // fault
-       assert(current != p);
+       if (current == p) {
+               /* a death IPI should be on its way, either from the RUNNING_S one, or
+                * from proc_take_cores with a __death.  in general, interrupts should
+                * be on when you call proc_destroy locally, but currently aren't for
+                * all things (like traphandlers).  since we're dying anyway, it seems
+                * reasonable to turn on interrupts.  note this means all non-proc
+                * management interrupt handlers must return (which they need to do
+                * anyway), so that we get back to this point.  Eventually, we can
+                * remove the enable_irq.  think about this (TODO) */
+               enable_irq();
+               udelay(1000000);
+               panic("Waiting too long for an IPI in proc_destroy()!");
+       }
        return;
 }
 
+/* Helper function.  Starting from prev, it will find the next free vcoreid,
+ * which is the next slot with a -1 in it.
+ * You better hold the lock before calling this. */
+static uint32_t get_free_vcoreid(struct proc *SAFE p, uint32_t prev)
+{
+       uint32_t i;
+       for (i = prev; i < MAX_NUM_CPUS; i++)
+               if (p->vcoremap[i] == -1)
+                       break;
+       if (i + 1 >= MAX_NUM_CPUS)
+               warn("At the end of the vcorelist.  Might want to check that out.");
+       return i;
+}
+
+/* Helper function.  Starting from prev, it will find the next busy vcoreid,
+ * which is the next slot with something other than a -1 in it.
+ * You better hold the lock before calling this. */
+static uint32_t get_busy_vcoreid(struct proc *SAFE p, uint32_t prev)
+{
+       uint32_t i;
+       for (i = prev; i < MAX_NUM_CPUS; i++)
+               if (p->vcoremap[i] != -1)
+                       break;
+       if (i + 1 >= MAX_NUM_CPUS)
+               warn("At the end of the vcorelist.  Might want to check that out.");
+       return i;
+}
+
+/* Helper function.  Find the vcoreid for a given physical core id.
+ * You better hold the lock before calling this. */
+static uint32_t get_vcoreid(struct proc *SAFE p, int32_t pcoreid)
+{
+       uint32_t i;
+       for (i = 0; i < MAX_NUM_CPUS; i++)
+               if (p->vcoremap[i] == pcoreid)
+                       break;
+       if (i + 1 >= MAX_NUM_CPUS)
+               warn("At the end of the vcorelist.  Might want to check that out.");
+       return i;
+}
+
+/* Gives process p the additional num cores listed in corelist.  You must be
+ * RUNNABLE_M or RUNNING_M before calling this.  If you're RUNNING_M, this will
+ * startup your new cores at the entry point with their virtual IDs.  If you're
+ * RUNNABLE_M, you should call proc_run after this so that the process can start
+ * to use its cores.
+ *
+ * If you're *_S, make sure your core0's TF is set (which is done when coming in
+ * via arch/trap.c and we are RUNNING_S), change your state, then call this.
+ * Then call proc_run().
+ *
+ * The reason I didn't bring the _S cases from core_request over here is so we
+ * can keep this family of calls dealing with only *_Ms, to avoiding caring if
+ * this is called from another core, and to avoid the need_to_idle business.
+ * The other way would be to have this function have the side effect of changing
+ * state, and finding another way to do the need_to_idle.
+ *
+ * In the event of an error, corelist will include all the cores that were *NOT*
+ * given to the process (cores that are still free).  Practically, this will be
+ * all of them, since it seems like an all or nothing deal right now.
+ *
+ * WARNING: You must hold the proc_lock before calling this!*/
+error_t proc_give_cores(struct proc *SAFE p, uint32_t corelist[], size_t *num)
+{
+       uint32_t free_vcoreid = 0;
+       switch (p->state) {
+               case (PROC_RUNNABLE_S):
+               case (PROC_RUNNING_S):
+                       panic("Don't give cores to a process in a *_S state!\n");
+                       return -EINVAL;
+                       break;
+               case (PROC_DYING):
+                       // just FYI, for debugging
+                       printk("[kernel] attempted to give cores to a DYING process.\n");
+                       return -EFAIL;
+                       break;
+               case (PROC_RUNNABLE_M):
+                       // set up vcoremap.  list should be empty, but could be called
+                       // multiple times before proc_running (someone changed their mind?)
+                       if (p->num_vcores) {
+                               printk("[kernel] Yaaaaaarrrrr!  Giving extra cores, are we?\n");
+                               // debugging: if we aren't packed, then there's a problem
+                               // somewhere, like someone forgot to take vcores after
+                               // preempting.
+                               for (int i = 0; i < p->num_vcores; i++)
+                                       assert(p->vcoremap[i]);
+                       }
+                       // add new items to the vcoremap
+                       for (int i = 0; i < *num; i++) {
+                               // find the next free slot, which should be the next one
+                               free_vcoreid = get_free_vcoreid(p, free_vcoreid);
+                               printd("setting vcore %d to pcore %d\n", free_vcoreid, corelist[i]);
+                               p->vcoremap[free_vcoreid] = corelist[i];
+                               p->num_vcores++;
+                       }
+                       break;
+               case (PROC_RUNNING_M):
+                       for (int i = 0; i < *num; i++) {
+                               free_vcoreid = get_free_vcoreid(p, free_vcoreid);
+                               printd("setting vcore %d to pcore %d\n", free_vcoreid, corelist[i]);
+                               p->vcoremap[free_vcoreid] = corelist[i];
+                               p->num_vcores++;
+                               // TODO: careful of active message deadlock (AMDL)
+                               assert(corelist[i] != core_id()); // sanity
+                               send_active_msg_sync(corelist[i], __startcore, (uint32_t)p,
+                                                    0, free_vcoreid);
+                       }
+                       break;
+               default:
+                       panic("Weird proc state %d in proc_give_cores()!\n", p->state);
+       }
+       return ESUCCESS;
+}
+
+/* Makes process p's coremap look like corelist (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.
+ *
+ * WARNING: You must hold the proc_lock before calling this!*/
+error_t proc_set_allcores(struct proc *SAFE p, uint32_t corelist[], size_t *num,
+                          amr_t message)
+{
+       panic("Set all cores not implemented.\n");
+}
+
+/* Takes from process p the num cores listed in corelist.  In the event of an
+ * error, corelist will contain the list of cores that are free, and num will
+ * contain how many items are in corelist.  This isn't implemented yet, but
+ * might be necessary later.  Or not, and we'll never do it.
+ *
+ * TODO: think about taking vcore0.  probably are issues...
+ *
+ * WARNING: You must hold the proc_lock before calling this!*/
+error_t proc_take_cores(struct proc *SAFE p, uint32_t corelist[], size_t *num,
+                        amr_t message)
+{
+       uint32_t vcoreid;
+       switch (p->state) {
+               case (PROC_RUNNABLE_M):
+                       assert(!message);
+                       break;
+               case (PROC_RUNNING_M):
+                       assert(message);
+                       break;
+               default:
+                       panic("Weird state %d in proc_take_cores()!\n", p->state);
+       }
+       spin_lock(&idle_lock);
+       assert((*num <= p->num_vcores) && (num_idlecores + *num <= num_cpus));
+       for (int i = 0; i < *num; i++) {
+               vcoreid = get_vcoreid(p, corelist[i]);
+               assert(p->vcoremap[vcoreid] == corelist[i]);
+               if (message)
+                       // TODO: careful of active message deadlock (AMDL)
+                       send_active_msg_sync(corelist[i], message, 0, 0, 0);
+               // give the pcore back to the idlecoremap
+               idlecoremap[num_idlecores++] = corelist[i];
+               p->vcoremap[vcoreid] = -1;
+       }
+       spin_unlock(&idle_lock);
+       p->num_vcores -= *num;
+       return 0;
+}
+
+/* Takes all cores from a process, which must be in an _M state.  Cores are
+ * placed back in the idlecoremap.  If there's a message, such as __death or
+ * __preempt, it will be sent to the cores.
+ *
+ * WARNING: You must hold the proc_lock before calling this! */
+error_t proc_take_allcores(struct proc *SAFE p, amr_t message)
+{
+       uint32_t active_vcoreid = 0;
+       switch (p->state) {
+               case (PROC_RUNNABLE_M):
+                       assert(!message);
+                       break;
+               case (PROC_RUNNING_M):
+                       assert(message);
+                       break;
+               default:
+                       panic("Weird state %d in proc_take_allcores()!\n", p->state);
+       }
+       spin_lock(&idle_lock);
+       assert(num_idlecores + p->num_vcores <= num_cpus); // sanity
+       for (int i = 0; i < p->num_vcores; i++) {
+               // find next active vcore
+               active_vcoreid = get_busy_vcoreid(p, active_vcoreid);
+               if (message)
+                       // TODO: careful of active message deadlock (AMDL)
+                       send_active_msg_sync(p->vcoremap[active_vcoreid], message,
+                                            (void *SNT)0, (void *SNT)0, (void *SNT)0);
+               // give the pcore back to the idlecoremap
+               idlecoremap[num_idlecores++] = p->vcoremap[active_vcoreid];
+               p->vcoremap[active_vcoreid] = -1;
+       }
+       spin_unlock(&idle_lock);
+       p->num_vcores = 0;
+       return 0;
+}
+
 /*
  * The process refcnt is the number of places the process 'exists' in the
  * system.  Creation counts as 1.  Having your page tables loaded somewhere
@@ -438,7 +659,7 @@ void __startcore(trapframe_t *tf, uint32_t srcid, void * a0, void * a1,
        trapframe_t local_tf;
        trapframe_t *tf_to_pop = (trapframe_t *CT(1))a1;
 
-       printk("Startcore on core %d\n", coreid);
+       printk("[kernel] Startcore on physical core %d\n", coreid);
        assert(p_to_run);
        // TODO: handle silly state (HSS)
        if (!tf_to_pop) {
@@ -475,6 +696,7 @@ void __death(trapframe_t *tf, uint32_t srcid, void *SNT a0, void *SNT a1,
 void print_idlecoremap(void)
 {
        spin_lock(&idle_lock);
+       printk("There are %d idle cores.\n", num_idlecores);
        for (int i = 0; i < num_idlecores; i++)
                printk("idlecoremap[%d] = %d\n", i, idlecoremap[i]);
        spin_unlock(&idle_lock);
diff --git a/kern/src/resource.c b/kern/src/resource.c
new file mode 100644 (file)
index 0000000..3571841
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2009 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ *
+ * Kernel resource management.
+ */
+
+#include <resource.h>
+#include <process.h>
+#include <stdio.h>
+#include <assert.h>
+
+/* 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). 
+ *
+ * It doesn't take the amount requested directly to avoid a race (or holding the
+ * proc_lock across the call), and allowing it to be called in other situations,
+ * such as if there was not a new request, but it's time to look at the
+ * difference between amt_wanted and amt_granted (maybe on a timer interrupt).
+ *
+ * Will return either the number actually granted or an error code.
+ */
+static ssize_t core_request(struct proc *p)
+{
+       size_t num_granted;
+       ssize_t amt_new;
+       uint32_t corelist[MAX_NUM_CPUS];
+       bool need_to_idle = FALSE;
+
+       spin_lock_irqsave(&p->proc_lock);
+       amt_new = p->resources[RES_CORES].amt_wanted -
+                 p->resources[RES_CORES].amt_granted;
+       if (amt_new < 0) {
+               p->resources[RES_CORES].amt_wanted = p->resources[RES_CORES].amt_granted;
+               spin_unlock_irqsave(&p->proc_lock);
+               return -EINVAL;
+       } else if (amt_new == 0) {
+               spin_unlock_irqsave(&p->proc_lock);
+               return 0;
+       }
+       // else, we try to handle the request
+
+       /* TODO: someone needs to decide if the process gets the resources.
+        * we just check to see if they are available and give them out.  This
+        * should call out to the scheduler or some other *smart* function.  You
+        * could also imagine just putting it on the scheduler's queue and letting
+        * that do the core request */
+       spin_lock(&idle_lock);
+       if (num_idlecores >= amt_new) {
+               for (int i = 0; i < amt_new; i++) {
+                       // grab the last one on the list
+                       corelist[i] = idlecoremap[num_idlecores-1];
+                       num_idlecores--;
+               }
+               num_granted = amt_new;
+       } else {
+               num_granted = 0;
+       }
+       spin_unlock(&idle_lock);
+       // Now, actually give them out
+       if (num_granted) {
+               p->resources[RES_CORES].amt_granted += num_granted;
+               switch (p->state) {
+                       case (PROC_RUNNING_S):
+                               // issue with if we're async or not (need to preempt it)
+                               // either of these should trip it.
+                               if ((current != p) || (p->vcoremap[0] != core_id()))
+                                       panic("We don't handle async RUNNING_S core requests yet.");
+                               /* in the async case, we'll need to bundle vcore0's TF.  this is
+                                * already done for the sync case (local syscall). */
+                               /* this process no longer runs on its old location (which is
+                                * this core, for now, since we don't handle async calls) */
+                               p->vcoremap[0] = -1;
+                               // will need to give up this core / idle later (sync)
+                               need_to_idle = TRUE;
+                               // change to runnable_m (it's TF is already saved)
+                               proc_set_state(p, PROC_RUNNABLE_M);
+                               break;
+                       case (PROC_RUNNABLE_S):
+                               /* Issues: being on the runnable_list, proc_set_state not liking
+                                * it, and not clearly thinking through how this would happen.
+                                * Perhaps an async call that gets serviced after you're
+                                * descheduled? */
+                               panic("Not supporting RUNNABLE_S -> RUNNABLE_M yet.\n");
+                               break;
+                       default:
+                               break;
+               }
+               /* give them the cores.  this will start up the extras if RUNNING_M */
+               proc_give_cores(p, corelist, &num_granted);
+               spin_unlock_irqsave(&p->proc_lock);
+               /* if there's a race on state (like DEATH), it'll get handled by
+                * proc_run or proc_destroy */
+               if (p->state == PROC_RUNNABLE_M)
+                       proc_run(p);
+               /* if we are moving to a partitionable core from a RUNNING_S on a
+                * management core, the kernel needs to do something else on this core
+                * (just like in proc_destroy).  __death cleans up the core and idles. */
+               if (need_to_idle)
+                       __death(0, 0, 0, 0, 0);
+       } else { // nothing granted, just return
+               spin_unlock_irqsave(&p->proc_lock);
+       }
+       return num_granted;
+}
+
+error_t resource_req(struct proc *p, int type, size_t amount, uint32_t flags)
+{
+       error_t retval;
+       printk("Received request for type: %d, amount: %d, flag: %d\n",
+              type, amount, flags);
+       if (flags & REQ_ASYNC)
+               // We have no sense of time yet, or of half-filling requests
+               printk("[kernel] Async requests treated synchronously for now.\n");
+
+       /* set the desired resource amount in the process's resource list. */
+       spin_lock_irqsave(&p->proc_lock);
+       size_t old_amount = p->resources[type].amt_wanted;
+       p->resources[type].amt_wanted = amount;
+       p->resources[type].flags = flags;
+       spin_unlock_irqsave(&p->proc_lock);
+
+       // no change in the amt_wanted
+       if (old_amount == amount)
+               return 0;
+
+       switch (type) {
+               case RES_CORES:
+                       retval = core_request(p);
+                       // i don't like this retval hackery
+                       if (retval < 0)
+                               return retval;
+                       else
+                               return 0;
+                       break;
+               case RES_MEMORY:
+                       // not clear if we should be in RUNNABLE_M or not
+                       printk("[kernel] Memory requests are not implemented.\n");
+                       return -EFAIL;
+                       break;
+               case RES_APPLE_PIES:
+                       printk("You can have all the apple pies you want.\n");
+                       break;
+               default:
+                       printk("[kernel] Unknown resource!  No oranges for you!\n");
+                       return -EINVAL;
+       }
+       return 0;
+}
+
+void print_resources(struct proc *p)
+{
+       printk("--------------------\n");
+       printk("PID: %d\n", p->env_id);
+       printk("--------------------\n");
+       for (int i = 0; i < MAX_NUM_RESOURCES; i++)
+               printk("Res type: %02d, amt wanted: %08d, amt granted: %08d\n", i,
+                      p->resources[i].amt_wanted, p->resources[i].amt_granted);
+}
+
+/* TODO: change this when we get rid of the env array */
+void print_all_resources(void)
+{
+       for (int i = 0; i < NENV; i++)
+               if (envs[i].state != ENV_FREE)
+                       print_resources(&envs[i]);
+}
index ebd80ec..566ad81 100644 (file)
@@ -22,6 +22,7 @@
 #include <syscall.h>
 #include <kmalloc.h>
 #include <stdio.h>
+#include <resource.h>
 #include <kfs.h> // eventually replace this with vfs.h
 
 #ifdef __NETWORK__
@@ -505,6 +506,13 @@ intreg_t syscall(struct proc *p, trapframe_t *tf, uintreg_t syscallno,
                case SYS_brk:
                        printk("brk not implemented yet\n");
                        return -EINVAL;
+               case SYS_resource_req:
+                       /* preemptively set the return code to 0.  if it's not, it will get
+                        * overwriten on a proper return path.  if it ends up being a core
+                        * request from a RUNNING_S, it will never return out this way
+                        */
+                       proc_set_syscall_retval(tf, ESUCCESS);
+                       return resource_req(p, a1, a2, a3);
 
        #ifdef __i386__
                case SYS_serial_write:
index 16e0e12..41ea04d 100644 (file)
@@ -1,5 +1,6 @@
 #include <lib.h>
 #include <ros/mman.h>
+#include <ros/resource.h>
 #include <stdio.h>
 
 // ghetto udelay, put in a lib somewhere and export the tsc freq
@@ -18,8 +19,6 @@ void udelay(uint64_t usec, uint64_t tsc_freq)
        return;
 }
 
-#include <arch/atomic.h>
-
 int main(int argc, char** argv)
 {
        uint32_t vcoreid;
@@ -29,6 +28,7 @@ int main(int argc, char** argv)
                cprintf("Hello from else vcore 0\n");
                void* addr;
                cprintf("Multi-Goodbye, world, from PID: %d!\n", sys_getpid());
+               /*
                addr = sys_mmap((void*SNT)USTACKTOP - 20*PGSIZE, 8*PGSIZE, 3, MAP_FIXED, 0, 0);
                cprintf("got addr = 0x%08x\n", addr);
                *(int*)addr = 0xdeadbeef;
@@ -37,13 +37,21 @@ int main(int argc, char** argv)
                cprintf("reading addr: 0x%08x\n", *(int*)addr);
                cprintf("reading addr+3pg: 0x%08x\n", *(int*)(addr + 3*PGSIZE));
                // this should fault
-               //*(int*)(addr - 3*PGSIZE) = 0xdeadbeef;
+               // *(int*)(addr - 3*PGSIZE) = 0xdeadbeef;
+               */
+               sys_resource_req(RES_CORES, 3, 0);
        }
+       // request a couple more (RUNNING_M when the request comes in)
+       if (vcoreid == 2)
+               sys_resource_req(RES_CORES, 5, 0);
+       /*
+       // heavy concurrent syscall tests
        for (int i = 0; i < 10; i++) {
-               for (int j = 0; j < 10; j++)
+               for (int j = 0; j < 100; j++)
                        sys_null();
                cprintf("Hello from vcore %d, iteration %d\n", vcoreid, i);
        }
+       */
        cprintf("Vcore %d Done!\n", vcoreid);
        while (1);
        udelay(5000000, 1995014570); // KVM's freq.  Whatever.
index b44cb70..6bf33ca 100644 (file)
@@ -66,9 +66,11 @@ void        sys_yield(void);
 int         sys_proc_create(char* path);
 error_t     sys_proc_run(int pid);
 /* Memory Management */
-void *COUNT(length)
-sys_mmap(void *SNT addr, size_t length, int prot, int flags, int fd,
-         size_t offset);
+void *COUNT(length) sys_mmap(void *SNT addr, size_t length, int prot, int flags,
+                             int fd, size_t offset);
+/* Resource Requests */
+ssize_t sys_resource_req(int type, size_t amount, uint32_t flags);
+
 /* Generic Async Call */
 error_t     waiton_syscall(syscall_desc_t* desc, syscall_rsp_t*COUNT(1) rsp);
 
index 077dd2e..f6076ae 100644 (file)
@@ -157,3 +157,9 @@ sys_mmap(void *SNT addr, size_t length, int prot, int flags, int fd,
        return (void*CT(length))TC(syscall(SYS_mmap, (uint32_t)addr, length, prot,
                              (int32_t)&extra_args, 0));
 }
+
+/* Request resources from the kernel.  Flags in ros/resource.h. */
+ssize_t sys_resource_req(int type, size_t amount, uint32_t flags)
+{
+       return syscall(SYS_resource_req, type, amount, flags, 0, 0);
+}