diff options
-rw-r--r-- | sys/amd64/include/vmm.h | 3 | ||||
-rw-r--r-- | sys/amd64/vmm/vmm.c | 75 | ||||
-rw-r--r-- | sys/amd64/vmm/vmm_dev.c | 10 | ||||
-rw-r--r-- | usr.sbin/bhyve/bhyverun.c | 15 |
4 files changed, 65 insertions, 38 deletions
diff --git a/sys/amd64/include/vmm.h b/sys/amd64/include/vmm.h index fab7e74..cc7c7ad 100644 --- a/sys/amd64/include/vmm.h +++ b/sys/amd64/include/vmm.h @@ -177,7 +177,8 @@ enum vcpu_state { VCPU_SLEEPING, }; -int vcpu_set_state(struct vm *vm, int vcpu, enum vcpu_state state); +int vcpu_set_state(struct vm *vm, int vcpu, enum vcpu_state state, + bool from_idle); enum vcpu_state vcpu_get_state(struct vm *vm, int vcpu, int *hostcpu); static int __inline diff --git a/sys/amd64/vmm/vmm.c b/sys/amd64/vmm/vmm.c index 2c86068..d727dd9 100644 --- a/sys/amd64/vmm/vmm.c +++ b/sys/amd64/vmm/vmm.c @@ -837,13 +837,35 @@ save_guest_fpustate(struct vcpu *vcpu) static VMM_STAT(VCPU_IDLE_TICKS, "number of ticks vcpu was idle"); static int -vcpu_set_state_locked(struct vcpu *vcpu, enum vcpu_state newstate) +vcpu_set_state_locked(struct vcpu *vcpu, enum vcpu_state newstate, + bool from_idle) { int error; vcpu_assert_locked(vcpu); /* + * State transitions from the vmmdev_ioctl() must always begin from + * the VCPU_IDLE state. This guarantees that there is only a single + * ioctl() operating on a vcpu at any point. + */ + if (from_idle) { + while (vcpu->state != VCPU_IDLE) + msleep_spin(&vcpu->state, &vcpu->mtx, "vmstat", hz); + } else { + KASSERT(vcpu->state != VCPU_IDLE, ("invalid transition from " + "vcpu idle state")); + } + + if (vcpu->state == VCPU_RUNNING) { + KASSERT(vcpu->hostcpu == curcpu, ("curcpu %d and hostcpu %d " + "mismatch for running vcpu", curcpu, vcpu->hostcpu)); + } else { + KASSERT(vcpu->hostcpu == NOCPU, ("Invalid hostcpu %d for a " + "vcpu that is not running", vcpu->hostcpu)); + } + + /* * The following state transitions are allowed: * IDLE -> FROZEN -> IDLE * FROZEN -> RUNNING -> FROZEN @@ -863,12 +885,19 @@ vcpu_set_state_locked(struct vcpu *vcpu, enum vcpu_state newstate) break; } - if (error == 0) - vcpu->state = newstate; + if (error) + return (EBUSY); + + vcpu->state = newstate; + if (newstate == VCPU_RUNNING) + vcpu->hostcpu = curcpu; else - error = EBUSY; + vcpu->hostcpu = NOCPU; - return (error); + if (newstate == VCPU_IDLE) + wakeup(&vcpu->state); + + return (0); } static void @@ -876,7 +905,7 @@ vcpu_require_state(struct vm *vm, int vcpuid, enum vcpu_state newstate) { int error; - if ((error = vcpu_set_state(vm, vcpuid, newstate)) != 0) + if ((error = vcpu_set_state(vm, vcpuid, newstate, false)) != 0) panic("Error %d setting state to %d\n", error, newstate); } @@ -885,7 +914,7 @@ vcpu_require_state_locked(struct vcpu *vcpu, enum vcpu_state newstate) { int error; - if ((error = vcpu_set_state_locked(vcpu, newstate)) != 0) + if ((error = vcpu_set_state_locked(vcpu, newstate, false)) != 0) panic("Error %d setting state to %d", error, newstate); } @@ -1131,9 +1160,7 @@ restart: restore_guest_fpustate(vcpu); vcpu_require_state(vm, vcpuid, VCPU_RUNNING); - vcpu->hostcpu = curcpu; error = VMRUN(vm->cookie, vcpuid, rip, pmap, &vm->rendezvous_func); - vcpu->hostcpu = NOCPU; vcpu_require_state(vm, vcpuid, VCPU_FROZEN); save_guest_fpustate(vcpu); @@ -1343,7 +1370,8 @@ vm_iommu_domain(struct vm *vm) } int -vcpu_set_state(struct vm *vm, int vcpuid, enum vcpu_state newstate) +vcpu_set_state(struct vm *vm, int vcpuid, enum vcpu_state newstate, + bool from_idle) { int error; struct vcpu *vcpu; @@ -1354,7 +1382,7 @@ vcpu_set_state(struct vm *vm, int vcpuid, enum vcpu_state newstate) vcpu = &vm->vcpu[vcpuid]; vcpu_lock(vcpu); - error = vcpu_set_state_locked(vcpu, newstate); + error = vcpu_set_state_locked(vcpu, newstate, from_idle); vcpu_unlock(vcpu); return (error); @@ -1475,19 +1503,28 @@ vcpu_notify_event(struct vm *vm, int vcpuid, bool lapic_intr) vcpu_lock(vcpu); hostcpu = vcpu->hostcpu; - if (hostcpu == NOCPU) { - if (vcpu->state == VCPU_SLEEPING) - wakeup_one(vcpu); - } else { - if (vcpu->state != VCPU_RUNNING) - panic("invalid vcpu state %d", vcpu->state); + if (vcpu->state == VCPU_RUNNING) { + KASSERT(hostcpu != NOCPU, ("vcpu running on invalid hostcpu")); if (hostcpu != curcpu) { - if (lapic_intr) + if (lapic_intr) { vlapic_post_intr(vcpu->vlapic, hostcpu, vmm_ipinum); - else + } else { ipi_cpu(hostcpu, vmm_ipinum); + } + } else { + /* + * If the 'vcpu' is running on 'curcpu' then it must + * be sending a notification to itself (e.g. SELF_IPI). + * The pending event will be picked up when the vcpu + * transitions back to guest context. + */ } + } else { + KASSERT(hostcpu == NOCPU, ("vcpu state %d not consistent " + "with hostcpu %d", vcpu->state, hostcpu)); + if (vcpu->state == VCPU_SLEEPING) + wakeup_one(vcpu); } vcpu_unlock(vcpu); } diff --git a/sys/amd64/vmm/vmm_dev.c b/sys/amd64/vmm/vmm_dev.c index 02847c2..4b5f691 100644 --- a/sys/amd64/vmm/vmm_dev.c +++ b/sys/amd64/vmm/vmm_dev.c @@ -197,7 +197,7 @@ vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag, goto done; } - error = vcpu_set_state(sc->vm, vcpu, VCPU_FROZEN); + error = vcpu_set_state(sc->vm, vcpu, VCPU_FROZEN, true); if (error) goto done; @@ -214,14 +214,14 @@ vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag, */ error = 0; for (vcpu = 0; vcpu < VM_MAXCPU; vcpu++) { - error = vcpu_set_state(sc->vm, vcpu, VCPU_FROZEN); + error = vcpu_set_state(sc->vm, vcpu, VCPU_FROZEN, true); if (error) break; } if (error) { while (--vcpu >= 0) - vcpu_set_state(sc->vm, vcpu, VCPU_IDLE); + vcpu_set_state(sc->vm, vcpu, VCPU_IDLE, false); goto done; } @@ -387,10 +387,10 @@ vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag, } if (state_changed == 1) { - vcpu_set_state(sc->vm, vcpu, VCPU_IDLE); + vcpu_set_state(sc->vm, vcpu, VCPU_IDLE, false); } else if (state_changed == 2) { for (vcpu = 0; vcpu < VM_MAXCPU; vcpu++) - vcpu_set_state(sc->vm, vcpu, VCPU_IDLE); + vcpu_set_state(sc->vm, vcpu, VCPU_IDLE, false); } done: diff --git a/usr.sbin/bhyve/bhyverun.c b/usr.sbin/bhyve/bhyverun.c index c1bc0d2..5cde323 100644 --- a/usr.sbin/bhyve/bhyverun.c +++ b/usr.sbin/bhyve/bhyverun.c @@ -493,19 +493,8 @@ vm_loop(struct vmctx *ctx, int vcpu, uint64_t rip) while (1) { error = vm_run(ctx, vcpu, rip, &vmexit[vcpu]); - if (error != 0) { - /* - * It is possible that 'vmmctl' or some other process - * has transitioned the vcpu to CANNOT_RUN state right - * before we tried to transition it to RUNNING. - * - * This is expected to be temporary so just retry. - */ - if (errno == EBUSY) - continue; - else - break; - } + if (error != 0) + break; prevcpu = vcpu; |