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

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'fix-lockdep-warning-for-htab-of-map'

Hou Tao says:

====================
The patch set fixes a lockdep warning for htab of map. The
warning is found when running test_maps. The warning occurs when
htab_put_fd_value() attempts to acquire map_idr_lock to free the map id
of the inner map while already holding the bucket lock (raw_spinlock_t).

The fix moves the invocation of free_htab_elem() after
htab_unlock_bucket() and adds a test case to verify the solution.
====================

Acked-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>

authored by

Alexei Starovoitov and committed by
Andrii Nakryiko
266a5579 269e7c97

+203 -38
+39 -17
kernel/bpf/hashtab.c
··· 896 896 static void htab_elem_free(struct bpf_htab *htab, struct htab_elem *l) 897 897 { 898 898 check_and_free_fields(htab, l); 899 + 900 + migrate_disable(); 899 901 if (htab->map.map_type == BPF_MAP_TYPE_PERCPU_HASH) 900 902 bpf_mem_cache_free(&htab->pcpu_ma, l->ptr_to_pptr); 901 903 bpf_mem_cache_free(&htab->ma, l); 904 + migrate_enable(); 902 905 } 903 906 904 907 static void htab_put_fd_value(struct bpf_htab *htab, struct htab_elem *l) ··· 951 948 if (htab_is_prealloc(htab)) { 952 949 bpf_map_dec_elem_count(&htab->map); 953 950 check_and_free_fields(htab, l); 954 - __pcpu_freelist_push(&htab->freelist, &l->fnode); 951 + pcpu_freelist_push(&htab->freelist, &l->fnode); 955 952 } else { 956 953 dec_elem_count(htab); 957 954 htab_elem_free(htab, l); ··· 1021 1018 */ 1022 1019 pl_new = this_cpu_ptr(htab->extra_elems); 1023 1020 l_new = *pl_new; 1024 - htab_put_fd_value(htab, old_elem); 1025 1021 *pl_new = old_elem; 1026 1022 } else { 1027 1023 struct pcpu_freelist_node *l; ··· 1107 1105 struct htab_elem *l_new = NULL, *l_old; 1108 1106 struct hlist_nulls_head *head; 1109 1107 unsigned long flags; 1108 + void *old_map_ptr; 1110 1109 struct bucket *b; 1111 1110 u32 key_size, hash; 1112 1111 int ret; ··· 1186 1183 hlist_nulls_add_head_rcu(&l_new->hash_node, head); 1187 1184 if (l_old) { 1188 1185 hlist_nulls_del_rcu(&l_old->hash_node); 1186 + 1187 + /* l_old has already been stashed in htab->extra_elems, free 1188 + * its special fields before it is available for reuse. Also 1189 + * save the old map pointer in htab of maps before unlock 1190 + * and release it after unlock. 1191 + */ 1192 + old_map_ptr = NULL; 1193 + if (htab_is_prealloc(htab)) { 1194 + if (map->ops->map_fd_put_ptr) 1195 + old_map_ptr = fd_htab_map_get_ptr(map, l_old); 1196 + check_and_free_fields(htab, l_old); 1197 + } 1198 + } 1199 + htab_unlock_bucket(htab, b, hash, flags); 1200 + if (l_old) { 1201 + if (old_map_ptr) 1202 + map->ops->map_fd_put_ptr(map, old_map_ptr, true); 1189 1203 if (!htab_is_prealloc(htab)) 1190 1204 free_htab_elem(htab, l_old); 1191 - else 1192 - check_and_free_fields(htab, l_old); 1193 1205 } 1194 - ret = 0; 1206 + return 0; 1195 1207 err: 1196 1208 htab_unlock_bucket(htab, b, hash, flags); 1197 1209 return ret; ··· 1450 1432 return ret; 1451 1433 1452 1434 l = lookup_elem_raw(head, hash, key, key_size); 1453 - 1454 - if (l) { 1435 + if (l) 1455 1436 hlist_nulls_del_rcu(&l->hash_node); 1456 - free_htab_elem(htab, l); 1457 - } else { 1437 + else 1458 1438 ret = -ENOENT; 1459 - } 1460 1439 1461 1440 htab_unlock_bucket(htab, b, hash, flags); 1441 + 1442 + if (l) 1443 + free_htab_elem(htab, l); 1462 1444 return ret; 1463 1445 } 1464 1446 ··· 1871 1853 * may cause deadlock. See comments in function 1872 1854 * prealloc_lru_pop(). Let us do bpf_lru_push_free() 1873 1855 * after releasing the bucket lock. 1856 + * 1857 + * For htab of maps, htab_put_fd_value() in 1858 + * free_htab_elem() may acquire a spinlock with bucket 1859 + * lock being held and it violates the lock rule, so 1860 + * invoke free_htab_elem() after unlock as well. 1874 1861 */ 1875 - if (is_lru_map) { 1876 - l->batch_flink = node_to_free; 1877 - node_to_free = l; 1878 - } else { 1879 - free_htab_elem(htab, l); 1880 - } 1862 + l->batch_flink = node_to_free; 1863 + node_to_free = l; 1881 1864 } 1882 1865 dst_key += key_size; 1883 1866 dst_val += value_size; ··· 1890 1871 while (node_to_free) { 1891 1872 l = node_to_free; 1892 1873 node_to_free = node_to_free->batch_flink; 1893 - htab_lru_push_free(htab, l); 1874 + if (is_lru_map) 1875 + htab_lru_push_free(htab, l); 1876 + else 1877 + free_htab_elem(htab, l); 1894 1878 } 1895 1879 1896 1880 next_batch:
+3
tools/testing/selftests/bpf/bpf_util.h
··· 67 67 #define sys_gettid() syscall(SYS_gettid) 68 68 #endif 69 69 70 + #ifndef ENOTSUPP 71 + #define ENOTSUPP 524 72 + #endif 70 73 71 74 #endif /* __BPF_UTIL__ */
-4
tools/testing/selftests/bpf/prog_tests/bpf_tcp_ca.c
··· 16 16 #include "tcp_ca_kfunc.skel.h" 17 17 #include "bpf_cc_cubic.skel.h" 18 18 19 - #ifndef ENOTSUPP 20 - #define ENOTSUPP 524 21 - #endif 22 - 23 19 static const unsigned int total_bytes = 10 * 1024 * 1024; 24 20 static int expected_stg = 0xeB9F; 25 21
-4
tools/testing/selftests/bpf/prog_tests/lsm_cgroup.c
··· 10 10 #include "cgroup_helpers.h" 11 11 #include "network_helpers.h" 12 12 13 - #ifndef ENOTSUPP 14 - #define ENOTSUPP 524 15 - #endif 16 - 17 13 static struct btf *btf; 18 14 19 15 static __u32 query_prog_cnt(int cgroup_fd, const char *attach_func)
+131 -1
tools/testing/selftests/bpf/prog_tests/map_in_map.c
··· 5 5 #include <sys/syscall.h> 6 6 #include <test_progs.h> 7 7 #include <bpf/btf.h> 8 + 8 9 #include "access_map_in_map.skel.h" 10 + #include "update_map_in_htab.skel.h" 9 11 10 12 struct thread_ctx { 11 13 pthread_barrier_t barrier; ··· 129 127 access_map_in_map__destroy(skel); 130 128 } 131 129 130 + static void add_del_fd_htab(int outer_fd) 131 + { 132 + int inner_fd, err; 133 + int key = 1; 134 + 135 + inner_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "arr1", 4, 4, 1, NULL); 136 + if (!ASSERT_OK_FD(inner_fd, "inner1")) 137 + return; 138 + err = bpf_map_update_elem(outer_fd, &key, &inner_fd, BPF_NOEXIST); 139 + close(inner_fd); 140 + if (!ASSERT_OK(err, "add")) 141 + return; 142 + 143 + /* Delete */ 144 + err = bpf_map_delete_elem(outer_fd, &key); 145 + ASSERT_OK(err, "del"); 146 + } 147 + 148 + static void overwrite_fd_htab(int outer_fd) 149 + { 150 + int inner_fd, err; 151 + int key = 1; 152 + 153 + inner_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "arr1", 4, 4, 1, NULL); 154 + if (!ASSERT_OK_FD(inner_fd, "inner1")) 155 + return; 156 + err = bpf_map_update_elem(outer_fd, &key, &inner_fd, BPF_NOEXIST); 157 + close(inner_fd); 158 + if (!ASSERT_OK(err, "add")) 159 + return; 160 + 161 + /* Overwrite */ 162 + inner_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "arr2", 4, 4, 1, NULL); 163 + if (!ASSERT_OK_FD(inner_fd, "inner2")) 164 + goto out; 165 + err = bpf_map_update_elem(outer_fd, &key, &inner_fd, BPF_EXIST); 166 + close(inner_fd); 167 + if (!ASSERT_OK(err, "overwrite")) 168 + goto out; 169 + 170 + err = bpf_map_delete_elem(outer_fd, &key); 171 + ASSERT_OK(err, "del"); 172 + return; 173 + out: 174 + bpf_map_delete_elem(outer_fd, &key); 175 + } 176 + 177 + static void lookup_delete_fd_htab(int outer_fd) 178 + { 179 + int key = 1, value; 180 + int inner_fd, err; 181 + 182 + inner_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "arr1", 4, 4, 1, NULL); 183 + if (!ASSERT_OK_FD(inner_fd, "inner1")) 184 + return; 185 + err = bpf_map_update_elem(outer_fd, &key, &inner_fd, BPF_NOEXIST); 186 + close(inner_fd); 187 + if (!ASSERT_OK(err, "add")) 188 + return; 189 + 190 + /* lookup_and_delete is not supported for htab of maps */ 191 + err = bpf_map_lookup_and_delete_elem(outer_fd, &key, &value); 192 + ASSERT_EQ(err, -ENOTSUPP, "lookup_del"); 193 + 194 + err = bpf_map_delete_elem(outer_fd, &key); 195 + ASSERT_OK(err, "del"); 196 + } 197 + 198 + static void batched_lookup_delete_fd_htab(int outer_fd) 199 + { 200 + int keys[2] = {1, 2}, values[2]; 201 + unsigned int cnt, batch; 202 + int inner_fd, err; 203 + 204 + inner_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "arr1", 4, 4, 1, NULL); 205 + if (!ASSERT_OK_FD(inner_fd, "inner1")) 206 + return; 207 + 208 + err = bpf_map_update_elem(outer_fd, &keys[0], &inner_fd, BPF_NOEXIST); 209 + close(inner_fd); 210 + if (!ASSERT_OK(err, "add1")) 211 + return; 212 + 213 + inner_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "arr2", 4, 4, 1, NULL); 214 + if (!ASSERT_OK_FD(inner_fd, "inner2")) 215 + goto out; 216 + err = bpf_map_update_elem(outer_fd, &keys[1], &inner_fd, BPF_NOEXIST); 217 + close(inner_fd); 218 + if (!ASSERT_OK(err, "add2")) 219 + goto out; 220 + 221 + /* batched lookup_and_delete */ 222 + cnt = ARRAY_SIZE(keys); 223 + err = bpf_map_lookup_and_delete_batch(outer_fd, NULL, &batch, keys, values, &cnt, NULL); 224 + ASSERT_TRUE((!err || err == -ENOENT), "delete_batch ret"); 225 + ASSERT_EQ(cnt, ARRAY_SIZE(keys), "delete_batch cnt"); 226 + 227 + out: 228 + bpf_map_delete_elem(outer_fd, &keys[0]); 229 + } 230 + 231 + static void test_update_map_in_htab(bool preallocate) 232 + { 233 + struct update_map_in_htab *skel; 234 + int err, fd; 235 + 236 + skel = update_map_in_htab__open(); 237 + if (!ASSERT_OK_PTR(skel, "open")) 238 + return; 239 + 240 + err = update_map_in_htab__load(skel); 241 + if (!ASSERT_OK(err, "load")) 242 + goto out; 243 + 244 + fd = preallocate ? bpf_map__fd(skel->maps.outer_htab_map) : 245 + bpf_map__fd(skel->maps.outer_alloc_htab_map); 246 + 247 + add_del_fd_htab(fd); 248 + overwrite_fd_htab(fd); 249 + lookup_delete_fd_htab(fd); 250 + batched_lookup_delete_fd_htab(fd); 251 + out: 252 + update_map_in_htab__destroy(skel); 253 + } 254 + 132 255 void test_map_in_map(void) 133 256 { 134 257 if (test__start_subtest("acc_map_in_array")) ··· 264 137 test_map_in_map_access("access_map_in_htab", "outer_htab_map"); 265 138 if (test__start_subtest("sleepable_acc_map_in_htab")) 266 139 test_map_in_map_access("sleepable_access_map_in_htab", "outer_htab_map"); 140 + if (test__start_subtest("update_map_in_htab")) 141 + test_update_map_in_htab(true); 142 + if (test__start_subtest("update_map_in_alloc_htab")) 143 + test_update_map_in_htab(false); 267 144 } 268 -
-4
tools/testing/selftests/bpf/prog_tests/sock_addr.c
··· 23 23 #include "getpeername_unix_prog.skel.h" 24 24 #include "network_helpers.h" 25 25 26 - #ifndef ENOTSUPP 27 - # define ENOTSUPP 524 28 - #endif 29 - 30 26 #define TEST_NS "sock_addr" 31 27 #define TEST_IF_PREFIX "test_sock_addr" 32 28 #define TEST_IPV4 "127.0.0.4"
+30
tools/testing/selftests/bpf/progs/update_map_in_htab.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (C) 2024. Huawei Technologies Co., Ltd */ 3 + #include <linux/bpf.h> 4 + #include <bpf/bpf_helpers.h> 5 + 6 + struct inner_map_type { 7 + __uint(type, BPF_MAP_TYPE_ARRAY); 8 + __uint(key_size, 4); 9 + __uint(value_size, 4); 10 + __uint(max_entries, 1); 11 + } inner_map SEC(".maps"); 12 + 13 + struct { 14 + __uint(type, BPF_MAP_TYPE_HASH_OF_MAPS); 15 + __type(key, int); 16 + __type(value, int); 17 + __uint(max_entries, 2); 18 + __array(values, struct inner_map_type); 19 + } outer_htab_map SEC(".maps"); 20 + 21 + struct { 22 + __uint(type, BPF_MAP_TYPE_HASH_OF_MAPS); 23 + __uint(map_flags, BPF_F_NO_PREALLOC); 24 + __type(key, int); 25 + __type(value, int); 26 + __uint(max_entries, 2); 27 + __array(values, struct inner_map_type); 28 + } outer_alloc_htab_map SEC(".maps"); 29 + 30 + char _license[] SEC("license") = "GPL";
-4
tools/testing/selftests/bpf/test_maps.c
··· 26 26 #include "test_maps.h" 27 27 #include "testing_helpers.h" 28 28 29 - #ifndef ENOTSUPP 30 - #define ENOTSUPP 524 31 - #endif 32 - 33 29 int skips; 34 30 35 31 static struct bpf_map_create_opts map_opts = { .sz = sizeof(map_opts) };
-4
tools/testing/selftests/bpf/test_verifier.c
··· 42 42 #include "../../../include/linux/filter.h" 43 43 #include "testing_helpers.h" 44 44 45 - #ifndef ENOTSUPP 46 - #define ENOTSUPP 524 47 - #endif 48 - 49 45 #define MAX_INSNS BPF_MAXINSNS 50 46 #define MAX_EXPECTED_INSNS 32 51 47 #define MAX_UNEXPECTED_INSNS 32