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