Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bf6dfb15 authored by Boris Ostrovsky's avatar Boris Ostrovsky Committed by David Vrabel
Browse files

xen/PMU: PMU emulation code



Add PMU emulation code that runs when we are processing a PMU interrupt.
This code will allow us not to trap to hypervisor on each MSR/LVTPC access
(of which there may be quite a few in the handler).

Signed-off-by: default avatarBoris Ostrovsky <boris.ostrovsky@oracle.com>
Reviewed-by: default avatarDavid Vrabel <david.vrabel@citrix.com>
Signed-off-by: default avatarDavid Vrabel <david.vrabel@citrix.com>
parent 6b08cd63
Loading
Loading
Loading
Loading
+185 −29
Original line number Diff line number Diff line
@@ -13,11 +13,20 @@
/* x86_pmu.handle_irq definition */
#include "../kernel/cpu/perf_event.h"


#define XENPMU_IRQ_PROCESSING    1
struct xenpmu {
	/* Shared page between hypervisor and domain */
static DEFINE_PER_CPU(struct xen_pmu_data *, xenpmu_shared);
#define get_xenpmu_data()    per_cpu(xenpmu_shared, smp_processor_id())
	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
@@ -169,11 +178,115 @@ static int is_intel_pmu_msr(u32 msr_index, int *type, int *index)
	}
}

bool pmu_msr_read(unsigned int msr, uint64_t *val, int *err)
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;
		}
@@ -181,6 +294,7 @@ bool pmu_msr_read(unsigned int msr, uint64_t *val, int *err)
		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;
		}
@@ -191,8 +305,11 @@ bool pmu_msr_read(unsigned int msr, uint64_t *val, int *err)

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;
		}
@@ -200,6 +317,7 @@ bool pmu_msr_write(unsigned int msr, uint32_t low, uint32_t high, int *err)
		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;
		}
@@ -210,6 +328,12 @@ bool pmu_msr_write(unsigned int msr, uint32_t low, uint32_t high, int *err)

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;

@@ -217,10 +341,22 @@ static unsigned long long xen_amd_read_pmc(int counter)
		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)
{
	int err;
	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);
@@ -230,6 +366,16 @@ static unsigned long long xen_intel_read_pmc(int 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)
@@ -249,6 +395,10 @@ int pmu_apic_update(uint32_t val)
	}

	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;
@@ -329,29 +479,34 @@ irqreturn_t xen_pmu_irq_handler(int irq, void *dev_id)
	int err, ret = IRQ_NONE;
	struct pt_regs regs;
	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;
	}

	err = HYPERVISOR_xenpmu_op(XENPMU_flush, NULL);
	if (err) {
		pr_warn_once("%s: failed hypercall, err: %d\n", __func__, err);
		return ret;
	}

	this_cpu_ptr(&xenpmu_shared)->flags =
		xenpmu_flags | XENPMU_IRQ_PROCESSING;
	xen_convert_regs(&xenpmu_data->pmu.r.regs, &regs,
			 xenpmu_data->pmu.pmu_flags);
	if (x86_pmu.handle_irq(&regs))
		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 (per_cpu(xenpmu_shared, cpu) != NULL);
	return (get_xenpmu_data() != NULL);
}

void xen_pmu_init(int cpu)
@@ -381,7 +536,8 @@ void xen_pmu_init(int cpu)
	if (err)
		goto fail;

	per_cpu(xenpmu_shared, cpu) = xenpmu_data;
	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);
@@ -409,6 +565,6 @@ void xen_pmu_finish(int cpu)

	(void)HYPERVISOR_xenpmu_op(XENPMU_finish, &xp);

	free_pages((unsigned long)per_cpu(xenpmu_shared, cpu), 0);
	per_cpu(xenpmu_shared, cpu) = NULL;
	free_pages((unsigned long)per_cpu(xenpmu_shared, cpu).xenpmu_data, 0);
	per_cpu(xenpmu_shared, cpu).xenpmu_data = NULL;
}