aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorToke Høiland-Jørgensen <toke@redhat.com>2021-11-05 16:23:43 +0100
committerToke Høiland-Jørgensen <toke@redhat.com>2022-05-25 23:43:06 +0200
commit337df854f98844b3a669dc3ddedb8edfa273cb9b (patch)
treee341fc5eeccdb44b0efabb952d8b68b2c23de46b
parentb003fccb24ba1de69b62a84f04ecf388c2d875d0 (diff)
downloadlinux-xdp-queueing-05.tar.gz
selftests/bpf: Add test for XDP queueing through PIFO mapsxdp-queueing-05
This adds a selftest that uses bpf_prog_run() of an XDP program that puts packets into a PIFO map, and then pulls them back out again through bpf_prog_run() of a dequeue program. Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
-rw-r--r--tools/testing/selftests/bpf/prog_tests/pifo_map.c126
-rw-r--r--tools/testing/selftests/bpf/prog_tests/xdp_pifo_test_run.c78
-rw-r--r--tools/testing/selftests/bpf/progs/pifo_map.c54
-rw-r--r--tools/testing/selftests/bpf/progs/test_xdp_pifo.c83
4 files changed, 341 insertions, 0 deletions
diff --git a/tools/testing/selftests/bpf/prog_tests/pifo_map.c b/tools/testing/selftests/bpf/prog_tests/pifo_map.c
new file mode 100644
index 00000000000000..31dacd6ffd28b2
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/pifo_map.c
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <test_progs.h>
+#include "pifo_map.skel.h"
+
+static int run_prog(int prog_fd, __u32 exp_retval)
+{
+ struct xdp_md ctx_in = {};
+ char data[10] = {};
+ DECLARE_LIBBPF_OPTS(bpf_test_run_opts, opts,
+ .data_in = data,
+ .data_size_in = sizeof(data),
+ .ctx_in = &ctx_in,
+ .ctx_size_in = sizeof(ctx_in),
+ .repeat = 1,
+ );
+ int err;
+
+ ctx_in.data_end = sizeof(data);
+ err = bpf_prog_test_run_opts(prog_fd, &opts);
+ if (!ASSERT_OK(err, "bpf_prog_test_run(valid)"))
+ return -1;
+ if (!ASSERT_EQ(opts.retval, exp_retval, "prog retval"))
+ return -1;
+
+ return 0;
+}
+
+static void check_map_counts(int map_fd, int start, int interval, int num, int exp_val)
+{
+ __u32 val, key, next_key, *kptr = NULL;
+ int i, err;
+
+ for (i = 0; i < num; i++) {
+ err = bpf_map_get_next_key(map_fd, kptr, &next_key);
+ if (!ASSERT_OK(err, "bpf_map_get_next_key()"))
+ return;
+
+ key = next_key;
+ kptr = &key;
+
+ if (!ASSERT_EQ(key, start + i * interval, "expected key"))
+ break;
+ err = bpf_map_lookup_elem(map_fd, &key, &val);
+ if (!ASSERT_OK(err, "bpf_map_lookup_elem()"))
+ break;
+ if (!ASSERT_EQ(val, exp_val, "map value"))
+ break;
+ }
+}
+
+static void run_enqueue_fail(struct pifo_map *skel, int start, int interval, __u32 exp_retval)
+{
+ int enqueue_fd;
+
+ skel->bss->start = start;
+ skel->data->interval = interval;
+
+ enqueue_fd = bpf_program__fd(skel->progs.pifo_enqueue);
+
+ if (run_prog(enqueue_fd, exp_retval))
+ return;
+}
+
+static void run_test(struct pifo_map *skel, int start, int interval)
+{
+ int enqueue_fd, dequeue_fd;
+
+ skel->bss->start = start;
+ skel->data->interval = interval;
+
+ enqueue_fd = bpf_program__fd(skel->progs.pifo_enqueue);
+ dequeue_fd = bpf_program__fd(skel->progs.pifo_dequeue);
+
+ if (run_prog(enqueue_fd, 0))
+ return;
+ check_map_counts(bpf_map__fd(skel->maps.pifo_map),
+ skel->bss->start, skel->data->interval,
+ skel->rodata->num_entries, 1);
+ run_prog(dequeue_fd, 0);
+}
+
+void test_pifo_map()
+{
+ struct pifo_map *skel = NULL;
+ int err;
+
+ skel = pifo_map__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "skel"))
+ return;
+
+ run_test(skel, 0, 1);
+ run_test(skel, 0, 10);
+ run_test(skel, 0, 100);
+
+ /* do a series of runs that keep advancing the priority, to check that
+ * we can keep rorating the two internal maps
+ */
+ run_test(skel, 0, 125);
+ run_test(skel, 1250, 1);
+ run_test(skel, 1250, 125);
+
+ /* after rotating, starting enqueue at prio 0 will now fail */
+ run_enqueue_fail(skel, 0, 1, -ERANGE);
+
+ run_test(skel, 2500, 125);
+ run_test(skel, 3750, 125);
+ run_test(skel, 5000, 125);
+
+ pifo_map__destroy(skel);
+
+ /* reopen but change rodata */
+ skel = pifo_map__open();
+ if (!ASSERT_OK_PTR(skel, "open skel"))
+ return;
+
+ skel->rodata->num_entries = 12;
+ err = pifo_map__load(skel);
+ if (!ASSERT_OK(err, "load skel"))
+ goto out;
+
+ /* fails because the map is too small */
+ run_enqueue_fail(skel, 0, 1, -EOVERFLOW);
+out:
+ pifo_map__destroy(skel);
+
+}
diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_pifo_test_run.c b/tools/testing/selftests/bpf/prog_tests/xdp_pifo_test_run.c
new file mode 100644
index 00000000000000..f2cc67d1eb3bc1
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/xdp_pifo_test_run.c
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <test_progs.h>
+#include <network_helpers.h>
+#include "test_xdp_pifo.skel.h"
+
+static void run_xdp_prog(int prog_fd, void *data, size_t data_size, int repeat)
+{
+ struct xdp_md ctx_in = {};
+ DECLARE_LIBBPF_OPTS(bpf_test_run_opts, opts,
+ .data_in = data,
+ .data_size_in = data_size,
+ .ctx_in = &ctx_in,
+ .ctx_size_in = sizeof(ctx_in),
+ .repeat = repeat,
+ .flags = BPF_F_TEST_XDP_LIVE_FRAMES,
+ );
+ int err;
+
+ ctx_in.data_end = ctx_in.data + sizeof(pkt_v4);
+ err = bpf_prog_test_run_opts(prog_fd, &opts);
+ ASSERT_OK(err, "bpf_prog_test_run(valid)");
+}
+
+static void run_dequeue_prog(int prog_fd, int exp_proto)
+{
+ struct ipv4_packet data_out;
+ DECLARE_LIBBPF_OPTS(bpf_test_run_opts, opts,
+ .data_out = &data_out,
+ .data_size_out = sizeof(data_out),
+ .repeat = 1,
+ );
+ int err;
+
+ err = bpf_prog_test_run_opts(prog_fd, &opts);
+ ASSERT_OK(err, "bpf_prog_test_run(valid)");
+ ASSERT_EQ(opts.retval, exp_proto == -1 ? 0 : 1, "valid-retval");
+ if (exp_proto >= 0) {
+ ASSERT_EQ(opts.data_size_out, sizeof(pkt_v4), "valid-datasize");
+ ASSERT_EQ(data_out.eth.h_proto, exp_proto, "valid-pkt");
+ } else {
+ ASSERT_EQ(opts.data_size_out, 0, "no-pkt-returned");
+ }
+}
+
+void test_xdp_pifo(void)
+{
+ int xdp_prog_fd, dequeue_prog_fd, i;
+ struct test_xdp_pifo *skel = NULL;
+ struct ipv4_packet data;
+
+ skel = test_xdp_pifo__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "skel"))
+ return;
+
+ xdp_prog_fd = bpf_program__fd(skel->progs.xdp_pifo);
+ dequeue_prog_fd = bpf_program__fd(skel->progs.dequeue_pifo);
+ data = pkt_v4;
+
+ run_xdp_prog(xdp_prog_fd, &data, sizeof(data), 3);
+
+ /* kernel program queues packets with prio 2, 1, 0 (in that order), we
+ * should get back 0 and 1, and 2 should get dropped on dequeue
+ */
+ run_dequeue_prog(dequeue_prog_fd, 0);
+ run_dequeue_prog(dequeue_prog_fd, 1);
+ run_dequeue_prog(dequeue_prog_fd, -1);
+
+ xdp_prog_fd = bpf_program__fd(skel->progs.xdp_pifo_inc);
+ run_xdp_prog(xdp_prog_fd, &data, sizeof(data), 1024);
+
+ skel->bss->pkt_count = 0;
+ skel->data->prio = 0;
+ skel->data->drop_above = 1024;
+ for (i = 0; i < 1024; i++)
+ run_dequeue_prog(dequeue_prog_fd, i*10);
+
+ test_xdp_pifo__destroy(skel);
+}
diff --git a/tools/testing/selftests/bpf/progs/pifo_map.c b/tools/testing/selftests/bpf/progs/pifo_map.c
new file mode 100644
index 00000000000000..9ed8e986e07866
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/pifo_map.c
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/bpf.h>
+#include <linux/if_ether.h>
+#include <bpf/bpf_helpers.h>
+
+struct {
+ __uint(type, BPF_MAP_TYPE_PIFO_GENERIC);
+ __uint(key_size, sizeof(__u32));
+ __uint(value_size, sizeof(__u32));
+ __uint(max_entries, 10);
+ __uint(map_extra, 1024); /* range */
+} pifo_map SEC(".maps");
+
+const volatile int num_entries = 10;
+volatile int interval = 10;
+volatile int start = 0;
+
+SEC("xdp")
+int pifo_dequeue(struct xdp_md *xdp)
+{
+ __u32 val, exp;
+ int i, ret;
+
+ for (i = 0; i < num_entries; i++) {
+ exp = start + i * interval;
+ ret = bpf_map_pop_elem(&pifo_map, &val);
+ if (ret)
+ return ret;
+ if (val != exp)
+ return 1;
+ }
+
+ return 0;
+}
+
+SEC("xdp")
+int pifo_enqueue(struct xdp_md *xdp)
+{
+ __u64 flags;
+ __u32 val;
+ int i, ret;
+
+ for (i = num_entries - 1; i >= 0; i--) {
+ val = start + i * interval;
+ flags = val;
+ ret = bpf_map_push_elem(&pifo_map, &val, flags);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/test_xdp_pifo.c b/tools/testing/selftests/bpf/progs/test_xdp_pifo.c
new file mode 100644
index 00000000000000..f23fc2fd75bd14
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_xdp_pifo.c
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/bpf.h>
+#include <linux/if_ether.h>
+#include <bpf/bpf_helpers.h>
+
+struct {
+ __uint(type, BPF_MAP_TYPE_PIFO_XDP);
+ __uint(key_size, sizeof(__u32));
+ __uint(value_size, sizeof(__u32));
+ __uint(max_entries, 1024);
+ __uint(map_extra, 8192); /* range */
+} pifo_map SEC(".maps");
+
+__u16 prio = 3;
+
+SEC("xdp")
+int xdp_pifo(struct xdp_md *xdp)
+{
+ void *data = (void *)(long)xdp->data;
+ void *data_end = (void *)(long)xdp->data_end;
+ struct ethhdr *eth = data;
+
+ if (eth + 1 > data_end)
+ return XDP_DROP;
+
+ /* We write the priority into the ethernet proto field so userspace can
+ * pick it back out and confirm that it's correct
+ */
+ eth->h_proto = --prio;
+ return bpf_redirect_map(&pifo_map, prio, 0);
+}
+
+SEC("xdp")
+int xdp_pifo_inc(struct xdp_md *xdp)
+{
+ void *data = (void *)(long)xdp->data;
+ void *data_end = (void *)(long)xdp->data_end;
+ struct ethhdr *eth = data;
+ int ret;
+
+ if (eth + 1 > data_end)
+ return XDP_DROP;
+
+ /* We write the priority into the ethernet proto field so userspace can
+ * pick it back out and confirm that it's correct
+ */
+ eth->h_proto = prio;
+ ret = bpf_redirect_map(&pifo_map, prio, 0);
+ prio += 10;
+ return ret;
+}
+
+__u16 pkt_count = 0;
+__u16 drop_above = 2;
+
+SEC("dequeue")
+void *dequeue_pifo(struct dequeue_ctx *ctx)
+{
+ __u64 prio = 0, pkt_prio = 0;
+ void *data, *data_end;
+ struct xdp_md *pkt;
+ struct ethhdr *eth;
+
+ pkt = (void *)bpf_packet_dequeue(ctx, &pifo_map, 0, &prio);
+ if (!pkt)
+ return NULL;
+
+ data = (void *)(long)pkt->data;
+ data_end = (void *)(long)pkt->data_end;
+ eth = data;
+
+ if (eth + 1 <= data_end)
+ pkt_prio = eth->h_proto;
+
+ if (pkt_prio != prio || ++pkt_count > drop_above) {
+ bpf_packet_drop(ctx, pkt);
+ return NULL;
+ }
+
+ return pkt;
+}
+
+char _license[] SEC("license") = "GPL";