// SPDX-License-Identifier: GPL-2.0 
 | 
#include <linux/types.h> 
 | 
#include <linux/interrupt.h> 
 | 
  
 | 
#include <asm/xen/hypercall.h> 
 | 
#include <xen/page.h> 
 | 
#include <xen/interface/xen.h> 
 | 
#include <xen/interface/vcpu.h> 
 | 
#include <xen/interface/xenpmu.h> 
 | 
  
 | 
#include "xen-ops.h" 
 | 
#include "pmu.h" 
 | 
  
 | 
/* x86_pmu.handle_irq definition */ 
 | 
#include "../events/perf_event.h" 
 | 
  
 | 
#define XENPMU_IRQ_PROCESSING    1 
 | 
struct xenpmu { 
 | 
    /* Shared page between hypervisor and domain */ 
 | 
    struct xen_pmu_data *xenpmu_data; 
 | 
  
 | 
    uint8_t flags; 
 | 
}; 
 | 
static DEFINE_PER_CPU(struct xenpmu, xenpmu_shared); 
 | 
#define get_xenpmu_data()    (this_cpu_ptr(&xenpmu_shared)->xenpmu_data) 
 | 
#define get_xenpmu_flags()   (this_cpu_ptr(&xenpmu_shared)->flags) 
 | 
  
 | 
/* Macro for computing address of a PMU MSR bank */ 
 | 
#define field_offset(ctxt, field) ((void *)((uintptr_t)ctxt + \ 
 | 
                        (uintptr_t)ctxt->field)) 
 | 
  
 | 
/* AMD PMU */ 
 | 
#define F15H_NUM_COUNTERS   6 
 | 
#define F10H_NUM_COUNTERS   4 
 | 
  
 | 
static __read_mostly uint32_t amd_counters_base; 
 | 
static __read_mostly uint32_t amd_ctrls_base; 
 | 
static __read_mostly int amd_msr_step; 
 | 
static __read_mostly int k7_counters_mirrored; 
 | 
static __read_mostly int amd_num_counters; 
 | 
  
 | 
/* Intel PMU */ 
 | 
#define MSR_TYPE_COUNTER            0 
 | 
#define MSR_TYPE_CTRL               1 
 | 
#define MSR_TYPE_GLOBAL             2 
 | 
#define MSR_TYPE_ARCH_COUNTER       3 
 | 
#define MSR_TYPE_ARCH_CTRL          4 
 | 
  
 | 
/* Number of general pmu registers (CPUID.EAX[0xa].EAX[8..15]) */ 
 | 
#define PMU_GENERAL_NR_SHIFT        8 
 | 
#define PMU_GENERAL_NR_BITS         8 
 | 
#define PMU_GENERAL_NR_MASK         (((1 << PMU_GENERAL_NR_BITS) - 1) \ 
 | 
                     << PMU_GENERAL_NR_SHIFT) 
 | 
  
 | 
/* Number of fixed pmu registers (CPUID.EDX[0xa].EDX[0..4]) */ 
 | 
#define PMU_FIXED_NR_SHIFT          0 
 | 
#define PMU_FIXED_NR_BITS           5 
 | 
#define PMU_FIXED_NR_MASK           (((1 << PMU_FIXED_NR_BITS) - 1) \ 
 | 
                     << PMU_FIXED_NR_SHIFT) 
 | 
  
 | 
/* Alias registers (0x4c1) for full-width writes to PMCs */ 
 | 
#define MSR_PMC_ALIAS_MASK          (~(MSR_IA32_PERFCTR0 ^ MSR_IA32_PMC0)) 
 | 
  
 | 
#define INTEL_PMC_TYPE_SHIFT        30 
 | 
  
 | 
static __read_mostly int intel_num_arch_counters, intel_num_fixed_counters; 
 | 
  
 | 
  
 | 
static void xen_pmu_arch_init(void) 
 | 
{ 
 | 
    if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) { 
 | 
  
 | 
        switch (boot_cpu_data.x86) { 
 | 
        case 0x15: 
 | 
            amd_num_counters = F15H_NUM_COUNTERS; 
 | 
            amd_counters_base = MSR_F15H_PERF_CTR; 
 | 
            amd_ctrls_base = MSR_F15H_PERF_CTL; 
 | 
            amd_msr_step = 2; 
 | 
            k7_counters_mirrored = 1; 
 | 
            break; 
 | 
        case 0x10: 
 | 
        case 0x12: 
 | 
        case 0x14: 
 | 
        case 0x16: 
 | 
        default: 
 | 
            amd_num_counters = F10H_NUM_COUNTERS; 
 | 
            amd_counters_base = MSR_K7_PERFCTR0; 
 | 
            amd_ctrls_base = MSR_K7_EVNTSEL0; 
 | 
            amd_msr_step = 1; 
 | 
            k7_counters_mirrored = 0; 
 | 
            break; 
 | 
        } 
 | 
    } else { 
 | 
        uint32_t eax, ebx, ecx, edx; 
 | 
  
 | 
        cpuid(0xa, &eax, &ebx, &ecx, &edx); 
 | 
  
 | 
        intel_num_arch_counters = (eax & PMU_GENERAL_NR_MASK) >> 
 | 
            PMU_GENERAL_NR_SHIFT; 
 | 
        intel_num_fixed_counters = (edx & PMU_FIXED_NR_MASK) >> 
 | 
            PMU_FIXED_NR_SHIFT; 
 | 
    } 
 | 
} 
 | 
  
 | 
static inline uint32_t get_fam15h_addr(u32 addr) 
 | 
{ 
 | 
    switch (addr) { 
 | 
    case MSR_K7_PERFCTR0: 
 | 
    case MSR_K7_PERFCTR1: 
 | 
    case MSR_K7_PERFCTR2: 
 | 
    case MSR_K7_PERFCTR3: 
 | 
        return MSR_F15H_PERF_CTR + (addr - MSR_K7_PERFCTR0); 
 | 
    case MSR_K7_EVNTSEL0: 
 | 
    case MSR_K7_EVNTSEL1: 
 | 
    case MSR_K7_EVNTSEL2: 
 | 
    case MSR_K7_EVNTSEL3: 
 | 
        return MSR_F15H_PERF_CTL + (addr - MSR_K7_EVNTSEL0); 
 | 
    default: 
 | 
        break; 
 | 
    } 
 | 
  
 | 
    return addr; 
 | 
} 
 | 
  
 | 
static inline bool is_amd_pmu_msr(unsigned int msr) 
 | 
{ 
 | 
    if ((msr >= MSR_F15H_PERF_CTL && 
 | 
         msr < MSR_F15H_PERF_CTR + (amd_num_counters * 2)) || 
 | 
        (msr >= MSR_K7_EVNTSEL0 && 
 | 
         msr < MSR_K7_PERFCTR0 + amd_num_counters)) 
 | 
        return true; 
 | 
  
 | 
    return false; 
 | 
} 
 | 
  
 | 
static int is_intel_pmu_msr(u32 msr_index, int *type, int *index) 
 | 
{ 
 | 
    u32 msr_index_pmc; 
 | 
  
 | 
    switch (msr_index) { 
 | 
    case MSR_CORE_PERF_FIXED_CTR_CTRL: 
 | 
    case MSR_IA32_DS_AREA: 
 | 
    case MSR_IA32_PEBS_ENABLE: 
 | 
        *type = MSR_TYPE_CTRL; 
 | 
        return true; 
 | 
  
 | 
    case MSR_CORE_PERF_GLOBAL_CTRL: 
 | 
    case MSR_CORE_PERF_GLOBAL_STATUS: 
 | 
    case MSR_CORE_PERF_GLOBAL_OVF_CTRL: 
 | 
        *type = MSR_TYPE_GLOBAL; 
 | 
        return true; 
 | 
  
 | 
    default: 
 | 
  
 | 
        if ((msr_index >= MSR_CORE_PERF_FIXED_CTR0) && 
 | 
            (msr_index < MSR_CORE_PERF_FIXED_CTR0 + 
 | 
                 intel_num_fixed_counters)) { 
 | 
            *index = msr_index - MSR_CORE_PERF_FIXED_CTR0; 
 | 
            *type = MSR_TYPE_COUNTER; 
 | 
            return true; 
 | 
        } 
 | 
  
 | 
        if ((msr_index >= MSR_P6_EVNTSEL0) && 
 | 
            (msr_index < MSR_P6_EVNTSEL0 +  intel_num_arch_counters)) { 
 | 
            *index = msr_index - MSR_P6_EVNTSEL0; 
 | 
            *type = MSR_TYPE_ARCH_CTRL; 
 | 
            return true; 
 | 
        } 
 | 
  
 | 
        msr_index_pmc = msr_index & MSR_PMC_ALIAS_MASK; 
 | 
        if ((msr_index_pmc >= MSR_IA32_PERFCTR0) && 
 | 
            (msr_index_pmc < MSR_IA32_PERFCTR0 + 
 | 
                     intel_num_arch_counters)) { 
 | 
            *type = MSR_TYPE_ARCH_COUNTER; 
 | 
            *index = msr_index_pmc - MSR_IA32_PERFCTR0; 
 | 
            return true; 
 | 
        } 
 | 
        return false; 
 | 
    } 
 | 
} 
 | 
  
 | 
static bool xen_intel_pmu_emulate(unsigned int msr, u64 *val, int type, 
 | 
                  int index, bool is_read) 
 | 
{ 
 | 
    uint64_t *reg = NULL; 
 | 
    struct xen_pmu_intel_ctxt *ctxt; 
 | 
    uint64_t *fix_counters; 
 | 
    struct xen_pmu_cntr_pair *arch_cntr_pair; 
 | 
    struct xen_pmu_data *xenpmu_data = get_xenpmu_data(); 
 | 
    uint8_t xenpmu_flags = get_xenpmu_flags(); 
 | 
  
 | 
  
 | 
    if (!xenpmu_data || !(xenpmu_flags & XENPMU_IRQ_PROCESSING)) 
 | 
        return false; 
 | 
  
 | 
    ctxt = &xenpmu_data->pmu.c.intel; 
 | 
  
 | 
    switch (msr) { 
 | 
    case MSR_CORE_PERF_GLOBAL_OVF_CTRL: 
 | 
        reg = &ctxt->global_ovf_ctrl; 
 | 
        break; 
 | 
    case MSR_CORE_PERF_GLOBAL_STATUS: 
 | 
        reg = &ctxt->global_status; 
 | 
        break; 
 | 
    case MSR_CORE_PERF_GLOBAL_CTRL: 
 | 
        reg = &ctxt->global_ctrl; 
 | 
        break; 
 | 
    case MSR_CORE_PERF_FIXED_CTR_CTRL: 
 | 
        reg = &ctxt->fixed_ctrl; 
 | 
        break; 
 | 
    default: 
 | 
        switch (type) { 
 | 
        case MSR_TYPE_COUNTER: 
 | 
            fix_counters = field_offset(ctxt, fixed_counters); 
 | 
            reg = &fix_counters[index]; 
 | 
            break; 
 | 
        case MSR_TYPE_ARCH_COUNTER: 
 | 
            arch_cntr_pair = field_offset(ctxt, arch_counters); 
 | 
            reg = &arch_cntr_pair[index].counter; 
 | 
            break; 
 | 
        case MSR_TYPE_ARCH_CTRL: 
 | 
            arch_cntr_pair = field_offset(ctxt, arch_counters); 
 | 
            reg = &arch_cntr_pair[index].control; 
 | 
            break; 
 | 
        default: 
 | 
            return false; 
 | 
        } 
 | 
    } 
 | 
  
 | 
    if (reg) { 
 | 
        if (is_read) 
 | 
            *val = *reg; 
 | 
        else { 
 | 
            *reg = *val; 
 | 
  
 | 
            if (msr == MSR_CORE_PERF_GLOBAL_OVF_CTRL) 
 | 
                ctxt->global_status &= (~(*val)); 
 | 
        } 
 | 
        return true; 
 | 
    } 
 | 
  
 | 
    return false; 
 | 
} 
 | 
  
 | 
static bool xen_amd_pmu_emulate(unsigned int msr, u64 *val, bool is_read) 
 | 
{ 
 | 
    uint64_t *reg = NULL; 
 | 
    int i, off = 0; 
 | 
    struct xen_pmu_amd_ctxt *ctxt; 
 | 
    uint64_t *counter_regs, *ctrl_regs; 
 | 
    struct xen_pmu_data *xenpmu_data = get_xenpmu_data(); 
 | 
    uint8_t xenpmu_flags = get_xenpmu_flags(); 
 | 
  
 | 
    if (!xenpmu_data || !(xenpmu_flags & XENPMU_IRQ_PROCESSING)) 
 | 
        return false; 
 | 
  
 | 
    if (k7_counters_mirrored && 
 | 
        ((msr >= MSR_K7_EVNTSEL0) && (msr <= MSR_K7_PERFCTR3))) 
 | 
        msr = get_fam15h_addr(msr); 
 | 
  
 | 
    ctxt = &xenpmu_data->pmu.c.amd; 
 | 
    for (i = 0; i < amd_num_counters; i++) { 
 | 
        if (msr == amd_ctrls_base + off) { 
 | 
            ctrl_regs = field_offset(ctxt, ctrls); 
 | 
            reg = &ctrl_regs[i]; 
 | 
            break; 
 | 
        } else if (msr == amd_counters_base + off) { 
 | 
            counter_regs = field_offset(ctxt, counters); 
 | 
            reg = &counter_regs[i]; 
 | 
            break; 
 | 
        } 
 | 
        off += amd_msr_step; 
 | 
    } 
 | 
  
 | 
    if (reg) { 
 | 
        if (is_read) 
 | 
            *val = *reg; 
 | 
        else 
 | 
            *reg = *val; 
 | 
  
 | 
        return true; 
 | 
    } 
 | 
    return false; 
 | 
} 
 | 
  
 | 
bool pmu_msr_read(unsigned int msr, uint64_t *val, int *err) 
 | 
{ 
 | 
    if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) { 
 | 
        if (is_amd_pmu_msr(msr)) { 
 | 
            if (!xen_amd_pmu_emulate(msr, val, 1)) 
 | 
                *val = native_read_msr_safe(msr, err); 
 | 
            return true; 
 | 
        } 
 | 
    } else { 
 | 
        int type, index; 
 | 
  
 | 
        if (is_intel_pmu_msr(msr, &type, &index)) { 
 | 
            if (!xen_intel_pmu_emulate(msr, val, type, index, 1)) 
 | 
                *val = native_read_msr_safe(msr, err); 
 | 
            return true; 
 | 
        } 
 | 
    } 
 | 
  
 | 
    return false; 
 | 
} 
 | 
  
 | 
bool pmu_msr_write(unsigned int msr, uint32_t low, uint32_t high, int *err) 
 | 
{ 
 | 
    uint64_t val = ((uint64_t)high << 32) | low; 
 | 
  
 | 
    if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) { 
 | 
        if (is_amd_pmu_msr(msr)) { 
 | 
            if (!xen_amd_pmu_emulate(msr, &val, 0)) 
 | 
                *err = native_write_msr_safe(msr, low, high); 
 | 
            return true; 
 | 
        } 
 | 
    } else { 
 | 
        int type, index; 
 | 
  
 | 
        if (is_intel_pmu_msr(msr, &type, &index)) { 
 | 
            if (!xen_intel_pmu_emulate(msr, &val, type, index, 0)) 
 | 
                *err = native_write_msr_safe(msr, low, high); 
 | 
            return true; 
 | 
        } 
 | 
    } 
 | 
  
 | 
    return false; 
 | 
} 
 | 
  
 | 
static unsigned long long xen_amd_read_pmc(int counter) 
 | 
{ 
 | 
    struct xen_pmu_amd_ctxt *ctxt; 
 | 
    uint64_t *counter_regs; 
 | 
    struct xen_pmu_data *xenpmu_data = get_xenpmu_data(); 
 | 
    uint8_t xenpmu_flags = get_xenpmu_flags(); 
 | 
  
 | 
    if (!xenpmu_data || !(xenpmu_flags & XENPMU_IRQ_PROCESSING)) { 
 | 
        uint32_t msr; 
 | 
        int err; 
 | 
  
 | 
        msr = amd_counters_base + (counter * amd_msr_step); 
 | 
        return native_read_msr_safe(msr, &err); 
 | 
    } 
 | 
  
 | 
    ctxt = &xenpmu_data->pmu.c.amd; 
 | 
    counter_regs = field_offset(ctxt, counters); 
 | 
    return counter_regs[counter]; 
 | 
} 
 | 
  
 | 
static unsigned long long xen_intel_read_pmc(int counter) 
 | 
{ 
 | 
    struct xen_pmu_intel_ctxt *ctxt; 
 | 
    uint64_t *fixed_counters; 
 | 
    struct xen_pmu_cntr_pair *arch_cntr_pair; 
 | 
    struct xen_pmu_data *xenpmu_data = get_xenpmu_data(); 
 | 
    uint8_t xenpmu_flags = get_xenpmu_flags(); 
 | 
  
 | 
    if (!xenpmu_data || !(xenpmu_flags & XENPMU_IRQ_PROCESSING)) { 
 | 
        uint32_t msr; 
 | 
        int err; 
 | 
  
 | 
        if (counter & (1 << INTEL_PMC_TYPE_SHIFT)) 
 | 
            msr = MSR_CORE_PERF_FIXED_CTR0 + (counter & 0xffff); 
 | 
        else 
 | 
            msr = MSR_IA32_PERFCTR0 + counter; 
 | 
  
 | 
        return native_read_msr_safe(msr, &err); 
 | 
    } 
 | 
  
 | 
    ctxt = &xenpmu_data->pmu.c.intel; 
 | 
    if (counter & (1 << INTEL_PMC_TYPE_SHIFT)) { 
 | 
        fixed_counters = field_offset(ctxt, fixed_counters); 
 | 
        return fixed_counters[counter & 0xffff]; 
 | 
    } 
 | 
  
 | 
    arch_cntr_pair = field_offset(ctxt, arch_counters); 
 | 
    return arch_cntr_pair[counter].counter; 
 | 
} 
 | 
  
 | 
unsigned long long xen_read_pmc(int counter) 
 | 
{ 
 | 
    if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) 
 | 
        return xen_amd_read_pmc(counter); 
 | 
    else 
 | 
        return xen_intel_read_pmc(counter); 
 | 
} 
 | 
  
 | 
int pmu_apic_update(uint32_t val) 
 | 
{ 
 | 
    int ret; 
 | 
    struct xen_pmu_data *xenpmu_data = get_xenpmu_data(); 
 | 
  
 | 
    if (!xenpmu_data) { 
 | 
        pr_warn_once("%s: pmudata not initialized\n", __func__); 
 | 
        return -EINVAL; 
 | 
    } 
 | 
  
 | 
    xenpmu_data->pmu.l.lapic_lvtpc = val; 
 | 
  
 | 
    if (get_xenpmu_flags() & XENPMU_IRQ_PROCESSING) 
 | 
        return 0; 
 | 
  
 | 
    ret = HYPERVISOR_xenpmu_op(XENPMU_lvtpc_set, NULL); 
 | 
  
 | 
    return ret; 
 | 
} 
 | 
  
 | 
/* perf callbacks */ 
 | 
static int xen_is_in_guest(void) 
 | 
{ 
 | 
    const struct xen_pmu_data *xenpmu_data = get_xenpmu_data(); 
 | 
  
 | 
    if (!xenpmu_data) { 
 | 
        pr_warn_once("%s: pmudata not initialized\n", __func__); 
 | 
        return 0; 
 | 
    } 
 | 
  
 | 
    if (!xen_initial_domain() || (xenpmu_data->domain_id >= DOMID_SELF)) 
 | 
        return 0; 
 | 
  
 | 
    return 1; 
 | 
} 
 | 
  
 | 
static int xen_is_user_mode(void) 
 | 
{ 
 | 
    const struct xen_pmu_data *xenpmu_data = get_xenpmu_data(); 
 | 
  
 | 
    if (!xenpmu_data) { 
 | 
        pr_warn_once("%s: pmudata not initialized\n", __func__); 
 | 
        return 0; 
 | 
    } 
 | 
  
 | 
    if (xenpmu_data->pmu.pmu_flags & PMU_SAMPLE_PV) 
 | 
        return (xenpmu_data->pmu.pmu_flags & PMU_SAMPLE_USER); 
 | 
    else 
 | 
        return !!(xenpmu_data->pmu.r.regs.cpl & 3); 
 | 
} 
 | 
  
 | 
static unsigned long xen_get_guest_ip(void) 
 | 
{ 
 | 
    const struct xen_pmu_data *xenpmu_data = get_xenpmu_data(); 
 | 
  
 | 
    if (!xenpmu_data) { 
 | 
        pr_warn_once("%s: pmudata not initialized\n", __func__); 
 | 
        return 0; 
 | 
    } 
 | 
  
 | 
    return xenpmu_data->pmu.r.regs.ip; 
 | 
} 
 | 
  
 | 
static struct perf_guest_info_callbacks xen_guest_cbs = { 
 | 
    .is_in_guest            = xen_is_in_guest, 
 | 
    .is_user_mode           = xen_is_user_mode, 
 | 
    .get_guest_ip           = xen_get_guest_ip, 
 | 
}; 
 | 
  
 | 
/* Convert registers from Xen's format to Linux' */ 
 | 
static void xen_convert_regs(const struct xen_pmu_regs *xen_regs, 
 | 
                 struct pt_regs *regs, uint64_t pmu_flags) 
 | 
{ 
 | 
    regs->ip = xen_regs->ip; 
 | 
    regs->cs = xen_regs->cs; 
 | 
    regs->sp = xen_regs->sp; 
 | 
  
 | 
    if (pmu_flags & PMU_SAMPLE_PV) { 
 | 
        if (pmu_flags & PMU_SAMPLE_USER) 
 | 
            regs->cs |= 3; 
 | 
        else 
 | 
            regs->cs &= ~3; 
 | 
    } else { 
 | 
        if (xen_regs->cpl) 
 | 
            regs->cs |= 3; 
 | 
        else 
 | 
            regs->cs &= ~3; 
 | 
    } 
 | 
} 
 | 
  
 | 
irqreturn_t xen_pmu_irq_handler(int irq, void *dev_id) 
 | 
{ 
 | 
    int err, ret = IRQ_NONE; 
 | 
    struct pt_regs regs = {0}; 
 | 
    const struct xen_pmu_data *xenpmu_data = get_xenpmu_data(); 
 | 
    uint8_t xenpmu_flags = get_xenpmu_flags(); 
 | 
  
 | 
    if (!xenpmu_data) { 
 | 
        pr_warn_once("%s: pmudata not initialized\n", __func__); 
 | 
        return ret; 
 | 
    } 
 | 
  
 | 
    this_cpu_ptr(&xenpmu_shared)->flags = 
 | 
        xenpmu_flags | XENPMU_IRQ_PROCESSING; 
 | 
    xen_convert_regs(&xenpmu_data->pmu.r.regs, ®s, 
 | 
             xenpmu_data->pmu.pmu_flags); 
 | 
    if (x86_pmu.handle_irq(®s)) 
 | 
        ret = IRQ_HANDLED; 
 | 
  
 | 
    /* Write out cached context to HW */ 
 | 
    err = HYPERVISOR_xenpmu_op(XENPMU_flush, NULL); 
 | 
    this_cpu_ptr(&xenpmu_shared)->flags = xenpmu_flags; 
 | 
    if (err) { 
 | 
        pr_warn_once("%s: failed hypercall, err: %d\n", __func__, err); 
 | 
        return IRQ_NONE; 
 | 
    } 
 | 
  
 | 
    return ret; 
 | 
} 
 | 
  
 | 
bool is_xen_pmu(int cpu) 
 | 
{ 
 | 
    return (get_xenpmu_data() != NULL); 
 | 
} 
 | 
  
 | 
void xen_pmu_init(int cpu) 
 | 
{ 
 | 
    int err; 
 | 
    struct xen_pmu_params xp; 
 | 
    unsigned long pfn; 
 | 
    struct xen_pmu_data *xenpmu_data; 
 | 
  
 | 
    BUILD_BUG_ON(sizeof(struct xen_pmu_data) > PAGE_SIZE); 
 | 
  
 | 
    if (xen_hvm_domain()) 
 | 
        return; 
 | 
  
 | 
    xenpmu_data = (struct xen_pmu_data *)get_zeroed_page(GFP_KERNEL); 
 | 
    if (!xenpmu_data) { 
 | 
        pr_err("VPMU init: No memory\n"); 
 | 
        return; 
 | 
    } 
 | 
    pfn = virt_to_pfn(xenpmu_data); 
 | 
  
 | 
    xp.val = pfn_to_mfn(pfn); 
 | 
    xp.vcpu = cpu; 
 | 
    xp.version.maj = XENPMU_VER_MAJ; 
 | 
    xp.version.min = XENPMU_VER_MIN; 
 | 
    err = HYPERVISOR_xenpmu_op(XENPMU_init, &xp); 
 | 
    if (err) 
 | 
        goto fail; 
 | 
  
 | 
    per_cpu(xenpmu_shared, cpu).xenpmu_data = xenpmu_data; 
 | 
    per_cpu(xenpmu_shared, cpu).flags = 0; 
 | 
  
 | 
    if (cpu == 0) { 
 | 
        perf_register_guest_info_callbacks(&xen_guest_cbs); 
 | 
        xen_pmu_arch_init(); 
 | 
    } 
 | 
  
 | 
    return; 
 | 
  
 | 
fail: 
 | 
    if (err == -EOPNOTSUPP || err == -ENOSYS) 
 | 
        pr_info_once("VPMU disabled by hypervisor.\n"); 
 | 
    else 
 | 
        pr_info_once("Could not initialize VPMU for cpu %d, error %d\n", 
 | 
            cpu, err); 
 | 
    free_pages((unsigned long)xenpmu_data, 0); 
 | 
} 
 | 
  
 | 
void xen_pmu_finish(int cpu) 
 | 
{ 
 | 
    struct xen_pmu_params xp; 
 | 
  
 | 
    if (xen_hvm_domain()) 
 | 
        return; 
 | 
  
 | 
    xp.vcpu = cpu; 
 | 
    xp.version.maj = XENPMU_VER_MAJ; 
 | 
    xp.version.min = XENPMU_VER_MIN; 
 | 
  
 | 
    (void)HYPERVISOR_xenpmu_op(XENPMU_finish, &xp); 
 | 
  
 | 
    free_pages((unsigned long)per_cpu(xenpmu_shared, cpu).xenpmu_data, 0); 
 | 
    per_cpu(xenpmu_shared, cpu).xenpmu_data = NULL; 
 | 
} 
 |