forked from beagleboard/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
xen/PMU: Initialization code for Xen PMU
Map shared data structure that will hold CPU registers, VPMU context, V/PCPU IDs of the CPU interrupted by PMU interrupt. Hypervisor fills this information in its handler and passes it to the guest for further processing. Set up PMU VIRQ. Now that perf infrastructure will assume that PMU is available on a PV guest we need to be careful and make sure that accesses via RDPMC instruction don't cause fatal traps by the hypervisor. Provide a nop RDPMC handler. For the same reason avoid issuing a warning on a write to APIC's LVTPC. Both of these will be made functional in later patches. Signed-off-by: Boris Ostrovsky <[email protected]> Reviewed-by: David Vrabel <[email protected]> Signed-off-by: David Vrabel <[email protected]>
- Loading branch information
Boris Ostrovsky
authored and
David Vrabel
committed
Aug 20, 2015
1 parent
5f14154
commit 65d0cf0
Showing
10 changed files
with
398 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
#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 "../kernel/cpu/perf_event.h" | ||
|
||
|
||
/* 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()) | ||
|
||
/* 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 ret = IRQ_NONE; | ||
struct pt_regs regs; | ||
const struct xen_pmu_data *xenpmu_data = get_xenpmu_data(); | ||
|
||
if (!xenpmu_data) { | ||
pr_warn_once("%s: pmudata not initialized\n", __func__); | ||
return ret; | ||
} | ||
|
||
xen_convert_regs(&xenpmu_data->pmu.r.regs, ®s, | ||
xenpmu_data->pmu.pmu_flags); | ||
if (x86_pmu.handle_irq(®s)) | ||
ret = IRQ_HANDLED; | ||
|
||
return ret; | ||
} | ||
|
||
bool is_xen_pmu(int cpu) | ||
{ | ||
return (per_cpu(xenpmu_shared, cpu) != 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; | ||
|
||
if (cpu == 0) | ||
perf_register_guest_info_callbacks(&xen_guest_cbs); | ||
|
||
return; | ||
|
||
fail: | ||
pr_warn_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), 0); | ||
per_cpu(xenpmu_shared, cpu) = NULL; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
#ifndef __XEN_PMU_H | ||
#define __XEN_PMU_H | ||
|
||
#include <xen/interface/xenpmu.h> | ||
|
||
irqreturn_t xen_pmu_irq_handler(int irq, void *dev_id); | ||
void xen_pmu_init(int cpu); | ||
void xen_pmu_finish(int cpu); | ||
bool is_xen_pmu(int cpu); | ||
|
||
#endif /* __XEN_PMU_H */ |
Oops, something went wrong.