vmm: Let the VMM control mwait vmexits (XCC)
authorBarret Rhoden <brho@cs.berkeley.edu>
Fri, 17 Nov 2017 15:59:27 +0000 (10:59 -0500)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 22 Nov 2017 16:49:34 +0000 (11:49 -0500)
We can handle mwaits in software, but only for power management halts - the
monitor won't work.  We tell the guest (via cpuid) that monitor/mwait is
not supported, so the only VMs that should be making mwait calls are
paravirtualized guests.

The greedy 2LS sets mwait exiting in the same way that it controls whether
or not halts trigger a vmexit.

The end result should be that guests can mwait at whatever sleep state they
want - preferably at least C2 (mwait 0x10), which would allow other
physical cores to reach a higher turbo mode setting.

Reinstall your kernel headers.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
kern/arch/x86/vmm/intel/vmx.c
kern/include/ros/vmm.h
user/vmm/sched.c
user/vmm/vmexit.c

index b99ffd2..690343e 100644 (file)
@@ -542,7 +542,9 @@ static struct vmxec cbec = {
        .try_set_0 = (CPU_BASED_MONITOR_EXITING),
        .policy_changeable = (
                 CPU_BASED_HLT_EXITING |
-                CPU_BASED_PAUSE_EXITING),
+                CPU_BASED_PAUSE_EXITING |
+                CPU_BASED_MWAIT_EXITING |
+                0),
 };
 
 static struct vmxec cb2ec = {
@@ -1308,6 +1310,9 @@ int intel_vmm_init(void) {
                return ret;
        }
        printk("VMX setup succeeded\n");
+       /* If this isn't true (we have VMX but not mwait), then we'll have to look
+        * closely at CPU_BASED_MWAIT_EXITING. */
+       assert(cpu_has_feat(CPU_FEAT_X86_MWAIT));
        return 0;
 }
 
@@ -1440,6 +1445,8 @@ int vmx_ctl_get_exits(struct vmx_vmm *vmx)
                ret |= VMM_CTL_EXIT_HALT;
        if (vmx->cpu_exec_ctls & CPU_BASED_PAUSE_EXITING)
                ret |= VMM_CTL_EXIT_PAUSE;
+       if (vmx->cpu_exec_ctls & CPU_BASED_MWAIT_EXITING)
+               ret |= VMM_CTL_EXIT_MWAIT;
        return ret;
 }
 
@@ -1459,6 +1466,11 @@ int vmx_ctl_set_exits(struct vmx_vmm *vmx, int vmm_exits)
                        error(ENOSYS, "VMX can't toggle EXIT_PAUSE");
                vmx_toggle_do |= CPU_BASED_PAUSE_EXITING;
        }
+       if (toggle_want & VMM_CTL_EXIT_MWAIT) {
+           if (!vmx_control_can_be_changed(&cbec, CPU_BASED_MWAIT_EXITING))
+                       error(ENOSYS, "VMX can't toggle EXIT_MWAIT");
+               vmx_toggle_do |= CPU_BASED_MWAIT_EXITING;
+       }
        /* This is being read concurrently by load_guest_pcore. */
        WRITE_ONCE(vmx->cpu_exec_ctls, vmx->cpu_exec_ctls ^ vmx_toggle_do);
        return 0;
index d797520..6ac39a0 100644 (file)
@@ -19,7 +19,8 @@
 
 #define VMM_CTL_EXIT_HALT              (1 << 0)
 #define VMM_CTL_EXIT_PAUSE             (1 << 1)
-#define VMM_CTL_ALL_EXITS              ((1 << 2) - 1)
+#define VMM_CTL_EXIT_MWAIT             (1 << 2)
+#define VMM_CTL_ALL_EXITS              ((1 << 3) - 1)
 
 #define VMM_CTL_FL_KERN_PRINTC         (1 << 0)
 #define VMM_CTL_ALL_FLAGS                      (VMM_CTL_FL_KERN_PRINTC)
index 5b56a99..a1f512a 100644 (file)
@@ -626,7 +626,8 @@ int vmm_init(struct virtual_machine *vm, struct vmm_gpcore_init *gpcis,
                assert(greedy_rnbl_guests);
                vcore_request_total(sched_nr_greedy_cores());
                syscall(SYS_vmm_ctl, VMM_CTL_SET_EXITS,
-                       syscall(SYS_vmm_ctl, VMM_CTL_GET_EXITS) & ~VMM_CTL_EXIT_HALT);
+                       syscall(SYS_vmm_ctl, VMM_CTL_GET_EXITS) &
+                               ~(VMM_CTL_EXIT_HALT | VMM_CTL_EXIT_MWAIT));
        }
        return 0;
 }
index 0648146..4c5db6a 100644 (file)
@@ -310,6 +310,22 @@ static bool handle_halt(struct guest_thread *gth)
        return TRUE;
 }
 
+/* The guest is told (via cpuid) that there is no monitor/mwait.  Callers of
+ * mwait are paravirtualized halts.
+ *
+ * We don't support monitor/mwait in software, so if they tried to mwait
+ * without break-on-interrupt and with interrupts disabled, they'll never
+ * wake up.  So we'll always break on interrupt. */
+static bool handle_mwait(struct guest_thread *gth)
+{
+       struct vm_trapframe *vm_tf = gth_to_vmtf(gth);
+       struct virtual_machine *vm = gth_to_vm(gth);
+
+       sleep_til_irq(gth);
+       vm_tf->tf_rip += 3;
+       return TRUE;
+}
+
 /* Is this a vmm specific thing?  or generic?
  *
  * what do we do when we want to kill the vm?  what are our other options? */
@@ -333,6 +349,8 @@ bool handle_vmexit(struct guest_thread *gth)
                return handle_apic_access(gth);
        case EXIT_REASON_HLT:
                return handle_halt(gth);
+       case EXIT_REASON_MWAIT_INSTRUCTION:
+               return handle_mwait(gth);
        case EXIT_REASON_EXTERNAL_INTERRUPT:
        case EXIT_REASON_APIC_WRITE:
                /* TODO: just ignore these? */