1 // SPDX-License-Identifier: GPL-2.0 2 3 #include <test_progs.h> 4 #include <sys/user.h> 5 #ifndef PAGE_SIZE /* on some archs it comes in sys/user.h */ 6 #include <unistd.h> 7 #define PAGE_SIZE getpagesize() 8 #endif 9 10 #include "arena_mem_usage.skel.h" 11 12 /* 13 * arena_map_mem_usage() is surfaced to user space through the map's 14 * /proc/<pid>/fdinfo/<fd> "memlock:" line (the same value bpftool map show 15 * prints). Read it directly so the test has no external dependency. 16 */ 17 static long map_memlock(int map_fd) 18 { 19 char path[64], line[128]; 20 long memlock = -1; 21 FILE *f; 22 23 snprintf(path, sizeof(path), "/proc/self/fdinfo/%d", map_fd); 24 f = fopen(path, "r"); 25 if (!ASSERT_OK_PTR(f, "open_fdinfo")) 26 return -1; 27 while (fgets(line, sizeof(line), f)) { 28 if (sscanf(line, "memlock:\t%ld", &memlock) == 1) 29 break; 30 } 31 fclose(f); 32 ASSERT_NEQ(memlock, -1, "parse_memlock"); 33 return memlock; 34 } 35 36 static int run(struct bpf_program *prog, const char *name) 37 { 38 LIBBPF_OPTS(bpf_test_run_opts, opts); 39 int err = bpf_prog_test_run_opts(bpf_program__fd(prog), &opts); 40 41 if (!ASSERT_OK(err, name)) 42 return -1; 43 if (!ASSERT_OK(opts.retval, name)) 44 return -1; 45 return 0; 46 } 47 48 void serial_test_arena_mem_usage(void) 49 { 50 struct arena_mem_usage *skel; 51 const long ps = PAGE_SIZE; 52 char *base; 53 size_t sz; 54 int fd, i; 55 56 skel = arena_mem_usage__open_and_load(); 57 if (!ASSERT_OK_PTR(skel, "open_load")) 58 return; 59 fd = bpf_map__fd(skel->maps.arena); 60 61 /* Fresh arena: no data pages, and the scratch page is not counted. */ 62 ASSERT_EQ(map_memlock(fd), 0, "initial"); 63 64 /* BPF-side allocation of 17 pages. */ 65 skel->bss->alloc_cnt = 17; 66 if (run(skel->progs.alloc, "alloc")) 67 goto out; 68 /* 69 * A NULL ptr means bpf_arena_alloc_pages() itself failed (e.g. the host 70 * is under memory pressure), not a miscount -- flag it distinctly so a 71 * red CI run is not mistaken for a counting bug. 72 */ 73 if (!ASSERT_OK_PTR(skel->bss->ptr, "arena_alloc_pages")) 74 goto out; 75 ASSERT_EQ(map_memlock(fd), 17 * ps, "after_alloc"); 76 77 /* Free a single page (arena_free_pages page_cnt==1 path). */ 78 skel->bss->free_byte_off = 0; 79 skel->bss->free_cnt = 1; 80 if (run(skel->progs.free_pages, "free_one")) 81 goto out; 82 ASSERT_EQ(map_memlock(fd), 16 * ps, "after_free_one"); 83 84 /* Free ten pages in one call (bulk path); only the freed pages count. */ 85 skel->bss->free_byte_off = 1 * ps; 86 skel->bss->free_cnt = 10; 87 if (run(skel->progs.free_pages, "free_bulk")) 88 goto out; 89 ASSERT_EQ(map_memlock(fd), 6 * ps, "after_free_bulk"); 90 91 /* Free the remaining six -> arena empty again. */ 92 skel->bss->free_byte_off = 11 * ps; 93 skel->bss->free_cnt = 6; 94 if (run(skel->progs.free_pages, "free_rest")) 95 goto out; 96 ASSERT_EQ(map_memlock(fd), 0, "after_free_rest"); 97 98 /* 99 * User-space fault-in: touching unallocated arena pages allocates them 100 * through arena_vm_fault(). libbpf mmap()s the arena at map_extra during 101 * load, so bpf_map__initial_value() hands back that base. 102 */ 103 base = bpf_map__initial_value(skel->maps.arena, &sz); 104 if (!ASSERT_OK_PTR(base, "arena_base")) 105 goto out; 106 for (i = 0; i < 8; i++) 107 base[i * ps] = 1; 108 ASSERT_EQ(map_memlock(fd), 8 * ps, "after_faultin"); 109 110 /* 111 * Free the faulted-in pages from BPF. They are mapped into the user vma 112 * (elevated refcount), so this also exercises the zap path. 113 */ 114 skel->bss->ptr = base; 115 skel->bss->free_byte_off = 0; 116 skel->bss->free_cnt = 8; 117 if (run(skel->progs.free_pages, "free_faulted")) 118 goto out; 119 ASSERT_EQ(map_memlock(fd), 0, "after_free_faulted"); 120 out: 121 arena_mem_usage__destroy(skel); 122 } 123