On 11/03/2014 12:17 PM, Radim Krčmář wrote:
> 2014-10-31 12:05-0400, Wei Huang:
>> This patch implemented vPMU for AMD platform. The design piggybacks
>> on the existing Intel structs (kvm_pmu and kvm_pmc), but only uses
>> the parts of generic counters. The kvm_pmu_ops interface is also
>> initialized in this patch.
>>
>> Signed-off-by: Wei Huang <[email protected]>
>> ---
>>  arch/x86/kvm/pmu_amd.c | 332 
>> ++++++++++++++++++++++++++++++++++++++++++++++---
>>  1 file changed, 318 insertions(+), 14 deletions(-)
>>
>> diff --git a/arch/x86/kvm/pmu_amd.c b/arch/x86/kvm/pmu_amd.c
>> index 6d6e1c3..19cfe26 100644
>> --- a/arch/x86/kvm/pmu_amd.c
>> +++ b/arch/x86/kvm/pmu_amd.c
>> @@ -16,58 +16,362 @@
>>  #include <linux/types.h>
>>  #include <linux/kvm_host.h>
>>  #include <linux/perf_event.h>
>> -#include <asm/perf_event.h>
>>  #include "x86.h"
>>  #include "cpuid.h"
>>  #include "lapic.h"
>>  
>> -void amd_pmu_cpuid_update(struct kvm_vcpu *vcpu)
>> +/* duplicated from amd_perfmon_event_map, K7 and above should work */
>> +static struct kvm_event_hw_type_mapping amd_event_mapping[] = {
>> +    [0] = { 0x76, 0x00, PERF_COUNT_HW_CPU_CYCLES },
>> +    [1] = { 0xc0, 0x00, PERF_COUNT_HW_INSTRUCTIONS },
>> +    [2] = { 0x80, 0x00, PERF_COUNT_HW_CACHE_REFERENCES },
>> +    [3] = { 0x81, 0x00, PERF_COUNT_HW_CACHE_MISSES },
> 
>> +    [4] = { 0xc4, 0x00, PERF_COUNT_HW_BRANCH_INSTRUCTIONS },
>> +    [5] = { 0xc5, 0x00, PERF_COUNT_HW_BRANCH_MISSES },
> 
> amd_perfmon_event_map has 0xc2 and 0xc3.
Sorry, will fix.
> 
>> +    [6] = { 0xd0, 0x00, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND },
>> +    [7] = { 0xd1, 0x00, PERF_COUNT_HW_STALLED_CYCLES_BACKEND },
>> +};
>> +
>> +static inline bool pmc_is_gp(struct kvm_pmc *pmc)
>>  {
>> +    return pmc->type == KVM_PMC_GP;
>>  }
> 
> What is the benefit of having the same implementation in both files?
> 

No benefits. In fact I can remove it from AMD code because it is always
true for AMD. Will fix.

> I'd prefer to have it in pmu.h and I'll try to note all functions that
> look identical.  As always, you can ignore anything in parentheses,
> there are only few real issues with this patch.
> 
>>  
>> -int amd_pmu_check_pmc(struct kvm_vcpu *vcpu, unsigned pmc)
>> +static inline struct kvm_pmc *get_gp_pmc(struct kvm_pmu *pmu, u32 msr,
>> +                                     u32 base)
> 
> (pmu.h)

I will try to merge common ones into pmu.h in next re-spin. This applies
to the rest (pmu.h and pmu.c) in your replies.

> 
>>  {
>> -    return 1;
>> +    if (msr >= base && msr < base + pmu->nr_arch_gp_counters)
>> +            return &pmu->gp_counters[msr - base];
>> +
>> +    return NULL;
>>  }
>>  
>> -int amd_pmu_read_pmc(struct kvm_vcpu *vcpu, unsigned pmc, u64 *data)
>> +static inline u64 pmc_bitmask(struct kvm_pmc *pmc)
> 
> (pmu.h)
> 
>>  {
>> -    return 1;
>> +    struct kvm_pmu *pmu = &pmc->vcpu->arch.pmu;
>> +
>> +    return pmu->counter_bitmask[pmc->type];
>>  }
>>  
>> -int amd_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
>> +static struct kvm_pmc *global_idx_to_pmc(struct kvm_pmu *pmu, int idx)
>>  {
>> -    return 1;
>> +    return get_gp_pmc(pmu, MSR_K7_EVNTSEL0 + idx, MSR_K7_EVNTSEL0);
>>  }
>>  
>> -int amd_pmu_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data)
>> +void amd_deliver_pmi(struct kvm_vcpu *vcpu)
> 
> static.
> 
> (pmu.c, rename to deliver_pmi and reuse for intel.)
> 

Same here. I will try to merge them as much as I can.

>>  {
>> -    return 1;
>> +    if (vcpu->arch.apic)
>> +            kvm_apic_local_deliver(vcpu->arch.apic, APIC_LVTPC);
>>  }
>>  
>> -bool amd_is_pmu_msr(struct kvm_vcpu *vcpu, u32 msr)
>> +static void trigger_pmi(struct irq_work *irq_work)
> 
> (pmu.c)
> 
>>  {
>> -    return 0;
>> +    struct kvm_pmu *pmu = container_of(irq_work, struct kvm_pmu,
>> +                    irq_work);
>> +    struct kvm_vcpu *vcpu = container_of(pmu, struct kvm_vcpu,
>> +                    arch.pmu);
>> +
>> +    amd_deliver_pmi(vcpu);
>>  }
>>  
>> -void amd_deliver_pmi(struct kvm_vcpu *vcpu)
>> +static u64 read_pmc(struct kvm_pmc *pmc)
> 
> (pmu.c)
> 
>> +{
>> +    u64 counter, enabled, running, result, delta, prev;
>> +
>> +    counter = pmc->counter;
>> +    prev = pmc->counter;
>> +
>> +    if (pmc->perf_event) {
>> +            delta = perf_event_read_value(pmc->perf_event,
>> +                                          &enabled, &running);
>> +            counter += delta;
>> +    }
>> +
>> +    result = counter & pmc_bitmask(pmc);
>> +
>> +    return result;
>> +}
> 
> No.  The one intel uses is better.
> (This one looks like a relic from experimenting.)

Yes, I inserted some debug message using "result" variable. Will fix.

> 
>> +
>> +static void stop_counter(struct kvm_pmc *pmc)
> 
> (pmu.c)
> 
>> +{
>> +    if (pmc->perf_event) {
>> +            pmc->counter = read_pmc(pmc);
>> +            perf_event_release_kernel(pmc->perf_event);
>> +            pmc->perf_event = NULL;
>> +    }
>> +}
>> +
>> +static unsigned find_hw_type_event(struct kvm_pmu *pmu, u8 event_select,
>> +                            u8 unit_mask)
>> +{
> 
> (Intel calls this find_arch_event.)

I don't quite agree with existing naming, find_arch_event, in
pmu_intel.c. This function isn't directly related to arch event. It is
for mapping from perf_counters to linux_perf_event. But I only changed
the version pmu_amd.c though.

> 
>> +    int i;
>> +
>> +    for (i = 0; i < ARRAY_SIZE(amd_event_mapping); i++)
>> +            if (amd_event_mapping[i].eventsel == event_select
>> +                && amd_event_mapping[i].unit_mask == unit_mask)
> 
> (And it could be less extensible, but shorter:
>                       return amd_event_mapping[i].event_type;
>       }
>       return PERF_COUNT_HW_MAX;
>   }
> )

Will fix

> 
>> +                    break;
>> +
>> +    if (i == ARRAY_SIZE(amd_event_mapping))
>> +            return PERF_COUNT_HW_MAX;
>> +
>> +    return amd_event_mapping[i].event_type;
>> +}
>> +
>> +static void kvm_perf_overflow_intr(struct perf_event *perf_event,
>> +            struct perf_sample_data *data, struct pt_regs *regs)
>> +{
> 
> (pmu.c)
> 
>> +    struct kvm_pmc *pmc = perf_event->overflow_handler_context;
>> +    struct kvm_pmu *pmu = &pmc->vcpu->arch.pmu;
>> +
>> +    if (!test_and_set_bit(pmc->idx,
>> +                          (unsigned long *)&pmu->reprogram_pmi)) {
> 
> (The useless set_bit for intel is not that bad to keep them separate;
>  please mind my radicality when it comes to abstracting.)
> 
>> +            kvm_make_request(KVM_REQ_PMU, pmc->vcpu);
>> +
>> +            if (!kvm_is_in_guest())
>> +                    irq_work_queue(&pmc->vcpu->arch.pmu.irq_work);
>> +            else
>> +                    kvm_make_request(KVM_REQ_PMI, pmc->vcpu);
>> +    }
>> +}
>> +
>> +static void kvm_perf_overflow(struct perf_event *perf_event,
>> +                          struct perf_sample_data *data,
>> +                          struct pt_regs *regs)
>> +{
> 
> (pmu.c, ditto.)
> 
>> +    struct kvm_pmc *pmc = perf_event->overflow_handler_context;
>> +    struct kvm_pmu *pmu = &pmc->vcpu->arch.pmu;
>> +
>> +    if (!test_and_set_bit(pmc->idx,
>> +                          (unsigned long *)&pmu->reprogram_pmi)) {
>> +            kvm_make_request(KVM_REQ_PMU, pmc->vcpu);
>> +    }
>> +}
>> +
>> +static void reprogram_counter(struct kvm_pmc *pmc, u32 type,
>> +            unsigned config, bool exclude_user, bool exclude_kernel,
>> +            bool intr)
> 
> (Could be merged in my world, I'll comment on previous patch.)

Will try.

> 
>> +{
>> +    struct perf_event *event;
>> +    struct perf_event_attr attr = {
>> +            .type = type,
>> +            .size = sizeof(attr),
>> +            .pinned = true,
>> +            .exclude_idle = true,
>> +            .exclude_host = 1,
>> +            .exclude_user = exclude_user,
>> +            .exclude_kernel = exclude_kernel,
>> +            .config = config,
>> +    };
>> +
>> +    attr.sample_period = (-pmc->counter) & pmc_bitmask(pmc);
>> +
>> +    event = perf_event_create_kernel_counter(&attr, -1, current,
>> +                                             intr?kvm_perf_overflow_intr :
>> +                                             kvm_perf_overflow, pmc);
>> +    if (IS_ERR(event)) {
>> +            printk_once("kvm: pmu event creation failed %ld\n",
>> +                        PTR_ERR(event));
>> +            return;
>> +    }
>> +
>> +    pmc->perf_event = event;
>> +    clear_bit(pmc->idx, (unsigned long 
>> *)&pmc->vcpu->arch.pmu.reprogram_pmi);
>> +}
>> +
>> +
>> +static void reprogram_gp_counter(struct kvm_pmc *pmc, u64 eventsel)
>> +{
> 
> (Ditto.)
> 
>> +    unsigned config, type = PERF_TYPE_RAW;
>> +    u8 event_select, unit_mask;
>> +
>> +    if (eventsel & ARCH_PERFMON_EVENTSEL_PIN_CONTROL)
>> +            printk_once("kvm pmu: pin control bit is ignored\n");
>> +
>> +    pmc->eventsel = eventsel;
>> +
>> +    stop_counter(pmc);
>> +
>> +    if (!(eventsel & ARCH_PERFMON_EVENTSEL_ENABLE))
>> +            return;
>> +
>> +    event_select = eventsel & ARCH_PERFMON_EVENTSEL_EVENT;
>> +    unit_mask = (eventsel & ARCH_PERFMON_EVENTSEL_UMASK) >> 8;
>> +
>> +    if (!(eventsel & (ARCH_PERFMON_EVENTSEL_EDGE |
>> +                      ARCH_PERFMON_EVENTSEL_INV |
>> +                      ARCH_PERFMON_EVENTSEL_CMASK |
>> +                      HSW_IN_TX |
>> +                      HSW_IN_TX_CHECKPOINTED))) {
>> +            config = find_hw_type_event(&pmc->vcpu->arch.pmu, event_select,
>> +                                     unit_mask);
>> +            if (config != PERF_COUNT_HW_MAX)
>> +                    type = PERF_TYPE_HARDWARE;
>> +    }
>> +
>> +    if (type == PERF_TYPE_RAW)
>> +            config = eventsel & X86_RAW_EVENT_MASK;
>> +
>> +    reprogram_counter(pmc, type, config,
>> +                      !(eventsel & ARCH_PERFMON_EVENTSEL_USR),
>> +                      !(eventsel & ARCH_PERFMON_EVENTSEL_OS),
>> +                      eventsel & ARCH_PERFMON_EVENTSEL_INT);
>> +}
>> +
>> +static void reprogram_idx(struct kvm_pmu *pmu, int idx)
>>  {
>> +    struct kvm_pmc *pmc = global_idx_to_pmc(pmu, idx);
>> +
>> +    if (!pmc || !pmc_is_gp(pmc))
>> +            return;
>> +    reprogram_gp_counter(pmc, pmc->eventsel);
>>  }
>>  
>>  void amd_handle_pmu_event(struct kvm_vcpu *vcpu)
>>  {
> 
> static.
> 
> (pmu.c)
> 
>> +    struct kvm_pmu *pmu = &vcpu->arch.pmu;
>> +    u64 bitmask;
>> +    int bit;
>> +
>> +    bitmask = pmu->reprogram_pmi;
>> +
>> +    for_each_set_bit(bit, (unsigned long *)&bitmask, X86_PMC_IDX_MAX) {
>> +            struct kvm_pmc *pmc = global_idx_to_pmc(pmu, bit);
>> +
>> +            if (unlikely(!pmc || !pmc->perf_event)) {
>> +                    clear_bit(bit, (unsigned long *)&pmu->reprogram_pmi);
>> +                    continue;
>> +            }
>> +
>> +            reprogram_idx(pmu, bit);
>> +    }
>>  }
>>  
>>  void amd_pmu_reset(struct kvm_vcpu *vcpu)
> 
> static.
> 
>>  {
>> +    struct kvm_pmu *pmu = &vcpu->arch.pmu;
>> +    int i;
>> +
>> +    irq_work_sync(&pmu->irq_work);
>> +
>> +    for (i = 0; i < AMD64_NUM_COUNTERS; i++) {
>> +            struct kvm_pmc *pmc = &pmu->gp_counters[i];
>> +
>> +            stop_counter(pmc);
>> +            pmc->counter = pmc->eventsel = 0;
>> +    }
>> +}
>> +
>> +void amd_pmu_destroy(struct kvm_vcpu *vcpu)
> 
> static.
> 
>> +{
>> +    amd_pmu_reset(vcpu);
>> +}
>> +
>> +int amd_pmu_check_pmc(struct kvm_vcpu *vcpu, unsigned pmc)
> 
> static.
> 
>> +{
>> +    struct kvm_pmu *pmu = &vcpu->arch.pmu;
>> +
>> +    pmc &= ~(3u << 30);
> 
> (-1u >> 2.)
> 
>> +    return (pmc >= pmu->nr_arch_gp_counters);
>> +}
>> +
>> +int amd_pmu_read_pmc(struct kvm_vcpu *vcpu, unsigned pmc, u64 *data)
> 
> static.
> 
>> +{
>> +    struct kvm_pmu *pmu = &vcpu->arch.pmu;
>> +    struct kvm_pmc *counters;
>> +    u64 ctr;
>> +
>> +    pmc &= ~(3u << 30);
>> +    if (pmc >= pmu->nr_arch_gp_counters)
>> +            return 1;
>> +
>> +    counters = pmu->gp_counters;
>> +    ctr = read_pmc(&counters[pmc]);
>> +    *data = ctr;
>> +
>> +    return 0;
>> +}
>> +
>> +int amd_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
> 
> static.
> 
>> +{
>> +    struct kvm_pmu *pmu = &vcpu->arch.pmu;
>> +    struct kvm_pmc *pmc;
>> +    u32 index = msr_info->index;
>> +    u64 data = msr_info->data;
>> +
>> +    if ((pmc = get_gp_pmc(pmu, index, MSR_K7_PERFCTR0))) {
>> +            if (!msr_info->host_initiated)
>> +                    data = (s64)data;
>> +            pmc->counter += data - read_pmc(pmc);
>> +            return 0;
>> +    } else if ((pmc = get_gp_pmc(pmu, index, MSR_K7_EVNTSEL0))) {
>> +            if (data == pmc->eventsel)
>> +                    return 0;
>> +            if (!(data & pmu->reserved_bits)) {
>> +                    reprogram_gp_counter(pmc, data);
>> +                    return 0;
>> +            }
>> +    }
>> +
>> +    return 1;
>> +}
>> +
>> +int amd_pmu_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data)
> 
> static.
> 
>> +{
>> +    struct kvm_pmu *pmu = &vcpu->arch.pmu;
>> +    struct kvm_pmc *pmc;
>> +
>> +    if ((pmc = get_gp_pmc(pmu, index, MSR_K7_PERFCTR0))) {
>> +            *data = read_pmc(pmc);
>> +            return 0;
>> +    } else if ((pmc = get_gp_pmc(pmu, index, MSR_K7_EVNTSEL0))) {
>> +            *data = pmc->eventsel;
>> +            return 0;
>> +    }
>> +
>> +    return 1;
>> +}
>> +
>> +void amd_pmu_cpuid_update(struct kvm_vcpu *vcpu)
> 
> static.
> 
>> +{
>> +    struct kvm_pmu *pmu = &vcpu->arch.pmu;
>> +
>> +    pmu->nr_arch_gp_counters = 0;
>> +    pmu->nr_arch_fixed_counters = 0;
>> +    pmu->counter_bitmask[KVM_PMC_GP] = 0;
>> +    pmu->version = 0;
>> +    pmu->reserved_bits = 0xffffffff00200000ull;
>> +
> 
>> +    /* configuration */
>> +    pmu->nr_arch_gp_counters = 4;
> 
> AMD64_NUM_COUNTERS?

Yes, will fix.

> 
>> +    pmu->nr_arch_fixed_counters = 0;
>> +    pmu->counter_bitmask[KVM_PMC_GP] = ((u64)1 << 48) - 1;
> 
> amd_pmu_cpuid_update doesn't depend on cpuid at all.
> (I'd keep this function empty.)
> 
>>  }
>>  
>>  void amd_pmu_init(struct kvm_vcpu *vcpu)
> 
> static.
> 
>>  {
>> +    int i;
>> +    struct kvm_pmu *pmu = &vcpu->arch.pmu;
>> +
>> +    memset(pmu, 0, sizeof(*pmu));
>> +    for (i = 0; i < AMD64_NUM_COUNTERS ; i++) {
>> +            pmu->gp_counters[i].type = KVM_PMC_GP;
>> +            pmu->gp_counters[i].vcpu = vcpu;
>> +            pmu->gp_counters[i].idx = i;
>> +    }
>> +
>> +    init_irq_work(&pmu->irq_work, trigger_pmi);
>> +    amd_pmu_cpuid_update(vcpu);
>>  }
>>  
>> -void amd_pmu_destroy(struct kvm_vcpu *vcpu)
>> +bool amd_is_pmu_msr(struct kvm_vcpu *vcpu, u32 msr)
> 
> static.
> 
>>  {
>> +    struct kvm_pmu *pmu = &vcpu->arch.pmu;
>> +    int ret;
>> +
>> +    ret = get_gp_pmc(pmu, msr, MSR_K7_PERFCTR0) ||
>> +            get_gp_pmc(pmu, msr, MSR_K7_EVNTSEL0);
>> +
>> +    return ret;
>>  }
>>  
>>  struct kvm_pmu_ops amd_pmu_ops = {
>> -- 
>> 1.8.3.1
>>
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to