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

selftests/bpf: Test outer map update operations in syscall program

Syscall program is running with rcu_read_lock_trace being held, so if
bpf_map_update_elem() or bpf_map_delete_elem() invokes
synchronize_rcu_tasks_trace() when operating on an outer map, there will
be dead-lock, so add a test to guarantee that it is dead-lock free.

Signed-off-by: Hou Tao <houtao1@huawei.com>
Link: https://lore.kernel.org/r/20231204140425.1480317-8-houtao@huaweicloud.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Hou Tao and committed by
Alexei Starovoitov
e3dd4082 1624918b

+119 -7
+28 -2
tools/testing/selftests/bpf/prog_tests/syscall.c
··· 12 12 int btf_fd; 13 13 }; 14 14 15 - void test_syscall(void) 15 + static void test_syscall_load_prog(void) 16 16 { 17 17 static char verifier_log[8192]; 18 18 struct args ctx = { ··· 32 32 if (!ASSERT_OK_PTR(skel, "skel_load")) 33 33 goto cleanup; 34 34 35 - prog_fd = bpf_program__fd(skel->progs.bpf_prog); 35 + prog_fd = bpf_program__fd(skel->progs.load_prog); 36 36 err = bpf_prog_test_run_opts(prog_fd, &tattr); 37 37 ASSERT_EQ(err, 0, "err"); 38 38 ASSERT_EQ(tattr.retval, 1, "retval"); ··· 52 52 close(ctx.map_fd); 53 53 if (ctx.btf_fd > 0) 54 54 close(ctx.btf_fd); 55 + } 56 + 57 + static void test_syscall_update_outer_map(void) 58 + { 59 + LIBBPF_OPTS(bpf_test_run_opts, opts); 60 + struct syscall *skel; 61 + int err, prog_fd; 62 + 63 + skel = syscall__open_and_load(); 64 + if (!ASSERT_OK_PTR(skel, "skel_load")) 65 + goto cleanup; 66 + 67 + prog_fd = bpf_program__fd(skel->progs.update_outer_map); 68 + err = bpf_prog_test_run_opts(prog_fd, &opts); 69 + ASSERT_EQ(err, 0, "err"); 70 + ASSERT_EQ(opts.retval, 1, "retval"); 71 + cleanup: 72 + syscall__destroy(skel); 73 + } 74 + 75 + void test_syscall(void) 76 + { 77 + if (test__start_subtest("load_prog")) 78 + test_syscall_load_prog(); 79 + if (test__start_subtest("update_outer_map")) 80 + test_syscall_update_outer_map(); 55 81 }
+91 -5
tools/testing/selftests/bpf/progs/syscall.c
··· 6 6 #include <bpf/bpf_tracing.h> 7 7 #include <../../../tools/include/linux/filter.h> 8 8 #include <linux/btf.h> 9 + #include <string.h> 10 + #include <errno.h> 9 11 10 12 char _license[] SEC("license") = "GPL"; 13 + 14 + struct bpf_map { 15 + int id; 16 + } __attribute__((preserve_access_index)); 11 17 12 18 struct args { 13 19 __u64 log_buf; ··· 32 26 #define BTF_TYPE_INT_ENC(name, encoding, bits_offset, bits, sz) \ 33 27 BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_INT, 0, 0), sz), \ 34 28 BTF_INT_ENC(encoding, bits_offset, bits) 29 + 30 + struct { 31 + __uint(type, BPF_MAP_TYPE_ARRAY); 32 + __type(key, int); 33 + __type(value, union bpf_attr); 34 + __uint(max_entries, 1); 35 + } bpf_attr_array SEC(".maps"); 36 + 37 + struct inner_map_type { 38 + __uint(type, BPF_MAP_TYPE_ARRAY); 39 + __uint(key_size, 4); 40 + __uint(value_size, 4); 41 + __uint(max_entries, 1); 42 + } inner_map SEC(".maps"); 43 + 44 + struct { 45 + __uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS); 46 + __type(key, int); 47 + __type(value, int); 48 + __uint(max_entries, 1); 49 + __array(values, struct inner_map_type); 50 + } outer_array_map SEC(".maps") = { 51 + .values = { 52 + [0] = &inner_map, 53 + }, 54 + }; 55 + 56 + static inline __u64 ptr_to_u64(const void *ptr) 57 + { 58 + return (__u64) (unsigned long) ptr; 59 + } 35 60 36 61 static int btf_load(void) 37 62 { ··· 95 58 } 96 59 97 60 SEC("syscall") 98 - int bpf_prog(struct args *ctx) 61 + int load_prog(struct args *ctx) 99 62 { 100 63 static char license[] = "GPL"; 101 64 static struct bpf_insn insns[] = { ··· 131 94 map_create_attr.max_entries = ctx->max_entries; 132 95 map_create_attr.btf_fd = ret; 133 96 134 - prog_load_attr.license = (long) license; 135 - prog_load_attr.insns = (long) insns; 97 + prog_load_attr.license = ptr_to_u64(license); 98 + prog_load_attr.insns = ptr_to_u64(insns); 136 99 prog_load_attr.log_buf = ctx->log_buf; 137 100 prog_load_attr.log_size = ctx->log_size; 138 101 prog_load_attr.log_level = 1; ··· 144 107 insns[3].imm = ret; 145 108 146 109 map_update_attr.map_fd = ret; 147 - map_update_attr.key = (long) &key; 148 - map_update_attr.value = (long) &value; 110 + map_update_attr.key = ptr_to_u64(&key); 111 + map_update_attr.value = ptr_to_u64(&value); 149 112 ret = bpf_sys_bpf(BPF_MAP_UPDATE_ELEM, &map_update_attr, sizeof(map_update_attr)); 150 113 if (ret < 0) 151 114 return ret; ··· 155 118 return ret; 156 119 ctx->prog_fd = ret; 157 120 return 1; 121 + } 122 + 123 + SEC("syscall") 124 + int update_outer_map(void *ctx) 125 + { 126 + int zero = 0, ret = 0, outer_fd = -1, inner_fd = -1, err; 127 + const int attr_sz = sizeof(union bpf_attr); 128 + union bpf_attr *attr; 129 + 130 + attr = bpf_map_lookup_elem((struct bpf_map *)&bpf_attr_array, &zero); 131 + if (!attr) 132 + goto out; 133 + 134 + memset(attr, 0, attr_sz); 135 + attr->map_id = ((struct bpf_map *)&outer_array_map)->id; 136 + outer_fd = bpf_sys_bpf(BPF_MAP_GET_FD_BY_ID, attr, attr_sz); 137 + if (outer_fd < 0) 138 + goto out; 139 + 140 + memset(attr, 0, attr_sz); 141 + attr->map_type = BPF_MAP_TYPE_ARRAY; 142 + attr->key_size = 4; 143 + attr->value_size = 4; 144 + attr->max_entries = 1; 145 + inner_fd = bpf_sys_bpf(BPF_MAP_CREATE, attr, attr_sz); 146 + if (inner_fd < 0) 147 + goto out; 148 + 149 + memset(attr, 0, attr_sz); 150 + attr->map_fd = outer_fd; 151 + attr->key = ptr_to_u64(&zero); 152 + attr->value = ptr_to_u64(&inner_fd); 153 + err = bpf_sys_bpf(BPF_MAP_UPDATE_ELEM, attr, attr_sz); 154 + if (err) 155 + goto out; 156 + 157 + memset(attr, 0, attr_sz); 158 + attr->map_fd = outer_fd; 159 + attr->key = ptr_to_u64(&zero); 160 + err = bpf_sys_bpf(BPF_MAP_DELETE_ELEM, attr, attr_sz); 161 + if (err) 162 + goto out; 163 + ret = 1; 164 + out: 165 + if (inner_fd >= 0) 166 + bpf_sys_close(inner_fd); 167 + if (outer_fd >= 0) 168 + bpf_sys_close(outer_fd); 169 + return ret; 158 170 }