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