Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F170740995
D35623.id110773.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
12 KB
Referenced Files
None
Subscribers
None
D35623.id110773.diff
View Options
diff --git a/sys/amd64/include/vmm.h b/sys/amd64/include/vmm.h
--- a/sys/amd64/include/vmm.h
+++ b/sys/amd64/include/vmm.h
@@ -31,6 +31,7 @@
#ifndef _VMM_H_
#define _VMM_H_
+#include <sys/cpuset.h>
#include <sys/sdt.h>
#include <x86/segments.h>
@@ -483,6 +484,7 @@
VM_CAP_BPT_EXIT,
VM_CAP_RDPID,
VM_CAP_RDTSCP,
+ VM_CAP_IPI_EXIT,
VM_CAP_MAX
};
@@ -630,6 +632,7 @@
VM_EXITCODE_DEBUG,
VM_EXITCODE_VMINSN,
VM_EXITCODE_BPT,
+ VM_EXITCODE_IPI,
VM_EXITCODE_MAX
};
@@ -737,6 +740,11 @@
struct {
enum vm_suspend_how how;
} suspended;
+ struct {
+ uint32_t mode;
+ uint8_t vector;
+ cpuset_t dmask;
+ } ipi;
struct vm_task_switch task_switch;
} u;
};
diff --git a/sys/amd64/vmm/amd/svm.c b/sys/amd64/vmm/amd/svm.c
--- a/sys/amd64/vmm/amd/svm.c
+++ b/sys/amd64/vmm/amd/svm.c
@@ -2315,6 +2315,7 @@
svm_setcap(void *arg, int vcpu, int type, int val)
{
struct svm_softc *sc;
+ struct vlapic *vlapic;
int error;
sc = arg;
@@ -2333,6 +2334,10 @@
if (val == 0)
error = EINVAL;
break;
+ case VM_CAP_IPI_EXIT:
+ vlapic = vm_lapic(sc->vm, vcpu);
+ vlapic->ipi_exit = val;
+ break;
default:
error = ENOENT;
break;
@@ -2344,6 +2349,7 @@
svm_getcap(void *arg, int vcpu, int type, int *retval)
{
struct svm_softc *sc;
+ struct vlapic *vlapic;
int error;
sc = arg;
@@ -2361,6 +2367,10 @@
case VM_CAP_UNRESTRICTED_GUEST:
*retval = 1; /* unrestricted guest is always enabled */
break;
+ case VM_CAP_IPI_EXIT:
+ vlapic = vm_lapic(sc->vm, vcpu);
+ *retval = vlapic->ipi_exit;
+ break;
default:
error = ENOENT;
break;
diff --git a/sys/amd64/vmm/intel/vmx.c b/sys/amd64/vmm/intel/vmx.c
--- a/sys/amd64/vmm/intel/vmx.c
+++ b/sys/amd64/vmm/intel/vmx.c
@@ -3504,6 +3504,7 @@
ret = 0;
break;
case VM_CAP_BPT_EXIT:
+ case VM_CAP_IPI_EXIT:
ret = 0;
break;
default:
@@ -3521,6 +3522,7 @@
{
struct vmx *vmx = arg;
struct vmcs *vmcs = &vmx->vmcs[vcpu];
+ struct vlapic *vlapic;
uint32_t baseval;
uint32_t *pptr;
int error;
@@ -3599,6 +3601,12 @@
reg = VMCS_EXCEPTION_BITMAP;
}
break;
+ case VM_CAP_IPI_EXIT:
+ retval = 0;
+
+ vlapic = vm_lapic(vmx->vm, vcpu);
+ vlapic->ipi_exit = val;
+ break;
default:
break;
}
diff --git a/sys/amd64/vmm/io/vlapic.c b/sys/amd64/vmm/io/vlapic.c
--- a/sys/amd64/vmm/io/vlapic.c
+++ b/sys/amd64/vmm/io/vlapic.c
@@ -84,6 +84,7 @@
static void vlapic_set_error(struct vlapic *, uint32_t, bool);
static void vlapic_callout_handler(void *arg);
+static void vlapic_reset(struct vlapic *vlapic);
static __inline uint32_t
vlapic_get_id(struct vlapic *vlapic)
@@ -957,13 +958,12 @@
{
int i;
bool phys;
- cpuset_t dmask;
+ cpuset_t dmask, ipimask;
uint64_t icrval;
- uint32_t dest, vec, mode;
+ uint32_t dest, vec, mode, shorthand;
struct vlapic *vlapic2;
struct vm_exit *vmexit;
struct LAPIC *lapic;
- uint16_t maxcpus;
lapic = vlapic->apic_page;
lapic->icr_lo &= ~APIC_DELSTAT_PEND;
@@ -975,97 +975,119 @@
dest = icrval >> (32 + 24);
vec = icrval & APIC_VECTOR_MASK;
mode = icrval & APIC_DELMODE_MASK;
+ phys = (icrval & APIC_DESTMODE_LOG) == 0;
+ shorthand = icrval & APIC_DEST_MASK;
- if (mode == APIC_DELMODE_FIXED && vec < 16) {
- vlapic_set_error(vlapic, APIC_ESR_SEND_ILLEGAL_VECTOR, false);
- VLAPIC_CTR1(vlapic, "Ignoring invalid IPI %d", vec);
- return (0);
+ VLAPIC_CTR2(vlapic, "icrlo 0x%016lx triggered ipi %d", icrval, vec);
+
+ switch (shorthand) {
+ case APIC_DEST_DESTFLD:
+ vlapic_calcdest(vlapic->vm, &dmask, dest, phys, false, x2apic(vlapic));
+ break;
+ case APIC_DEST_SELF:
+ CPU_SETOF(vlapic->vcpuid, &dmask);
+ break;
+ case APIC_DEST_ALLISELF:
+ dmask = vm_active_cpus(vlapic->vm);
+ break;
+ case APIC_DEST_ALLESELF:
+ dmask = vm_active_cpus(vlapic->vm);
+ CPU_CLR(vlapic->vcpuid, &dmask);
+ break;
+ default:
+ __assert_unreachable();
}
- VLAPIC_CTR2(vlapic, "icrlo 0x%016lx triggered ipi %d", icrval, vec);
+ /*
+ * ipimask is a set of vCPUs needing userland handling of the current
+ * IPI.
+ */
+ CPU_ZERO(&ipimask);
- if (mode == APIC_DELMODE_FIXED || mode == APIC_DELMODE_NMI) {
- switch (icrval & APIC_DEST_MASK) {
- case APIC_DEST_DESTFLD:
- phys = ((icrval & APIC_DESTMODE_LOG) == 0);
- vlapic_calcdest(vlapic->vm, &dmask, dest, phys, false,
- x2apic(vlapic));
- break;
- case APIC_DEST_SELF:
- CPU_SETOF(vlapic->vcpuid, &dmask);
- break;
- case APIC_DEST_ALLISELF:
- dmask = vm_active_cpus(vlapic->vm);
- break;
- case APIC_DEST_ALLESELF:
- dmask = vm_active_cpus(vlapic->vm);
- CPU_CLR(vlapic->vcpuid, &dmask);
- break;
- default:
- CPU_ZERO(&dmask); /* satisfy gcc */
- break;
+ switch (mode) {
+ case APIC_DELMODE_FIXED:
+ if (vec < 16) {
+ vlapic_set_error(vlapic, APIC_ESR_SEND_ILLEGAL_VECTOR,
+ false);
+ VLAPIC_CTR1(vlapic, "Ignoring invalid IPI %d", vec);
+ return (0);
}
CPU_FOREACH_ISSET(i, &dmask) {
- if (mode == APIC_DELMODE_FIXED) {
- lapic_intr_edge(vlapic->vm, i, vec);
- vmm_stat_array_incr(vlapic->vm, vlapic->vcpuid,
- IPIS_SENT, i, 1);
- VLAPIC_CTR2(vlapic, "vlapic sending ipi %d "
- "to vcpuid %d", vec, i);
- } else {
- vm_inject_nmi(vlapic->vm, i);
- VLAPIC_CTR1(vlapic, "vlapic sending ipi nmi "
- "to vcpuid %d", i);
- }
+ lapic_intr_edge(vlapic->vm, i, vec);
+ vmm_stat_array_incr(vlapic->vm, vlapic->vcpuid,
+ IPIS_SENT, i, 1);
+ VLAPIC_CTR2(vlapic,
+ "vlapic sending ipi %d to vcpuid %d", vec, i);
}
- return (0); /* handled completely in the kernel */
- }
+ break;
+ case APIC_DELMODE_NMI:
+ CPU_FOREACH_ISSET(i, &dmask) {
+ vm_inject_nmi(vlapic->vm, i);
+ VLAPIC_CTR1(vlapic,
+ "vlapic sending ipi nmi to vcpuid %d", i);
+ }
- maxcpus = vm_get_maxcpus(vlapic->vm);
- if (mode == APIC_DELMODE_INIT) {
+ break;
+ case APIC_DELMODE_INIT:
if ((icrval & APIC_LEVEL_MASK) == APIC_LEVEL_DEASSERT)
- return (0);
-
- if (vlapic->vcpuid == 0 && dest != 0 && dest < maxcpus) {
- vlapic2 = vm_lapic(vlapic->vm, dest);
-
- /* move from INIT to waiting-for-SIPI state */
- if (vlapic2->boot_state == BS_INIT) {
- vlapic2->boot_state = BS_SIPI;
- }
+ break;
- return (0);
+ CPU_FOREACH_ISSET(i, &dmask) {
+ vlapic2 = vm_lapic(vlapic->vm, i);
+ vlapic2->boot_state = BS_SIPI;
+ CPU_SET(i, &ipimask);
}
- }
-
- if (mode == APIC_DELMODE_STARTUP) {
- if (vlapic->vcpuid == 0 && dest != 0 && dest < maxcpus) {
- vlapic2 = vm_lapic(vlapic->vm, dest);
+ break;
+ case APIC_DELMODE_STARTUP:
+ CPU_FOREACH_ISSET(i, &dmask) {
+ vlapic2 = vm_lapic(vlapic->vm, i);
/*
* Ignore SIPIs in any state other than wait-for-SIPI
*/
if (vlapic2->boot_state != BS_SIPI)
- return (0);
-
+ continue;
+ /*
+ * TODO:
+ * This should be triggered from userspace.
+ */
+ vlapic_reset(vlapic2);
vlapic2->boot_state = BS_RUNNING;
+ CPU_SET(i, &ipimask);
+ }
- *retu = true;
- vmexit = vm_exitinfo(vlapic->vm, vlapic->vcpuid);
- vmexit->exitcode = VM_EXITCODE_SPINUP_AP;
- vmexit->u.spinup_ap.vcpu = dest;
- vmexit->u.spinup_ap.rip = vec << PAGE_SHIFT;
+ break;
+ default:
+ return (1);
+ }
- return (0);
+ if (!CPU_EMPTY(&ipimask)) {
+ vmexit = vm_exitinfo(vlapic->vm, vlapic->vcpuid);
+ vmexit->exitcode = VM_EXITCODE_IPI;
+ vmexit->u.ipi.mode = mode;
+ vmexit->u.ipi.vector = vec;
+ vmexit->u.ipi.dmask = dmask;
+
+ *retu = true;
+
+ /*
+ * Old bhyve versions don't support the IPI exit. Translate it
+ * into the old style.
+ */
+ if (!vlapic->ipi_exit) {
+ if (mode == APIC_DELMODE_STARTUP) {
+ vmexit->exitcode = VM_EXITCODE_SPINUP_AP;
+ vmexit->u.spinup_ap.vcpu = CPU_FFS(&ipimask) - 1;
+ vmexit->u.spinup_ap.rip = vec << PAGE_SHIFT;
+ } else {
+ *retu = false;
+ }
}
}
- /*
- * This will cause a return to userland.
- */
- return (1);
+ return (0);
}
void
@@ -1467,6 +1489,8 @@
if (vlapic->vcpuid == 0)
vlapic->msr_apicbase |= APICBASE_BSP;
+ vlapic->ipi_exit = false;
+
vlapic_reset(vlapic);
}
diff --git a/sys/amd64/vmm/io/vlapic_priv.h b/sys/amd64/vmm/io/vlapic_priv.h
--- a/sys/amd64/vmm/io/vlapic_priv.h
+++ b/sys/amd64/vmm/io/vlapic_priv.h
@@ -183,6 +183,8 @@
*/
uint32_t svr_last;
uint32_t lvt_last[VLAPIC_MAXLVT_INDEX + 1];
+
+ bool ipi_exit;
};
void vlapic_init(struct vlapic *vlapic);
diff --git a/usr.sbin/bhyve/bhyverun.c b/usr.sbin/bhyve/bhyverun.c
--- a/usr.sbin/bhyve/bhyverun.c
+++ b/usr.sbin/bhyve/bhyverun.c
@@ -46,6 +46,7 @@
#endif
#include <amd64/vmm/intel/vmcs.h>
+#include <x86/apicreg.h>
#include <machine/atomic.h>
#include <machine/segments.h>
@@ -194,6 +195,15 @@
static cpuset_t cpumask;
+static volatile cpuset_t suspended_cpus;
+static pthread_mutex_t suspended_cpus_mtx;
+static pthread_cond_t suspended_cpus_cond;
+/*
+ * vcpus in an invalid state that are waiting for an SIPI to initialize and
+ * start themself.
+ */
+static volatile cpuset_t zombie_cpus;
+
static void vm_loop(struct vmctx *ctx, int vcpu, uint64_t rip);
static struct vm_exit *vmexit;
@@ -514,6 +524,18 @@
}
#endif
+static void
+spin_if_suspended(int vcpuid)
+{
+ pthread_mutex_lock(&suspended_cpus_mtx);
+ CPU_SET(vcpuid, &suspended_cpus);
+ while (CPU_ISSET(vcpuid, &zombie_cpus)) {
+ pthread_cond_wait(&suspended_cpus_cond, &suspended_cpus_mtx);
+ }
+ CPU_CLR(vcpuid, &suspended_cpus);
+ pthread_mutex_unlock(&suspended_cpus_mtx);
+}
+
int
fbsdrun_virtio_msix(void)
{
@@ -538,6 +560,7 @@
checkpoint_cpu_add(vcpu);
#endif
gdb_cpu_add(vcpu);
+ spin_if_suspended(vcpu);
vm_loop(mtp->mt_ctx, vcpu, vmexit[vcpu].rip);
@@ -563,8 +586,12 @@
CPU_SET_ATOMIC(newcpu, &cpumask);
- if (suspend)
+ if (suspend) {
+ pthread_mutex_lock(&suspended_cpus_mtx);
+ CPU_SET(newcpu, &zombie_cpus);
+ pthread_mutex_unlock(&suspended_cpus_mtx);
vm_suspend_cpu(ctx, newcpu);
+ }
/*
* Set up the vmexit struct to allow execution to start
@@ -815,6 +842,7 @@
checkpoint_cpu_suspend(*pvcpu);
#endif
gdb_cpu_mtrap(*pvcpu);
+ spin_if_suspended(*pvcpu);
#ifdef BHYVE_SNAPSHOT
checkpoint_cpu_resume(*pvcpu);
#endif
@@ -921,6 +949,7 @@
checkpoint_cpu_suspend(*pvcpu);
#endif
gdb_cpu_suspend(*pvcpu);
+ spin_if_suspended(*pvcpu);
#ifdef BHYVE_SNAPSHOT
checkpoint_cpu_resume(*pvcpu);
#endif
@@ -935,6 +964,62 @@
return (VMEXIT_CONTINUE);
}
+static int
+vmexit_ipi(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
+{
+ int error = -1;
+ int i;
+ switch (vmexit->u.ipi.mode) {
+ case APIC_DELMODE_INIT:
+ /* Request cpus to enter zombie state. */
+ pthread_mutex_lock(&suspended_cpus_mtx);
+ CPU_OR(&zombie_cpus, &zombie_cpus, &vmexit->u.ipi.dmask);
+ pthread_mutex_unlock(&suspended_cpus_mtx);
+
+ /* Suspend cpus. */
+ CPU_FOREACH_ISSET (i, &vmexit->u.ipi.dmask) {
+ error = vm_suspend_cpu(ctx, i);
+ if (error) {
+ warnx("%s: failed to suspend cpu %d\n",
+ __func__, i);
+ break;
+ }
+ }
+
+ /* Wait until all cpus are suspended. */
+ cpuset_t cpus;
+ while (true) {
+ pthread_mutex_lock(&suspended_cpus_mtx);
+ CPU_AND(&cpus, &suspended_cpus, &vmexit->u.ipi.dmask);
+ if (CPU_EQUAL(&cpus, &vmexit->u.ipi.dmask)) {
+ break;
+ }
+ pthread_mutex_unlock(&suspended_cpus_mtx);
+ usleep(100);
+ }
+ pthread_mutex_unlock(&suspended_cpus_mtx);
+
+ break;
+ case APIC_DELMODE_STARTUP:
+ CPU_FOREACH_ISSET (i, &vmexit->u.ipi.dmask) {
+ spinup_ap(ctx, i, vmexit->u.ipi.vector << PAGE_SHIFT);
+ }
+
+ /* Release cpus from zombie state. */
+ pthread_mutex_lock(&suspended_cpus_mtx);
+ CPU_ANDNOT(&zombie_cpus, &zombie_cpus, &vmexit->u.ipi.dmask);
+ pthread_cond_signal(&suspended_cpus_cond);
+ pthread_mutex_unlock(&suspended_cpus_mtx);
+
+ error = 0;
+ break;
+ default:
+ break;
+ }
+
+ return (error);
+}
+
static vmexit_handler_t handler[VM_EXITCODE_MAX] = {
[VM_EXITCODE_INOUT] = vmexit_inout,
[VM_EXITCODE_INOUT_STR] = vmexit_inout,
@@ -951,6 +1036,7 @@
[VM_EXITCODE_TASK_SWITCH] = vmexit_task_switch,
[VM_EXITCODE_DEBUG] = vmexit_debug,
[VM_EXITCODE_BPT] = vmexit_breakpoint,
+ [VM_EXITCODE_IPI] = vmexit_ipi,
};
static void
@@ -1151,6 +1237,9 @@
error = vm_set_capability(ctx, vcpu, VM_CAP_UNRESTRICTED_GUEST, 1);
assert(error == 0);
+ error = vm_set_capability(ctx, vcpu, VM_CAP_IPI_EXIT, 1);
+ assert(error == 0);
+
fbsdrun_addcpu(ctx, vcpu, rip, suspend);
}
@@ -1372,6 +1461,23 @@
if (argc > 1)
usage(1);
+ CPU_ZERO(&suspended_cpus);
+ CPU_ZERO(&zombie_cpus);
+
+ error = pthread_mutex_init(&suspended_cpus_mtx, NULL);
+ if (error) {
+ fprintf(stderr,
+ "Unable to initialize mutex for suspended cpus\n");
+ exit(4);
+ }
+
+ error = pthread_cond_init(&suspended_cpus_cond, NULL);
+ if (error) {
+ fprintf(stderr,
+ "Unable to initialize cond for suspended cpus\n");
+ exit(4);
+ }
+
#ifdef BHYVE_SNAPSHOT
if (restore_file != NULL) {
error = load_restore_file(restore_file, &rstate);
diff --git a/usr.sbin/bhyve/spinup_ap.c b/usr.sbin/bhyve/spinup_ap.c
--- a/usr.sbin/bhyve/spinup_ap.c
+++ b/usr.sbin/bhyve/spinup_ap.c
@@ -98,6 +98,9 @@
error = vm_set_capability(ctx, newcpu, VM_CAP_UNRESTRICTED_GUEST, 1);
assert(error == 0);
+ error = vm_set_capability(ctx, newcpu, VM_CAP_IPI_EXIT, 1);
+ assert(error == 0);
+
spinup_ap_realmode(ctx, newcpu, &rip);
vm_resume_cpu(ctx, newcpu);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Sep 7, 8:42 AM (3 h, 50 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
38398482
Default Alt Text
D35623.id110773.diff (12 KB)
Attached To
Mode
D35623: vmm: permit some IPIs to be handled by userspace
Attached
Detach File
Event Timeline
Log In to Comment