Skip to contentSkip to conversation

bpf: program testing framework

PATCH In review Alexei Starovoitov 4 participants 18 messages Mar 31, 2017 – Apr 1, 2017

Subsystems (9)
lore
Share & tools
lore
#0 lore raw

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
#1 lore raw

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
index 2ae39a3e9ead..bbb513da5075 100644
@@ -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
index 28317a04c34d..a1d95386f562 100644
@@ -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
index c35ebfe6d84d..ab0cf4c43690 100644
@@ -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, siz
 	case 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
index 9b681550e3a3..9086ffbb5085 100644
@@ -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
new file mode 100644
index 000000000000..27b2992a0692
@@ -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
index dfb9f61a2fd5..15e9a81ffebe 100644
@@ -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
#2 lore raw

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
#3 lore raw

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.

#4 lore raw

Re: [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_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.

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() 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:

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_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.

Quoted 5 lines

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.

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_run command 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
#5 lore raw

PATCH v2 2/6 tools/lib/bpf: add support for BPF_PROG_TEST_RUN command

In reply to Alexei Starovoitov

add 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
index 1ea08ce35567..a1d95386f562 100644
@@ -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
index 9b58d20e8c93..f84c398c11f4 100644
@@ -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
index 93f021932623..edb4daeff7a5 100644
@@ -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
Acked-by on v2 2/6
#6 lore raw

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_RUN command to libbpf.a

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
index 1ea08ce35567..a1d95386f562 100644
@@ -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
index 9b58d20e8c93..f84c398c11f4 100644
@@ -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
index 93f021932623..edb4daeff7a5 100644
@@ -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
#7 lore raw

PATCH v2 3/6 tools/lib/bpf: expose bpf_program__set_type()

In reply to Alexei Starovoitov

expose 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
index ac6eb863b2a4..1a2c07eb7795 100644
@@ -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
index b30394f9947a..32c7252f734e 100644
@@ -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
#9 lore raw

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.

#10 lore raw

Re: [PATCH v2 net-next 3/6] tools/lib/bpf: expose bpf_program__set_type()

In reply to Alexei Starovoitov
Quoted 15 lines · Alexei Starovoitov

expose 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 +0 -0
index ac6eb863b2a4..1a2c07eb7795 100644

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
index b30394f9947a..32c7252f734e 100644
@@ -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 -0
  bool 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.

#11 lore raw

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 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 -1
index ac6eb863b2a4..1a2c07eb7795 100644
@@ -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 -0
 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.

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
index b30394f9947a..32c7252f734e 100644
@@ -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_xxx become 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_xxx should be updated like bpf_program__set_xxx, since enum bpf_prog_type is 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.

#12 lore raw

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 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 -1
index ac6eb863b2a4..1a2c07eb7795 100644
@@ -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 -0
 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.

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
index b30394f9947a..32c7252f734e 100644
@@ -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_xxx become 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_xxx should be updated like bpf_program__set_xxx, since enum bpf_prog_type is 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.

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.

#13 lore raw

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.

#14 lore raw

PATCH v2 4/6 selftests/bpf: add a test for overlapping packet range checks

In reply to Alexei Starovoitov

add 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
index 6a1ad58cb66f..ff68c9419a67 100644
@@ -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
#15 lore raw

PATCH v2 5/6 selftests/bpf: add a test for basic XDP functionality

In reply to Alexei Starovoitov

add 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
index ff68c9419a67..76cbe1d42dda 100644
@@ -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
new file mode 100644
index 000000000000..e4cd252a1b20
@@ -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
#16 lore raw

PATCH v2 6/6 selftests/bpf: add l4 load balancer test based on sched_cls

In reply to Alexei Starovoitov

this 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
index 76cbe1d42dda..32fb7a294f0f 100644
@@ -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
maintainer
#17 lore raw
In reply to Alexei Starovoitov

From: 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 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.

Quoted 7 lines

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

Series applied, thanks Alexei.