linux/tools/testing/selftests/bpf/map_tests/htab_map_batch_ops.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/* Copyright (c) 2019 Facebook  */
   3#include <stdio.h>
   4#include <errno.h>
   5#include <string.h>
   6
   7#include <bpf/bpf.h>
   8#include <bpf/libbpf.h>
   9
  10#include <bpf_util.h>
  11#include <test_maps.h>
  12
  13static void map_batch_update(int map_fd, __u32 max_entries, int *keys,
  14                             void *values, bool is_pcpu)
  15{
  16        typedef BPF_DECLARE_PERCPU(int, value);
  17        value *v = NULL;
  18        int i, j, err;
  19        DECLARE_LIBBPF_OPTS(bpf_map_batch_opts, opts,
  20                .elem_flags = 0,
  21                .flags = 0,
  22        );
  23
  24        if (is_pcpu)
  25                v = (value *)values;
  26
  27        for (i = 0; i < max_entries; i++) {
  28                keys[i] = i + 1;
  29                if (is_pcpu)
  30                        for (j = 0; j < bpf_num_possible_cpus(); j++)
  31                                bpf_percpu(v[i], j) = i + 2 + j;
  32                else
  33                        ((int *)values)[i] = i + 2;
  34        }
  35
  36        err = bpf_map_update_batch(map_fd, keys, values, &max_entries, &opts);
  37        CHECK(err, "bpf_map_update_batch()", "error:%s\n", strerror(errno));
  38}
  39
  40static void map_batch_verify(int *visited, __u32 max_entries,
  41                             int *keys, void *values, bool is_pcpu)
  42{
  43        typedef BPF_DECLARE_PERCPU(int, value);
  44        value *v = NULL;
  45        int i, j;
  46
  47        if (is_pcpu)
  48                v = (value *)values;
  49
  50        memset(visited, 0, max_entries * sizeof(*visited));
  51        for (i = 0; i < max_entries; i++) {
  52
  53                if (is_pcpu) {
  54                        for (j = 0; j < bpf_num_possible_cpus(); j++) {
  55                                CHECK(keys[i] + 1 + j != bpf_percpu(v[i], j),
  56                                      "key/value checking",
  57                                      "error: i %d j %d key %d value %d\n",
  58                                      i, j, keys[i], bpf_percpu(v[i],  j));
  59                        }
  60                } else {
  61                        CHECK(keys[i] + 1 != ((int *)values)[i],
  62                              "key/value checking",
  63                              "error: i %d key %d value %d\n", i, keys[i],
  64                              ((int *)values)[i]);
  65                }
  66
  67                visited[i] = 1;
  68
  69        }
  70        for (i = 0; i < max_entries; i++) {
  71                CHECK(visited[i] != 1, "visited checking",
  72                      "error: keys array at index %d missing\n", i);
  73        }
  74}
  75
  76void __test_map_lookup_and_delete_batch(bool is_pcpu)
  77{
  78        __u32 batch, count, total, total_success;
  79        typedef BPF_DECLARE_PERCPU(int, value);
  80        int map_fd, *keys, *visited, key;
  81        const __u32 max_entries = 10;
  82        value pcpu_values[max_entries];
  83        int err, step, value_size;
  84        bool nospace_err;
  85        void *values;
  86        struct bpf_create_map_attr xattr = {
  87                .name = "hash_map",
  88                .map_type = is_pcpu ? BPF_MAP_TYPE_PERCPU_HASH :
  89                            BPF_MAP_TYPE_HASH,
  90                .key_size = sizeof(int),
  91                .value_size = sizeof(int),
  92        };
  93        DECLARE_LIBBPF_OPTS(bpf_map_batch_opts, opts,
  94                .elem_flags = 0,
  95                .flags = 0,
  96        );
  97
  98        xattr.max_entries = max_entries;
  99        map_fd = bpf_create_map_xattr(&xattr);
 100        CHECK(map_fd == -1,
 101              "bpf_create_map_xattr()", "error:%s\n", strerror(errno));
 102
 103        value_size = is_pcpu ? sizeof(value) : sizeof(int);
 104        keys = malloc(max_entries * sizeof(int));
 105        if (is_pcpu)
 106                values = pcpu_values;
 107        else
 108                values = malloc(max_entries * sizeof(int));
 109        visited = malloc(max_entries * sizeof(int));
 110        CHECK(!keys || !values || !visited, "malloc()",
 111              "error:%s\n", strerror(errno));
 112
 113        /* test 1: lookup/delete an empty hash table, -ENOENT */
 114        count = max_entries;
 115        err = bpf_map_lookup_and_delete_batch(map_fd, NULL, &batch, keys,
 116                                              values, &count, &opts);
 117        CHECK((err && errno != ENOENT), "empty map",
 118              "error: %s\n", strerror(errno));
 119
 120        /* populate elements to the map */
 121        map_batch_update(map_fd, max_entries, keys, values, is_pcpu);
 122
 123        /* test 2: lookup/delete with count = 0, success */
 124        count = 0;
 125        err = bpf_map_lookup_and_delete_batch(map_fd, NULL, &batch, keys,
 126                                              values, &count, &opts);
 127        CHECK(err, "count = 0", "error: %s\n", strerror(errno));
 128
 129        /* test 3: lookup/delete with count = max_entries, success */
 130        memset(keys, 0, max_entries * sizeof(*keys));
 131        memset(values, 0, max_entries * value_size);
 132        count = max_entries;
 133        err = bpf_map_lookup_and_delete_batch(map_fd, NULL, &batch, keys,
 134                                              values, &count, &opts);
 135        CHECK((err && errno != ENOENT), "count = max_entries",
 136               "error: %s\n", strerror(errno));
 137        CHECK(count != max_entries, "count = max_entries",
 138              "count = %u, max_entries = %u\n", count, max_entries);
 139        map_batch_verify(visited, max_entries, keys, values, is_pcpu);
 140
 141        /* bpf_map_get_next_key() should return -ENOENT for an empty map. */
 142        err = bpf_map_get_next_key(map_fd, NULL, &key);
 143        CHECK(!err, "bpf_map_get_next_key()", "error: %s\n", strerror(errno));
 144
 145        /* test 4: lookup/delete in a loop with various steps. */
 146        total_success = 0;
 147        for (step = 1; step < max_entries; step++) {
 148                map_batch_update(map_fd, max_entries, keys, values, is_pcpu);
 149                memset(keys, 0, max_entries * sizeof(*keys));
 150                memset(values, 0, max_entries * value_size);
 151                total = 0;
 152                /* iteratively lookup/delete elements with 'step'
 153                 * elements each
 154                 */
 155                count = step;
 156                nospace_err = false;
 157                while (true) {
 158                        err = bpf_map_lookup_batch(map_fd,
 159                                                   total ? &batch : NULL,
 160                                                   &batch, keys + total,
 161                                                   values +
 162                                                   total * value_size,
 163                                                   &count, &opts);
 164                        /* It is possible that we are failing due to buffer size
 165                         * not big enough. In such cases, let us just exit and
 166                         * go with large steps. Not that a buffer size with
 167                         * max_entries should always work.
 168                         */
 169                        if (err && errno == ENOSPC) {
 170                                nospace_err = true;
 171                                break;
 172                        }
 173
 174                        CHECK((err && errno != ENOENT), "lookup with steps",
 175                              "error: %s\n", strerror(errno));
 176
 177                        total += count;
 178                        if (err)
 179                                break;
 180
 181                }
 182                if (nospace_err == true)
 183                        continue;
 184
 185                CHECK(total != max_entries, "lookup with steps",
 186                      "total = %u, max_entries = %u\n", total, max_entries);
 187                map_batch_verify(visited, max_entries, keys, values, is_pcpu);
 188
 189                total = 0;
 190                count = step;
 191                while (total < max_entries) {
 192                        if (max_entries - total < step)
 193                                count = max_entries - total;
 194                        err = bpf_map_delete_batch(map_fd,
 195                                                   keys + total,
 196                                                   &count, &opts);
 197                        CHECK((err && errno != ENOENT), "delete batch",
 198                              "error: %s\n", strerror(errno));
 199                        total += count;
 200                        if (err)
 201                                break;
 202                }
 203                CHECK(total != max_entries, "delete with steps",
 204                      "total = %u, max_entries = %u\n", total, max_entries);
 205
 206                /* check map is empty, errono == ENOENT */
 207                err = bpf_map_get_next_key(map_fd, NULL, &key);
 208                CHECK(!err || errno != ENOENT, "bpf_map_get_next_key()",
 209                      "error: %s\n", strerror(errno));
 210
 211                /* iteratively lookup/delete elements with 'step'
 212                 * elements each
 213                 */
 214                map_batch_update(map_fd, max_entries, keys, values, is_pcpu);
 215                memset(keys, 0, max_entries * sizeof(*keys));
 216                memset(values, 0, max_entries * value_size);
 217                total = 0;
 218                count = step;
 219                nospace_err = false;
 220                while (true) {
 221                        err = bpf_map_lookup_and_delete_batch(map_fd,
 222                                                        total ? &batch : NULL,
 223                                                        &batch, keys + total,
 224                                                        values +
 225                                                        total * value_size,
 226                                                        &count, &opts);
 227                        /* It is possible that we are failing due to buffer size
 228                         * not big enough. In such cases, let us just exit and
 229                         * go with large steps. Not that a buffer size with
 230                         * max_entries should always work.
 231                         */
 232                        if (err && errno == ENOSPC) {
 233                                nospace_err = true;
 234                                break;
 235                        }
 236
 237                        CHECK((err && errno != ENOENT), "lookup with steps",
 238                              "error: %s\n", strerror(errno));
 239
 240                        total += count;
 241                        if (err)
 242                                break;
 243                }
 244
 245                if (nospace_err == true)
 246                        continue;
 247
 248                CHECK(total != max_entries, "lookup/delete with steps",
 249                      "total = %u, max_entries = %u\n", total, max_entries);
 250
 251                map_batch_verify(visited, max_entries, keys, values, is_pcpu);
 252                err = bpf_map_get_next_key(map_fd, NULL, &key);
 253                CHECK(!err, "bpf_map_get_next_key()", "error: %s\n",
 254                      strerror(errno));
 255
 256                total_success++;
 257        }
 258
 259        CHECK(total_success == 0, "check total_success",
 260              "unexpected failure\n");
 261        free(keys);
 262        free(visited);
 263        if (!is_pcpu)
 264                free(values);
 265}
 266
 267void htab_map_batch_ops(void)
 268{
 269        __test_map_lookup_and_delete_batch(false);
 270        printf("test_%s:PASS\n", __func__);
 271}
 272
 273void htab_percpu_map_batch_ops(void)
 274{
 275        __test_map_lookup_and_delete_batch(true);
 276        printf("test_%s:PASS\n", __func__);
 277}
 278
 279void test_htab_map_batch_ops(void)
 280{
 281        htab_map_batch_ops();
 282        htab_percpu_map_batch_ops();
 283}
 284