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