Commit c82d28cb authored by Marc Zyngier's avatar Marc Zyngier

KVM: arm64: PMU: Distinguish between 64bit counter and 64bit overflow

The PMU architecture makes a subtle difference between a 64bit
counter and a counter that has a 64bit overflow. This is for example
the case of the cycle counter, which can generate an overflow on
a 32bit boundary if PMCR_EL0.LC==0 despite the accumulation being
done on 64 bits.

Use this distinction in the few cases where it matters in the code,
as we will reuse this with PMUv3p5 long counters.
Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20221113163832.3154370-5-maz@kernel.org
parent acdd8a4e
...@@ -50,6 +50,11 @@ static u32 kvm_pmu_event_mask(struct kvm *kvm) ...@@ -50,6 +50,11 @@ static u32 kvm_pmu_event_mask(struct kvm *kvm)
* @select_idx: The counter index * @select_idx: The counter index
*/ */
static bool kvm_pmu_idx_is_64bit(struct kvm_vcpu *vcpu, u64 select_idx) static bool kvm_pmu_idx_is_64bit(struct kvm_vcpu *vcpu, u64 select_idx)
{
return (select_idx == ARMV8_PMU_CYCLE_IDX);
}
static bool kvm_pmu_idx_has_64bit_overflow(struct kvm_vcpu *vcpu, u64 select_idx)
{ {
return (select_idx == ARMV8_PMU_CYCLE_IDX && return (select_idx == ARMV8_PMU_CYCLE_IDX &&
__vcpu_sys_reg(vcpu, PMCR_EL0) & ARMV8_PMU_PMCR_LC); __vcpu_sys_reg(vcpu, PMCR_EL0) & ARMV8_PMU_PMCR_LC);
...@@ -57,7 +62,8 @@ static bool kvm_pmu_idx_is_64bit(struct kvm_vcpu *vcpu, u64 select_idx) ...@@ -57,7 +62,8 @@ static bool kvm_pmu_idx_is_64bit(struct kvm_vcpu *vcpu, u64 select_idx)
static bool kvm_pmu_counter_can_chain(struct kvm_vcpu *vcpu, u64 idx) static bool kvm_pmu_counter_can_chain(struct kvm_vcpu *vcpu, u64 idx)
{ {
return (!(idx & 1) && (idx + 1) < ARMV8_PMU_CYCLE_IDX); return (!(idx & 1) && (idx + 1) < ARMV8_PMU_CYCLE_IDX &&
!kvm_pmu_idx_has_64bit_overflow(vcpu, idx));
} }
static struct kvm_vcpu *kvm_pmc_to_vcpu(struct kvm_pmc *pmc) static struct kvm_vcpu *kvm_pmc_to_vcpu(struct kvm_pmc *pmc)
...@@ -97,7 +103,7 @@ u64 kvm_pmu_get_counter_value(struct kvm_vcpu *vcpu, u64 select_idx) ...@@ -97,7 +103,7 @@ u64 kvm_pmu_get_counter_value(struct kvm_vcpu *vcpu, u64 select_idx)
counter += perf_event_read_value(pmc->perf_event, &enabled, counter += perf_event_read_value(pmc->perf_event, &enabled,
&running); &running);
if (select_idx != ARMV8_PMU_CYCLE_IDX) if (!kvm_pmu_idx_is_64bit(vcpu, select_idx))
counter = lower_32_bits(counter); counter = lower_32_bits(counter);
return counter; return counter;
...@@ -423,6 +429,23 @@ static void kvm_pmu_counter_increment(struct kvm_vcpu *vcpu, ...@@ -423,6 +429,23 @@ static void kvm_pmu_counter_increment(struct kvm_vcpu *vcpu,
} }
} }
/* Compute the sample period for a given counter value */
static u64 compute_period(struct kvm_vcpu *vcpu, u64 select_idx, u64 counter)
{
u64 val;
if (kvm_pmu_idx_is_64bit(vcpu, select_idx)) {
if (!kvm_pmu_idx_has_64bit_overflow(vcpu, select_idx))
val = -(counter & GENMASK(31, 0));
else
val = (-counter) & GENMASK(63, 0);
} else {
val = (-counter) & GENMASK(31, 0);
}
return val;
}
/** /**
* When the perf event overflows, set the overflow status and inform the vcpu. * When the perf event overflows, set the overflow status and inform the vcpu.
*/ */
...@@ -442,10 +465,7 @@ static void kvm_pmu_perf_overflow(struct perf_event *perf_event, ...@@ -442,10 +465,7 @@ static void kvm_pmu_perf_overflow(struct perf_event *perf_event,
* Reset the sample period to the architectural limit, * Reset the sample period to the architectural limit,
* i.e. the point where the counter overflows. * i.e. the point where the counter overflows.
*/ */
period = -(local64_read(&perf_event->count)); period = compute_period(vcpu, idx, local64_read(&perf_event->count));
if (!kvm_pmu_idx_is_64bit(vcpu, pmc->idx))
period &= GENMASK(31, 0);
local64_set(&perf_event->hw.period_left, 0); local64_set(&perf_event->hw.period_left, 0);
perf_event->attr.sample_period = period; perf_event->attr.sample_period = period;
...@@ -571,14 +591,13 @@ static void kvm_pmu_create_perf_event(struct kvm_vcpu *vcpu, u64 select_idx) ...@@ -571,14 +591,13 @@ static void kvm_pmu_create_perf_event(struct kvm_vcpu *vcpu, u64 select_idx)
/* /*
* If counting with a 64bit counter, advertise it to the perf * If counting with a 64bit counter, advertise it to the perf
* code, carefully dealing with the initial sample period. * code, carefully dealing with the initial sample period
* which also depends on the overflow.
*/ */
if (kvm_pmu_idx_is_64bit(vcpu, select_idx)) { if (kvm_pmu_idx_is_64bit(vcpu, select_idx))
attr.config1 |= PERF_ATTR_CFG1_COUNTER_64BIT; attr.config1 |= PERF_ATTR_CFG1_COUNTER_64BIT;
attr.sample_period = (-counter) & GENMASK(63, 0);
} else { attr.sample_period = compute_period(vcpu, select_idx, counter);
attr.sample_period = (-counter) & GENMASK(31, 0);
}
event = perf_event_create_kernel_counter(&attr, -1, current, event = perf_event_create_kernel_counter(&attr, -1, current,
kvm_pmu_perf_overflow, pmc); kvm_pmu_perf_overflow, pmc);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment