aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm
diff options
context:
space:
mode:
authorChristoffer Dall <christoffer.dall@linaro.org>2013-11-19 20:43:19 -0500
committerChristoffer Dall <christoffer.dall@linaro.org>2013-12-21 12:55:17 -0500
commit478a8237f656d86d25b3e4e4bf3c48f590156294 (patch)
treea75bc587c1589389525727f3035f74cbab9c4ea3 /arch/arm
parentbeb11fc71370bb49c58d7454d5d9c5a00a7cdb4b (diff)
arm: KVM: Don't return PSCI_INVAL if waitqueue is inactive
The current KVM implementation of PSCI returns INVALID_PARAMETERS if the waitqueue for the corresponding CPU is not active. This does not seem correct, since KVM should not care what the specific thread is doing, for example, user space may not have called KVM_RUN on this VCPU yet or the thread may be busy looping to user space because it received a signal; this is really up to the user space implementation. Instead we should check specifically that the CPU is marked as being turned off, regardless of the VCPU thread state, and if it is, we shall simply clear the pause flag on the CPU and wake up the thread if it happens to be blocked for us. Further, the implementation seems to be racy when executing multiple VCPU threads. There really isn't a reasonable user space programming scheme to ensure all secondary CPUs have reached kvm_vcpu_first_run_init before turning on the boot CPU. Therefore, set the pause flag on the vcpu at VCPU init time (which can reasonably be expected to be completed for all CPUs by user space before running any VCPUs) and clear both this flag and the feature (in case the feature can somehow get set again in the future) and ping the waitqueue on turning on a VCPU using PSCI. Reported-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/kvm/arm.c30
-rw-r--r--arch/arm/kvm/psci.c11
2 files changed, 25 insertions, 16 deletions
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
index 2a700e00528d..151eb9160482 100644
--- a/arch/arm/kvm/arm.c
+++ b/arch/arm/kvm/arm.c
@@ -478,15 +478,6 @@ static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu)
478 return ret; 478 return ret;
479 } 479 }
480 480
481 /*
482 * Handle the "start in power-off" case by calling into the
483 * PSCI code.
484 */
485 if (test_and_clear_bit(KVM_ARM_VCPU_POWER_OFF, vcpu->arch.features)) {
486 *vcpu_reg(vcpu, 0) = KVM_PSCI_FN_CPU_OFF;
487 kvm_psci_call(vcpu);
488 }
489
490 return 0; 481 return 0;
491} 482}
492 483
@@ -700,6 +691,24 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level,
700 return -EINVAL; 691 return -EINVAL;
701} 692}
702 693
694static int kvm_arch_vcpu_ioctl_vcpu_init(struct kvm_vcpu *vcpu,
695 struct kvm_vcpu_init *init)
696{
697 int ret;
698
699 ret = kvm_vcpu_set_target(vcpu, init);
700 if (ret)
701 return ret;
702
703 /*
704 * Handle the "start in power-off" case by marking the VCPU as paused.
705 */
706 if (__test_and_clear_bit(KVM_ARM_VCPU_POWER_OFF, vcpu->arch.features))
707 vcpu->arch.pause = true;
708
709 return 0;
710}
711
703long kvm_arch_vcpu_ioctl(struct file *filp, 712long kvm_arch_vcpu_ioctl(struct file *filp,
704 unsigned int ioctl, unsigned long arg) 713 unsigned int ioctl, unsigned long arg)
705{ 714{
@@ -713,8 +722,7 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
713 if (copy_from_user(&init, argp, sizeof(init))) 722 if (copy_from_user(&init, argp, sizeof(init)))
714 return -EFAULT; 723 return -EFAULT;
715 724
716 return kvm_vcpu_set_target(vcpu, &init); 725 return kvm_arch_vcpu_ioctl_vcpu_init(vcpu, &init);
717
718 } 726 }
719 case KVM_SET_ONE_REG: 727 case KVM_SET_ONE_REG:
720 case KVM_GET_ONE_REG: { 728 case KVM_GET_ONE_REG: {
diff --git a/arch/arm/kvm/psci.c b/arch/arm/kvm/psci.c
index 0881bf169fbc..448f60e8d23c 100644
--- a/arch/arm/kvm/psci.c
+++ b/arch/arm/kvm/psci.c
@@ -54,15 +54,15 @@ static unsigned long kvm_psci_vcpu_on(struct kvm_vcpu *source_vcpu)
54 } 54 }
55 } 55 }
56 56
57 if (!vcpu) 57 /*
58 * Make sure the caller requested a valid CPU and that the CPU is
59 * turned off.
60 */
61 if (!vcpu || !vcpu->arch.pause)
58 return KVM_PSCI_RET_INVAL; 62 return KVM_PSCI_RET_INVAL;
59 63
60 target_pc = *vcpu_reg(source_vcpu, 2); 64 target_pc = *vcpu_reg(source_vcpu, 2);
61 65
62 wq = kvm_arch_vcpu_wq(vcpu);
63 if (!waitqueue_active(wq))
64 return KVM_PSCI_RET_INVAL;
65
66 kvm_reset_vcpu(vcpu); 66 kvm_reset_vcpu(vcpu);
67 67
68 /* Gracefully handle Thumb2 entry point */ 68 /* Gracefully handle Thumb2 entry point */
@@ -79,6 +79,7 @@ static unsigned long kvm_psci_vcpu_on(struct kvm_vcpu *source_vcpu)
79 vcpu->arch.pause = false; 79 vcpu->arch.pause = false;
80 smp_mb(); /* Make sure the above is visible */ 80 smp_mb(); /* Make sure the above is visible */
81 81
82 wq = kvm_arch_vcpu_wq(vcpu);
82 wake_up_interruptible(wq); 83 wake_up_interruptible(wq);
83 84
84 return KVM_PSCI_RET_SUCCESS; 85 return KVM_PSCI_RET_SUCCESS;