162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * trace event based perf event profiling/tracing 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2009 Red Hat Inc, Peter Zijlstra 662306a36Sopenharmony_ci * Copyright (C) 2009-2010 Frederic Weisbecker <fweisbec@gmail.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/kprobes.h> 1162306a36Sopenharmony_ci#include <linux/security.h> 1262306a36Sopenharmony_ci#include "trace.h" 1362306a36Sopenharmony_ci#include "trace_probe.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistatic char __percpu *perf_trace_buf[PERF_NR_CONTEXTS]; 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* 1862306a36Sopenharmony_ci * Force it to be aligned to unsigned long to avoid misaligned accesses 1962306a36Sopenharmony_ci * surprises 2062306a36Sopenharmony_ci */ 2162306a36Sopenharmony_citypedef typeof(unsigned long [PERF_MAX_TRACE_SIZE / sizeof(unsigned long)]) 2262306a36Sopenharmony_ci perf_trace_t; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* Count the events in use (per event id, not per instance) */ 2562306a36Sopenharmony_cistatic int total_ref_count; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic int perf_trace_event_perm(struct trace_event_call *tp_event, 2862306a36Sopenharmony_ci struct perf_event *p_event) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci int ret; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci if (tp_event->perf_perm) { 3362306a36Sopenharmony_ci ret = tp_event->perf_perm(tp_event, p_event); 3462306a36Sopenharmony_ci if (ret) 3562306a36Sopenharmony_ci return ret; 3662306a36Sopenharmony_ci } 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci /* 3962306a36Sopenharmony_ci * We checked and allowed to create parent, 4062306a36Sopenharmony_ci * allow children without checking. 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_ci if (p_event->parent) 4362306a36Sopenharmony_ci return 0; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci /* 4662306a36Sopenharmony_ci * It's ok to check current process (owner) permissions in here, 4762306a36Sopenharmony_ci * because code below is called only via perf_event_open syscall. 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci /* The ftrace function trace is allowed only for root. */ 5162306a36Sopenharmony_ci if (ftrace_event_is_function(tp_event)) { 5262306a36Sopenharmony_ci ret = perf_allow_tracepoint(&p_event->attr); 5362306a36Sopenharmony_ci if (ret) 5462306a36Sopenharmony_ci return ret; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (!is_sampling_event(p_event)) 5762306a36Sopenharmony_ci return 0; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci /* 6062306a36Sopenharmony_ci * We don't allow user space callchains for function trace 6162306a36Sopenharmony_ci * event, due to issues with page faults while tracing page 6262306a36Sopenharmony_ci * fault handler and its overall trickiness nature. 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_ci if (!p_event->attr.exclude_callchain_user) 6562306a36Sopenharmony_ci return -EINVAL; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci /* 6862306a36Sopenharmony_ci * Same reason to disable user stack dump as for user space 6962306a36Sopenharmony_ci * callchains above. 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_ci if (p_event->attr.sample_type & PERF_SAMPLE_STACK_USER) 7262306a36Sopenharmony_ci return -EINVAL; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci /* No tracing, just counting, so no obvious leak */ 7662306a36Sopenharmony_ci if (!(p_event->attr.sample_type & PERF_SAMPLE_RAW)) 7762306a36Sopenharmony_ci return 0; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci /* Some events are ok to be traced by non-root users... */ 8062306a36Sopenharmony_ci if (p_event->attach_state == PERF_ATTACH_TASK) { 8162306a36Sopenharmony_ci if (tp_event->flags & TRACE_EVENT_FL_CAP_ANY) 8262306a36Sopenharmony_ci return 0; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* 8662306a36Sopenharmony_ci * ...otherwise raw tracepoint data can be a severe data leak, 8762306a36Sopenharmony_ci * only allow root to have these. 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_ci ret = perf_allow_tracepoint(&p_event->attr); 9062306a36Sopenharmony_ci if (ret) 9162306a36Sopenharmony_ci return ret; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci return 0; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic int perf_trace_event_reg(struct trace_event_call *tp_event, 9762306a36Sopenharmony_ci struct perf_event *p_event) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct hlist_head __percpu *list; 10062306a36Sopenharmony_ci int ret = -ENOMEM; 10162306a36Sopenharmony_ci int cpu; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci p_event->tp_event = tp_event; 10462306a36Sopenharmony_ci if (tp_event->perf_refcount++ > 0) 10562306a36Sopenharmony_ci return 0; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci list = alloc_percpu(struct hlist_head); 10862306a36Sopenharmony_ci if (!list) 10962306a36Sopenharmony_ci goto fail; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci for_each_possible_cpu(cpu) 11262306a36Sopenharmony_ci INIT_HLIST_HEAD(per_cpu_ptr(list, cpu)); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci tp_event->perf_events = list; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if (!total_ref_count) { 11762306a36Sopenharmony_ci char __percpu *buf; 11862306a36Sopenharmony_ci int i; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci for (i = 0; i < PERF_NR_CONTEXTS; i++) { 12162306a36Sopenharmony_ci buf = (char __percpu *)alloc_percpu(perf_trace_t); 12262306a36Sopenharmony_ci if (!buf) 12362306a36Sopenharmony_ci goto fail; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci perf_trace_buf[i] = buf; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci ret = tp_event->class->reg(tp_event, TRACE_REG_PERF_REGISTER, NULL); 13062306a36Sopenharmony_ci if (ret) 13162306a36Sopenharmony_ci goto fail; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci total_ref_count++; 13462306a36Sopenharmony_ci return 0; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cifail: 13762306a36Sopenharmony_ci if (!total_ref_count) { 13862306a36Sopenharmony_ci int i; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci for (i = 0; i < PERF_NR_CONTEXTS; i++) { 14162306a36Sopenharmony_ci free_percpu(perf_trace_buf[i]); 14262306a36Sopenharmony_ci perf_trace_buf[i] = NULL; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (!--tp_event->perf_refcount) { 14762306a36Sopenharmony_ci free_percpu(tp_event->perf_events); 14862306a36Sopenharmony_ci tp_event->perf_events = NULL; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return ret; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic void perf_trace_event_unreg(struct perf_event *p_event) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct trace_event_call *tp_event = p_event->tp_event; 15762306a36Sopenharmony_ci int i; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (--tp_event->perf_refcount > 0) 16062306a36Sopenharmony_ci return; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci tp_event->class->reg(tp_event, TRACE_REG_PERF_UNREGISTER, NULL); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci /* 16562306a36Sopenharmony_ci * Ensure our callback won't be called anymore. The buffers 16662306a36Sopenharmony_ci * will be freed after that. 16762306a36Sopenharmony_ci */ 16862306a36Sopenharmony_ci tracepoint_synchronize_unregister(); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci free_percpu(tp_event->perf_events); 17162306a36Sopenharmony_ci tp_event->perf_events = NULL; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (!--total_ref_count) { 17462306a36Sopenharmony_ci for (i = 0; i < PERF_NR_CONTEXTS; i++) { 17562306a36Sopenharmony_ci free_percpu(perf_trace_buf[i]); 17662306a36Sopenharmony_ci perf_trace_buf[i] = NULL; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic int perf_trace_event_open(struct perf_event *p_event) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci struct trace_event_call *tp_event = p_event->tp_event; 18462306a36Sopenharmony_ci return tp_event->class->reg(tp_event, TRACE_REG_PERF_OPEN, p_event); 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic void perf_trace_event_close(struct perf_event *p_event) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci struct trace_event_call *tp_event = p_event->tp_event; 19062306a36Sopenharmony_ci tp_event->class->reg(tp_event, TRACE_REG_PERF_CLOSE, p_event); 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic int perf_trace_event_init(struct trace_event_call *tp_event, 19462306a36Sopenharmony_ci struct perf_event *p_event) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci int ret; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci ret = perf_trace_event_perm(tp_event, p_event); 19962306a36Sopenharmony_ci if (ret) 20062306a36Sopenharmony_ci return ret; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci ret = perf_trace_event_reg(tp_event, p_event); 20362306a36Sopenharmony_ci if (ret) 20462306a36Sopenharmony_ci return ret; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci ret = perf_trace_event_open(p_event); 20762306a36Sopenharmony_ci if (ret) { 20862306a36Sopenharmony_ci perf_trace_event_unreg(p_event); 20962306a36Sopenharmony_ci return ret; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci return 0; 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ciint perf_trace_init(struct perf_event *p_event) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci struct trace_event_call *tp_event; 21862306a36Sopenharmony_ci u64 event_id = p_event->attr.config; 21962306a36Sopenharmony_ci int ret = -EINVAL; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci mutex_lock(&event_mutex); 22262306a36Sopenharmony_ci list_for_each_entry(tp_event, &ftrace_events, list) { 22362306a36Sopenharmony_ci if (tp_event->event.type == event_id && 22462306a36Sopenharmony_ci tp_event->class && tp_event->class->reg && 22562306a36Sopenharmony_ci trace_event_try_get_ref(tp_event)) { 22662306a36Sopenharmony_ci ret = perf_trace_event_init(tp_event, p_event); 22762306a36Sopenharmony_ci if (ret) 22862306a36Sopenharmony_ci trace_event_put_ref(tp_event); 22962306a36Sopenharmony_ci break; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci mutex_unlock(&event_mutex); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci return ret; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_civoid perf_trace_destroy(struct perf_event *p_event) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci mutex_lock(&event_mutex); 24062306a36Sopenharmony_ci perf_trace_event_close(p_event); 24162306a36Sopenharmony_ci perf_trace_event_unreg(p_event); 24262306a36Sopenharmony_ci trace_event_put_ref(p_event->tp_event); 24362306a36Sopenharmony_ci mutex_unlock(&event_mutex); 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci#ifdef CONFIG_KPROBE_EVENTS 24762306a36Sopenharmony_ciint perf_kprobe_init(struct perf_event *p_event, bool is_retprobe) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci int ret; 25062306a36Sopenharmony_ci char *func = NULL; 25162306a36Sopenharmony_ci struct trace_event_call *tp_event; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (p_event->attr.kprobe_func) { 25462306a36Sopenharmony_ci func = strndup_user(u64_to_user_ptr(p_event->attr.kprobe_func), 25562306a36Sopenharmony_ci KSYM_NAME_LEN); 25662306a36Sopenharmony_ci if (IS_ERR(func)) { 25762306a36Sopenharmony_ci ret = PTR_ERR(func); 25862306a36Sopenharmony_ci return (ret == -EINVAL) ? -E2BIG : ret; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (func[0] == '\0') { 26262306a36Sopenharmony_ci kfree(func); 26362306a36Sopenharmony_ci func = NULL; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci tp_event = create_local_trace_kprobe( 26862306a36Sopenharmony_ci func, (void *)(unsigned long)(p_event->attr.kprobe_addr), 26962306a36Sopenharmony_ci p_event->attr.probe_offset, is_retprobe); 27062306a36Sopenharmony_ci if (IS_ERR(tp_event)) { 27162306a36Sopenharmony_ci ret = PTR_ERR(tp_event); 27262306a36Sopenharmony_ci goto out; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci mutex_lock(&event_mutex); 27662306a36Sopenharmony_ci ret = perf_trace_event_init(tp_event, p_event); 27762306a36Sopenharmony_ci if (ret) 27862306a36Sopenharmony_ci destroy_local_trace_kprobe(tp_event); 27962306a36Sopenharmony_ci mutex_unlock(&event_mutex); 28062306a36Sopenharmony_ciout: 28162306a36Sopenharmony_ci kfree(func); 28262306a36Sopenharmony_ci return ret; 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_civoid perf_kprobe_destroy(struct perf_event *p_event) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci mutex_lock(&event_mutex); 28862306a36Sopenharmony_ci perf_trace_event_close(p_event); 28962306a36Sopenharmony_ci perf_trace_event_unreg(p_event); 29062306a36Sopenharmony_ci trace_event_put_ref(p_event->tp_event); 29162306a36Sopenharmony_ci mutex_unlock(&event_mutex); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci destroy_local_trace_kprobe(p_event->tp_event); 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci#endif /* CONFIG_KPROBE_EVENTS */ 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci#ifdef CONFIG_UPROBE_EVENTS 29862306a36Sopenharmony_ciint perf_uprobe_init(struct perf_event *p_event, 29962306a36Sopenharmony_ci unsigned long ref_ctr_offset, bool is_retprobe) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci int ret; 30262306a36Sopenharmony_ci char *path = NULL; 30362306a36Sopenharmony_ci struct trace_event_call *tp_event; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (!p_event->attr.uprobe_path) 30662306a36Sopenharmony_ci return -EINVAL; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci path = strndup_user(u64_to_user_ptr(p_event->attr.uprobe_path), 30962306a36Sopenharmony_ci PATH_MAX); 31062306a36Sopenharmony_ci if (IS_ERR(path)) { 31162306a36Sopenharmony_ci ret = PTR_ERR(path); 31262306a36Sopenharmony_ci return (ret == -EINVAL) ? -E2BIG : ret; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci if (path[0] == '\0') { 31562306a36Sopenharmony_ci ret = -EINVAL; 31662306a36Sopenharmony_ci goto out; 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci tp_event = create_local_trace_uprobe(path, p_event->attr.probe_offset, 32062306a36Sopenharmony_ci ref_ctr_offset, is_retprobe); 32162306a36Sopenharmony_ci if (IS_ERR(tp_event)) { 32262306a36Sopenharmony_ci ret = PTR_ERR(tp_event); 32362306a36Sopenharmony_ci goto out; 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci /* 32762306a36Sopenharmony_ci * local trace_uprobe need to hold event_mutex to call 32862306a36Sopenharmony_ci * uprobe_buffer_enable() and uprobe_buffer_disable(). 32962306a36Sopenharmony_ci * event_mutex is not required for local trace_kprobes. 33062306a36Sopenharmony_ci */ 33162306a36Sopenharmony_ci mutex_lock(&event_mutex); 33262306a36Sopenharmony_ci ret = perf_trace_event_init(tp_event, p_event); 33362306a36Sopenharmony_ci if (ret) 33462306a36Sopenharmony_ci destroy_local_trace_uprobe(tp_event); 33562306a36Sopenharmony_ci mutex_unlock(&event_mutex); 33662306a36Sopenharmony_ciout: 33762306a36Sopenharmony_ci kfree(path); 33862306a36Sopenharmony_ci return ret; 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_civoid perf_uprobe_destroy(struct perf_event *p_event) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci mutex_lock(&event_mutex); 34462306a36Sopenharmony_ci perf_trace_event_close(p_event); 34562306a36Sopenharmony_ci perf_trace_event_unreg(p_event); 34662306a36Sopenharmony_ci trace_event_put_ref(p_event->tp_event); 34762306a36Sopenharmony_ci mutex_unlock(&event_mutex); 34862306a36Sopenharmony_ci destroy_local_trace_uprobe(p_event->tp_event); 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci#endif /* CONFIG_UPROBE_EVENTS */ 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ciint perf_trace_add(struct perf_event *p_event, int flags) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci struct trace_event_call *tp_event = p_event->tp_event; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if (!(flags & PERF_EF_START)) 35762306a36Sopenharmony_ci p_event->hw.state = PERF_HES_STOPPED; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci /* 36062306a36Sopenharmony_ci * If TRACE_REG_PERF_ADD returns false; no custom action was performed 36162306a36Sopenharmony_ci * and we need to take the default action of enqueueing our event on 36262306a36Sopenharmony_ci * the right per-cpu hlist. 36362306a36Sopenharmony_ci */ 36462306a36Sopenharmony_ci if (!tp_event->class->reg(tp_event, TRACE_REG_PERF_ADD, p_event)) { 36562306a36Sopenharmony_ci struct hlist_head __percpu *pcpu_list; 36662306a36Sopenharmony_ci struct hlist_head *list; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci pcpu_list = tp_event->perf_events; 36962306a36Sopenharmony_ci if (WARN_ON_ONCE(!pcpu_list)) 37062306a36Sopenharmony_ci return -EINVAL; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci list = this_cpu_ptr(pcpu_list); 37362306a36Sopenharmony_ci hlist_add_head_rcu(&p_event->hlist_entry, list); 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci return 0; 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_civoid perf_trace_del(struct perf_event *p_event, int flags) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci struct trace_event_call *tp_event = p_event->tp_event; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci /* 38462306a36Sopenharmony_ci * If TRACE_REG_PERF_DEL returns false; no custom action was performed 38562306a36Sopenharmony_ci * and we need to take the default action of dequeueing our event from 38662306a36Sopenharmony_ci * the right per-cpu hlist. 38762306a36Sopenharmony_ci */ 38862306a36Sopenharmony_ci if (!tp_event->class->reg(tp_event, TRACE_REG_PERF_DEL, p_event)) 38962306a36Sopenharmony_ci hlist_del_rcu(&p_event->hlist_entry); 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_civoid *perf_trace_buf_alloc(int size, struct pt_regs **regs, int *rctxp) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci char *raw_data; 39562306a36Sopenharmony_ci int rctx; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci BUILD_BUG_ON(PERF_MAX_TRACE_SIZE % sizeof(unsigned long)); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE, 40062306a36Sopenharmony_ci "perf buffer not large enough, wanted %d, have %d", 40162306a36Sopenharmony_ci size, PERF_MAX_TRACE_SIZE)) 40262306a36Sopenharmony_ci return NULL; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci *rctxp = rctx = perf_swevent_get_recursion_context(); 40562306a36Sopenharmony_ci if (rctx < 0) 40662306a36Sopenharmony_ci return NULL; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (regs) 40962306a36Sopenharmony_ci *regs = this_cpu_ptr(&__perf_regs[rctx]); 41062306a36Sopenharmony_ci raw_data = this_cpu_ptr(perf_trace_buf[rctx]); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* zero the dead bytes from align to not leak stack to user */ 41362306a36Sopenharmony_ci memset(&raw_data[size - sizeof(u64)], 0, sizeof(u64)); 41462306a36Sopenharmony_ci return raw_data; 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(perf_trace_buf_alloc); 41762306a36Sopenharmony_ciNOKPROBE_SYMBOL(perf_trace_buf_alloc); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_civoid perf_trace_buf_update(void *record, u16 type) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci struct trace_entry *entry = record; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci tracing_generic_entry_update(entry, type, tracing_gen_ctx()); 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ciNOKPROBE_SYMBOL(perf_trace_buf_update); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci#ifdef CONFIG_FUNCTION_TRACER 42862306a36Sopenharmony_cistatic void 42962306a36Sopenharmony_ciperf_ftrace_function_call(unsigned long ip, unsigned long parent_ip, 43062306a36Sopenharmony_ci struct ftrace_ops *ops, struct ftrace_regs *fregs) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci struct ftrace_entry *entry; 43362306a36Sopenharmony_ci struct perf_event *event; 43462306a36Sopenharmony_ci struct hlist_head head; 43562306a36Sopenharmony_ci struct pt_regs regs; 43662306a36Sopenharmony_ci int rctx; 43762306a36Sopenharmony_ci int bit; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if (!rcu_is_watching()) 44062306a36Sopenharmony_ci return; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci bit = ftrace_test_recursion_trylock(ip, parent_ip); 44362306a36Sopenharmony_ci if (bit < 0) 44462306a36Sopenharmony_ci return; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci if ((unsigned long)ops->private != smp_processor_id()) 44762306a36Sopenharmony_ci goto out; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci event = container_of(ops, struct perf_event, ftrace_ops); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci /* 45262306a36Sopenharmony_ci * @event->hlist entry is NULL (per INIT_HLIST_NODE), and all 45362306a36Sopenharmony_ci * the perf code does is hlist_for_each_entry_rcu(), so we can 45462306a36Sopenharmony_ci * get away with simply setting the @head.first pointer in order 45562306a36Sopenharmony_ci * to create a singular list. 45662306a36Sopenharmony_ci */ 45762306a36Sopenharmony_ci head.first = &event->hlist_entry; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci#define ENTRY_SIZE (ALIGN(sizeof(struct ftrace_entry) + sizeof(u32), \ 46062306a36Sopenharmony_ci sizeof(u64)) - sizeof(u32)) 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci BUILD_BUG_ON(ENTRY_SIZE > PERF_MAX_TRACE_SIZE); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci memset(®s, 0, sizeof(regs)); 46562306a36Sopenharmony_ci perf_fetch_caller_regs(®s); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci entry = perf_trace_buf_alloc(ENTRY_SIZE, NULL, &rctx); 46862306a36Sopenharmony_ci if (!entry) 46962306a36Sopenharmony_ci goto out; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci entry->ip = ip; 47262306a36Sopenharmony_ci entry->parent_ip = parent_ip; 47362306a36Sopenharmony_ci perf_trace_buf_submit(entry, ENTRY_SIZE, rctx, TRACE_FN, 47462306a36Sopenharmony_ci 1, ®s, &head, NULL); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ciout: 47762306a36Sopenharmony_ci ftrace_test_recursion_unlock(bit); 47862306a36Sopenharmony_ci#undef ENTRY_SIZE 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic int perf_ftrace_function_register(struct perf_event *event) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci struct ftrace_ops *ops = &event->ftrace_ops; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci ops->func = perf_ftrace_function_call; 48662306a36Sopenharmony_ci ops->private = (void *)(unsigned long)nr_cpu_ids; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci return register_ftrace_function(ops); 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_cistatic int perf_ftrace_function_unregister(struct perf_event *event) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci struct ftrace_ops *ops = &event->ftrace_ops; 49462306a36Sopenharmony_ci int ret = unregister_ftrace_function(ops); 49562306a36Sopenharmony_ci ftrace_free_filter(ops); 49662306a36Sopenharmony_ci return ret; 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ciint perf_ftrace_event_register(struct trace_event_call *call, 50062306a36Sopenharmony_ci enum trace_reg type, void *data) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci struct perf_event *event = data; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci switch (type) { 50562306a36Sopenharmony_ci case TRACE_REG_REGISTER: 50662306a36Sopenharmony_ci case TRACE_REG_UNREGISTER: 50762306a36Sopenharmony_ci break; 50862306a36Sopenharmony_ci case TRACE_REG_PERF_REGISTER: 50962306a36Sopenharmony_ci case TRACE_REG_PERF_UNREGISTER: 51062306a36Sopenharmony_ci return 0; 51162306a36Sopenharmony_ci case TRACE_REG_PERF_OPEN: 51262306a36Sopenharmony_ci return perf_ftrace_function_register(data); 51362306a36Sopenharmony_ci case TRACE_REG_PERF_CLOSE: 51462306a36Sopenharmony_ci return perf_ftrace_function_unregister(data); 51562306a36Sopenharmony_ci case TRACE_REG_PERF_ADD: 51662306a36Sopenharmony_ci event->ftrace_ops.private = (void *)(unsigned long)smp_processor_id(); 51762306a36Sopenharmony_ci return 1; 51862306a36Sopenharmony_ci case TRACE_REG_PERF_DEL: 51962306a36Sopenharmony_ci event->ftrace_ops.private = (void *)(unsigned long)nr_cpu_ids; 52062306a36Sopenharmony_ci return 1; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci return -EINVAL; 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci#endif /* CONFIG_FUNCTION_TRACER */ 526