Commit 0e73ef1d authored by Andrii Nakryiko's avatar Andrii Nakryiko

Merge branch 'bpf: Add missed stats for kprobes'

Jiri Olsa says:

====================
hi,
at the moment we can't retrieve the number of missed kprobe
executions and subsequent execution of BPF programs.

This patchset adds:
  - counting of missed execution on attach layer for:
    . kprobes attached through perf link (kprobe/ftrace)
    . kprobes attached through kprobe.multi link (fprobe)
  - counting of recursion_misses for BPF kprobe programs

It's still technically possible to create kprobe without perf link (using
SET_BPF perf ioctl) in which case we don't have a way to retrieve the kprobe's
'missed' count. However both libbpf and cilium/ebpf libraries use perf link
if it's available, and for old kernels without perf link support we can use
BPF program to retrieve the kprobe missed count.

v3 changes:
  - added acks [Song]
  - make test_missed not serial [Andrii]

Also available at:
  https://git.kernel.org/pub/scm/linux/kernel/git/jolsa/perf.git
  bpf/missed_stats

thanks,
jirka
====================
Signed-off-by: default avatarAndrii Nakryiko <andrii@kernel.org>
parents e0fa6523 85981e0f
...@@ -2922,6 +2922,22 @@ static inline int sock_map_bpf_prog_query(const union bpf_attr *attr, ...@@ -2922,6 +2922,22 @@ static inline int sock_map_bpf_prog_query(const union bpf_attr *attr,
#endif /* CONFIG_BPF_SYSCALL */ #endif /* CONFIG_BPF_SYSCALL */
#endif /* CONFIG_NET && CONFIG_BPF_SYSCALL */ #endif /* CONFIG_NET && CONFIG_BPF_SYSCALL */
static __always_inline void
bpf_prog_inc_misses_counters(const struct bpf_prog_array *array)
{
const struct bpf_prog_array_item *item;
struct bpf_prog *prog;
if (unlikely(!array))
return;
item = &array->items[0];
while ((prog = READ_ONCE(item->prog))) {
bpf_prog_inc_misses_counter(prog);
item++;
}
}
#if defined(CONFIG_INET) && defined(CONFIG_BPF_SYSCALL) #if defined(CONFIG_INET) && defined(CONFIG_BPF_SYSCALL)
void bpf_sk_reuseport_detach(struct sock *sk); void bpf_sk_reuseport_detach(struct sock *sk);
int bpf_fd_reuseport_array_lookup_elem(struct bpf_map *map, void *key, int bpf_fd_reuseport_array_lookup_elem(struct bpf_map *map, void *key,
......
...@@ -761,7 +761,8 @@ struct bpf_raw_event_map *bpf_get_raw_tracepoint(const char *name); ...@@ -761,7 +761,8 @@ struct bpf_raw_event_map *bpf_get_raw_tracepoint(const char *name);
void bpf_put_raw_tracepoint(struct bpf_raw_event_map *btp); void bpf_put_raw_tracepoint(struct bpf_raw_event_map *btp);
int bpf_get_perf_event_info(const struct perf_event *event, u32 *prog_id, int bpf_get_perf_event_info(const struct perf_event *event, u32 *prog_id,
u32 *fd_type, const char **buf, u32 *fd_type, const char **buf,
u64 *probe_offset, u64 *probe_addr); u64 *probe_offset, u64 *probe_addr,
unsigned long *missed);
int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog); int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog);
int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog); int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog);
#else #else
...@@ -801,7 +802,7 @@ static inline void bpf_put_raw_tracepoint(struct bpf_raw_event_map *btp) ...@@ -801,7 +802,7 @@ static inline void bpf_put_raw_tracepoint(struct bpf_raw_event_map *btp)
static inline int bpf_get_perf_event_info(const struct perf_event *event, static inline int bpf_get_perf_event_info(const struct perf_event *event,
u32 *prog_id, u32 *fd_type, u32 *prog_id, u32 *fd_type,
const char **buf, u64 *probe_offset, const char **buf, u64 *probe_offset,
u64 *probe_addr) u64 *probe_addr, unsigned long *missed)
{ {
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
...@@ -877,6 +878,7 @@ extern void perf_kprobe_destroy(struct perf_event *event); ...@@ -877,6 +878,7 @@ extern void perf_kprobe_destroy(struct perf_event *event);
extern int bpf_get_kprobe_info(const struct perf_event *event, extern int bpf_get_kprobe_info(const struct perf_event *event,
u32 *fd_type, const char **symbol, u32 *fd_type, const char **symbol,
u64 *probe_offset, u64 *probe_addr, u64 *probe_offset, u64 *probe_addr,
unsigned long *missed,
bool perf_type_tracepoint); bool perf_type_tracepoint);
#endif #endif
#ifdef CONFIG_UPROBE_EVENTS #ifdef CONFIG_UPROBE_EVENTS
......
...@@ -6532,6 +6532,7 @@ struct bpf_link_info { ...@@ -6532,6 +6532,7 @@ struct bpf_link_info {
__aligned_u64 addrs; __aligned_u64 addrs;
__u32 count; /* in/out: kprobe_multi function count */ __u32 count; /* in/out: kprobe_multi function count */
__u32 flags; __u32 flags;
__u64 missed;
} kprobe_multi; } kprobe_multi;
struct { struct {
__u32 type; /* enum bpf_perf_event_type */ __u32 type; /* enum bpf_perf_event_type */
...@@ -6547,6 +6548,7 @@ struct bpf_link_info { ...@@ -6547,6 +6548,7 @@ struct bpf_link_info {
__u32 name_len; __u32 name_len;
__u32 offset; /* offset from func_name */ __u32 offset; /* offset from func_name */
__u64 addr; __u64 addr;
__u64 missed;
} kprobe; /* BPF_PERF_EVENT_KPROBE, BPF_PERF_EVENT_KRETPROBE */ } kprobe; /* BPF_PERF_EVENT_KPROBE, BPF_PERF_EVENT_KRETPROBE */
struct { struct {
__aligned_u64 tp_name; /* in/out */ __aligned_u64 tp_name; /* in/out */
......
...@@ -3374,7 +3374,7 @@ static void bpf_perf_link_dealloc(struct bpf_link *link) ...@@ -3374,7 +3374,7 @@ static void bpf_perf_link_dealloc(struct bpf_link *link)
static int bpf_perf_link_fill_common(const struct perf_event *event, static int bpf_perf_link_fill_common(const struct perf_event *event,
char __user *uname, u32 ulen, char __user *uname, u32 ulen,
u64 *probe_offset, u64 *probe_addr, u64 *probe_offset, u64 *probe_addr,
u32 *fd_type) u32 *fd_type, unsigned long *missed)
{ {
const char *buf; const char *buf;
u32 prog_id; u32 prog_id;
...@@ -3385,7 +3385,7 @@ static int bpf_perf_link_fill_common(const struct perf_event *event, ...@@ -3385,7 +3385,7 @@ static int bpf_perf_link_fill_common(const struct perf_event *event,
return -EINVAL; return -EINVAL;
err = bpf_get_perf_event_info(event, &prog_id, fd_type, &buf, err = bpf_get_perf_event_info(event, &prog_id, fd_type, &buf,
probe_offset, probe_addr); probe_offset, probe_addr, missed);
if (err) if (err)
return err; return err;
if (!uname) if (!uname)
...@@ -3408,6 +3408,7 @@ static int bpf_perf_link_fill_common(const struct perf_event *event, ...@@ -3408,6 +3408,7 @@ static int bpf_perf_link_fill_common(const struct perf_event *event,
static int bpf_perf_link_fill_kprobe(const struct perf_event *event, static int bpf_perf_link_fill_kprobe(const struct perf_event *event,
struct bpf_link_info *info) struct bpf_link_info *info)
{ {
unsigned long missed;
char __user *uname; char __user *uname;
u64 addr, offset; u64 addr, offset;
u32 ulen, type; u32 ulen, type;
...@@ -3416,7 +3417,7 @@ static int bpf_perf_link_fill_kprobe(const struct perf_event *event, ...@@ -3416,7 +3417,7 @@ static int bpf_perf_link_fill_kprobe(const struct perf_event *event,
uname = u64_to_user_ptr(info->perf_event.kprobe.func_name); uname = u64_to_user_ptr(info->perf_event.kprobe.func_name);
ulen = info->perf_event.kprobe.name_len; ulen = info->perf_event.kprobe.name_len;
err = bpf_perf_link_fill_common(event, uname, ulen, &offset, &addr, err = bpf_perf_link_fill_common(event, uname, ulen, &offset, &addr,
&type); &type, &missed);
if (err) if (err)
return err; return err;
if (type == BPF_FD_TYPE_KRETPROBE) if (type == BPF_FD_TYPE_KRETPROBE)
...@@ -3425,6 +3426,7 @@ static int bpf_perf_link_fill_kprobe(const struct perf_event *event, ...@@ -3425,6 +3426,7 @@ static int bpf_perf_link_fill_kprobe(const struct perf_event *event,
info->perf_event.type = BPF_PERF_EVENT_KPROBE; info->perf_event.type = BPF_PERF_EVENT_KPROBE;
info->perf_event.kprobe.offset = offset; info->perf_event.kprobe.offset = offset;
info->perf_event.kprobe.missed = missed;
if (!kallsyms_show_value(current_cred())) if (!kallsyms_show_value(current_cred()))
addr = 0; addr = 0;
info->perf_event.kprobe.addr = addr; info->perf_event.kprobe.addr = addr;
...@@ -3444,7 +3446,7 @@ static int bpf_perf_link_fill_uprobe(const struct perf_event *event, ...@@ -3444,7 +3446,7 @@ static int bpf_perf_link_fill_uprobe(const struct perf_event *event,
uname = u64_to_user_ptr(info->perf_event.uprobe.file_name); uname = u64_to_user_ptr(info->perf_event.uprobe.file_name);
ulen = info->perf_event.uprobe.name_len; ulen = info->perf_event.uprobe.name_len;
err = bpf_perf_link_fill_common(event, uname, ulen, &offset, &addr, err = bpf_perf_link_fill_common(event, uname, ulen, &offset, &addr,
&type); &type, NULL);
if (err) if (err)
return err; return err;
...@@ -3480,7 +3482,7 @@ static int bpf_perf_link_fill_tracepoint(const struct perf_event *event, ...@@ -3480,7 +3482,7 @@ static int bpf_perf_link_fill_tracepoint(const struct perf_event *event,
uname = u64_to_user_ptr(info->perf_event.tracepoint.tp_name); uname = u64_to_user_ptr(info->perf_event.tracepoint.tp_name);
ulen = info->perf_event.tracepoint.name_len; ulen = info->perf_event.tracepoint.name_len;
info->perf_event.type = BPF_PERF_EVENT_TRACEPOINT; info->perf_event.type = BPF_PERF_EVENT_TRACEPOINT;
return bpf_perf_link_fill_common(event, uname, ulen, NULL, NULL, NULL); return bpf_perf_link_fill_common(event, uname, ulen, NULL, NULL, NULL, NULL);
} }
static int bpf_perf_link_fill_perf_event(const struct perf_event *event, static int bpf_perf_link_fill_perf_event(const struct perf_event *event,
...@@ -4813,7 +4815,7 @@ static int bpf_task_fd_query(const union bpf_attr *attr, ...@@ -4813,7 +4815,7 @@ static int bpf_task_fd_query(const union bpf_attr *attr,
err = bpf_get_perf_event_info(event, &prog_id, &fd_type, err = bpf_get_perf_event_info(event, &prog_id, &fd_type,
&buf, &probe_offset, &buf, &probe_offset,
&probe_addr); &probe_addr, NULL);
if (!err) if (!err)
err = bpf_task_fd_query_copy(attr, uattr, prog_id, err = bpf_task_fd_query_copy(attr, uattr, prog_id,
fd_type, buf, fd_type, buf,
......
...@@ -117,6 +117,9 @@ unsigned int trace_call_bpf(struct trace_event_call *call, void *ctx) ...@@ -117,6 +117,9 @@ unsigned int trace_call_bpf(struct trace_event_call *call, void *ctx)
* and don't send kprobe event into ring-buffer, * and don't send kprobe event into ring-buffer,
* so return zero here * so return zero here
*/ */
rcu_read_lock();
bpf_prog_inc_misses_counters(rcu_dereference(call->prog_array));
rcu_read_unlock();
ret = 0; ret = 0;
goto out; goto out;
} }
...@@ -2384,7 +2387,8 @@ int bpf_probe_unregister(struct bpf_raw_event_map *btp, struct bpf_prog *prog) ...@@ -2384,7 +2387,8 @@ int bpf_probe_unregister(struct bpf_raw_event_map *btp, struct bpf_prog *prog)
int bpf_get_perf_event_info(const struct perf_event *event, u32 *prog_id, int bpf_get_perf_event_info(const struct perf_event *event, u32 *prog_id,
u32 *fd_type, const char **buf, u32 *fd_type, const char **buf,
u64 *probe_offset, u64 *probe_addr) u64 *probe_offset, u64 *probe_addr,
unsigned long *missed)
{ {
bool is_tracepoint, is_syscall_tp; bool is_tracepoint, is_syscall_tp;
struct bpf_prog *prog; struct bpf_prog *prog;
...@@ -2419,7 +2423,7 @@ int bpf_get_perf_event_info(const struct perf_event *event, u32 *prog_id, ...@@ -2419,7 +2423,7 @@ int bpf_get_perf_event_info(const struct perf_event *event, u32 *prog_id,
#ifdef CONFIG_KPROBE_EVENTS #ifdef CONFIG_KPROBE_EVENTS
if (flags & TRACE_EVENT_FL_KPROBE) if (flags & TRACE_EVENT_FL_KPROBE)
err = bpf_get_kprobe_info(event, fd_type, buf, err = bpf_get_kprobe_info(event, fd_type, buf,
probe_offset, probe_addr, probe_offset, probe_addr, missed,
event->attr.type == PERF_TYPE_TRACEPOINT); event->attr.type == PERF_TYPE_TRACEPOINT);
#endif #endif
#ifdef CONFIG_UPROBE_EVENTS #ifdef CONFIG_UPROBE_EVENTS
...@@ -2614,6 +2618,7 @@ static int bpf_kprobe_multi_link_fill_link_info(const struct bpf_link *link, ...@@ -2614,6 +2618,7 @@ static int bpf_kprobe_multi_link_fill_link_info(const struct bpf_link *link,
kmulti_link = container_of(link, struct bpf_kprobe_multi_link, link); kmulti_link = container_of(link, struct bpf_kprobe_multi_link, link);
info->kprobe_multi.count = kmulti_link->cnt; info->kprobe_multi.count = kmulti_link->cnt;
info->kprobe_multi.flags = kmulti_link->flags; info->kprobe_multi.flags = kmulti_link->flags;
info->kprobe_multi.missed = kmulti_link->fp.nmissed;
if (!uaddrs) if (!uaddrs)
return 0; return 0;
...@@ -2710,6 +2715,7 @@ kprobe_multi_link_prog_run(struct bpf_kprobe_multi_link *link, ...@@ -2710,6 +2715,7 @@ kprobe_multi_link_prog_run(struct bpf_kprobe_multi_link *link,
int err; int err;
if (unlikely(__this_cpu_inc_return(bpf_prog_active) != 1)) { if (unlikely(__this_cpu_inc_return(bpf_prog_active) != 1)) {
bpf_prog_inc_misses_counter(link->link.prog);
err = 0; err = 0;
goto out; goto out;
} }
......
...@@ -1189,6 +1189,12 @@ static const struct file_operations kprobe_events_ops = { ...@@ -1189,6 +1189,12 @@ static const struct file_operations kprobe_events_ops = {
.write = probes_write, .write = probes_write,
}; };
static unsigned long trace_kprobe_missed(struct trace_kprobe *tk)
{
return trace_kprobe_is_return(tk) ?
tk->rp.kp.nmissed + tk->rp.nmissed : tk->rp.kp.nmissed;
}
/* Probes profiling interfaces */ /* Probes profiling interfaces */
static int probes_profile_seq_show(struct seq_file *m, void *v) static int probes_profile_seq_show(struct seq_file *m, void *v)
{ {
...@@ -1200,8 +1206,7 @@ static int probes_profile_seq_show(struct seq_file *m, void *v) ...@@ -1200,8 +1206,7 @@ static int probes_profile_seq_show(struct seq_file *m, void *v)
return 0; return 0;
tk = to_trace_kprobe(ev); tk = to_trace_kprobe(ev);
nmissed = trace_kprobe_is_return(tk) ? nmissed = trace_kprobe_missed(tk);
tk->rp.kp.nmissed + tk->rp.nmissed : tk->rp.kp.nmissed;
seq_printf(m, " %-44s %15lu %15lu\n", seq_printf(m, " %-44s %15lu %15lu\n",
trace_probe_name(&tk->tp), trace_probe_name(&tk->tp),
trace_kprobe_nhit(tk), trace_kprobe_nhit(tk),
...@@ -1547,7 +1552,8 @@ NOKPROBE_SYMBOL(kretprobe_perf_func); ...@@ -1547,7 +1552,8 @@ NOKPROBE_SYMBOL(kretprobe_perf_func);
int bpf_get_kprobe_info(const struct perf_event *event, u32 *fd_type, int bpf_get_kprobe_info(const struct perf_event *event, u32 *fd_type,
const char **symbol, u64 *probe_offset, const char **symbol, u64 *probe_offset,
u64 *probe_addr, bool perf_type_tracepoint) u64 *probe_addr, unsigned long *missed,
bool perf_type_tracepoint)
{ {
const char *pevent = trace_event_name(event->tp_event); const char *pevent = trace_event_name(event->tp_event);
const char *group = event->tp_event->class->system; const char *group = event->tp_event->class->system;
...@@ -1566,6 +1572,8 @@ int bpf_get_kprobe_info(const struct perf_event *event, u32 *fd_type, ...@@ -1566,6 +1572,8 @@ int bpf_get_kprobe_info(const struct perf_event *event, u32 *fd_type,
*probe_addr = kallsyms_show_value(current_cred()) ? *probe_addr = kallsyms_show_value(current_cred()) ?
(unsigned long)tk->rp.kp.addr : 0; (unsigned long)tk->rp.kp.addr : 0;
*symbol = tk->symbol; *symbol = tk->symbol;
if (missed)
*missed = trace_kprobe_missed(tk);
return 0; return 0;
} }
#endif /* CONFIG_PERF_EVENTS */ #endif /* CONFIG_PERF_EVENTS */
......
...@@ -265,6 +265,7 @@ show_kprobe_multi_json(struct bpf_link_info *info, json_writer_t *wtr) ...@@ -265,6 +265,7 @@ show_kprobe_multi_json(struct bpf_link_info *info, json_writer_t *wtr)
jsonw_bool_field(json_wtr, "retprobe", jsonw_bool_field(json_wtr, "retprobe",
info->kprobe_multi.flags & BPF_F_KPROBE_MULTI_RETURN); info->kprobe_multi.flags & BPF_F_KPROBE_MULTI_RETURN);
jsonw_uint_field(json_wtr, "func_cnt", info->kprobe_multi.count); jsonw_uint_field(json_wtr, "func_cnt", info->kprobe_multi.count);
jsonw_uint_field(json_wtr, "missed", info->kprobe_multi.missed);
jsonw_name(json_wtr, "funcs"); jsonw_name(json_wtr, "funcs");
jsonw_start_array(json_wtr); jsonw_start_array(json_wtr);
addrs = u64_to_ptr(info->kprobe_multi.addrs); addrs = u64_to_ptr(info->kprobe_multi.addrs);
...@@ -301,6 +302,7 @@ show_perf_event_kprobe_json(struct bpf_link_info *info, json_writer_t *wtr) ...@@ -301,6 +302,7 @@ show_perf_event_kprobe_json(struct bpf_link_info *info, json_writer_t *wtr)
jsonw_string_field(wtr, "func", jsonw_string_field(wtr, "func",
u64_to_ptr(info->perf_event.kprobe.func_name)); u64_to_ptr(info->perf_event.kprobe.func_name));
jsonw_uint_field(wtr, "offset", info->perf_event.kprobe.offset); jsonw_uint_field(wtr, "offset", info->perf_event.kprobe.offset);
jsonw_uint_field(wtr, "missed", info->perf_event.kprobe.missed);
} }
static void static void
...@@ -641,6 +643,8 @@ static void show_kprobe_multi_plain(struct bpf_link_info *info) ...@@ -641,6 +643,8 @@ static void show_kprobe_multi_plain(struct bpf_link_info *info)
else else
printf("\n\tkprobe.multi "); printf("\n\tkprobe.multi ");
printf("func_cnt %u ", info->kprobe_multi.count); printf("func_cnt %u ", info->kprobe_multi.count);
if (info->kprobe_multi.missed)
printf("missed %llu ", info->kprobe_multi.missed);
addrs = (__u64 *)u64_to_ptr(info->kprobe_multi.addrs); addrs = (__u64 *)u64_to_ptr(info->kprobe_multi.addrs);
qsort(addrs, info->kprobe_multi.count, sizeof(__u64), cmp_u64); qsort(addrs, info->kprobe_multi.count, sizeof(__u64), cmp_u64);
...@@ -683,6 +687,8 @@ static void show_perf_event_kprobe_plain(struct bpf_link_info *info) ...@@ -683,6 +687,8 @@ static void show_perf_event_kprobe_plain(struct bpf_link_info *info)
printf("%s", buf); printf("%s", buf);
if (info->perf_event.kprobe.offset) if (info->perf_event.kprobe.offset)
printf("+%#x", info->perf_event.kprobe.offset); printf("+%#x", info->perf_event.kprobe.offset);
if (info->perf_event.kprobe.missed)
printf(" missed %llu", info->perf_event.kprobe.missed);
printf(" "); printf(" ");
} }
......
...@@ -6532,6 +6532,7 @@ struct bpf_link_info { ...@@ -6532,6 +6532,7 @@ struct bpf_link_info {
__aligned_u64 addrs; __aligned_u64 addrs;
__u32 count; /* in/out: kprobe_multi function count */ __u32 count; /* in/out: kprobe_multi function count */
__u32 flags; __u32 flags;
__u64 missed;
} kprobe_multi; } kprobe_multi;
struct { struct {
__u32 type; /* enum bpf_perf_event_type */ __u32 type; /* enum bpf_perf_event_type */
...@@ -6547,6 +6548,7 @@ struct bpf_link_info { ...@@ -6547,6 +6548,7 @@ struct bpf_link_info {
__u32 name_len; __u32 name_len;
__u32 offset; /* offset from func_name */ __u32 offset; /* offset from func_name */
__u64 addr; __u64 addr;
__u64 missed;
} kprobe; /* BPF_PERF_EVENT_KPROBE, BPF_PERF_EVENT_KRETPROBE */ } kprobe; /* BPF_PERF_EVENT_KPROBE, BPF_PERF_EVENT_KRETPROBE */
struct { struct {
__aligned_u64 tp_name; /* in/out */ __aligned_u64 tp_name; /* in/out */
......
...@@ -10,3 +10,4 @@ fexit_test/fexit_many_args # fexit_many_args:FAIL:fexit_ma ...@@ -10,3 +10,4 @@ fexit_test/fexit_many_args # fexit_many_args:FAIL:fexit_ma
fill_link_info/kprobe_multi_link_info # bpf_program__attach_kprobe_multi_opts unexpected error: -95 fill_link_info/kprobe_multi_link_info # bpf_program__attach_kprobe_multi_opts unexpected error: -95
fill_link_info/kretprobe_multi_link_info # bpf_program__attach_kprobe_multi_opts unexpected error: -95 fill_link_info/kretprobe_multi_link_info # bpf_program__attach_kprobe_multi_opts unexpected error: -95
fill_link_info/kprobe_multi_invalid_ubuff # bpf_program__attach_kprobe_multi_opts unexpected error: -95 fill_link_info/kprobe_multi_invalid_ubuff # bpf_program__attach_kprobe_multi_opts unexpected error: -95
missed/kprobe_recursion # missed_kprobe_recursion__attach unexpected error: -95 (errno 95)
...@@ -138,6 +138,10 @@ __bpf_kfunc void bpf_iter_testmod_seq_destroy(struct bpf_iter_testmod_seq *it) ...@@ -138,6 +138,10 @@ __bpf_kfunc void bpf_iter_testmod_seq_destroy(struct bpf_iter_testmod_seq *it)
it->cnt = 0; it->cnt = 0;
} }
__bpf_kfunc void bpf_kfunc_common_test(void)
{
}
struct bpf_testmod_btf_type_tag_1 { struct bpf_testmod_btf_type_tag_1 {
int a; int a;
}; };
...@@ -343,6 +347,7 @@ BTF_SET8_START(bpf_testmod_common_kfunc_ids) ...@@ -343,6 +347,7 @@ BTF_SET8_START(bpf_testmod_common_kfunc_ids)
BTF_ID_FLAGS(func, bpf_iter_testmod_seq_new, KF_ITER_NEW) BTF_ID_FLAGS(func, bpf_iter_testmod_seq_new, KF_ITER_NEW)
BTF_ID_FLAGS(func, bpf_iter_testmod_seq_next, KF_ITER_NEXT | KF_RET_NULL) BTF_ID_FLAGS(func, bpf_iter_testmod_seq_next, KF_ITER_NEXT | KF_RET_NULL)
BTF_ID_FLAGS(func, bpf_iter_testmod_seq_destroy, KF_ITER_DESTROY) BTF_ID_FLAGS(func, bpf_iter_testmod_seq_destroy, KF_ITER_DESTROY)
BTF_ID_FLAGS(func, bpf_kfunc_common_test)
BTF_SET8_END(bpf_testmod_common_kfunc_ids) BTF_SET8_END(bpf_testmod_common_kfunc_ids)
static const struct btf_kfunc_id_set bpf_testmod_common_kfunc_set = { static const struct btf_kfunc_id_set bpf_testmod_common_kfunc_set = {
......
...@@ -104,4 +104,6 @@ void bpf_kfunc_call_test_fail1(struct prog_test_fail1 *p); ...@@ -104,4 +104,6 @@ void bpf_kfunc_call_test_fail1(struct prog_test_fail1 *p);
void bpf_kfunc_call_test_fail2(struct prog_test_fail2 *p); void bpf_kfunc_call_test_fail2(struct prog_test_fail2 *p);
void bpf_kfunc_call_test_fail3(struct prog_test_fail3 *p); void bpf_kfunc_call_test_fail3(struct prog_test_fail3 *p);
void bpf_kfunc_call_test_mem_len_fail1(void *mem, int len); void bpf_kfunc_call_test_mem_len_fail1(void *mem, int len);
void bpf_kfunc_common_test(void) __ksym;
#endif /* _BPF_TESTMOD_KFUNC_H */ #endif /* _BPF_TESTMOD_KFUNC_H */
// SPDX-License-Identifier: GPL-2.0
#include <test_progs.h>
#include "missed_kprobe.skel.h"
#include "missed_kprobe_recursion.skel.h"
#include "missed_tp_recursion.skel.h"
/*
* Putting kprobe on bpf_fentry_test1 that calls bpf_kfunc_common_test
* kfunc, which has also kprobe on. The latter won't get triggered due
* to kprobe recursion check and kprobe missed counter is incremented.
*/
static void test_missed_perf_kprobe(void)
{
LIBBPF_OPTS(bpf_test_run_opts, topts);
struct bpf_link_info info = {};
struct missed_kprobe *skel;
__u32 len = sizeof(info);
int err, prog_fd;
skel = missed_kprobe__open_and_load();
if (!ASSERT_OK_PTR(skel, "missed_kprobe__open_and_load"))
goto cleanup;
err = missed_kprobe__attach(skel);
if (!ASSERT_OK(err, "missed_kprobe__attach"))
goto cleanup;
prog_fd = bpf_program__fd(skel->progs.trigger);
err = bpf_prog_test_run_opts(prog_fd, &topts);
ASSERT_OK(err, "test_run");
ASSERT_EQ(topts.retval, 0, "test_run");
err = bpf_link_get_info_by_fd(bpf_link__fd(skel->links.test2), &info, &len);
if (!ASSERT_OK(err, "bpf_link_get_info_by_fd"))
goto cleanup;
ASSERT_EQ(info.type, BPF_LINK_TYPE_PERF_EVENT, "info.type");
ASSERT_EQ(info.perf_event.type, BPF_PERF_EVENT_KPROBE, "info.perf_event.type");
ASSERT_EQ(info.perf_event.kprobe.missed, 1, "info.perf_event.kprobe.missed");
cleanup:
missed_kprobe__destroy(skel);
}
static __u64 get_missed_count(int fd)
{
struct bpf_prog_info info = {};
__u32 len = sizeof(info);
int err;
err = bpf_prog_get_info_by_fd(fd, &info, &len);
if (!ASSERT_OK(err, "bpf_prog_get_info_by_fd"))
return (__u64) -1;
return info.recursion_misses;
}
/*
* Putting kprobe.multi on bpf_fentry_test1 that calls bpf_kfunc_common_test
* kfunc which has 3 perf event kprobes and 1 kprobe.multi attached.
*
* Because fprobe (kprobe.multi attach layear) does not have strict recursion
* check the kprobe's bpf_prog_active check is hit for test2-5.
*/
static void test_missed_kprobe_recursion(void)
{
LIBBPF_OPTS(bpf_test_run_opts, topts);
struct missed_kprobe_recursion *skel;
int err, prog_fd;
skel = missed_kprobe_recursion__open_and_load();
if (!ASSERT_OK_PTR(skel, "missed_kprobe_recursion__open_and_load"))
goto cleanup;
err = missed_kprobe_recursion__attach(skel);
if (!ASSERT_OK(err, "missed_kprobe_recursion__attach"))
goto cleanup;
prog_fd = bpf_program__fd(skel->progs.trigger);
err = bpf_prog_test_run_opts(prog_fd, &topts);
ASSERT_OK(err, "test_run");
ASSERT_EQ(topts.retval, 0, "test_run");
ASSERT_EQ(get_missed_count(bpf_program__fd(skel->progs.test1)), 0, "test1_recursion_misses");
ASSERT_EQ(get_missed_count(bpf_program__fd(skel->progs.test2)), 1, "test2_recursion_misses");
ASSERT_EQ(get_missed_count(bpf_program__fd(skel->progs.test3)), 1, "test3_recursion_misses");
ASSERT_EQ(get_missed_count(bpf_program__fd(skel->progs.test4)), 1, "test4_recursion_misses");
ASSERT_EQ(get_missed_count(bpf_program__fd(skel->progs.test5)), 1, "test5_recursion_misses");
cleanup:
missed_kprobe_recursion__destroy(skel);
}
/*
* Putting kprobe on bpf_fentry_test1 that calls bpf_printk and invokes
* bpf_trace_printk tracepoint. The bpf_trace_printk tracepoint has test[234]
* programs attached to it.
*
* Because kprobe execution goes through bpf_prog_active check, programs
* attached to the tracepoint will fail the recursion check and increment
* the recursion_misses stats.
*/
static void test_missed_tp_recursion(void)
{
LIBBPF_OPTS(bpf_test_run_opts, topts);
struct missed_tp_recursion *skel;
int err, prog_fd;
skel = missed_tp_recursion__open_and_load();
if (!ASSERT_OK_PTR(skel, "missed_tp_recursion__open_and_load"))
goto cleanup;
err = missed_tp_recursion__attach(skel);
if (!ASSERT_OK(err, "missed_tp_recursion__attach"))
goto cleanup;
prog_fd = bpf_program__fd(skel->progs.trigger);
err = bpf_prog_test_run_opts(prog_fd, &topts);
ASSERT_OK(err, "test_run");
ASSERT_EQ(topts.retval, 0, "test_run");
ASSERT_EQ(get_missed_count(bpf_program__fd(skel->progs.test1)), 0, "test1_recursion_misses");
ASSERT_EQ(get_missed_count(bpf_program__fd(skel->progs.test2)), 1, "test2_recursion_misses");
ASSERT_EQ(get_missed_count(bpf_program__fd(skel->progs.test3)), 1, "test3_recursion_misses");
ASSERT_EQ(get_missed_count(bpf_program__fd(skel->progs.test4)), 1, "test4_recursion_misses");
cleanup:
missed_tp_recursion__destroy(skel);
}
void test_missed(void)
{
if (test__start_subtest("perf_kprobe"))
test_missed_perf_kprobe();
if (test__start_subtest("kprobe_recursion"))
test_missed_kprobe_recursion();
if (test__start_subtest("tp_recursion"))
test_missed_tp_recursion();
}
// SPDX-License-Identifier: GPL-2.0
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include "../bpf_testmod/bpf_testmod_kfunc.h"
char _license[] SEC("license") = "GPL";
/*
* No tests in here, just to trigger 'bpf_fentry_test*'
* through tracing test_run
*/
SEC("fentry/bpf_modify_return_test")
int BPF_PROG(trigger)
{
return 0;
}
SEC("kprobe/bpf_fentry_test1")
int test1(struct pt_regs *ctx)
{
bpf_kfunc_common_test();
return 0;
}
SEC("kprobe/bpf_kfunc_common_test")
int test2(struct pt_regs *ctx)
{
return 0;
}
// SPDX-License-Identifier: GPL-2.0
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include "../bpf_testmod/bpf_testmod_kfunc.h"
char _license[] SEC("license") = "GPL";
/*
* No tests in here, just to trigger 'bpf_fentry_test*'
* through tracing test_run
*/
SEC("fentry/bpf_modify_return_test")
int BPF_PROG(trigger)
{
return 0;
}
SEC("kprobe.multi/bpf_fentry_test1")
int test1(struct pt_regs *ctx)
{
bpf_kfunc_common_test();
return 0;
}
SEC("kprobe/bpf_kfunc_common_test")
int test2(struct pt_regs *ctx)
{
return 0;
}
SEC("kprobe/bpf_kfunc_common_test")
int test3(struct pt_regs *ctx)
{
return 0;
}
SEC("kprobe/bpf_kfunc_common_test")
int test4(struct pt_regs *ctx)
{
return 0;
}
SEC("kprobe.multi/bpf_kfunc_common_test")
int test5(struct pt_regs *ctx)
{
return 0;
}
// SPDX-License-Identifier: GPL-2.0
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
char _license[] SEC("license") = "GPL";
/*
* No tests in here, just to trigger 'bpf_fentry_test*'
* through tracing test_run
*/
SEC("fentry/bpf_modify_return_test")
int BPF_PROG(trigger)
{
return 0;
}
SEC("kprobe/bpf_fentry_test1")
int test1(struct pt_regs *ctx)
{
bpf_printk("test");
return 0;
}
SEC("tp/bpf_trace/bpf_trace_printk")
int test2(struct pt_regs *ctx)
{
return 0;
}
SEC("tp/bpf_trace/bpf_trace_printk")
int test3(struct pt_regs *ctx)
{
return 0;
}
SEC("tp/bpf_trace/bpf_trace_printk")
int test4(struct pt_regs *ctx)
{
return 0;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment