Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

bpf: Allow bpf sample programs (*_user.c) to change bpf_map_def

The current bpf_map_def is statically defined during compile
time. This patch allows the *_user.c program to change it during
runtime. It is done by adding load_bpf_file_fixup_map() which
takes a callback. The callback will be called before creating
each map so that it has a chance to modify the bpf_map_def.

The current usecase is to change max_entries in map_perf_test.
It is interesting to test with a much bigger map size in
some cases (e.g. the following patch on bpf_lru_map.c).
However, it is hard to find one size to fit all testing
environment. Hence, it is handy to take the max_entries
as a cmdline arg and then configure the bpf_map_def during
runtime.

This patch adds two cmdline args. One is to configure
the map's max_entries. Another is to configure the max_cnt
which controls how many times a syscall is called.

Signed-off-by: Martin KaFai Lau <kafai@fb.com>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Martin KaFai Lau and committed by
David S. Miller
9fd63d05 bf8db5d2

+201 -74
+96 -18
samples/bpf/bpf_load.c
··· 21 21 #include <sys/mman.h> 22 22 #include <poll.h> 23 23 #include <ctype.h> 24 + #include <assert.h> 24 25 #include "libbpf.h" 25 26 #include "bpf_load.h" 26 27 #include "perf-sys.h" ··· 37 36 int event_fd[MAX_PROGS]; 38 37 int prog_cnt; 39 38 int prog_array_fd = -1; 40 - 41 - struct bpf_map_def { 42 - unsigned int type; 43 - unsigned int key_size; 44 - unsigned int value_size; 45 - unsigned int max_entries; 46 - unsigned int map_flags; 47 - unsigned int inner_map_idx; 48 - }; 49 39 50 40 static int populate_prog_array(const char *event, int prog_fd) 51 41 { ··· 185 193 return 0; 186 194 } 187 195 188 - static int load_maps(struct bpf_map_def *maps, int len) 196 + static int load_maps(struct bpf_map_def *maps, int len, 197 + const char **map_names, fixup_map_cb fixup_map) 189 198 { 190 199 int i; 191 200 192 201 for (i = 0; i < len / sizeof(struct bpf_map_def); i++) { 202 + if (fixup_map) 203 + fixup_map(&maps[i], map_names[i], i); 193 204 194 205 if (maps[i].type == BPF_MAP_TYPE_ARRAY_OF_MAPS || 195 206 maps[i].type == BPF_MAP_TYPE_HASH_OF_MAPS) { ··· 275 280 return 0; 276 281 } 277 282 278 - int load_bpf_file(char *path) 283 + static int cmp_symbols(const void *l, const void *r) 279 284 { 280 - int fd, i; 285 + const GElf_Sym *lsym = (const GElf_Sym *)l; 286 + const GElf_Sym *rsym = (const GElf_Sym *)r; 287 + 288 + if (lsym->st_value < rsym->st_value) 289 + return -1; 290 + else if (lsym->st_value > rsym->st_value) 291 + return 1; 292 + else 293 + return 0; 294 + } 295 + 296 + static int get_sorted_map_names(Elf *elf, Elf_Data *symbols, int maps_shndx, 297 + int strtabidx, char **map_names) 298 + { 299 + GElf_Sym map_symbols[MAX_MAPS]; 300 + int i, nr_maps = 0; 301 + 302 + for (i = 0; i < symbols->d_size / sizeof(GElf_Sym); i++) { 303 + assert(nr_maps < MAX_MAPS); 304 + if (!gelf_getsym(symbols, i, &map_symbols[nr_maps])) 305 + continue; 306 + if (map_symbols[nr_maps].st_shndx != maps_shndx) 307 + continue; 308 + nr_maps++; 309 + } 310 + 311 + qsort(map_symbols, nr_maps, sizeof(GElf_Sym), cmp_symbols); 312 + 313 + for (i = 0; i < nr_maps; i++) { 314 + char *map_name; 315 + 316 + map_name = elf_strptr(elf, strtabidx, map_symbols[i].st_name); 317 + if (!map_name) { 318 + printf("cannot get map symbol\n"); 319 + return 1; 320 + } 321 + 322 + map_names[i] = strdup(map_name); 323 + if (!map_names[i]) { 324 + printf("strdup(%s): %s(%d)\n", map_name, 325 + strerror(errno), errno); 326 + return 1; 327 + } 328 + } 329 + 330 + return 0; 331 + } 332 + 333 + static int do_load_bpf_file(const char *path, fixup_map_cb fixup_map) 334 + { 335 + int fd, i, ret, maps_shndx = -1, strtabidx = -1; 281 336 Elf *elf; 282 337 GElf_Ehdr ehdr; 283 338 GElf_Shdr shdr, shdr_prog; 284 - Elf_Data *data, *data_prog, *symbols = NULL; 285 - char *shname, *shname_prog; 339 + Elf_Data *data, *data_prog, *data_maps = NULL, *symbols = NULL; 340 + char *shname, *shname_prog, *map_names[MAX_MAPS] = { NULL }; 286 341 287 342 /* reset global variables */ 288 343 kern_version = 0; ··· 380 335 } 381 336 memcpy(&kern_version, data->d_buf, sizeof(int)); 382 337 } else if (strcmp(shname, "maps") == 0) { 383 - processed_sec[i] = true; 384 - if (load_maps(data->d_buf, data->d_size)) 385 - return 1; 338 + maps_shndx = i; 339 + data_maps = data; 386 340 } else if (shdr.sh_type == SHT_SYMTAB) { 341 + strtabidx = shdr.sh_link; 387 342 symbols = data; 388 343 } 344 + } 345 + 346 + ret = 1; 347 + 348 + if (!symbols) { 349 + printf("missing SHT_SYMTAB section\n"); 350 + goto done; 351 + } 352 + 353 + if (data_maps) { 354 + if (get_sorted_map_names(elf, symbols, maps_shndx, strtabidx, 355 + map_names)) 356 + goto done; 357 + 358 + if (load_maps(data_maps->d_buf, data_maps->d_size, 359 + (const char **)map_names, fixup_map)) 360 + goto done; 361 + 362 + processed_sec[maps_shndx] = true; 389 363 } 390 364 391 365 /* load programs that need map fixup (relocations) */ ··· 463 399 load_and_attach(shname, data->d_buf, data->d_size); 464 400 } 465 401 402 + ret = 0; 403 + done: 404 + for (i = 0; i < MAX_MAPS; i++) 405 + free(map_names[i]); 466 406 close(fd); 467 - return 0; 407 + return ret; 408 + } 409 + 410 + int load_bpf_file(char *path) 411 + { 412 + return do_load_bpf_file(path, NULL); 413 + } 414 + 415 + int load_bpf_file_fixup_map(const char *path, fixup_map_cb fixup_map) 416 + { 417 + return do_load_bpf_file(path, fixup_map); 468 418 } 469 419 470 420 void read_trace_pipe(void)
+13
samples/bpf/bpf_load.h
··· 6 6 #define MAX_MAPS 32 7 7 #define MAX_PROGS 32 8 8 9 + struct bpf_map_def { 10 + unsigned int type; 11 + unsigned int key_size; 12 + unsigned int value_size; 13 + unsigned int max_entries; 14 + unsigned int map_flags; 15 + unsigned int inner_map_idx; 16 + }; 17 + 18 + typedef void (*fixup_map_cb)(struct bpf_map_def *map, const char *map_name, 19 + int idx); 20 + 9 21 extern int map_fd[MAX_MAPS]; 10 22 extern int prog_fd[MAX_PROGS]; 11 23 extern int event_fd[MAX_PROGS]; ··· 37 25 * returns zero on success 38 26 */ 39 27 int load_bpf_file(char *path); 28 + int load_bpf_file_fixup_map(const char *path, fixup_map_cb fixup_map); 40 29 41 30 void read_trace_pipe(void); 42 31 struct ksym {
+92 -56
samples/bpf/map_perf_test_user.c
··· 24 24 #include "libbpf.h" 25 25 #include "bpf_load.h" 26 26 27 - #define MAX_CNT 1000000 27 + #define TEST_BIT(t) (1U << (t)) 28 28 29 29 static __u64 time_get_ns(void) 30 30 { ··· 34 34 return ts.tv_sec * 1000000000ull + ts.tv_nsec; 35 35 } 36 36 37 - #define HASH_PREALLOC (1 << 0) 38 - #define PERCPU_HASH_PREALLOC (1 << 1) 39 - #define HASH_KMALLOC (1 << 2) 40 - #define PERCPU_HASH_KMALLOC (1 << 3) 41 - #define LRU_HASH_PREALLOC (1 << 4) 42 - #define NOCOMMON_LRU_HASH_PREALLOC (1 << 5) 43 - #define LPM_KMALLOC (1 << 6) 44 - #define HASH_LOOKUP (1 << 7) 45 - #define ARRAY_LOOKUP (1 << 8) 37 + enum test_type { 38 + HASH_PREALLOC, 39 + PERCPU_HASH_PREALLOC, 40 + HASH_KMALLOC, 41 + PERCPU_HASH_KMALLOC, 42 + LRU_HASH_PREALLOC, 43 + NOCOMMON_LRU_HASH_PREALLOC, 44 + LPM_KMALLOC, 45 + HASH_LOOKUP, 46 + ARRAY_LOOKUP, 47 + NR_TESTS, 48 + }; 49 + 50 + const char *test_map_names[NR_TESTS] = { 51 + [HASH_PREALLOC] = "hash_map", 52 + [PERCPU_HASH_PREALLOC] = "percpu_hash_map", 53 + [HASH_KMALLOC] = "hash_map_alloc", 54 + [PERCPU_HASH_KMALLOC] = "percpu_hash_map_alloc", 55 + [LRU_HASH_PREALLOC] = "lru_hash_map", 56 + [NOCOMMON_LRU_HASH_PREALLOC] = "nocommon_lru_hash_map", 57 + [LPM_KMALLOC] = "lpm_trie_map_alloc", 58 + [HASH_LOOKUP] = "hash_map", 59 + [ARRAY_LOOKUP] = "array_map", 60 + }; 46 61 47 62 static int test_flags = ~0; 63 + static uint32_t num_map_entries; 64 + static uint32_t max_cnt = 1000000; 65 + 66 + static int check_test_flags(enum test_type t) 67 + { 68 + return test_flags & TEST_BIT(t); 69 + } 48 70 49 71 static void test_hash_prealloc(int cpu) 50 72 { ··· 74 52 int i; 75 53 76 54 start_time = time_get_ns(); 77 - for (i = 0; i < MAX_CNT; i++) 55 + for (i = 0; i < max_cnt; i++) 78 56 syscall(__NR_getuid); 79 57 printf("%d:hash_map_perf pre-alloc %lld events per sec\n", 80 - cpu, MAX_CNT * 1000000000ll / (time_get_ns() - start_time)); 58 + cpu, max_cnt * 1000000000ll / (time_get_ns() - start_time)); 81 59 } 82 60 83 - static void do_test_lru(int lru_test_flag, int cpu) 61 + static void do_test_lru(enum test_type test, int cpu) 84 62 { 85 63 struct sockaddr_in6 in6 = { .sin6_family = AF_INET6 }; 86 64 const char *test_name; ··· 90 68 in6.sin6_addr.s6_addr16[0] = 0xdead; 91 69 in6.sin6_addr.s6_addr16[1] = 0xbeef; 92 70 93 - if (lru_test_flag & LRU_HASH_PREALLOC) { 71 + if (test == LRU_HASH_PREALLOC) { 94 72 test_name = "lru_hash_map_perf"; 95 73 in6.sin6_addr.s6_addr16[7] = 0; 96 - } else if (lru_test_flag & NOCOMMON_LRU_HASH_PREALLOC) { 74 + } else if (test == NOCOMMON_LRU_HASH_PREALLOC) { 97 75 test_name = "nocommon_lru_hash_map_perf"; 98 76 in6.sin6_addr.s6_addr16[7] = 1; 99 77 } else { ··· 101 79 } 102 80 103 81 start_time = time_get_ns(); 104 - for (i = 0; i < MAX_CNT; i++) { 82 + for (i = 0; i < max_cnt; i++) { 105 83 ret = connect(-1, (const struct sockaddr *)&in6, sizeof(in6)); 106 84 assert(ret == -1 && errno == EBADF); 107 85 } 108 86 printf("%d:%s pre-alloc %lld events per sec\n", 109 87 cpu, test_name, 110 - MAX_CNT * 1000000000ll / (time_get_ns() - start_time)); 88 + max_cnt * 1000000000ll / (time_get_ns() - start_time)); 111 89 } 112 90 113 91 static void test_lru_hash_prealloc(int cpu) ··· 126 104 int i; 127 105 128 106 start_time = time_get_ns(); 129 - for (i = 0; i < MAX_CNT; i++) 107 + for (i = 0; i < max_cnt; i++) 130 108 syscall(__NR_geteuid); 131 109 printf("%d:percpu_hash_map_perf pre-alloc %lld events per sec\n", 132 - cpu, MAX_CNT * 1000000000ll / (time_get_ns() - start_time)); 110 + cpu, max_cnt * 1000000000ll / (time_get_ns() - start_time)); 133 111 } 134 112 135 113 static void test_hash_kmalloc(int cpu) ··· 138 116 int i; 139 117 140 118 start_time = time_get_ns(); 141 - for (i = 0; i < MAX_CNT; i++) 119 + for (i = 0; i < max_cnt; i++) 142 120 syscall(__NR_getgid); 143 121 printf("%d:hash_map_perf kmalloc %lld events per sec\n", 144 - cpu, MAX_CNT * 1000000000ll / (time_get_ns() - start_time)); 122 + cpu, max_cnt * 1000000000ll / (time_get_ns() - start_time)); 145 123 } 146 124 147 125 static void test_percpu_hash_kmalloc(int cpu) ··· 150 128 int i; 151 129 152 130 start_time = time_get_ns(); 153 - for (i = 0; i < MAX_CNT; i++) 131 + for (i = 0; i < max_cnt; i++) 154 132 syscall(__NR_getegid); 155 133 printf("%d:percpu_hash_map_perf kmalloc %lld events per sec\n", 156 - cpu, MAX_CNT * 1000000000ll / (time_get_ns() - start_time)); 134 + cpu, max_cnt * 1000000000ll / (time_get_ns() - start_time)); 157 135 } 158 136 159 137 static void test_lpm_kmalloc(int cpu) ··· 162 140 int i; 163 141 164 142 start_time = time_get_ns(); 165 - for (i = 0; i < MAX_CNT; i++) 143 + for (i = 0; i < max_cnt; i++) 166 144 syscall(__NR_gettid); 167 145 printf("%d:lpm_perf kmalloc %lld events per sec\n", 168 - cpu, MAX_CNT * 1000000000ll / (time_get_ns() - start_time)); 146 + cpu, max_cnt * 1000000000ll / (time_get_ns() - start_time)); 169 147 } 170 148 171 149 static void test_hash_lookup(int cpu) ··· 174 152 int i; 175 153 176 154 start_time = time_get_ns(); 177 - for (i = 0; i < MAX_CNT; i++) 155 + for (i = 0; i < max_cnt; i++) 178 156 syscall(__NR_getpgid, 0); 179 157 printf("%d:hash_lookup %lld lookups per sec\n", 180 - cpu, MAX_CNT * 1000000000ll * 64 / (time_get_ns() - start_time)); 158 + cpu, max_cnt * 1000000000ll * 64 / (time_get_ns() - start_time)); 181 159 } 182 160 183 161 static void test_array_lookup(int cpu) ··· 186 164 int i; 187 165 188 166 start_time = time_get_ns(); 189 - for (i = 0; i < MAX_CNT; i++) 167 + for (i = 0; i < max_cnt; i++) 190 168 syscall(__NR_getpgrp, 0); 191 169 printf("%d:array_lookup %lld lookups per sec\n", 192 - cpu, MAX_CNT * 1000000000ll * 64 / (time_get_ns() - start_time)); 170 + cpu, max_cnt * 1000000000ll * 64 / (time_get_ns() - start_time)); 193 171 } 172 + 173 + typedef void (*test_func)(int cpu); 174 + const test_func test_funcs[] = { 175 + [HASH_PREALLOC] = test_hash_prealloc, 176 + [PERCPU_HASH_PREALLOC] = test_percpu_hash_prealloc, 177 + [HASH_KMALLOC] = test_hash_kmalloc, 178 + [PERCPU_HASH_KMALLOC] = test_percpu_hash_kmalloc, 179 + [LRU_HASH_PREALLOC] = test_lru_hash_prealloc, 180 + [NOCOMMON_LRU_HASH_PREALLOC] = test_nocommon_lru_hash_prealloc, 181 + [LPM_KMALLOC] = test_lpm_kmalloc, 182 + [HASH_LOOKUP] = test_hash_lookup, 183 + [ARRAY_LOOKUP] = test_array_lookup, 184 + }; 194 185 195 186 static void loop(int cpu) 196 187 { 197 188 cpu_set_t cpuset; 189 + int i; 198 190 199 191 CPU_ZERO(&cpuset); 200 192 CPU_SET(cpu, &cpuset); 201 193 sched_setaffinity(0, sizeof(cpuset), &cpuset); 202 194 203 - if (test_flags & HASH_PREALLOC) 204 - test_hash_prealloc(cpu); 205 - 206 - if (test_flags & PERCPU_HASH_PREALLOC) 207 - test_percpu_hash_prealloc(cpu); 208 - 209 - if (test_flags & HASH_KMALLOC) 210 - test_hash_kmalloc(cpu); 211 - 212 - if (test_flags & PERCPU_HASH_KMALLOC) 213 - test_percpu_hash_kmalloc(cpu); 214 - 215 - if (test_flags & LRU_HASH_PREALLOC) 216 - test_lru_hash_prealloc(cpu); 217 - 218 - if (test_flags & NOCOMMON_LRU_HASH_PREALLOC) 219 - test_nocommon_lru_hash_prealloc(cpu); 220 - 221 - if (test_flags & LPM_KMALLOC) 222 - test_lpm_kmalloc(cpu); 223 - 224 - if (test_flags & HASH_LOOKUP) 225 - test_hash_lookup(cpu); 226 - 227 - if (test_flags & ARRAY_LOOKUP) 228 - test_array_lookup(cpu); 195 + for (i = 0; i < NR_TESTS; i++) { 196 + if (check_test_flags(i)) 197 + test_funcs[i](cpu); 198 + } 229 199 } 230 200 231 201 static void run_perf_test(int tasks) ··· 274 260 assert(!r); 275 261 } 276 262 263 + static void fixup_map(struct bpf_map_def *map, const char *name, int idx) 264 + { 265 + int i; 266 + 267 + if (num_map_entries <= 0) 268 + return; 269 + 270 + /* Only change the max_entries for the enabled test(s) */ 271 + for (i = 0; i < NR_TESTS; i++) { 272 + if (!strcmp(test_map_names[i], name) && 273 + (check_test_flags(i))) { 274 + map->max_entries = num_map_entries; 275 + } 276 + } 277 + } 278 + 277 279 int main(int argc, char **argv) 278 280 { 279 281 struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; ··· 305 275 if (argc > 2) 306 276 num_cpu = atoi(argv[2]) ? : num_cpu; 307 277 308 - if (load_bpf_file(filename)) { 278 + if (argc > 3) 279 + num_map_entries = atoi(argv[3]); 280 + 281 + if (argc > 4) 282 + max_cnt = atoi(argv[4]); 283 + 284 + if (load_bpf_file_fixup_map(filename, fixup_map)) { 309 285 printf("%s", bpf_log_buf); 310 286 return 1; 311 287 }