KVM: PPC: Book3S: PR: Make svcpu -> vcpu store preempt savvy
authorAlexander Graf <agraf@suse.de>
Fri, 29 Nov 2013 01:29:00 +0000 (02:29 +0100)
committerAlexander Graf <agraf@suse.de>
Mon, 9 Dec 2013 08:41:39 +0000 (09:41 +0100)
As soon as we get back to our "highmem" handler in virtual address
space we may get preempted. Today the reason we can get preempted is
that we replay interrupts and all the lazy logic thinks we have
interrupts enabled.

However, it's not hard to make the code interruptible and that way
we can enable and handle interrupts even earlier.

This fixes random guest crashes that happened with CONFIG_PREEMPT=y
for me.

Signed-off-by: Alexander Graf <agraf@suse.de>
arch/powerpc/include/asm/kvm_book3s_asm.h
arch/powerpc/kvm/book3s_pr.c

index 0bd9348a4db91264d5e8a104f17ed77afcd5f815..412b2f389474a3c6c9b27120cf6f208fc5b25c8a 100644 (file)
@@ -106,6 +106,7 @@ struct kvmppc_host_state {
 };
 
 struct kvmppc_book3s_shadow_vcpu {
+       bool in_use;
        ulong gpr[14];
        u32 cr;
        u32 xer;
index fe14ca3dd171cd60b3c07191fa3bdd78a141e2a6..5b9e9063cfaf0c407be267a67ab26d35fcc84a93 100644 (file)
@@ -66,6 +66,7 @@ static void kvmppc_core_vcpu_load_pr(struct kvm_vcpu *vcpu, int cpu)
        struct kvmppc_book3s_shadow_vcpu *svcpu = svcpu_get(vcpu);
        memcpy(svcpu->slb, to_book3s(vcpu)->slb_shadow, sizeof(svcpu->slb));
        svcpu->slb_max = to_book3s(vcpu)->slb_shadow_max;
+       svcpu->in_use = 0;
        svcpu_put(svcpu);
 #endif
        vcpu->cpu = smp_processor_id();
@@ -78,6 +79,9 @@ static void kvmppc_core_vcpu_put_pr(struct kvm_vcpu *vcpu)
 {
 #ifdef CONFIG_PPC_BOOK3S_64
        struct kvmppc_book3s_shadow_vcpu *svcpu = svcpu_get(vcpu);
+       if (svcpu->in_use) {
+               kvmppc_copy_from_svcpu(vcpu, svcpu);
+       }
        memcpy(to_book3s(vcpu)->slb_shadow, svcpu->slb, sizeof(svcpu->slb));
        to_book3s(vcpu)->slb_shadow_max = svcpu->slb_max;
        svcpu_put(svcpu);
@@ -110,12 +114,26 @@ void kvmppc_copy_to_svcpu(struct kvmppc_book3s_shadow_vcpu *svcpu,
        svcpu->ctr = vcpu->arch.ctr;
        svcpu->lr  = vcpu->arch.lr;
        svcpu->pc  = vcpu->arch.pc;
+       svcpu->in_use = true;
 }
 
 /* Copy data touched by real-mode code from shadow vcpu back to vcpu */
 void kvmppc_copy_from_svcpu(struct kvm_vcpu *vcpu,
                            struct kvmppc_book3s_shadow_vcpu *svcpu)
 {
+       /*
+        * vcpu_put would just call us again because in_use hasn't
+        * been updated yet.
+        */
+       preempt_disable();
+
+       /*
+        * Maybe we were already preempted and synced the svcpu from
+        * our preempt notifiers. Don't bother touching this svcpu then.
+        */
+       if (!svcpu->in_use)
+               goto out;
+
        vcpu->arch.gpr[0] = svcpu->gpr[0];
        vcpu->arch.gpr[1] = svcpu->gpr[1];
        vcpu->arch.gpr[2] = svcpu->gpr[2];
@@ -139,6 +157,10 @@ void kvmppc_copy_from_svcpu(struct kvm_vcpu *vcpu,
        vcpu->arch.fault_dar   = svcpu->fault_dar;
        vcpu->arch.fault_dsisr = svcpu->fault_dsisr;
        vcpu->arch.last_inst   = svcpu->last_inst;
+       svcpu->in_use = false;
+
+out:
+       preempt_enable();
 }
 
 static int kvmppc_core_check_requests_pr(struct kvm_vcpu *vcpu)