diff --git a/sys/amd64/vmm/vmm.c b/sys/amd64/vmm/vmm.c --- a/sys/amd64/vmm/vmm.c +++ b/sys/amd64/vmm/vmm.c @@ -276,6 +276,7 @@ &vm_maxcpu, 0, "Maximum number of vCPUs"); static void vcpu_notify_event_locked(struct vcpu *vcpu, bool lapic_intr); +static int vm_handle_rendezvous(struct vcpu *vcpu, bool wait); /* global statistics */ VMM_STAT(VCPU_MIGRATIONS, "vcpu migration across host cpus"); @@ -997,7 +998,9 @@ vcpu_set_state_locked(struct vcpu *vcpu, enum vcpu_state newstate, bool from_idle) { + struct vcpu *rendezvous_vcpu; int error; + uint16_t maxcpus; vcpu_assert_locked(vcpu); @@ -1012,6 +1015,30 @@ vcpu_notify_event_locked(vcpu, false); VMM_CTR1(vcpu, "vcpu state change from %s to " "idle requested", vcpu_state2str(vcpu->state)); + /* + * If the vCPU we're requesting a state change for, is currently + * waiting for rendezvous completion, we're going to deadlock + * because we're waiting for the other vCPU to transition to IDLE + * and the other vCPU waits for us completing the rendezvous. + * Therefore, we have to handle any pending rendezvous. + */ + if (__predict_false( + atomic_load_ptr(&vcpu->vm->rendezvous_func) != + NULL)) { + vcpu_unlock(vcpu); + maxcpus = vm_get_maxcpus(vcpu->vm); + for (uint16_t i = 0; i < maxcpus; i++) { + rendezvous_vcpu = vm_vcpu(vcpu->vm, i); + if (rendezvous_vcpu == NULL) + continue; + vm_handle_rendezvous(rendezvous_vcpu, false); + } + vcpu_lock(vcpu); + /* Recheck vCPU state to avoid an unnecessary sleep. */ + if (vcpu->state == VCPU_IDLE) { + break; + } + } msleep_spin(&vcpu->state, &vcpu->mtx, "vmstat", hz); } } else { @@ -1084,7 +1111,7 @@ } static int -vm_handle_rendezvous(struct vcpu *vcpu) +vm_handle_rendezvous(struct vcpu *vcpu, bool wait) { struct vm *vm = vcpu->vm; struct thread *td; @@ -1112,6 +1139,12 @@ wakeup(&vm->rendezvous_func); break; } + + if (!wait) { + mtx_unlock(&vm->rendezvous_mtx); + return (0); + } + VMM_CTR0(vcpu, "Wait for rendezvous completion"); mtx_sleep(&vm->rendezvous_func, &vm->rendezvous_mtx, 0, "vmrndv", hz); @@ -1386,7 +1419,7 @@ } else { VMM_CTR0(vcpu, "Rendezvous during suspend"); vcpu_unlock(vcpu); - error = vm_handle_rendezvous(vcpu); + error = vm_handle_rendezvous(vcpu, true); vcpu_lock(vcpu); } } @@ -1602,7 +1635,7 @@ vioapic_process_eoi(vm, vme->u.ioapic_eoi.vector); break; case VM_EXITCODE_RENDEZVOUS: - error = vm_handle_rendezvous(vcpu); + error = vm_handle_rendezvous(vcpu, true); break; case VM_EXITCODE_HLT: intr_disabled = ((vme->u.hlt.rflags & PSL_I) == 0); @@ -2345,7 +2378,7 @@ */ VMM_CTR0(vcpu, "Rendezvous already in progress"); mtx_unlock(&vm->rendezvous_mtx); - error = vm_handle_rendezvous(vcpu); + error = vm_handle_rendezvous(vcpu, true); if (error != 0) return (error); goto restart; @@ -2369,7 +2402,7 @@ vcpu_notify_event(vm_vcpu(vm, i), false); } - return (vm_handle_rendezvous(vcpu)); + return (vm_handle_rendezvous(vcpu, true)); } struct vatpic *