KVM: arm64: Allow creating the PMU without the in-kernel GIC
authorChristoffer Dall <cdall@linaro.org>
Tue, 2 May 2017 11:41:02 +0000 (13:41 +0200)
committerChristoffer Dall <cdall@linaro.org>
Thu, 8 Jun 2017 14:59:44 +0000 (16:59 +0200)
Since we got support for devices in userspace which allows reporting the
PMU overflow output status to userspace, we should actually allow
creating the PMU on systems without an in-kernel irqchip, which in turn
requires us to slightly clarify error codes for the ABI and move things
around for the initialization phase.

Signed-off-by: Christoffer Dall <cdall@linaro.org>
Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
Documentation/virtual/kvm/devices/vcpu.txt
include/kvm/arm_pmu.h
virt/kvm/arm/arm.c
virt/kvm/arm/pmu.c

index 02f50686c41854f7fc2ce054a368ddaa4f8a9dc5..d7236a3e01dc227f32c27a10c135ca09bc1bfbe1 100644 (file)
@@ -16,7 +16,9 @@ Parameters: in kvm_device_attr.addr the address for PMU overflow interrupt is a
 Returns: -EBUSY: The PMU overflow interrupt is already set
          -ENXIO: The overflow interrupt not set when attempting to get it
          -ENODEV: PMUv3 not supported
-         -EINVAL: Invalid PMU overflow interrupt number supplied
+         -EINVAL: Invalid PMU overflow interrupt number supplied or
+                  trying to set the IRQ number without using an in-kernel
+                  irqchip.
 
 A value describing the PMUv3 (Performance Monitor Unit v3) overflow interrupt
 number for this vcpu. This interrupt could be a PPI or SPI, but the interrupt
@@ -25,11 +27,11 @@ all vcpus, while as an SPI it must be a separate number per vcpu.
 
 1.2 ATTRIBUTE: KVM_ARM_VCPU_PMU_V3_INIT
 Parameters: no additional parameter in kvm_device_attr.addr
-Returns: -ENODEV: PMUv3 not supported
-         -ENXIO: PMUv3 not properly configured as required prior to calling this
-                 attribute
+Returns: -ENODEV: PMUv3 not supported or GIC not initialized
+         -ENXIO: PMUv3 not properly configured or in-kernel irqchip not
+                 configured as required prior to calling this attribute
          -EBUSY: PMUv3 already initialized
 
-Request the initialization of the PMUv3.  This must be done after creating the
-in-kernel irqchip.  Creating a PMU with a userspace irqchip is currently not
-supported.
+Request the initialization of the PMUv3.  If using the PMUv3 with an in-kernel
+virtual GIC implementation, this must be done after initializing the in-kernel
+irqchip.
index 1ab4633adf4fd16971edfa32f197a9e7b24c2d7b..f6e030617467f4d98d0727d480ea3d5d1ac7a0bc 100644 (file)
@@ -35,6 +35,7 @@ struct kvm_pmu {
        int irq_num;
        struct kvm_pmc pmc[ARMV8_PMU_MAX_COUNTERS];
        bool ready;
+       bool created;
        bool irq_level;
 };
 
@@ -63,6 +64,7 @@ int kvm_arm_pmu_v3_get_attr(struct kvm_vcpu *vcpu,
                            struct kvm_device_attr *attr);
 int kvm_arm_pmu_v3_has_attr(struct kvm_vcpu *vcpu,
                            struct kvm_device_attr *attr);
+int kvm_arm_pmu_v3_enable(struct kvm_vcpu *vcpu);
 #else
 struct kvm_pmu {
 };
@@ -112,6 +114,10 @@ static inline int kvm_arm_pmu_v3_has_attr(struct kvm_vcpu *vcpu,
 {
        return -ENXIO;
 }
+static inline int kvm_arm_pmu_v3_enable(struct kvm_vcpu *vcpu)
+{
+       return 0;
+}
 #endif
 
 #endif
index cac5c2f2ddbabbd976c3e056c2d357ded0f0bd45..72816d3f23a7ce9a33d6d8432f4035c807061c55 100644 (file)
@@ -527,6 +527,10 @@ static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu)
        }
 
        ret = kvm_timer_enable(vcpu);
+       if (ret)
+               return ret;
+
+       ret = kvm_arm_pmu_v3_enable(vcpu);
 
        return ret;
 }
index 2451607dc25e3c5deaf94bec54361d2003fc2894..96a9cab3998216225912dbc94f2d5ff4555be2ae 100644 (file)
@@ -449,29 +449,50 @@ bool kvm_arm_support_pmu_v3(void)
        return (perf_num_counters() > 0);
 }
 
-static int kvm_arm_pmu_v3_init(struct kvm_vcpu *vcpu)
+int kvm_arm_pmu_v3_enable(struct kvm_vcpu *vcpu)
 {
-       if (!kvm_arm_support_pmu_v3())
-               return -ENODEV;
+       if (!vcpu->arch.pmu.created)
+               return 0;
 
        /*
-        * We currently require an in-kernel VGIC to use the PMU emulation,
-        * because we do not support forwarding PMU overflow interrupts to
-        * userspace yet.
+        * A valid interrupt configuration for the PMU is either to have a
+        * properly configured interrupt number and using an in-kernel
+        * irqchip, or to neither set an IRQ nor create an in-kernel irqchip.
         */
-       if (!irqchip_in_kernel(vcpu->kvm) || !vgic_initialized(vcpu->kvm))
+       if (kvm_arm_pmu_irq_initialized(vcpu) != irqchip_in_kernel(vcpu->kvm))
+               return -EINVAL;
+
+       kvm_pmu_vcpu_reset(vcpu);
+       vcpu->arch.pmu.ready = true;
+
+       return 0;
+}
+
+static int kvm_arm_pmu_v3_init(struct kvm_vcpu *vcpu)
+{
+       if (!kvm_arm_support_pmu_v3())
                return -ENODEV;
 
-       if (!test_bit(KVM_ARM_VCPU_PMU_V3, vcpu->arch.features) ||
-           !kvm_arm_pmu_irq_initialized(vcpu))
+       if (!test_bit(KVM_ARM_VCPU_PMU_V3, vcpu->arch.features))
                return -ENXIO;
 
-       if (kvm_arm_pmu_v3_ready(vcpu))
+       if (vcpu->arch.pmu.created)
                return -EBUSY;
 
-       kvm_pmu_vcpu_reset(vcpu);
-       vcpu->arch.pmu.ready = true;
+       if (irqchip_in_kernel(vcpu->kvm)) {
+               /*
+                * If using the PMU with an in-kernel virtual GIC
+                * implementation, we require the GIC to be already
+                * initialized when initializing the PMU.
+                */
+               if (!vgic_initialized(vcpu->kvm))
+                       return -ENODEV;
+
+               if (!kvm_arm_pmu_irq_initialized(vcpu))
+                       return -ENXIO;
+       }
 
+       vcpu->arch.pmu.created = true;
        return 0;
 }
 
@@ -510,6 +531,9 @@ int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
                int __user *uaddr = (int __user *)(long)attr->addr;
                int irq;
 
+               if (!irqchip_in_kernel(vcpu->kvm))
+                       return -EINVAL;
+
                if (!test_bit(KVM_ARM_VCPU_PMU_V3, vcpu->arch.features))
                        return -ENODEV;
 
@@ -544,6 +568,9 @@ int kvm_arm_pmu_v3_get_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
                int __user *uaddr = (int __user *)(long)attr->addr;
                int irq;
 
+               if (!irqchip_in_kernel(vcpu->kvm))
+                       return -EINVAL;
+
                if (!test_bit(KVM_ARM_VCPU_PMU_V3, vcpu->arch.features))
                        return -ENODEV;