18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * trace event based perf event profiling/tracing 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2009 Red Hat Inc, Peter Zijlstra 68c2ecf20Sopenharmony_ci * Copyright (C) 2009-2010 Frederic Weisbecker <fweisbec@gmail.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/kprobes.h> 118c2ecf20Sopenharmony_ci#include <linux/security.h> 128c2ecf20Sopenharmony_ci#include "trace.h" 138c2ecf20Sopenharmony_ci#include "trace_probe.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_cistatic char __percpu *perf_trace_buf[PERF_NR_CONTEXTS]; 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/* 188c2ecf20Sopenharmony_ci * Force it to be aligned to unsigned long to avoid misaligned accesses 198c2ecf20Sopenharmony_ci * suprises 208c2ecf20Sopenharmony_ci */ 218c2ecf20Sopenharmony_citypedef typeof(unsigned long [PERF_MAX_TRACE_SIZE / sizeof(unsigned long)]) 228c2ecf20Sopenharmony_ci perf_trace_t; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* Count the events in use (per event id, not per instance) */ 258c2ecf20Sopenharmony_cistatic int total_ref_count; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic int perf_trace_event_perm(struct trace_event_call *tp_event, 288c2ecf20Sopenharmony_ci struct perf_event *p_event) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci int ret; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci if (tp_event->perf_perm) { 338c2ecf20Sopenharmony_ci ret = tp_event->perf_perm(tp_event, p_event); 348c2ecf20Sopenharmony_ci if (ret) 358c2ecf20Sopenharmony_ci return ret; 368c2ecf20Sopenharmony_ci } 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci /* 398c2ecf20Sopenharmony_ci * We checked and allowed to create parent, 408c2ecf20Sopenharmony_ci * allow children without checking. 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_ci if (p_event->parent) 438c2ecf20Sopenharmony_ci return 0; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci /* 468c2ecf20Sopenharmony_ci * It's ok to check current process (owner) permissions in here, 478c2ecf20Sopenharmony_ci * because code below is called only via perf_event_open syscall. 488c2ecf20Sopenharmony_ci */ 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci /* The ftrace function trace is allowed only for root. */ 518c2ecf20Sopenharmony_ci if (ftrace_event_is_function(tp_event)) { 528c2ecf20Sopenharmony_ci ret = perf_allow_tracepoint(&p_event->attr); 538c2ecf20Sopenharmony_ci if (ret) 548c2ecf20Sopenharmony_ci return ret; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci if (!is_sampling_event(p_event)) 578c2ecf20Sopenharmony_ci return 0; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci /* 608c2ecf20Sopenharmony_ci * We don't allow user space callchains for function trace 618c2ecf20Sopenharmony_ci * event, due to issues with page faults while tracing page 628c2ecf20Sopenharmony_ci * fault handler and its overall trickiness nature. 638c2ecf20Sopenharmony_ci */ 648c2ecf20Sopenharmony_ci if (!p_event->attr.exclude_callchain_user) 658c2ecf20Sopenharmony_ci return -EINVAL; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci /* 688c2ecf20Sopenharmony_ci * Same reason to disable user stack dump as for user space 698c2ecf20Sopenharmony_ci * callchains above. 708c2ecf20Sopenharmony_ci */ 718c2ecf20Sopenharmony_ci if (p_event->attr.sample_type & PERF_SAMPLE_STACK_USER) 728c2ecf20Sopenharmony_ci return -EINVAL; 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci /* No tracing, just counting, so no obvious leak */ 768c2ecf20Sopenharmony_ci if (!(p_event->attr.sample_type & PERF_SAMPLE_RAW)) 778c2ecf20Sopenharmony_ci return 0; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci /* Some events are ok to be traced by non-root users... */ 808c2ecf20Sopenharmony_ci if (p_event->attach_state == PERF_ATTACH_TASK) { 818c2ecf20Sopenharmony_ci if (tp_event->flags & TRACE_EVENT_FL_CAP_ANY) 828c2ecf20Sopenharmony_ci return 0; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci /* 868c2ecf20Sopenharmony_ci * ...otherwise raw tracepoint data can be a severe data leak, 878c2ecf20Sopenharmony_ci * only allow root to have these. 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_ci ret = perf_allow_tracepoint(&p_event->attr); 908c2ecf20Sopenharmony_ci if (ret) 918c2ecf20Sopenharmony_ci return ret; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci return 0; 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic int perf_trace_event_reg(struct trace_event_call *tp_event, 978c2ecf20Sopenharmony_ci struct perf_event *p_event) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci struct hlist_head __percpu *list; 1008c2ecf20Sopenharmony_ci int ret = -ENOMEM; 1018c2ecf20Sopenharmony_ci int cpu; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci p_event->tp_event = tp_event; 1048c2ecf20Sopenharmony_ci if (tp_event->perf_refcount++ > 0) 1058c2ecf20Sopenharmony_ci return 0; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci list = alloc_percpu(struct hlist_head); 1088c2ecf20Sopenharmony_ci if (!list) 1098c2ecf20Sopenharmony_ci goto fail; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) 1128c2ecf20Sopenharmony_ci INIT_HLIST_HEAD(per_cpu_ptr(list, cpu)); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci tp_event->perf_events = list; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (!total_ref_count) { 1178c2ecf20Sopenharmony_ci char __percpu *buf; 1188c2ecf20Sopenharmony_ci int i; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci for (i = 0; i < PERF_NR_CONTEXTS; i++) { 1218c2ecf20Sopenharmony_ci buf = (char __percpu *)alloc_percpu(perf_trace_t); 1228c2ecf20Sopenharmony_ci if (!buf) 1238c2ecf20Sopenharmony_ci goto fail; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci perf_trace_buf[i] = buf; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci ret = tp_event->class->reg(tp_event, TRACE_REG_PERF_REGISTER, NULL); 1308c2ecf20Sopenharmony_ci if (ret) 1318c2ecf20Sopenharmony_ci goto fail; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci total_ref_count++; 1348c2ecf20Sopenharmony_ci return 0; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cifail: 1378c2ecf20Sopenharmony_ci if (!total_ref_count) { 1388c2ecf20Sopenharmony_ci int i; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci for (i = 0; i < PERF_NR_CONTEXTS; i++) { 1418c2ecf20Sopenharmony_ci free_percpu(perf_trace_buf[i]); 1428c2ecf20Sopenharmony_ci perf_trace_buf[i] = NULL; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if (!--tp_event->perf_refcount) { 1478c2ecf20Sopenharmony_ci free_percpu(tp_event->perf_events); 1488c2ecf20Sopenharmony_ci tp_event->perf_events = NULL; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci return ret; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic void perf_trace_event_unreg(struct perf_event *p_event) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci struct trace_event_call *tp_event = p_event->tp_event; 1578c2ecf20Sopenharmony_ci int i; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (--tp_event->perf_refcount > 0) 1608c2ecf20Sopenharmony_ci goto out; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci tp_event->class->reg(tp_event, TRACE_REG_PERF_UNREGISTER, NULL); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci /* 1658c2ecf20Sopenharmony_ci * Ensure our callback won't be called anymore. The buffers 1668c2ecf20Sopenharmony_ci * will be freed after that. 1678c2ecf20Sopenharmony_ci */ 1688c2ecf20Sopenharmony_ci tracepoint_synchronize_unregister(); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci free_percpu(tp_event->perf_events); 1718c2ecf20Sopenharmony_ci tp_event->perf_events = NULL; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if (!--total_ref_count) { 1748c2ecf20Sopenharmony_ci for (i = 0; i < PERF_NR_CONTEXTS; i++) { 1758c2ecf20Sopenharmony_ci free_percpu(perf_trace_buf[i]); 1768c2ecf20Sopenharmony_ci perf_trace_buf[i] = NULL; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ciout: 1808c2ecf20Sopenharmony_ci module_put(tp_event->mod); 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic int perf_trace_event_open(struct perf_event *p_event) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci struct trace_event_call *tp_event = p_event->tp_event; 1868c2ecf20Sopenharmony_ci return tp_event->class->reg(tp_event, TRACE_REG_PERF_OPEN, p_event); 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic void perf_trace_event_close(struct perf_event *p_event) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci struct trace_event_call *tp_event = p_event->tp_event; 1928c2ecf20Sopenharmony_ci tp_event->class->reg(tp_event, TRACE_REG_PERF_CLOSE, p_event); 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic int perf_trace_event_init(struct trace_event_call *tp_event, 1968c2ecf20Sopenharmony_ci struct perf_event *p_event) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci int ret; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci ret = perf_trace_event_perm(tp_event, p_event); 2018c2ecf20Sopenharmony_ci if (ret) 2028c2ecf20Sopenharmony_ci return ret; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci ret = perf_trace_event_reg(tp_event, p_event); 2058c2ecf20Sopenharmony_ci if (ret) 2068c2ecf20Sopenharmony_ci return ret; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci ret = perf_trace_event_open(p_event); 2098c2ecf20Sopenharmony_ci if (ret) { 2108c2ecf20Sopenharmony_ci perf_trace_event_unreg(p_event); 2118c2ecf20Sopenharmony_ci return ret; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci return 0; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ciint perf_trace_init(struct perf_event *p_event) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci struct trace_event_call *tp_event; 2208c2ecf20Sopenharmony_ci u64 event_id = p_event->attr.config; 2218c2ecf20Sopenharmony_ci int ret = -EINVAL; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci mutex_lock(&event_mutex); 2248c2ecf20Sopenharmony_ci list_for_each_entry(tp_event, &ftrace_events, list) { 2258c2ecf20Sopenharmony_ci if (tp_event->event.type == event_id && 2268c2ecf20Sopenharmony_ci tp_event->class && tp_event->class->reg && 2278c2ecf20Sopenharmony_ci try_module_get(tp_event->mod)) { 2288c2ecf20Sopenharmony_ci ret = perf_trace_event_init(tp_event, p_event); 2298c2ecf20Sopenharmony_ci if (ret) 2308c2ecf20Sopenharmony_ci module_put(tp_event->mod); 2318c2ecf20Sopenharmony_ci break; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci mutex_unlock(&event_mutex); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci return ret; 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_civoid perf_trace_destroy(struct perf_event *p_event) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci mutex_lock(&event_mutex); 2428c2ecf20Sopenharmony_ci perf_trace_event_close(p_event); 2438c2ecf20Sopenharmony_ci perf_trace_event_unreg(p_event); 2448c2ecf20Sopenharmony_ci mutex_unlock(&event_mutex); 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci#ifdef CONFIG_KPROBE_EVENTS 2488c2ecf20Sopenharmony_ciint perf_kprobe_init(struct perf_event *p_event, bool is_retprobe) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci int ret; 2518c2ecf20Sopenharmony_ci char *func = NULL; 2528c2ecf20Sopenharmony_ci struct trace_event_call *tp_event; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (p_event->attr.kprobe_func) { 2558c2ecf20Sopenharmony_ci func = kzalloc(KSYM_NAME_LEN, GFP_KERNEL); 2568c2ecf20Sopenharmony_ci if (!func) 2578c2ecf20Sopenharmony_ci return -ENOMEM; 2588c2ecf20Sopenharmony_ci ret = strncpy_from_user( 2598c2ecf20Sopenharmony_ci func, u64_to_user_ptr(p_event->attr.kprobe_func), 2608c2ecf20Sopenharmony_ci KSYM_NAME_LEN); 2618c2ecf20Sopenharmony_ci if (ret == KSYM_NAME_LEN) 2628c2ecf20Sopenharmony_ci ret = -E2BIG; 2638c2ecf20Sopenharmony_ci if (ret < 0) 2648c2ecf20Sopenharmony_ci goto out; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (func[0] == '\0') { 2678c2ecf20Sopenharmony_ci kfree(func); 2688c2ecf20Sopenharmony_ci func = NULL; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci tp_event = create_local_trace_kprobe( 2738c2ecf20Sopenharmony_ci func, (void *)(unsigned long)(p_event->attr.kprobe_addr), 2748c2ecf20Sopenharmony_ci p_event->attr.probe_offset, is_retprobe); 2758c2ecf20Sopenharmony_ci if (IS_ERR(tp_event)) { 2768c2ecf20Sopenharmony_ci ret = PTR_ERR(tp_event); 2778c2ecf20Sopenharmony_ci goto out; 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci mutex_lock(&event_mutex); 2818c2ecf20Sopenharmony_ci ret = perf_trace_event_init(tp_event, p_event); 2828c2ecf20Sopenharmony_ci if (ret) 2838c2ecf20Sopenharmony_ci destroy_local_trace_kprobe(tp_event); 2848c2ecf20Sopenharmony_ci mutex_unlock(&event_mutex); 2858c2ecf20Sopenharmony_ciout: 2868c2ecf20Sopenharmony_ci kfree(func); 2878c2ecf20Sopenharmony_ci return ret; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_civoid perf_kprobe_destroy(struct perf_event *p_event) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci mutex_lock(&event_mutex); 2938c2ecf20Sopenharmony_ci perf_trace_event_close(p_event); 2948c2ecf20Sopenharmony_ci perf_trace_event_unreg(p_event); 2958c2ecf20Sopenharmony_ci mutex_unlock(&event_mutex); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci destroy_local_trace_kprobe(p_event->tp_event); 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci#endif /* CONFIG_KPROBE_EVENTS */ 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci#ifdef CONFIG_UPROBE_EVENTS 3028c2ecf20Sopenharmony_ciint perf_uprobe_init(struct perf_event *p_event, 3038c2ecf20Sopenharmony_ci unsigned long ref_ctr_offset, bool is_retprobe) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci int ret; 3068c2ecf20Sopenharmony_ci char *path = NULL; 3078c2ecf20Sopenharmony_ci struct trace_event_call *tp_event; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if (!p_event->attr.uprobe_path) 3108c2ecf20Sopenharmony_ci return -EINVAL; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci path = strndup_user(u64_to_user_ptr(p_event->attr.uprobe_path), 3138c2ecf20Sopenharmony_ci PATH_MAX); 3148c2ecf20Sopenharmony_ci if (IS_ERR(path)) { 3158c2ecf20Sopenharmony_ci ret = PTR_ERR(path); 3168c2ecf20Sopenharmony_ci return (ret == -EINVAL) ? -E2BIG : ret; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci if (path[0] == '\0') { 3198c2ecf20Sopenharmony_ci ret = -EINVAL; 3208c2ecf20Sopenharmony_ci goto out; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci tp_event = create_local_trace_uprobe(path, p_event->attr.probe_offset, 3248c2ecf20Sopenharmony_ci ref_ctr_offset, is_retprobe); 3258c2ecf20Sopenharmony_ci if (IS_ERR(tp_event)) { 3268c2ecf20Sopenharmony_ci ret = PTR_ERR(tp_event); 3278c2ecf20Sopenharmony_ci goto out; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci /* 3318c2ecf20Sopenharmony_ci * local trace_uprobe need to hold event_mutex to call 3328c2ecf20Sopenharmony_ci * uprobe_buffer_enable() and uprobe_buffer_disable(). 3338c2ecf20Sopenharmony_ci * event_mutex is not required for local trace_kprobes. 3348c2ecf20Sopenharmony_ci */ 3358c2ecf20Sopenharmony_ci mutex_lock(&event_mutex); 3368c2ecf20Sopenharmony_ci ret = perf_trace_event_init(tp_event, p_event); 3378c2ecf20Sopenharmony_ci if (ret) 3388c2ecf20Sopenharmony_ci destroy_local_trace_uprobe(tp_event); 3398c2ecf20Sopenharmony_ci mutex_unlock(&event_mutex); 3408c2ecf20Sopenharmony_ciout: 3418c2ecf20Sopenharmony_ci kfree(path); 3428c2ecf20Sopenharmony_ci return ret; 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_civoid perf_uprobe_destroy(struct perf_event *p_event) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci mutex_lock(&event_mutex); 3488c2ecf20Sopenharmony_ci perf_trace_event_close(p_event); 3498c2ecf20Sopenharmony_ci perf_trace_event_unreg(p_event); 3508c2ecf20Sopenharmony_ci mutex_unlock(&event_mutex); 3518c2ecf20Sopenharmony_ci destroy_local_trace_uprobe(p_event->tp_event); 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci#endif /* CONFIG_UPROBE_EVENTS */ 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ciint perf_trace_add(struct perf_event *p_event, int flags) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci struct trace_event_call *tp_event = p_event->tp_event; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci if (!(flags & PERF_EF_START)) 3608c2ecf20Sopenharmony_ci p_event->hw.state = PERF_HES_STOPPED; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci /* 3638c2ecf20Sopenharmony_ci * If TRACE_REG_PERF_ADD returns false; no custom action was performed 3648c2ecf20Sopenharmony_ci * and we need to take the default action of enqueueing our event on 3658c2ecf20Sopenharmony_ci * the right per-cpu hlist. 3668c2ecf20Sopenharmony_ci */ 3678c2ecf20Sopenharmony_ci if (!tp_event->class->reg(tp_event, TRACE_REG_PERF_ADD, p_event)) { 3688c2ecf20Sopenharmony_ci struct hlist_head __percpu *pcpu_list; 3698c2ecf20Sopenharmony_ci struct hlist_head *list; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci pcpu_list = tp_event->perf_events; 3728c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!pcpu_list)) 3738c2ecf20Sopenharmony_ci return -EINVAL; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci list = this_cpu_ptr(pcpu_list); 3768c2ecf20Sopenharmony_ci hlist_add_head_rcu(&p_event->hlist_entry, list); 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci return 0; 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_civoid perf_trace_del(struct perf_event *p_event, int flags) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci struct trace_event_call *tp_event = p_event->tp_event; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci /* 3878c2ecf20Sopenharmony_ci * If TRACE_REG_PERF_DEL returns false; no custom action was performed 3888c2ecf20Sopenharmony_ci * and we need to take the default action of dequeueing our event from 3898c2ecf20Sopenharmony_ci * the right per-cpu hlist. 3908c2ecf20Sopenharmony_ci */ 3918c2ecf20Sopenharmony_ci if (!tp_event->class->reg(tp_event, TRACE_REG_PERF_DEL, p_event)) 3928c2ecf20Sopenharmony_ci hlist_del_rcu(&p_event->hlist_entry); 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_civoid *perf_trace_buf_alloc(int size, struct pt_regs **regs, int *rctxp) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci char *raw_data; 3988c2ecf20Sopenharmony_ci int rctx; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci BUILD_BUG_ON(PERF_MAX_TRACE_SIZE % sizeof(unsigned long)); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE, 4038c2ecf20Sopenharmony_ci "perf buffer not large enough")) 4048c2ecf20Sopenharmony_ci return NULL; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci *rctxp = rctx = perf_swevent_get_recursion_context(); 4078c2ecf20Sopenharmony_ci if (rctx < 0) 4088c2ecf20Sopenharmony_ci return NULL; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci if (regs) 4118c2ecf20Sopenharmony_ci *regs = this_cpu_ptr(&__perf_regs[rctx]); 4128c2ecf20Sopenharmony_ci raw_data = this_cpu_ptr(perf_trace_buf[rctx]); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci /* zero the dead bytes from align to not leak stack to user */ 4158c2ecf20Sopenharmony_ci memset(&raw_data[size - sizeof(u64)], 0, sizeof(u64)); 4168c2ecf20Sopenharmony_ci return raw_data; 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(perf_trace_buf_alloc); 4198c2ecf20Sopenharmony_ciNOKPROBE_SYMBOL(perf_trace_buf_alloc); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_civoid perf_trace_buf_update(void *record, u16 type) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci struct trace_entry *entry = record; 4248c2ecf20Sopenharmony_ci int pc = preempt_count(); 4258c2ecf20Sopenharmony_ci unsigned long flags; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci local_save_flags(flags); 4288c2ecf20Sopenharmony_ci tracing_generic_entry_update(entry, type, flags, pc); 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ciNOKPROBE_SYMBOL(perf_trace_buf_update); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci#ifdef CONFIG_FUNCTION_TRACER 4338c2ecf20Sopenharmony_cistatic void 4348c2ecf20Sopenharmony_ciperf_ftrace_function_call(unsigned long ip, unsigned long parent_ip, 4358c2ecf20Sopenharmony_ci struct ftrace_ops *ops, struct pt_regs *pt_regs) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci struct ftrace_entry *entry; 4388c2ecf20Sopenharmony_ci struct perf_event *event; 4398c2ecf20Sopenharmony_ci struct hlist_head head; 4408c2ecf20Sopenharmony_ci struct pt_regs regs; 4418c2ecf20Sopenharmony_ci int rctx; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci if ((unsigned long)ops->private != smp_processor_id()) 4448c2ecf20Sopenharmony_ci return; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci event = container_of(ops, struct perf_event, ftrace_ops); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* 4498c2ecf20Sopenharmony_ci * @event->hlist entry is NULL (per INIT_HLIST_NODE), and all 4508c2ecf20Sopenharmony_ci * the perf code does is hlist_for_each_entry_rcu(), so we can 4518c2ecf20Sopenharmony_ci * get away with simply setting the @head.first pointer in order 4528c2ecf20Sopenharmony_ci * to create a singular list. 4538c2ecf20Sopenharmony_ci */ 4548c2ecf20Sopenharmony_ci head.first = &event->hlist_entry; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci#define ENTRY_SIZE (ALIGN(sizeof(struct ftrace_entry) + sizeof(u32), \ 4578c2ecf20Sopenharmony_ci sizeof(u64)) - sizeof(u32)) 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci BUILD_BUG_ON(ENTRY_SIZE > PERF_MAX_TRACE_SIZE); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci memset(®s, 0, sizeof(regs)); 4628c2ecf20Sopenharmony_ci perf_fetch_caller_regs(®s); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci entry = perf_trace_buf_alloc(ENTRY_SIZE, NULL, &rctx); 4658c2ecf20Sopenharmony_ci if (!entry) 4668c2ecf20Sopenharmony_ci return; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci entry->ip = ip; 4698c2ecf20Sopenharmony_ci entry->parent_ip = parent_ip; 4708c2ecf20Sopenharmony_ci perf_trace_buf_submit(entry, ENTRY_SIZE, rctx, TRACE_FN, 4718c2ecf20Sopenharmony_ci 1, ®s, &head, NULL); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci#undef ENTRY_SIZE 4748c2ecf20Sopenharmony_ci} 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_cistatic int perf_ftrace_function_register(struct perf_event *event) 4778c2ecf20Sopenharmony_ci{ 4788c2ecf20Sopenharmony_ci struct ftrace_ops *ops = &event->ftrace_ops; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci ops->flags = FTRACE_OPS_FL_RCU; 4818c2ecf20Sopenharmony_ci ops->func = perf_ftrace_function_call; 4828c2ecf20Sopenharmony_ci ops->private = (void *)(unsigned long)nr_cpu_ids; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci return register_ftrace_function(ops); 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic int perf_ftrace_function_unregister(struct perf_event *event) 4888c2ecf20Sopenharmony_ci{ 4898c2ecf20Sopenharmony_ci struct ftrace_ops *ops = &event->ftrace_ops; 4908c2ecf20Sopenharmony_ci int ret = unregister_ftrace_function(ops); 4918c2ecf20Sopenharmony_ci ftrace_free_filter(ops); 4928c2ecf20Sopenharmony_ci return ret; 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ciint perf_ftrace_event_register(struct trace_event_call *call, 4968c2ecf20Sopenharmony_ci enum trace_reg type, void *data) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci struct perf_event *event = data; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci switch (type) { 5018c2ecf20Sopenharmony_ci case TRACE_REG_REGISTER: 5028c2ecf20Sopenharmony_ci case TRACE_REG_UNREGISTER: 5038c2ecf20Sopenharmony_ci break; 5048c2ecf20Sopenharmony_ci case TRACE_REG_PERF_REGISTER: 5058c2ecf20Sopenharmony_ci case TRACE_REG_PERF_UNREGISTER: 5068c2ecf20Sopenharmony_ci return 0; 5078c2ecf20Sopenharmony_ci case TRACE_REG_PERF_OPEN: 5088c2ecf20Sopenharmony_ci return perf_ftrace_function_register(data); 5098c2ecf20Sopenharmony_ci case TRACE_REG_PERF_CLOSE: 5108c2ecf20Sopenharmony_ci return perf_ftrace_function_unregister(data); 5118c2ecf20Sopenharmony_ci case TRACE_REG_PERF_ADD: 5128c2ecf20Sopenharmony_ci event->ftrace_ops.private = (void *)(unsigned long)smp_processor_id(); 5138c2ecf20Sopenharmony_ci return 1; 5148c2ecf20Sopenharmony_ci case TRACE_REG_PERF_DEL: 5158c2ecf20Sopenharmony_ci event->ftrace_ops.private = (void *)(unsigned long)nr_cpu_ids; 5168c2ecf20Sopenharmony_ci return 1; 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci return -EINVAL; 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci#endif /* CONFIG_FUNCTION_TRACER */ 522