Since many guests will never touch the PMU, they need not pay the cost
of context swapping those registers.

Use an enum to implement a simple state machine for PMU register
access. The PMU is either free or guest owned. We only need to context
swap if the PMU registers are guest owned. The PMU initially starts as
free and only transitions to guest owned if a guest has touched the
PMU registers.

Signed-off-by: Colton Lewis <[email protected]>
---
 arch/arm64/include/asm/kvm_host.h  |  1 +
 arch/arm64/include/asm/kvm_types.h |  6 +++++-
 arch/arm64/kvm/debug.c             |  5 +++--
 arch/arm64/kvm/pmu-direct.c        | 21 +++++++++++++++++++--
 arch/arm64/kvm/sys_regs.c          | 29 ++++++++++++++++-------------
 include/kvm/arm_pmu.h              |  8 ++++++++
 6 files changed, 52 insertions(+), 18 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h 
b/arch/arm64/include/asm/kvm_host.h
index 9c7e9b92dfbd3..32573b10d9c5b 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -1445,6 +1445,7 @@ static inline bool kvm_system_needs_idmapped_vectors(void)
        return cpus_have_final_cap(ARM64_SPECTRE_V3A);
 }
 
+void kvm_arm_setup_mdcr_el2(struct kvm_vcpu *vcpu);
 void kvm_init_host_debug_data(void);
 void kvm_debug_init_vhe(void);
 void kvm_vcpu_load_debug(struct kvm_vcpu *vcpu);
diff --git a/arch/arm64/include/asm/kvm_types.h 
b/arch/arm64/include/asm/kvm_types.h
index 9a126b9e2d7c9..4e39cbc80aa0b 100644
--- a/arch/arm64/include/asm/kvm_types.h
+++ b/arch/arm64/include/asm/kvm_types.h
@@ -4,5 +4,9 @@
 
 #define KVM_ARCH_NR_OBJS_PER_MEMORY_CACHE 40
 
-#endif /* _ASM_ARM64_KVM_TYPES_H */
+enum vcpu_pmu_register_access {
+       VCPU_PMU_ACCESS_FREE,
+       VCPU_PMU_ACCESS_GUEST_OWNED,
+};
 
+#endif /* _ASM_ARM64_KVM_TYPES_H */
diff --git a/arch/arm64/kvm/debug.c b/arch/arm64/kvm/debug.c
index c84321277d893..ab80325e67c5c 100644
--- a/arch/arm64/kvm/debug.c
+++ b/arch/arm64/kvm/debug.c
@@ -35,7 +35,7 @@ static int cpu_has_spe(u64 dfr0)
  *  - Self-hosted Trace Filter controls (MDCR_EL2_TTRF)
  *  - Self-hosted Trace (MDCR_EL2_TTRF/MDCR_EL2_E2TB)
  */
-static void kvm_arm_setup_mdcr_el2(struct kvm_vcpu *vcpu)
+void kvm_arm_setup_mdcr_el2(struct kvm_vcpu *vcpu)
 {
        preempt_disable();
 
@@ -63,7 +63,8 @@ static void kvm_arm_setup_mdcr_el2(struct kvm_vcpu *vcpu)
                 * fine grain traps and enforce counter access with
                 * HPMN.
                 */
-               if (!vcpu_on_unsupported_cpu(vcpu) &&
+               if (kvm_pmu_get_access(vcpu) == VCPU_PMU_ACCESS_GUEST_OWNED &&
+                   !vcpu_on_unsupported_cpu(vcpu) &&
                    cpus_have_final_cap(ARM64_HAS_FGT) &&
                    (cpus_have_final_cap(ARM64_HAS_HPMN0) || nr_guest_cntr > 
0)) {
                        vcpu->arch.mdcr_el2 &= ~(MDCR_EL2_TPM | MDCR_EL2_TPMCR 
| MDCR_EL2_HPMN);
diff --git a/arch/arm64/kvm/pmu-direct.c b/arch/arm64/kvm/pmu-direct.c
index 044f011c9c84b..bb1f3dca03869 100644
--- a/arch/arm64/kvm/pmu-direct.c
+++ b/arch/arm64/kvm/pmu-direct.c
@@ -269,7 +269,7 @@ void kvm_pmu_load(struct kvm_vcpu *vcpu)
         * If we aren't guest-owned then we know the guest isn't using
         * the PMU anyway, so no need to bother with the swap.
         */
-       if (!kvm_pmu_is_partitioned(vcpu->kvm))
+       if (vcpu->arch.pmu.access != VCPU_PMU_ACCESS_GUEST_OWNED)
                return;
 
        preempt_disable();
@@ -343,7 +343,7 @@ void kvm_pmu_put(struct kvm_vcpu *vcpu)
         * accessing the PMU anyway, so no need to bother with the
         * swap.
         */
-       if (!kvm_pmu_is_partitioned(vcpu->kvm))
+       if (vcpu->arch.pmu.access != VCPU_PMU_ACCESS_GUEST_OWNED)
                return;
 
        preempt_disable();
@@ -388,3 +388,20 @@ void kvm_pmu_put(struct kvm_vcpu *vcpu)
        kvm_pmu_set_guest_counters(pmu, 0);
        preempt_enable();
 }
+
+/**
+ * kvm_pmu_set_guest_owned() - Give PMU ownership to guest
+ * @vcpu: Pointer to vcpu struct
+ *
+ * Reconfigure the guest for physical access of PMU hardware if
+ * allowed. This means reconfiguring mdcr_el2.
+ *
+ */
+void kvm_pmu_set_guest_owned(struct kvm_vcpu *vcpu)
+{
+       if (kvm_pmu_is_partitioned(vcpu->kvm) &&
+           vcpu->arch.pmu.access == VCPU_PMU_ACCESS_FREE) {
+               vcpu->arch.pmu.access = VCPU_PMU_ACCESS_GUEST_OWNED;
+               kvm_arm_setup_mdcr_el2(vcpu);
+       }
+}
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 94572bc52c32a..f0eebeeb5ed96 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -1085,15 +1085,17 @@ static void pmu_reg_write(struct kvm_vcpu *vcpu, enum 
vcpu_sysreg reg, u64 val,
        u64 mask;
        int idx;
 
+       kvm_pmu_set_guest_owned(vcpu);
+
        switch (reg) {
        case PMCR_EL0:
-               if (kvm_pmu_is_partitioned(vcpu->kvm))
+               if (kvm_pmu_get_access(vcpu) == VCPU_PMU_ACCESS_GUEST_OWNED)
                        kvm_pmu_direct_pmcr_write(vcpu, val);
                else
                        kvm_pmu_handle_pmcr(vcpu, val);
                break;
        case PMSELR_EL0:
-               if (kvm_pmu_is_partitioned(vcpu->kvm))
+               if (kvm_pmu_get_access(vcpu) == VCPU_PMU_ACCESS_GUEST_OWNED)
                        write_sysreg(val, pmselr_el0);
                else
                        __vcpu_assign_sys_reg(vcpu, reg, val);
@@ -1101,7 +1103,7 @@ static void pmu_reg_write(struct kvm_vcpu *vcpu, enum 
vcpu_sysreg reg, u64 val,
        case PMEVCNTR0_EL0 ... PMCCNTR_EL0:
                idx = reg - PMEVCNTR0_EL0;
 
-               if (kvm_pmu_is_partitioned(vcpu->kvm)) {
+               if (kvm_pmu_get_access(vcpu) == VCPU_PMU_ACCESS_GUEST_OWNED) {
                        if (idx == ARMV8_PMU_CYCLE_IDX)
                                write_sysreg(val, pmccntr_el0);
                        else
@@ -1122,7 +1124,7 @@ static void pmu_reg_write(struct kvm_vcpu *vcpu, enum 
vcpu_sysreg reg, u64 val,
                }
                break;
        case PMCNTENSET_EL0:
-               if (kvm_pmu_is_partitioned(vcpu->kvm)) {
+               if (kvm_pmu_get_access(vcpu) == VCPU_PMU_ACCESS_GUEST_OWNED) {
                        if (set)
                                write_sysreg(val, pmcntenset_el0);
                        else
@@ -1139,7 +1141,7 @@ static void pmu_reg_write(struct kvm_vcpu *vcpu, enum 
vcpu_sysreg reg, u64 val,
                }
                break;
        case PMINTENSET_EL1:
-               if (kvm_pmu_is_partitioned(vcpu->kvm)) {
+               if (kvm_pmu_get_access(vcpu) == VCPU_PMU_ACCESS_GUEST_OWNED) {
                        if (set)
                                write_sysreg(val, pmintenset_el1);
                        else
@@ -1166,7 +1168,7 @@ static void pmu_reg_write(struct kvm_vcpu *vcpu, enum 
vcpu_sysreg reg, u64 val,
                local_irq_restore(flags);
                break;
        case PMUSERENR_EL0:
-               if (kvm_pmu_is_partitioned(vcpu->kvm))
+               if (kvm_pmu_get_access(vcpu) == VCPU_PMU_ACCESS_GUEST_OWNED)
                        write_sysreg(val, pmuserenr_el0);
                else
                        __vcpu_assign_sys_reg(vcpu, reg, val);
@@ -1175,7 +1177,6 @@ static void pmu_reg_write(struct kvm_vcpu *vcpu, enum 
vcpu_sysreg reg, u64 val,
                WARN_ON(1);
                break;
        }
-
 }
 
 /**
@@ -1192,15 +1193,17 @@ static u64 pmu_reg_read(struct kvm_vcpu *vcpu, enum 
vcpu_sysreg reg)
        u64 val = 0;
        int idx;
 
+       kvm_pmu_set_guest_owned(vcpu);
+
        switch (reg) {
        case PMCR_EL0:
-               if (kvm_pmu_is_partitioned(vcpu->kvm))
+               if (kvm_pmu_get_access(vcpu) == VCPU_PMU_ACCESS_GUEST_OWNED)
                        val = kvm_pmu_direct_pmcr_read(vcpu);
                else
                        val = kvm_vcpu_read_pmcr(vcpu);
                break;
        case PMSELR_EL0:
-               if (kvm_pmu_is_partitioned(vcpu->kvm))
+               if (kvm_pmu_get_access(vcpu) == VCPU_PMU_ACCESS_GUEST_OWNED)
                        val = read_sysreg(pmselr_el0);
                else
                        val = __vcpu_sys_reg(vcpu, reg);
@@ -1208,7 +1211,7 @@ static u64 pmu_reg_read(struct kvm_vcpu *vcpu, enum 
vcpu_sysreg reg)
        case PMEVCNTR0_EL0 ... PMCCNTR_EL0:
                idx = reg - PMEVCNTR0_EL0;
 
-               if (kvm_pmu_is_partitioned(vcpu->kvm)) {
+               if (kvm_pmu_get_access(vcpu) == VCPU_PMU_ACCESS_GUEST_OWNED) {
                        if (idx == ARMV8_PMU_CYCLE_IDX)
                                val = read_sysreg(pmccntr_el0);
                        else
@@ -1221,7 +1224,7 @@ static u64 pmu_reg_read(struct kvm_vcpu *vcpu, enum 
vcpu_sysreg reg)
                val = __vcpu_sys_reg(vcpu, reg);
                break;
        case PMCNTENSET_EL0:
-               if (kvm_pmu_is_partitioned(vcpu->kvm)) {
+               if (kvm_pmu_get_access(vcpu) == VCPU_PMU_ACCESS_GUEST_OWNED) {
                        val = read_sysreg(pmcntenset_el0);
                        val &= 
kvm_pmu_guest_counter_mask(vcpu->kvm->arch.arm_pmu);
                } else {
@@ -1229,7 +1232,7 @@ static u64 pmu_reg_read(struct kvm_vcpu *vcpu, enum 
vcpu_sysreg reg)
                }
                break;
        case PMINTENSET_EL1:
-               if (kvm_pmu_is_partitioned(vcpu->kvm)) {
+               if (kvm_pmu_get_access(vcpu) == VCPU_PMU_ACCESS_GUEST_OWNED) {
                        val = read_sysreg(pmintenset_el1);
                        val &= 
kvm_pmu_guest_counter_mask(vcpu->kvm->arch.arm_pmu);
                } else {
@@ -1240,7 +1243,7 @@ static u64 pmu_reg_read(struct kvm_vcpu *vcpu, enum 
vcpu_sysreg reg)
                val = __vcpu_sys_reg(vcpu, reg);
                break;
        case PMUSERENR_EL0:
-               if (kvm_pmu_is_partitioned(vcpu->kvm))
+               if (kvm_pmu_get_access(vcpu) == VCPU_PMU_ACCESS_GUEST_OWNED)
                        val = read_sysreg(pmuserenr_el0);
                else
                        val = __vcpu_sys_reg(vcpu, reg);
diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h
index 61f8d4ed35e10..b77ddb94dc99b 100644
--- a/include/kvm/arm_pmu.h
+++ b/include/kvm/arm_pmu.h
@@ -7,6 +7,7 @@
 #ifndef __ASM_ARM_KVM_PMU_H
 #define __ASM_ARM_KVM_PMU_H
 
+#include <linux/kvm_types.h>
 #include <linux/perf_event.h>
 #include <linux/perf/arm_pmuv3.h>
 #include <linux/perf/arm_pmu.h>
@@ -43,6 +44,7 @@ struct kvm_pmu {
        int irq_num;
        bool created;
        bool irq_level;
+       enum vcpu_pmu_register_access access;
 };
 
 struct arm_pmu_entry {
@@ -103,6 +105,9 @@ u64 kvm_pmu_host_counter_mask(struct arm_pmu *pmu);
 u64 kvm_pmu_guest_counter_mask(struct arm_pmu *pmu);
 void kvm_pmu_load(struct kvm_vcpu *vcpu);
 void kvm_pmu_put(struct kvm_vcpu *vcpu);
+void kvm_pmu_set_guest_owned(struct kvm_vcpu *vcpu);
+
+#define kvm_pmu_get_access(vcpu)       ((vcpu)->arch.pmu.access)
 
 /*
  * Updates the vcpu's view of the pmu events for this cpu.
@@ -147,6 +152,8 @@ static inline bool kvm_pmu_is_partitioned(struct kvm *kvm)
 {
        return false;
 }
+
+#define kvm_pmu_get_access(vcpu)       (VCPU_PMU_ACCESS_FREE)
 static inline void kvm_pmu_direct_pmcr_write(struct kvm_vcpu *vcpu, u64 val) {}
 static inline u64 kvm_pmu_direct_pmcr_read(struct kvm_vcpu *vcpu)
 {
@@ -154,6 +161,7 @@ static inline u64 kvm_pmu_direct_pmcr_read(struct kvm_vcpu 
*vcpu)
 }
 static inline void kvm_pmu_load(struct kvm_vcpu *vcpu) {}
 static inline void kvm_pmu_put(struct kvm_vcpu *vcpu) {}
+static inline void kvm_pmu_set_guest_owned(struct kvm_vcpu *vcpu) {}
 static inline void kvm_pmu_set_counter_value(struct kvm_vcpu *vcpu,
                                             u64 select_idx, u64 val) {}
 static inline void kvm_pmu_set_counter_value_user(struct kvm_vcpu *vcpu,
-- 
2.54.0.1136.gdb2ca164c4-goog


Reply via email to