[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH v23 12/15] x86/VPMU: Handle PMU interrupts for PV(H) guests
Add support for handling PMU interrupts for PV(H) guests. VPMU for the interrupted VCPU is unloaded until the guest issues XENPMU_flush hypercall. This allows the guest to access PMU MSR values that are stored in VPMU context which is shared between hypervisor and domain, thus avoiding traps to hypervisor. Since the interrupt handler may now force VPMU context save (i.e. set VPMU_CONTEXT_SAVE flag) we need to make changes to amd_vpmu_save() which until now expected this flag to be set only when the counters were stopped. Signed-off-by: Boris Ostrovsky <boris.ostrovsky@xxxxxxxxxx> Acked-by: Daniel De Graaf <dgdegra@xxxxxxxxxxxxx> --- Changes in v23: * Define xen_pmu_arch unions that were deferred in patch 2 * Verify MSRs that are loaded from guest in amd_vpmu_load() xen/arch/x86/hvm/svm/vpmu.c | 67 +++++++--- xen/arch/x86/hvm/vmx/vpmu_core2.c | 91 ++++++++++++- xen/arch/x86/hvm/vpmu.c | 265 +++++++++++++++++++++++++++++++++----- xen/include/asm-x86/hvm/vpmu.h | 10 +- xen/include/public/arch-x86/pmu.h | 33 ++++- xen/include/public/pmu.h | 2 + xen/include/xsm/dummy.h | 4 +- xen/xsm/flask/hooks.c | 2 + 8 files changed, 412 insertions(+), 62 deletions(-) diff --git a/xen/arch/x86/hvm/svm/vpmu.c b/xen/arch/x86/hvm/svm/vpmu.c index eeef25d..9cead0e 100644 --- a/xen/arch/x86/hvm/svm/vpmu.c +++ b/xen/arch/x86/hvm/svm/vpmu.c @@ -45,6 +45,7 @@ static unsigned int __read_mostly num_counters; static const u32 __read_mostly *counters; static const u32 __read_mostly *ctrls; static bool_t __read_mostly k7_counters_mirrored; +static unsigned int __read_mostly ctxt_sz; #define F10H_NUM_COUNTERS 4 #define F15H_NUM_COUNTERS 6 @@ -201,27 +202,56 @@ static inline void context_load(struct vcpu *v) } } -static void amd_vpmu_load(struct vcpu *v) +static int amd_vpmu_load(struct vcpu *v, bool_t from_guest) { struct vpmu_struct *vpmu = vcpu_vpmu(v); - struct xen_pmu_amd_ctxt *ctxt = vpmu->context; - uint64_t *ctrl_regs = vpmu_reg_pointer(ctxt, ctrls); + struct xen_pmu_amd_ctxt *ctxt; + uint64_t *ctrl_regs; + unsigned int i; vpmu_reset(vpmu, VPMU_FROZEN); - if ( vpmu_is_set(vpmu, VPMU_CONTEXT_LOADED) ) + if ( !from_guest && vpmu_is_set(vpmu, VPMU_CONTEXT_LOADED) ) { - unsigned int i; + ctxt = vpmu->context; + ctrl_regs = vpmu_reg_pointer(ctxt, ctrls); for ( i = 0; i < num_counters; i++ ) wrmsrl(ctrls[i], ctrl_regs[i]); - return; + return 0; + } + + if ( from_guest ) + { + unsigned int num_enabled = 0; + + ASSERT(!is_hvm_vcpu(v)); + + ctxt = &vpmu->xenpmu_data->pmu.c.amd; + ctrl_regs = vpmu_reg_pointer(ctxt, ctrls); + for ( i = 0; i < num_counters; i++ ) + { + if ( (ctrl_regs[i] & CTRL_RSVD_MASK) != ctrl_rsvd[i] ) + return -EINVAL; + + if ( is_pmu_enabled(ctrl_regs[i]) ) + num_enabled++; + } + + if ( num_enabled ) + vpmu_set(vpmu, VPMU_RUNNING); + else + vpmu_reset(vpmu, VPMU_RUNNING); + + memcpy(vpmu->context, &vpmu->xenpmu_data->pmu.c.amd, ctxt_sz); } vpmu_set(vpmu, VPMU_CONTEXT_LOADED); context_load(v); + + return 0; } static inline void context_save(struct vcpu *v) @@ -236,22 +266,17 @@ static inline void context_save(struct vcpu *v) rdmsrl(counters[i], counter_regs[i]); } -static int amd_vpmu_save(struct vcpu *v) +static int amd_vpmu_save(struct vcpu *v, bool_t to_guest) { struct vpmu_struct *vpmu = vcpu_vpmu(v); unsigned int i; - /* - * Stop the counters. If we came here via vpmu_save_force (i.e. - * when VPMU_CONTEXT_SAVE is set) counters are already stopped. - */ + for ( i = 0; i < num_counters; i++ ) + wrmsrl(ctrls[i], 0); + if ( !vpmu_is_set(vpmu, VPMU_CONTEXT_SAVE) ) { vpmu_set(vpmu, VPMU_FROZEN); - - for ( i = 0; i < num_counters; i++ ) - wrmsrl(ctrls[i], 0); - return 0; } @@ -264,6 +289,12 @@ static int amd_vpmu_save(struct vcpu *v) has_hvm_container_vcpu(v) && is_msr_bitmap_on(vpmu) ) amd_vpmu_unset_msr_bitmap(v); + if ( to_guest ) + { + ASSERT(!is_hvm_vcpu(v)); + memcpy(&vpmu->xenpmu_data->pmu.c.amd, vpmu->context, ctxt_sz); + } + return 1; } @@ -451,8 +482,7 @@ int svm_vpmu_initialise(struct vcpu *v) if ( !counters ) return -EINVAL; - ctxt = xzalloc_bytes(sizeof(*ctxt) + - 2 * sizeof(uint64_t) * num_counters); + ctxt = xzalloc_bytes(ctxt_sz); if ( !ctxt ) { printk(XENLOG_G_WARNING "Insufficient memory for PMU, " @@ -516,6 +546,9 @@ int __init amd_vpmu_init(void) ctrl_rsvd[i] &= CTRL_RSVD_MASK; } + ctxt_sz = sizeof(struct xen_pmu_amd_ctxt) + + 2 * sizeof(uint64_t) * num_counters; + return 0; } diff --git a/xen/arch/x86/hvm/vmx/vpmu_core2.c b/xen/arch/x86/hvm/vmx/vpmu_core2.c index e8d9bb4..52a945f 100644 --- a/xen/arch/x86/hvm/vmx/vpmu_core2.c +++ b/xen/arch/x86/hvm/vmx/vpmu_core2.c @@ -90,6 +90,9 @@ static unsigned int __read_mostly arch_pmc_cnt, fixed_pmc_cnt; static uint64_t __read_mostly fixed_ctrl_mask, fixed_counters_mask; static uint64_t __read_mostly global_ovf_ctrl_mask; +/* VPMU context size */ +static unsigned int __read_mostly ctxt_sz; + /* * QUIRK to workaround an issue on various family 6 cpus. * The issue leads to endless PMC interrupt loops on the processor. @@ -312,7 +315,7 @@ static inline void __core2_vpmu_save(struct vcpu *v) rdmsrl(MSR_CORE_PERF_GLOBAL_STATUS, core2_vpmu_cxt->global_status); } -static int core2_vpmu_save(struct vcpu *v) +static int core2_vpmu_save(struct vcpu *v, bool_t to_guest) { struct vpmu_struct *vpmu = vcpu_vpmu(v); @@ -329,6 +332,12 @@ static int core2_vpmu_save(struct vcpu *v) has_hvm_container_vcpu(v) && cpu_has_vmx_msr_bitmap ) core2_vpmu_unset_msr_bitmap(v->arch.hvm_vmx.msr_bitmap); + if ( to_guest ) + { + ASSERT(!is_hvm_vcpu(v)); + memcpy(&vpmu->xenpmu_data->pmu.c.intel, vpmu->context, ctxt_sz); + } + return 1; } @@ -365,16 +374,83 @@ static inline void __core2_vpmu_load(struct vcpu *v) } } -static void core2_vpmu_load(struct vcpu *v) +static int core2_vpmu_verify(struct vcpu *v) +{ + unsigned int i; + struct vpmu_struct *vpmu = vcpu_vpmu(v); + struct xen_pmu_intel_ctxt *core2_vpmu_cxt = + &v->arch.vpmu.xenpmu_data->pmu.c.intel; + uint64_t *fixed_counters = vpmu_reg_pointer(core2_vpmu_cxt, fixed_counters); + struct xen_pmu_cntr_pair *xen_pmu_cntr_pair = + vpmu_reg_pointer(core2_vpmu_cxt, arch_counters); + uint64_t fixed_ctrl; + uint64_t *priv_context = vpmu->priv_context; + uint64_t enabled_cntrs = 0; + + if ( core2_vpmu_cxt->global_ovf_ctrl & global_ovf_ctrl_mask ) + return -EINVAL; + + fixed_ctrl = core2_vpmu_cxt->fixed_ctrl; + if ( fixed_ctrl & fixed_ctrl_mask ) + return -EINVAL; + + for ( i = 0; i < fixed_pmc_cnt; i++ ) + { + if ( fixed_counters[i] & fixed_counters_mask ) + return -EINVAL; + if ( (fixed_ctrl >> (i * FIXED_CTR_CTRL_BITS)) & 3 ) + enabled_cntrs |= (1ULL << i); + } + enabled_cntrs <<= 32; + + for ( i = 0; i < arch_pmc_cnt; i++ ) + { + uint64_t control = xen_pmu_cntr_pair[i].control; + + if ( control & ARCH_CTRL_MASK ) + return -EINVAL; + if ( control & ARCH_CNTR_ENABLED ) + enabled_cntrs |= (1ULL << i); + } + + if ( vpmu_is_set(vcpu_vpmu(v), VPMU_CPU_HAS_DS) && + !is_canonical_address(core2_vpmu_cxt->ds_area) ) + return -EINVAL; + + if ( (core2_vpmu_cxt->global_ctrl & enabled_cntrs) || + (core2_vpmu_cxt->ds_area != 0) ) + vpmu_set(vpmu, VPMU_RUNNING); + else + vpmu_reset(vpmu, VPMU_RUNNING); + + *priv_context = enabled_cntrs; + + return 0; +} + +static int core2_vpmu_load(struct vcpu *v, bool_t from_guest) { struct vpmu_struct *vpmu = vcpu_vpmu(v); if ( vpmu_is_set(vpmu, VPMU_CONTEXT_LOADED) ) - return; + return 0; + + if ( from_guest ) + { + int ret; + + ASSERT(!is_hvm_vcpu(v)); + ret = core2_vpmu_verify(v); + if ( ret ) + return ret; + memcpy(vpmu->context, &v->arch.vpmu.xenpmu_data->pmu.c.intel, ctxt_sz); + } vpmu_set(vpmu, VPMU_CONTEXT_LOADED); __core2_vpmu_load(v); + + return 0; } static int core2_vpmu_alloc_resource(struct vcpu *v) @@ -397,10 +473,7 @@ static int core2_vpmu_alloc_resource(struct vcpu *v) vmx_write_guest_msr(MSR_CORE_PERF_GLOBAL_CTRL, 0); } - core2_vpmu_cxt = xzalloc_bytes(sizeof(*core2_vpmu_cxt) + - sizeof(uint64_t) * fixed_pmc_cnt + - sizeof(struct xen_pmu_cntr_pair) * - arch_pmc_cnt); + core2_vpmu_cxt = xzalloc_bytes(ctxt_sz); p = xzalloc(uint64_t); if ( !core2_vpmu_cxt || !p ) goto out_err; @@ -923,6 +996,10 @@ int __init core2_vpmu_init(void) (((1ULL << fixed_pmc_cnt) - 1) << 32) | ((1ULL << arch_pmc_cnt) - 1)); + ctxt_sz = sizeof(struct xen_pmu_intel_ctxt) + + sizeof(uint64_t) * fixed_pmc_cnt + + sizeof(struct xen_pmu_cntr_pair) * arch_pmc_cnt; + check_pmc_quirk(); if ( sizeof(struct xen_pmu_data) + sizeof(uint64_t) * fixed_pmc_cnt + diff --git a/xen/arch/x86/hvm/vpmu.c b/xen/arch/x86/hvm/vpmu.c index 07fa368..4d09c86 100644 --- a/xen/arch/x86/hvm/vpmu.c +++ b/xen/arch/x86/hvm/vpmu.c @@ -85,31 +85,56 @@ static void __init parse_vpmu_param(char *s) void vpmu_lvtpc_update(uint32_t val) { struct vpmu_struct *vpmu; + struct vcpu *curr = current; - if ( vpmu_mode == XENPMU_MODE_OFF ) + if ( likely(vpmu_mode == XENPMU_MODE_OFF) ) return; - vpmu = vcpu_vpmu(current); + vpmu = vcpu_vpmu(curr); vpmu->hw_lapic_lvtpc = PMU_APIC_VECTOR | (val & APIC_LVT_MASKED); - apic_write(APIC_LVTPC, vpmu->hw_lapic_lvtpc); + + /* Postpone APIC updates for PV(H) guests if PMU interrupt is pending */ + if ( is_hvm_vcpu(curr) || !vpmu->xenpmu_data || + !vpmu_is_set(vpmu, VPMU_CACHED) ) + apic_write(APIC_LVTPC, vpmu->hw_lapic_lvtpc); } int vpmu_do_wrmsr(unsigned int msr, uint64_t msr_content, uint64_t supported) { - struct vpmu_struct *vpmu = vcpu_vpmu(current); + struct vcpu *curr = current; + struct vpmu_struct *vpmu; if ( vpmu_mode == XENPMU_MODE_OFF ) return 0; + vpmu = vcpu_vpmu(curr); if ( vpmu->arch_vpmu_ops && vpmu->arch_vpmu_ops->do_wrmsr ) - return vpmu->arch_vpmu_ops->do_wrmsr(msr, msr_content, supported); + { + int ret = vpmu->arch_vpmu_ops->do_wrmsr(msr, msr_content, supported); + + /* + * We may have received a PMU interrupt during WRMSR handling + * and since do_wrmsr may load VPMU context we should save + * (and unload) it again. + */ + if ( !is_hvm_vcpu(curr) && vpmu->xenpmu_data && + vpmu_is_set(vpmu, VPMU_CACHED) ) + { + vpmu_set(vpmu, VPMU_CONTEXT_SAVE); + vpmu->arch_vpmu_ops->arch_vpmu_save(curr, 0); + vpmu_reset(vpmu, VPMU_CONTEXT_SAVE | VPMU_CONTEXT_LOADED); + } + return ret; + } + return 0; } int vpmu_do_rdmsr(unsigned int msr, uint64_t *msr_content) { - struct vpmu_struct *vpmu = vcpu_vpmu(current); + struct vcpu *curr = current; + struct vpmu_struct *vpmu; if ( vpmu_mode == XENPMU_MODE_OFF ) { @@ -117,39 +142,184 @@ int vpmu_do_rdmsr(unsigned int msr, uint64_t *msr_content) return 0; } + vpmu = vcpu_vpmu(curr); if ( vpmu->arch_vpmu_ops && vpmu->arch_vpmu_ops->do_rdmsr ) - return vpmu->arch_vpmu_ops->do_rdmsr(msr, msr_content); + { + int ret = vpmu->arch_vpmu_ops->do_rdmsr(msr, msr_content); + + if ( !is_hvm_vcpu(curr) && vpmu->xenpmu_data && + vpmu_is_set(vpmu, VPMU_CACHED) ) + { + vpmu_set(vpmu, VPMU_CONTEXT_SAVE); + vpmu->arch_vpmu_ops->arch_vpmu_save(curr, 0); + vpmu_reset(vpmu, VPMU_CONTEXT_SAVE | VPMU_CONTEXT_LOADED); + } + return ret; + } else *msr_content = 0; return 0; } +static inline struct vcpu *choose_hwdom_vcpu(void) +{ + unsigned idx; + + if ( hardware_domain->max_vcpus == 0 ) + return NULL; + + idx = smp_processor_id() % hardware_domain->max_vcpus; + + return hardware_domain->vcpu[idx]; +} + void vpmu_do_interrupt(struct cpu_user_regs *regs) { - struct vcpu *v = current; - struct vpmu_struct *vpmu = vcpu_vpmu(v); + struct vcpu *sampled = current, *sampling; + struct vpmu_struct *vpmu; + struct vlapic *vlapic; + u32 vlapic_lvtpc; - if ( vpmu->arch_vpmu_ops ) + /* dom0 will handle interrupt for special domains (e.g. idle domain) */ + if ( sampled->domain->domain_id >= DOMID_FIRST_RESERVED ) { - struct vlapic *vlapic = vcpu_vlapic(v); - u32 vlapic_lvtpc; + sampling = choose_hwdom_vcpu(); + if ( !sampling ) + return; + } + else + sampling = sampled; + + vpmu = vcpu_vpmu(sampling); + if ( !vpmu->arch_vpmu_ops ) + return; + + /* PV(H) guest */ + if ( !is_hvm_vcpu(sampling) ) + { + const struct cpu_user_regs *cur_regs; + uint64_t *flags = &vpmu->xenpmu_data->pmu.pmu_flags; + domid_t domid = DOMID_SELF; + + if ( !vpmu->xenpmu_data ) + return; + + if ( is_pvh_vcpu(sampling) && + !vpmu->arch_vpmu_ops->do_interrupt(regs) ) + return; - if ( !vpmu->arch_vpmu_ops->do_interrupt(regs) || - !is_vlapic_lvtpc_enabled(vlapic) ) + if ( vpmu_is_set(vpmu, VPMU_CACHED) ) return; - vlapic_lvtpc = vlapic_get_reg(vlapic, APIC_LVTPC); + /* PV guest will be reading PMU MSRs from xenpmu_data */ + vpmu_set(vpmu, VPMU_CONTEXT_SAVE | VPMU_CONTEXT_LOADED); + vpmu->arch_vpmu_ops->arch_vpmu_save(sampling, 1); + vpmu_reset(vpmu, VPMU_CONTEXT_SAVE | VPMU_CONTEXT_LOADED); + + if ( has_hvm_container_vcpu(sampled) ) + *flags = 0; + else + *flags = PMU_SAMPLE_PV; - switch ( GET_APIC_DELIVERY_MODE(vlapic_lvtpc) ) + /* Store appropriate registers in xenpmu_data */ + /* FIXME: 32-bit PVH should go here as well */ + if ( is_pv_32bit_vcpu(sampling) ) { - case APIC_MODE_FIXED: - vlapic_set_irq(vlapic, vlapic_lvtpc & APIC_VECTOR_MASK, 0); - break; - case APIC_MODE_NMI: - v->nmi_pending = 1; - break; + /* + * 32-bit dom0 cannot process Xen's addresses (which are 64 bit) + * and therefore we treat it the same way as a non-privileged + * PV 32-bit domain. + */ + struct compat_pmu_regs *cmp; + + cur_regs = guest_cpu_user_regs(); + + cmp = (void *)&vpmu->xenpmu_data->pmu.r.regs; + cmp->ip = cur_regs->rip; + cmp->sp = cur_regs->rsp; + cmp->flags = cur_regs->eflags; + cmp->ss = cur_regs->ss; + cmp->cs = cur_regs->cs; + if ( (cmp->cs & 3) > 1 ) + *flags |= PMU_SAMPLE_USER; } + else + { + struct xen_pmu_regs *r = &vpmu->xenpmu_data->pmu.r.regs; + + if ( (vpmu_mode & XENPMU_MODE_SELF) ) + cur_regs = guest_cpu_user_regs(); + else if ( !guest_mode(regs) && is_hardware_domain(sampling->domain) ) + { + cur_regs = regs; + domid = DOMID_XEN; + } + else + cur_regs = guest_cpu_user_regs(); + + r->ip = cur_regs->rip; + r->sp = cur_regs->rsp; + r->flags = cur_regs->eflags; + + if ( !has_hvm_container_vcpu(sampled) ) + { + r->ss = cur_regs->ss; + r->cs = cur_regs->cs; + if ( !(sampled->arch.flags & TF_kernel_mode) ) + *flags |= PMU_SAMPLE_USER; + } + else + { + struct segment_register seg; + + hvm_get_segment_register(sampled, x86_seg_cs, &seg); + r->cs = seg.sel; + hvm_get_segment_register(sampled, x86_seg_ss, &seg); + r->ss = seg.sel; + r->cpl = seg.attr.fields.dpl; + if ( !(sampled->arch.hvm_vcpu.guest_cr[0] & X86_CR0_PE) ) + *flags |= PMU_SAMPLE_REAL; + } + } + + vpmu->xenpmu_data->domain_id = domid; + vpmu->xenpmu_data->vcpu_id = sampled->vcpu_id; + if ( is_hardware_domain(sampling->domain) ) + vpmu->xenpmu_data->pcpu_id = smp_processor_id(); + else + vpmu->xenpmu_data->pcpu_id = sampled->vcpu_id; + + vpmu->hw_lapic_lvtpc |= APIC_LVT_MASKED; + apic_write(APIC_LVTPC, vpmu->hw_lapic_lvtpc); + *flags |= PMU_CACHED; + vpmu_set(vpmu, VPMU_CACHED); + + send_guest_vcpu_virq(sampling, VIRQ_XENPMU); + + return; + } + + /* HVM guests */ + vlapic = vcpu_vlapic(sampling); + + /* We don't support (yet) HVM dom0 */ + ASSERT(sampling == sampled); + + if ( !vpmu->arch_vpmu_ops->do_interrupt(regs) || + !is_vlapic_lvtpc_enabled(vlapic) ) + return; + + vlapic_lvtpc = vlapic_get_reg(vlapic, APIC_LVTPC); + + switch ( GET_APIC_DELIVERY_MODE(vlapic_lvtpc) ) + { + case APIC_MODE_FIXED: + vlapic_set_irq(vlapic, vlapic_lvtpc & APIC_VECTOR_MASK, 0); + break; + case APIC_MODE_NMI: + sampling->nmi_pending = 1; + break; } } @@ -174,7 +344,7 @@ static void vpmu_save_force(void *arg) vpmu_set(vpmu, VPMU_CONTEXT_SAVE); if ( vpmu->arch_vpmu_ops ) - (void)vpmu->arch_vpmu_ops->arch_vpmu_save(v); + (void)vpmu->arch_vpmu_ops->arch_vpmu_save(v, 0); vpmu_reset(vpmu, VPMU_CONTEXT_SAVE); @@ -193,20 +363,20 @@ void vpmu_save(struct vcpu *v) per_cpu(last_vcpu, pcpu) = v; if ( vpmu->arch_vpmu_ops ) - if ( vpmu->arch_vpmu_ops->arch_vpmu_save(v) ) + if ( vpmu->arch_vpmu_ops->arch_vpmu_save(v, 0) ) vpmu_reset(vpmu, VPMU_CONTEXT_LOADED); apic_write(APIC_LVTPC, PMU_APIC_VECTOR | APIC_LVT_MASKED); } -void vpmu_load(struct vcpu *v) +int vpmu_load(struct vcpu *v, bool_t from_guest) { struct vpmu_struct *vpmu = vcpu_vpmu(v); int pcpu = smp_processor_id(); struct vcpu *prev = NULL; if ( !vpmu_is_set(vpmu, VPMU_CONTEXT_ALLOCATED) ) - return; + return 0; /* First time this VCPU is running here */ if ( vpmu->last_pcpu != pcpu ) @@ -245,15 +415,23 @@ void vpmu_load(struct vcpu *v) local_irq_enable(); /* Only when PMU is counting, we load PMU context immediately. */ - if ( !vpmu_is_set(vpmu, VPMU_RUNNING) ) - return; + if ( !vpmu_is_set(vpmu, VPMU_RUNNING) || + (!is_hvm_vcpu(vpmu_vcpu(vpmu)) && vpmu_is_set(vpmu, VPMU_CACHED)) ) + return 0; if ( vpmu->arch_vpmu_ops && vpmu->arch_vpmu_ops->arch_vpmu_load ) { apic_write_around(APIC_LVTPC, vpmu->hw_lapic_lvtpc); /* Arch code needs to set VPMU_CONTEXT_LOADED */ - vpmu->arch_vpmu_ops->arch_vpmu_load(v); + if ( vpmu->arch_vpmu_ops->arch_vpmu_load(v, from_guest) ) + { + apic_write_around(APIC_LVTPC, + vpmu->hw_lapic_lvtpc | APIC_LVT_MASKED); + return 1; + } } + + return 0; } void vpmu_initialise(struct vcpu *v) @@ -265,6 +443,8 @@ void vpmu_initialise(struct vcpu *v) BUILD_BUG_ON(sizeof(struct xen_pmu_intel_ctxt) > XENPMU_CTXT_PAD_SZ); BUILD_BUG_ON(sizeof(struct xen_pmu_amd_ctxt) > XENPMU_CTXT_PAD_SZ); + BUILD_BUG_ON(sizeof(struct xen_pmu_regs) > XENPMU_REGS_PAD_SZ); + BUILD_BUG_ON(sizeof(struct compat_pmu_regs) > XENPMU_REGS_PAD_SZ); ASSERT(!vpmu->flags && !vpmu->context); @@ -449,7 +629,10 @@ void vpmu_dump(struct vcpu *v) long do_xenpmu_op(unsigned int op, XEN_GUEST_HANDLE_PARAM(xen_pmu_params_t) arg) { int ret; + struct vcpu *curr; struct xen_pmu_params pmu_params = {.val = 0}; + struct xen_pmu_data *xenpmu_data; + struct vpmu_struct *vpmu; if ( !opt_vpmu_enabled ) return -EOPNOTSUPP; @@ -552,6 +735,30 @@ long do_xenpmu_op(unsigned int op, XEN_GUEST_HANDLE_PARAM(xen_pmu_params_t) arg) pvpmu_finish(current->domain, &pmu_params); break; + case XENPMU_lvtpc_set: + xenpmu_data = current->arch.vpmu.xenpmu_data; + if ( xenpmu_data == NULL ) + return -EINVAL; + vpmu_lvtpc_update(xenpmu_data->pmu.l.lapic_lvtpc); + break; + + case XENPMU_flush: + curr = current; + vpmu = vcpu_vpmu(curr); + xenpmu_data = curr->arch.vpmu.xenpmu_data; + if ( xenpmu_data == NULL ) + return -EINVAL; + xenpmu_data->pmu.pmu_flags &= ~PMU_CACHED; + vpmu_reset(vpmu, VPMU_CACHED); + vpmu_lvtpc_update(xenpmu_data->pmu.l.lapic_lvtpc); + if ( vpmu_load(curr, 1) ) + { + xenpmu_data->pmu.pmu_flags |= PMU_CACHED; + vpmu_set(vpmu, VPMU_CACHED); + return -EIO; + } + break ; + default: ret = -EINVAL; } diff --git a/xen/include/asm-x86/hvm/vpmu.h b/xen/include/asm-x86/hvm/vpmu.h index 642a4b7..f486d2f 100644 --- a/xen/include/asm-x86/hvm/vpmu.h +++ b/xen/include/asm-x86/hvm/vpmu.h @@ -47,8 +47,8 @@ struct arch_vpmu_ops { unsigned int *eax, unsigned int *ebx, unsigned int *ecx, unsigned int *edx); void (*arch_vpmu_destroy)(struct vcpu *v); - int (*arch_vpmu_save)(struct vcpu *v); - void (*arch_vpmu_load)(struct vcpu *v); + int (*arch_vpmu_save)(struct vcpu *v, bool_t to_guest); + int (*arch_vpmu_load)(struct vcpu *v, bool_t from_guest); void (*arch_vpmu_dump)(const struct vcpu *); }; @@ -75,6 +75,8 @@ struct vpmu_struct { #define VPMU_CONTEXT_SAVE 0x8 /* Force context save */ #define VPMU_FROZEN 0x10 /* Stop counters while VCPU is not running */ #define VPMU_PASSIVE_DOMAIN_ALLOCATED 0x20 +/* PV(H) guests: VPMU registers are accessed by guest from shared page */ +#define VPMU_CACHED 0x40 static inline void vpmu_set(struct vpmu_struct *vpmu, const u32 mask) { @@ -107,7 +109,7 @@ void vpmu_do_cpuid(unsigned int input, unsigned int *eax, unsigned int *ebx, void vpmu_initialise(struct vcpu *v); void vpmu_destroy(struct vcpu *v); void vpmu_save(struct vcpu *v); -void vpmu_load(struct vcpu *v); +int vpmu_load(struct vcpu *v, bool_t from_guest); void vpmu_dump(struct vcpu *v); extern int acquire_pmu_ownership(int pmu_ownership); @@ -126,7 +128,7 @@ static inline void vpmu_switch_from(struct vcpu *prev) static inline void vpmu_switch_to(struct vcpu *next) { if ( vpmu_mode & (XENPMU_MODE_SELF | XENPMU_MODE_HV) ) - vpmu_load(next); + vpmu_load(next, 0); } #endif /* __ASM_X86_HVM_VPMU_H_*/ diff --git a/xen/include/public/arch-x86/pmu.h b/xen/include/public/arch-x86/pmu.h index a295c00..016b8cd 100644 --- a/xen/include/public/arch-x86/pmu.h +++ b/xen/include/public/arch-x86/pmu.h @@ -53,6 +53,9 @@ DEFINE_XEN_GUEST_HANDLE(xen_pmu_regs_t); /* PMU flags */ #define PMU_CACHED (1<<0) /* PMU MSRs are cached in the context */ +#define PMU_SAMPLE_USER (1<<1) /* Sample is from user or kernel mode */ +#define PMU_SAMPLE_REAL (1<<2) /* Sample is from realmode */ +#define PMU_SAMPLE_PV (1<<3) /* Sample from a PV guest */ /* * Architecture-specific information describing state of the processor at @@ -77,12 +80,34 @@ struct xen_pmu_arch { /* WO for hypervisor, RO for guest */ uint64_t pmu_flags; - /* Placeholder for APIC LVTPC register */ - uint64_t lvtpc_pad; + /* + * APIC LVTPC register. + * RW for both hypervisor and guest. + * Only APIC_LVT_MASKED bit is loaded by the hypervisor into hardware + * during XENPMU_flush or XENPMU_lvtpc_set. + */ + union { + uint32_t lapic_lvtpc; + uint64_t pad; + } l; - /* Placeholder for vendor-specific PMU registers */ + /* + * Vendor-specific PMU registers. + * RW for both hypervisor and guest. + * Guest's updates to this field are verified and then loaded by the + * hypervisor into hardware during XENPMU_flush + */ + union { + struct xen_pmu_amd_ctxt amd; + struct xen_pmu_intel_ctxt intel; + + /* + * Padding for contexts (fixed parts only, does not include MSR banks + * that are specified by offsets) + */ #define XENPMU_CTXT_PAD_SZ 128 - uint64_t pmu_regs_pad[XENPMU_CTXT_PAD_SZ / 8]; + uint8_t pad[XENPMU_CTXT_PAD_SZ]; + } c; }; typedef struct xen_pmu_arch xen_pmu_arch_t; DEFINE_XEN_GUEST_HANDLE(xen_pmu_arch_t); diff --git a/xen/include/public/pmu.h b/xen/include/public/pmu.h index e6307b5..7a45783 100644 --- a/xen/include/public/pmu.h +++ b/xen/include/public/pmu.h @@ -27,6 +27,8 @@ #define XENPMU_feature_set 3 #define XENPMU_init 4 #define XENPMU_finish 5 +#define XENPMU_lvtpc_set 6 +#define XENPMU_flush 7 /* Write cached MSR values to HW */ /* ` } */ /* Parameters structure for HYPERVISOR_xenpmu_op call */ diff --git a/xen/include/xsm/dummy.h b/xen/include/xsm/dummy.h index 6456f72..37e6aa3 100644 --- a/xen/include/xsm/dummy.h +++ b/xen/include/xsm/dummy.h @@ -705,7 +705,9 @@ static XSM_INLINE int xsm_pmu_op (XSM_DEFAULT_ARG struct domain *d, int op) case XENPMU_feature_get: return xsm_default_action(XSM_PRIV, d, current->domain); case XENPMU_init: - case XENPMU_finish: + case XENPMU_finish: + case XENPMU_lvtpc_set: + case XENPMU_flush: return xsm_default_action(XSM_HOOK, d, current->domain); default: return -EPERM; diff --git a/xen/xsm/flask/hooks.c b/xen/xsm/flask/hooks.c index aefcbda..4aa3e79 100644 --- a/xen/xsm/flask/hooks.c +++ b/xen/xsm/flask/hooks.c @@ -1594,6 +1594,8 @@ static int flask_pmu_op (struct domain *d, unsigned int op) XEN2__PMU_CTRL, NULL); case XENPMU_init: case XENPMU_finish: + case XENPMU_lvtpc_set: + case XENPMU_flush: return avc_has_perm(dsid, SECINITSID_XEN, SECCLASS_XEN2, XEN2__PMU_USE, NULL); default: -- 1.8.1.4 _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx http://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |