linux/samples/bpf/test_maps.c
<<
>>
Prefs
   1/*
   2 * Testsuite for eBPF maps
   3 *
   4 * Copyright (c) 2014 PLUMgrid, http://plumgrid.com
   5 * Copyright (c) 2016 Facebook
   6 *
   7 * This program is free software; you can redistribute it and/or
   8 * modify it under the terms of version 2 of the GNU General Public
   9 * License as published by the Free Software Foundation.
  10 */
  11#include <stdio.h>
  12#include <unistd.h>
  13#include <linux/bpf.h>
  14#include <errno.h>
  15#include <string.h>
  16#include <assert.h>
  17#include <sys/wait.h>
  18#include <stdlib.h>
  19#include "libbpf.h"
  20
  21static int map_flags;
  22
  23/* sanity tests for map API */
  24static void test_hashmap_sanity(int i, void *data)
  25{
  26        long long key, next_key, value;
  27        int map_fd;
  28
  29        map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value),
  30                                2, map_flags);
  31        if (map_fd < 0) {
  32                printf("failed to create hashmap '%s'\n", strerror(errno));
  33                exit(1);
  34        }
  35
  36        key = 1;
  37        value = 1234;
  38        /* insert key=1 element */
  39        assert(bpf_update_elem(map_fd, &key, &value, BPF_ANY) == 0);
  40
  41        value = 0;
  42        /* BPF_NOEXIST means: add new element if it doesn't exist */
  43        assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == -1 &&
  44               /* key=1 already exists */
  45               errno == EEXIST);
  46
  47        assert(bpf_update_elem(map_fd, &key, &value, -1) == -1 && errno == EINVAL);
  48
  49        /* check that key=1 can be found */
  50        assert(bpf_lookup_elem(map_fd, &key, &value) == 0 && value == 1234);
  51
  52        key = 2;
  53        /* check that key=2 is not found */
  54        assert(bpf_lookup_elem(map_fd, &key, &value) == -1 && errno == ENOENT);
  55
  56        /* BPF_EXIST means: update existing element */
  57        assert(bpf_update_elem(map_fd, &key, &value, BPF_EXIST) == -1 &&
  58               /* key=2 is not there */
  59               errno == ENOENT);
  60
  61        /* insert key=2 element */
  62        assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == 0);
  63
  64        /* key=1 and key=2 were inserted, check that key=0 cannot be inserted
  65         * due to max_entries limit
  66         */
  67        key = 0;
  68        assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == -1 &&
  69               errno == E2BIG);
  70
  71        /* check that key = 0 doesn't exist */
  72        assert(bpf_delete_elem(map_fd, &key) == -1 && errno == ENOENT);
  73
  74        /* iterate over two elements */
  75        assert(bpf_get_next_key(map_fd, &key, &next_key) == 0 &&
  76               (next_key == 1 || next_key == 2));
  77        assert(bpf_get_next_key(map_fd, &next_key, &next_key) == 0 &&
  78               (next_key == 1 || next_key == 2));
  79        assert(bpf_get_next_key(map_fd, &next_key, &next_key) == -1 &&
  80               errno == ENOENT);
  81
  82        /* delete both elements */
  83        key = 1;
  84        assert(bpf_delete_elem(map_fd, &key) == 0);
  85        key = 2;
  86        assert(bpf_delete_elem(map_fd, &key) == 0);
  87        assert(bpf_delete_elem(map_fd, &key) == -1 && errno == ENOENT);
  88
  89        key = 0;
  90        /* check that map is empty */
  91        assert(bpf_get_next_key(map_fd, &key, &next_key) == -1 &&
  92               errno == ENOENT);
  93        close(map_fd);
  94}
  95
  96/* sanity tests for percpu map API */
  97static void test_percpu_hashmap_sanity(int task, void *data)
  98{
  99        long long key, next_key;
 100        int expected_key_mask = 0;
 101        unsigned int nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
 102        long long value[nr_cpus];
 103        int map_fd, i;
 104
 105        map_fd = bpf_create_map(BPF_MAP_TYPE_PERCPU_HASH, sizeof(key),
 106                                sizeof(value[0]), 2, map_flags);
 107        if (map_fd < 0) {
 108                printf("failed to create hashmap '%s'\n", strerror(errno));
 109                exit(1);
 110        }
 111
 112        for (i = 0; i < nr_cpus; i++)
 113                value[i] = i + 100;
 114        key = 1;
 115        /* insert key=1 element */
 116        assert(!(expected_key_mask & key));
 117        assert(bpf_update_elem(map_fd, &key, value, BPF_ANY) == 0);
 118        expected_key_mask |= key;
 119
 120        /* BPF_NOEXIST means: add new element if it doesn't exist */
 121        assert(bpf_update_elem(map_fd, &key, value, BPF_NOEXIST) == -1 &&
 122               /* key=1 already exists */
 123               errno == EEXIST);
 124
 125        /* -1 is an invalid flag */
 126        assert(bpf_update_elem(map_fd, &key, value, -1) == -1 &&
 127               errno == EINVAL);
 128
 129        /* check that key=1 can be found. value could be 0 if the lookup
 130         * was run from a different cpu.
 131         */
 132        value[0] = 1;
 133        assert(bpf_lookup_elem(map_fd, &key, value) == 0 && value[0] == 100);
 134
 135        key = 2;
 136        /* check that key=2 is not found */
 137        assert(bpf_lookup_elem(map_fd, &key, value) == -1 && errno == ENOENT);
 138
 139        /* BPF_EXIST means: update existing element */
 140        assert(bpf_update_elem(map_fd, &key, value, BPF_EXIST) == -1 &&
 141               /* key=2 is not there */
 142               errno == ENOENT);
 143
 144        /* insert key=2 element */
 145        assert(!(expected_key_mask & key));
 146        assert(bpf_update_elem(map_fd, &key, value, BPF_NOEXIST) == 0);
 147        expected_key_mask |= key;
 148
 149        /* key=1 and key=2 were inserted, check that key=0 cannot be inserted
 150         * due to max_entries limit
 151         */
 152        key = 0;
 153        assert(bpf_update_elem(map_fd, &key, value, BPF_NOEXIST) == -1 &&
 154               errno == E2BIG);
 155
 156        /* check that key = 0 doesn't exist */
 157        assert(bpf_delete_elem(map_fd, &key) == -1 && errno == ENOENT);
 158
 159        /* iterate over two elements */
 160        while (!bpf_get_next_key(map_fd, &key, &next_key)) {
 161                assert((expected_key_mask & next_key) == next_key);
 162                expected_key_mask &= ~next_key;
 163
 164                assert(bpf_lookup_elem(map_fd, &next_key, value) == 0);
 165                for (i = 0; i < nr_cpus; i++)
 166                        assert(value[i] == i + 100);
 167
 168                key = next_key;
 169        }
 170        assert(errno == ENOENT);
 171
 172        /* Update with BPF_EXIST */
 173        key = 1;
 174        assert(bpf_update_elem(map_fd, &key, value, BPF_EXIST) == 0);
 175
 176        /* delete both elements */
 177        key = 1;
 178        assert(bpf_delete_elem(map_fd, &key) == 0);
 179        key = 2;
 180        assert(bpf_delete_elem(map_fd, &key) == 0);
 181        assert(bpf_delete_elem(map_fd, &key) == -1 && errno == ENOENT);
 182
 183        key = 0;
 184        /* check that map is empty */
 185        assert(bpf_get_next_key(map_fd, &key, &next_key) == -1 &&
 186               errno == ENOENT);
 187        close(map_fd);
 188}
 189
 190static void test_arraymap_sanity(int i, void *data)
 191{
 192        int key, next_key, map_fd;
 193        long long value;
 194
 195        map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(key), sizeof(value),
 196                                2, 0);
 197        if (map_fd < 0) {
 198                printf("failed to create arraymap '%s'\n", strerror(errno));
 199                exit(1);
 200        }
 201
 202        key = 1;
 203        value = 1234;
 204        /* insert key=1 element */
 205        assert(bpf_update_elem(map_fd, &key, &value, BPF_ANY) == 0);
 206
 207        value = 0;
 208        assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == -1 &&
 209               errno == EEXIST);
 210
 211        /* check that key=1 can be found */
 212        assert(bpf_lookup_elem(map_fd, &key, &value) == 0 && value == 1234);
 213
 214        key = 0;
 215        /* check that key=0 is also found and zero initialized */
 216        assert(bpf_lookup_elem(map_fd, &key, &value) == 0 && value == 0);
 217
 218
 219        /* key=0 and key=1 were inserted, check that key=2 cannot be inserted
 220         * due to max_entries limit
 221         */
 222        key = 2;
 223        assert(bpf_update_elem(map_fd, &key, &value, BPF_EXIST) == -1 &&
 224               errno == E2BIG);
 225
 226        /* check that key = 2 doesn't exist */
 227        assert(bpf_lookup_elem(map_fd, &key, &value) == -1 && errno == ENOENT);
 228
 229        /* iterate over two elements */
 230        assert(bpf_get_next_key(map_fd, &key, &next_key) == 0 &&
 231               next_key == 0);
 232        assert(bpf_get_next_key(map_fd, &next_key, &next_key) == 0 &&
 233               next_key == 1);
 234        assert(bpf_get_next_key(map_fd, &next_key, &next_key) == -1 &&
 235               errno == ENOENT);
 236
 237        /* delete shouldn't succeed */
 238        key = 1;
 239        assert(bpf_delete_elem(map_fd, &key) == -1 && errno == EINVAL);
 240
 241        close(map_fd);
 242}
 243
 244static void test_percpu_arraymap_many_keys(void)
 245{
 246        unsigned nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
 247        unsigned nr_keys = 20000;
 248        long values[nr_cpus];
 249        int key, map_fd, i;
 250
 251        map_fd = bpf_create_map(BPF_MAP_TYPE_PERCPU_ARRAY, sizeof(key),
 252                                sizeof(values[0]), nr_keys, 0);
 253        if (map_fd < 0) {
 254                printf("failed to create per-cpu arraymap '%s'\n",
 255                       strerror(errno));
 256                exit(1);
 257        }
 258
 259        for (i = 0; i < nr_cpus; i++)
 260                values[i] = i + 10;
 261
 262        for (key = 0; key < nr_keys; key++)
 263                assert(bpf_update_elem(map_fd, &key, values, BPF_ANY) == 0);
 264
 265        for (key = 0; key < nr_keys; key++) {
 266                for (i = 0; i < nr_cpus; i++)
 267                        values[i] = 0;
 268                assert(bpf_lookup_elem(map_fd, &key, values) == 0);
 269                for (i = 0; i < nr_cpus; i++)
 270                        assert(values[i] == i + 10);
 271        }
 272
 273        close(map_fd);
 274}
 275
 276static void test_percpu_arraymap_sanity(int i, void *data)
 277{
 278        unsigned nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
 279        long values[nr_cpus];
 280        int key, next_key, map_fd;
 281
 282        map_fd = bpf_create_map(BPF_MAP_TYPE_PERCPU_ARRAY, sizeof(key),
 283                                sizeof(values[0]), 2, 0);
 284        if (map_fd < 0) {
 285                printf("failed to create arraymap '%s'\n", strerror(errno));
 286                exit(1);
 287        }
 288
 289        for (i = 0; i < nr_cpus; i++)
 290                values[i] = i + 100;
 291
 292        key = 1;
 293        /* insert key=1 element */
 294        assert(bpf_update_elem(map_fd, &key, values, BPF_ANY) == 0);
 295
 296        values[0] = 0;
 297        assert(bpf_update_elem(map_fd, &key, values, BPF_NOEXIST) == -1 &&
 298               errno == EEXIST);
 299
 300        /* check that key=1 can be found */
 301        assert(bpf_lookup_elem(map_fd, &key, values) == 0 && values[0] == 100);
 302
 303        key = 0;
 304        /* check that key=0 is also found and zero initialized */
 305        assert(bpf_lookup_elem(map_fd, &key, values) == 0 &&
 306               values[0] == 0 && values[nr_cpus - 1] == 0);
 307
 308
 309        /* check that key=2 cannot be inserted due to max_entries limit */
 310        key = 2;
 311        assert(bpf_update_elem(map_fd, &key, values, BPF_EXIST) == -1 &&
 312               errno == E2BIG);
 313
 314        /* check that key = 2 doesn't exist */
 315        assert(bpf_lookup_elem(map_fd, &key, values) == -1 && errno == ENOENT);
 316
 317        /* iterate over two elements */
 318        assert(bpf_get_next_key(map_fd, &key, &next_key) == 0 &&
 319               next_key == 0);
 320        assert(bpf_get_next_key(map_fd, &next_key, &next_key) == 0 &&
 321               next_key == 1);
 322        assert(bpf_get_next_key(map_fd, &next_key, &next_key) == -1 &&
 323               errno == ENOENT);
 324
 325        /* delete shouldn't succeed */
 326        key = 1;
 327        assert(bpf_delete_elem(map_fd, &key) == -1 && errno == EINVAL);
 328
 329        close(map_fd);
 330}
 331
 332#define MAP_SIZE (32 * 1024)
 333static void test_map_large(void)
 334{
 335        struct bigkey {
 336                int a;
 337                char b[116];
 338                long long c;
 339        } key;
 340        int map_fd, i, value;
 341
 342        /* allocate 4Mbyte of memory */
 343        map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value),
 344                                MAP_SIZE, map_flags);
 345        if (map_fd < 0) {
 346                printf("failed to create large map '%s'\n", strerror(errno));
 347                exit(1);
 348        }
 349
 350        for (i = 0; i < MAP_SIZE; i++) {
 351                key = (struct bigkey) {.c = i};
 352                value = i;
 353                assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == 0);
 354        }
 355        key.c = -1;
 356        assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == -1 &&
 357               errno == E2BIG);
 358
 359        /* iterate through all elements */
 360        for (i = 0; i < MAP_SIZE; i++)
 361                assert(bpf_get_next_key(map_fd, &key, &key) == 0);
 362        assert(bpf_get_next_key(map_fd, &key, &key) == -1 && errno == ENOENT);
 363
 364        key.c = 0;
 365        assert(bpf_lookup_elem(map_fd, &key, &value) == 0 && value == 0);
 366        key.a = 1;
 367        assert(bpf_lookup_elem(map_fd, &key, &value) == -1 && errno == ENOENT);
 368
 369        close(map_fd);
 370}
 371
 372/* fork N children and wait for them to complete */
 373static void run_parallel(int tasks, void (*fn)(int i, void *data), void *data)
 374{
 375        pid_t pid[tasks];
 376        int i;
 377
 378        for (i = 0; i < tasks; i++) {
 379                pid[i] = fork();
 380                if (pid[i] == 0) {
 381                        fn(i, data);
 382                        exit(0);
 383                } else if (pid[i] == -1) {
 384                        printf("couldn't spawn #%d process\n", i);
 385                        exit(1);
 386                }
 387        }
 388        for (i = 0; i < tasks; i++) {
 389                int status;
 390
 391                assert(waitpid(pid[i], &status, 0) == pid[i]);
 392                assert(status == 0);
 393        }
 394}
 395
 396static void test_map_stress(void)
 397{
 398        run_parallel(100, test_hashmap_sanity, NULL);
 399        run_parallel(100, test_percpu_hashmap_sanity, NULL);
 400        run_parallel(100, test_arraymap_sanity, NULL);
 401        run_parallel(100, test_percpu_arraymap_sanity, NULL);
 402}
 403
 404#define TASKS 1024
 405#define DO_UPDATE 1
 406#define DO_DELETE 0
 407static void do_work(int fn, void *data)
 408{
 409        int map_fd = ((int *)data)[0];
 410        int do_update = ((int *)data)[1];
 411        int i;
 412        int key, value;
 413
 414        for (i = fn; i < MAP_SIZE; i += TASKS) {
 415                key = value = i;
 416                if (do_update)
 417                        assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == 0);
 418                else
 419                        assert(bpf_delete_elem(map_fd, &key) == 0);
 420        }
 421}
 422
 423static void test_map_parallel(void)
 424{
 425        int i, map_fd, key = 0, value = 0;
 426        int data[2];
 427
 428        map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value),
 429                                MAP_SIZE, map_flags);
 430        if (map_fd < 0) {
 431                printf("failed to create map for parallel test '%s'\n",
 432                       strerror(errno));
 433                exit(1);
 434        }
 435
 436        data[0] = map_fd;
 437        data[1] = DO_UPDATE;
 438        /* use the same map_fd in children to add elements to this map
 439         * child_0 adds key=0, key=1024, key=2048, ...
 440         * child_1 adds key=1, key=1025, key=2049, ...
 441         * child_1023 adds key=1023, ...
 442         */
 443        run_parallel(TASKS, do_work, data);
 444
 445        /* check that key=0 is already there */
 446        assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == -1 &&
 447               errno == EEXIST);
 448
 449        /* check that all elements were inserted */
 450        key = -1;
 451        for (i = 0; i < MAP_SIZE; i++)
 452                assert(bpf_get_next_key(map_fd, &key, &key) == 0);
 453        assert(bpf_get_next_key(map_fd, &key, &key) == -1 && errno == ENOENT);
 454
 455        /* another check for all elements */
 456        for (i = 0; i < MAP_SIZE; i++) {
 457                key = MAP_SIZE - i - 1;
 458                assert(bpf_lookup_elem(map_fd, &key, &value) == 0 &&
 459                       value == key);
 460        }
 461
 462        /* now let's delete all elemenets in parallel */
 463        data[1] = DO_DELETE;
 464        run_parallel(TASKS, do_work, data);
 465
 466        /* nothing should be left */
 467        key = -1;
 468        assert(bpf_get_next_key(map_fd, &key, &key) == -1 && errno == ENOENT);
 469}
 470
 471static void run_all_tests(void)
 472{
 473        test_hashmap_sanity(0, NULL);
 474        test_percpu_hashmap_sanity(0, NULL);
 475        test_arraymap_sanity(0, NULL);
 476        test_percpu_arraymap_sanity(0, NULL);
 477        test_percpu_arraymap_many_keys();
 478
 479        test_map_large();
 480        test_map_parallel();
 481        test_map_stress();
 482}
 483
 484int main(void)
 485{
 486        map_flags = 0;
 487        run_all_tests();
 488        map_flags = BPF_F_NO_PREALLOC;
 489        run_all_tests();
 490        printf("test_maps: OK\n");
 491        return 0;
 492}
 493