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 'Add lookup_and_delete_elem support to BPF hash map types'

Denis Salopek says:

====================

This patch series extends the existing bpf_map_lookup_and_delete_elem()
functionality with 4 more map types:
- BPF_MAP_TYPE_HASH,
- BPF_MAP_TYPE_PERCPU_HASH,
- BPF_MAP_TYPE_LRU_HASH and
- BPF_MAP_TYPE_LRU_PERCPU_HASH.

Patch 1 adds most of its functionality and logic as well as
documentation.

As it was previously limited to only stacks and queues which do not
support the BPF_F_LOCK flag, patch 2 enables its usage by adding a new
libbpf API bpf_map_lookup_and_delete_elem_flags() based on the existing
bpf_map_lookup_elem_flags().

Patch 3 adds selftests for lookup_and_delete_elem().

Changes in patch 1:
v7: Minor formating nits, add Acked-by.
v6: Remove unneeded flag check, minor code/format fixes.
v5: Split patch to 3 patches. Extend BPF_MAP_LOOKUP_AND_DELETE_ELEM
documentation with this changes.
v4: Fix the return value for unsupported map types.
v3: Add bpf_map_lookup_and_delete_elem_flags() and enable BPF_F_LOCK
flag, change CHECKs to ASSERT_OKs, initialize variables to 0.
v2: Add functionality for LRU/per-CPU, add test_progs tests.

Changes in patch 2:
v7: No change.
v6: Add Acked-by.
v5: Move to the newest libbpf version (0.4.0).

Changes in patch 3:
v7: Remove ASSERT_GE macro which is already added in some other commit,
change ASSERT_OK to ASSERT_OK_PTR, add Acked-by.
v6: Remove PERCPU macros, add ASSERT_GE macro to test_progs.h, remove
leftover code.
v5: Use more appropriate macros. Better check for changed value.
====================

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>

+511 -4
+2
include/linux/bpf.h
··· 70 70 void *(*map_lookup_elem_sys_only)(struct bpf_map *map, void *key); 71 71 int (*map_lookup_batch)(struct bpf_map *map, const union bpf_attr *attr, 72 72 union bpf_attr __user *uattr); 73 + int (*map_lookup_and_delete_elem)(struct bpf_map *map, void *key, 74 + void *value, u64 flags); 73 75 int (*map_lookup_and_delete_batch)(struct bpf_map *map, 74 76 const union bpf_attr *attr, 75 77 union bpf_attr __user *uattr);
+13
include/uapi/linux/bpf.h
··· 527 527 * Look up an element with the given *key* in the map referred to 528 528 * by the file descriptor *fd*, and if found, delete the element. 529 529 * 530 + * For **BPF_MAP_TYPE_QUEUE** and **BPF_MAP_TYPE_STACK** map 531 + * types, the *flags* argument needs to be set to 0, but for other 532 + * map types, it may be specified as: 533 + * 534 + * **BPF_F_LOCK** 535 + * Look up and delete the value of a spin-locked map 536 + * without returning the lock. This must be specified if 537 + * the elements contain a spinlock. 538 + * 530 539 * The **BPF_MAP_TYPE_QUEUE** and **BPF_MAP_TYPE_STACK** map types 531 540 * implement this command as a "pop" operation, deleting the top 532 541 * element rather than one corresponding to *key*. ··· 545 536 * This command is only valid for the following map types: 546 537 * * **BPF_MAP_TYPE_QUEUE** 547 538 * * **BPF_MAP_TYPE_STACK** 539 + * * **BPF_MAP_TYPE_HASH** 540 + * * **BPF_MAP_TYPE_PERCPU_HASH** 541 + * * **BPF_MAP_TYPE_LRU_HASH** 542 + * * **BPF_MAP_TYPE_LRU_PERCPU_HASH** 548 543 * 549 544 * Return 550 545 * Returns zero on success. On error, -1 is returned and *errno*
+98
kernel/bpf/hashtab.c
··· 1401 1401 rcu_read_unlock(); 1402 1402 } 1403 1403 1404 + static int __htab_map_lookup_and_delete_elem(struct bpf_map *map, void *key, 1405 + void *value, bool is_lru_map, 1406 + bool is_percpu, u64 flags) 1407 + { 1408 + struct bpf_htab *htab = container_of(map, struct bpf_htab, map); 1409 + struct hlist_nulls_head *head; 1410 + unsigned long bflags; 1411 + struct htab_elem *l; 1412 + u32 hash, key_size; 1413 + struct bucket *b; 1414 + int ret; 1415 + 1416 + key_size = map->key_size; 1417 + 1418 + hash = htab_map_hash(key, key_size, htab->hashrnd); 1419 + b = __select_bucket(htab, hash); 1420 + head = &b->head; 1421 + 1422 + ret = htab_lock_bucket(htab, b, hash, &bflags); 1423 + if (ret) 1424 + return ret; 1425 + 1426 + l = lookup_elem_raw(head, hash, key, key_size); 1427 + if (!l) { 1428 + ret = -ENOENT; 1429 + } else { 1430 + if (is_percpu) { 1431 + u32 roundup_value_size = round_up(map->value_size, 8); 1432 + void __percpu *pptr; 1433 + int off = 0, cpu; 1434 + 1435 + pptr = htab_elem_get_ptr(l, key_size); 1436 + for_each_possible_cpu(cpu) { 1437 + bpf_long_memcpy(value + off, 1438 + per_cpu_ptr(pptr, cpu), 1439 + roundup_value_size); 1440 + off += roundup_value_size; 1441 + } 1442 + } else { 1443 + u32 roundup_key_size = round_up(map->key_size, 8); 1444 + 1445 + if (flags & BPF_F_LOCK) 1446 + copy_map_value_locked(map, value, l->key + 1447 + roundup_key_size, 1448 + true); 1449 + else 1450 + copy_map_value(map, value, l->key + 1451 + roundup_key_size); 1452 + check_and_init_map_lock(map, value); 1453 + } 1454 + 1455 + hlist_nulls_del_rcu(&l->hash_node); 1456 + if (!is_lru_map) 1457 + free_htab_elem(htab, l); 1458 + } 1459 + 1460 + htab_unlock_bucket(htab, b, hash, bflags); 1461 + 1462 + if (is_lru_map && l) 1463 + bpf_lru_push_free(&htab->lru, &l->lru_node); 1464 + 1465 + return ret; 1466 + } 1467 + 1468 + static int htab_map_lookup_and_delete_elem(struct bpf_map *map, void *key, 1469 + void *value, u64 flags) 1470 + { 1471 + return __htab_map_lookup_and_delete_elem(map, key, value, false, false, 1472 + flags); 1473 + } 1474 + 1475 + static int htab_percpu_map_lookup_and_delete_elem(struct bpf_map *map, 1476 + void *key, void *value, 1477 + u64 flags) 1478 + { 1479 + return __htab_map_lookup_and_delete_elem(map, key, value, false, true, 1480 + flags); 1481 + } 1482 + 1483 + static int htab_lru_map_lookup_and_delete_elem(struct bpf_map *map, void *key, 1484 + void *value, u64 flags) 1485 + { 1486 + return __htab_map_lookup_and_delete_elem(map, key, value, true, false, 1487 + flags); 1488 + } 1489 + 1490 + static int htab_lru_percpu_map_lookup_and_delete_elem(struct bpf_map *map, 1491 + void *key, void *value, 1492 + u64 flags) 1493 + { 1494 + return __htab_map_lookup_and_delete_elem(map, key, value, true, true, 1495 + flags); 1496 + } 1497 + 1404 1498 static int 1405 1499 __htab_map_lookup_and_delete_batch(struct bpf_map *map, 1406 1500 const union bpf_attr *attr, ··· 2028 1934 .map_free = htab_map_free, 2029 1935 .map_get_next_key = htab_map_get_next_key, 2030 1936 .map_lookup_elem = htab_map_lookup_elem, 1937 + .map_lookup_and_delete_elem = htab_map_lookup_and_delete_elem, 2031 1938 .map_update_elem = htab_map_update_elem, 2032 1939 .map_delete_elem = htab_map_delete_elem, 2033 1940 .map_gen_lookup = htab_map_gen_lookup, ··· 2049 1954 .map_free = htab_map_free, 2050 1955 .map_get_next_key = htab_map_get_next_key, 2051 1956 .map_lookup_elem = htab_lru_map_lookup_elem, 1957 + .map_lookup_and_delete_elem = htab_lru_map_lookup_and_delete_elem, 2052 1958 .map_lookup_elem_sys_only = htab_lru_map_lookup_elem_sys, 2053 1959 .map_update_elem = htab_lru_map_update_elem, 2054 1960 .map_delete_elem = htab_lru_map_delete_elem, ··· 2173 2077 .map_free = htab_map_free, 2174 2078 .map_get_next_key = htab_map_get_next_key, 2175 2079 .map_lookup_elem = htab_percpu_map_lookup_elem, 2080 + .map_lookup_and_delete_elem = htab_percpu_map_lookup_and_delete_elem, 2176 2081 .map_update_elem = htab_percpu_map_update_elem, 2177 2082 .map_delete_elem = htab_map_delete_elem, 2178 2083 .map_seq_show_elem = htab_percpu_map_seq_show_elem, ··· 2193 2096 .map_free = htab_map_free, 2194 2097 .map_get_next_key = htab_map_get_next_key, 2195 2098 .map_lookup_elem = htab_lru_percpu_map_lookup_elem, 2099 + .map_lookup_and_delete_elem = htab_lru_percpu_map_lookup_and_delete_elem, 2196 2100 .map_update_elem = htab_lru_percpu_map_update_elem, 2197 2101 .map_delete_elem = htab_lru_map_delete_elem, 2198 2102 .map_seq_show_elem = htab_percpu_map_seq_show_elem,
+30 -4
kernel/bpf/syscall.c
··· 1483 1483 return err; 1484 1484 } 1485 1485 1486 - #define BPF_MAP_LOOKUP_AND_DELETE_ELEM_LAST_FIELD value 1486 + #define BPF_MAP_LOOKUP_AND_DELETE_ELEM_LAST_FIELD flags 1487 1487 1488 1488 static int map_lookup_and_delete_elem(union bpf_attr *attr) 1489 1489 { ··· 1499 1499 if (CHECK_ATTR(BPF_MAP_LOOKUP_AND_DELETE_ELEM)) 1500 1500 return -EINVAL; 1501 1501 1502 + if (attr->flags & ~BPF_F_LOCK) 1503 + return -EINVAL; 1504 + 1502 1505 f = fdget(ufd); 1503 1506 map = __bpf_map_get(f); 1504 1507 if (IS_ERR(map)) ··· 1512 1509 goto err_put; 1513 1510 } 1514 1511 1512 + if (attr->flags && 1513 + (map->map_type == BPF_MAP_TYPE_QUEUE || 1514 + map->map_type == BPF_MAP_TYPE_STACK)) { 1515 + err = -EINVAL; 1516 + goto err_put; 1517 + } 1518 + 1519 + if ((attr->flags & BPF_F_LOCK) && 1520 + !map_value_has_spin_lock(map)) { 1521 + err = -EINVAL; 1522 + goto err_put; 1523 + } 1524 + 1515 1525 key = __bpf_copy_key(ukey, map->key_size); 1516 1526 if (IS_ERR(key)) { 1517 1527 err = PTR_ERR(key); 1518 1528 goto err_put; 1519 1529 } 1520 1530 1521 - value_size = map->value_size; 1531 + value_size = bpf_map_value_size(map); 1522 1532 1523 1533 err = -ENOMEM; 1524 1534 value = kmalloc(value_size, GFP_USER | __GFP_NOWARN); 1525 1535 if (!value) 1526 1536 goto free_key; 1527 1537 1538 + err = -ENOTSUPP; 1528 1539 if (map->map_type == BPF_MAP_TYPE_QUEUE || 1529 1540 map->map_type == BPF_MAP_TYPE_STACK) { 1530 1541 err = map->ops->map_pop_elem(map, value); 1531 - } else { 1532 - err = -ENOTSUPP; 1542 + } else if (map->map_type == BPF_MAP_TYPE_HASH || 1543 + map->map_type == BPF_MAP_TYPE_PERCPU_HASH || 1544 + map->map_type == BPF_MAP_TYPE_LRU_HASH || 1545 + map->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH) { 1546 + if (!bpf_map_is_dev_bound(map)) { 1547 + bpf_disable_instrumentation(); 1548 + rcu_read_lock(); 1549 + err = map->ops->map_lookup_and_delete_elem(map, key, value, attr->flags); 1550 + rcu_read_unlock(); 1551 + bpf_enable_instrumentation(); 1552 + } 1533 1553 } 1534 1554 1535 1555 if (err)
+13
tools/include/uapi/linux/bpf.h
··· 527 527 * Look up an element with the given *key* in the map referred to 528 528 * by the file descriptor *fd*, and if found, delete the element. 529 529 * 530 + * For **BPF_MAP_TYPE_QUEUE** and **BPF_MAP_TYPE_STACK** map 531 + * types, the *flags* argument needs to be set to 0, but for other 532 + * map types, it may be specified as: 533 + * 534 + * **BPF_F_LOCK** 535 + * Look up and delete the value of a spin-locked map 536 + * without returning the lock. This must be specified if 537 + * the elements contain a spinlock. 538 + * 530 539 * The **BPF_MAP_TYPE_QUEUE** and **BPF_MAP_TYPE_STACK** map types 531 540 * implement this command as a "pop" operation, deleting the top 532 541 * element rather than one corresponding to *key*. ··· 545 536 * This command is only valid for the following map types: 546 537 * * **BPF_MAP_TYPE_QUEUE** 547 538 * * **BPF_MAP_TYPE_STACK** 539 + * * **BPF_MAP_TYPE_HASH** 540 + * * **BPF_MAP_TYPE_PERCPU_HASH** 541 + * * **BPF_MAP_TYPE_LRU_HASH** 542 + * * **BPF_MAP_TYPE_LRU_PERCPU_HASH** 548 543 * 549 544 * Return 550 545 * Returns zero on success. On error, -1 is returned and *errno*
+13
tools/lib/bpf/bpf.c
··· 458 458 return sys_bpf(BPF_MAP_LOOKUP_AND_DELETE_ELEM, &attr, sizeof(attr)); 459 459 } 460 460 461 + int bpf_map_lookup_and_delete_elem_flags(int fd, const void *key, void *value, __u64 flags) 462 + { 463 + union bpf_attr attr; 464 + 465 + memset(&attr, 0, sizeof(attr)); 466 + attr.map_fd = fd; 467 + attr.key = ptr_to_u64(key); 468 + attr.value = ptr_to_u64(value); 469 + attr.flags = flags; 470 + 471 + return sys_bpf(BPF_MAP_LOOKUP_AND_DELETE_ELEM, &attr, sizeof(attr)); 472 + } 473 + 461 474 int bpf_map_delete_elem(int fd, const void *key) 462 475 { 463 476 union bpf_attr attr;
+2
tools/lib/bpf/bpf.h
··· 124 124 __u64 flags); 125 125 LIBBPF_API int bpf_map_lookup_and_delete_elem(int fd, const void *key, 126 126 void *value); 127 + LIBBPF_API int bpf_map_lookup_and_delete_elem_flags(int fd, const void *key, 128 + void *value, __u64 flags); 127 129 LIBBPF_API int bpf_map_delete_elem(int fd, const void *key); 128 130 LIBBPF_API int bpf_map_get_next_key(int fd, const void *key, void *next_key); 129 131 LIBBPF_API int bpf_map_freeze(int fd);
+1
tools/lib/bpf/libbpf.map
··· 361 361 bpf_linker__new; 362 362 bpf_map__initial_value; 363 363 bpf_map__inner_map; 364 + bpf_map_lookup_and_delete_elem_flags; 364 365 bpf_object__gen_loader; 365 366 bpf_object__set_kversion; 366 367 bpf_tc_attach;
+288
tools/testing/selftests/bpf/prog_tests/lookup_and_delete.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + 3 + #include <test_progs.h> 4 + #include "test_lookup_and_delete.skel.h" 5 + 6 + #define START_VALUE 1234 7 + #define NEW_VALUE 4321 8 + #define MAX_ENTRIES 2 9 + 10 + static int duration; 11 + static int nr_cpus; 12 + 13 + static int fill_values(int map_fd) 14 + { 15 + __u64 key, value = START_VALUE; 16 + int err; 17 + 18 + for (key = 1; key < MAX_ENTRIES + 1; key++) { 19 + err = bpf_map_update_elem(map_fd, &key, &value, BPF_NOEXIST); 20 + if (!ASSERT_OK(err, "bpf_map_update_elem")) 21 + return -1; 22 + } 23 + 24 + return 0; 25 + } 26 + 27 + static int fill_values_percpu(int map_fd) 28 + { 29 + __u64 key, value[nr_cpus]; 30 + int i, err; 31 + 32 + for (i = 0; i < nr_cpus; i++) 33 + value[i] = START_VALUE; 34 + 35 + for (key = 1; key < MAX_ENTRIES + 1; key++) { 36 + err = bpf_map_update_elem(map_fd, &key, value, BPF_NOEXIST); 37 + if (!ASSERT_OK(err, "bpf_map_update_elem")) 38 + return -1; 39 + } 40 + 41 + return 0; 42 + } 43 + 44 + static struct test_lookup_and_delete *setup_prog(enum bpf_map_type map_type, 45 + int *map_fd) 46 + { 47 + struct test_lookup_and_delete *skel; 48 + int err; 49 + 50 + skel = test_lookup_and_delete__open(); 51 + if (!ASSERT_OK_PTR(skel, "test_lookup_and_delete__open")) 52 + return NULL; 53 + 54 + err = bpf_map__set_type(skel->maps.hash_map, map_type); 55 + if (!ASSERT_OK(err, "bpf_map__set_type")) 56 + goto cleanup; 57 + 58 + err = bpf_map__set_max_entries(skel->maps.hash_map, MAX_ENTRIES); 59 + if (!ASSERT_OK(err, "bpf_map__set_max_entries")) 60 + goto cleanup; 61 + 62 + err = test_lookup_and_delete__load(skel); 63 + if (!ASSERT_OK(err, "test_lookup_and_delete__load")) 64 + goto cleanup; 65 + 66 + *map_fd = bpf_map__fd(skel->maps.hash_map); 67 + if (!ASSERT_GE(*map_fd, 0, "bpf_map__fd")) 68 + goto cleanup; 69 + 70 + return skel; 71 + 72 + cleanup: 73 + test_lookup_and_delete__destroy(skel); 74 + return NULL; 75 + } 76 + 77 + /* Triggers BPF program that updates map with given key and value */ 78 + static int trigger_tp(struct test_lookup_and_delete *skel, __u64 key, 79 + __u64 value) 80 + { 81 + int err; 82 + 83 + skel->bss->set_pid = getpid(); 84 + skel->bss->set_key = key; 85 + skel->bss->set_value = value; 86 + 87 + err = test_lookup_and_delete__attach(skel); 88 + if (!ASSERT_OK(err, "test_lookup_and_delete__attach")) 89 + return -1; 90 + 91 + syscall(__NR_getpgid); 92 + 93 + test_lookup_and_delete__detach(skel); 94 + 95 + return 0; 96 + } 97 + 98 + static void test_lookup_and_delete_hash(void) 99 + { 100 + struct test_lookup_and_delete *skel; 101 + __u64 key, value; 102 + int map_fd, err; 103 + 104 + /* Setup program and fill the map. */ 105 + skel = setup_prog(BPF_MAP_TYPE_HASH, &map_fd); 106 + if (!ASSERT_OK_PTR(skel, "setup_prog")) 107 + return; 108 + 109 + err = fill_values(map_fd); 110 + if (!ASSERT_OK(err, "fill_values")) 111 + goto cleanup; 112 + 113 + /* Lookup and delete element. */ 114 + key = 1; 115 + err = bpf_map_lookup_and_delete_elem(map_fd, &key, &value); 116 + if (!ASSERT_OK(err, "bpf_map_lookup_and_delete_elem")) 117 + goto cleanup; 118 + 119 + /* Fetched value should match the initially set value. */ 120 + if (CHECK(value != START_VALUE, "bpf_map_lookup_and_delete_elem", 121 + "unexpected value=%lld\n", value)) 122 + goto cleanup; 123 + 124 + /* Check that the entry is non existent. */ 125 + err = bpf_map_lookup_elem(map_fd, &key, &value); 126 + if (!ASSERT_ERR(err, "bpf_map_lookup_elem")) 127 + goto cleanup; 128 + 129 + cleanup: 130 + test_lookup_and_delete__destroy(skel); 131 + } 132 + 133 + static void test_lookup_and_delete_percpu_hash(void) 134 + { 135 + struct test_lookup_and_delete *skel; 136 + __u64 key, val, value[nr_cpus]; 137 + int map_fd, err, i; 138 + 139 + /* Setup program and fill the map. */ 140 + skel = setup_prog(BPF_MAP_TYPE_PERCPU_HASH, &map_fd); 141 + if (!ASSERT_OK_PTR(skel, "setup_prog")) 142 + return; 143 + 144 + err = fill_values_percpu(map_fd); 145 + if (!ASSERT_OK(err, "fill_values_percpu")) 146 + goto cleanup; 147 + 148 + /* Lookup and delete element. */ 149 + key = 1; 150 + err = bpf_map_lookup_and_delete_elem(map_fd, &key, value); 151 + if (!ASSERT_OK(err, "bpf_map_lookup_and_delete_elem")) 152 + goto cleanup; 153 + 154 + for (i = 0; i < nr_cpus; i++) { 155 + val = value[i]; 156 + 157 + /* Fetched value should match the initially set value. */ 158 + if (CHECK(val != START_VALUE, "map value", 159 + "unexpected for cpu %d: %lld\n", i, val)) 160 + goto cleanup; 161 + } 162 + 163 + /* Check that the entry is non existent. */ 164 + err = bpf_map_lookup_elem(map_fd, &key, value); 165 + if (!ASSERT_ERR(err, "bpf_map_lookup_elem")) 166 + goto cleanup; 167 + 168 + cleanup: 169 + test_lookup_and_delete__destroy(skel); 170 + } 171 + 172 + static void test_lookup_and_delete_lru_hash(void) 173 + { 174 + struct test_lookup_and_delete *skel; 175 + __u64 key, value; 176 + int map_fd, err; 177 + 178 + /* Setup program and fill the LRU map. */ 179 + skel = setup_prog(BPF_MAP_TYPE_LRU_HASH, &map_fd); 180 + if (!ASSERT_OK_PTR(skel, "setup_prog")) 181 + return; 182 + 183 + err = fill_values(map_fd); 184 + if (!ASSERT_OK(err, "fill_values")) 185 + goto cleanup; 186 + 187 + /* Insert new element at key=3, should reuse LRU element. */ 188 + key = 3; 189 + err = trigger_tp(skel, key, NEW_VALUE); 190 + if (!ASSERT_OK(err, "trigger_tp")) 191 + goto cleanup; 192 + 193 + /* Lookup and delete element 3. */ 194 + err = bpf_map_lookup_and_delete_elem(map_fd, &key, &value); 195 + if (!ASSERT_OK(err, "bpf_map_lookup_and_delete_elem")) 196 + goto cleanup; 197 + 198 + /* Value should match the new value. */ 199 + if (CHECK(value != NEW_VALUE, "bpf_map_lookup_and_delete_elem", 200 + "unexpected value=%lld\n", value)) 201 + goto cleanup; 202 + 203 + /* Check that entries 3 and 1 are non existent. */ 204 + err = bpf_map_lookup_elem(map_fd, &key, &value); 205 + if (!ASSERT_ERR(err, "bpf_map_lookup_elem")) 206 + goto cleanup; 207 + 208 + key = 1; 209 + err = bpf_map_lookup_elem(map_fd, &key, &value); 210 + if (!ASSERT_ERR(err, "bpf_map_lookup_elem")) 211 + goto cleanup; 212 + 213 + cleanup: 214 + test_lookup_and_delete__destroy(skel); 215 + } 216 + 217 + static void test_lookup_and_delete_lru_percpu_hash(void) 218 + { 219 + struct test_lookup_and_delete *skel; 220 + __u64 key, val, value[nr_cpus]; 221 + int map_fd, err, i, cpucnt = 0; 222 + 223 + /* Setup program and fill the LRU map. */ 224 + skel = setup_prog(BPF_MAP_TYPE_LRU_PERCPU_HASH, &map_fd); 225 + if (!ASSERT_OK_PTR(skel, "setup_prog")) 226 + return; 227 + 228 + err = fill_values_percpu(map_fd); 229 + if (!ASSERT_OK(err, "fill_values_percpu")) 230 + goto cleanup; 231 + 232 + /* Insert new element at key=3, should reuse LRU element 1. */ 233 + key = 3; 234 + err = trigger_tp(skel, key, NEW_VALUE); 235 + if (!ASSERT_OK(err, "trigger_tp")) 236 + goto cleanup; 237 + 238 + /* Clean value. */ 239 + for (i = 0; i < nr_cpus; i++) 240 + value[i] = 0; 241 + 242 + /* Lookup and delete element 3. */ 243 + err = bpf_map_lookup_and_delete_elem(map_fd, &key, value); 244 + if (!ASSERT_OK(err, "bpf_map_lookup_and_delete_elem")) { 245 + goto cleanup; 246 + } 247 + 248 + /* Check if only one CPU has set the value. */ 249 + for (i = 0; i < nr_cpus; i++) { 250 + val = value[i]; 251 + if (val) { 252 + if (CHECK(val != NEW_VALUE, "map value", 253 + "unexpected for cpu %d: %lld\n", i, val)) 254 + goto cleanup; 255 + cpucnt++; 256 + } 257 + } 258 + if (CHECK(cpucnt != 1, "map value", "set for %d CPUs instead of 1!\n", 259 + cpucnt)) 260 + goto cleanup; 261 + 262 + /* Check that entries 3 and 1 are non existent. */ 263 + err = bpf_map_lookup_elem(map_fd, &key, &value); 264 + if (!ASSERT_ERR(err, "bpf_map_lookup_elem")) 265 + goto cleanup; 266 + 267 + key = 1; 268 + err = bpf_map_lookup_elem(map_fd, &key, &value); 269 + if (!ASSERT_ERR(err, "bpf_map_lookup_elem")) 270 + goto cleanup; 271 + 272 + cleanup: 273 + test_lookup_and_delete__destroy(skel); 274 + } 275 + 276 + void test_lookup_and_delete(void) 277 + { 278 + nr_cpus = bpf_num_possible_cpus(); 279 + 280 + if (test__start_subtest("lookup_and_delete")) 281 + test_lookup_and_delete_hash(); 282 + if (test__start_subtest("lookup_and_delete_percpu")) 283 + test_lookup_and_delete_percpu_hash(); 284 + if (test__start_subtest("lookup_and_delete_lru")) 285 + test_lookup_and_delete_lru_hash(); 286 + if (test__start_subtest("lookup_and_delete_lru_percpu")) 287 + test_lookup_and_delete_lru_percpu_hash(); 288 + }
+26
tools/testing/selftests/bpf/progs/test_lookup_and_delete.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + #include "vmlinux.h" 4 + #include <bpf/bpf_helpers.h> 5 + 6 + __u32 set_pid = 0; 7 + __u64 set_key = 0; 8 + __u64 set_value = 0; 9 + 10 + struct { 11 + __uint(type, BPF_MAP_TYPE_HASH); 12 + __uint(max_entries, 2); 13 + __type(key, __u64); 14 + __type(value, __u64); 15 + } hash_map SEC(".maps"); 16 + 17 + SEC("tp/syscalls/sys_enter_getpgid") 18 + int bpf_lookup_and_delete_test(const void *ctx) 19 + { 20 + if (set_pid == bpf_get_current_pid_tgid() >> 32) 21 + bpf_map_update_elem(&hash_map, &set_key, &set_value, BPF_NOEXIST); 22 + 23 + return 0; 24 + } 25 + 26 + char _license[] SEC("license") = "GPL";
+8
tools/testing/selftests/bpf/test_lru_map.c
··· 231 231 assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1 && 232 232 errno == ENOENT); 233 233 234 + /* lookup elem key=1 and delete it, then check it doesn't exist */ 235 + key = 1; 236 + assert(!bpf_map_lookup_and_delete_elem(lru_map_fd, &key, &value)); 237 + assert(value[0] == 1234); 238 + 239 + /* remove the same element from the expected map */ 240 + assert(!bpf_map_delete_elem(expected_map_fd, &key)); 241 + 234 242 assert(map_equal(lru_map_fd, expected_map_fd)); 235 243 236 244 close(expected_map_fd);
+17
tools/testing/selftests/bpf/test_maps.c
··· 65 65 assert(bpf_map_lookup_elem(fd, &key, &value) == 0 && value == 1234); 66 66 67 67 key = 2; 68 + value = 1234; 69 + /* Insert key=2 element. */ 70 + assert(bpf_map_update_elem(fd, &key, &value, BPF_ANY) == 0); 71 + 72 + /* Check that key=2 matches the value and delete it */ 73 + assert(bpf_map_lookup_and_delete_elem(fd, &key, &value) == 0 && value == 1234); 74 + 68 75 /* Check that key=2 is not found. */ 69 76 assert(bpf_map_lookup_elem(fd, &key, &value) == -1 && errno == ENOENT); 70 77 ··· 173 166 /* Insert key=1 element. */ 174 167 assert(!(expected_key_mask & key)); 175 168 assert(bpf_map_update_elem(fd, &key, value, BPF_ANY) == 0); 169 + 170 + /* Lookup and delete elem key=1 and check value. */ 171 + assert(bpf_map_lookup_and_delete_elem(fd, &key, value) == 0 && 172 + bpf_percpu(value,0) == 100); 173 + 174 + for (i = 0; i < nr_cpus; i++) 175 + bpf_percpu(value,i) = i + 100; 176 + 177 + /* Insert key=1 element which should not exist. */ 178 + assert(bpf_map_update_elem(fd, &key, value, BPF_NOEXIST) == 0); 176 179 expected_key_mask |= key; 177 180 178 181 /* BPF_NOEXIST means add new element if it doesn't exist. */