^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) * Stress test for transparent huge pages, memory compaction and migration.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) * Authors: Konstantin Khlebnikov <koct9i@gmail.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * This is free and unencumbered software released into the public domain.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #include <stdlib.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <stdio.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <stdint.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <err.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <time.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <unistd.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <fcntl.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <string.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <sys/mman.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #define PAGE_SHIFT 12
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #define HPAGE_SHIFT 21
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #define PAGE_SIZE (1 << PAGE_SHIFT)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #define HPAGE_SIZE (1 << HPAGE_SHIFT)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) #define PAGEMAP_PRESENT(ent) (((ent) & (1ull << 63)) != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) #define PAGEMAP_PFN(ent) ((ent) & ((1ull << 55) - 1))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) int pagemap_fd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) int64_t allocate_transhuge(void *ptr)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) uint64_t ent[2];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) /* drop pmd */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) if (mmap(ptr, HPAGE_SIZE, PROT_READ | PROT_WRITE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) MAP_FIXED | MAP_ANONYMOUS |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) MAP_NORESERVE | MAP_PRIVATE, -1, 0) != ptr)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) errx(2, "mmap transhuge");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) if (madvise(ptr, HPAGE_SIZE, MADV_HUGEPAGE))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) err(2, "MADV_HUGEPAGE");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) /* allocate transparent huge page */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) *(volatile void **)ptr = ptr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) if (pread(pagemap_fd, ent, sizeof(ent),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) (uintptr_t)ptr >> (PAGE_SHIFT - 3)) != sizeof(ent))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) err(2, "read pagemap");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) if (PAGEMAP_PRESENT(ent[0]) && PAGEMAP_PRESENT(ent[1]) &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) PAGEMAP_PFN(ent[0]) + 1 == PAGEMAP_PFN(ent[1]) &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) !(PAGEMAP_PFN(ent[0]) & ((1 << (HPAGE_SHIFT - PAGE_SHIFT)) - 1)))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) return PAGEMAP_PFN(ent[0]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) int main(int argc, char **argv)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) size_t ram, len;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) void *ptr, *p;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) struct timespec a, b;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) double s;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) uint8_t *map;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) size_t map_len;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) ram = sysconf(_SC_PHYS_PAGES);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) if (ram > SIZE_MAX / sysconf(_SC_PAGESIZE) / 4)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) ram = SIZE_MAX / 4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) ram *= sysconf(_SC_PAGESIZE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) if (argc == 1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) len = ram;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) else if (!strcmp(argv[1], "-h"))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) errx(1, "usage: %s [size in MiB]", argv[0]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) len = atoll(argv[1]) << 20;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) warnx("allocate %zd transhuge pages, using %zd MiB virtual memory"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) " and %zd MiB of ram", len >> HPAGE_SHIFT, len >> 20,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) len >> (20 + HPAGE_SHIFT - PAGE_SHIFT - 1));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) pagemap_fd = open("/proc/self/pagemap", O_RDONLY);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) if (pagemap_fd < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) err(2, "open pagemap");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) len -= len % HPAGE_SIZE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) ptr = mmap(NULL, len + HPAGE_SIZE, PROT_READ | PROT_WRITE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) MAP_ANONYMOUS | MAP_NORESERVE | MAP_PRIVATE, -1, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) if (ptr == MAP_FAILED)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) err(2, "initial mmap");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) ptr += HPAGE_SIZE - (uintptr_t)ptr % HPAGE_SIZE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) if (madvise(ptr, len, MADV_HUGEPAGE))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) err(2, "MADV_HUGEPAGE");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) map_len = ram >> (HPAGE_SHIFT - 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) map = malloc(map_len);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) if (!map)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) errx(2, "map malloc");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) while (1) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) int nr_succeed = 0, nr_failed = 0, nr_pages = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) memset(map, 0, map_len);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) clock_gettime(CLOCK_MONOTONIC, &a);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) for (p = ptr; p < ptr + len; p += HPAGE_SIZE) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) int64_t pfn;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) pfn = allocate_transhuge(p);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) if (pfn < 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) nr_failed++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) size_t idx = pfn >> (HPAGE_SHIFT - PAGE_SHIFT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) nr_succeed++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) if (idx >= map_len) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) map = realloc(map, idx + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) if (!map)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) errx(2, "map realloc");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) memset(map + map_len, 0, idx + 1 - map_len);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) map_len = idx + 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) if (!map[idx])
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) nr_pages++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) map[idx] = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) /* split transhuge page, keep last page */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) if (madvise(p, HPAGE_SIZE - PAGE_SIZE, MADV_DONTNEED))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) err(2, "MADV_DONTNEED");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) clock_gettime(CLOCK_MONOTONIC, &b);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) s = b.tv_sec - a.tv_sec + (b.tv_nsec - a.tv_nsec) / 1000000000.;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) warnx("%.3f s/loop, %.3f ms/page, %10.3f MiB/s\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) "%4d succeed, %4d failed, %4d different pages",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) s, s * 1000 / (len >> HPAGE_SHIFT), len / s / (1 << 20),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) nr_succeed, nr_failed, nr_pages);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) }