selftests/bpf: Check inner map deletion
authorMartynas Pumputis <m@lambda.lt>
Mon, 19 Jul 2021 17:38:38 +0000 (19:38 +0200)
committerAndrii Nakryiko <andrii@kernel.org>
Mon, 19 Jul 2021 22:58:07 +0000 (15:58 -0700)
Add a test case to check whether an unsuccessful creation of an outer
map of a BTF-defined map-in-map destroys the inner map.

As bpf_object__create_map() is a static function, we cannot just call it
from the test case and then check whether a map accessible via
map->inner_map_fd has been closed. Instead, we iterate over all maps and
check whether the map "$MAP_NAME.inner" does not exist.

Signed-off-by: Martynas Pumputis <m@lambda.lt>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Acked-by: John Fastabend <john.fastabend@gmail.com>
Link: https://lore.kernel.org/bpf/20210719173838.423148-3-m@lambda.lt
tools/testing/selftests/bpf/progs/test_map_in_map_invalid.c [new file with mode: 0644]
tools/testing/selftests/bpf/test_maps.c

diff --git a/tools/testing/selftests/bpf/progs/test_map_in_map_invalid.c b/tools/testing/selftests/bpf/progs/test_map_in_map_invalid.c
new file mode 100644 (file)
index 0000000..703c08e
--- /dev/null
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2021 Isovalent, Inc. */
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+
+struct inner {
+       __uint(type, BPF_MAP_TYPE_ARRAY);
+       __type(key, __u32);
+       __type(value, int);
+       __uint(max_entries, 4);
+};
+
+struct {
+       __uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
+       __uint(max_entries, 0); /* This will make map creation to fail */
+       __uint(key_size, sizeof(__u32));
+       __array(values, struct inner);
+} mim SEC(".maps");
+
+SEC("xdp")
+int xdp_noop0(struct xdp_md *ctx)
+{
+       return XDP_PASS;
+}
+
+char _license[] SEC("license") = "GPL";
index 30cbf5d..d832d13 100644 (file)
@@ -1153,12 +1153,16 @@ out_sockmap:
 }
 
 #define MAPINMAP_PROG "./test_map_in_map.o"
+#define MAPINMAP_INVALID_PROG "./test_map_in_map_invalid.o"
 static void test_map_in_map(void)
 {
        struct bpf_object *obj;
        struct bpf_map *map;
        int mim_fd, fd, err;
        int pos = 0;
+       struct bpf_map_info info = {};
+       __u32 len = sizeof(info);
+       __u32 id = 0;
 
        obj = bpf_object__open(MAPINMAP_PROG);
 
@@ -1228,11 +1232,68 @@ static void test_map_in_map(void)
        }
 
        close(fd);
+       fd = -1;
        bpf_object__close(obj);
+
+       /* Test that failing bpf_object__create_map() destroys the inner map */
+       obj = bpf_object__open(MAPINMAP_INVALID_PROG);
+       err = libbpf_get_error(obj);
+       if (err) {
+               printf("Failed to load %s program: %d %d",
+                      MAPINMAP_INVALID_PROG, err, errno);
+               goto out_map_in_map;
+       }
+
+       map = bpf_object__find_map_by_name(obj, "mim");
+       if (!map) {
+               printf("Failed to load array of maps from test prog\n");
+               goto out_map_in_map;
+       }
+
+       err = bpf_object__load(obj);
+       if (!err) {
+               printf("Loading obj supposed to fail\n");
+               goto out_map_in_map;
+       }
+
+       /* Iterate over all maps to check whether the internal map
+        * ("mim.internal") has been destroyed.
+        */
+       while (true) {
+               err = bpf_map_get_next_id(id, &id);
+               if (err) {
+                       if (errno == ENOENT)
+                               break;
+                       printf("Failed to get next map: %d", errno);
+                       goto out_map_in_map;
+               }
+
+               fd = bpf_map_get_fd_by_id(id);
+               if (fd < 0) {
+                       if (errno == ENOENT)
+                               continue;
+                       printf("Failed to get map by id %u: %d", id, errno);
+                       goto out_map_in_map;
+               }
+
+               err = bpf_obj_get_info_by_fd(fd, &info, &len);
+               if (err) {
+                       printf("Failed to get map info by fd %d: %d", fd,
+                              errno);
+                       goto out_map_in_map;
+               }
+
+               if (!strcmp(info.name, "mim.inner")) {
+                       printf("Inner map mim.inner was not destroyed\n");
+                       goto out_map_in_map;
+               }
+       }
+
        return;
 
 out_map_in_map:
-       close(fd);
+       if (fd >= 0)
+               close(fd);
        exit(1);
 }