1 // SPDX-License-Identifier: GPL-2.0 2 /* Test selecting other page sizes for mmap/shmget. 3 4 Before running this huge pages for each huge page size must have been 5 reserved. 6 For large pages beyond MAX_PAGE_ORDER (like 1GB on x86) boot options must 7 be used. 1GB wouldn't be tested if it isn't available. 8 Also shmmax must be increased. 9 And you need to run as root to work around some weird permissions in shm. 10 And nothing using huge pages should run in parallel. 11 When the program aborts you may need to clean up the shm segments with 12 ipcrm -m by hand, like this 13 sudo ipcs | awk '$1 == "0x00000000" {print $2}' | xargs -n1 sudo ipcrm -m 14 (warning this will remove all if someone else uses them) */ 15 16 #define _GNU_SOURCE 1 17 #include <sys/mman.h> 18 #include <stdlib.h> 19 #include <stdio.h> 20 #include <sys/ipc.h> 21 #include <sys/shm.h> 22 #include <sys/stat.h> 23 #include <glob.h> 24 #include <assert.h> 25 #include <unistd.h> 26 #include <stdarg.h> 27 #include <string.h> 28 #include "vm_util.h" 29 #include "../kselftest.h" 30 31 #define MAP_HUGE_2MB (21 << MAP_HUGE_SHIFT) 32 #define MAP_HUGE_1GB (30 << MAP_HUGE_SHIFT) 33 #define MAP_HUGE_SHIFT 26 34 #define MAP_HUGE_MASK 0x3f 35 #if !defined(MAP_HUGETLB) 36 #define MAP_HUGETLB 0x40000 37 #endif 38 39 #define SHM_HUGETLB 04000 /* segment will use huge TLB pages */ 40 #define SHM_HUGE_SHIFT 26 41 #define SHM_HUGE_MASK 0x3f 42 #define SHM_HUGE_2MB (21 << SHM_HUGE_SHIFT) 43 #define SHM_HUGE_1GB (30 << SHM_HUGE_SHIFT) 44 45 #define NUM_PAGESIZES 5 46 #define NUM_PAGES 4 47 48 unsigned long page_sizes[NUM_PAGESIZES]; 49 int num_page_sizes; 50 51 int ilog2(unsigned long v) 52 { 53 int l = 0; 54 while ((1UL << l) < v) 55 l++; 56 return l; 57 } 58 59 void show(unsigned long ps) 60 { 61 char buf[100]; 62 63 if (ps == getpagesize()) 64 return; 65 66 ksft_print_msg("%luMB: ", ps >> 20); 67 68 fflush(stdout); 69 snprintf(buf, sizeof buf, 70 "cat /sys/kernel/mm/hugepages/hugepages-%lukB/free_hugepages", 71 ps >> 10); 72 system(buf); 73 } 74 75 unsigned long read_sysfs(int warn, char *fmt, ...) 76 { 77 char *line = NULL; 78 size_t linelen = 0; 79 char buf[100]; 80 FILE *f; 81 va_list ap; 82 unsigned long val = 0; 83 84 va_start(ap, fmt); 85 vsnprintf(buf, sizeof buf, fmt, ap); 86 va_end(ap); 87 88 f = fopen(buf, "r"); 89 if (!f) { 90 if (warn) 91 ksft_print_msg("missing %s\n", buf); 92 return 0; 93 } 94 if (getline(&line, &linelen, f) > 0) { 95 sscanf(line, "%lu", &val); 96 } 97 fclose(f); 98 free(line); 99 return val; 100 } 101 102 unsigned long read_free(unsigned long ps) 103 { 104 return read_sysfs(ps != getpagesize(), 105 "/sys/kernel/mm/hugepages/hugepages-%lukB/free_hugepages", 106 ps >> 10); 107 } 108 109 void test_mmap(unsigned long size, unsigned flags) 110 { 111 char *map; 112 unsigned long before, after; 113 114 before = read_free(size); 115 map = mmap(NULL, size*NUM_PAGES, PROT_READ|PROT_WRITE, 116 MAP_PRIVATE|MAP_ANONYMOUS|MAP_HUGETLB|flags, -1, 0); 117 if (map == MAP_FAILED) 118 ksft_exit_fail_msg("mmap: %s\n", strerror(errno)); 119 120 memset(map, 0xff, size*NUM_PAGES); 121 after = read_free(size); 122 123 show(size); 124 ksft_test_result(size == getpagesize() || (before - after) == NUM_PAGES, 125 "%s mmap\n", __func__); 126 127 if (munmap(map, size * NUM_PAGES)) 128 ksft_exit_fail_msg("%s: unmap %s\n", __func__, strerror(errno)); 129 } 130 131 void test_shmget(unsigned long size, unsigned flags) 132 { 133 int id; 134 unsigned long before, after; 135 struct shm_info i; 136 char *map; 137 138 before = read_free(size); 139 id = shmget(IPC_PRIVATE, size * NUM_PAGES, IPC_CREAT|0600|flags); 140 if (id < 0) { 141 if (errno == EPERM) { 142 ksft_test_result_skip("shmget requires root privileges: %s\n", 143 strerror(errno)); 144 return; 145 } 146 ksft_exit_fail_msg("shmget: %s\n", strerror(errno)); 147 } 148 149 if (shmctl(id, SHM_INFO, (void *)&i) < 0) 150 ksft_exit_fail_msg("shmctl: %s\n", strerror(errno)); 151 152 map = shmat(id, NULL, 0600); 153 if (map == MAP_FAILED) 154 ksft_exit_fail_msg("shmat: %s\n", strerror(errno)); 155 156 shmctl(id, IPC_RMID, NULL); 157 158 memset(map, 0xff, size*NUM_PAGES); 159 after = read_free(size); 160 161 show(size); 162 ksft_test_result(size == getpagesize() || (before - after) == NUM_PAGES, 163 "%s: mmap\n", __func__); 164 if (shmdt(map)) 165 ksft_exit_fail_msg("%s: shmdt: %s\n", __func__, strerror(errno)); 166 } 167 168 void find_pagesizes(void) 169 { 170 unsigned long largest = getpagesize(); 171 int i; 172 glob_t g; 173 174 glob("/sys/kernel/mm/hugepages/hugepages-*kB", 0, NULL, &g); 175 assert(g.gl_pathc <= NUM_PAGESIZES); 176 for (i = 0; (i < g.gl_pathc) && (num_page_sizes < NUM_PAGESIZES); i++) { 177 sscanf(g.gl_pathv[i], "/sys/kernel/mm/hugepages/hugepages-%lukB", 178 &page_sizes[num_page_sizes]); 179 page_sizes[num_page_sizes] <<= 10; 180 ksft_print_msg("Found %luMB\n", page_sizes[i] >> 20); 181 182 if (page_sizes[num_page_sizes] > largest) 183 largest = page_sizes[i]; 184 185 if (read_free(page_sizes[num_page_sizes]) >= NUM_PAGES) 186 num_page_sizes++; 187 else 188 ksft_print_msg("SKIP for size %lu MB as not enough huge pages, need %u\n", 189 page_sizes[num_page_sizes] >> 20, NUM_PAGES); 190 } 191 globfree(&g); 192 193 if (read_sysfs(0, "/proc/sys/kernel/shmmax") < NUM_PAGES * largest) 194 ksft_exit_fail_msg("Please do echo %lu > /proc/sys/kernel/shmmax", 195 largest * NUM_PAGES); 196 197 #if defined(__x86_64__) 198 if (largest != 1U<<30) { 199 ksft_exit_fail_msg("No GB pages available on x86-64\n" 200 "Please boot with hugepagesz=1G hugepages=%d\n", NUM_PAGES); 201 } 202 #endif 203 } 204 205 int main(void) 206 { 207 unsigned default_hps = default_huge_page_size(); 208 int i; 209 210 ksft_print_header(); 211 212 find_pagesizes(); 213 214 if (!num_page_sizes) 215 ksft_finished(); 216 217 ksft_set_plan(2 * num_page_sizes + 3); 218 219 for (i = 0; i < num_page_sizes; i++) { 220 unsigned long ps = page_sizes[i]; 221 int arg = ilog2(ps) << MAP_HUGE_SHIFT; 222 223 ksft_print_msg("Testing %luMB mmap with shift %x\n", ps >> 20, arg); 224 test_mmap(ps, MAP_HUGETLB | arg); 225 } 226 227 ksft_print_msg("Testing default huge mmap\n"); 228 test_mmap(default_hps, MAP_HUGETLB); 229 230 ksft_print_msg("Testing non-huge shmget\n"); 231 test_shmget(getpagesize(), 0); 232 233 for (i = 0; i < num_page_sizes; i++) { 234 unsigned long ps = page_sizes[i]; 235 int arg = ilog2(ps) << SHM_HUGE_SHIFT; 236 ksft_print_msg("Testing %luMB shmget with shift %x\n", ps >> 20, arg); 237 test_shmget(ps, SHM_HUGETLB | arg); 238 } 239 240 ksft_print_msg("default huge shmget\n"); 241 test_shmget(default_hps, SHM_HUGETLB); 242 243 ksft_finished(); 244 } 245