Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/bpf: Test load and dump metadata with btftool and skel

This is a simple test to check that loading and dumping metadata
in btftool works, whether or not metadata contents are used by the
program.

A C test is also added to make sure the skeleton code can read the
metadata values.

Signed-off-by: YiFei Zhu <zhuyifei@google.com>
Signed-off-by: Stanislav Fomichev <sdf@google.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Andrii Nakryiko <andriin@fb.com>
Cc: YiFei Zhu <zhuyifei1999@gmail.com>
Link: https://lore.kernel.org/bpf/20200915234543.3220146-6-sdf@google.com

authored by

YiFei Zhu and committed by
Alexei Starovoitov
d42d1cc4 aff52e68

+255 -1
+2 -1
tools/testing/selftests/bpf/Makefile
··· 68 68 test_tc_edt.sh \ 69 69 test_xdping.sh \ 70 70 test_bpftool_build.sh \ 71 - test_bpftool.sh 71 + test_bpftool.sh \ 72 + test_bpftool_metadata.sh \ 72 73 73 74 TEST_PROGS_EXTENDED := with_addr.sh \ 74 75 with_tunnels.sh \
+141
tools/testing/selftests/bpf/prog_tests/metadata.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + 3 + /* 4 + * Copyright 2020 Google LLC. 5 + */ 6 + 7 + #include <test_progs.h> 8 + #include <cgroup_helpers.h> 9 + #include <network_helpers.h> 10 + 11 + #include "metadata_unused.skel.h" 12 + #include "metadata_used.skel.h" 13 + 14 + static int duration; 15 + 16 + static int prog_holds_map(int prog_fd, int map_fd) 17 + { 18 + struct bpf_prog_info prog_info = {}; 19 + struct bpf_prog_info map_info = {}; 20 + __u32 prog_info_len; 21 + __u32 map_info_len; 22 + __u32 *map_ids; 23 + int nr_maps; 24 + int ret; 25 + int i; 26 + 27 + map_info_len = sizeof(map_info); 28 + ret = bpf_obj_get_info_by_fd(map_fd, &map_info, &map_info_len); 29 + if (ret) 30 + return -errno; 31 + 32 + prog_info_len = sizeof(prog_info); 33 + ret = bpf_obj_get_info_by_fd(prog_fd, &prog_info, &prog_info_len); 34 + if (ret) 35 + return -errno; 36 + 37 + map_ids = calloc(prog_info.nr_map_ids, sizeof(__u32)); 38 + if (!map_ids) 39 + return -ENOMEM; 40 + 41 + nr_maps = prog_info.nr_map_ids; 42 + memset(&prog_info, 0, sizeof(prog_info)); 43 + prog_info.nr_map_ids = nr_maps; 44 + prog_info.map_ids = ptr_to_u64(map_ids); 45 + prog_info_len = sizeof(prog_info); 46 + 47 + ret = bpf_obj_get_info_by_fd(prog_fd, &prog_info, &prog_info_len); 48 + if (ret) { 49 + ret = -errno; 50 + goto free_map_ids; 51 + } 52 + 53 + ret = -ENOENT; 54 + for (i = 0; i < prog_info.nr_map_ids; i++) { 55 + if (map_ids[i] == map_info.id) { 56 + ret = 0; 57 + break; 58 + } 59 + } 60 + 61 + free_map_ids: 62 + free(map_ids); 63 + return ret; 64 + } 65 + 66 + static void test_metadata_unused(void) 67 + { 68 + struct metadata_unused *obj; 69 + int err; 70 + 71 + obj = metadata_unused__open_and_load(); 72 + if (CHECK(!obj, "skel-load", "errno %d", errno)) 73 + return; 74 + 75 + err = prog_holds_map(bpf_program__fd(obj->progs.prog), 76 + bpf_map__fd(obj->maps.rodata)); 77 + if (CHECK(err, "prog-holds-rodata", "errno: %d", err)) 78 + return; 79 + 80 + /* Assert that we can access the metadata in skel and the values are 81 + * what we expect. 82 + */ 83 + if (CHECK(strncmp(obj->rodata->bpf_metadata_a, "foo", 84 + sizeof(obj->rodata->bpf_metadata_a)), 85 + "bpf_metadata_a", "expected \"foo\", value differ")) 86 + goto close_bpf_object; 87 + if (CHECK(obj->rodata->bpf_metadata_b != 1, "bpf_metadata_b", 88 + "expected 1, got %d", obj->rodata->bpf_metadata_b)) 89 + goto close_bpf_object; 90 + 91 + /* Assert that binding metadata map to prog again succeeds. */ 92 + err = bpf_prog_bind_map(bpf_program__fd(obj->progs.prog), 93 + bpf_map__fd(obj->maps.rodata), NULL); 94 + CHECK(err, "rebind_map", "errno %d, expected 0", errno); 95 + 96 + close_bpf_object: 97 + metadata_unused__destroy(obj); 98 + } 99 + 100 + static void test_metadata_used(void) 101 + { 102 + struct metadata_used *obj; 103 + int err; 104 + 105 + obj = metadata_used__open_and_load(); 106 + if (CHECK(!obj, "skel-load", "errno %d", errno)) 107 + return; 108 + 109 + err = prog_holds_map(bpf_program__fd(obj->progs.prog), 110 + bpf_map__fd(obj->maps.rodata)); 111 + if (CHECK(err, "prog-holds-rodata", "errno: %d", err)) 112 + return; 113 + 114 + /* Assert that we can access the metadata in skel and the values are 115 + * what we expect. 116 + */ 117 + if (CHECK(strncmp(obj->rodata->bpf_metadata_a, "bar", 118 + sizeof(obj->rodata->bpf_metadata_a)), 119 + "metadata_a", "expected \"bar\", value differ")) 120 + goto close_bpf_object; 121 + if (CHECK(obj->rodata->bpf_metadata_b != 2, "metadata_b", 122 + "expected 2, got %d", obj->rodata->bpf_metadata_b)) 123 + goto close_bpf_object; 124 + 125 + /* Assert that binding metadata map to prog again succeeds. */ 126 + err = bpf_prog_bind_map(bpf_program__fd(obj->progs.prog), 127 + bpf_map__fd(obj->maps.rodata), NULL); 128 + CHECK(err, "rebind_map", "errno %d, expected 0", errno); 129 + 130 + close_bpf_object: 131 + metadata_used__destroy(obj); 132 + } 133 + 134 + void test_metadata(void) 135 + { 136 + if (test__start_subtest("unused")) 137 + test_metadata_unused(); 138 + 139 + if (test__start_subtest("used")) 140 + test_metadata_used(); 141 + }
+15
tools/testing/selftests/bpf/progs/metadata_unused.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + 3 + #include <linux/bpf.h> 4 + #include <bpf/bpf_helpers.h> 5 + 6 + volatile const char bpf_metadata_a[] SEC(".rodata") = "foo"; 7 + volatile const int bpf_metadata_b SEC(".rodata") = 1; 8 + 9 + SEC("cgroup_skb/egress") 10 + int prog(struct xdp_md *ctx) 11 + { 12 + return 0; 13 + } 14 + 15 + char _license[] SEC("license") = "GPL";
+15
tools/testing/selftests/bpf/progs/metadata_used.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + 3 + #include <linux/bpf.h> 4 + #include <bpf/bpf_helpers.h> 5 + 6 + volatile const char bpf_metadata_a[] SEC(".rodata") = "bar"; 7 + volatile const int bpf_metadata_b SEC(".rodata") = 2; 8 + 9 + SEC("cgroup_skb/egress") 10 + int prog(struct xdp_md *ctx) 11 + { 12 + return bpf_metadata_b ? 1 : 0; 13 + } 14 + 15 + char _license[] SEC("license") = "GPL";
+82
tools/testing/selftests/bpf/test_bpftool_metadata.sh
··· 1 + #!/bin/sh 2 + # SPDX-License-Identifier: GPL-2.0 3 + 4 + # Kselftest framework requirement - SKIP code is 4. 5 + ksft_skip=4 6 + 7 + TESTNAME=bpftool_metadata 8 + BPF_FS=$(awk '$3 == "bpf" {print $2; exit}' /proc/mounts) 9 + BPF_DIR=$BPF_FS/test_$TESTNAME 10 + 11 + _cleanup() 12 + { 13 + set +e 14 + rm -rf $BPF_DIR 2> /dev/null 15 + } 16 + 17 + cleanup_skip() 18 + { 19 + echo "selftests: $TESTNAME [SKIP]" 20 + _cleanup 21 + 22 + exit $ksft_skip 23 + } 24 + 25 + cleanup() 26 + { 27 + if [ "$?" = 0 ]; then 28 + echo "selftests: $TESTNAME [PASS]" 29 + else 30 + echo "selftests: $TESTNAME [FAILED]" 31 + fi 32 + _cleanup 33 + } 34 + 35 + if [ $(id -u) -ne 0 ]; then 36 + echo "selftests: $TESTNAME [SKIP] Need root privileges" 37 + exit $ksft_skip 38 + fi 39 + 40 + if [ -z "$BPF_FS" ]; then 41 + echo "selftests: $TESTNAME [SKIP] Could not run test without bpffs mounted" 42 + exit $ksft_skip 43 + fi 44 + 45 + if ! bpftool version > /dev/null 2>&1; then 46 + echo "selftests: $TESTNAME [SKIP] Could not run test without bpftool" 47 + exit $ksft_skip 48 + fi 49 + 50 + set -e 51 + 52 + trap cleanup_skip EXIT 53 + 54 + mkdir $BPF_DIR 55 + 56 + trap cleanup EXIT 57 + 58 + bpftool prog load metadata_unused.o $BPF_DIR/unused 59 + 60 + METADATA_PLAIN="$(bpftool prog)" 61 + echo "$METADATA_PLAIN" | grep 'a = "foo"' > /dev/null 62 + echo "$METADATA_PLAIN" | grep 'b = 1' > /dev/null 63 + 64 + bpftool prog --json | grep '"metadata":{"a":"foo","b":1}' > /dev/null 65 + 66 + bpftool map | grep 'metadata.rodata' > /dev/null 67 + 68 + rm $BPF_DIR/unused 69 + 70 + bpftool prog load metadata_used.o $BPF_DIR/used 71 + 72 + METADATA_PLAIN="$(bpftool prog)" 73 + echo "$METADATA_PLAIN" | grep 'a = "bar"' > /dev/null 74 + echo "$METADATA_PLAIN" | grep 'b = 2' > /dev/null 75 + 76 + bpftool prog --json | grep '"metadata":{"a":"bar","b":2}' > /dev/null 77 + 78 + bpftool map | grep 'metadata.rodata' > /dev/null 79 + 80 + rm $BPF_DIR/used 81 + 82 + exit 0