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 12 static char *perf_trace_buf[4]; 13 14 /* 15 * Force it to be aligned to unsigned long to avoid misaligned accesses 16 * suprises 17 */ 18 typedef 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) */ 22 static int total_ref_count; 23 24 static 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 64 fail: 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 82 int 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 104 int 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 119 void perf_trace_disable(struct perf_event *p_event) 120 { 121 hlist_del_rcu(&p_event->hlist_entry); 122 } 123 124 void 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 } 150 out: 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 } 183 EXPORT_SYMBOL_GPL(perf_trace_buf_prepare); 184