1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright 2010 ARM Ltd.
4*4882a593Smuzhiyun * Copyright 2012 Advanced Micro Devices, Inc., Robert Richter
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * Perf-events backend for OProfile.
7*4882a593Smuzhiyun */
8*4882a593Smuzhiyun #include <linux/perf_event.h>
9*4882a593Smuzhiyun #include <linux/platform_device.h>
10*4882a593Smuzhiyun #include <linux/oprofile.h>
11*4882a593Smuzhiyun #include <linux/slab.h>
12*4882a593Smuzhiyun
13*4882a593Smuzhiyun /*
14*4882a593Smuzhiyun * Per performance monitor configuration as set via oprofilefs.
15*4882a593Smuzhiyun */
16*4882a593Smuzhiyun struct op_counter_config {
17*4882a593Smuzhiyun unsigned long count;
18*4882a593Smuzhiyun unsigned long enabled;
19*4882a593Smuzhiyun unsigned long event;
20*4882a593Smuzhiyun unsigned long unit_mask;
21*4882a593Smuzhiyun unsigned long kernel;
22*4882a593Smuzhiyun unsigned long user;
23*4882a593Smuzhiyun struct perf_event_attr attr;
24*4882a593Smuzhiyun };
25*4882a593Smuzhiyun
26*4882a593Smuzhiyun static int oprofile_perf_enabled;
27*4882a593Smuzhiyun static DEFINE_MUTEX(oprofile_perf_mutex);
28*4882a593Smuzhiyun
29*4882a593Smuzhiyun static struct op_counter_config *counter_config;
30*4882a593Smuzhiyun static DEFINE_PER_CPU(struct perf_event **, perf_events);
31*4882a593Smuzhiyun static int num_counters;
32*4882a593Smuzhiyun
33*4882a593Smuzhiyun /*
34*4882a593Smuzhiyun * Overflow callback for oprofile.
35*4882a593Smuzhiyun */
op_overflow_handler(struct perf_event * event,struct perf_sample_data * data,struct pt_regs * regs)36*4882a593Smuzhiyun static void op_overflow_handler(struct perf_event *event,
37*4882a593Smuzhiyun struct perf_sample_data *data, struct pt_regs *regs)
38*4882a593Smuzhiyun {
39*4882a593Smuzhiyun int id;
40*4882a593Smuzhiyun u32 cpu = smp_processor_id();
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun for (id = 0; id < num_counters; ++id)
43*4882a593Smuzhiyun if (per_cpu(perf_events, cpu)[id] == event)
44*4882a593Smuzhiyun break;
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun if (id != num_counters)
47*4882a593Smuzhiyun oprofile_add_sample(regs, id);
48*4882a593Smuzhiyun else
49*4882a593Smuzhiyun pr_warn("oprofile: ignoring spurious overflow on cpu %u\n",
50*4882a593Smuzhiyun cpu);
51*4882a593Smuzhiyun }
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun /*
54*4882a593Smuzhiyun * Called by oprofile_perf_setup to create perf attributes to mirror the oprofile
55*4882a593Smuzhiyun * settings in counter_config. Attributes are created as `pinned' events and
56*4882a593Smuzhiyun * so are permanently scheduled on the PMU.
57*4882a593Smuzhiyun */
op_perf_setup(void)58*4882a593Smuzhiyun static void op_perf_setup(void)
59*4882a593Smuzhiyun {
60*4882a593Smuzhiyun int i;
61*4882a593Smuzhiyun u32 size = sizeof(struct perf_event_attr);
62*4882a593Smuzhiyun struct perf_event_attr *attr;
63*4882a593Smuzhiyun
64*4882a593Smuzhiyun for (i = 0; i < num_counters; ++i) {
65*4882a593Smuzhiyun attr = &counter_config[i].attr;
66*4882a593Smuzhiyun memset(attr, 0, size);
67*4882a593Smuzhiyun attr->type = PERF_TYPE_RAW;
68*4882a593Smuzhiyun attr->size = size;
69*4882a593Smuzhiyun attr->config = counter_config[i].event;
70*4882a593Smuzhiyun attr->sample_period = counter_config[i].count;
71*4882a593Smuzhiyun attr->pinned = 1;
72*4882a593Smuzhiyun }
73*4882a593Smuzhiyun }
74*4882a593Smuzhiyun
op_create_counter(int cpu,int event)75*4882a593Smuzhiyun static int op_create_counter(int cpu, int event)
76*4882a593Smuzhiyun {
77*4882a593Smuzhiyun struct perf_event *pevent;
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun if (!counter_config[event].enabled || per_cpu(perf_events, cpu)[event])
80*4882a593Smuzhiyun return 0;
81*4882a593Smuzhiyun
82*4882a593Smuzhiyun pevent = perf_event_create_kernel_counter(&counter_config[event].attr,
83*4882a593Smuzhiyun cpu, NULL,
84*4882a593Smuzhiyun op_overflow_handler, NULL);
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun if (IS_ERR(pevent))
87*4882a593Smuzhiyun return PTR_ERR(pevent);
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun if (pevent->state != PERF_EVENT_STATE_ACTIVE) {
90*4882a593Smuzhiyun perf_event_release_kernel(pevent);
91*4882a593Smuzhiyun pr_warn("oprofile: failed to enable event %d on CPU %d\n",
92*4882a593Smuzhiyun event, cpu);
93*4882a593Smuzhiyun return -EBUSY;
94*4882a593Smuzhiyun }
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun per_cpu(perf_events, cpu)[event] = pevent;
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun return 0;
99*4882a593Smuzhiyun }
100*4882a593Smuzhiyun
op_destroy_counter(int cpu,int event)101*4882a593Smuzhiyun static void op_destroy_counter(int cpu, int event)
102*4882a593Smuzhiyun {
103*4882a593Smuzhiyun struct perf_event *pevent = per_cpu(perf_events, cpu)[event];
104*4882a593Smuzhiyun
105*4882a593Smuzhiyun if (pevent) {
106*4882a593Smuzhiyun perf_event_release_kernel(pevent);
107*4882a593Smuzhiyun per_cpu(perf_events, cpu)[event] = NULL;
108*4882a593Smuzhiyun }
109*4882a593Smuzhiyun }
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun /*
112*4882a593Smuzhiyun * Called by oprofile_perf_start to create active perf events based on the
113*4882a593Smuzhiyun * perviously configured attributes.
114*4882a593Smuzhiyun */
op_perf_start(void)115*4882a593Smuzhiyun static int op_perf_start(void)
116*4882a593Smuzhiyun {
117*4882a593Smuzhiyun int cpu, event, ret = 0;
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun for_each_online_cpu(cpu) {
120*4882a593Smuzhiyun for (event = 0; event < num_counters; ++event) {
121*4882a593Smuzhiyun ret = op_create_counter(cpu, event);
122*4882a593Smuzhiyun if (ret)
123*4882a593Smuzhiyun return ret;
124*4882a593Smuzhiyun }
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun return ret;
128*4882a593Smuzhiyun }
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun /*
131*4882a593Smuzhiyun * Called by oprofile_perf_stop at the end of a profiling run.
132*4882a593Smuzhiyun */
op_perf_stop(void)133*4882a593Smuzhiyun static void op_perf_stop(void)
134*4882a593Smuzhiyun {
135*4882a593Smuzhiyun int cpu, event;
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun for_each_online_cpu(cpu)
138*4882a593Smuzhiyun for (event = 0; event < num_counters; ++event)
139*4882a593Smuzhiyun op_destroy_counter(cpu, event);
140*4882a593Smuzhiyun }
141*4882a593Smuzhiyun
oprofile_perf_create_files(struct dentry * root)142*4882a593Smuzhiyun static int oprofile_perf_create_files(struct dentry *root)
143*4882a593Smuzhiyun {
144*4882a593Smuzhiyun unsigned int i;
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun for (i = 0; i < num_counters; i++) {
147*4882a593Smuzhiyun struct dentry *dir;
148*4882a593Smuzhiyun char buf[4];
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun snprintf(buf, sizeof buf, "%d", i);
151*4882a593Smuzhiyun dir = oprofilefs_mkdir(root, buf);
152*4882a593Smuzhiyun oprofilefs_create_ulong(dir, "enabled", &counter_config[i].enabled);
153*4882a593Smuzhiyun oprofilefs_create_ulong(dir, "event", &counter_config[i].event);
154*4882a593Smuzhiyun oprofilefs_create_ulong(dir, "count", &counter_config[i].count);
155*4882a593Smuzhiyun oprofilefs_create_ulong(dir, "unit_mask", &counter_config[i].unit_mask);
156*4882a593Smuzhiyun oprofilefs_create_ulong(dir, "kernel", &counter_config[i].kernel);
157*4882a593Smuzhiyun oprofilefs_create_ulong(dir, "user", &counter_config[i].user);
158*4882a593Smuzhiyun }
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun return 0;
161*4882a593Smuzhiyun }
162*4882a593Smuzhiyun
oprofile_perf_setup(void)163*4882a593Smuzhiyun static int oprofile_perf_setup(void)
164*4882a593Smuzhiyun {
165*4882a593Smuzhiyun raw_spin_lock(&oprofilefs_lock);
166*4882a593Smuzhiyun op_perf_setup();
167*4882a593Smuzhiyun raw_spin_unlock(&oprofilefs_lock);
168*4882a593Smuzhiyun return 0;
169*4882a593Smuzhiyun }
170*4882a593Smuzhiyun
oprofile_perf_start(void)171*4882a593Smuzhiyun static int oprofile_perf_start(void)
172*4882a593Smuzhiyun {
173*4882a593Smuzhiyun int ret = -EBUSY;
174*4882a593Smuzhiyun
175*4882a593Smuzhiyun mutex_lock(&oprofile_perf_mutex);
176*4882a593Smuzhiyun if (!oprofile_perf_enabled) {
177*4882a593Smuzhiyun ret = 0;
178*4882a593Smuzhiyun op_perf_start();
179*4882a593Smuzhiyun oprofile_perf_enabled = 1;
180*4882a593Smuzhiyun }
181*4882a593Smuzhiyun mutex_unlock(&oprofile_perf_mutex);
182*4882a593Smuzhiyun return ret;
183*4882a593Smuzhiyun }
184*4882a593Smuzhiyun
oprofile_perf_stop(void)185*4882a593Smuzhiyun static void oprofile_perf_stop(void)
186*4882a593Smuzhiyun {
187*4882a593Smuzhiyun mutex_lock(&oprofile_perf_mutex);
188*4882a593Smuzhiyun if (oprofile_perf_enabled)
189*4882a593Smuzhiyun op_perf_stop();
190*4882a593Smuzhiyun oprofile_perf_enabled = 0;
191*4882a593Smuzhiyun mutex_unlock(&oprofile_perf_mutex);
192*4882a593Smuzhiyun }
193*4882a593Smuzhiyun
194*4882a593Smuzhiyun #ifdef CONFIG_PM
195*4882a593Smuzhiyun
oprofile_perf_suspend(struct platform_device * dev,pm_message_t state)196*4882a593Smuzhiyun static int oprofile_perf_suspend(struct platform_device *dev, pm_message_t state)
197*4882a593Smuzhiyun {
198*4882a593Smuzhiyun mutex_lock(&oprofile_perf_mutex);
199*4882a593Smuzhiyun if (oprofile_perf_enabled)
200*4882a593Smuzhiyun op_perf_stop();
201*4882a593Smuzhiyun mutex_unlock(&oprofile_perf_mutex);
202*4882a593Smuzhiyun return 0;
203*4882a593Smuzhiyun }
204*4882a593Smuzhiyun
oprofile_perf_resume(struct platform_device * dev)205*4882a593Smuzhiyun static int oprofile_perf_resume(struct platform_device *dev)
206*4882a593Smuzhiyun {
207*4882a593Smuzhiyun mutex_lock(&oprofile_perf_mutex);
208*4882a593Smuzhiyun if (oprofile_perf_enabled && op_perf_start())
209*4882a593Smuzhiyun oprofile_perf_enabled = 0;
210*4882a593Smuzhiyun mutex_unlock(&oprofile_perf_mutex);
211*4882a593Smuzhiyun return 0;
212*4882a593Smuzhiyun }
213*4882a593Smuzhiyun
214*4882a593Smuzhiyun static struct platform_driver oprofile_driver = {
215*4882a593Smuzhiyun .driver = {
216*4882a593Smuzhiyun .name = "oprofile-perf",
217*4882a593Smuzhiyun },
218*4882a593Smuzhiyun .resume = oprofile_perf_resume,
219*4882a593Smuzhiyun .suspend = oprofile_perf_suspend,
220*4882a593Smuzhiyun };
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun static struct platform_device *oprofile_pdev;
223*4882a593Smuzhiyun
init_driverfs(void)224*4882a593Smuzhiyun static int __init init_driverfs(void)
225*4882a593Smuzhiyun {
226*4882a593Smuzhiyun int ret;
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun ret = platform_driver_register(&oprofile_driver);
229*4882a593Smuzhiyun if (ret)
230*4882a593Smuzhiyun return ret;
231*4882a593Smuzhiyun
232*4882a593Smuzhiyun oprofile_pdev = platform_device_register_simple(
233*4882a593Smuzhiyun oprofile_driver.driver.name, 0, NULL, 0);
234*4882a593Smuzhiyun if (IS_ERR(oprofile_pdev)) {
235*4882a593Smuzhiyun ret = PTR_ERR(oprofile_pdev);
236*4882a593Smuzhiyun platform_driver_unregister(&oprofile_driver);
237*4882a593Smuzhiyun }
238*4882a593Smuzhiyun
239*4882a593Smuzhiyun return ret;
240*4882a593Smuzhiyun }
241*4882a593Smuzhiyun
exit_driverfs(void)242*4882a593Smuzhiyun static void exit_driverfs(void)
243*4882a593Smuzhiyun {
244*4882a593Smuzhiyun platform_device_unregister(oprofile_pdev);
245*4882a593Smuzhiyun platform_driver_unregister(&oprofile_driver);
246*4882a593Smuzhiyun }
247*4882a593Smuzhiyun
248*4882a593Smuzhiyun #else
249*4882a593Smuzhiyun
init_driverfs(void)250*4882a593Smuzhiyun static inline int init_driverfs(void) { return 0; }
exit_driverfs(void)251*4882a593Smuzhiyun static inline void exit_driverfs(void) { }
252*4882a593Smuzhiyun
253*4882a593Smuzhiyun #endif /* CONFIG_PM */
254*4882a593Smuzhiyun
oprofile_perf_exit(void)255*4882a593Smuzhiyun void oprofile_perf_exit(void)
256*4882a593Smuzhiyun {
257*4882a593Smuzhiyun int cpu, id;
258*4882a593Smuzhiyun struct perf_event *event;
259*4882a593Smuzhiyun
260*4882a593Smuzhiyun for_each_possible_cpu(cpu) {
261*4882a593Smuzhiyun for (id = 0; id < num_counters; ++id) {
262*4882a593Smuzhiyun event = per_cpu(perf_events, cpu)[id];
263*4882a593Smuzhiyun if (event)
264*4882a593Smuzhiyun perf_event_release_kernel(event);
265*4882a593Smuzhiyun }
266*4882a593Smuzhiyun
267*4882a593Smuzhiyun kfree(per_cpu(perf_events, cpu));
268*4882a593Smuzhiyun }
269*4882a593Smuzhiyun
270*4882a593Smuzhiyun kfree(counter_config);
271*4882a593Smuzhiyun exit_driverfs();
272*4882a593Smuzhiyun }
273*4882a593Smuzhiyun
oprofile_perf_init(struct oprofile_operations * ops)274*4882a593Smuzhiyun int __init oprofile_perf_init(struct oprofile_operations *ops)
275*4882a593Smuzhiyun {
276*4882a593Smuzhiyun int cpu, ret = 0;
277*4882a593Smuzhiyun
278*4882a593Smuzhiyun ret = init_driverfs();
279*4882a593Smuzhiyun if (ret)
280*4882a593Smuzhiyun return ret;
281*4882a593Smuzhiyun
282*4882a593Smuzhiyun num_counters = perf_num_counters();
283*4882a593Smuzhiyun if (num_counters <= 0) {
284*4882a593Smuzhiyun pr_info("oprofile: no performance counters\n");
285*4882a593Smuzhiyun ret = -ENODEV;
286*4882a593Smuzhiyun goto out;
287*4882a593Smuzhiyun }
288*4882a593Smuzhiyun
289*4882a593Smuzhiyun counter_config = kcalloc(num_counters,
290*4882a593Smuzhiyun sizeof(struct op_counter_config), GFP_KERNEL);
291*4882a593Smuzhiyun
292*4882a593Smuzhiyun if (!counter_config) {
293*4882a593Smuzhiyun pr_info("oprofile: failed to allocate %d "
294*4882a593Smuzhiyun "counters\n", num_counters);
295*4882a593Smuzhiyun ret = -ENOMEM;
296*4882a593Smuzhiyun num_counters = 0;
297*4882a593Smuzhiyun goto out;
298*4882a593Smuzhiyun }
299*4882a593Smuzhiyun
300*4882a593Smuzhiyun for_each_possible_cpu(cpu) {
301*4882a593Smuzhiyun per_cpu(perf_events, cpu) = kcalloc(num_counters,
302*4882a593Smuzhiyun sizeof(struct perf_event *), GFP_KERNEL);
303*4882a593Smuzhiyun if (!per_cpu(perf_events, cpu)) {
304*4882a593Smuzhiyun pr_info("oprofile: failed to allocate %d perf events "
305*4882a593Smuzhiyun "for cpu %d\n", num_counters, cpu);
306*4882a593Smuzhiyun ret = -ENOMEM;
307*4882a593Smuzhiyun goto out;
308*4882a593Smuzhiyun }
309*4882a593Smuzhiyun }
310*4882a593Smuzhiyun
311*4882a593Smuzhiyun ops->create_files = oprofile_perf_create_files;
312*4882a593Smuzhiyun ops->setup = oprofile_perf_setup;
313*4882a593Smuzhiyun ops->start = oprofile_perf_start;
314*4882a593Smuzhiyun ops->stop = oprofile_perf_stop;
315*4882a593Smuzhiyun ops->shutdown = oprofile_perf_stop;
316*4882a593Smuzhiyun ops->cpu_type = op_name_from_perf_id();
317*4882a593Smuzhiyun
318*4882a593Smuzhiyun if (!ops->cpu_type)
319*4882a593Smuzhiyun ret = -ENODEV;
320*4882a593Smuzhiyun else
321*4882a593Smuzhiyun pr_info("oprofile: using %s\n", ops->cpu_type);
322*4882a593Smuzhiyun
323*4882a593Smuzhiyun out:
324*4882a593Smuzhiyun if (ret)
325*4882a593Smuzhiyun oprofile_perf_exit();
326*4882a593Smuzhiyun
327*4882a593Smuzhiyun return ret;
328*4882a593Smuzhiyun }
329