blob: 1d1ae1b874ea876fabc2839e26bf33738b6a1de1 [file] [log] [blame]
Boris Ostrovsky65d0cf02015-08-10 16:34:34 -04001#include <linux/types.h>
2#include <linux/interrupt.h>
3
4#include <asm/xen/hypercall.h>
5#include <xen/page.h>
6#include <xen/interface/xen.h>
7#include <xen/interface/vcpu.h>
8#include <xen/interface/xenpmu.h>
9
10#include "xen-ops.h"
11#include "pmu.h"
12
13/* x86_pmu.handle_irq definition */
14#include "../kernel/cpu/perf_event.h"
15
16
17/* Shared page between hypervisor and domain */
18static DEFINE_PER_CPU(struct xen_pmu_data *, xenpmu_shared);
19#define get_xenpmu_data() per_cpu(xenpmu_shared, smp_processor_id())
20
21/* perf callbacks */
22static int xen_is_in_guest(void)
23{
24 const struct xen_pmu_data *xenpmu_data = get_xenpmu_data();
25
26 if (!xenpmu_data) {
27 pr_warn_once("%s: pmudata not initialized\n", __func__);
28 return 0;
29 }
30
31 if (!xen_initial_domain() || (xenpmu_data->domain_id >= DOMID_SELF))
32 return 0;
33
34 return 1;
35}
36
37static int xen_is_user_mode(void)
38{
39 const struct xen_pmu_data *xenpmu_data = get_xenpmu_data();
40
41 if (!xenpmu_data) {
42 pr_warn_once("%s: pmudata not initialized\n", __func__);
43 return 0;
44 }
45
46 if (xenpmu_data->pmu.pmu_flags & PMU_SAMPLE_PV)
47 return (xenpmu_data->pmu.pmu_flags & PMU_SAMPLE_USER);
48 else
49 return !!(xenpmu_data->pmu.r.regs.cpl & 3);
50}
51
52static unsigned long xen_get_guest_ip(void)
53{
54 const struct xen_pmu_data *xenpmu_data = get_xenpmu_data();
55
56 if (!xenpmu_data) {
57 pr_warn_once("%s: pmudata not initialized\n", __func__);
58 return 0;
59 }
60
61 return xenpmu_data->pmu.r.regs.ip;
62}
63
64static struct perf_guest_info_callbacks xen_guest_cbs = {
65 .is_in_guest = xen_is_in_guest,
66 .is_user_mode = xen_is_user_mode,
67 .get_guest_ip = xen_get_guest_ip,
68};
69
70/* Convert registers from Xen's format to Linux' */
71static void xen_convert_regs(const struct xen_pmu_regs *xen_regs,
72 struct pt_regs *regs, uint64_t pmu_flags)
73{
74 regs->ip = xen_regs->ip;
75 regs->cs = xen_regs->cs;
76 regs->sp = xen_regs->sp;
77
78 if (pmu_flags & PMU_SAMPLE_PV) {
79 if (pmu_flags & PMU_SAMPLE_USER)
80 regs->cs |= 3;
81 else
82 regs->cs &= ~3;
83 } else {
84 if (xen_regs->cpl)
85 regs->cs |= 3;
86 else
87 regs->cs &= ~3;
88 }
89}
90
91irqreturn_t xen_pmu_irq_handler(int irq, void *dev_id)
92{
93 int ret = IRQ_NONE;
94 struct pt_regs regs;
95 const struct xen_pmu_data *xenpmu_data = get_xenpmu_data();
96
97 if (!xenpmu_data) {
98 pr_warn_once("%s: pmudata not initialized\n", __func__);
99 return ret;
100 }
101
102 xen_convert_regs(&xenpmu_data->pmu.r.regs, &regs,
103 xenpmu_data->pmu.pmu_flags);
104 if (x86_pmu.handle_irq(&regs))
105 ret = IRQ_HANDLED;
106
107 return ret;
108}
109
110bool is_xen_pmu(int cpu)
111{
112 return (per_cpu(xenpmu_shared, cpu) != NULL);
113}
114
115void xen_pmu_init(int cpu)
116{
117 int err;
118 struct xen_pmu_params xp;
119 unsigned long pfn;
120 struct xen_pmu_data *xenpmu_data;
121
122 BUILD_BUG_ON(sizeof(struct xen_pmu_data) > PAGE_SIZE);
123
124 if (xen_hvm_domain())
125 return;
126
127 xenpmu_data = (struct xen_pmu_data *)get_zeroed_page(GFP_KERNEL);
128 if (!xenpmu_data) {
129 pr_err("VPMU init: No memory\n");
130 return;
131 }
132 pfn = virt_to_pfn(xenpmu_data);
133
134 xp.val = pfn_to_mfn(pfn);
135 xp.vcpu = cpu;
136 xp.version.maj = XENPMU_VER_MAJ;
137 xp.version.min = XENPMU_VER_MIN;
138 err = HYPERVISOR_xenpmu_op(XENPMU_init, &xp);
139 if (err)
140 goto fail;
141
142 per_cpu(xenpmu_shared, cpu) = xenpmu_data;
143
144 if (cpu == 0)
145 perf_register_guest_info_callbacks(&xen_guest_cbs);
146
147 return;
148
149fail:
150 pr_warn_once("Could not initialize VPMU for cpu %d, error %d\n",
151 cpu, err);
152 free_pages((unsigned long)xenpmu_data, 0);
153}
154
155void xen_pmu_finish(int cpu)
156{
157 struct xen_pmu_params xp;
158
159 if (xen_hvm_domain())
160 return;
161
162 xp.vcpu = cpu;
163 xp.version.maj = XENPMU_VER_MAJ;
164 xp.version.min = XENPMU_VER_MIN;
165
166 (void)HYPERVISOR_xenpmu_op(XENPMU_finish, &xp);
167
168 free_pages((unsigned long)per_cpu(xenpmu_shared, cpu), 0);
169 per_cpu(xenpmu_shared, cpu) = NULL;
170}