1bcea3f96SSteven Rostedt (VMware) // SPDX-License-Identifier: GPL-2.0 297d5a220SFrederic Weisbecker /* 397d5a220SFrederic Weisbecker * trace event based perf event profiling/tracing 497d5a220SFrederic Weisbecker * 590eec103SPeter Zijlstra * Copyright (C) 2009 Red Hat Inc, Peter Zijlstra 697d5a220SFrederic Weisbecker * Copyright (C) 2009-2010 Frederic Weisbecker <fweisbec@gmail.com> 797d5a220SFrederic Weisbecker */ 897d5a220SFrederic Weisbecker 997d5a220SFrederic Weisbecker #include <linux/module.h> 1097d5a220SFrederic Weisbecker #include <linux/kprobes.h> 1197d5a220SFrederic Weisbecker #include "trace.h" 12e12f03d7SSong Liu #include "trace_probe.h" 1397d5a220SFrederic Weisbecker 146016ee13SNamhyung Kim static char __percpu *perf_trace_buf[PERF_NR_CONTEXTS]; 1597d5a220SFrederic Weisbecker 16eb1e7961SFrederic Weisbecker /* 17eb1e7961SFrederic Weisbecker * Force it to be aligned to unsigned long to avoid misaligned accesses 18eb1e7961SFrederic Weisbecker * suprises 19eb1e7961SFrederic Weisbecker */ 20eb1e7961SFrederic Weisbecker typedef typeof(unsigned long [PERF_MAX_TRACE_SIZE / sizeof(unsigned long)]) 21eb1e7961SFrederic Weisbecker perf_trace_t; 2297d5a220SFrederic Weisbecker 2397d5a220SFrederic Weisbecker /* Count the events in use (per event id, not per instance) */ 2497d5a220SFrederic Weisbecker static int total_ref_count; 2597d5a220SFrederic Weisbecker 262425bcb9SSteven Rostedt (Red Hat) static int perf_trace_event_perm(struct trace_event_call *tp_event, 2761c32659SFrederic Weisbecker struct perf_event *p_event) 2861c32659SFrederic Weisbecker { 29d5b5f391SPeter Zijlstra if (tp_event->perf_perm) { 30d5b5f391SPeter Zijlstra int ret = tp_event->perf_perm(tp_event, p_event); 31d5b5f391SPeter Zijlstra if (ret) 32d5b5f391SPeter Zijlstra return ret; 33d5b5f391SPeter Zijlstra } 34d5b5f391SPeter Zijlstra 35f4be073dSJiri Olsa /* 36f4be073dSJiri Olsa * We checked and allowed to create parent, 37f4be073dSJiri Olsa * allow children without checking. 38f4be073dSJiri Olsa */ 39f4be073dSJiri Olsa if (p_event->parent) 40f4be073dSJiri Olsa return 0; 41f4be073dSJiri Olsa 42f4be073dSJiri Olsa /* 43f4be073dSJiri Olsa * It's ok to check current process (owner) permissions in here, 44f4be073dSJiri Olsa * because code below is called only via perf_event_open syscall. 45f4be073dSJiri Olsa */ 46f4be073dSJiri Olsa 47ced39002SJiri Olsa /* The ftrace function trace is allowed only for root. */ 48cfa77bc4SJiri Olsa if (ftrace_event_is_function(tp_event)) { 49cfa77bc4SJiri Olsa if (perf_paranoid_tracepoint_raw() && !capable(CAP_SYS_ADMIN)) 50ced39002SJiri Olsa return -EPERM; 51ced39002SJiri Olsa 520a74c5b3SJiri Olsa if (!is_sampling_event(p_event)) 530a74c5b3SJiri Olsa return 0; 540a74c5b3SJiri Olsa 55cfa77bc4SJiri Olsa /* 56cfa77bc4SJiri Olsa * We don't allow user space callchains for function trace 57cfa77bc4SJiri Olsa * event, due to issues with page faults while tracing page 58cfa77bc4SJiri Olsa * fault handler and its overall trickiness nature. 59cfa77bc4SJiri Olsa */ 60cfa77bc4SJiri Olsa if (!p_event->attr.exclude_callchain_user) 61cfa77bc4SJiri Olsa return -EINVAL; 6263c45f4bSJiri Olsa 6363c45f4bSJiri Olsa /* 6463c45f4bSJiri Olsa * Same reason to disable user stack dump as for user space 6563c45f4bSJiri Olsa * callchains above. 6663c45f4bSJiri Olsa */ 6763c45f4bSJiri Olsa if (p_event->attr.sample_type & PERF_SAMPLE_STACK_USER) 6863c45f4bSJiri Olsa return -EINVAL; 69cfa77bc4SJiri Olsa } 70cfa77bc4SJiri Olsa 7161c32659SFrederic Weisbecker /* No tracing, just counting, so no obvious leak */ 7261c32659SFrederic Weisbecker if (!(p_event->attr.sample_type & PERF_SAMPLE_RAW)) 7361c32659SFrederic Weisbecker return 0; 7461c32659SFrederic Weisbecker 7561c32659SFrederic Weisbecker /* Some events are ok to be traced by non-root users... */ 7661c32659SFrederic Weisbecker if (p_event->attach_state == PERF_ATTACH_TASK) { 7761c32659SFrederic Weisbecker if (tp_event->flags & TRACE_EVENT_FL_CAP_ANY) 7861c32659SFrederic Weisbecker return 0; 7961c32659SFrederic Weisbecker } 8061c32659SFrederic Weisbecker 8161c32659SFrederic Weisbecker /* 8261c32659SFrederic Weisbecker * ...otherwise raw tracepoint data can be a severe data leak, 8361c32659SFrederic Weisbecker * only allow root to have these. 8461c32659SFrederic Weisbecker */ 8561c32659SFrederic Weisbecker if (perf_paranoid_tracepoint_raw() && !capable(CAP_SYS_ADMIN)) 8661c32659SFrederic Weisbecker return -EPERM; 8761c32659SFrederic Weisbecker 8861c32659SFrederic Weisbecker return 0; 8961c32659SFrederic Weisbecker } 9061c32659SFrederic Weisbecker 912425bcb9SSteven Rostedt (Red Hat) static int perf_trace_event_reg(struct trace_event_call *tp_event, 921c024ecaSPeter Zijlstra struct perf_event *p_event) 9397d5a220SFrederic Weisbecker { 946016ee13SNamhyung Kim struct hlist_head __percpu *list; 95ceec0b6fSJiri Olsa int ret = -ENOMEM; 961c024ecaSPeter Zijlstra int cpu; 9797d5a220SFrederic Weisbecker 981c024ecaSPeter Zijlstra p_event->tp_event = tp_event; 991c024ecaSPeter Zijlstra if (tp_event->perf_refcount++ > 0) 10097d5a220SFrederic Weisbecker return 0; 10197d5a220SFrederic Weisbecker 1021c024ecaSPeter Zijlstra list = alloc_percpu(struct hlist_head); 1031c024ecaSPeter Zijlstra if (!list) 1041c024ecaSPeter Zijlstra goto fail; 1051c024ecaSPeter Zijlstra 1061c024ecaSPeter Zijlstra for_each_possible_cpu(cpu) 1071c024ecaSPeter Zijlstra INIT_HLIST_HEAD(per_cpu_ptr(list, cpu)); 1081c024ecaSPeter Zijlstra 1091c024ecaSPeter Zijlstra tp_event->perf_events = list; 11097d5a220SFrederic Weisbecker 11197d5a220SFrederic Weisbecker if (!total_ref_count) { 1126016ee13SNamhyung Kim char __percpu *buf; 113b7e2ecefSPeter Zijlstra int i; 114b7e2ecefSPeter Zijlstra 1157ae07ea3SFrederic Weisbecker for (i = 0; i < PERF_NR_CONTEXTS; i++) { 1166016ee13SNamhyung Kim buf = (char __percpu *)alloc_percpu(perf_trace_t); 11797d5a220SFrederic Weisbecker if (!buf) 1181c024ecaSPeter Zijlstra goto fail; 11997d5a220SFrederic Weisbecker 1201c024ecaSPeter Zijlstra perf_trace_buf[i] = buf; 121b7e2ecefSPeter Zijlstra } 12297d5a220SFrederic Weisbecker } 12397d5a220SFrederic Weisbecker 124ceec0b6fSJiri Olsa ret = tp_event->class->reg(tp_event, TRACE_REG_PERF_REGISTER, NULL); 1251c024ecaSPeter Zijlstra if (ret) 1261c024ecaSPeter Zijlstra goto fail; 1271c024ecaSPeter Zijlstra 12897d5a220SFrederic Weisbecker total_ref_count++; 12997d5a220SFrederic Weisbecker return 0; 13097d5a220SFrederic Weisbecker 1311c024ecaSPeter Zijlstra fail: 132b7e2ecefSPeter Zijlstra if (!total_ref_count) { 133b7e2ecefSPeter Zijlstra int i; 134b7e2ecefSPeter Zijlstra 1357ae07ea3SFrederic Weisbecker for (i = 0; i < PERF_NR_CONTEXTS; i++) { 136b7e2ecefSPeter Zijlstra free_percpu(perf_trace_buf[i]); 137b7e2ecefSPeter Zijlstra perf_trace_buf[i] = NULL; 138b7e2ecefSPeter Zijlstra } 13997d5a220SFrederic Weisbecker } 14097d5a220SFrederic Weisbecker 1411c024ecaSPeter Zijlstra if (!--tp_event->perf_refcount) { 1421c024ecaSPeter Zijlstra free_percpu(tp_event->perf_events); 1431c024ecaSPeter Zijlstra tp_event->perf_events = NULL; 14497d5a220SFrederic Weisbecker } 14597d5a220SFrederic Weisbecker 14697d5a220SFrederic Weisbecker return ret; 14797d5a220SFrederic Weisbecker } 14897d5a220SFrederic Weisbecker 149ceec0b6fSJiri Olsa static void perf_trace_event_unreg(struct perf_event *p_event) 150ceec0b6fSJiri Olsa { 1512425bcb9SSteven Rostedt (Red Hat) struct trace_event_call *tp_event = p_event->tp_event; 152ceec0b6fSJiri Olsa int i; 153ceec0b6fSJiri Olsa 154ceec0b6fSJiri Olsa if (--tp_event->perf_refcount > 0) 155ceec0b6fSJiri Olsa goto out; 156ceec0b6fSJiri Olsa 157ceec0b6fSJiri Olsa tp_event->class->reg(tp_event, TRACE_REG_PERF_UNREGISTER, NULL); 158ceec0b6fSJiri Olsa 159ceec0b6fSJiri Olsa /* 160ceec0b6fSJiri Olsa * Ensure our callback won't be called anymore. The buffers 161ceec0b6fSJiri Olsa * will be freed after that. 162ceec0b6fSJiri Olsa */ 163ceec0b6fSJiri Olsa tracepoint_synchronize_unregister(); 164ceec0b6fSJiri Olsa 165ceec0b6fSJiri Olsa free_percpu(tp_event->perf_events); 166ceec0b6fSJiri Olsa tp_event->perf_events = NULL; 167ceec0b6fSJiri Olsa 168ceec0b6fSJiri Olsa if (!--total_ref_count) { 169ceec0b6fSJiri Olsa for (i = 0; i < PERF_NR_CONTEXTS; i++) { 170ceec0b6fSJiri Olsa free_percpu(perf_trace_buf[i]); 171ceec0b6fSJiri Olsa perf_trace_buf[i] = NULL; 172ceec0b6fSJiri Olsa } 173ceec0b6fSJiri Olsa } 174ceec0b6fSJiri Olsa out: 175ceec0b6fSJiri Olsa module_put(tp_event->mod); 176ceec0b6fSJiri Olsa } 177ceec0b6fSJiri Olsa 178ceec0b6fSJiri Olsa static int perf_trace_event_open(struct perf_event *p_event) 179ceec0b6fSJiri Olsa { 1802425bcb9SSteven Rostedt (Red Hat) struct trace_event_call *tp_event = p_event->tp_event; 181ceec0b6fSJiri Olsa return tp_event->class->reg(tp_event, TRACE_REG_PERF_OPEN, p_event); 182ceec0b6fSJiri Olsa } 183ceec0b6fSJiri Olsa 184ceec0b6fSJiri Olsa static void perf_trace_event_close(struct perf_event *p_event) 185ceec0b6fSJiri Olsa { 1862425bcb9SSteven Rostedt (Red Hat) struct trace_event_call *tp_event = p_event->tp_event; 187ceec0b6fSJiri Olsa tp_event->class->reg(tp_event, TRACE_REG_PERF_CLOSE, p_event); 188ceec0b6fSJiri Olsa } 189ceec0b6fSJiri Olsa 1902425bcb9SSteven Rostedt (Red Hat) static int perf_trace_event_init(struct trace_event_call *tp_event, 191ceec0b6fSJiri Olsa struct perf_event *p_event) 192ceec0b6fSJiri Olsa { 193ceec0b6fSJiri Olsa int ret; 194ceec0b6fSJiri Olsa 195ceec0b6fSJiri Olsa ret = perf_trace_event_perm(tp_event, p_event); 196ceec0b6fSJiri Olsa if (ret) 197ceec0b6fSJiri Olsa return ret; 198ceec0b6fSJiri Olsa 199ceec0b6fSJiri Olsa ret = perf_trace_event_reg(tp_event, p_event); 200ceec0b6fSJiri Olsa if (ret) 201ceec0b6fSJiri Olsa return ret; 202ceec0b6fSJiri Olsa 203ceec0b6fSJiri Olsa ret = perf_trace_event_open(p_event); 204ceec0b6fSJiri Olsa if (ret) { 205ceec0b6fSJiri Olsa perf_trace_event_unreg(p_event); 206ceec0b6fSJiri Olsa return ret; 207ceec0b6fSJiri Olsa } 208ceec0b6fSJiri Olsa 209ceec0b6fSJiri Olsa return 0; 210ceec0b6fSJiri Olsa } 211ceec0b6fSJiri Olsa 2121c024ecaSPeter Zijlstra int perf_trace_init(struct perf_event *p_event) 21397d5a220SFrederic Weisbecker { 2142425bcb9SSteven Rostedt (Red Hat) struct trace_event_call *tp_event; 2150022ceddSVince Weaver u64 event_id = p_event->attr.config; 21697d5a220SFrederic Weisbecker int ret = -EINVAL; 21797d5a220SFrederic Weisbecker 21897d5a220SFrederic Weisbecker mutex_lock(&event_mutex); 2191c024ecaSPeter Zijlstra list_for_each_entry(tp_event, &ftrace_events, list) { 220ff5f149bSSteven Rostedt if (tp_event->event.type == event_id && 221a1d0ce82SSteven Rostedt tp_event->class && tp_event->class->reg && 2221c024ecaSPeter Zijlstra try_module_get(tp_event->mod)) { 2231c024ecaSPeter Zijlstra ret = perf_trace_event_init(tp_event, p_event); 2249cb627d5SLi Zefan if (ret) 2259cb627d5SLi Zefan module_put(tp_event->mod); 22697d5a220SFrederic Weisbecker break; 22797d5a220SFrederic Weisbecker } 22897d5a220SFrederic Weisbecker } 22997d5a220SFrederic Weisbecker mutex_unlock(&event_mutex); 23097d5a220SFrederic Weisbecker 23197d5a220SFrederic Weisbecker return ret; 23297d5a220SFrederic Weisbecker } 23397d5a220SFrederic Weisbecker 234ceec0b6fSJiri Olsa void perf_trace_destroy(struct perf_event *p_event) 235ceec0b6fSJiri Olsa { 236ceec0b6fSJiri Olsa mutex_lock(&event_mutex); 237ceec0b6fSJiri Olsa perf_trace_event_close(p_event); 238ceec0b6fSJiri Olsa perf_trace_event_unreg(p_event); 239ceec0b6fSJiri Olsa mutex_unlock(&event_mutex); 240ceec0b6fSJiri Olsa } 241ceec0b6fSJiri Olsa 242e12f03d7SSong Liu #ifdef CONFIG_KPROBE_EVENTS 243e12f03d7SSong Liu int perf_kprobe_init(struct perf_event *p_event, bool is_retprobe) 244e12f03d7SSong Liu { 245e12f03d7SSong Liu int ret; 246e12f03d7SSong Liu char *func = NULL; 247e12f03d7SSong Liu struct trace_event_call *tp_event; 248e12f03d7SSong Liu 249e12f03d7SSong Liu if (p_event->attr.kprobe_func) { 250e12f03d7SSong Liu func = kzalloc(KSYM_NAME_LEN, GFP_KERNEL); 251e12f03d7SSong Liu if (!func) 252e12f03d7SSong Liu return -ENOMEM; 253e12f03d7SSong Liu ret = strncpy_from_user( 254e12f03d7SSong Liu func, u64_to_user_ptr(p_event->attr.kprobe_func), 255e12f03d7SSong Liu KSYM_NAME_LEN); 2565da13ab8SMasami Hiramatsu if (ret == KSYM_NAME_LEN) 2575da13ab8SMasami Hiramatsu ret = -E2BIG; 258e12f03d7SSong Liu if (ret < 0) 259e12f03d7SSong Liu goto out; 260e12f03d7SSong Liu 261e12f03d7SSong Liu if (func[0] == '\0') { 262e12f03d7SSong Liu kfree(func); 263e12f03d7SSong Liu func = NULL; 264e12f03d7SSong Liu } 265e12f03d7SSong Liu } 266e12f03d7SSong Liu 267e12f03d7SSong Liu tp_event = create_local_trace_kprobe( 268e12f03d7SSong Liu func, (void *)(unsigned long)(p_event->attr.kprobe_addr), 269e12f03d7SSong Liu p_event->attr.probe_offset, is_retprobe); 270e12f03d7SSong Liu if (IS_ERR(tp_event)) { 271e12f03d7SSong Liu ret = PTR_ERR(tp_event); 272e12f03d7SSong Liu goto out; 273e12f03d7SSong Liu } 274e12f03d7SSong Liu 275e12f03d7SSong Liu ret = perf_trace_event_init(tp_event, p_event); 276e12f03d7SSong Liu if (ret) 277e12f03d7SSong Liu destroy_local_trace_kprobe(tp_event); 278e12f03d7SSong Liu out: 279e12f03d7SSong Liu kfree(func); 280e12f03d7SSong Liu return ret; 281e12f03d7SSong Liu } 282e12f03d7SSong Liu 283e12f03d7SSong Liu void perf_kprobe_destroy(struct perf_event *p_event) 284e12f03d7SSong Liu { 285e12f03d7SSong Liu perf_trace_event_close(p_event); 286e12f03d7SSong Liu perf_trace_event_unreg(p_event); 287e12f03d7SSong Liu 288e12f03d7SSong Liu destroy_local_trace_kprobe(p_event->tp_event); 289e12f03d7SSong Liu } 290e12f03d7SSong Liu #endif /* CONFIG_KPROBE_EVENTS */ 291e12f03d7SSong Liu 29233ea4b24SSong Liu #ifdef CONFIG_UPROBE_EVENTS 293*a6ca88b2SSong Liu int perf_uprobe_init(struct perf_event *p_event, 294*a6ca88b2SSong Liu unsigned long ref_ctr_offset, bool is_retprobe) 29533ea4b24SSong Liu { 29633ea4b24SSong Liu int ret; 29733ea4b24SSong Liu char *path = NULL; 29833ea4b24SSong Liu struct trace_event_call *tp_event; 29933ea4b24SSong Liu 30033ea4b24SSong Liu if (!p_event->attr.uprobe_path) 30133ea4b24SSong Liu return -EINVAL; 30233ea4b24SSong Liu path = kzalloc(PATH_MAX, GFP_KERNEL); 30333ea4b24SSong Liu if (!path) 30433ea4b24SSong Liu return -ENOMEM; 30533ea4b24SSong Liu ret = strncpy_from_user( 30633ea4b24SSong Liu path, u64_to_user_ptr(p_event->attr.uprobe_path), PATH_MAX); 3070eadcc7aSSong Liu if (ret == PATH_MAX) 3080eadcc7aSSong Liu return -E2BIG; 30933ea4b24SSong Liu if (ret < 0) 31033ea4b24SSong Liu goto out; 31133ea4b24SSong Liu if (path[0] == '\0') { 31233ea4b24SSong Liu ret = -EINVAL; 31333ea4b24SSong Liu goto out; 31433ea4b24SSong Liu } 31533ea4b24SSong Liu 316*a6ca88b2SSong Liu tp_event = create_local_trace_uprobe(path, p_event->attr.probe_offset, 317*a6ca88b2SSong Liu ref_ctr_offset, is_retprobe); 31833ea4b24SSong Liu if (IS_ERR(tp_event)) { 31933ea4b24SSong Liu ret = PTR_ERR(tp_event); 32033ea4b24SSong Liu goto out; 32133ea4b24SSong Liu } 32233ea4b24SSong Liu 32333ea4b24SSong Liu /* 32433ea4b24SSong Liu * local trace_uprobe need to hold event_mutex to call 32533ea4b24SSong Liu * uprobe_buffer_enable() and uprobe_buffer_disable(). 32633ea4b24SSong Liu * event_mutex is not required for local trace_kprobes. 32733ea4b24SSong Liu */ 32833ea4b24SSong Liu mutex_lock(&event_mutex); 32933ea4b24SSong Liu ret = perf_trace_event_init(tp_event, p_event); 33033ea4b24SSong Liu if (ret) 33133ea4b24SSong Liu destroy_local_trace_uprobe(tp_event); 33233ea4b24SSong Liu mutex_unlock(&event_mutex); 33333ea4b24SSong Liu out: 33433ea4b24SSong Liu kfree(path); 33533ea4b24SSong Liu return ret; 33633ea4b24SSong Liu } 33733ea4b24SSong Liu 33833ea4b24SSong Liu void perf_uprobe_destroy(struct perf_event *p_event) 33933ea4b24SSong Liu { 34033ea4b24SSong Liu mutex_lock(&event_mutex); 34133ea4b24SSong Liu perf_trace_event_close(p_event); 34233ea4b24SSong Liu perf_trace_event_unreg(p_event); 34333ea4b24SSong Liu mutex_unlock(&event_mutex); 34433ea4b24SSong Liu destroy_local_trace_uprobe(p_event->tp_event); 34533ea4b24SSong Liu } 34633ea4b24SSong Liu #endif /* CONFIG_UPROBE_EVENTS */ 34733ea4b24SSong Liu 348a4eaf7f1SPeter Zijlstra int perf_trace_add(struct perf_event *p_event, int flags) 34997d5a220SFrederic Weisbecker { 3502425bcb9SSteven Rostedt (Red Hat) struct trace_event_call *tp_event = p_event->tp_event; 351466c81c4SPeter Zijlstra 352466c81c4SPeter Zijlstra if (!(flags & PERF_EF_START)) 353466c81c4SPeter Zijlstra p_event->hw.state = PERF_HES_STOPPED; 354466c81c4SPeter Zijlstra 355466c81c4SPeter Zijlstra /* 356466c81c4SPeter Zijlstra * If TRACE_REG_PERF_ADD returns false; no custom action was performed 357466c81c4SPeter Zijlstra * and we need to take the default action of enqueueing our event on 358466c81c4SPeter Zijlstra * the right per-cpu hlist. 359466c81c4SPeter Zijlstra */ 360466c81c4SPeter Zijlstra if (!tp_event->class->reg(tp_event, TRACE_REG_PERF_ADD, p_event)) { 3616016ee13SNamhyung Kim struct hlist_head __percpu *pcpu_list; 3621c024ecaSPeter Zijlstra struct hlist_head *list; 36397d5a220SFrederic Weisbecker 3646016ee13SNamhyung Kim pcpu_list = tp_event->perf_events; 3656016ee13SNamhyung Kim if (WARN_ON_ONCE(!pcpu_list)) 3661c024ecaSPeter Zijlstra return -EINVAL; 36797d5a220SFrederic Weisbecker 3686016ee13SNamhyung Kim list = this_cpu_ptr(pcpu_list); 3691c024ecaSPeter Zijlstra hlist_add_head_rcu(&p_event->hlist_entry, list); 370466c81c4SPeter Zijlstra } 3711c024ecaSPeter Zijlstra 372466c81c4SPeter Zijlstra return 0; 3731c024ecaSPeter Zijlstra } 3741c024ecaSPeter Zijlstra 375a4eaf7f1SPeter Zijlstra void perf_trace_del(struct perf_event *p_event, int flags) 3761c024ecaSPeter Zijlstra { 3772425bcb9SSteven Rostedt (Red Hat) struct trace_event_call *tp_event = p_event->tp_event; 378466c81c4SPeter Zijlstra 379466c81c4SPeter Zijlstra /* 380466c81c4SPeter Zijlstra * If TRACE_REG_PERF_DEL returns false; no custom action was performed 381466c81c4SPeter Zijlstra * and we need to take the default action of dequeueing our event from 382466c81c4SPeter Zijlstra * the right per-cpu hlist. 383466c81c4SPeter Zijlstra */ 384466c81c4SPeter Zijlstra if (!tp_event->class->reg(tp_event, TRACE_REG_PERF_DEL, p_event)) 3851c024ecaSPeter Zijlstra hlist_del_rcu(&p_event->hlist_entry); 3861c024ecaSPeter Zijlstra } 3871c024ecaSPeter Zijlstra 3881e1dcd93SAlexei Starovoitov void *perf_trace_buf_alloc(int size, struct pt_regs **regs, int *rctxp) 38997d5a220SFrederic Weisbecker { 3901c024ecaSPeter Zijlstra char *raw_data; 3911e1dcd93SAlexei Starovoitov int rctx; 39297d5a220SFrederic Weisbecker 393eb1e7961SFrederic Weisbecker BUILD_BUG_ON(PERF_MAX_TRACE_SIZE % sizeof(unsigned long)); 394eb1e7961SFrederic Weisbecker 395cd92bf61SOleg Nesterov if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE, 396cd92bf61SOleg Nesterov "perf buffer not large enough")) 397cd92bf61SOleg Nesterov return NULL; 398cd92bf61SOleg Nesterov 3991e1dcd93SAlexei Starovoitov *rctxp = rctx = perf_swevent_get_recursion_context(); 4001e1dcd93SAlexei Starovoitov if (rctx < 0) 4011c024ecaSPeter Zijlstra return NULL; 40297d5a220SFrederic Weisbecker 40386038c5eSPeter Zijlstra (Intel) if (regs) 4041e1dcd93SAlexei Starovoitov *regs = this_cpu_ptr(&__perf_regs[rctx]); 4051e1dcd93SAlexei Starovoitov raw_data = this_cpu_ptr(perf_trace_buf[rctx]); 40697d5a220SFrederic Weisbecker 40797d5a220SFrederic Weisbecker /* zero the dead bytes from align to not leak stack to user */ 408eb1e7961SFrederic Weisbecker memset(&raw_data[size - sizeof(u64)], 0, sizeof(u64)); 4091e1dcd93SAlexei Starovoitov return raw_data; 4101e1dcd93SAlexei Starovoitov } 4111e1dcd93SAlexei Starovoitov EXPORT_SYMBOL_GPL(perf_trace_buf_alloc); 4121e1dcd93SAlexei Starovoitov NOKPROBE_SYMBOL(perf_trace_buf_alloc); 41397d5a220SFrederic Weisbecker 4141e1dcd93SAlexei Starovoitov void perf_trace_buf_update(void *record, u16 type) 4151e1dcd93SAlexei Starovoitov { 4161e1dcd93SAlexei Starovoitov struct trace_entry *entry = record; 4171e1dcd93SAlexei Starovoitov int pc = preempt_count(); 4181e1dcd93SAlexei Starovoitov unsigned long flags; 4191e1dcd93SAlexei Starovoitov 42087f44bbcSPeter Zijlstra local_save_flags(flags); 42187f44bbcSPeter Zijlstra tracing_generic_entry_update(entry, flags, pc); 42297d5a220SFrederic Weisbecker entry->type = type; 42397d5a220SFrederic Weisbecker } 4241e1dcd93SAlexei Starovoitov NOKPROBE_SYMBOL(perf_trace_buf_update); 425ced39002SJiri Olsa 426ced39002SJiri Olsa #ifdef CONFIG_FUNCTION_TRACER 427ced39002SJiri Olsa static void 4282f5f6ad9SSteven Rostedt perf_ftrace_function_call(unsigned long ip, unsigned long parent_ip, 429a1e2e31dSSteven Rostedt struct ftrace_ops *ops, struct pt_regs *pt_regs) 430ced39002SJiri Olsa { 431ced39002SJiri Olsa struct ftrace_entry *entry; 432466c81c4SPeter Zijlstra struct perf_event *event; 433466c81c4SPeter Zijlstra struct hlist_head head; 434ced39002SJiri Olsa struct pt_regs regs; 435ced39002SJiri Olsa int rctx; 436ced39002SJiri Olsa 437466c81c4SPeter Zijlstra if ((unsigned long)ops->private != smp_processor_id()) 438b8ebfd3fSOleg Nesterov return; 439b8ebfd3fSOleg Nesterov 440466c81c4SPeter Zijlstra event = container_of(ops, struct perf_event, ftrace_ops); 441466c81c4SPeter Zijlstra 442466c81c4SPeter Zijlstra /* 443466c81c4SPeter Zijlstra * @event->hlist entry is NULL (per INIT_HLIST_NODE), and all 444466c81c4SPeter Zijlstra * the perf code does is hlist_for_each_entry_rcu(), so we can 445466c81c4SPeter Zijlstra * get away with simply setting the @head.first pointer in order 446466c81c4SPeter Zijlstra * to create a singular list. 447466c81c4SPeter Zijlstra */ 448466c81c4SPeter Zijlstra head.first = &event->hlist_entry; 449466c81c4SPeter Zijlstra 450ced39002SJiri Olsa #define ENTRY_SIZE (ALIGN(sizeof(struct ftrace_entry) + sizeof(u32), \ 451ced39002SJiri Olsa sizeof(u64)) - sizeof(u32)) 452ced39002SJiri Olsa 453ced39002SJiri Olsa BUILD_BUG_ON(ENTRY_SIZE > PERF_MAX_TRACE_SIZE); 454ced39002SJiri Olsa 455ec5e099dSAlexei Starovoitov memset(®s, 0, sizeof(regs)); 456ced39002SJiri Olsa perf_fetch_caller_regs(®s); 457ced39002SJiri Olsa 4581e1dcd93SAlexei Starovoitov entry = perf_trace_buf_alloc(ENTRY_SIZE, NULL, &rctx); 459ced39002SJiri Olsa if (!entry) 460ced39002SJiri Olsa return; 461ced39002SJiri Olsa 462ced39002SJiri Olsa entry->ip = ip; 463ced39002SJiri Olsa entry->parent_ip = parent_ip; 4641e1dcd93SAlexei Starovoitov perf_trace_buf_submit(entry, ENTRY_SIZE, rctx, TRACE_FN, 465466c81c4SPeter Zijlstra 1, ®s, &head, NULL); 466ced39002SJiri Olsa 467ced39002SJiri Olsa #undef ENTRY_SIZE 468ced39002SJiri Olsa } 469ced39002SJiri Olsa 470ced39002SJiri Olsa static int perf_ftrace_function_register(struct perf_event *event) 471ced39002SJiri Olsa { 472ced39002SJiri Olsa struct ftrace_ops *ops = &event->ftrace_ops; 473ced39002SJiri Olsa 4741dd311e6SPeter Zijlstra ops->flags = FTRACE_OPS_FL_RCU; 475ced39002SJiri Olsa ops->func = perf_ftrace_function_call; 476466c81c4SPeter Zijlstra ops->private = (void *)(unsigned long)nr_cpu_ids; 477466c81c4SPeter Zijlstra 478ced39002SJiri Olsa return register_ftrace_function(ops); 479ced39002SJiri Olsa } 480ced39002SJiri Olsa 481ced39002SJiri Olsa static int perf_ftrace_function_unregister(struct perf_event *event) 482ced39002SJiri Olsa { 483ced39002SJiri Olsa struct ftrace_ops *ops = &event->ftrace_ops; 4845500fa51SJiri Olsa int ret = unregister_ftrace_function(ops); 4855500fa51SJiri Olsa ftrace_free_filter(ops); 4865500fa51SJiri Olsa return ret; 487ced39002SJiri Olsa } 488ced39002SJiri Olsa 4892425bcb9SSteven Rostedt (Red Hat) int perf_ftrace_event_register(struct trace_event_call *call, 490ced39002SJiri Olsa enum trace_reg type, void *data) 491ced39002SJiri Olsa { 492466c81c4SPeter Zijlstra struct perf_event *event = data; 493466c81c4SPeter Zijlstra 494ced39002SJiri Olsa switch (type) { 495ced39002SJiri Olsa case TRACE_REG_REGISTER: 496ced39002SJiri Olsa case TRACE_REG_UNREGISTER: 497ced39002SJiri Olsa break; 498ced39002SJiri Olsa case TRACE_REG_PERF_REGISTER: 499ced39002SJiri Olsa case TRACE_REG_PERF_UNREGISTER: 500ced39002SJiri Olsa return 0; 501ced39002SJiri Olsa case TRACE_REG_PERF_OPEN: 502ced39002SJiri Olsa return perf_ftrace_function_register(data); 503ced39002SJiri Olsa case TRACE_REG_PERF_CLOSE: 504ced39002SJiri Olsa return perf_ftrace_function_unregister(data); 505ced39002SJiri Olsa case TRACE_REG_PERF_ADD: 506466c81c4SPeter Zijlstra event->ftrace_ops.private = (void *)(unsigned long)smp_processor_id(); 507466c81c4SPeter Zijlstra return 1; 508ced39002SJiri Olsa case TRACE_REG_PERF_DEL: 509466c81c4SPeter Zijlstra event->ftrace_ops.private = (void *)(unsigned long)nr_cpu_ids; 510466c81c4SPeter Zijlstra return 1; 511ced39002SJiri Olsa } 512ced39002SJiri Olsa 513ced39002SJiri Olsa return -EINVAL; 514ced39002SJiri Olsa } 515ced39002SJiri Olsa #endif /* CONFIG_FUNCTION_TRACER */ 516