[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [RFC LINUX PATCH v1 3/3] perf kvm: implement Xen hypervisor stacktraces
Using the new VPMU 0.2 interface. This is backwards compatible with VPMU 0.1: the new 'struct xen_pmu_hv_stacktrace` is stored at the end of the page, and stacktrace_nr would be 0 on old hypervisors. Signed-off-by: Edwin Török <edwin.torok@xxxxxxxxx> --- arch/x86/events/core.c | 4 ++- arch/x86/xen/pmu.c | 73 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 70 insertions(+), 7 deletions(-) diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c index ad63bd408cd9..1fca4a77f353 100644 --- a/arch/x86/events/core.c +++ b/arch/x86/events/core.c @@ -2764,12 +2764,14 @@ perf_callchain_kernel(struct perf_callchain_entry_ctx *entry, struct pt_regs *re struct unwind_state state; unsigned long addr; + perf_hypervisor_callchain(entry, regs); + if (perf_guest_state()) { /* TODO: We don't support guest os callchain now */ return; } - if (perf_callchain_store(entry, regs->ip)) + if (!regs->ip || perf_callchain_store(entry, regs->ip)) return; if (perf_hw_regs(regs)) diff --git a/arch/x86/xen/pmu.c b/arch/x86/xen/pmu.c index b92dc739fdfb..4996b6904e0b 100644 --- a/arch/x86/xen/pmu.c +++ b/arch/x86/xen/pmu.c @@ -19,11 +19,13 @@ struct xenpmu { /* Shared page between hypervisor and domain */ struct xen_pmu_data *xenpmu_data; + const struct xen_pmu_hv_stacktrace *xenpmu_hv_stacktrace; 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_hv_stacktrace() (this_cpu_ptr(&xenpmu_shared)->xenpmu_hv_stacktrace) #define get_xenpmu_flags() (this_cpu_ptr(&xenpmu_shared)->flags) /* Macro for computing address of a PMU MSR bank */ @@ -436,8 +438,19 @@ static unsigned int xen_guest_state(void) return state; } - if (!xen_initial_domain() || (xenpmu_data->domain_id >= DOMID_SELF)) - return state; + if (!xen_initial_domain() || (xenpmu_data->domain_id >= DOMID_SELF)) { + if (xenpmu_data->domain_id == DOMID_XEN) { + /* when inside Xen we output the hypervisor stacktrace if available, + * but only look at guest stacktrace if this is our domid + */ + const struct xen_pmu_hv_stacktrace *xenpmu_hv_stacktrace = + get_xenpmu_hv_stacktrace(); + if (!xenpmu_hv_stacktrace || + xenpmu_hv_stacktrace->guest_domain_id == DOMID_SELF) + return state; + } else + return state; + } state |= PERF_GUEST_ACTIVE; @@ -463,10 +476,54 @@ static unsigned long xen_get_guest_ip(void) return xenpmu_data->pmu.r.regs.ip; } +static void xen_convert_regs(const struct xen_pmu_regs *xen_regs, + struct pt_regs *regs, uint64_t pmu_flags); + +static void xen_hypervisor_callchain(struct perf_callchain_entry_ctx *entry, + struct pt_regs *regs) +{ + if (!entry) + return; + + const struct xen_pmu_data *xenpmu_data = get_xenpmu_data(); + + if (!xenpmu_data) { + pr_warn_once("%s: pmudata not initialized\n", __func__); + return; + } + + if (xenpmu_data->domain_id != DOMID_XEN) + return; + + if (!regs->ip || perf_callchain_store(entry, regs->ip)) + return; + + const struct xen_pmu_hv_stacktrace *pmu_stack = + get_xenpmu_hv_stacktrace(); + + const unsigned int stacktrace_nr = pmu_stack->stacktrace_nr; + + if (stacktrace_nr > ARRAY_SIZE(pmu_stack->stacktrace)) { + pr_warn_once("%s: stacktrace_nr out of bounds: %d", __func__, + stacktrace_nr); + return; + } + + for (unsigned int i = 0; i < stacktrace_nr; i++) { + uint64_t addr = + pmu_stack->stacktrace[PMU_MAX_STACKTRACE - 1 - i]; + if (!addr || perf_callchain_store(entry, addr)) + break; + } + + xen_convert_regs(&pmu_stack->guest.r.regs, regs, + xenpmu_data->pmu.pmu_flags); +} + static struct perf_guest_info_callbacks xen_guest_cbs = { .state = xen_guest_state, .get_ip = xen_get_guest_ip, - .hypervisor_callchain = NULL + .hypervisor_callchain = xen_hypervisor_callchain }; /* Convert registers from Xen's format to Linux' */ @@ -490,7 +547,6 @@ static void xen_convert_regs(const struct xen_pmu_regs *xen_regs, } } - irqreturn_t xen_pmu_irq_handler(int irq, void *dev_id) { int err, ret = IRQ_NONE; @@ -527,7 +583,7 @@ void xen_pmu_init(int cpu) { int err; struct xen_pmu_params xp; - unsigned long pfn; + unsigned long pfn, pmu_page; struct xen_pmu_data *xenpmu_data; BUILD_BUG_ON(sizeof(struct xen_pmu_data) > PAGE_SIZE); @@ -535,7 +591,8 @@ void xen_pmu_init(int cpu) if (xen_hvm_domain() || (cpu != 0 && !is_xen_pmu)) return; - xenpmu_data = (struct xen_pmu_data *)get_zeroed_page(GFP_KERNEL); + pmu_page = get_zeroed_page(GFP_KERNEL); + xenpmu_data = (struct xen_pmu_data *)pmu_page; if (!xenpmu_data) { pr_err("VPMU init: No memory\n"); return; @@ -551,6 +608,10 @@ void xen_pmu_init(int cpu) goto fail; per_cpu(xenpmu_shared, cpu).xenpmu_data = xenpmu_data; + per_cpu(xenpmu_shared, cpu).xenpmu_hv_stacktrace = + (const struct xen_pmu_hv_stacktrace *) + (pmu_page + PAGE_SIZE - + sizeof(struct xen_pmu_hv_stacktrace)); per_cpu(xenpmu_shared, cpu).flags = 0; if (!is_xen_pmu) { -- 2.47.1
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |