at v4.9-rc6 503 lines 14 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 /* 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}