Merge branch 'perf/urgent' into perf/core, to pick up fixes
Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
@@ -530,6 +530,15 @@ config FUNCTION_PROFILER
|
||||
|
||||
If in doubt, say N.
|
||||
|
||||
config BPF_KPROBE_OVERRIDE
|
||||
bool "Enable BPF programs to override a kprobed function"
|
||||
depends on BPF_EVENTS
|
||||
depends on FUNCTION_ERROR_INJECTION
|
||||
default n
|
||||
help
|
||||
Allows BPF to override the execution of a probed function and
|
||||
set a different return value. This is used for error injection.
|
||||
|
||||
config FTRACE_MCOUNT_RECORD
|
||||
def_bool y
|
||||
depends on DYNAMIC_FTRACE
|
||||
|
@@ -13,6 +13,10 @@
|
||||
#include <linux/filter.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/kprobes.h>
|
||||
#include <linux/error-injection.h>
|
||||
|
||||
#include "trace_probe.h"
|
||||
#include "trace.h"
|
||||
|
||||
u64 bpf_get_stackid(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5);
|
||||
@@ -76,6 +80,23 @@ unsigned int trace_call_bpf(struct trace_event_call *call, void *ctx)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(trace_call_bpf);
|
||||
|
||||
#ifdef CONFIG_BPF_KPROBE_OVERRIDE
|
||||
BPF_CALL_2(bpf_override_return, struct pt_regs *, regs, unsigned long, rc)
|
||||
{
|
||||
regs_set_return_value(regs, rc);
|
||||
override_function_with_return(regs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct bpf_func_proto bpf_override_return_proto = {
|
||||
.func = bpf_override_return,
|
||||
.gpl_only = true,
|
||||
.ret_type = RET_INTEGER,
|
||||
.arg1_type = ARG_PTR_TO_CTX,
|
||||
.arg2_type = ARG_ANYTHING,
|
||||
};
|
||||
#endif
|
||||
|
||||
BPF_CALL_3(bpf_probe_read, void *, dst, u32, size, const void *, unsafe_ptr)
|
||||
{
|
||||
int ret;
|
||||
@@ -224,7 +245,7 @@ BPF_CALL_5(bpf_trace_printk, char *, fmt, u32, fmt_size, u64, arg1,
|
||||
*/
|
||||
#define __BPF_TP_EMIT() __BPF_ARG3_TP()
|
||||
#define __BPF_TP(...) \
|
||||
__trace_printk(1 /* Fake ip will not be printed. */, \
|
||||
__trace_printk(0 /* Fake ip */, \
|
||||
fmt, ##__VA_ARGS__)
|
||||
|
||||
#define __BPF_ARG1_TP(...) \
|
||||
@@ -556,6 +577,10 @@ static const struct bpf_func_proto *kprobe_prog_func_proto(enum bpf_func_id func
|
||||
return &bpf_get_stackid_proto;
|
||||
case BPF_FUNC_perf_event_read_value:
|
||||
return &bpf_perf_event_read_value_proto;
|
||||
#ifdef CONFIG_BPF_KPROBE_OVERRIDE
|
||||
case BPF_FUNC_override_return:
|
||||
return &bpf_override_return_proto;
|
||||
#endif
|
||||
default:
|
||||
return tracing_func_proto(func_id);
|
||||
}
|
||||
@@ -773,6 +798,15 @@ int perf_event_attach_bpf_prog(struct perf_event *event,
|
||||
struct bpf_prog_array *new_array;
|
||||
int ret = -EEXIST;
|
||||
|
||||
/*
|
||||
* Kprobe override only works if they are on the function entry,
|
||||
* and only if they are on the opt-in list.
|
||||
*/
|
||||
if (prog->kprobe_override &&
|
||||
(!trace_kprobe_on_func_entry(event->tp_event) ||
|
||||
!trace_kprobe_error_injectable(event->tp_event)))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&bpf_event_mutex);
|
||||
|
||||
if (event->prog)
|
||||
@@ -825,3 +859,26 @@ void perf_event_detach_bpf_prog(struct perf_event *event)
|
||||
unlock:
|
||||
mutex_unlock(&bpf_event_mutex);
|
||||
}
|
||||
|
||||
int perf_event_query_prog_array(struct perf_event *event, void __user *info)
|
||||
{
|
||||
struct perf_event_query_bpf __user *uquery = info;
|
||||
struct perf_event_query_bpf query = {};
|
||||
int ret;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
if (event->attr.type != PERF_TYPE_TRACEPOINT)
|
||||
return -EINVAL;
|
||||
if (copy_from_user(&query, uquery, sizeof(query)))
|
||||
return -EFAULT;
|
||||
|
||||
mutex_lock(&bpf_event_mutex);
|
||||
ret = bpf_prog_array_copy_info(event->tp_event->prog_array,
|
||||
uquery->ids,
|
||||
query.ids_len,
|
||||
&uquery->prog_cnt);
|
||||
mutex_unlock(&bpf_event_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@@ -4456,7 +4456,6 @@ unregister_ftrace_function_probe_func(char *glob, struct trace_array *tr,
|
||||
func_g.type = filter_parse_regex(glob, strlen(glob),
|
||||
&func_g.search, ¬);
|
||||
func_g.len = strlen(func_g.search);
|
||||
func_g.search = glob;
|
||||
|
||||
/* we do not support '!' for function probes */
|
||||
if (WARN_ON(not))
|
||||
@@ -5015,7 +5014,6 @@ int ftrace_regex_release(struct inode *inode, struct file *file)
|
||||
|
||||
parser = &iter->parser;
|
||||
if (trace_parser_loaded(parser)) {
|
||||
parser->buffer[parser->idx] = 0;
|
||||
ftrace_match_records(iter->hash, parser->buffer, parser->idx);
|
||||
}
|
||||
|
||||
@@ -5329,7 +5327,6 @@ ftrace_graph_release(struct inode *inode, struct file *file)
|
||||
parser = &fgd->parser;
|
||||
|
||||
if (trace_parser_loaded((parser))) {
|
||||
parser->buffer[parser->idx] = 0;
|
||||
ret = ftrace_graph_set_hash(fgd->new_hash,
|
||||
parser->buffer);
|
||||
}
|
||||
|
@@ -627,10 +627,10 @@ int ring_buffer_wait(struct ring_buffer *buffer, int cpu, bool full)
|
||||
* as data is added to any of the @buffer's cpu buffers. Otherwise
|
||||
* it will wait for data to be added to a specific cpu buffer.
|
||||
*
|
||||
* Returns POLLIN | POLLRDNORM if data exists in the buffers,
|
||||
* Returns EPOLLIN | EPOLLRDNORM if data exists in the buffers,
|
||||
* zero otherwise.
|
||||
*/
|
||||
int ring_buffer_poll_wait(struct ring_buffer *buffer, int cpu,
|
||||
__poll_t ring_buffer_poll_wait(struct ring_buffer *buffer, int cpu,
|
||||
struct file *filp, poll_table *poll_table)
|
||||
{
|
||||
struct ring_buffer_per_cpu *cpu_buffer;
|
||||
@@ -665,7 +665,7 @@ int ring_buffer_poll_wait(struct ring_buffer *buffer, int cpu,
|
||||
|
||||
if ((cpu == RING_BUFFER_ALL_CPUS && !ring_buffer_empty(buffer)) ||
|
||||
(cpu != RING_BUFFER_ALL_CPUS && !ring_buffer_empty_cpu(buffer, cpu)))
|
||||
return POLLIN | POLLRDNORM;
|
||||
return EPOLLIN | EPOLLRDNORM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@@ -530,8 +530,6 @@ int trace_pid_write(struct trace_pid_list *filtered_pids,
|
||||
ubuf += ret;
|
||||
cnt -= ret;
|
||||
|
||||
parser.buffer[parser.idx] = 0;
|
||||
|
||||
ret = -EINVAL;
|
||||
if (kstrtoul(parser.buffer, 0, &val))
|
||||
break;
|
||||
@@ -1236,18 +1234,18 @@ int trace_get_user(struct trace_parser *parser, const char __user *ubuf,
|
||||
cnt--;
|
||||
}
|
||||
|
||||
parser->idx = 0;
|
||||
|
||||
/* only spaces were written */
|
||||
if (isspace(ch)) {
|
||||
if (isspace(ch) || !ch) {
|
||||
*ppos += read;
|
||||
ret = read;
|
||||
goto out;
|
||||
}
|
||||
|
||||
parser->idx = 0;
|
||||
}
|
||||
|
||||
/* read the non-space input */
|
||||
while (cnt && !isspace(ch)) {
|
||||
while (cnt && !isspace(ch) && ch) {
|
||||
if (parser->idx < parser->size - 1)
|
||||
parser->buffer[parser->idx++] = ch;
|
||||
else {
|
||||
@@ -1262,12 +1260,14 @@ int trace_get_user(struct trace_parser *parser, const char __user *ubuf,
|
||||
}
|
||||
|
||||
/* We either got finished input or we have to wait for another call. */
|
||||
if (isspace(ch)) {
|
||||
if (isspace(ch) || !ch) {
|
||||
parser->buffer[parser->idx] = 0;
|
||||
parser->cont = false;
|
||||
} else if (parser->idx < parser->size - 1) {
|
||||
parser->cont = true;
|
||||
parser->buffer[parser->idx++] = ch;
|
||||
/* Make sure the parsed string always terminates with '\0'. */
|
||||
parser->buffer[parser->idx] = 0;
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
@@ -5616,26 +5616,26 @@ static int tracing_release_pipe(struct inode *inode, struct file *file)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
static __poll_t
|
||||
trace_poll(struct trace_iterator *iter, struct file *filp, poll_table *poll_table)
|
||||
{
|
||||
struct trace_array *tr = iter->tr;
|
||||
|
||||
/* Iterators are static, they should be filled or empty */
|
||||
if (trace_buffer_iter(iter, iter->cpu_file))
|
||||
return POLLIN | POLLRDNORM;
|
||||
return EPOLLIN | EPOLLRDNORM;
|
||||
|
||||
if (tr->trace_flags & TRACE_ITER_BLOCK)
|
||||
/*
|
||||
* Always select as readable when in blocking mode
|
||||
*/
|
||||
return POLLIN | POLLRDNORM;
|
||||
return EPOLLIN | EPOLLRDNORM;
|
||||
else
|
||||
return ring_buffer_poll_wait(iter->trace_buffer->buffer, iter->cpu_file,
|
||||
filp, poll_table);
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
static __poll_t
|
||||
tracing_poll_pipe(struct file *filp, poll_table *poll_table)
|
||||
{
|
||||
struct trace_iterator *iter = filp->private_data;
|
||||
@@ -6589,7 +6589,7 @@ static int tracing_buffers_open(struct inode *inode, struct file *filp)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
static __poll_t
|
||||
tracing_buffers_poll(struct file *filp, poll_table *poll_table)
|
||||
{
|
||||
struct ftrace_buffer_info *info = filp->private_data;
|
||||
|
@@ -885,8 +885,6 @@ ftrace_event_write(struct file *file, const char __user *ubuf,
|
||||
if (*parser.buffer == '!')
|
||||
set = 0;
|
||||
|
||||
parser.buffer[parser.idx] = 0;
|
||||
|
||||
ret = ftrace_set_clr_event(tr, parser.buffer + !set, set);
|
||||
if (ret)
|
||||
goto out_put;
|
||||
|
@@ -400,7 +400,6 @@ enum regex_type filter_parse_regex(char *buff, int len, char **search, int *not)
|
||||
for (i = 0; i < len; i++) {
|
||||
if (buff[i] == '*') {
|
||||
if (!i) {
|
||||
*search = buff + 1;
|
||||
type = MATCH_END_ONLY;
|
||||
} else if (i == len - 1) {
|
||||
if (type == MATCH_END_ONLY)
|
||||
@@ -410,14 +409,14 @@ enum regex_type filter_parse_regex(char *buff, int len, char **search, int *not)
|
||||
buff[i] = 0;
|
||||
break;
|
||||
} else { /* pattern continues, use full glob */
|
||||
type = MATCH_GLOB;
|
||||
break;
|
||||
return MATCH_GLOB;
|
||||
}
|
||||
} else if (strchr("[?\\", buff[i])) {
|
||||
type = MATCH_GLOB;
|
||||
break;
|
||||
return MATCH_GLOB;
|
||||
}
|
||||
}
|
||||
if (buff[0] == '*')
|
||||
*search = buff + 1;
|
||||
|
||||
return type;
|
||||
}
|
||||
|
@@ -21,6 +21,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/error-injection.h>
|
||||
|
||||
#include "trace_probe.h"
|
||||
|
||||
@@ -42,7 +43,6 @@ struct trace_kprobe {
|
||||
(offsetof(struct trace_kprobe, tp.args) + \
|
||||
(sizeof(struct probe_arg) * (n)))
|
||||
|
||||
|
||||
static nokprobe_inline bool trace_kprobe_is_return(struct trace_kprobe *tk)
|
||||
{
|
||||
return tk->rp.handler != NULL;
|
||||
@@ -87,6 +87,30 @@ static nokprobe_inline unsigned long trace_kprobe_nhit(struct trace_kprobe *tk)
|
||||
return nhit;
|
||||
}
|
||||
|
||||
bool trace_kprobe_on_func_entry(struct trace_event_call *call)
|
||||
{
|
||||
struct trace_kprobe *tk = (struct trace_kprobe *)call->data;
|
||||
|
||||
return kprobe_on_func_entry(tk->rp.kp.addr,
|
||||
tk->rp.kp.addr ? NULL : tk->rp.kp.symbol_name,
|
||||
tk->rp.kp.addr ? 0 : tk->rp.kp.offset);
|
||||
}
|
||||
|
||||
bool trace_kprobe_error_injectable(struct trace_event_call *call)
|
||||
{
|
||||
struct trace_kprobe *tk = (struct trace_kprobe *)call->data;
|
||||
unsigned long addr;
|
||||
|
||||
if (tk->symbol) {
|
||||
addr = (unsigned long)
|
||||
kallsyms_lookup_name(trace_kprobe_symbol(tk));
|
||||
addr += tk->rp.kp.offset;
|
||||
} else {
|
||||
addr = (unsigned long)tk->rp.kp.addr;
|
||||
}
|
||||
return within_error_injection_list(addr);
|
||||
}
|
||||
|
||||
static int register_kprobe_event(struct trace_kprobe *tk);
|
||||
static int unregister_kprobe_event(struct trace_kprobe *tk);
|
||||
|
||||
@@ -1178,7 +1202,7 @@ static int kretprobe_event_define_fields(struct trace_event_call *event_call)
|
||||
#ifdef CONFIG_PERF_EVENTS
|
||||
|
||||
/* Kprobe profile handler */
|
||||
static void
|
||||
static int
|
||||
kprobe_perf_func(struct trace_kprobe *tk, struct pt_regs *regs)
|
||||
{
|
||||
struct trace_event_call *call = &tk->tp.call;
|
||||
@@ -1187,12 +1211,31 @@ kprobe_perf_func(struct trace_kprobe *tk, struct pt_regs *regs)
|
||||
int size, __size, dsize;
|
||||
int rctx;
|
||||
|
||||
if (bpf_prog_array_valid(call) && !trace_call_bpf(call, regs))
|
||||
return;
|
||||
if (bpf_prog_array_valid(call)) {
|
||||
unsigned long orig_ip = instruction_pointer(regs);
|
||||
int ret;
|
||||
|
||||
ret = trace_call_bpf(call, regs);
|
||||
|
||||
/*
|
||||
* We need to check and see if we modified the pc of the
|
||||
* pt_regs, and if so clear the kprobe and return 1 so that we
|
||||
* don't do the single stepping.
|
||||
* The ftrace kprobe handler leaves it up to us to re-enable
|
||||
* preemption here before returning if we've modified the ip.
|
||||
*/
|
||||
if (orig_ip != instruction_pointer(regs)) {
|
||||
reset_current_kprobe();
|
||||
preempt_enable_no_resched();
|
||||
return 1;
|
||||
}
|
||||
if (!ret)
|
||||
return 0;
|
||||
}
|
||||
|
||||
head = this_cpu_ptr(call->perf_events);
|
||||
if (hlist_empty(head))
|
||||
return;
|
||||
return 0;
|
||||
|
||||
dsize = __get_data_size(&tk->tp, regs);
|
||||
__size = sizeof(*entry) + tk->tp.size + dsize;
|
||||
@@ -1201,13 +1244,14 @@ kprobe_perf_func(struct trace_kprobe *tk, struct pt_regs *regs)
|
||||
|
||||
entry = perf_trace_buf_alloc(size, NULL, &rctx);
|
||||
if (!entry)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
entry->ip = (unsigned long)tk->rp.kp.addr;
|
||||
memset(&entry[1], 0, dsize);
|
||||
store_trace_args(sizeof(*entry), &tk->tp, regs, (u8 *)&entry[1], dsize);
|
||||
perf_trace_buf_submit(entry, size, rctx, call->event.type, 1, regs,
|
||||
head, NULL);
|
||||
return 0;
|
||||
}
|
||||
NOKPROBE_SYMBOL(kprobe_perf_func);
|
||||
|
||||
@@ -1283,6 +1327,7 @@ static int kprobe_register(struct trace_event_call *event,
|
||||
static int kprobe_dispatcher(struct kprobe *kp, struct pt_regs *regs)
|
||||
{
|
||||
struct trace_kprobe *tk = container_of(kp, struct trace_kprobe, rp.kp);
|
||||
int ret = 0;
|
||||
|
||||
raw_cpu_inc(*tk->nhit);
|
||||
|
||||
@@ -1290,9 +1335,9 @@ static int kprobe_dispatcher(struct kprobe *kp, struct pt_regs *regs)
|
||||
kprobe_trace_func(tk, regs);
|
||||
#ifdef CONFIG_PERF_EVENTS
|
||||
if (tk->tp.flags & TP_FLAG_PROFILE)
|
||||
kprobe_perf_func(tk, regs);
|
||||
ret = kprobe_perf_func(tk, regs);
|
||||
#endif
|
||||
return 0; /* We don't tweek kernel, so just return 0 */
|
||||
return ret;
|
||||
}
|
||||
NOKPROBE_SYMBOL(kprobe_dispatcher);
|
||||
|
||||
|
@@ -252,6 +252,8 @@ struct symbol_cache;
|
||||
unsigned long update_symbol_cache(struct symbol_cache *sc);
|
||||
void free_symbol_cache(struct symbol_cache *sc);
|
||||
struct symbol_cache *alloc_symbol_cache(const char *sym, long offset);
|
||||
bool trace_kprobe_on_func_entry(struct trace_event_call *call);
|
||||
bool trace_kprobe_error_injectable(struct trace_event_call *call);
|
||||
#else
|
||||
/* uprobes do not support symbol fetch methods */
|
||||
#define fetch_symbol_u8 NULL
|
||||
@@ -277,6 +279,16 @@ alloc_symbol_cache(const char *sym, long offset)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline bool trace_kprobe_on_func_entry(struct trace_event_call *call)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool trace_kprobe_error_injectable(struct trace_event_call *call)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif /* CONFIG_KPROBE_EVENTS */
|
||||
|
||||
struct probe_arg {
|
||||
|
@@ -1,13 +1,14 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/compiler.h>
|
||||
#include "trace.h"
|
||||
|
||||
int DYN_FTRACE_TEST_NAME(void)
|
||||
noinline __noclone int DYN_FTRACE_TEST_NAME(void)
|
||||
{
|
||||
/* used to call mcount */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int DYN_FTRACE_TEST_NAME2(void)
|
||||
noinline __noclone int DYN_FTRACE_TEST_NAME2(void)
|
||||
{
|
||||
/* used to call mcount */
|
||||
return 0;
|
||||
|
@@ -608,7 +608,7 @@ static int probes_seq_show(struct seq_file *m, void *v)
|
||||
|
||||
/* Don't print "0x (null)" when offset is 0 */
|
||||
if (tu->offset) {
|
||||
seq_printf(m, "0x%p", (void *)tu->offset);
|
||||
seq_printf(m, "0x%px", (void *)tu->offset);
|
||||
} else {
|
||||
switch (sizeof(void *)) {
|
||||
case 4:
|
||||
|
Reference in New Issue
Block a user