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