// SPDX-License-Identifier: GPL-2.0 #include #include #ifndef PAGE_SIZE /* on some archs it comes in sys/user.h */ #include #define PAGE_SIZE getpagesize() #endif #include "arena_mem_usage.skel.h" /* * arena_map_mem_usage() is surfaced to user space through the map's * /proc//fdinfo/ "memlock:" line (the same value bpftool map show * prints). Read it directly so the test has no external dependency. */ static long map_memlock(int map_fd) { char path[64], line[128]; long memlock = -1; FILE *f; snprintf(path, sizeof(path), "/proc/self/fdinfo/%d", map_fd); f = fopen(path, "r"); if (!ASSERT_OK_PTR(f, "open_fdinfo")) return -1; while (fgets(line, sizeof(line), f)) { if (sscanf(line, "memlock:\t%ld", &memlock) == 1) break; } fclose(f); ASSERT_NEQ(memlock, -1, "parse_memlock"); return memlock; } static int run(struct bpf_program *prog, const char *name) { LIBBPF_OPTS(bpf_test_run_opts, opts); int err = bpf_prog_test_run_opts(bpf_program__fd(prog), &opts); if (!ASSERT_OK(err, name)) return -1; if (!ASSERT_OK(opts.retval, name)) return -1; return 0; } void serial_test_arena_mem_usage(void) { struct arena_mem_usage *skel; const long ps = PAGE_SIZE; char *base; size_t sz; int fd, i; skel = arena_mem_usage__open_and_load(); if (!ASSERT_OK_PTR(skel, "open_load")) return; fd = bpf_map__fd(skel->maps.arena); /* Fresh arena: no data pages, and the scratch page is not counted. */ ASSERT_EQ(map_memlock(fd), 0, "initial"); /* BPF-side allocation of 17 pages. */ skel->bss->alloc_cnt = 17; if (run(skel->progs.alloc, "alloc")) goto out; /* * A NULL ptr means bpf_arena_alloc_pages() itself failed (e.g. the host * is under memory pressure), not a miscount -- flag it distinctly so a * red CI run is not mistaken for a counting bug. */ if (!ASSERT_OK_PTR(skel->bss->ptr, "arena_alloc_pages")) goto out; ASSERT_EQ(map_memlock(fd), 17 * ps, "after_alloc"); /* Free a single page (arena_free_pages page_cnt==1 path). */ skel->bss->free_byte_off = 0; skel->bss->free_cnt = 1; if (run(skel->progs.free_pages, "free_one")) goto out; ASSERT_EQ(map_memlock(fd), 16 * ps, "after_free_one"); /* Free ten pages in one call (bulk path); only the freed pages count. */ skel->bss->free_byte_off = 1 * ps; skel->bss->free_cnt = 10; if (run(skel->progs.free_pages, "free_bulk")) goto out; ASSERT_EQ(map_memlock(fd), 6 * ps, "after_free_bulk"); /* Free the remaining six -> arena empty again. */ skel->bss->free_byte_off = 11 * ps; skel->bss->free_cnt = 6; if (run(skel->progs.free_pages, "free_rest")) goto out; ASSERT_EQ(map_memlock(fd), 0, "after_free_rest"); /* * User-space fault-in: touching unallocated arena pages allocates them * through arena_vm_fault(). libbpf mmap()s the arena at map_extra during * load, so bpf_map__initial_value() hands back that base. */ base = bpf_map__initial_value(skel->maps.arena, &sz); if (!ASSERT_OK_PTR(base, "arena_base")) goto out; for (i = 0; i < 8; i++) base[i * ps] = 1; ASSERT_EQ(map_memlock(fd), 8 * ps, "after_faultin"); /* * Free the faulted-in pages from BPF. They are mapped into the user vma * (elevated refcount), so this also exercises the zap path. */ skel->bss->ptr = base; skel->bss->free_byte_off = 0; skel->bss->free_cnt = 8; if (run(skel->progs.free_pages, "free_faulted")) goto out; ASSERT_EQ(map_memlock(fd), 0, "after_free_faulted"); out: arena_mem_usage__destroy(skel); }