Preemption functions
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 28 Apr 2010 02:40:30 +0000 (19:40 -0700)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 3 Nov 2011 00:35:45 +0000 (17:35 -0700)
Half-temporary functions to preempt or warn about preemption for a
process's core (or all cores).  There are also test apps to run while
this is going on (msr_nice_while will yield when warned).  There are
monitor functions added to measure to test these things.

The time to preempt is really low, enough so that I wonder if it's
working or not.

kern/include/process.h
kern/src/Makefrag
kern/src/init.c
kern/src/kfs.c
kern/src/manager.c
kern/src/monitor.c
kern/src/process.c
tests/msr_dumb_while.c [new file with mode: 0644]
tests/msr_nice_while.c [new file with mode: 0644]
tests/msr_single_while.c [new file with mode: 0644]

index 575791f..141ed95 100644 (file)
@@ -121,6 +121,14 @@ void __proc_kmsg_pending(struct proc *p, bool ipi_pending);
 void __map_vcore(struct proc *p, uint32_t vcoreid, uint32_t pcoreid);
 void __unmap_vcore(struct proc *p, uint32_t vcoreid);
 
+/* Preemption management.  Some of these will change */
+void __proc_preempt_warn(struct proc *p, uint32_t vcoreid, uint64_t when);
+void __proc_preempt_warnall(struct proc *p, uint64_t when);
+bool __proc_preempt_core(struct proc *p, uint32_t pcoreid);
+bool __proc_preempt_all(struct proc *p);
+void proc_preempt_core(struct proc *p, uint32_t pcoreid, uint64_t usec);
+void proc_preempt_all(struct proc *p, uint64_t usec);
+
 /* Will probably have generic versions of these later. */
 void proc_incref(struct proc *SAFE p, size_t count);
 void proc_decref(struct proc *SAFE p, size_t count);
index ddf751d..cd0fd72 100644 (file)
@@ -60,7 +60,10 @@ KERN_APPFILES += \
                  $(TESTS_DIR)/pthread_test \
                  $(TESTS_DIR)/idle \
                  $(TESTS_DIR)/fillmeup \
-                 $(TESTS_DIR)/msr_get_cores
+                 $(TESTS_DIR)/msr_get_cores \
+                 $(TESTS_DIR)/msr_dumb_while \
+                 $(TESTS_DIR)/msr_nice_while \
+                 $(TESTS_DIR)/msr_single_while
 endif
 
 KERN_LDFLAGS   := $(KERN_LDFLAGS) -L$(OBJDIR)/$(KERN_DIR) \
index 76dcbd1..f6d7e61 100644 (file)
@@ -8,6 +8,7 @@
 #error "Yeah, it's not possible to build ROS with BSD on Core 0, sorry......"
 #else
 
+#include <ros/timer.h>
 #include <arch/arch.h>
 #include <arch/console.h>
 #include <multiboot.h>
@@ -81,6 +82,7 @@ void kernel_init(multiboot_info_t *mboot_info)
        kernel_msg_init();
        sysenter_init();
        timer_init();
+       train_timing();
        
        // At this point our boot paths diverge based on arch. 
        arch_init();
index 8fbe66a..d67b39e 100644 (file)
@@ -48,6 +48,9 @@ DECL_PROG(pthread_test);
 DECL_PROG(idle);
 DECL_PROG(fillmeup);
 DECL_PROG(msr_get_cores);
+DECL_PROG(msr_dumb_while);
+DECL_PROG(msr_nice_while);
+DECL_PROG(msr_single_while);
 DECL_FILE(kfs_test_txt);
 DECL_FILE(hello_txt);
 #endif
@@ -63,6 +66,9 @@ struct kfs_entry kfs[MAX_KFS_FILES] = {
        KFS_PENTRY(idle)
        KFS_PENTRY(fillmeup)
        KFS_PENTRY(msr_get_cores)
+       KFS_PENTRY(msr_dumb_while)
+       KFS_PENTRY(msr_nice_while)
+       KFS_PENTRY(msr_single_while)
        KFS_FENTRY(kfs_test_txt)
        KFS_FENTRY(hello_txt)
 #endif
index 86614f5..5a30b8c 100644 (file)
@@ -66,7 +66,8 @@ void manager_brho(void)
                case 0:
                        // TODO: need to store the pid for future manager runs, not the *p
                        //p = kfs_proc_create(kfs_lookup_path("pthread_test"));
-                       p = kfs_proc_create(kfs_lookup_path("mhello"));
+                       //p = kfs_proc_create(kfs_lookup_path("mhello"));
+                       p = kfs_proc_create(kfs_lookup_path("msr_dumb_while"));
                        // being proper and all:
                        spin_lock(&p->proc_lock);
                        __proc_set_state(p, PROC_RUNNABLE_S);
index 70ede47..97e586e 100644 (file)
@@ -26,6 +26,7 @@
 #include <syscall.h>
 #include <kmalloc.h>
 
+#include <ros/timer.h>
 #include <ros/memlayout.h>
 
 #define CMDBUF_SIZE    80      // enough for one VGA text line
@@ -455,23 +456,149 @@ int mon_notify(int argc, char *NTS *NT COUNT(argc) argv, trapframe_t *tf)
 /* Micro-benchmarky Measurements */
 int mon_measure(int argc, char *NTS *NT COUNT(argc) argv, trapframe_t *tf)
 {
+       uint64_t begin = 0, diff = 0;
+       bool self_ipi_pending = FALSE;
+       uint32_t end_refcnt = 0;
+
        if (argc < 2) {
                printk("Usage: measure OPTION\n");
-               printk("\topt1: whatever\n");
+               printk("\tkill PID : kill proc PID\n");
+               printk("\tpreempt PID : preempt proc PID (no delay)\n");
+               printk("\tpreempt PID [pcore] : preempt PID's pcore (no delay)\n");
+               printk("\tpreempt-warn PID : warn-preempt proc PID (pending)\n");
+               printk("\tpreempt-warn PID [pcore] : warn-preempt proc PID's pcore\n");
+               printk("\tpreempt-raw PID : raw-preempt proc PID\n");
+               printk("\tpreempt-raw PID [pcore] : raw-preempt proc PID's pcore\n");
                return 1;
        }
-       if (!strcmp(argv[1], "opt1")) {
-               print_idlecoremap();
-       } else if (!strcmp(argv[1], "opt2")) {
-               if (argc != 3) {
-                       printk("ERRRRRRRRRR.\n");
+       if (!strcmp(argv[1], "kill")) {
+               if (argc < 3) {
+                       printk("Give me a pid number.\n");
                        return 1;
                }
-               print_proc_info(strtol(argv[2], 0, 0));
+               struct proc *p = pid2proc(strtol(argv[2], 0, 0));
+               if (!p) {
+                       printk("No such proc\n");
+                       return 1;
+               }
+               begin = start_timing();
+               proc_destroy(p);
+               proc_decref(p, 1);
+               /* this is a little ghetto. it's not fully free yet, but we are also
+                * slowing it down by messing with it, esp with the busy waiting on a
+                * hyperthreaded core. */
+               spin_on(p->env_cr3);
+               /* No noticeable difference using stop_timing instead of read_tsc() */
+               diff = stop_timing(begin);
+       } else if (!strcmp(argv[1], "preempt")) {
+               if (argc < 3) {
+                       printk("Give me a pid number.\n");
+                       return 1;
+               }
+               struct proc *p = pid2proc(strtol(argv[2], 0, 0));
+               if (!p) {
+                       printk("No such proc\n");
+                       return 1;
+               }
+               if (argc == 4) { /* single core being preempted, warned but no delay */
+                       uint32_t pcoreid = strtol(argv[3], 0, 0);
+                       begin = start_timing();
+                       proc_preempt_core(p, pcoreid, 1000000); // 1 sec warning
+                       /* done when unmapped (right before abandoning) */
+                       spin_on(p->procinfo->pcoremap[pcoreid].valid);
+                       diff = stop_timing(begin);
+               } else { /* preempt all cores, warned but no delay */
+                       end_refcnt = p->env_refcnt - p->procinfo->num_vcores;
+                       begin = start_timing();
+                       proc_preempt_all(p, 1000000);
+                       /* a little ghetto, implies no one is using p */
+                       spin_on(p->env_refcnt != end_refcnt);
+                       diff = stop_timing(begin);
+               }
+               proc_decref(p, 1);
+       } else if (!strcmp(argv[1], "preempt-warn")) {
+               if (argc < 3) {
+                       printk("Give me a pid number.\n");
+                       return 1;
+               }
+               struct proc *p = pid2proc(strtol(argv[2], 0, 0));
+               if (!p) {
+                       printk("No such proc\n");
+                       return 1;
+               }
+               if (argc == 4) { /* single core being preempted-warned */
+                       uint32_t pcoreid = strtol(argv[3], 0, 0);
+                       spin_lock(&p->proc_lock);
+                       uint32_t vcoreid = p->procinfo->pcoremap[pcoreid].vcoreid;
+                       if (!p->procinfo->pcoremap[pcoreid].valid) {
+                               printk("Pick a mapped pcore\n");
+                               spin_unlock(&p->proc_lock);
+                               return 1;
+                       }
+                       begin = start_timing();
+                       __proc_preempt_warn(p, vcoreid, 1000000); // 1 sec
+                       spin_unlock(&p->proc_lock);
+                       /* done when unmapped (right before abandoning) */
+                       spin_on(p->procinfo->pcoremap[pcoreid].valid);
+                       diff = stop_timing(begin);
+               } else { /* preempt-warn all cores */
+                       printk("Warning, this won't work if they can't yield their "
+                              "last vcore, will stop at 1!\n");
+                       spin_lock(&p->proc_lock);
+                       begin = start_timing();
+                       __proc_preempt_warnall(p, 1000000);
+                       spin_unlock(&p->proc_lock);
+                       /* target cores do the unmapping / changing of the num_vcores */
+                       spin_on(p->procinfo->num_vcores > 1);
+                       diff = stop_timing(begin);
+               }
+               proc_decref(p, 1);
+       } else if (!strcmp(argv[1], "preempt-raw")) {
+               if (argc < 3) {
+                       printk("Give me a pid number.\n");
+                       return 1;
+               }
+               struct proc *p = pid2proc(strtol(argv[2], 0, 0));
+               if (!p) {
+                       printk("No such proc\n");
+                       return 1;
+               }
+               if (argc == 4) { /* single core preempted, no warning or waiting */
+                       uint32_t pcoreid = strtol(argv[3], 0, 0);
+                       spin_lock(&p->proc_lock);
+                       if (!p->procinfo->pcoremap[pcoreid].valid) {
+                               printk("Pick a mapped pcore\n");
+                               spin_unlock(&p->proc_lock);
+                               return 1;
+                       }
+                       begin = start_timing();
+                       self_ipi_pending = __proc_preempt_core(p, pcoreid);
+                       spin_unlock(&p->proc_lock);
+                       __proc_kmsg_pending(p, self_ipi_pending);
+                       /* done when unmapped (right before abandoning) */
+                       spin_on(p->procinfo->pcoremap[pcoreid].valid);
+                       diff = stop_timing(begin);
+                       /* TODO: (RMS), if num_vcores == 0, RUNNABLE_M, schedule */
+               } else { /* preempt all cores, no warning or waiting */
+                       spin_lock(&p->proc_lock);
+                       end_refcnt = p->env_refcnt - p->procinfo->num_vcores;
+                       begin = start_timing();
+                       self_ipi_pending = __proc_preempt_all(p);
+                       /* TODO: (RMS), RUNNABLE_M, schedule */
+                       spin_unlock(&p->proc_lock);
+                       __proc_kmsg_pending(p, self_ipi_pending);
+                       /* a little ghetto, implies no one else is using p */
+                       spin_on(p->env_refcnt != end_refcnt);
+                       diff = stop_timing(begin);
+               }
+               proc_decref(p, 1);
        } else {
                printk("Bad option\n");
                return 1;
        }
+       printk("[Tired Giraffe Accent] Took %llu usec (%llu nsec) to finish.\n",
+              diff * 1000000 / system_timing.tsc_freq,
+              diff * 1000000000 / system_timing.tsc_freq);
        return 0;
 }
 
index 54a1d5d..fe301d7 100644 (file)
@@ -42,7 +42,7 @@ uint32_t num_mgmtcores = 1;
 /* 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). */
-static void put_idle_core(uint32_t coreid)
+void put_idle_core(uint32_t coreid)
 {
        spin_lock(&idle_lock);
 #ifdef __CONFIG_EXPER_TRADPROC__ /* often a good check, but hurts performance */
@@ -704,7 +704,7 @@ void proc_destroy(struct proc *p)
        if (current == p)
 #endif /* __CONFIG_EXPER_TRADPROC__ */
                self_ipi_pending = TRUE;
-       
+
        switch (p->state) {
                case PROC_DYING: // someone else killed this already.
                        spin_unlock(&p->proc_lock);
@@ -861,15 +861,15 @@ void proc_yield(struct proc *SAFE p, bool being_nice)
                        schedule_proc(p);
                        break;
                case (PROC_RUNNING_M):
-#ifdef __CONFIG_OSDI__
-                       /* Ghetto, for OSDI: */
                        printd("[K] Process %d (%p) is yielding on vcore %d\n", p->pid, p,
                               get_vcoreid(p, core_id()));
+                       /* TODO: (RMS) the Scheduler cannot handle the Runnable Ms (RMS), so
+                        * don't yield the last vcore.  It's ghetto and for OSDI, but it
+                        * needs to be fixed for all builds, not just CONFIG_OSDI. */
                        if (p->procinfo->num_vcores == 1) {
                                spin_unlock(&p->proc_lock);
                                return;
                        }
-#endif /* __CONFIG_OSDI__ */
                        __seq_start_write(&p->procinfo->coremap_seqctr);
                        // give up core
                        __unmap_vcore(p, get_vcoreid(p, core_id()));
@@ -880,6 +880,7 @@ void proc_yield(struct proc *SAFE p, bool being_nice)
                        // add to idle list
                        put_idle_core(core_id());
                        // last vcore?  then we really want 1, and to yield the gang
+                       // TODO: (RMS) will actually do this.
                        if (p->procinfo->num_vcores == 0) {
                                p->resources[RES_CORES].amt_wanted = 1;
                                __proc_set_state(p, PROC_RUNNABLE_M);
@@ -968,6 +969,152 @@ void proc_notify(struct proc *p, unsigned int notif, struct notif_event *ne)
        do_notify(p, nm->vcoreid, ne->ne_type, ne);
 }
 
+/************************  Preemption Functions  ******************************
+ * Don't rely on these much - I'll be sure to change them up a bit.
+ *
+ * Careful about what takes a vcoreid and what takes a pcoreid.  Also, there may
+ * be weird glitches with setting the state to RUNNABLE_M.  It is somewhat in
+ * flux.  The num_vcores is changed after take_cores, but some of the messages
+ * (or local traps) may not yet be ready to handle seeing their future state.
+ * But they should be, so fix those when they pop up.
+ *
+ * TODO: (RMS) we need to actually make the scheduler handle RUNNABLE_Ms and
+ * then schedule these, or change proc_destroy to not assume they need to be
+ * descheduled.
+ *
+ * Another thing to do would be to make the _core functions take a pcorelist,
+ * and not just one pcoreid. */
+
+/* Sets a preempt_pending warning for p's vcore, to go off 'when'.  If you care
+ * about locking, do it before calling.  Takes a vcoreid! */
+void __proc_preempt_warn(struct proc *p, uint32_t vcoreid, uint64_t when)
+{
+       /* danger with doing this unlocked: preempt_pending is set, but never 0'd,
+        * since it is unmapped and not dealt with (TODO)*/
+       p->procinfo->vcoremap[vcoreid].preempt_pending = when;
+       /* notify, if they want to hear about this event.  regardless of how they
+        * want it, we can send this as a bit.  Subject to change. */
+       if (p->procdata->notif_methods[NE_PREEMPT_PENDING].flags | NOTIF_WANTED)
+               do_notify(p, vcoreid, NE_PREEMPT_PENDING, 0);
+       /* TODO: consider putting in some lookup place for the alarm to find it.
+        * til then, it'll have to scan the vcoremap (O(n) instead of O(m)) */
+}
+
+/* Warns all active vcores of an impending preemption.  Hold the lock if you
+ * care about the mapping (and you should). */
+void __proc_preempt_warnall(struct proc *p, uint64_t when)
+{
+       uint32_t active_vcoreid = 0;
+       for (int i = 0; i < p->procinfo->num_vcores; i++) {
+               active_vcoreid = get_busy_vcoreid(p, active_vcoreid);
+               __proc_preempt_warn(p, active_vcoreid, when);
+               active_vcoreid++;
+       }
+       /* TODO: consider putting in some lookup place for the alarm to find it.
+        * til then, it'll have to scan the vcoremap (O(n) instead of O(m)) */
+}
+
+// TODO: function to set an alarm, if none is outstanding
+
+/* Raw function to preempt a single core.  Returns TRUE if the calling core will
+ * get a kmsg.  If you care about locking, do it before calling. */
+bool __proc_preempt_core(struct proc *p, uint32_t pcoreid)
+{
+       uint32_t vcoreid = get_vcoreid(p, pcoreid);
+
+       p->procinfo->vcoremap[vcoreid].preempt_served = TRUE;
+       // expects a pcorelist.  assumes pcore is mapped and running_m
+       return __proc_take_cores(p, &pcoreid, 1, __preempt, p, 0, 0);
+}
+
+/* Raw function to preempt every vcore.  Returns TRUE if the calling core will
+ * get a kmsg.  If you care about locking, do it before calling. */
+bool __proc_preempt_all(struct proc *p)
+{
+       /* instead of doing this, we could just preempt_served all possible vcores,
+        * and not just the active ones.  We would need to sort out a way to deal
+        * with stale preempt_serveds first.  This might be just as fast anyways. */
+       uint32_t active_vcoreid = 0;
+       for (int i = 0; i < p->procinfo->num_vcores; i++) {
+               active_vcoreid = get_busy_vcoreid(p, active_vcoreid);
+               p->procinfo->vcoremap[active_vcoreid].preempt_served = TRUE;
+               active_vcoreid++;
+       }
+       return __proc_take_allcores(p, __preempt, p, 0, 0);
+}
+
+/* Warns and preempts a vcore from p.  No delaying / alarming, or anything.  The
+ * warning will be for u usec from now. */
+void proc_preempt_core(struct proc *p, uint32_t pcoreid, uint64_t usec)
+{
+       bool self_ipi_pending = FALSE;
+       uint64_t warn_time = read_tsc() + usec * 1000000 / system_timing.tsc_freq;
+
+       /* DYING could be okay */
+       if (p->state != PROC_RUNNING_M) {
+               warn("Tried to preempt from a non RUNNING_M proc!");
+               return;
+       }
+       spin_lock(&p->proc_lock);
+       if (is_mapped_vcore(p, pcoreid)) {
+               __proc_preempt_warn(p, get_vcoreid(p, pcoreid), warn_time);
+               self_ipi_pending = __proc_preempt_core(p, pcoreid);
+       } else {
+               warn("Pcore doesn't belong to the process!!");
+       }
+       /* TODO: (RMS) do this once a scheduler can handle RUNNABLE_M, and make sure
+        * to schedule it */
+       #if 0
+       if (!p->procinfo->num_vcores) {
+               __proc_set_state(p, PROC_RUNNABLE_M);
+               schedule_proc(p);
+       }
+       #endif
+       spin_unlock(&p->proc_lock);
+       __proc_kmsg_pending(p, self_ipi_pending);
+}
+
+/* Warns and preempts all from p.  No delaying / alarming, or anything.  The
+ * warning will be for u usec from now. */
+void proc_preempt_all(struct proc *p, uint64_t usec)
+{
+       bool self_ipi_pending = FALSE;
+       uint64_t warn_time = read_tsc() + usec * 1000000 / system_timing.tsc_freq;
+
+       spin_lock(&p->proc_lock);
+       /* DYING could be okay */
+       if (p->state != PROC_RUNNING_M) {
+               warn("Tried to preempt from a non RUNNING_M proc!");
+               spin_unlock(&p->proc_lock);
+               return;
+       }
+       __proc_preempt_warnall(p, warn_time);
+       self_ipi_pending = __proc_preempt_all(p);
+       assert(!p->procinfo->num_vcores);
+       /* TODO: (RMS) do this once a scheduler can handle RUNNABLE_M, and make sure
+        * to schedule it */
+       #if 0
+       __proc_set_state(p, PROC_RUNNABLE_M);
+       schedule_proc(p);
+       #endif
+       spin_unlock(&p->proc_lock);
+       __proc_kmsg_pending(p, self_ipi_pending);
+}
+
+/* Give the specific pcore to proc p.  Lots of assumptions, so don't really use
+ * this.  The proc needs to be _M and prepared for it.  the pcore needs to be
+ * free, etc. */
+void proc_give(struct proc *p, uint32_t pcoreid)
+{
+       bool self_ipi_pending = FALSE;
+
+       spin_lock(&p->proc_lock);
+       // expects a pcorelist, we give it a list of one
+       self_ipi_pending = __proc_give_cores(p, &pcoreid, 1);
+       spin_unlock(&p->proc_lock);
+       __proc_kmsg_pending(p, self_ipi_pending);
+}
+
 /* Global version of the helper, for sys_get_vcoreid (might phase that syscall
  * out). */
 uint32_t proc_get_vcoreid(struct proc *SAFE p, uint32_t pcoreid)
@@ -1240,7 +1387,7 @@ bool __proc_take_allcores(struct proc *SAFE p, amr_t message,
  * There should already be a kmsg waiting for us, since when we checked state to
  * see a message was coming, the message had already been sent before unlocking.
  * Note we do not need interrupts enabled for this to work (you can receive a
- * message before its IPI by polling), though in most cases they will be. 
+ * message before its IPI by polling), though in most cases they will be.
  *
  * TODO: consider inlining this, so __FUNCTION__ works (will require effort in
  * core_request(). */
@@ -1407,7 +1554,8 @@ void __preempt(trapframe_t *tf, uint32_t srcid, void *a0, void *a1, void *a2)
        struct proc *p = (struct proc*)a0;
 
        if (p != current)
-               warn("__preempt arrived for a process that was not current!");
+               panic("__preempt arrived for a process (%p) that was not current (%p)!",
+                     p, current);
        assert(!in_kernel(tf));
        /* We shouldn't need to lock here, since unmapping happens on the pcore and
         * mapping would only happen if the vcore was free, which it isn't until
@@ -1417,7 +1565,7 @@ void __preempt(trapframe_t *tf, uint32_t srcid, void *a0, void *a1, void *a2)
        /* either __preempt or proc_yield() ends the preempt phase. */
        p->procinfo->vcoremap[vcoreid].preempt_pending = 0;
        vcpd = &p->procdata->vcore_preempt_data[vcoreid];
-       printk("[kernel] received __preempt for proc %d's vcore %d on pcore %d\n",
+       printd("[kernel] received __preempt for proc %d's vcore %d on pcore %d\n",
               p->procinfo->pid, vcoreid, core_id());
 
        /* save the old tf in the preempt slot, save the silly state, and signal the
@@ -1503,8 +1651,9 @@ void print_proc_info(pid_t pid)
        for (int i = 0; i < MAX_NUM_RESOURCES; i++)
                printk("\tRes type: %02d, amt wanted: %08d, amt granted: %08d\n", i,
                       p->resources[i].amt_wanted, p->resources[i].amt_granted);
-       printk("Vcore 0's Last Trapframe:\n");
-       print_trapframe(&p->env_tf);
+       /* No one cares, and it clutters the terminal */
+       //printk("Vcore 0's Last Trapframe:\n");
+       //print_trapframe(&p->env_tf);
        spin_unlock(&p->proc_lock);
        proc_decref(p, 1); /* decref for the pid2proc reference */
 }
diff --git a/tests/msr_dumb_while.c b/tests/msr_dumb_while.c
new file mode 100644 (file)
index 0000000..a56f583
--- /dev/null
@@ -0,0 +1,26 @@
+/* tests/msr_dumb_while.c
+ *
+ * This requests the max_vcores in the system, then just dumbly while loops. */
+
+#include <rstdio.h>
+#include <vcore.h>
+
+int main(int argc, char** argv)
+{
+
+       /* don't forget to enable notifs on vcore0.  if you don't, the kernel will
+        * restart your _S with notifs disabled, which is a path to confusion. */
+       struct preempt_data *vcpd = &__procdata.vcore_preempt_data[0];
+       vcpd->notif_enabled = TRUE;
+
+       vcore_request(max_vcores());
+
+       /* should never make it here */
+       return -1;
+}
+
+void vcore_entry(void)
+{
+       while(1);
+}
+
diff --git a/tests/msr_nice_while.c b/tests/msr_nice_while.c
new file mode 100644 (file)
index 0000000..0556f29
--- /dev/null
@@ -0,0 +1,36 @@
+/* tests/msr_dumb_while.c
+ *
+ * This requests the max_vcores in the system, then just while loops in a
+ * userthread.  The pthread code will nicely yield if it detects an incoming
+ * preemption. */
+
+#include <ros/notification.h>
+#include <stdlib.h>
+#include <vcore.h>
+#include <pthread.h>
+#include <rassert.h>
+
+void *while_thread(void *arg)
+{
+       while (1);
+}
+
+int main(int argc, char** argv)
+{
+       pthread_t *my_threads = malloc(sizeof(pthread_t) * max_vcores());
+
+       /* set up to receive the PREEMPT_PENDING notif */
+       struct notif_method *nm;
+       nm = &__procdata.notif_methods[NE_PREEMPT_PENDING];
+       nm->flags |= NOTIF_WANTED | NOTIF_IPI;
+
+       /* actually only need one less, since the _S will be pthread 0 */
+       for (int i = 0; i < max_vcores() - 1; i++)
+               pthread_create(&my_threads[i], NULL, &while_thread, NULL);
+
+       assert(num_vcores() == max_vcores());
+       while (1);
+
+       /* should never make it here */
+       return -1;
+}
diff --git a/tests/msr_single_while.c b/tests/msr_single_while.c
new file mode 100644 (file)
index 0000000..f0f5234
--- /dev/null
@@ -0,0 +1,10 @@
+/* tests/msr_single_while.c
+ *
+ * just sits in a while loop in _S mode. */
+
+int main(int argc, char** argv)
+{
+       while(1);
+       return -1;
+}
+