Allow sys_halt_core() to monitor notif_pending
authorBarret Rhoden <brho@cs.berkeley.edu>
Mon, 20 Nov 2017 19:20:08 +0000 (14:20 -0500)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 22 Nov 2017 16:49:34 +0000 (11:49 -0500)
This lets the 2LS halt the core and wait for someone to touch
notif_pending, which is touched for any event delivery to the vcore.

2LSs that want to wake vcores without sending a full event will need to use
wake_vcore().  The force_ipi is to help them break out of spinning
uthreads.  We'll see if that's needed or not.  Some of this ties into the
long range 2LS changes I've been planning.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
kern/arch/riscv/arch.h
kern/arch/x86/arch.h
kern/arch/x86/idle.c
kern/src/syscall.c
user/parlib/include/parlib/riscv/arch.h
user/parlib/include/parlib/vcore.h
user/parlib/include/parlib/x86/arch.h
user/parlib/vcore.c

index 9e6a18a..d1f603a 100644 (file)
@@ -158,6 +158,12 @@ static __inline void reboot(void)
 
 extern void cpu_halt(void);
 
+struct preempt_data;
+static inline void cpu_halt_notif_pending(struct preempt_data *vcpd)
+{
+       cpu_halt();
+}
+
 static inline void prefetch(void *addr)
 {
 }
index 68db46f..0d3e935 100644 (file)
@@ -43,6 +43,8 @@ void tlbflush(void);
 void tlb_flush_global(void);
 /* idle.c */
 void cpu_halt(void);
+struct preempt_data;
+void cpu_halt_notif_pending(struct preempt_data *vcpd);
 
 static inline void breakpoint(void)
 {
index e5029b8..202016d 100644 (file)
@@ -22,6 +22,24 @@ void cpu_halt(void)
        disable_irq();
 }
 
+/* Atomically enables interrupts and halts.  It will wake if notif_pending was
+ * set (racy), and if we have mwait, it will wake if notif_pending gets set.
+ * It returns with IRQs off. */
+void cpu_halt_notif_pending(struct preempt_data *vcpd)
+{
+       if (cpu_has_feat(CPU_FEAT_X86_MWAIT))
+               asm volatile("monitor" : : "a"(&vcpd->notif_pending), "c"(0), "d"(0));
+       if (vcpd->notif_pending)
+               return;
+       /* Note we don't use the ecx=1 setting - we actually want to sti so that we
+        * handle the IRQ and not just wake from it. */
+       if (cpu_has_feat(CPU_FEAT_X86_MWAIT))
+               asm volatile("sti; mwait" : : "c"(0x0), "a"(x86_cstate) : "memory");
+       else
+               asm volatile("sti; hlt" : : : "memory");
+       disable_irq();
+}
+
 void set_pstate(unsigned int pstate)
 {
        uint64_t perf_ctl;
index 9de2669..0659e12 100644 (file)
@@ -1414,7 +1414,8 @@ static int sys_vc_entry(struct proc *p)
 
 /* This will halt the core, waking on an IRQ.  These could be kernel IRQs for
  * things like timers or devices, or they could be IPIs for RKMs (__notify for
- * an evq with IPIs for a syscall completion, etc).
+ * an evq with IPIs for a syscall completion, etc).  With arch support, this
+ * will also wake on a write to notif_pending.
  *
  * We don't need to finish the syscall early (worried about the syscall struct,
  * on the vcore's stack).  The syscall will finish before any __preempt RKM
@@ -1424,16 +1425,22 @@ static int sys_vc_entry(struct proc *p)
  *
  * In the future, RKM code might avoid sending IPIs if the core is already in
  * the kernel.  That code will need to check the CPU's state in some manner, and
- * send if the core is halted/idle.
+ * send if the core is halted/idle.  Or perhaps use mwait, if there's arch
+ * support.
  *
  * The core must wake up for RKMs, including RKMs that arrive while the kernel
- * is trying to halt.  The core need not abort the halt for notif_pending for
- * the vcore, only for a __notify or other RKM.  Anyone setting notif_pending
- * should then attempt to __notify (o/w it's probably a bug). */
+ * is trying to halt.
+ *
+ * If our hardware supports something like monitor/mwait, we'll abort if
+ * notif_pending was or gets set.  Note that whoever writes notif_pending may
+ * send an IPI regardless of whether or not we have mwait.  That's up to the
+ * ev_q settings (so basically userspace).  If userspace doesn't want an IPI, a
+ * notif will wake it up, but it won't break it out of a uthread loop. */
 static int sys_halt_core(struct proc *p, unsigned long usec)
 {
        struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
        struct preempt_data *vcpd;
+
        /* The user can only halt CG cores!  (ones it owns) */
        if (management_core())
                return -1;
@@ -1446,18 +1453,17 @@ static int sys_halt_core(struct proc *p, unsigned long usec)
                enable_irq();
                return 0;
        }
-       /* This situation possible, though the check is not necessary.  We can't
-        * assert notif_pending isn't set, since another core may be in the
-        * proc_notify.  Thus we can't tell if this check here caught a bug, or just
-        * aborted early. */
        vcpd = &p->procdata->vcore_preempt_data[pcpui->owning_vcoreid];
-       if (vcpd->notif_pending) {
-               __set_cpu_state(pcpui, CPU_STATE_KERNEL);
-               enable_irq();
-               return 0;
-       }
-       cpu_halt();
+       /* We pretend to not be in vcore context so other cores will send us IPIs
+        * (__notify).  If we do get a __notify, we'll have set notif_disabled back
+        * on before we handle the message, since it's a routine KMSG.  Note that
+        * other vcores will think we are not in vcore context.  This is no
+        * different to when we pop contexts: 'briefly' leave VC ctx, check
+        * notif_pending, and (possibly) abort and set notif_disabled. */
+       vcpd->notif_disabled = false;
+       cpu_halt_notif_pending(vcpd);
        __set_cpu_state(pcpui, CPU_STATE_KERNEL);
+       vcpd->notif_disabled = true;
        enable_irq();
        return 0;
 }
index 9948823..e24bd36 100644 (file)
@@ -99,4 +99,9 @@ static inline void restore_fp_state(struct ancillary_state* silly)
        write_fsr(fsr);
 }
 
+static inline bool arch_has_mwait(void)
+{
+       return false;
+}
+
 __END_DECLS
index e7a1753..43ae52b 100644 (file)
@@ -57,6 +57,7 @@ void cpu_relax_vc(uint32_t vcoreid);
 uint32_t get_vcoreid(void);
 bool check_vcoreid(const char *str, uint32_t vcoreid);
 void __attribute__((noreturn)) vcore_yield_or_restart(void);
+void vcore_wake(uint32_t vcoreid, bool force_ipi);
 
 /* This works so long as we don't dlopen parlib (which we never do) */
 #define get_tlsvar_linaddr(_vcoreid, _var)                                     \
index 00d632d..6899887 100644 (file)
@@ -161,6 +161,11 @@ static inline void restore_fp_state(struct ancillary_state *silly)
        }
 }
 
+static inline bool arch_has_mwait(void)
+{
+       return cpu_has_feat(CPU_FEAT_X86_MWAIT);
+}
+
 /* Cpuid helper function originally from Barret's fputest. */
 static inline void parlib_cpuid(uint32_t level1, uint32_t level2,
                                 uint32_t *eaxp, uint32_t *ebxp,
index 32c89e3..c431680 100644 (file)
@@ -521,3 +521,14 @@ void __attribute__((noreturn)) vcore_yield_or_restart(void)
        set_stack_pointer((void*)vcpd->vcore_stack);
        vcore_entry();
 }
+
+void vcore_wake(uint32_t vcoreid, bool force_ipi)
+{
+       struct preempt_data *vcpd = vcpd_of(vcoreid);
+
+       vcpd->notif_pending = true;
+       if (vcoreid == vcore_id())
+               return;
+       if (force_ipi || !arch_has_mwait())
+               sys_self_notify(vcoreid, EV_NONE, 0, true);
+}