1/* 2 * trace event based perf event profiling/tracing 3 * 4 * Copyright (C) 2009 Red Hat Inc, Peter Zijlstra <pzijlstr@redhat.com> 5 * Copyright (C) 2009-2010 Frederic Weisbecker <fweisbec@gmail.com> 6 */ 7 8#include <linux/module.h> 9#include <linux/kprobes.h> 10#include "trace.h" 11 12static char *perf_trace_buf[4]; 13 14/* 15 * Force it to be aligned to unsigned long to avoid misaligned accesses 16 * suprises 17 */ 18typedef typeof(unsigned long [PERF_MAX_TRACE_SIZE / sizeof(unsigned long)]) 19 perf_trace_t; 20 21/* Count the events in use (per event id, not per instance) */ 22static int total_ref_count; 23 24static int perf_trace_event_init(struct ftrace_event_call *tp_event, 25 struct perf_event *p_event) 26{ 27 struct hlist_head *list; 28 int ret = -ENOMEM; 29 int cpu; 30 31 p_event->tp_event = tp_event; 32 if (tp_event->perf_refcount++ > 0) 33 return 0; 34 35 list = alloc_percpu(struct hlist_head); 36 if (!list) 37 goto fail; 38 39 for_each_possible_cpu(cpu) 40 INIT_HLIST_HEAD(per_cpu_ptr(list, cpu)); 41 42 tp_event->perf_events = list; 43 44 if (!total_ref_count) { 45 char *buf; 46 int i; 47 48 for (i = 0; i < 4; i++) { 49 buf = (char *)alloc_percpu(perf_trace_t); 50 if (!buf) 51 goto fail; 52 53 perf_trace_buf[i] = buf; 54 } 55 } 56 57 ret = tp_event->class->reg(tp_event, TRACE_REG_PERF_REGISTER); 58 if (ret) 59 goto fail; 60 61 total_ref_count++; 62 return 0; 63 64fail: 65 if (!total_ref_count) { 66 int i; 67 68 for (i = 0; i < 4; i++) { 69 free_percpu(perf_trace_buf[i]); 70 perf_trace_buf[i] = NULL; 71 } 72 } 73 74 if (!--tp_event->perf_refcount) { 75 free_percpu(tp_event->perf_events); 76 tp_event->perf_events = NULL; 77 } 78 79 return ret; 80} 81 82int perf_trace_init(struct perf_event *p_event) 83{ 84 struct ftrace_event_call *tp_event; 85 int event_id = p_event->attr.config; 86 int ret = -EINVAL; 87 88 mutex_lock(&event_mutex); 89 list_for_each_entry(tp_event, &ftrace_events, list) { 90 if (tp_event->event.type == event_id && 91 tp_event->class && tp_event->class->reg && 92 try_module_get(tp_event->mod)) { 93 ret = perf_trace_event_init(tp_event, p_event); 94 if (ret) 95 module_put(tp_event->mod); 96 break; 97 } 98 } 99 mutex_unlock(&event_mutex); 100 101 return ret; 102} 103 104int perf_trace_enable(struct perf_event *p_event) 105{ 106 struct ftrace_event_call *tp_event = p_event->tp_event; 107 struct hlist_head *list; 108 109 list = tp_event->perf_events; 110 if (WARN_ON_ONCE(!list)) 111 return -EINVAL; 112 113 list = this_cpu_ptr(list); 114 hlist_add_head_rcu(&p_event->hlist_entry, list); 115 116 return 0; 117} 118 119void perf_trace_disable(struct perf_event *p_event) 120{ 121 hlist_del_rcu(&p_event->hlist_entry); 122} 123 124void perf_trace_destroy(struct perf_event *p_event) 125{ 126 struct ftrace_event_call *tp_event = p_event->tp_event; 127 int i; 128 129 mutex_lock(&event_mutex); 130 if (--tp_event->perf_refcount > 0) 131 goto out; 132 133 tp_event->class->reg(tp_event, TRACE_REG_PERF_UNREGISTER); 134 135 /* 136 * Ensure our callback won't be called anymore. The buffers 137 * will be freed after that. 138 */ 139 tracepoint_synchronize_unregister(); 140 141 free_percpu(tp_event->perf_events); 142 tp_event->perf_events = NULL; 143 144 if (!--total_ref_count) { 145 for (i = 0; i < 4; i++) { 146 free_percpu(perf_trace_buf[i]); 147 perf_trace_buf[i] = NULL; 148 } 149 } 150out: 151 module_put(tp_event->mod); 152 mutex_unlock(&event_mutex); 153} 154 155__kprobes void *perf_trace_buf_prepare(int size, unsigned short type, 156 struct pt_regs *regs, int *rctxp) 157{ 158 struct trace_entry *entry; 159 unsigned long flags; 160 char *raw_data; 161 int pc; 162 163 BUILD_BUG_ON(PERF_MAX_TRACE_SIZE % sizeof(unsigned long)); 164 165 pc = preempt_count(); 166 167 *rctxp = perf_swevent_get_recursion_context(); 168 if (*rctxp < 0) 169 return NULL; 170 171 raw_data = this_cpu_ptr(perf_trace_buf[*rctxp]); 172 173 /* zero the dead bytes from align to not leak stack to user */ 174 memset(&raw_data[size - sizeof(u64)], 0, sizeof(u64)); 175 176 entry = (struct trace_entry *)raw_data; 177 local_save_flags(flags); 178 tracing_generic_entry_update(entry, flags, pc); 179 entry->type = type; 180 181 return raw_data; 182} 183EXPORT_SYMBOL_GPL(perf_trace_buf_prepare); 184