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 'bpf-support-atomic-update-for-htab-of-maps'

Hou Tao says:

====================
bpf: Support atomic update for htab of maps

From: Hou Tao <houtao1@huawei.com>

Hi,

The motivation for the patch set comes from the question raised by Cody
Haas [1]. When trying to concurrently lookup and update an existing
element in a htab of maps, the lookup procedure may return -ENOENT
unexpectedly. The first revision of the patch set tried to resolve the
problem by making the insertion of the new element and the deletion of
the old element being atomic from the perspective of the lookup process.
While the solution would benefit all hash maps, it does not fully
resolved the problem due to the immediate reuse issue. Therefore, in v2
of the patch set, it only fixes the problem for fd htab.

Please see individual patches for details. Comments are always welcome.

v3:
* rebase on bpf_next/for-next
* add Acked-by tags

v2: https://lore.kernel.org/bpf/20250308135110.953269-1-houtao@huaweicloud.com/
* only support atomic update for fd htab

v1: https://lore.kernel.org/bpf/20250204082848.13471-1-hotforest@gmail.com

[1]: https://lore.kernel.org/xdp-newbies/CAH7f-ULFTwKdoH_t2SFc5rWCVYLEg-14d1fBYWH2eekudsnTRg@mail.gmail.com/
====================

Link: https://patch.msgid.link/20250401062250.543403-1-houtao@huaweicloud.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

+289 -76
+72 -76
kernel/bpf/hashtab.c
··· 175 175 htab->map.map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH; 176 176 } 177 177 178 + static inline bool is_fd_htab(const struct bpf_htab *htab) 179 + { 180 + return htab->map.map_type == BPF_MAP_TYPE_HASH_OF_MAPS; 181 + } 182 + 183 + static inline void *htab_elem_value(struct htab_elem *l, u32 key_size) 184 + { 185 + return l->key + round_up(key_size, 8); 186 + } 187 + 178 188 static inline void htab_elem_set_ptr(struct htab_elem *l, u32 key_size, 179 189 void __percpu *pptr) 180 190 { 181 - *(void __percpu **)(l->key + roundup(key_size, 8)) = pptr; 191 + *(void __percpu **)htab_elem_value(l, key_size) = pptr; 182 192 } 183 193 184 194 static inline void __percpu *htab_elem_get_ptr(struct htab_elem *l, u32 key_size) 185 195 { 186 - return *(void __percpu **)(l->key + roundup(key_size, 8)); 196 + return *(void __percpu **)htab_elem_value(l, key_size); 187 197 } 188 198 189 199 static void *fd_htab_map_get_ptr(const struct bpf_map *map, struct htab_elem *l) 190 200 { 191 - return *(void **)(l->key + roundup(map->key_size, 8)); 201 + return *(void **)htab_elem_value(l, map->key_size); 192 202 } 193 203 194 204 static struct htab_elem *get_htab_elem(struct bpf_htab *htab, int i) ··· 206 196 return (struct htab_elem *) (htab->elems + i * (u64)htab->elem_size); 207 197 } 208 198 199 + /* Both percpu and fd htab support in-place update, so no need for 200 + * extra elem. LRU itself can remove the least used element, so 201 + * there is no need for an extra elem during map_update. 202 + */ 209 203 static bool htab_has_extra_elems(struct bpf_htab *htab) 210 204 { 211 - return !htab_is_percpu(htab) && !htab_is_lru(htab); 205 + return !htab_is_percpu(htab) && !htab_is_lru(htab) && !is_fd_htab(htab); 212 206 } 213 207 214 208 static void htab_free_prealloced_timers_and_wq(struct bpf_htab *htab) ··· 229 215 elem = get_htab_elem(htab, i); 230 216 if (btf_record_has_field(htab->map.record, BPF_TIMER)) 231 217 bpf_obj_free_timer(htab->map.record, 232 - elem->key + round_up(htab->map.key_size, 8)); 218 + htab_elem_value(elem, htab->map.key_size)); 233 219 if (btf_record_has_field(htab->map.record, BPF_WORKQUEUE)) 234 220 bpf_obj_free_workqueue(htab->map.record, 235 - elem->key + round_up(htab->map.key_size, 8)); 221 + htab_elem_value(elem, htab->map.key_size)); 236 222 cond_resched(); 237 223 } 238 224 } ··· 259 245 cond_resched(); 260 246 } 261 247 } else { 262 - bpf_obj_free_fields(htab->map.record, elem->key + round_up(htab->map.key_size, 8)); 248 + bpf_obj_free_fields(htab->map.record, 249 + htab_elem_value(elem, htab->map.key_size)); 263 250 cond_resched(); 264 251 } 265 252 cond_resched(); ··· 468 453 { 469 454 bool percpu = (attr->map_type == BPF_MAP_TYPE_PERCPU_HASH || 470 455 attr->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH); 471 - bool lru = (attr->map_type == BPF_MAP_TYPE_LRU_HASH || 472 - attr->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH); 473 456 /* percpu_lru means each cpu has its own LRU list. 474 457 * it is different from BPF_MAP_TYPE_PERCPU_HASH where 475 458 * the map's value itself is percpu. percpu_lru has ··· 562 549 if (err) 563 550 goto free_map_locked; 564 551 565 - if (!percpu && !lru) { 566 - /* lru itself can remove the least used element, so 567 - * there is no need for an extra elem during map_update. 568 - */ 552 + if (htab_has_extra_elems(htab)) { 569 553 err = alloc_extra_elems(htab); 570 554 if (err) 571 555 goto free_prealloc; ··· 680 670 struct htab_elem *l = __htab_map_lookup_elem(map, key); 681 671 682 672 if (l) 683 - return l->key + round_up(map->key_size, 8); 673 + return htab_elem_value(l, map->key_size); 684 674 685 675 return NULL; 686 676 } ··· 719 709 if (l) { 720 710 if (mark) 721 711 bpf_lru_node_set_ref(&l->lru_node); 722 - return l->key + round_up(map->key_size, 8); 712 + return htab_elem_value(l, map->key_size); 723 713 } 724 714 725 715 return NULL; ··· 773 763 for_each_possible_cpu(cpu) 774 764 bpf_obj_free_fields(htab->map.record, per_cpu_ptr(pptr, cpu)); 775 765 } else { 776 - void *map_value = elem->key + round_up(htab->map.key_size, 8); 766 + void *map_value = htab_elem_value(elem, htab->map.key_size); 777 767 778 768 bpf_obj_free_fields(htab->map.record, map_value); 779 769 } ··· 978 968 979 969 static bool fd_htab_map_needs_adjust(const struct bpf_htab *htab) 980 970 { 981 - return htab->map.map_type == BPF_MAP_TYPE_HASH_OF_MAPS && 982 - BITS_PER_LONG == 64; 971 + return is_fd_htab(htab) && BITS_PER_LONG == 64; 983 972 } 984 973 985 974 static struct htab_elem *alloc_htab_elem(struct bpf_htab *htab, void *key, ··· 1048 1039 htab_elem_set_ptr(l_new, key_size, pptr); 1049 1040 } else if (fd_htab_map_needs_adjust(htab)) { 1050 1041 size = round_up(size, 8); 1051 - memcpy(l_new->key + round_up(key_size, 8), value, size); 1042 + memcpy(htab_elem_value(l_new, key_size), value, size); 1052 1043 } else { 1053 - copy_map_value(&htab->map, 1054 - l_new->key + round_up(key_size, 8), 1055 - value); 1044 + copy_map_value(&htab->map, htab_elem_value(l_new, key_size), value); 1056 1045 } 1057 1046 1058 1047 l_new->hash = hash; ··· 1079 1072 u64 map_flags) 1080 1073 { 1081 1074 struct bpf_htab *htab = container_of(map, struct bpf_htab, map); 1082 - struct htab_elem *l_new = NULL, *l_old; 1075 + struct htab_elem *l_new, *l_old; 1083 1076 struct hlist_nulls_head *head; 1084 1077 unsigned long flags; 1085 - void *old_map_ptr; 1086 1078 struct bucket *b; 1087 1079 u32 key_size, hash; 1088 1080 int ret; ··· 1112 1106 if (l_old) { 1113 1107 /* grab the element lock and update value in place */ 1114 1108 copy_map_value_locked(map, 1115 - l_old->key + round_up(key_size, 8), 1109 + htab_elem_value(l_old, key_size), 1116 1110 value, false); 1117 1111 return 0; 1118 1112 } ··· 1140 1134 * and update element in place 1141 1135 */ 1142 1136 copy_map_value_locked(map, 1143 - l_old->key + round_up(key_size, 8), 1137 + htab_elem_value(l_old, key_size), 1144 1138 value, false); 1145 1139 ret = 0; 1146 1140 goto err; ··· 1162 1156 hlist_nulls_del_rcu(&l_old->hash_node); 1163 1157 1164 1158 /* l_old has already been stashed in htab->extra_elems, free 1165 - * its special fields before it is available for reuse. Also 1166 - * save the old map pointer in htab of maps before unlock 1167 - * and release it after unlock. 1159 + * its special fields before it is available for reuse. 1168 1160 */ 1169 - old_map_ptr = NULL; 1170 - if (htab_is_prealloc(htab)) { 1171 - if (map->ops->map_fd_put_ptr) 1172 - old_map_ptr = fd_htab_map_get_ptr(map, l_old); 1161 + if (htab_is_prealloc(htab)) 1173 1162 check_and_free_fields(htab, l_old); 1174 - } 1175 1163 } 1176 1164 htab_unlock_bucket(b, flags); 1177 - if (l_old) { 1178 - if (old_map_ptr) 1179 - map->ops->map_fd_put_ptr(map, old_map_ptr, true); 1180 - if (!htab_is_prealloc(htab)) 1181 - free_htab_elem(htab, l_old); 1182 - } 1165 + if (l_old && !htab_is_prealloc(htab)) 1166 + free_htab_elem(htab, l_old); 1183 1167 return 0; 1184 1168 err: 1185 1169 htab_unlock_bucket(b, flags); ··· 1216 1220 l_new = prealloc_lru_pop(htab, key, hash); 1217 1221 if (!l_new) 1218 1222 return -ENOMEM; 1219 - copy_map_value(&htab->map, 1220 - l_new->key + round_up(map->key_size, 8), value); 1223 + copy_map_value(&htab->map, htab_elem_value(l_new, map->key_size), value); 1221 1224 1222 1225 ret = htab_lock_bucket(b, &flags); 1223 1226 if (ret) ··· 1250 1255 return ret; 1251 1256 } 1252 1257 1253 - static long __htab_percpu_map_update_elem(struct bpf_map *map, void *key, 1258 + static long htab_map_update_elem_in_place(struct bpf_map *map, void *key, 1254 1259 void *value, u64 map_flags, 1255 - bool onallcpus) 1260 + bool percpu, bool onallcpus) 1256 1261 { 1257 1262 struct bpf_htab *htab = container_of(map, struct bpf_htab, map); 1258 - struct htab_elem *l_new = NULL, *l_old; 1263 + struct htab_elem *l_new, *l_old; 1259 1264 struct hlist_nulls_head *head; 1265 + void *old_map_ptr = NULL; 1260 1266 unsigned long flags; 1261 1267 struct bucket *b; 1262 1268 u32 key_size, hash; ··· 1288 1292 goto err; 1289 1293 1290 1294 if (l_old) { 1291 - /* per-cpu hash map can update value in-place */ 1292 - pcpu_copy_value(htab, htab_elem_get_ptr(l_old, key_size), 1293 - value, onallcpus); 1295 + /* Update value in-place */ 1296 + if (percpu) { 1297 + pcpu_copy_value(htab, htab_elem_get_ptr(l_old, key_size), 1298 + value, onallcpus); 1299 + } else { 1300 + void **inner_map_pptr = htab_elem_value(l_old, key_size); 1301 + 1302 + old_map_ptr = *inner_map_pptr; 1303 + WRITE_ONCE(*inner_map_pptr, *(void **)value); 1304 + } 1294 1305 } else { 1295 1306 l_new = alloc_htab_elem(htab, key, value, key_size, 1296 - hash, true, onallcpus, NULL); 1307 + hash, percpu, onallcpus, NULL); 1297 1308 if (IS_ERR(l_new)) { 1298 1309 ret = PTR_ERR(l_new); 1299 1310 goto err; 1300 1311 } 1301 1312 hlist_nulls_add_head_rcu(&l_new->hash_node, head); 1302 1313 } 1303 - ret = 0; 1304 1314 err: 1305 1315 htab_unlock_bucket(b, flags); 1316 + if (old_map_ptr) 1317 + map->ops->map_fd_put_ptr(map, old_map_ptr, true); 1306 1318 return ret; 1307 1319 } 1308 1320 ··· 1387 1383 static long htab_percpu_map_update_elem(struct bpf_map *map, void *key, 1388 1384 void *value, u64 map_flags) 1389 1385 { 1390 - return __htab_percpu_map_update_elem(map, key, value, map_flags, false); 1386 + return htab_map_update_elem_in_place(map, key, value, map_flags, true, false); 1391 1387 } 1392 1388 1393 1389 static long htab_lru_percpu_map_update_elem(struct bpf_map *map, void *key, ··· 1504 1500 /* We only free timer on uref dropping to zero */ 1505 1501 if (btf_record_has_field(htab->map.record, BPF_TIMER)) 1506 1502 bpf_obj_free_timer(htab->map.record, 1507 - l->key + round_up(htab->map.key_size, 8)); 1503 + htab_elem_value(l, htab->map.key_size)); 1508 1504 if (btf_record_has_field(htab->map.record, BPF_WORKQUEUE)) 1509 1505 bpf_obj_free_workqueue(htab->map.record, 1510 - l->key + round_up(htab->map.key_size, 8)); 1506 + htab_elem_value(l, htab->map.key_size)); 1511 1507 } 1512 1508 cond_resched_rcu(); 1513 1509 } ··· 1619 1615 off += roundup_value_size; 1620 1616 } 1621 1617 } else { 1622 - u32 roundup_key_size = round_up(map->key_size, 8); 1618 + void *src = htab_elem_value(l, map->key_size); 1623 1619 1624 1620 if (flags & BPF_F_LOCK) 1625 - copy_map_value_locked(map, value, l->key + 1626 - roundup_key_size, 1627 - true); 1621 + copy_map_value_locked(map, value, src, true); 1628 1622 else 1629 - copy_map_value(map, value, l->key + 1630 - roundup_key_size); 1623 + copy_map_value(map, value, src); 1631 1624 /* Zeroing special fields in the temp buffer */ 1632 1625 check_and_init_map_value(map, value); 1633 1626 } ··· 1681 1680 bool is_percpu) 1682 1681 { 1683 1682 struct bpf_htab *htab = container_of(map, struct bpf_htab, map); 1684 - u32 bucket_cnt, total, key_size, value_size, roundup_key_size; 1685 1683 void *keys = NULL, *values = NULL, *value, *dst_key, *dst_val; 1686 1684 void __user *uvalues = u64_to_user_ptr(attr->batch.values); 1687 1685 void __user *ukeys = u64_to_user_ptr(attr->batch.keys); 1688 1686 void __user *ubatch = u64_to_user_ptr(attr->batch.in_batch); 1689 1687 u32 batch, max_count, size, bucket_size, map_id; 1688 + u32 bucket_cnt, total, key_size, value_size; 1690 1689 struct htab_elem *node_to_free = NULL; 1691 1690 u64 elem_map_flags, map_flags; 1692 1691 struct hlist_nulls_head *head; ··· 1721 1720 return -ENOENT; 1722 1721 1723 1722 key_size = htab->map.key_size; 1724 - roundup_key_size = round_up(htab->map.key_size, 8); 1725 1723 value_size = htab->map.value_size; 1726 1724 size = round_up(value_size, 8); 1727 1725 if (is_percpu) ··· 1812 1812 off += size; 1813 1813 } 1814 1814 } else { 1815 - value = l->key + roundup_key_size; 1816 - if (map->map_type == BPF_MAP_TYPE_HASH_OF_MAPS) { 1815 + value = htab_elem_value(l, key_size); 1816 + if (is_fd_htab(htab)) { 1817 1817 struct bpf_map **inner_map = value; 1818 1818 1819 1819 /* Actual value is the id of the inner map */ ··· 2063 2063 static int __bpf_hash_map_seq_show(struct seq_file *seq, struct htab_elem *elem) 2064 2064 { 2065 2065 struct bpf_iter_seq_hash_map_info *info = seq->private; 2066 - u32 roundup_key_size, roundup_value_size; 2067 2066 struct bpf_iter__bpf_map_elem ctx = {}; 2068 2067 struct bpf_map *map = info->map; 2069 2068 struct bpf_iter_meta meta; 2070 2069 int ret = 0, off = 0, cpu; 2070 + u32 roundup_value_size; 2071 2071 struct bpf_prog *prog; 2072 2072 void __percpu *pptr; 2073 2073 ··· 2077 2077 ctx.meta = &meta; 2078 2078 ctx.map = info->map; 2079 2079 if (elem) { 2080 - roundup_key_size = round_up(map->key_size, 8); 2081 2080 ctx.key = elem->key; 2082 2081 if (!info->percpu_value_buf) { 2083 - ctx.value = elem->key + roundup_key_size; 2082 + ctx.value = htab_elem_value(elem, map->key_size); 2084 2083 } else { 2085 2084 roundup_value_size = round_up(map->value_size, 8); 2086 2085 pptr = htab_elem_get_ptr(elem, map->key_size); ··· 2164 2165 struct hlist_nulls_head *head; 2165 2166 struct hlist_nulls_node *n; 2166 2167 struct htab_elem *elem; 2167 - u32 roundup_key_size; 2168 2168 int i, num_elems = 0; 2169 2169 void __percpu *pptr; 2170 2170 struct bucket *b; ··· 2178 2180 2179 2181 is_percpu = htab_is_percpu(htab); 2180 2182 2181 - roundup_key_size = round_up(map->key_size, 8); 2182 2183 /* migration has been disabled, so percpu value prepared here will be 2183 2184 * the same as the one seen by the bpf program with 2184 2185 * bpf_map_lookup_elem(). ··· 2193 2196 pptr = htab_elem_get_ptr(elem, map->key_size); 2194 2197 val = this_cpu_ptr(pptr); 2195 2198 } else { 2196 - val = elem->key + roundup_key_size; 2199 + val = htab_elem_value(elem, map->key_size); 2197 2200 } 2198 2201 num_elems++; 2199 2202 ret = callback_fn((u64)(long)map, (u64)(long)key, ··· 2408 2411 ret = __htab_lru_percpu_map_update_elem(map, key, value, 2409 2412 map_flags, true); 2410 2413 else 2411 - ret = __htab_percpu_map_update_elem(map, key, value, map_flags, 2412 - true); 2414 + ret = htab_map_update_elem_in_place(map, key, value, map_flags, 2415 + true, true); 2413 2416 rcu_read_unlock(); 2414 2417 2415 2418 return ret; ··· 2533 2536 return ret; 2534 2537 } 2535 2538 2536 - /* only called from syscall */ 2539 + /* Only called from syscall */ 2537 2540 int bpf_fd_htab_map_update_elem(struct bpf_map *map, struct file *map_file, 2538 2541 void *key, void *value, u64 map_flags) 2539 2542 { 2540 2543 void *ptr; 2541 2544 int ret; 2542 - u32 ufd = *(u32 *)value; 2543 2545 2544 - ptr = map->ops->map_fd_get_ptr(map, map_file, ufd); 2546 + ptr = map->ops->map_fd_get_ptr(map, map_file, *(int *)value); 2545 2547 if (IS_ERR(ptr)) 2546 2548 return PTR_ERR(ptr); 2547 2549 2548 2550 /* The htab bucket lock is always held during update operations in fd 2549 2551 * htab map, and the following rcu_read_lock() is only used to avoid 2550 - * the WARN_ON_ONCE in htab_map_update_elem(). 2552 + * the WARN_ON_ONCE in htab_map_update_elem_in_place(). 2551 2553 */ 2552 2554 rcu_read_lock(); 2553 - ret = htab_map_update_elem(map, key, &ptr, map_flags); 2555 + ret = htab_map_update_elem_in_place(map, key, &ptr, map_flags, false, false); 2554 2556 rcu_read_unlock(); 2555 2557 if (ret) 2556 2558 map->ops->map_fd_put_ptr(map, ptr, false);
+192
tools/testing/selftests/bpf/prog_tests/fd_htab_lookup.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (C) 2025. Huawei Technologies Co., Ltd */ 3 + #define _GNU_SOURCE 4 + #include <stdbool.h> 5 + #include <test_progs.h> 6 + #include "fd_htab_lookup.skel.h" 7 + 8 + struct htab_op_ctx { 9 + int fd; 10 + int loop; 11 + unsigned int entries; 12 + bool stop; 13 + }; 14 + 15 + #define ERR_TO_RETVAL(where, err) ((void *)(long)(((where) << 12) | (-err))) 16 + 17 + static void *htab_lookup_fn(void *arg) 18 + { 19 + struct htab_op_ctx *ctx = arg; 20 + int i = 0; 21 + 22 + while (i++ < ctx->loop && !ctx->stop) { 23 + unsigned int j; 24 + 25 + for (j = 0; j < ctx->entries; j++) { 26 + unsigned int key = j, zero = 0, value; 27 + int inner_fd, err; 28 + 29 + err = bpf_map_lookup_elem(ctx->fd, &key, &value); 30 + if (err) { 31 + ctx->stop = true; 32 + return ERR_TO_RETVAL(1, err); 33 + } 34 + 35 + inner_fd = bpf_map_get_fd_by_id(value); 36 + if (inner_fd < 0) { 37 + /* The old map has been freed */ 38 + if (inner_fd == -ENOENT) 39 + continue; 40 + ctx->stop = true; 41 + return ERR_TO_RETVAL(2, inner_fd); 42 + } 43 + 44 + err = bpf_map_lookup_elem(inner_fd, &zero, &value); 45 + if (err) { 46 + close(inner_fd); 47 + ctx->stop = true; 48 + return ERR_TO_RETVAL(3, err); 49 + } 50 + close(inner_fd); 51 + 52 + if (value != key) { 53 + ctx->stop = true; 54 + return ERR_TO_RETVAL(4, -EINVAL); 55 + } 56 + } 57 + } 58 + 59 + return NULL; 60 + } 61 + 62 + static void *htab_update_fn(void *arg) 63 + { 64 + struct htab_op_ctx *ctx = arg; 65 + int i = 0; 66 + 67 + while (i++ < ctx->loop && !ctx->stop) { 68 + unsigned int j; 69 + 70 + for (j = 0; j < ctx->entries; j++) { 71 + unsigned int key = j, zero = 0; 72 + int inner_fd, err; 73 + 74 + inner_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, NULL, 4, 4, 1, NULL); 75 + if (inner_fd < 0) { 76 + ctx->stop = true; 77 + return ERR_TO_RETVAL(1, inner_fd); 78 + } 79 + 80 + err = bpf_map_update_elem(inner_fd, &zero, &key, 0); 81 + if (err) { 82 + close(inner_fd); 83 + ctx->stop = true; 84 + return ERR_TO_RETVAL(2, err); 85 + } 86 + 87 + err = bpf_map_update_elem(ctx->fd, &key, &inner_fd, BPF_EXIST); 88 + if (err) { 89 + close(inner_fd); 90 + ctx->stop = true; 91 + return ERR_TO_RETVAL(3, err); 92 + } 93 + close(inner_fd); 94 + } 95 + } 96 + 97 + return NULL; 98 + } 99 + 100 + static int setup_htab(int fd, unsigned int entries) 101 + { 102 + unsigned int i; 103 + 104 + for (i = 0; i < entries; i++) { 105 + unsigned int key = i, zero = 0; 106 + int inner_fd, err; 107 + 108 + inner_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, NULL, 4, 4, 1, NULL); 109 + if (!ASSERT_OK_FD(inner_fd, "new array")) 110 + return -1; 111 + 112 + err = bpf_map_update_elem(inner_fd, &zero, &key, 0); 113 + if (!ASSERT_OK(err, "init array")) { 114 + close(inner_fd); 115 + return -1; 116 + } 117 + 118 + err = bpf_map_update_elem(fd, &key, &inner_fd, 0); 119 + if (!ASSERT_OK(err, "init outer")) { 120 + close(inner_fd); 121 + return -1; 122 + } 123 + close(inner_fd); 124 + } 125 + 126 + return 0; 127 + } 128 + 129 + static int get_int_from_env(const char *name, int dft) 130 + { 131 + const char *value; 132 + 133 + value = getenv(name); 134 + if (!value) 135 + return dft; 136 + 137 + return atoi(value); 138 + } 139 + 140 + void test_fd_htab_lookup(void) 141 + { 142 + unsigned int i, wr_nr = 8, rd_nr = 16; 143 + pthread_t tids[wr_nr + rd_nr]; 144 + struct fd_htab_lookup *skel; 145 + struct htab_op_ctx ctx; 146 + int err; 147 + 148 + skel = fd_htab_lookup__open_and_load(); 149 + if (!ASSERT_OK_PTR(skel, "fd_htab_lookup__open_and_load")) 150 + return; 151 + 152 + ctx.fd = bpf_map__fd(skel->maps.outer_map); 153 + ctx.loop = get_int_from_env("FD_HTAB_LOOP_NR", 5); 154 + ctx.stop = false; 155 + ctx.entries = 8; 156 + 157 + err = setup_htab(ctx.fd, ctx.entries); 158 + if (err) 159 + goto destroy; 160 + 161 + memset(tids, 0, sizeof(tids)); 162 + for (i = 0; i < wr_nr; i++) { 163 + err = pthread_create(&tids[i], NULL, htab_update_fn, &ctx); 164 + if (!ASSERT_OK(err, "pthread_create")) { 165 + ctx.stop = true; 166 + goto reap; 167 + } 168 + } 169 + for (i = 0; i < rd_nr; i++) { 170 + err = pthread_create(&tids[i + wr_nr], NULL, htab_lookup_fn, &ctx); 171 + if (!ASSERT_OK(err, "pthread_create")) { 172 + ctx.stop = true; 173 + goto reap; 174 + } 175 + } 176 + 177 + reap: 178 + for (i = 0; i < wr_nr + rd_nr; i++) { 179 + void *ret = NULL; 180 + char desc[32]; 181 + 182 + if (!tids[i]) 183 + continue; 184 + 185 + snprintf(desc, sizeof(desc), "thread %u", i + 1); 186 + err = pthread_join(tids[i], &ret); 187 + ASSERT_OK(err, desc); 188 + ASSERT_EQ(ret, NULL, desc); 189 + } 190 + destroy: 191 + fd_htab_lookup__destroy(skel); 192 + }
+25
tools/testing/selftests/bpf/progs/fd_htab_lookup.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (C) 2025. Huawei Technologies Co., Ltd */ 3 + #include <linux/bpf.h> 4 + #include <bpf/bpf_helpers.h> 5 + 6 + char _license[] SEC("license") = "GPL"; 7 + 8 + struct inner_map_type { 9 + __uint(type, BPF_MAP_TYPE_ARRAY); 10 + __uint(key_size, 4); 11 + __uint(value_size, 4); 12 + __uint(max_entries, 1); 13 + } inner_map SEC(".maps"); 14 + 15 + struct { 16 + __uint(type, BPF_MAP_TYPE_HASH_OF_MAPS); 17 + __uint(max_entries, 64); 18 + __type(key, int); 19 + __type(value, int); 20 + __array(values, struct inner_map_type); 21 + } outer_map SEC(".maps") = { 22 + .values = { 23 + [0] = &inner_map, 24 + }, 25 + };