Add logic in the HLT exit handler to detect if the guest has put all vcpus

to sleep permanently by executing a HLT with interrupts disabled.

When this condition is detected the guest with be suspended with a reason of
VM_SUSPEND_HALT and the bhyve(8) process will exit.

Tested by executing "halt" inside a RHEL7-beta guest.

Discussed with:	grehan@
Reviewed by:	jhb@, tychon@
This commit is contained in:
Neel Natu 2014-05-02 00:33:56 +00:00
parent eab20bceca
commit e50ce2aa06
3 changed files with 43 additions and 7 deletions

View File

@ -33,6 +33,7 @@ enum vm_suspend_how {
VM_SUSPEND_NONE, VM_SUSPEND_NONE,
VM_SUSPEND_RESET, VM_SUSPEND_RESET,
VM_SUSPEND_POWEROFF, VM_SUSPEND_POWEROFF,
VM_SUSPEND_HALT,
VM_SUSPEND_LAST VM_SUSPEND_LAST
}; };

View File

@ -142,6 +142,8 @@ struct vm {
int suspend; int suspend;
volatile cpuset_t suspended_cpus; volatile cpuset_t suspended_cpus;
volatile cpuset_t halted_cpus;
}; };
static int vmm_initialized; static int vmm_initialized;
@ -1006,9 +1008,13 @@ vm_handle_hlt(struct vm *vm, int vcpuid, bool intr_disabled, bool *retu)
{ {
struct vcpu *vcpu; struct vcpu *vcpu;
const char *wmesg; const char *wmesg;
int t; int t, vcpu_halted, vm_halted;
KASSERT(!CPU_ISSET(vcpuid, &vm->halted_cpus), ("vcpu already halted"));
vcpu = &vm->vcpu[vcpuid]; vcpu = &vm->vcpu[vcpuid];
vcpu_halted = 0;
vm_halted = 0;
vcpu_lock(vcpu); vcpu_lock(vcpu);
while (1) { while (1) {
@ -1032,10 +1038,26 @@ vm_handle_hlt(struct vm *vm, int vcpuid, bool intr_disabled, bool *retu)
} }
} }
if (vlapic_enabled(vcpu->vlapic)) /*
wmesg = "vmidle"; * Some Linux guests implement "halt" by having all vcpus
else * execute HLT with interrupts disabled. 'halted_cpus' keeps
* track of the vcpus that have entered this state. When all
* vcpus enter the halted state the virtual machine is halted.
*/
if (intr_disabled) {
wmesg = "vmhalt"; wmesg = "vmhalt";
VCPU_CTR0(vm, vcpuid, "Halted");
if (!vcpu_halted) {
vcpu_halted = 1;
CPU_SET_ATOMIC(vcpuid, &vm->halted_cpus);
}
if (CPU_CMP(&vm->halted_cpus, &vm->active_cpus) == 0) {
vm_halted = 1;
break;
}
} else {
wmesg = "vmidle";
}
t = ticks; t = ticks;
vcpu_require_state_locked(vcpu, VCPU_SLEEPING); vcpu_require_state_locked(vcpu, VCPU_SLEEPING);
@ -1043,8 +1065,15 @@ vm_handle_hlt(struct vm *vm, int vcpuid, bool intr_disabled, bool *retu)
vcpu_require_state_locked(vcpu, VCPU_FROZEN); vcpu_require_state_locked(vcpu, VCPU_FROZEN);
vmm_stat_incr(vm, vcpuid, VCPU_IDLE_TICKS, ticks - t); vmm_stat_incr(vm, vcpuid, VCPU_IDLE_TICKS, ticks - t);
} }
if (vcpu_halted)
CPU_CLR_ATOMIC(vcpuid, &vm->halted_cpus);
vcpu_unlock(vcpu); vcpu_unlock(vcpu);
if (vm_halted)
vm_suspend(vm, VM_SUSPEND_HALT);
return (0); return (0);
} }

View File

@ -453,7 +453,6 @@ vmexit_suspend(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
enum vm_suspend_how how; enum vm_suspend_how how;
how = vmexit->u.suspended.how; how = vmexit->u.suspended.how;
assert(how == VM_SUSPEND_RESET || how == VM_SUSPEND_POWEROFF);
fbsdrun_deletecpu(ctx, *pvcpu); fbsdrun_deletecpu(ctx, *pvcpu);
@ -470,10 +469,17 @@ vmexit_suspend(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
} }
pthread_mutex_unlock(&resetcpu_mtx); pthread_mutex_unlock(&resetcpu_mtx);
if (how == VM_SUSPEND_RESET) switch (how) {
case VM_SUSPEND_RESET:
exit(0); exit(0);
if (how == VM_SUSPEND_POWEROFF) case VM_SUSPEND_POWEROFF:
exit(1); exit(1);
case VM_SUSPEND_HALT:
exit(2);
default:
fprintf(stderr, "vmexit_suspend: invalid reason %d\n", how);
exit(100);
}
return (0); /* NOTREACHED */ return (0); /* NOTREACHED */
} }