bpf: add bpf_get_stack_helper
Subsystems (9)
cover bpf: add bpf_get_stack_helper
Currently, stackmap and bpf_get_stackid helper are provided for bpf program to get the stack trace. This approach has a limitation though. If two stack traces have the same hash, only one will get stored in the stackmap table,
so some stack traces are missing from user perspective.
This patch implements a new helper, bpf_get_stack, will send stack traces directly to bpf program. The bpf program is able to see all stack traces, and then can do in-kernel processing or send stack traces to user space through shared map or bpf_perf_event_output.
Patches #1 and #2 implemented the core kernel support. Patch #3 synced the new helper to tools headers. Patches #4 and #5 added a test in samples/bpf by attaching to a kprobe, and Patch #6 added a test in tools/bpf by attaching to a tracepoint.
Yonghong Song (6):
bpf: change prototype for stack_map_get_build_id_offset
bpf: add bpf_get_stack helper
tools/bpf: add bpf_get_stack helper to tools headers samples/bpf: move common-purpose perf_event functions to bpf_load.c samples/bpf: add a test for bpf_get_stack helper tools/bpf: add a test case for bpf_get_stack helper
include/linux/bpf.h | 1 + include/linux/filter.h | 3 +- include/uapi/linux/bpf.h | 17 ++- kernel/bpf/stackmap.c | 69 ++++++++-- kernel/bpf/syscall.c | 12 +- kernel/bpf/verifier.c | 3 + kernel/trace/bpf_trace.c | 50 +++++++- samples/bpf/Makefile | 4 + samples/bpf/bpf_load.c | 104 +++++++++++++++ samples/bpf/bpf_load.h | 5 + samples/bpf/trace_get_stack_kern.c | 80 ++++++++++++ samples/bpf/trace_get_stack_user.c | 150 ++++++++++++++++++++++ samples/bpf/trace_output_user.c | 113 ++-------------- tools/include/uapi/linux/bpf.h | 17 ++- tools/testing/selftests/bpf/bpf_helpers.h | 2 + tools/testing/selftests/bpf/test_progs.c | 41 +++++- tools/testing/selftests/bpf/test_stacktrace_map.c | 20 ++- 17 files changed, 568 insertions(+), 123 deletions(-) create mode 100644 samples/bpf/trace_get_stack_kern.c create mode 100644 samples/bpf/trace_get_stack_user.c
Signature · 1 line
2.9.5
RFC PATCH 1/6 bpf: change prototype for stack_map_get_build_id_offset
This patch didn't incur functionality change. The function prototype got changed so that the same function can be reused later.
--- kernel/bpf/stackmap.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-)
kernel/bpf/stackmap.c +5 -8
@@ -262,16 +262,11 @@ static int stack_map_get_build_id(struct vm_area_struct *vma,return ret; } -static void stack_map_get_build_id_offset (struct bpf_map *map, -struct stack_map_bucket *bucket, +static void stack_map_get_build_id_offset (struct bpf_stack_build_id *id_offs,u64 *ips,u32 trace_nr,bool user) {int i;struct vm_area_struct *vma; -struct bpf_stack_build_id *id_offs; - - bucket->nr = trace_nr; - id_offs = (struct bpf_stack_build_id *)bucket->data;/* * We cannot do up_read() in nmi context, so build_id lookup is
@@ -361,8 +356,10 @@ BPF_CALL_3(bpf_get_stackid, struct pt_regs *, regs, struct bpf_map *, map,pcpu_freelist_pop (&smap->freelist);if (unlikely (!new_bucket))return -ENOMEM ; -stack_map_get_build_id_offset (map, new_bucket, ips, - trace_nr, user); + new_bucket->nr = trace_nr; +stack_map_get_build_id_offset ( + (struct bpf_stack_build_id *)new_bucket->data, + ips, trace_nr, user); trace_len = trace_nr *sizeof (struct bpf_stack_build_id );if (hash_matches && bucket->nr == trace_nr &&memcmp (bucket->data, new_bucket->data, trace_len) ==0 ) {
Signature · 1 line
2.9.5
RFC PATCH 2/6 bpf: add bpf_get_stack helper
In reply to Yonghong SongCurrently, stackmap and bpf_get_stackid helper are provided for bpf program to get the stack trace. This approach has a limitation though. If two stack traces have the same hash, only one will get stored in the stackmap table,
so some stack traces are missing from user perspective.
This patch implements a new helper, bpf_get_stack, will send stack traces directly to bpf program. The bpf program is able to see all stack traces, and then can do in-kernel processing or send stack traces to user space through shared map or bpf_perf_event_output.
--- include/linux/bpf.h | 1 + include/linux/filter.h | 3 ++- include/uapi/linux/bpf.h | 17 +++++++++++++-- kernel/bpf/stackmap.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ kernel/bpf/syscall.c | 12 ++++++++++- kernel/bpf/verifier.c | 3 +++ kernel/trace/bpf_trace.c | 50 +++++++++++++++++++++++++++++++++++++++++- 7 files changed, 137 insertions(+), 5 deletions(-)
include/linux/bpf.h +1 -0
@@ -676,6 +676,7 @@ extern const struct bpf_func_proto bpf_get_current_comm_proto;extern const struct bpf_func_proto bpf_skb_vlan_push_proto;extern const struct bpf_func_proto bpf_skb_vlan_pop_proto;extern const struct bpf_func_proto bpf_get_stackid_proto; +extern const struct bpf_func_proto bpf_get_stack_proto;extern const struct bpf_func_proto bpf_sock_map_update_proto;/* Shared helpers among cBPF and eBPF. */
include/linux/filter.h +2 -1
@@ -467,7 +467,8 @@ struct bpf_prog { dst_needed:1, /* Do we need dst entry? */ blinded:1, /* Was blinded */ is_func:1, /* program is a bpf function */ - kprobe_override:1; /* Do we override a kprobe? */ + kprobe_override:1, /* Do we override a kprobe? */ + need_callchain_buf:1; /* Needs callchain buffer? */enum bpf_prog_type type;/* Type of BPF program */ enum bpf_attach_type expected_attach_type;/* For some prog types */ u32 len;/* Number of filter blocks */
include/uapi/linux/bpf.h +15 -2
@@ -517,6 +517,17 @@ union bpf_attr {* other bits - reserved * Return: >= 0 stackid on success or negative error * +* int bpf_get_stack(ctx, buf, size, flags) +* walk user or kernel stack and store the ips in buf +* @ctx: struct pt_regs* +* @buf: user buffer to fill stack +* @size: the buf size +* @flags: bits 0-7 - numer of stack frames to skip +* bit 8 - collect user stack instead of kernel +* bit 11 - get build-id as well if user stack +* other bits - reserved +* Return: >= 0 size copied on success or negative error +* * s64 bpf_csum_diff(from, from_size, to, to_size, seed) * calculate csum diff * @from: raw from buffer
@@ -821,7 +832,8 @@ union bpf_attr {FN (msg_apply_bytes), \FN (msg_cork_bytes), \FN (msg_pull_data), \ -FN (bind), +FN (bind), \ +FN (get_stack),/* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call
@@ -855,11 +867,12 @@ enum bpf_func_id {/* BPF_FUNC_skb_set_tunnel_key and BPF_FUNC_skb_get_tunnel_key flags. */ #define BPF_F_TUNINFO_IPV6 (1ULL << 0) -/* BPF_FUNC_get_stackid flags. */ +/* BPF_FUNC_get_stackid and BPF_FUNC_get_stack flags. */ #define BPF_F_SKIP_FIELD_MASK 0xffULL#define BPF_F_USER_STACK (1ULL << 8)#define BPF_F_FAST_STACK_CMP (1ULL << 9)#define BPF_F_REUSE_STACKID (1ULL << 10) +#define BPF_F_USER_BUILD_ID (1ULL << 11)/* BPF_FUNC_skb_set_tunnel_key flags. */ #define BPF_F_ZERO_CSUM_TX (1ULL << 1)
kernel/bpf/stackmap.c +56 -0
62 lines not on this page · raw message
kernel/bpf/syscall.c +11 -1
@@ -984,10 +984,13 @@ void bpf_prog_free_id(struct bpf_prog *prog, bool do_idr_lock)static void __bpf_prog_put_rcu (struct rcu_head *rcu) { struct bpf_prog_aux *aux = container_of(rcu, struct bpf_prog_aux, rcu); +bool need_callchain_buf = aux->prog->need_callchain_buf;free_used_maps (aux);bpf_prog_uncharge_memlock (aux->prog);security_bpf_prog_free (aux); +if (need_callchain_buf) +put_callchain_buffers ();bpf_prog_free (aux->prog); }
@@ -1004,7 +1007,8 @@ static void __bpf_prog_put(struct bpf_prog *prog, bool do_idr_lock)bpf_prog_kallsyms_del (prog->aux->func[i]);bpf_prog_kallsyms_del (prog); -call_rcu (&prog->aux->rcu, __bpf_prog_put_rcu); +synchronize_rcu (); +__bpf_prog_put_rcu (&prog->aux->rcu); } }
@@ -1341,6 +1345,12 @@ static int bpf_prog_load(union bpf_attr *attr)if (err) goto free_used_maps; +if (prog->need_callchain_buf) { + err =get_callchain_buffers (sysctl_perf_event_max_stack); +if (err) + goto free_used_maps; + } + err =bpf_prog_new_fd (prog);if (err <0 ) {/* failed to allocate fd.
kernel/bpf/verifier.c +3 -0
@@ -2460,6 +2460,9 @@ static int check_helper_call(struct bpf_verifier_env *env, int func_id, int insnif (err)return err; +if (func_id == BPF_FUNC_get_stack) + env->prog->need_callchain_buf = true; +if (changes_data)clear_all_pkt_pointers (env);return 0 ;
kernel/trace/bpf_trace.c +49 -1
98 lines not on this page · raw message
Signature · 1 line
2.9.5
Re: [RFC PATCH bpf-next 2/6] bpf: add bpf_get_stack helper
Quoted 82 lines · Yonghong Song
Currently, stackmap and
bpf_get_stackidhelper are provided for bpf program to get the stack trace. This approach has a limitation though. If two stack traces have the same hash, only one will get stored in the stackmap table,
so some stack traces are missing from user perspective.This patch implements a new helper,
bpf_get_stack, will send stack traces directly to bpf program. The bpf program is able to see all stack traces, and then can do in-kernel processing or send stack traces to user space through shared map orbpf_perf_event_output.Signed-off-by Yonghong Song <[email protected]>--- include/linux/bpf.h | 1 + include/linux/filter.h | 3 ++- include/uapi/linux/bpf.h | 17 +++++++++++++-- kernel/bpf/stackmap.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ kernel/bpf/syscall.c | 12 ++++++++++- kernel/bpf/verifier.c | 3 +++ kernel/trace/bpf_trace.c | 50 +++++++++++++++++++++++++++++++++++++++++- 7 files changed, 137 insertions(+), 5 deletions(-)include/linux/bpf.h +1
-0@@ -676,6 +676,7 @@ extern const struct bpf_func_proto bpf_get_current_comm_proto;extern const struct bpf_func_proto bpf_skb_vlan_push_proto;extern const struct bpf_func_proto bpf_skb_vlan_pop_proto;extern const struct bpf_func_proto bpf_get_stackid_proto; +extern const struct bpf_func_proto bpf_get_stack_proto;extern const struct bpf_func_proto bpf_sock_map_update_proto;/* Shared helpers among cBPF and eBPF. */ include/linux/filter.h +2
-1@@ -467,7 +467,8 @@ struct bpf_prog { dst_needed:1, /* Do we need dst entry? */ blinded:1, /* Was blinded */ is_func:1, /* program is a bpf function */ - kprobe_override:1; /* Do we override a kprobe? */ + kprobe_override:1, /* Do we override a kprobe? */ + need_callchain_buf:1; /* Needs callchain buffer? */enum bpf_prog_type type;/* Type of BPF program */ enum bpf_attach_type expected_attach_type;/* For some prog types */ u32 len;/* Number of filter blocks */ include/uapi/linux/bpf.h +13
-1@@ -517,6 +517,17 @@ union bpf_attr {* other bits - reserved * Return: >= 0 stackid on success or negative error * +* int bpf_get_stack(ctx, buf, size, flags) +* walk user or kernel stack and store the ips in buf +* @ctx: struct pt_regs* +* @buf: user buffer to fill stack +* @size: the buf size +* @flags: bits 0-7 - numer of stack frames to skip +* bit 8 - collect user stack instead of kernel +* bit 11 - get build-id as well if user stack +* other bits - reserved +* Return: >= 0 size copied on success or negative error +* * s64 bpf_csum_diff(from, from_size, to, to_size, seed) * calculate csum diff * @from: raw from buffer @@ -821,7 +832,8 @@ union bpf_attr {FN (msg_apply_bytes), \FN (msg_cork_bytes), \FN (msg_pull_data), \ -FN (bind), +FN (bind), \ +FN (get_stack),/* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call
include/uapi/linux/bpf.h enum bpf_func_id { · line 875
Quoted 10 lines
include/uapi/linux/bpf.h +2
-1@@ -855,11 +867,12 @@ enum bpf_func_id {/* BPF_FUNC_skb_set_tunnel_key and BPF_FUNC_skb_get_tunnel_key flags. */ #define BPF_F_TUNINFO_IPV6 (1ULL << 0) -/* BPF_FUNC_get_stackid flags. */ +/* BPF_FUNC_get_stackid and BPF_FUNC_get_stack flags. */ #define BPF_F_SKIP_FIELD_MASK 0xffULL#define BPF_F_USER_STACK (1ULL << 8)#define BPF_F_FAST_STACK_CMP (1ULL << 9)#define BPF_F_REUSE_STACKID (1ULL << 10) +#define BPF_F_USER_BUILD_ID (1ULL << 11)
the comment above is not quite correct. This new flag is only available for new helper.
Quoted 24 lines
include/uapi/linux/bpf.h +0
-0/* BPF_FUNC_skb_set_tunnel_key flags. */ #define BPF_F_ZERO_CSUM_TX (1ULL << 1)kernel/bpf/stackmap.c +13
-0@@ -402,6 +402,62 @@ const struct bpf_func_proto bpf_get_stackid_proto = { .arg3_type = ARG_ANYTHING, }; +BPF_CALL_4(bpf_get_stack, struct pt_regs *, regs, void *, buf, u32, size, + u64, flags) +{ +u32 init_nr, trace_nr, copy_len, elem_size, num_elem; +bool user_build_id = flags &BPF_F_USER_BUILD_ID ; +u32 skip = flags &BPF_F_SKIP_FIELD_MASK ; +bool user = flags &BPF_F_USER_STACK ; +struct perf_callchain_entry *trace; +bool kernel = !user; +u64 *ips; + +if (unlikely (flags & ~(BPF_F_SKIP_FIELD_MASK |BPF_F_USER_STACK | +BPF_F_USER_BUILD_ID )))
kernel/bpf/stackmap.c const struct bpf_func_proto bpf_get_stackid_proto = { · line 427
Quoted 10 lines
kernel/bpf/stackmap.c +10
-0+return -EINVAL ; + + elem_size = (user && user_build_id) ?sizeof (struct bpf_stack_build_id ) + :sizeof (u64); +if (unlikely (size % elem_size)) +return -EINVAL ; + + num_elem = size / elem_size; +if (sysctl_perf_event_max_stack < num_elem) + init_nr =0 ;
prog's buffer should be zero padded in this case since it points to uninit_mem.
Quoted 6 lines
kernel/bpf/stackmap.c +6
-0+else + init_nr = sysctl_perf_event_max_stack - num_elem; + trace =get_perf_callchain (regs, init_nr, kernel, user, + sysctl_perf_event_max_stack, false, false); +if (unlikely (!trace)) +return -EFAULT ;
kernel/bpf/stackmap.c const struct bpf_func_proto bpf_get_stackid_proto = { · line 443
Quoted 10 lines
kernel/bpf/stackmap.c +10
-0+ + trace_nr = trace->nr - init_nr; +if (trace_nr <= skip) +return -EFAULT ; + + trace_nr -= skip; + trace_nr = (trace_nr <= num_elem) ? trace_nr : num_elem; + copy_len = trace_nr * elem_size; + ips = trace->ip + skip + init_nr; +if (user && user_build_id)
the combination of kern + user_build_id should probably be rejected earlier with einval.
Quoted 4 lines
kernel/bpf/stackmap.c +4
-0+stack_map_get_build_id_offset (buf, ips, trace_nr, user); + else +memcpy (buf ,ips ,copy_len ); +
kernel/bpf/stackmap.c const struct bpf_func_proto bpf_get_stackid_proto = { · line 457
Quoted 10 lines
kernel/bpf/stackmap.c +10
-0+return copy_len; +} + +const struct bpf_func_proto bpf_get_stack_proto = { + .func = bpf_get_stack, + .gpl_only = true, + .ret_type =RET_INTEGER , + .arg1_type =ARG_PTR_TO_CTX , + .arg2_type =ARG_PTR_TO_UNINIT_MEM , + .arg3_type =ARG_CONST_SIZE_OR_ZERO ,
why allow zero size?
I'm not sure the helper will work correctly when size=0
Quoted 25 lines
kernel/bpf/stackmap.c +3
-0+ .arg4_type = ARG_ANYTHING, +}; +/* Called from eBPF program */ static void *stack_map_lookup_elem (struct bpf_map *map,void *key) {kernel/bpf/syscall.c +3
-0@@ -984,10 +984,13 @@ void bpf_prog_free_id(struct bpf_prog *prog, bool do_idr_lock)static void __bpf_prog_put_rcu (struct rcu_head *rcu) { struct bpf_prog_aux *aux = container_of(rcu, struct bpf_prog_aux, rcu); +bool need_callchain_buf = aux->prog->need_callchain_buf;free_used_maps (aux);bpf_prog_uncharge_memlock (aux->prog);security_bpf_prog_free (aux); +if (need_callchain_buf) +put_callchain_buffers ();bpf_prog_free (aux->prog); }
kernel/bpf/syscall.c static void __bpf_prog_put(struct bpf_prog *prog, bool do_idr_lock) · line 1011
Quoted 6 lines
kernel/bpf/syscall.c +2
-1@@ -1004,7 +1007,8 @@ static void __bpf_prog_put(struct bpf_prog *prog, bool do_idr_lock)bpf_prog_kallsyms_del (prog->aux->func[i]);bpf_prog_kallsyms_del (prog); -call_rcu (&prog->aux->rcu, __bpf_prog_put_rcu); +synchronize_rcu (); +__bpf_prog_put_rcu (&prog->aux->rcu);
there should have been lockdep splat.
We cannot call synchronize_rcu here, since we cannot sleep in some cases.
Re: [RFC PATCH bpf-next 2/6] bpf: add bpf_get_stack helper
Quoted 88 lines · Alexei Starovoitov
Quoted 87 lines · Yonghong Song
Currently, stackmap and
bpf_get_stackidhelper are provided for bpf program to get the stack trace. This approach has a limitation though. If two stack traces have the same hash, only one will get stored in the stackmap table,
so some stack traces are missing from user perspective.This patch implements a new helper,
bpf_get_stack, will send stack traces directly to bpf program. The bpf program is able to see all stack traces, and then can do in-kernel processing or send stack traces to user space through shared map orbpf_perf_event_output.Signed-off-by Yonghong Song <[email protected]>---
include/linux/bpf.h | 1 +
include/linux/filter.h | 3 ++-
include/uapi/linux/bpf.h | 17 +++++++++++++-- kernel/bpf/stackmap.c | 56
++++++++++++++++++++++++++++++++++++++++++++++++
kernel/bpf/syscall.c | 12 ++++++++++- kernel/bpf/verifier.c | 3 +++
kernel/trace/bpf_trace.c | 50 +++++++++++++++++++++++++++++++++++++++++-7 files changed, 137 insertions(+), 5 deletions(-)include/linux/bpf.h +1
-0@@ -676,6 +676,7 @@ extern const struct bpf_func_proto bpf_get_current_comm_proto; extern const struct bpf_func_proto bpf_skb_vlan_push_proto; extern const struct bpf_func_proto bpf_skb_vlan_pop_proto; extern const struct bpf_func_proto bpf_get_stackid_proto; +extern const struct bpf_func_proto bpf_get_stack_proto; extern const struct bpf_func_proto bpf_sock_map_update_proto;/* Shared helpers among cBPF and eBPF. */
include/linux/filter.h +0
-0@@ -467,7 +467,8 @@ struct bpf_prog {dst_needed:1, /* Do we need dst entry? */ blinded:1, /* Was blinded */
is_func:1, /* program is a bpf function */
- kprobe_override:1; /* Do we override a kprobe? */ + kprobe_override:1, /* Do we override a kprobe? */ + need_callchain_buf:1; /* Needs callchain buffer? */ enum bpf_prog_type type; /* Type of BPF program */ enum bpf_attach_typeexpected_attach_type; /* For some prog types */ u32 len; /* Number of filter blocks */include/uapi/linux/bpf.h +13
-1@@ -517,6 +517,17 @@ union bpf_attr { * other bits - reserved * Return: >= 0 stackid on success or negative error * + * int bpf_get_stack(ctx, buf, size, flags) + * walk user or kernel stack and store the ips in buf + * @ctx: struct pt_regs* + * @buf: user buffer to fill stack + * @size: the buf size + * @flags: bits 0-7 - numer of stack frames to skip + * bit 8 - collect user stack instead of kernel + * bit 11 - get build-id as well if user stack + * other bits - reserved + * Return: >= 0 size copied on success or negative error + * * s64 bpf_csum_diff(from, from_size, to, to_size, seed) * calculate csum diff * @from: raw from buffer@@ -821,7 +832,8 @@ union bpf_attr { FN(msg_apply_bytes), \ FN(msg_cork_bytes), \ FN(msg_pull_data), \ - FN(bind), + FN(bind), \ + FN(get_stack), /* integer value in 'imm' field of BPF_CALL instruction selects which helper* function eBPF program intends to call @@ -855,11 +867,12 @@ enum bpf_func_id { /* BPF_FUNC_skb_set_tunnel_key and BPF_FUNC_skb_get_tunnel_key flags. */ #define BPF_F_TUNINFO_IPV6 (1ULL << 0)
Quoted 10 lines
include/uapi/linux/bpf.h enum bpf_func_id { · line 875
Quoted 7 lines
include/uapi/linux/bpf.h +2
-1-/* BPF_FUNC_get_stackid flags. */ +/* BPF_FUNC_get_stackid and BPF_FUNC_get_stack flags. */ #define BPF_F_SKIP_FIELD_MASK 0xffULL #define BPF_F_USER_STACK (1ULL << 8) #define BPF_F_FAST_STACK_CMP (1ULL << 9) #define BPF_F_REUSE_STACKID (1ULL << 10) +#define BPF_F_USER_BUILD_ID (1ULL << 11)the comment above is not quite correct. This new flag is only available for new helper.
Right, some flags are used for both helpers and some are only used for one of them. Will make it clear in the next revision.
Quoted 33 lines
Quoted 33 lines
include/uapi/linux/bpf.h +0
-0/* BPF_FUNC_skb_set_tunnel_key flags. */ #define BPF_F_ZERO_CSUM_TX (1ULL << 1)kernel/bpf/stackmap.c +18
-0@@ -402,6 +402,62 @@ const struct bpf_func_proto bpf_get_stackid_proto = { .arg3_type = ARG_ANYTHING, }; +BPF_CALL_4(bpf_get_stack, struct pt_regs *, regs, void *, buf, u32, size, + u64, flags) +{ + u32 init_nr, trace_nr, copy_len, elem_size, num_elem; + bool user_build_id = flags & BPF_F_USER_BUILD_ID; + u32 skip = flags & BPF_F_SKIP_FIELD_MASK; + bool user = flags & BPF_F_USER_STACK; + struct perf_callchain_entry *trace; + bool kernel = !user; + u64 *ips; + + if (unlikely(flags & ~(BPF_F_SKIP_FIELD_MASK | BPF_F_USER_STACK | + BPF_F_USER_BUILD_ID))) + return -EINVAL; + + elem_size = (user && user_build_id) ? sizeof(structbpf_stack_build_id ) + : sizeof(u64); + if (unlikely(size % elem_size))
Quoted 8 lines
kernel/bpf/stackmap.c const struct bpf_func_proto bpf_get_stackid_proto = { · line 427
Quoted 5 lines
kernel/bpf/stackmap.c +5
-0+ return -EINVAL; + + num_elem = size / elem_size; + if (sysctl_perf_event_max_stack < num_elem) + init_nr = 0;prog's buffer should be zero padded in this case since it points to
uninit_mem.
Will make the change in the next revision.
Quoted 12 lines
Quoted 12 lines
kernel/bpf/stackmap.c +11
-0+ else + init_nr = sysctl_perf_event_max_stack - num_elem; + trace = get_perf_callchain(regs, init_nr, kernel, user, + sysctl_perf_event_max_stack, false, false); + if (unlikely(!trace)) + return -EFAULT; + + trace_nr = trace->nr - init_nr; + if (trace_nr <= skip) + return -EFAULT; +
Quoted 8 lines
kernel/bpf/stackmap.c const struct bpf_func_proto bpf_get_stackid_proto = { · line 443
Quoted 5 lines
kernel/bpf/stackmap.c +5
-0+ trace_nr -= skip; + trace_nr = (trace_nr <= num_elem) ? trace_nr : num_elem; + copy_len = trace_nr * elem_size; + ips = trace->ip + skip + init_nr; + if (user && user_build_id)the combination of kern +
user_build_idshould probably be rejected earlier with einval.
Right, I missed this case. It should be rejected.
Quoted 10 lines
Quoted 10 lines
kernel/bpf/stackmap.c +9
-0+ stack_map_get_build_id_offset(buf, ips, trace_nr, user); + else + memcpy(buf, ips, copy_len); + + return copy_len; +} + +const struct bpf_func_proto bpf_get_stack_proto = { + .func = bpf_get_stack,
Quoted 8 lines
kernel/bpf/stackmap.c const struct bpf_func_proto bpf_get_stackid_proto = { · line 457
Quoted 5 lines
kernel/bpf/stackmap.c +5
-0+ .gpl_only = true, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_PTR_TO_UNINIT_MEM, + .arg3_type = ARG_CONST_SIZE_OR_ZERO,why allow zero size?
I'm not sure the helper will work correctly when size=0
The only reason I had is to make bpf program easier so
they do not need to test zero size, similiar to bpf_probe_read/bpf_perf_event_output.
I have double checked my implementation and it should handle zero size properly.
Let me double check whether whether not allowing zero can still have reasonable bpf program coding which passes verifier.
Quoted 31 lines
Quoted 31 lines
kernel/bpf/stackmap.c +3
-0+ .arg4_type = ARG_ANYTHING, +}; +/* Called from eBPF program */
static void *stack_map_lookup_elem(structbpf_map*map, void *key) {kernel/bpf/syscall.c +3
-0@@ -984,10 +984,13 @@ void bpf_prog_free_id(struct bpf_prog *prog, bool do_idr_lock) static void __bpf_prog_put_rcu(struct rcu_head *rcu) { struct bpf_prog_aux *aux = container_of(rcu, struct bpf_prog_aux, rcu); + bool need_callchain_buf = aux->prog->need_callchain_buf; free_used_maps(aux); bpf_prog_uncharge_memlock(aux->prog); security_bpf_prog_free(aux); + if (need_callchain_buf) + put_callchain_buffers(); bpf_prog_free(aux->prog); }@@ -1004,7 +1007,8 @@ static void __bpf_prog_put(struct bpf_prog *prog, bool do_idr_lock) bpf_prog_kallsyms_del(prog->aux->func[i]); bpf_prog_kallsyms_del(prog);
Quoted 7 lines
kernel/bpf/syscall.c static void __bpf_prog_put(struct bpf_prog *prog, bool do_idr_lock) · line 1011
Quoted 3 lines
kernel/bpf/syscall.c +2
-1- call_rcu(&prog->aux->rcu, __bpf_prog_put_rcu); + synchronize_rcu(); + __bpf_prog_put_rcu(&prog->aux->rcu);there should have been lockdep splat.
We cannot callsynchronize_rcuhere, since we cannot sleep in some cases.
Let me double check this. The following is the reason why I am using synchronize_rcu().
With call_rcu(&prog->aux->rcu, __bpf_prog_put_rcu) and _bpf_prog_put_rcu calls put_callchain_buffers() which calls mutex_lock(), the runtime with CONFIG_DEBUG_ATOMIC_SLEEP=y will complains since potential sleep inside the call_rcu is not allowed.
Re: [RFC PATCH bpf-next 2/6] bpf: add bpf_get_stack helper
Quoted 13 lines · Yonghong Song
Quoted 5 lines
Quoted 5 lines
kernel/bpf/syscall.c +0
-0@@ -1004,7 +1007,8 @@ static void __bpf_prog_put(struct bpf_prog *prog, bool do_idr_lock)bpf_prog_kallsyms_del (prog->aux->func[i]);bpf_prog_kallsyms_del (prog);Quoted 7 lines
kernel/bpf/syscall.c static void __bpf_prog_put(struct bpf_prog *prog, bool do_idr_lock) · line 1011
Quoted 3 lines
kernel/bpf/syscall.c +2
-1-call_rcu (&prog->aux->rcu, __bpf_prog_put_rcu); +synchronize_rcu (); +__bpf_prog_put_rcu (&prog->aux->rcu);there should have been lockdep splat.
We cannot callsynchronize_rcuhere, since we cannot sleep in some cases.
Quoted 8 lines
Let me double check this. The following is the reason why I am using
synchronize_rcu().With
call_rcu(&prog->aux->rcu, __bpf_prog_put_rcu)and_bpf_prog_put_rcucallsput_callchain_buffers()which callsmutex_lock(), the runtime withCONFIG_DEBUG_ATOMIC_SLEEP=ywill complains since potential sleep inside thecall_rcuis not allowed.
I see. Indeed. We cannot call put_callchain_buffers() from rcu callback, but doing synchronize_rcu() here is also not possible.
How about moving put_callchain into bpf_prog_free_deferred() ?
Daniel Borkmann
maintainerRe: [RFC PATCH bpf-next 2/6] bpf: add bpf_get_stack helper
Quoted 23 lines · Alexei Starovoitov
Quoted 13 lines · Yonghong Song
Quoted 5 lines
Quoted 5 lines
kernel/bpf/syscall.c +0
-0@@ -1004,7 +1007,8 @@ static void __bpf_prog_put(struct bpf_prog *prog, bool do_idr_lock) bpf_prog_kallsyms_del(prog->aux->func[i]); bpf_prog_kallsyms_del(prog);Quoted 7 lines
kernel/bpf/syscall.c static void __bpf_prog_put(struct bpf_prog *prog, bool do_idr_lock) · line 1011
Quoted 3 lines
kernel/bpf/syscall.c +2
-1- call_rcu(&prog->aux->rcu, __bpf_prog_put_rcu); + synchronize_rcu(); + __bpf_prog_put_rcu(&prog->aux->rcu);there should have been lockdep splat.
We cannot callsynchronize_rcuhere, since we cannot sleep in some cases.Quoted 8 lines
Let me double check this. The following is the reason why I am using
synchronize_rcu().With
call_rcu(&prog->aux->rcu, __bpf_prog_put_rcu)and_bpf_prog_put_rcucallsput_callchain_buffers()which callsmutex_lock(), the runtime withCONFIG_DEBUG_ATOMIC_SLEEP=ywill complains since potential sleep inside thecall_rcuis not allowed.
Quoted 3 lines
I see. Indeed. We cannot call
put_callchain_buffers()from rcu callback, but doingsynchronize_rcu()here is also not possible.
How about movingput_callchainintobpf_prog_free_deferred()?
+1, the assumption is that you can call bpf_prog_put() and also the bpf_map_put() from any context. Sleeping here for a long time might subtly break things badly.
Re: [RFC PATCH bpf-next 2/6] bpf: add bpf_get_stack helper
Quoted 24 lines · Daniel Borkmann
Quoted 23 lines · Alexei Starovoitov
Quoted 21 lines · Yonghong Song
Quoted 5 lines
Quoted 5 lines
kernel/bpf/syscall.c +0
-0@@ -1004,7 +1007,8 @@ static void __bpf_prog_put(struct bpf_prog *prog, bool do_idr_lock) bpf_prog_kallsyms_del(prog->aux->func[i]); bpf_prog_kallsyms_del(prog);Quoted 7 lines
kernel/bpf/syscall.c static void __bpf_prog_put(struct bpf_prog *prog, bool do_idr_lock) · line 1011
Quoted 3 lines
kernel/bpf/syscall.c +2
-1- call_rcu(&prog->aux->rcu, __bpf_prog_put_rcu); + synchronize_rcu(); + __bpf_prog_put_rcu(&prog->aux->rcu);there should have been lockdep splat.
We cannot callsynchronize_rcuhere, since we cannot sleep in some cases.Let me double check this. The following is the reason why I am using
synchronize_rcu().With
call_rcu(&prog->aux->rcu, __bpf_prog_put_rcu)and_bpf_prog_put_rcucallsput_callchain_buffers()which callsmutex_lock(), the runtime withCONFIG_DEBUG_ATOMIC_SLEEP=ywill complains since potential sleep inside thecall_rcuis not allowed.
Quoted 7 lines
Quoted 3 lines
I see. Indeed. We cannot call
put_callchain_buffers()from rcu callback, but doingsynchronize_rcu()here is also not possible.
How about movingput_callchainintobpf_prog_free_deferred()?+1, the assumption is that you can call
bpf_prog_put()and also thebpf_map_put()from any context. Sleeping here for a long time might subtly break things badly.
Thanks for the suggestion! This should work.
RFC PATCH 3/6 tools/bpf: add bpf_get_stack helper to tools headers
In reply to Yonghong Song--- tools/include/uapi/linux/bpf.h | 17 +++++++++++++++-- tools/testing/selftests/bpf/bpf_helpers.h | 2 ++ 2 files changed, 17 insertions(+), 2 deletions(-)
tools/include/uapi/linux/bpf.h +15 -2
@@ -517,6 +517,17 @@ union bpf_attr {* other bits - reserved * Return: >= 0 stackid on success or negative error * +* int bpf_get_stack(ctx, buf, size, flags) +* walk user or kernel stack and store the ips in buf +* @ctx: struct pt_regs* +* @buf: user buffer to fill stack +* @size: the buf size +* @flags: bits 0-7 - numer of stack frames to skip +* bit 8 - collect user stack instead of kernel +* bit 11 - get build-id as well if user stack +* other bits - reserved +* Return: >= 0 size copied on success or negative error +* * s64 bpf_csum_diff(from, from_size, to, to_size, seed) * calculate csum diff * @from: raw from buffer
@@ -821,7 +832,8 @@ union bpf_attr {FN (msg_apply_bytes), \FN (msg_cork_bytes), \FN (msg_pull_data), \ -FN (bind), +FN (bind), \ +FN (get_stack),/* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call
@@ -855,11 +867,12 @@ enum bpf_func_id {/* BPF_FUNC_skb_set_tunnel_key and BPF_FUNC_skb_get_tunnel_key flags. */ #define BPF_F_TUNINFO_IPV6 (1ULL << 0) -/* BPF_FUNC_get_stackid flags. */ +/* BPF_FUNC_get_stackid and BPF_FUNC_get_stack flags. */ #define BPF_F_SKIP_FIELD_MASK 0xffULL#define BPF_F_USER_STACK (1ULL << 8)#define BPF_F_FAST_STACK_CMP (1ULL << 9)#define BPF_F_REUSE_STACKID (1ULL << 10) +#define BPF_F_USER_BUILD_ID (1ULL << 11)/* BPF_FUNC_skb_set_tunnel_key flags. */ #define BPF_F_ZERO_CSUM_TX (1ULL << 1)
tools/testing/selftests/bpf/bpf_helpers.h +2 -0
@@ -96,6 +96,8 @@ static int (*bpf_msg_pull_data)(void *ctx, int start, int end, int flags) = (void *) BPF_FUNC_msg_pull_data;static int (*bpf_bind)(void *ctx,void *addr,int addr_len) = (void *) BPF_FUNC_bind; +static int (*bpf_get_stack)(void *ctx,void *buf,int size,int flags) = + (void *) BPF_FUNC_get_stack;/* llvm builtin functions that eBPF C program may use to * emit BPF_LD_ABS and BPF_LD_IND instructions
Signature · 1 line
2.9.5
RFC PATCH 4/6 samples/bpf: move common-purpose perf_event functions to bpf_load.c
In reply to Yonghong SongThere is no functionality change in this patch. The common-purpose perf_event functions are moved from trace_output_user.c to bpf_load.c so that these function can be reused later.
--- samples/bpf/bpf_load.c | 104 ++++++++++++++++++++++++++++++++++++ samples/bpf/bpf_load.h | 5 ++ samples/bpf/trace_output_user.c | 113 ++++------------------------------------ 3 files changed, 118 insertions(+), 104 deletions(-)
samples/bpf/bpf_load.c +104 -0
@@ -713,3 +713,107 @@ struct ksym *ksym_search(long key)return &syms[0 ]; } +static int page_size; +static int page_cnt =8 ; +static volatile struct perf_event_mmap_page *header; + +static int perf_event_mmap (int fd) +{ +void *base; +int mmap_size; + + page_size =getpagesize (); + mmap_size = page_size * (page_cnt +1 ); + + base =mmap (NULL , mmap_size,PROT_READ |PROT_WRITE ,MAP_SHARED , fd,0 ); +if (base ==MAP_FAILED ) { +printf ("mmap err\n" ); +return -1 ; + } + + header = base; +return 0 ; +} + +static int perf_event_poll (int fd) +{ +struct pollfd pfd = { .fd = fd, .events =POLLIN }; + +return poll (&pfd,1 ,1000 ); +} + +struct perf_event_sample { +struct perf_event_header header; +__u32 size; +char data[]; +}; + +static void perf_event_read (perf_event_print_fn fn) +{ +__u64 data_tail = header->data_tail; +__u64 data_head = header->data_head; +__u64 buffer_size = page_cnt * page_size; +void *base, *begin, *end; +char buf[256 ]; + + asmvolatile ("" :::"memory" );/* in real code it should be smp_rmb() */ +if (data_head == data_tail) +return ; + + base = ((char *)header) + page_size; + + begin = base + data_tail % buffer_size; + end = base + data_head % buffer_size; + +while (begin != end) { +struct perf_event_sample *e; + + e = begin; +if (begin + e->header.size > base + buffer_size) { +long len = base + buffer_size - begin; + +assert (len < e->header.size); +memcpy (buf, begin, len); +memcpy (buf + len, base, e->header.size - len); + e = (void *) buf; + begin = base + e->header.size - len; + }else if (begin + e->header.size == base + buffer_size) { + begin = base; + }else { + begin += e->header.size; + } + +if (e->header.type ==PERF_RECORD_SAMPLE ) { +fn (e->data, e->size); + }else if (e->header.type ==PERF_RECORD_LOST ) { +struct { +struct perf_event_header header; +__u64 id; +__u64 lost; + } *lost = (void *) e; +printf ("lost %lld events\n" , lost->lost); + }else { +printf ("unknown event type=%d size=%d\n" , + e->header.type, e->header.size); + } + } + +__sync_synchronize ();/* smp_mb() */ + header->data_tail = data_head; +} + +int perf_event_poller (int fd,perf_event_exec_fn exec_fn, +perf_event_print_fn output_fn) +{ +if (perf_event_mmap (fd) <0 ) +return 1 ; + +exec_fn (); + +for (;;) { +perf_event_poll (fd); +perf_event_read (output_fn); + } + +return 0 ; +}
samples/bpf/bpf_load.h +5 -0
@@ -62,4 +62,9 @@ struct ksym {int load_kallsyms (void );struct ksym *ksym_search (long key);int bpf_set_link_xdp_fd (int ifindex,int fd,__u32 flags); + +typedef void (*perf_event_exec_fn )(void ); +typedef void (*perf_event_print_fn )(void *data,int size); +int perf_event_poller (int fd,perf_event_exec_fn exec_fn, +perf_event_print_fn output_fn);#endif
samples/bpf/trace_output_user.c +9 -104
133 lines not on this page · raw message
Signature · 1 line
2.9.5
RFC PATCH 5/6 samples/bpf: add a test for bpf_get_stack helper
In reply to Yonghong SongThe test attached a kprobe program to kernel function sys_write. It tested to get stack for user space, kernel space and user space with build_id request. It also tested to get user and kernel stack into the same buffer with back-to-back bpf_get_stack helper calls.
Whenever the kernel stack is available, the user space application will check to ensure that sys_write/SyS_write is part of the stack.
--- samples/bpf/Makefile | 4 + samples/bpf/trace_get_stack_kern.c | 80 ++++++++++++++++++++ samples/bpf/trace_get_stack_user.c | 150 +++++++++++++++++++++++++++++++++++++ 3 files changed, 234 insertions(+) create mode 100644 samples/bpf/trace_get_stack_kern.c create mode 100644 samples/bpf/trace_get_stack_user.c
samples/bpf/Makefile +4 -0
@@ -44,6 +44,7 @@ hostprogs-y += xdp_monitor hostprogs-y += xdp_rxq_info hostprogs-y += syscall_tp hostprogs-y += cpustat +hostprogs-y += trace_get_stack # Libbpf dependencies LIBBPF := ../../tools/lib/bpf/bpf.o ../../tools/lib/bpf/nlattr.o
@@ -95,6 +96,7 @@ xdp_monitor-objs := bpf_load.o $(LIBBPF) xdp_monitor_user.o xdp_rxq_info-objs := bpf_load.o $(LIBBPF) xdp_rxq_info_user.o syscall_tp-objs := bpf_load.o $(LIBBPF) syscall_tp_user.o cpustat-objs := bpf_load.o $(LIBBPF) cpustat_user.o +trace_get_stack-objs := bpf_load.o $(LIBBPF) trace_get_stack_user.o # Tell kbuild to always build the programs always := $(hostprogs-y)
@@ -148,6 +150,7 @@ always += xdp_rxq_info_kern.o always += xdp2skb_meta_kern.o always += syscall_tp_kern.o always += cpustat_kern.o +always += trace_get_stack_kern.o HOSTCFLAGS += -I$(objtree)/usr/include HOSTCFLAGS += -I$(srctree)/tools/lib/
@@ -193,6 +196,7 @@ HOSTLOADLIBES_xdp_monitor += -lelf HOSTLOADLIBES_xdp_rxq_info += -lelf HOSTLOADLIBES_syscall_tp += -lelf HOSTLOADLIBES_cpustat += -lelf +HOSTLOADLIBES_trace_get_stack += -lelf # Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline: # make samples/bpf/ LLC=~/git/llvm/build/bin/llc CLANG=~/git/llvm/build/bin/clang
samples/bpf/trace_get_stack_kern.c new file +80 -0
80 lines not on this page · raw message
samples/bpf/trace_get_stack_user.c new file +150 -0
150 lines not on this page · raw message
Signature · 1 line
2.9.5
RFC PATCH 6/6 tools/bpf: add a test case for bpf_get_stack helper
In reply to Yonghong SongThe test_stacktrace_map is enhanced to call bpf_get_stack in the helper to get the stack trace as well. The stack traces from bpf_get_stack and bpf_get_stackid are compared to ensure that for the same stack as represented as the same hash, their ip addresses must be the same.
--- tools/testing/selftests/bpf/test_progs.c | 41 ++++++++++++++++++++++- tools/testing/selftests/bpf/test_stacktrace_map.c | 20 +++++++++-- 2 files changed, 57 insertions(+), 4 deletions(-)
tools/testing/selftests/bpf/test_progs.c +40 -1
@@ -865,9 +865,39 @@ static int compare_map_keys(int map1_fd, int map2_fd)return 0 ; } +static int compare_stack_ips (int smap_fd,int amap_fd) +{ +int max_len =PERF_MAX_STACK_DEPTH *sizeof (__u64); +__u32 key, next_key, *cur_key_p, *next_key_p; +char val_buf1[max_len], val_buf2[max_len]; +int i, err; + + cur_key_p =NULL ; + next_key_p = &key; +while (bpf_map_get_next_key (smap_fd, cur_key_p, next_key_p) ==0 ) { + err =bpf_map_lookup_elem (smap_fd, next_key_p, val_buf1); +if (err) +return err; + err =bpf_map_lookup_elem (amap_fd, next_key_p, val_buf2); +if (err) +return err; +for (i =0 ; i < max_len; i++) { +if (val_buf1[i] != val_buf2[i]) +return -1 ; + } + key = *next_key_p; + cur_key_p = &key; + next_key_p = &next_key; + } +if (errno !=ENOENT ) +return -1 ; + +return 0 ; +} +static void test_stacktrace_map () { -int control_map_fd, stackid_hmap_fd, stackmap_fd; +int control_map_fd, stackid_hmap_fd, stackmap_fd, stack_amap_fd;const char *file ="./test_stacktrace_map.o" ;int bytes, efd, err, pmu_fd, prog_fd;struct perf_event_attr attr = {};
@@ -925,6 +955,10 @@ static void test_stacktrace_map()if (stackmap_fd <0 ) goto disable_pmu; + stack_amap_fd =bpf_find_map (__func__, obj,"stack_amap" ); +if (stack_amap_fd <0 ) + goto disable_pmu; +/* give some time for bpf program run */ sleep (1 );
@@ -946,6 +980,11 @@ static void test_stacktrace_map() "err %d errno %d\n", err, errno)) goto disable_pmu_noerr; + err =compare_stack_ips (stackmap_fd, stack_amap_fd); +if (CHECK (err,"compare_stack_ips stackmap vs. stack_amap" , +"err %d errno %d\n" , err, errno)) + goto disable_pmu_noerr; + goto disable_pmu_noerr; disable_pmu: error_cnt++;
tools/testing/selftests/bpf/test_stacktrace_map.c +17 -3
@@ -19,14 +19,21 @@ struct bpf_map_def SEC("maps") stackid_hmap = { .type = BPF_MAP_TYPE_HASH, .key_size = sizeof(__u32), .value_size = sizeof(__u32), - .max_entries = 10000, + .max_entries = 16384, }; struct bpf_map_def SEC("maps") stackmap = { .type = BPF_MAP_TYPE_STACK_TRACE, .key_size = sizeof(__u32), .value_size = sizeof(__u64) * PERF_MAX_STACK_DEPTH, - .max_entries = 10000, + .max_entries = 16384, +}; + +struct bpf_map_def SEC("maps") stack_amap = { + .type = BPF_MAP_TYPE_ARRAY, + .key_size = sizeof(__u32), + .value_size = sizeof(__u64) * PERF_MAX_STACK_DEPTH, + .max_entries = 16384, };/* taken from /sys/kernel/debug/tracing/events/sched/sched_switch/format */
@@ -44,7 +51,10 @@ struct sched_switch_args { SEC("tracepoint/sched/sched_switch")int oncpu (struct sched_switch_args *ctx) { +__u32 max_len =PERF_MAX_STACK_DEPTH *sizeof (__u64);__u32 key =0 , val =0 , *value_p; +void *stack_p; + value_p =bpf_map_lookup_elem (&control_map, &key);if (value_p && *value_p)
@@ -52,8 +62,12 @@ int oncpu(struct sched_switch_args *ctx)/* The size of stackmap and stackid_hmap should be the same */ key =bpf_get_stackid (ctx, &stackmap,0 ); -if ((int )key >=0 ) +if ((int )key >=0 ) {bpf_map_update_elem (&stackid_hmap, &key, &val,0 ); + stack_p =bpf_map_lookup_elem (&stack_amap, &key); +if (stack_p) +bpf_get_stack (ctx, stack_p, max_len,0 ); + }return 0 ; }
Signature · 1 line
2.9.5