summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorneel <neel@FreeBSD.org>2014-03-18 02:49:28 +0000
committerneel <neel@FreeBSD.org>2014-03-18 02:49:28 +0000
commit3818d6630581a43f9e1c797e9ae4ec07b565e3a8 (patch)
tree8a0d757d4b17d99b8295e0cc763172cdba5d68db
parentdfd2dcdc0190d527c03860c5a86c3ab56b9f5205 (diff)
downloadFreeBSD-src-3818d6630581a43f9e1c797e9ae4ec07b565e3a8.zip
FreeBSD-src-3818d6630581a43f9e1c797e9ae4ec07b565e3a8.tar.gz
When a vcpu is deactivated it must also unblock any rendezvous that may be
blocked on it. This is done by issuing a wakeup after clearing the 'vcpuid' from 'active_cpus'. Also, use CPU_CLR_ATOMIC() to guarantee visibility of the updated 'active_cpus' across all host cpus.
-rw-r--r--sys/amd64/vmm/vmm.c58
1 files changed, 46 insertions, 12 deletions
diff --git a/sys/amd64/vmm/vmm.c b/sys/amd64/vmm/vmm.c
index f1f6031..3b78623 100644
--- a/sys/amd64/vmm/vmm.c
+++ b/sys/amd64/vmm/vmm.c
@@ -130,7 +130,7 @@ struct vm {
* An active vcpu is one that has been started implicitly (BSP) or
* explicitly (AP) by sending it a startup ipi.
*/
- cpuset_t active_cpus;
+ volatile cpuset_t active_cpus;
struct mtx rendezvous_mtx;
cpuset_t rendezvous_req_cpus;
@@ -969,8 +969,12 @@ vm_handle_rendezvous(struct vm *vm, int vcpuid)
mtx_lock(&vm->rendezvous_mtx);
while (vm->rendezvous_func != NULL) {
+ /* 'rendezvous_req_cpus' must be a subset of 'active_cpus' */
+ CPU_AND(&vm->rendezvous_req_cpus, &vm->active_cpus);
+
if (vcpuid != -1 &&
- CPU_ISSET(vcpuid, &vm->rendezvous_req_cpus)) {
+ CPU_ISSET(vcpuid, &vm->rendezvous_req_cpus) &&
+ !CPU_ISSET(vcpuid, &vm->rendezvous_done_cpus)) {
VCPU_CTR0(vm, vcpuid, "Calling rendezvous func");
(*vm->rendezvous_func)(vm, vcpuid, vm->rendezvous_arg);
CPU_SET(vcpuid, &vm->rendezvous_done_cpus);
@@ -997,9 +1001,10 @@ vm_handle_hlt(struct vm *vm, int vcpuid, bool intr_disabled, bool *retu)
{
struct vm_exit *vmexit;
struct vcpu *vcpu;
- int t, timo;
+ int t, timo, spindown;
vcpu = &vm->vcpu[vcpuid];
+ spindown = 0;
vcpu_lock(vcpu);
@@ -1026,17 +1031,25 @@ vm_handle_hlt(struct vm *vm, int vcpuid, bool intr_disabled, bool *retu)
* Spindown the vcpu if the apic is disabled and it
* had entered the halted state.
*/
- *retu = true;
- vmexit = vm_exitinfo(vm, vcpuid);
- vmexit->exitcode = VM_EXITCODE_SPINDOWN_CPU;
- vm_deactivate_cpu(vm, vcpuid);
- VCPU_CTR0(vm, vcpuid, "spinning down cpu");
+ spindown = 1;
}
vcpu_require_state_locked(vcpu, VCPU_FROZEN);
vmm_stat_incr(vm, vcpuid, VCPU_IDLE_TICKS, ticks - t);
}
vcpu_unlock(vcpu);
+ /*
+ * Since 'vm_deactivate_cpu()' grabs a sleep mutex we must call it
+ * outside the confines of the vcpu spinlock.
+ */
+ if (spindown) {
+ *retu = true;
+ vmexit = vm_exitinfo(vm, vcpuid);
+ vmexit->exitcode = VM_EXITCODE_SPINDOWN_CPU;
+ vm_deactivate_cpu(vm, vcpuid);
+ VCPU_CTR0(vm, vcpuid, "spinning down cpu");
+ }
+
return (0);
}
@@ -1541,16 +1554,37 @@ void
vm_activate_cpu(struct vm *vm, int vcpuid)
{
- if (vcpuid >= 0 && vcpuid < VM_MAXCPU)
- CPU_SET(vcpuid, &vm->active_cpus);
+ KASSERT(vcpuid >= 0 && vcpuid < VM_MAXCPU,
+ ("vm_activate_cpu: invalid vcpuid %d", vcpuid));
+ KASSERT(!CPU_ISSET(vcpuid, &vm->active_cpus),
+ ("vm_activate_cpu: vcpuid %d is already active", vcpuid));
+
+ VCPU_CTR0(vm, vcpuid, "activated");
+ CPU_SET_ATOMIC(vcpuid, &vm->active_cpus);
}
static void
vm_deactivate_cpu(struct vm *vm, int vcpuid)
{
- if (vcpuid >= 0 && vcpuid < VM_MAXCPU)
- CPU_CLR(vcpuid, &vm->active_cpus);
+ KASSERT(vcpuid >= 0 && vcpuid < VM_MAXCPU,
+ ("vm_deactivate_cpu: invalid vcpuid %d", vcpuid));
+ KASSERT(CPU_ISSET(vcpuid, &vm->active_cpus),
+ ("vm_deactivate_cpu: vcpuid %d is not active", vcpuid));
+
+ VCPU_CTR0(vm, vcpuid, "deactivated");
+ CPU_CLR_ATOMIC(vcpuid, &vm->active_cpus);
+
+ /*
+ * If a vcpu rendezvous is in progress then it could be blocked
+ * on 'vcpuid' - unblock it before disappearing forever.
+ */
+ mtx_lock(&vm->rendezvous_mtx);
+ if (vm->rendezvous_func != NULL) {
+ VCPU_CTR0(vm, vcpuid, "unblock rendezvous after deactivation");
+ wakeup(&vm->rendezvous_func);
+ }
+ mtx_unlock(&vm->rendezvous_mtx);
}
cpuset_t
OpenPOWER on IntegriCloud