at v4.6-rc1 492 lines 13 kB view raw
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}