selftests: Add test for multiple attachments of freplace program
authorToke Høiland-Jørgensen <toke@redhat.com>
Tue, 29 Sep 2020 12:45:54 +0000 (14:45 +0200)
committerAlexei Starovoitov <ast@kernel.org>
Tue, 29 Sep 2020 20:09:24 +0000 (13:09 -0700)
This adds a selftest for attaching an freplace program to multiple targets
simultaneously.

Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Andrii Nakryiko <andriin@fb.com>
Link: https://lore.kernel.org/bpf/160138355497.48470.17568077161540217107.stgit@toke.dk
tools/testing/selftests/bpf/prog_tests/fexit_bpf2bpf.c
tools/testing/selftests/bpf/progs/freplace_get_constant.c [new file with mode: 0644]

index eda6827..2b94e82 100644 (file)
@@ -2,36 +2,79 @@
 /* Copyright (c) 2019 Facebook */
 #include <test_progs.h>
 #include <network_helpers.h>
+#include <bpf/btf.h>
+
+typedef int (*test_cb)(struct bpf_object *obj);
+
+static int check_data_map(struct bpf_object *obj, int prog_cnt, bool reset)
+{
+       struct bpf_map *data_map = NULL, *map;
+       __u64 *result = NULL;
+       const int zero = 0;
+       __u32 duration = 0;
+       int ret = -1, i;
+
+       result = malloc((prog_cnt + 32 /* spare */) * sizeof(__u64));
+       if (CHECK(!result, "alloc_memory", "failed to alloc memory"))
+               return -ENOMEM;
+
+       bpf_object__for_each_map(map, obj)
+               if (bpf_map__is_internal(map)) {
+                       data_map = map;
+                       break;
+               }
+       if (CHECK(!data_map, "find_data_map", "data map not found\n"))
+               goto out;
+
+       ret = bpf_map_lookup_elem(bpf_map__fd(data_map), &zero, result);
+       if (CHECK(ret, "get_result",
+                 "failed to get output data: %d\n", ret))
+               goto out;
+
+       for (i = 0; i < prog_cnt; i++) {
+               if (CHECK(result[i] != 1, "result",
+                         "fexit_bpf2bpf result[%d] failed err %llu\n",
+                         i, result[i]))
+                       goto out;
+               result[i] = 0;
+       }
+       if (reset) {
+               ret = bpf_map_update_elem(bpf_map__fd(data_map), &zero, result, 0);
+               if (CHECK(ret, "reset_result", "failed to reset result\n"))
+                       goto out;
+       }
+
+       ret = 0;
+out:
+       free(result);
+       return ret;
+}
 
 static void test_fexit_bpf2bpf_common(const char *obj_file,
                                      const char *target_obj_file,
                                      int prog_cnt,
                                      const char **prog_name,
-                                     bool run_prog)
+                                     bool run_prog,
+                                     test_cb cb)
 {
-       struct bpf_object *obj = NULL, *pkt_obj;
-       int err, pkt_fd, i;
-       struct bpf_link **link = NULL;
+       struct bpf_object *obj = NULL, *tgt_obj;
        struct bpf_program **prog = NULL;
+       struct bpf_link **link = NULL;
        __u32 duration = 0, retval;
-       struct bpf_map *data_map;
-       const int zero = 0;
-       __u64 *result = NULL;
+       int err, tgt_fd, i;
 
        err = bpf_prog_load(target_obj_file, BPF_PROG_TYPE_UNSPEC,
-                           &pkt_obj, &pkt_fd);
+                           &tgt_obj, &tgt_fd);
        if (CHECK(err, "tgt_prog_load", "file %s err %d errno %d\n",
                  target_obj_file, err, errno))
                return;
        DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts,
-                           .attach_prog_fd = pkt_fd,
+                           .attach_prog_fd = tgt_fd,
                           );
 
        link = calloc(sizeof(struct bpf_link *), prog_cnt);
        prog = calloc(sizeof(struct bpf_program *), prog_cnt);
-       result = malloc((prog_cnt + 32 /* spare */) * sizeof(__u64));
-       if (CHECK(!link || !prog || !result, "alloc_memory",
-                 "failed to alloc memory"))
+       if (CHECK(!link || !prog, "alloc_memory", "failed to alloc memory"))
                goto close_prog;
 
        obj = bpf_object__open_file(obj_file, &opts);
@@ -53,39 +96,33 @@ static void test_fexit_bpf2bpf_common(const char *obj_file,
                        goto close_prog;
        }
 
-       if (!run_prog)
-               goto close_prog;
+       if (cb) {
+               err = cb(obj);
+               if (err)
+                       goto close_prog;
+       }
 
-       data_map = bpf_object__find_map_by_name(obj, "fexit_bp.bss");
-       if (CHECK(!data_map, "find_data_map", "data map not found\n"))
+       if (!run_prog)
                goto close_prog;
 
-       err = bpf_prog_test_run(pkt_fd, 1, &pkt_v6, sizeof(pkt_v6),
+       err = bpf_prog_test_run(tgt_fd, 1, &pkt_v6, sizeof(pkt_v6),
                                NULL, NULL, &retval, &duration);
        CHECK(err || retval, "ipv6",
              "err %d errno %d retval %d duration %d\n",
              err, errno, retval, duration);
 
-       err = bpf_map_lookup_elem(bpf_map__fd(data_map), &zero, result);
-       if (CHECK(err, "get_result",
-                 "failed to get output data: %d\n", err))
+       if (check_data_map(obj, prog_cnt, false))
                goto close_prog;
 
-       for (i = 0; i < prog_cnt; i++)
-               if (CHECK(result[i] != 1, "result", "fexit_bpf2bpf failed err %llu\n",
-                         result[i]))
-                       goto close_prog;
-
 close_prog:
        for (i = 0; i < prog_cnt; i++)
                if (!IS_ERR_OR_NULL(link[i]))
                        bpf_link__destroy(link[i]);
        if (!IS_ERR_OR_NULL(obj))
                bpf_object__close(obj);
-       bpf_object__close(pkt_obj);
+       bpf_object__close(tgt_obj);
        free(link);
        free(prog);
-       free(result);
 }
 
 static void test_target_no_callees(void)
@@ -96,7 +133,7 @@ static void test_target_no_callees(void)
        test_fexit_bpf2bpf_common("./fexit_bpf2bpf_simple.o",
                                  "./test_pkt_md_access.o",
                                  ARRAY_SIZE(prog_name),
-                                 prog_name, true);
+                                 prog_name, true, NULL);
 }
 
 static void test_target_yes_callees(void)
@@ -110,7 +147,7 @@ static void test_target_yes_callees(void)
        test_fexit_bpf2bpf_common("./fexit_bpf2bpf.o",
                                  "./test_pkt_access.o",
                                  ARRAY_SIZE(prog_name),
-                                 prog_name, true);
+                                 prog_name, true, NULL);
 }
 
 static void test_func_replace(void)
@@ -128,7 +165,7 @@ static void test_func_replace(void)
        test_fexit_bpf2bpf_common("./fexit_bpf2bpf.o",
                                  "./test_pkt_access.o",
                                  ARRAY_SIZE(prog_name),
-                                 prog_name, true);
+                                 prog_name, true, NULL);
 }
 
 static void test_func_replace_verify(void)
@@ -139,7 +176,60 @@ static void test_func_replace_verify(void)
        test_fexit_bpf2bpf_common("./freplace_connect4.o",
                                  "./connect4_prog.o",
                                  ARRAY_SIZE(prog_name),
-                                 prog_name, false);
+                                 prog_name, false, NULL);
+}
+
+static int test_second_attach(struct bpf_object *obj)
+{
+       const char *prog_name = "freplace/get_constant";
+       const char *tgt_name = prog_name + 9; /* cut off freplace/ */
+       const char *tgt_obj_file = "./test_pkt_access.o";
+       struct bpf_program *prog = NULL;
+       struct bpf_object *tgt_obj;
+       __u32 duration = 0, retval;
+       struct bpf_link *link;
+       int err = 0, tgt_fd;
+
+       prog = bpf_object__find_program_by_title(obj, prog_name);
+       if (CHECK(!prog, "find_prog", "prog %s not found\n", prog_name))
+               return -ENOENT;
+
+       err = bpf_prog_load(tgt_obj_file, BPF_PROG_TYPE_UNSPEC,
+                           &tgt_obj, &tgt_fd);
+       if (CHECK(err, "second_prog_load", "file %s err %d errno %d\n",
+                 tgt_obj_file, err, errno))
+               return err;
+
+       link = bpf_program__attach_freplace(prog, tgt_fd, tgt_name);
+       if (CHECK(IS_ERR(link), "second_link", "failed to attach second link prog_fd %d tgt_fd %d\n", bpf_program__fd(prog), tgt_fd))
+               goto out;
+
+       err = bpf_prog_test_run(tgt_fd, 1, &pkt_v6, sizeof(pkt_v6),
+                               NULL, NULL, &retval, &duration);
+       if (CHECK(err || retval, "ipv6",
+                 "err %d errno %d retval %d duration %d\n",
+                 err, errno, retval, duration))
+               goto out;
+
+       err = check_data_map(obj, 1, true);
+       if (err)
+               goto out;
+
+out:
+       bpf_link__destroy(link);
+       bpf_object__close(tgt_obj);
+       return err;
+}
+
+static void test_func_replace_multi(void)
+{
+       const char *prog_name[] = {
+               "freplace/get_constant",
+       };
+       test_fexit_bpf2bpf_common("./freplace_get_constant.o",
+                                 "./test_pkt_access.o",
+                                 ARRAY_SIZE(prog_name),
+                                 prog_name, true, test_second_attach);
 }
 
 static void test_func_sockmap_update(void)
@@ -150,7 +240,7 @@ static void test_func_sockmap_update(void)
        test_fexit_bpf2bpf_common("./freplace_cls_redirect.o",
                                  "./test_cls_redirect.o",
                                  ARRAY_SIZE(prog_name),
-                                 prog_name, false);
+                                 prog_name, false, NULL);
 }
 
 static void test_obj_load_failure_common(const char *obj_file,
@@ -222,4 +312,6 @@ void test_fexit_bpf2bpf(void)
                test_func_replace_return_code();
        if (test__start_subtest("func_map_prog_compatibility"))
                test_func_map_prog_compatibility();
+       if (test__start_subtest("func_replace_multi"))
+               test_func_replace_multi();
 }
diff --git a/tools/testing/selftests/bpf/progs/freplace_get_constant.c b/tools/testing/selftests/bpf/progs/freplace_get_constant.c
new file mode 100644 (file)
index 0000000..8f0ecf9
--- /dev/null
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_endian.h>
+
+volatile __u64 test_get_constant = 0;
+SEC("freplace/get_constant")
+int new_get_constant(long val)
+{
+       if (val != 123)
+               return 0;
+       test_get_constant = 1;
+       return test_get_constant; /* original get_constant() returns val - 122 */
+}
+char _license[] SEC("license") = "GPL";