bpf: program testing framework
Subsystems (9)
v2 cover bpf: program testing framework
Development and testing of networking bpf programs is quite cumbersome. Especially tricky are XDP programs that attach to real netdevices and program development feels like working on the car engine while the car is in motion.
Another problem is ongoing changes to upstream llvm core that can introduce an optimization that verifier will not recognize. llvm bpf backend tests have no ability to run the programs. To improve this situation introduce BPF_PROG_TEST_RUN command to test and performance benchmark bpf programs.
It achieves several goals:
- development of xdp and skb based bpf programs can be done in a canned environment with unit tests
- program performance optimizations can be benchmarked outside of networking core (without driver and skb costs)
- continuous testing of upstream changes is finally practical
Patches 4,5,6 add C based test cases of various complexity to cover some sched_cls and xdp features. More tests will be added in the future. The tests were run on centos7 only.
For now the framework supports only skb and xdp programs. In the future it can be extended to socket_filter and tracing program types.
More details are in individual patches.
v1->v2:
- rename bpf_program_test_run->bpf_prog_test_run
- add missing #include <linux/bpf.h> since libbpf.h shouldn't depend on prior includes
- reordered patches 3 and 4 to keep bisect clean
Alexei Starovoitov (6):
bpf: introduce BPF_PROG_TEST_RUN command tools/lib/bpf: add support for BPF_PROG_TEST_RUN command tools/lib/bpf: expose bpf_program__set_type() selftests/bpf: add a test for overlapping packet range checks selftests/bpf: add a test for basic XDP functionality selftests/bpf: add l4 load balancer test based on sched_cls
include/linux/bpf.h | 7 + include/uapi/linux/bpf.h | 12 + kernel/bpf/syscall.c | 27 +- net/Makefile | 2 +- net/bpf/Makefile | 1 + net/bpf/test_run.c | 172 ++++++++ net/core/filter.c | 5 + tools/include/uapi/linux/bpf.h | 24 ++ tools/lib/bpf/bpf.c | 24 ++ tools/lib/bpf/bpf.h | 4 +- tools/lib/bpf/libbpf.c | 3 +- tools/lib/bpf/libbpf.h | 2 + tools/testing/selftests/bpf/Makefile | 17 +- tools/testing/selftests/bpf/test_iptunnel_common.h | 37 ++ tools/testing/selftests/bpf/test_l4lb.c | 474 +++++++++++++++++++++ tools/testing/selftests/bpf/test_pkt_access.c | 64 +++ tools/testing/selftests/bpf/test_progs.c | 284 ++++++++++++ tools/testing/selftests/bpf/test_xdp.c | 236 ++++++++++ 18 files changed, 1385 insertions(+), 10 deletions(-) create mode 100644 net/bpf/Makefile create mode 100644 net/bpf/test_run.c create mode 100644 tools/testing/selftests/bpf/test_iptunnel_common.h create mode 100644 tools/testing/selftests/bpf/test_l4lb.c create mode 100644 tools/testing/selftests/bpf/test_pkt_access.c create mode 100644 tools/testing/selftests/bpf/test_progs.c create mode 100644 tools/testing/selftests/bpf/test_xdp.c
Signature · 1 line
2.9.3
PATCH v2 1/6 bpf: introduce BPF_PROG_TEST_RUN command
development and testing of networking bpf programs is quite cumbersome. Despite availability of user space bpf interpreters the kernel is the ultimate authority and execution environment.
Current test frameworks for TC include creation of netns, veth, qdiscs and use of various packet generators just to test functionality of a bpf program. XDP testing is even more complicated, since qemu needs to be started with gro/gso disabled and precise queue configuration, transferring of xdp program from host into guest, attaching to virtio/eth0 and generating traffic from the host while capturing the results from the guest.
Moreover analyzing performance bottlenecks in XDP program is impossible in virtio environment, since cost of running the program is tiny comparing to the overhead of virtio packet processing, so performance testing can only be done on physical nic with another server generating traffic.
Furthermore ongoing changes to user space control plane of production applications cannot be run on the test servers leaving bpf programs stubbed out for testing.
Last but not least, the upstream llvm changes are validated by the bpf backend testsuite which has no ability to test the code generated.
To improve this situation introduce BPF_PROG_TEST_RUN command to test and performance benchmark bpf programs.
Joint work with Daniel Borkmann.
--- include/linux/bpf.h | 7 ++ include/uapi/linux/bpf.h | 12 ++++ kernel/bpf/syscall.c | 27 +++++++- net/Makefile | 2 +- net/bpf/Makefile | 1 + net/bpf/test_run.c | 172 +++++++++++++++++++++++++++++++++++++++++++++++ net/core/filter.c | 5 ++ 7 files changed, 223 insertions(+), 3 deletions(-) create mode 100644 net/bpf/Makefile create mode 100644 net/bpf/test_run.c
include/linux/bpf.h +7 -0
@@ -169,6 +169,8 @@ struct bpf_verifier_ops {const struct bpf_insn *src, struct bpf_insn *dst, struct bpf_prog *prog); +int (*test_run)(struct bpf_prog *prog,const union bpf_attr *kattr, +union bpf_attr __user *uattr); };struct bpf_prog_type_list {
@@ -233,6 +235,11 @@ typedef unsigned long (*bpf_ctx_copy_t)(void *dst, const void *src,u64 bpf_event_output (struct bpf_map *map,u64 flags,void *meta,u64 meta_size,void *ctx,u64 ctx_size,bpf_ctx_copy_t ctx_copy); +int bpf_prog_test_run_xdp (struct bpf_prog *prog,const union bpf_attr *kattr, +union bpf_attr __user *uattr); +int bpf_prog_test_run_skb (struct bpf_prog *prog,const union bpf_attr *kattr, +union bpf_attr __user *uattr); +#ifdef CONFIG_BPF_SYSCALL DECLARE_PER_CPU (int, bpf_prog_active);
include/uapi/linux/bpf.h +12 -0
@@ -81,6 +81,7 @@ enum bpf_cmd {BPF_OBJ_GET ,BPF_PROG_ATTACH ,BPF_PROG_DETACH , + BPF_PROG_TEST_RUN, }; enum bpf_map_type {
@@ -189,6 +190,17 @@ union bpf_attr {__u32 attach_type;__u32 attach_flags; }; + +struct {/* anonymous struct used by BPF_PROG_TEST_RUN command */ +__u32 prog_fd; +__u32 retval; +__u32 data_size_in; +__u32 data_size_out; +__aligned_u64 data_in; +__aligned_u64 data_out; +__u32 repeat; +__u32 duration; + } test; } __attribute__((aligned(8)));/* BPF helper function descriptions:
kernel/bpf/syscall.c +25 -2
@@ -973,6 +973,28 @@ static int bpf_prog_detach(const union bpf_attr *attr) }#endif /* CONFIG_CGROUP_BPF */ +#define BPF_PROG_TEST_RUN_LAST_FIELD test.duration + +static int bpf_prog_test_run (const union bpf_attr *attr, +union bpf_attr __user *uattr) +{ +struct bpf_prog *prog; +int ret = -ENOTSUPP ; + +if (CHECK_ATTR (BPF_PROG_TEST_RUN )) +return -EINVAL ; + + prog =bpf_prog_get (attr->test.prog_fd); +if (IS_ERR (prog)) +return PTR_ERR (prog); + +if (prog->aux->ops->test_run) + ret = prog->aux->ops->test_run (prog, attr, uattr); + +bpf_prog_put (prog); +return ret; +} + SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size) {union bpf_attr attr = {};
@@ -1039,7 +1061,6 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, sizcase BPF_OBJ_GET : err =bpf_obj_get (&attr);break ; -#ifdef CONFIG_CGROUP_BPF case BPF_PROG_ATTACH : err =bpf_prog_attach (&attr);
@@ -1048,7 +1069,9 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz err =bpf_prog_detach (&attr);break ;#endif - +case BPF_PROG_TEST_RUN : + err =bpf_prog_test_run (&attr, uattr); +break ;default : err = -EINVAL ;break ;
net/Makefile +1 -1
@@ -12,7 +12,7 @@ obj-$(CONFIG_NET) += $(tmp-y) # LLC has to be linked before the files in net/802/ obj-$(CONFIG_LLC) += llc/ -obj-$(CONFIG_NET) += ethernet/ 802/ sched/ netlink/ +obj-$(CONFIG_NET) += ethernet/ 802/ sched/ netlink/ bpf/ obj-$(CONFIG_NETFILTER) += netfilter/ obj-$(CONFIG_INET) += ipv4/ obj-$(CONFIG_XFRM) += xfrm/
net/bpf/Makefile new file +1 -0
@@ -0,0 +1 @@ +obj-y := test_run.o
net/bpf/test_run.c new file +172 -0
172 lines not on this page · raw message
net/core/filter.c +5 -0
@@ -3309,24 +3309,28 @@ static const struct bpf_verifier_ops tc_cls_act_ops = { .is_valid_access = tc_cls_act_is_valid_access, .convert_ctx_access = tc_cls_act_convert_ctx_access, .gen_prologue = tc_cls_act_prologue, + .test_run = bpf_prog_test_run_skb, };static const struct bpf_verifier_ops xdp_ops = { .get_func_proto = xdp_func_proto, .is_valid_access = xdp_is_valid_access, .convert_ctx_access = xdp_convert_ctx_access, + .test_run = bpf_prog_test_run_xdp, };static const struct bpf_verifier_ops cg_skb_ops = { .get_func_proto = cg_skb_func_proto, .is_valid_access = sk_filter_is_valid_access, .convert_ctx_access = bpf_convert_ctx_access, + .test_run = bpf_prog_test_run_skb, };static const struct bpf_verifier_ops lwt_inout_ops = { .get_func_proto = lwt_inout_func_proto, .is_valid_access = lwt_is_valid_access, .convert_ctx_access = bpf_convert_ctx_access, + .test_run = bpf_prog_test_run_skb, };static const struct bpf_verifier_ops lwt_xmit_ops = {
@@ -3334,6 +3338,7 @@ static const struct bpf_verifier_ops lwt_xmit_ops = { .is_valid_access = lwt_is_valid_access, .convert_ctx_access = bpf_convert_ctx_access, .gen_prologue = tc_cls_act_prologue, + .test_run = bpf_prog_test_run_skb, };static const struct bpf_verifier_ops cg_sock_ops = {
Signature · 1 line
2.9.3
Re: [PATCH v2 net-next 1/6] bpf: introduce BPF_PROG_TEST_RUN command
net/bpf/test_run.c line 34
Quoted 8 lines · On Thu, 30 Mar 2017 21:45:38 -0700 Alexei Starovoitov <[email protected]> wrote:
static u32
bpf_test_run(struct bpf_prog *prog, void *ctx, u32 repeat, u32 *time)net/bpf/test_run.c new file +7
-0+{ +u64 time_start, time_spent =0 ; +u32 ret =0 , i; + +if (!repeat) + repeat =1 ; + time_start =ktime_get_ns ();
I've found that is useful to record the CPU cycles, as it is more useful for comparing between CPUs. The nanosec time measurement varies too much between CPUs and GHz. I do use nanosec measurements myself a lot, but that is mostly because it is easier to relate to pps rates. For eBPF code execution I think it is more useful to get a cycles cost count?
I've been using tsc[1] (rdtsc) to get the CPU cycles, I believe get_cycles() the more generic call, which have arch specific impl. (but can return 0 if no arch support).
The best solution would be to use the perf infrastructure and PMU counter to get both PMU cycles and instructions, as that also tell you about the pipeline efficiency like instructions per cycles. I only got this partly working in [1][2].
[1] https://github.com/netoptimizer/prototype-kernel/blob/master/kernel/include/linux/time_bench.h
[2] https://github.com/netoptimizer/prototype-kernel/blob/master/kernel/lib/time_bench.c
Quoted 16 lines
net/bpf/test_run.c new file +16
-0+for (i =0 ; i < repeat; i++) { + ret =bpf_test_run_one (prog, ctx); +if (need_resched ()) { +if (signal_pending (current)) +break ; + time_spent +=ktime_get_ns () - time_start; +cond_resched (); + time_start =ktime_get_ns (); + } + } + time_spent +=ktime_get_ns () - time_start; +do_div (time_spent, repeat); + *time = time_spent >U32_MAX ?U32_MAX : (u32 )time_spent; + +return ret; +}
Signature · 4 lines
Best regards, Jesper Dangaard Brouer MSc.CS, Principal Kernel Engineer at Red Hat LinkedIn: http://www.linkedin.com/in/brouer
Re: [PATCH v2 net-next 1/6] bpf: introduce BPF_PROG_TEST_RUN command
Quoted 18 lines · Jesper Dangaard Brouer
net/bpf/test_run.c line 34
Quoted 8 lines · On Thu, 30 Mar 2017 21:45:38 -0700 Alexei Starovoitov <[email protected]> wrote:
static u32
bpf_test_run(struct bpf_prog *prog, void *ctx, u32 repeat, u32 *time)net/bpf/test_run.c new file +7
-0+{ +u64 time_start, time_spent =0 ; +u32 ret =0 , i; + +if (!repeat) + repeat =1 ; + time_start =ktime_get_ns ();I've found that is useful to record the CPU cycles, as it is more useful for comparing between CPUs. The nanosec time measurement varies too much between CPUs and GHz. I do use nanosec measurements myself a lot, but that is mostly because it is easier to relate to pps rates. For eBPF code execution I think it is more useful to get a cycles cost count?
for micro-benchmarking of an instruction or small primitives like spin_lock and irq_save/restore, yes. Cycles are more interesting to look at. Here it's the whole program which in case of networking likely does at least a few map lookups.
Also this duration field is more of sanity test then actual metric.
Quoted 8 lines
I've been using tsc[1] (rdtsc) to get the CPU cycles, I believe
get_cycles()the more generic call, which have arch specific impl. (but can return 0 if no arch support).The best solution would be to use the perf infrastructure and PMU counter to get both PMU cycles and instructions, as that also tell you about the pipeline efficiency like instructions per cycles. I only got this partly working in [1][2].
to use get_cycles() or perf_event_create_kernel_counter() the current simple loop would become kthread pinned to cpu and so on. imo it's an overkill.
The only reason 'duration' being reported is a sanity test with user space measurements.
What this command allows to do is:
$ time ./my_bpf_benchmark The reported time should match the kernel reported 'duration'. The tiny difference will come from resched. That's sanity part. Now we can also do $ perf record ./my_bpf_benchmark and get all perf goodness for free without adding any kernel code. I want this test_run command to stay execution only. All pmu and performance metrics should stay on perf side. In case of performance optimization of bpf programs we're trying to improve perf by changing the way program is written, hence we need perf to point out which line of C code is costly. Second is improving performance by changing JIT, map implementations and so on. Here we also want full perf tool power. Unfortunately there is an issue with perf today, since as soon as my_bpf_benchmark exits, bpf prog is unloaded and ksym is gone, so 'perf report' cannot associate addresses back to source code. We discussed a solution with Arnaldo. So that's orthogonal work in progress which is needed regardless of this test_run command.
User space can also pin itself to cpu instead of asking kernel to do it and run the same program on multiple cpus in parallel testing interaction between concurrent map accesses and so on. So by keeping test_run command as execution only primitive we allow user space to do all the fancy tricks and measurements.
Jesper Dangaard Brouer
Acked-byRe: [PATCH v2 net-next 1/6] bpf: introduce BPF_PROG_TEST_RUN command
Quoted 20 lines · On Sat, 1 Apr 2017 08:45:01 -0700 Alexei Starovoitov <[email protected]> wrote:
Quoted 18 lines · Jesper Dangaard Brouer
net/bpf/test_run.c line 34
Quoted 8 lines · On Thu, 30 Mar 2017 21:45:38 -0700 Alexei Starovoitov <[email protected]> wrote:
static u32
bpf_test_run(struct bpf_prog *prog, void *ctx, u32 repeat, u32 *time)net/bpf/test_run.c new file +7
-0+{ +u64 time_start, time_spent =0 ; +u32 ret =0 , i; + +if (!repeat) + repeat =1 ; + time_start =ktime_get_ns ();I've found that is useful to record the CPU cycles, as it is more useful for comparing between CPUs. The nanosec time measurement varies too much between CPUs and GHz. I do use nanosec measurements myself a lot, but that is mostly because it is easier to relate to pps rates. For eBPF code execution I think it is more useful to get a cycles cost count?
Quoted 5 lines
for micro-benchmarking of an instruction or small primitives like
spin_lockand irq_save/restore, yes. Cycles are more interesting to look at. Here it's the whole program which in case of networking likely does at least a few map lookups.
Also this duration field is more of sanity test then actual metric.
Okay, if it was only a sanity metric.
Quoted 15 lines
Quoted 8 lines
I've been using tsc[1] (rdtsc) to get the CPU cycles, I believe
get_cycles()the more generic call, which have arch specific impl. (but can return 0 if no arch support).The best solution would be to use the perf infrastructure and PMU counter to get both PMU cycles and instructions, as that also tell you about the pipeline efficiency like instructions per cycles. I only got this partly working in [1][2].
to use
get_cycles()orperf_event_create_kernel_counter()the current simple loop would become kthread pinned to cpu and so on. imo it's an overkill.
The only reason 'duration' being reported is a sanity test with user space measurements.
What this command allows to do is:
Quoted 5 lines
$ time ./my_bpf_benchmark The reported time should match the kernel reported 'duration'. The tiny difference will come from resched. That's sanity part. Now we can also do $ perf record ./my_bpf_benchmark
Make perfect sense, to handle it this way.
Quoted 9 lines
and get all perf goodness for free without adding any kernel code. I want this
test_runcommand to stay execution only. All pmu and performance metrics should stay on perf side.
In case of performance optimization of bpf programs we're trying to improve perf by changing the way program is written, hence we need perf to point out which line of C code is costly. Second is improving performance by changing JIT, map implementations and so on. Here we also want full perf tool power.
Quoted 5 lines
Unfortunately there is an issue with perf today, since as soon as
my_bpf_benchmarkexits, bpf prog is unloaded and ksym is gone, so 'perf report' cannot associate addresses back to source code. We discussed a solution with Arnaldo. So that's orthogonal work in progress which is needed regardless of thistest_runcommand.
Yes, that is rather unfortunate. Good to hear there is work in this area.
I've started using:
sysctl net/core/bpf_jit_kallsyms=1
and adding --kallsyms=/proc/kallsyms to perf report, which is helpful.
Quoted 5 lines
User space can also pin itself to cpu instead of asking kernel to do it and run the same program on multiple cpus in parallel testing interaction between concurrent map accesses and so on. So by keeping
test_runcommand as execution only primitive we allow user space to do all the fancy tricks and measurements.
Sound good to me! :-)
Signature · 4 lines
Best regards, Jesper Dangaard Brouer MSc.CS, Principal Kernel Engineer at Red Hat LinkedIn: http://www.linkedin.com/in/brouer
PATCH v2 2/6 tools/lib/bpf: add support for BPF_PROG_TEST_RUN command
In reply to Alexei Starovoitovadd support for BPF_PROG_TEST_RUN command to libbpf.a
--- tools/include/uapi/linux/bpf.h | 24 ++++++++++++++++++++++++ tools/lib/bpf/bpf.c | 24 ++++++++++++++++++++++++ tools/lib/bpf/bpf.h | 4 +++- 3 files changed, 51 insertions(+), 1 deletion(-)
tools/include/uapi/linux/bpf.h +24 -0
@@ -81,6 +81,7 @@ enum bpf_cmd {BPF_OBJ_GET ,BPF_PROG_ATTACH ,BPF_PROG_DETACH , + BPF_PROG_TEST_RUN, }; enum bpf_map_type {
@@ -189,6 +190,17 @@ union bpf_attr {__u32 attach_type;__u32 attach_flags; }; + +struct {/* anonymous struct used by BPF_PROG_TEST_RUN command */ +__u32 prog_fd; +__u32 retval; +__u32 data_size_in; +__u32 data_size_out; +__aligned_u64 data_in; +__aligned_u64 data_out; +__u32 repeat; +__u32 duration; + } test; } __attribute__((aligned(8)));/* BPF helper function descriptions:
@@ -459,6 +471,18 @@ union bpf_attr {* Return: * > 0 length of the string including the trailing NUL on success * < 0 error +* +* u64 bpf_bpf_get_socket_cookie(skb) +* Get the cookie for the socket stored inside sk_buff. +* @skb: pointer to skb +* Return: 8 Bytes non-decreasing number on success or 0 if the socket +* field is missing inside sk_buff +* +* u32 bpf_get_socket_uid(skb) +* Get the owner uid of the socket stored inside sk_buff. +* @skb: pointer to skb +* Return: uid of the socket owner on success or 0 if the socket pointer +* inside sk_buff is NULL */ #define __BPF_FUNC_MAPPER (FN ) \ FN(unspec), \
tools/lib/bpf/bpf.c +24 -0
@@ -209,3 +209,27 @@ int bpf_prog_detach(int target_fd, enum bpf_attach_type type)return sys_bpf (BPF_PROG_DETACH , &attr,sizeof (attr)); } + +int bpf_prog_test_run (int prog_fd,int repeat,void *data,__u32 size, +void *data_out,__u32 *size_out,__u32 *retval, +__u32 *duration) +{ +union bpf_attr attr; +int ret; + +bzero (&attr,sizeof (attr)); + attr.test.prog_fd = prog_fd; + attr.test.data_in =ptr_to_u64 (data); + attr.test.data_out =ptr_to_u64 (data_out); + attr.test.data_size_in = size; + attr.test.repeat = repeat; + + ret =sys_bpf (BPF_PROG_TEST_RUN , &attr,sizeof (attr)); +if (size_out) + *size_out = attr.test.data_size_out; +if (retval) + *retval = attr.test.retval; +if (duration) + *duration = attr.test.duration; +return ret; +}
tools/lib/bpf/bpf.h +3 -1
@@ -47,6 +47,8 @@ int bpf_obj_get(const char *pathname);int bpf_prog_attach (int prog_fd,int attachable_fd,enum bpf_attach_type type,unsigned int flags);int bpf_prog_detach (int attachable_fd,enum bpf_attach_type type); - +int bpf_prog_test_run (int prog_fd,int repeat,void *data,__u32 size, +void *data_out,__u32 *size_out,__u32 *retval, +__u32 *duration);#endif
Signature · 1 line
2.9.3
Re: [PATCH v2 net-next 2/6] tools/lib/bpf: add support for BPF_PROG_TEST_RUN command
Quoted 5 lines · Alexei Starovoitov
add support for
BPF_PROG_TEST_RUNcommand to libbpf.aSigned-off-by Alexei Starovoitov <[email protected]>Acked-by Daniel Borkmann <[email protected]>Acked-by Martin KaFai Lau <[email protected]>
Quoted 101 lines
--- tools/include/uapi/linux/bpf.h | 24 ++++++++++++++++++++++++ tools/lib/bpf/bpf.c | 24 ++++++++++++++++++++++++ tools/lib/bpf/bpf.h | 4 +++- 3 files changed, 51 insertions(+), 1 deletion(-)tools/include/uapi/linux/bpf.h +24
-0@@ -81,6 +81,7 @@ enum bpf_cmd {BPF_OBJ_GET ,BPF_PROG_ATTACH ,BPF_PROG_DETACH , + BPF_PROG_TEST_RUN, }; enum bpf_map_type {@@ -189,6 +190,17 @@ union bpf_attr {__u32 attach_type;__u32 attach_flags; }; + +struct {/* anonymous struct used by BPF_PROG_TEST_RUN command */ +__u32 prog_fd; +__u32 retval; +__u32 data_size_in; +__u32 data_size_out; +__aligned_u64 data_in; +__aligned_u64 data_out; +__u32 repeat; +__u32 duration; + } test; } __attribute__((aligned(8)));/* BPF helper function descriptions: @@ -459,6 +471,18 @@ union bpf_attr {* Return: * > 0 length of the string including the trailing NUL on success * < 0 error +* +* u64 bpf_bpf_get_socket_cookie(skb) +* Get the cookie for the socket stored inside sk_buff. +* @skb: pointer to skb +* Return: 8 Bytes non-decreasing number on success or 0 if the socket +* field is missing inside sk_buff +* +* u32 bpf_get_socket_uid(skb) +* Get the owner uid of the socket stored inside sk_buff. +* @skb: pointer to skb +* Return: uid of the socket owner on success or 0 if the socket pointer +* inside sk_buff is NULL */ #define __BPF_FUNC_MAPPER (FN ) \ FN(unspec), \tools/lib/bpf/bpf.c +24
-0@@ -209,3 +209,27 @@ int bpf_prog_detach(int target_fd, enum bpf_attach_type type)return sys_bpf (BPF_PROG_DETACH , &attr,sizeof (attr)); } + +int bpf_prog_test_run (int prog_fd,int repeat,void *data,__u32 size, +void *data_out,__u32 *size_out,__u32 *retval, +__u32 *duration) +{ +union bpf_attr attr; +int ret; + +bzero (&attr,sizeof (attr)); + attr.test.prog_fd = prog_fd; + attr.test.data_in =ptr_to_u64 (data); + attr.test.data_out =ptr_to_u64 (data_out); + attr.test.data_size_in = size; + attr.test.repeat = repeat; + + ret =sys_bpf (BPF_PROG_TEST_RUN , &attr,sizeof (attr)); +if (size_out) + *size_out = attr.test.data_size_out; +if (retval) + *retval = attr.test.retval; +if (duration) + *duration = attr.test.duration; +return ret; +}tools/lib/bpf/bpf.h +3
-1@@ -47,6 +47,8 @@ int bpf_obj_get(const char *pathname);int bpf_prog_attach (int prog_fd,int attachable_fd,enum bpf_attach_type type,unsigned int flags);int bpf_prog_detach (int attachable_fd,enum bpf_attach_type type); - +int bpf_prog_test_run (int prog_fd,int repeat,void *data,__u32 size, +void *data_out,__u32 *size_out,__u32 *retval, +__u32 *duration);#endif
PATCH v2 3/6 tools/lib/bpf: expose bpf_program__set_type()
In reply to Alexei Starovoitovexpose bpf_program__set_type() to set program type
--- tools/lib/bpf/libbpf.c | 3 +-- tools/lib/bpf/libbpf.h | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-)
tools/lib/bpf/libbpf.c +1 -2
@@ -1618,8 +1618,7 @@ int bpf_program__nth_fd(struct bpf_program *prog, int n)return fd; } -static void bpf_program__set_type (struct bpf_program *prog, -enum bpf_prog_type type) +void bpf_program__set_type (struct bpf_program *prog,enum bpf_prog_type type) { prog->type = type; }
tools/lib/bpf/libbpf.h +2 -0
@@ -25,6 +25,7 @@#include <stdint.h> #include <stdbool.h> #include <sys/types.h> // for size_t +#include <linux/bpf.h> enum libbpf_errno { __LIBBPF_ERRNO__START =4000 ,
@@ -185,6 +186,7 @@ int bpf_program__set_sched_cls(struct bpf_program *prog);int bpf_program__set_sched_act (struct bpf_program *prog);int bpf_program__set_xdp (struct bpf_program *prog);int bpf_program__set_perf_event (struct bpf_program *prog); +void bpf_program__set_type (struct bpf_program *prog,enum bpf_prog_type type);bool bpf_program__is_socket_filter (struct bpf_program *prog);bool bpf_program__is_tracepoint (struct bpf_program *prog);
Signature · 1 line
2.9.3
Re: [PATCH v2 net-next 3/6] tools/lib/bpf: expose bpf_program__set_type()
Hi Alexei,
Please see the patch I sent. Since we export bpf_program__set_type(), bpf_program__set_xxx() should be built based on it.
Thank you.
Quoted 7 lines · Alexei Starovoitov
expose
bpf_program__set_type()to set program typeSigned-off-by Alexei Starovoitov <[email protected]>Acked-by Daniel Borkmann <[email protected]>Acked-by Martin KaFai Lau <[email protected]>---
Re: [PATCH v2 net-next 3/6] tools/lib/bpf: expose bpf_program__set_type()
Quoted 4 lines · Wangnan (F)
Hi Alexei,
Please see the patch I sent. Since we export
bpf_program__set_type(),bpf_program__set_xxx()should be built based on it.
Replied to your patch. I still think simply adding #include <linux/bpf.h> here like I did in this patch is much cleaner. That's the whole purpose of uapi header to be used in such libraries and apps. Copy-pasting enum bpf_prog_type from bpf.h into bunch of macros in libbpf.h just doesn't look right and likely an indication that whatever you do with your proprietary stuff is fragile. Clearly you're trying to avoid including bpf.h not because of perf, which has its own copy of bpf.h in tools/include/
Just like iproute2 has its copy of bpf.h as well.
Re: [PATCH v2 net-next 3/6] tools/lib/bpf: expose bpf_program__set_type()
In reply to Alexei StarovoitovQuoted 15 lines · Alexei Starovoitov
expose
bpf_program__set_type()to set program typeSigned-off-by Alexei Starovoitov <[email protected]>Acked-by Daniel Borkmann <[email protected]>Acked-by Martin KaFai Lau <[email protected]>--- tools/lib/bpf/libbpf.c | 3 +-- tools/lib/bpf/libbpf.h | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-)tools/lib/bpf/libbpf.c +0
-0
tools/lib/bpf/libbpf.c int bpf_program__nth_fd(struct bpf_program *prog, int n) · line 1624
Quoted 9 lines
tools/lib/bpf/libbpf.c +1
-2@@ -1618,8 +1618,7 @@ int bpf_program__nth_fd(struct bpf_program *prog, int n)return fd; } -static void bpf_program__set_type (struct bpf_program *prog, -enum bpf_prog_type type) +void bpf_program__set_type (struct bpf_program *prog,enum bpf_prog_type type) { prog->type = type; }
Since it become a public interface, we need to check if prog is a NULL pointer and check if the value of type is okay, and let it return an errno.
Quoted 13 lines
tools/lib/bpf/libbpf.h +1
-0@@ -25,6 +25,7 @@#include <stdint.h> #include <stdbool.h> #include <sys/types.h> // for size_t +#include <linux/bpf.h> enum libbpf_errno { __LIBBPF_ERRNO__START =4000 ,
tools/lib/bpf/libbpf.h int bpf_program__set_sched_cls(struct bpf_program *prog); · line 190
Quoted 5 lines
tools/lib/bpf/libbpf.h +1
-0@@ -185,6 +186,7 @@ int bpf_program__set_sched_cls(struct bpf_program *prog);int bpf_program__set_sched_act (struct bpf_program *prog);int bpf_program__set_xdp (struct bpf_program *prog);int bpf_program__set_perf_event (struct bpf_program *prog); +void bpf_program__set_type (struct bpf_program *prog,enum bpf_prog_type type);
The above bpf_program__set_xxx become redundancy. It should be generated using macro as static inline functions.
tools/lib/bpf/libbpf.h int bpf_program__set_sched_cls(struct bpf_program *prog); · line 192
Quoted 2 lines
tools/lib/bpf/libbpf.h +0
-0bool bpf_program__is_socket_filter (struct bpf_program *prog);bool bpf_program__is_tracepoint (struct bpf_program *prog);
bpf_program__is_xxx should be updated like bpf_program__set_xxx, since enum bpf_prog_type is not a problem now.
Thank you.
Re: [PATCH v2 net-next 3/6] tools/lib/bpf: expose bpf_program__set_type()
Quoted 24 lines · Wangnan (F)
Quoted 21 lines · Alexei Starovoitov
expose
bpf_program__set_type()to set program typeSigned-off-by Alexei Starovoitov <[email protected]>Acked-by Daniel Borkmann <[email protected]>Acked-by Martin KaFai Lau <[email protected]>--- tools/lib/bpf/libbpf.c | 3 +-- tools/lib/bpf/libbpf.h | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-)tools/lib/bpf/libbpf.c +1
-1@@ -1618,8 +1618,7 @@ int bpf_program__nth_fd(struct bpf_program *prog, int n)return fd; } -static void bpf_program__set_type(struct bpf_program *prog, -enum bpf_prog_type type) +void bpf_program__set_type(struct bpf_program *prog, enum
Quoted 8 lines
tools/lib/bpf/libbpf.c int bpf_program__nth_fd(struct bpf_program *prog, int n) · line 1624
Quoted 4 lines
tools/lib/bpf/libbpf.c +0
-0bpf_prog_type type) { prog->type = type; }Since it become a public interface, we need to check if prog is a NULL pointer and check if the value of type is okay, and let it return an errno.
I strongly disagree with such defensive programming. It's a cause of bugs for users of this library.
I think you're trying to mimic c++ setters/getters, but c++ never checks 'this != null', since passing null into setter is a bug of the user of the library and not the library. The setters also should have 'void' return type when setters cannot fail. That is exactly the case here.
If, in the future, we decide that this libbpf shouldn't support all bpf program types then you'd need to change the prototype of this function to return error code and change all places where this function is called to check for error code.
It may or may not be the right approach.
For example today the only user of bpf_program__set*() methods is perf/util/bpf-loader.c and it calls bpf_program__set_kprobe() and bpf_program__set_tracepoint() _without_ checking the return value which is _correct_ thing to do. Instead the current prototype of 'int bpf_program__set_tracepoint(struct bpf_program *prog); is not correct and I suggest you to fix it.
You also need to do other cleanup. Like in bpf_object__elf_finish() you have:
if (!obj_elf_valid(obj))
return;
if (obj->efile.elf) {which is redundant. It's another example where mistakes creep in due to defensive programming.
Another bug in bpf_object__close() which does:
if (!obj)
return;again defensive programming strikes, since
you're not checking IS_ERR(obj) and that's what bpf_object__open() returns, so most users of the library (who don't read the source code and just using it based on .h) will do
obj = bpf_object__open(...);bpf_object__close(obj);
and current 'if (!obj)' won't help and it will segfault. I hit this issue will developing this patch set.
Quoted 23 lines
Quoted 12 lines
tools/lib/bpf/libbpf.h +1
-0@@ -25,6 +25,7 @@#include <stdint.h> #include <stdbool.h> #include <sys/types.h> // for size_t +#include <linux/bpf.h> enum libbpf_errno { __LIBBPF_ERRNO__START =4000 ,tools/lib/bpf/libbpf.h int bpf_program__set_sched_cls(struct bpf_program · line 192
Quoted 7 lines
tools/lib/bpf/libbpf.h +1
-0@@ -185,6 +186,7 @@ int bpf_program__set_sched_cls(struct bpf_program *prog);int bpf_program__set_sched_act (struct bpf_program *prog);int bpf_program__set_xdp (struct bpf_program *prog);int bpf_program__set_perf_event (struct bpf_program *prog); +void bpf_program__set_type (struct bpf_program *prog,enum bpf_prog_type type);The above
bpf_program__set_xxxbecome redundancy. It should be generated using macro as static inline functions.
Quoted 5 lines
Quoted 2 lines
bool bpf_program__is_socket_filter(struct bpf_program *prog); bool bpf_program__is_tracepoint(struct bpf_program *prog);
bpf_program__is_xxxshould be updated likebpf_program__set_xxx, since enumbpf_prog_typeis not a problem now.
All of these suggestions is a cleanup for your code that you need to do yourself. I actually suggest you kill all bpf_program__is*() and all but one bpf_program__set*() functions.
The current user perf/util/bpf-loader.c should be converted to using bpf_program__set_type() with _void_ return code that I'm introducing here.
Overall, I think, tools/lib/bpf/ is a nice library and it can be used by many projects, but I suggest to stop making excuses based on your proprietary usage of it.
Also please cc me in the future on changes to the library. It still has my copyrights, though a lot has changed, since last time I looked at it and it's my fault for not pay attention earlier.
Re: [PATCH v2 net-next 3/6] tools/lib/bpf: expose bpf_program__set_type()
Quoted 113 lines · Alexei Starovoitov
Quoted 24 lines · Wangnan (F)
Quoted 21 lines · Alexei Starovoitov
expose
bpf_program__set_type()to set program typeSigned-off-by Alexei Starovoitov <[email protected]>Acked-by Daniel Borkmann <[email protected]>Acked-by Martin KaFai Lau <[email protected]>--- tools/lib/bpf/libbpf.c | 3 +-- tools/lib/bpf/libbpf.h | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-)tools/lib/bpf/libbpf.c +1
-1@@ -1618,8 +1618,7 @@ int bpf_program__nth_fd(struct bpf_program *prog, int n)return fd; } -static void bpf_program__set_type(struct bpf_program *prog, -enum bpf_prog_type type) +void bpf_program__set_type(struct bpf_program *prog, enumQuoted 8 lines
tools/lib/bpf/libbpf.c int bpf_program__nth_fd(struct bpf_program *prog, int n) · line 1624
Quoted 4 lines
tools/lib/bpf/libbpf.c +0
-0bpf_prog_type type) { prog->type = type; }Since it become a public interface, we need to check if prog is a NULL pointer and check if the value of type is okay, and let it return an errno.
I strongly disagree with such defensive programming. It's a cause of bugs for users of this library.
I think you're trying to mimic c++ setters/getters, but c++ never checks 'this != null', since passing null into setter is a bug of the user of the library and not the library. The setters also should have 'void' return type when setters cannot fail. That is exactly the case here.
If, in the future, we decide that this libbpf shouldn't support all bpf program types then you'd need to change the prototype of this function to return error code and change all places where this function is called to check for error code.
It may or may not be the right approach.
For example today the only user of bpf_program__set*() methods isperf/util/bpf-loader.cand it callsbpf_program__set_kprobe()andbpf_program__set_tracepoint()_without_ checking the return value which is _correct_ thing to do. Instead the current prototype of 'intbpf_program__set_tracepoint(struct bpf_program *prog); is not correct and I suggest you to fix it.You also need to do other cleanup. Like in
bpf_object__elf_finish()you have:if (!obj_elf_valid(obj)) return; if (obj->efile.elf) {which is redundant. It's another example where mistakes creep in due to defensive programming.
Another bug in
bpf_object__close()which does:if (!obj) return;again defensive programming strikes, since
you're not checkingIS_ERR(obj)and that's whatbpf_object__open()returns, so most users of the library (who don't read the source code and just using it based on .h) will doobj =
bpf_object__open(...);bpf_object__close(obj);and current 'if (!obj)' won't help and it will segfault. I hit this issue will developing this patch set.
Quoted 23 lines
Quoted 12 lines
tools/lib/bpf/libbpf.h +1
-0@@ -25,6 +25,7 @@#include <stdint.h> #include <stdbool.h> #include <sys/types.h> // for size_t +#include <linux/bpf.h> enum libbpf_errno { __LIBBPF_ERRNO__START =4000 ,tools/lib/bpf/libbpf.h int bpf_program__set_sched_cls(struct bpf_program · line 192
Quoted 7 lines
tools/lib/bpf/libbpf.h +1
-0@@ -185,6 +186,7 @@ int bpf_program__set_sched_cls(struct bpf_program *prog);int bpf_program__set_sched_act (struct bpf_program *prog);int bpf_program__set_xdp (struct bpf_program *prog);int bpf_program__set_perf_event (struct bpf_program *prog); +void bpf_program__set_type (struct bpf_program *prog,enum bpf_prog_type type);The above
bpf_program__set_xxxbecome redundancy. It should be generated using macro as static inline functions.Quoted 5 lines
Quoted 2 lines
bool bpf_program__is_socket_filter(struct bpf_program *prog); bool bpf_program__is_tracepoint(struct bpf_program *prog);
bpf_program__is_xxxshould be updated likebpf_program__set_xxx, since enumbpf_prog_typeis not a problem now.All of these suggestions is a cleanup for your code that you need to do yourself. I actually suggest you kill all bpf_program__is*() and all but one bpf_program__set*() functions.
The current userperf/util/bpf-loader.cshould be converted to usingbpf_program__set_type()with _void_ return code that I'm introducing here.
Quoted 8 lines
Overall, I think,
tools/lib/bpf/is a nice library and it can be used by many projects, but I suggest to stop making excuses based on your proprietary usage of it.Also please cc me in the future on changes to the library. It still has my copyrights, though a lot has changed, since last time I looked at it and it's my fault for not pay attention earlier.
OK. Then let your patch be merged first then let me do the code cleanup.
Thank you.
Re: [PATCH v2 net-next 3/6] tools/lib/bpf: expose bpf_program__set_type()
Quoted 2 lines · Wangnan (F)
OK. Then let your patch be merged first then let me do the code cleanup.
Thanks!
Once these two lib/bpf patches applied to net-next we can apply the same to tip and there will be no conflicts during the merge window.
I'm also planning to fix few things later:
- there is an obscure error
libbpf: relocation failed: no 11 section
It's printed when llvm generates access to global variables. I think we need to improve the error message to point out the actual reason of the failure, so the user can fix the program.
- there is another equally obscure error
libbpf: Program 'foo' contains non-map related relo data pointing to section 8 It's happening when program is compiled with -g.
It's just a bug in libbpf and the fix is straightforward.
so I suspect these fixes will also go into both net-next and tip.
PATCH v2 4/6 selftests/bpf: add a test for overlapping packet range checks
In reply to Alexei Starovoitovadd simple C test case for llvm and verifier range check fix from commit b1977682a385 ("bpf: improve verifier packet range checks")
--- tools/testing/selftests/bpf/Makefile | 17 +++- tools/testing/selftests/bpf/test_pkt_access.c | 64 ++++++++++++ tools/testing/selftests/bpf/test_progs.c | 138 ++++++++++++++++++++++++++ 3 files changed, 215 insertions(+), 4 deletions(-) create mode 100644 tools/testing/selftests/bpf/test_pkt_access.c create mode 100644 tools/testing/selftests/bpf/test_progs.c
tools/testing/selftests/bpf/Makefile +13 -4
@@ -1,16 +1,18 @@ LIBDIR := ../../../lib BPFDIR := $(LIBDIR)/bpf -CFLAGS += -Wall -O2 -I../../../include/uapi -I$(LIBDIR) -LDLIBS += -lcap +CFLAGS += -Wall -O2 -I../../../include/uapi -I$(LIBDIR) -I../../../include +LDLIBS += -lcap -lelf -TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map +TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs + +TEST_GEN_FILES = test_pkt_access.o TEST_PROGS := test_kmod.sh include ../lib.mk -BPFOBJ := $(OUTPUT)/bpf.o +BPFOBJ := $(OUTPUT)/libbpf.a $(TEST_GEN_PROGS): $(BPFOBJ)
@@ -21,3 +23,10 @@ $(TEST_GEN_PROGS): $(BPFOBJ) $(BPFOBJ): force $(MAKE) -C $(BPFDIR) OUTPUT=$(OUTPUT)/ + +CLANG ?= clang + +%.o: %.c + $(CLANG) -I../../../include/uapi -I../../../../samples/bpf/ \ + -D__x86_64__ -Wno-compare-distinct-pointer-types \ + -O2 -target bpf -c $< -o $@
tools/testing/selftests/bpf/test_pkt_access.c new file +64 -0
64 lines not on this page · raw message
tools/testing/selftests/bpf/test_progs.c new file +138 -0
138 lines not on this page · raw message
Signature · 1 line
2.9.3
PATCH v2 5/6 selftests/bpf: add a test for basic XDP functionality
In reply to Alexei Starovoitovadd C test for xdp_adjust_head(), packet rewrite and map lookups
--- tools/testing/selftests/bpf/Makefile | 2 +- tools/testing/selftests/bpf/test_iptunnel_common.h | 37 ++++ tools/testing/selftests/bpf/test_progs.c | 58 +++++ tools/testing/selftests/bpf/test_xdp.c | 236 +++++++++++++++++++++ 4 files changed, 332 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/bpf/test_iptunnel_common.h create mode 100644 tools/testing/selftests/bpf/test_xdp.c
tools/testing/selftests/bpf/Makefile +1 -1
@@ -6,7 +6,7 @@ LDLIBS += -lcap -lelf TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs -TEST_GEN_FILES = test_pkt_access.o +TEST_GEN_FILES = test_pkt_access.o test_xdp.o TEST_PROGS := test_kmod.sh
tools/testing/selftests/bpf/test_iptunnel_common.h new file +37 -0
@@ -0,0 +1,37 @@ +/* Copyright (c) 2016 Facebook +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of version 2 of the GNU General Public +* License as published by the Free Software Foundation. +*/ +#ifndef _TEST_IPTNL_COMMON_H +#define _TEST_IPTNL_COMMON_H + +#include <linux/types.h> + +#define MAX_IPTNL_ENTRIES 256U + +struct vip { +union { +__u32 v6[4 ]; +__u32 v4; + } daddr; +__u16 dport; +__u16 family; +__u8 protocol; +}; + +struct iptnl_info { +union { +__u32 v6[4 ]; +__u32 v4; + } saddr; +union { +__u32 v6[4 ]; +__u32 v4; + } daddr; +__u16 family; +__u8 dmac[6 ]; +}; + +#endif
tools/testing/selftests/bpf/test_progs.c +58 -0
82 lines not on this page · raw message
tools/testing/selftests/bpf/test_xdp.c new file +236 -0
236 lines not on this page · raw message
Signature · 1 line
2.9.3
PATCH v2 6/6 selftests/bpf: add l4 load balancer test based on sched_cls
In reply to Alexei Starovoitovthis l4lb demo is a comprehensive test case for LLVM codegen and kernel verifier. It's using fully inlined jhash(), complex packet parsing and multiple map lookups of different types to stress llvm and verifier.
The map sizes, map population and test vectors are artificial to exercise different paths through the bpf program.
--- tools/testing/selftests/bpf/Makefile | 2 +- tools/testing/selftests/bpf/test_l4lb.c | 474 +++++++++++++++++++++++++++++++ tools/testing/selftests/bpf/test_progs.c | 88 ++++++ 3 files changed, 563 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/bpf/test_l4lb.c
tools/testing/selftests/bpf/Makefile +1 -1
@@ -6,7 +6,7 @@ LDLIBS += -lcap -lelf TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs -TEST_GEN_FILES = test_pkt_access.o test_xdp.o +TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o TEST_PROGS := test_kmod.sh
tools/testing/selftests/bpf/test_l4lb.c new file +474 -0
474 lines not on this page · raw message
tools/testing/selftests/bpf/test_progs.c +88 -0
123 lines not on this page · raw message
Signature · 1 line
2.9.3
David Miller
maintainerFrom: Alexei Starovoitov <[email protected]>
Date: Thu, 30 Mar 2017 21:45:37 -0700
Quoted 23 lines
Development and testing of networking bpf programs is quite cumbersome. Especially tricky are XDP programs that attach to real netdevices and program development feels like working on the car engine while the car is in motion.
Another problem is ongoing changes to upstream llvm core that can introduce an optimization that verifier will not recognize. llvm bpf backend tests have no ability to run the programs. To improve this situation introduceBPF_PROG_TEST_RUNcommand to test and performance benchmark bpf programs.
It achieves several goals:
- development of xdp and skb based bpf programs can be done in a canned environment with unit tests
- program performance optimizations can be benchmarked outside of networking core (without driver and skb costs)
- continuous testing of upstream changes is finally practicalPatches 4,5,6 add C based test cases of various complexity to cover some
sched_clsand xdp features. More tests will be added in the future. The tests were run on centos7 only.For now the framework supports only skb and xdp programs. In the future it can be extended to
socket_filterand tracing program types.
Quoted 7 lines
More details are in individual patches.
v1->v2:
- renamebpf_program_test_run->bpf_prog_test_run
- add missing #include <linux/bpf.h> sincelibbpf.hshouldn't depend on prior includes
- reordered patches 3 and 4 to keep bisect clean
Series applied, thanks Alexei.