1/* 2 * Testsuite for eBPF maps 3 * 4 * Copyright (c) 2014 PLUMgrid, http://plumgrid.com 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of version 2 of the GNU General Public 8 * License as published by the Free Software Foundation. 9 */ 10#include <stdio.h> 11#include <unistd.h> 12#include <linux/bpf.h> 13#include <errno.h> 14#include <string.h> 15#include <assert.h> 16#include <sys/wait.h> 17#include <stdlib.h> 18#include "libbpf.h" 19 20/* sanity tests for map API */ 21static void test_hashmap_sanity(int i, void *data) 22{ 23 long long key, next_key, value; 24 int map_fd; 25 26 map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value), 2); 27 if (map_fd < 0) { 28 printf("failed to create hashmap '%s'\n", strerror(errno)); 29 exit(1); 30 } 31 32 key = 1; 33 value = 1234; 34 /* insert key=1 element */ 35 assert(bpf_update_elem(map_fd, &key, &value, BPF_ANY) == 0); 36 37 value = 0; 38 /* BPF_NOEXIST means: add new element if it doesn't exist */ 39 assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == -1 && 40 /* key=1 already exists */ 41 errno == EEXIST); 42 43 assert(bpf_update_elem(map_fd, &key, &value, -1) == -1 && errno == EINVAL); 44 45 /* check that key=1 can be found */ 46 assert(bpf_lookup_elem(map_fd, &key, &value) == 0 && value == 1234); 47 48 key = 2; 49 /* check that key=2 is not found */ 50 assert(bpf_lookup_elem(map_fd, &key, &value) == -1 && errno == ENOENT); 51 52 /* BPF_EXIST means: update existing element */ 53 assert(bpf_update_elem(map_fd, &key, &value, BPF_EXIST) == -1 && 54 /* key=2 is not there */ 55 errno == ENOENT); 56 57 /* insert key=2 element */ 58 assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == 0); 59 60 /* key=1 and key=2 were inserted, check that key=0 cannot be inserted 61 * due to max_entries limit 62 */ 63 key = 0; 64 assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == -1 && 65 errno == E2BIG); 66 67 /* check that key = 0 doesn't exist */ 68 assert(bpf_delete_elem(map_fd, &key) == -1 && errno == ENOENT); 69 70 /* iterate over two elements */ 71 assert(bpf_get_next_key(map_fd, &key, &next_key) == 0 && 72 (next_key == 1 || next_key == 2)); 73 assert(bpf_get_next_key(map_fd, &next_key, &next_key) == 0 && 74 (next_key == 1 || next_key == 2)); 75 assert(bpf_get_next_key(map_fd, &next_key, &next_key) == -1 && 76 errno == ENOENT); 77 78 /* delete both elements */ 79 key = 1; 80 assert(bpf_delete_elem(map_fd, &key) == 0); 81 key = 2; 82 assert(bpf_delete_elem(map_fd, &key) == 0); 83 assert(bpf_delete_elem(map_fd, &key) == -1 && errno == ENOENT); 84 85 key = 0; 86 /* check that map is empty */ 87 assert(bpf_get_next_key(map_fd, &key, &next_key) == -1 && 88 errno == ENOENT); 89 close(map_fd); 90} 91 92static void test_arraymap_sanity(int i, void *data) 93{ 94 int key, next_key, map_fd; 95 long long value; 96 97 map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(key), sizeof(value), 2); 98 if (map_fd < 0) { 99 printf("failed to create arraymap '%s'\n", strerror(errno)); 100 exit(1); 101 } 102 103 key = 1; 104 value = 1234; 105 /* insert key=1 element */ 106 assert(bpf_update_elem(map_fd, &key, &value, BPF_ANY) == 0); 107 108 value = 0; 109 assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == -1 && 110 errno == EEXIST); 111 112 /* check that key=1 can be found */ 113 assert(bpf_lookup_elem(map_fd, &key, &value) == 0 && value == 1234); 114 115 key = 0; 116 /* check that key=0 is also found and zero initialized */ 117 assert(bpf_lookup_elem(map_fd, &key, &value) == 0 && value == 0); 118 119 120 /* key=0 and key=1 were inserted, check that key=2 cannot be inserted 121 * due to max_entries limit 122 */ 123 key = 2; 124 assert(bpf_update_elem(map_fd, &key, &value, BPF_EXIST) == -1 && 125 errno == E2BIG); 126 127 /* check that key = 2 doesn't exist */ 128 assert(bpf_lookup_elem(map_fd, &key, &value) == -1 && errno == ENOENT); 129 130 /* iterate over two elements */ 131 assert(bpf_get_next_key(map_fd, &key, &next_key) == 0 && 132 next_key == 0); 133 assert(bpf_get_next_key(map_fd, &next_key, &next_key) == 0 && 134 next_key == 1); 135 assert(bpf_get_next_key(map_fd, &next_key, &next_key) == -1 && 136 errno == ENOENT); 137 138 /* delete shouldn't succeed */ 139 key = 1; 140 assert(bpf_delete_elem(map_fd, &key) == -1 && errno == EINVAL); 141 142 close(map_fd); 143} 144 145#define MAP_SIZE (32 * 1024) 146static void test_map_large(void) 147{ 148 struct bigkey { 149 int a; 150 char b[116]; 151 long long c; 152 } key; 153 int map_fd, i, value; 154 155 /* allocate 4Mbyte of memory */ 156 map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value), 157 MAP_SIZE); 158 if (map_fd < 0) { 159 printf("failed to create large map '%s'\n", strerror(errno)); 160 exit(1); 161 } 162 163 for (i = 0; i < MAP_SIZE; i++) { 164 key = (struct bigkey) {.c = i}; 165 value = i; 166 assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == 0); 167 } 168 key.c = -1; 169 assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == -1 && 170 errno == E2BIG); 171 172 /* iterate through all elements */ 173 for (i = 0; i < MAP_SIZE; i++) 174 assert(bpf_get_next_key(map_fd, &key, &key) == 0); 175 assert(bpf_get_next_key(map_fd, &key, &key) == -1 && errno == ENOENT); 176 177 key.c = 0; 178 assert(bpf_lookup_elem(map_fd, &key, &value) == 0 && value == 0); 179 key.a = 1; 180 assert(bpf_lookup_elem(map_fd, &key, &value) == -1 && errno == ENOENT); 181 182 close(map_fd); 183} 184 185/* fork N children and wait for them to complete */ 186static void run_parallel(int tasks, void (*fn)(int i, void *data), void *data) 187{ 188 pid_t pid[tasks]; 189 int i; 190 191 for (i = 0; i < tasks; i++) { 192 pid[i] = fork(); 193 if (pid[i] == 0) { 194 fn(i, data); 195 exit(0); 196 } else if (pid[i] == -1) { 197 printf("couldn't spawn #%d process\n", i); 198 exit(1); 199 } 200 } 201 for (i = 0; i < tasks; i++) { 202 int status; 203 204 assert(waitpid(pid[i], &status, 0) == pid[i]); 205 assert(status == 0); 206 } 207} 208 209static void test_map_stress(void) 210{ 211 run_parallel(100, test_hashmap_sanity, NULL); 212 run_parallel(100, test_arraymap_sanity, NULL); 213} 214 215#define TASKS 1024 216#define DO_UPDATE 1 217#define DO_DELETE 0 218static void do_work(int fn, void *data) 219{ 220 int map_fd = ((int *)data)[0]; 221 int do_update = ((int *)data)[1]; 222 int i; 223 int key, value; 224 225 for (i = fn; i < MAP_SIZE; i += TASKS) { 226 key = value = i; 227 if (do_update) 228 assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == 0); 229 else 230 assert(bpf_delete_elem(map_fd, &key) == 0); 231 } 232} 233 234static void test_map_parallel(void) 235{ 236 int i, map_fd, key = 0, value = 0; 237 int data[2]; 238 239 map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value), 240 MAP_SIZE); 241 if (map_fd < 0) { 242 printf("failed to create map for parallel test '%s'\n", 243 strerror(errno)); 244 exit(1); 245 } 246 247 data[0] = map_fd; 248 data[1] = DO_UPDATE; 249 /* use the same map_fd in children to add elements to this map 250 * child_0 adds key=0, key=1024, key=2048, ... 251 * child_1 adds key=1, key=1025, key=2049, ... 252 * child_1023 adds key=1023, ... 253 */ 254 run_parallel(TASKS, do_work, data); 255 256 /* check that key=0 is already there */ 257 assert(bpf_update_elem(map_fd, &key, &value, BPF_NOEXIST) == -1 && 258 errno == EEXIST); 259 260 /* check that all elements were inserted */ 261 key = -1; 262 for (i = 0; i < MAP_SIZE; i++) 263 assert(bpf_get_next_key(map_fd, &key, &key) == 0); 264 assert(bpf_get_next_key(map_fd, &key, &key) == -1 && errno == ENOENT); 265 266 /* another check for all elements */ 267 for (i = 0; i < MAP_SIZE; i++) { 268 key = MAP_SIZE - i - 1; 269 assert(bpf_lookup_elem(map_fd, &key, &value) == 0 && 270 value == key); 271 } 272 273 /* now let's delete all elemenets in parallel */ 274 data[1] = DO_DELETE; 275 run_parallel(TASKS, do_work, data); 276 277 /* nothing should be left */ 278 key = -1; 279 assert(bpf_get_next_key(map_fd, &key, &key) == -1 && errno == ENOENT); 280} 281 282int main(void) 283{ 284 test_hashmap_sanity(0, NULL); 285 test_arraymap_sanity(0, NULL); 286 test_map_large(); 287 test_map_parallel(); 288 test_map_stress(); 289 printf("test_maps: OK\n"); 290 return 0; 291} 292