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 17 #include <sys/mman.h> 18 #include <linux/mman.h> 19 #include <stdlib.h> 20 #include <stdio.h> 21 #include <sys/ipc.h> 22 #include <sys/shm.h> 23 #include <sys/stat.h> 24 #include <glob.h> 25 #include <assert.h> 26 #include <unistd.h> 27 #include <stdarg.h> 28 #include <string.h> 29 #include "vm_util.h" 30 #include "../kselftest.h" 31 32 #if !defined(MAP_HUGETLB) 33 #define MAP_HUGETLB 0x40000 34 #endif 35 36 #define SHM_HUGETLB 04000 /* segment will use huge TLB pages */ 37 #ifndef SHM_HUGE_SHIFT 38 #define SHM_HUGE_SHIFT 26 39 #endif 40 #ifndef SHM_HUGE_MASK 41 #define SHM_HUGE_MASK 0x3f 42 #endif 43 #ifndef SHM_HUGE_2MB 44 #define SHM_HUGE_2MB (21 << SHM_HUGE_SHIFT) 45 #endif 46 #ifndef SHM_HUGE_1GB 47 #define SHM_HUGE_1GB (30 << SHM_HUGE_SHIFT) 48 #endif 49 50 #define NUM_PAGESIZES 5 51 #define NUM_PAGES 4 52 53 unsigned long page_sizes[NUM_PAGESIZES]; 54 int num_page_sizes; 55 56 int ilog2(unsigned long v) 57 { 58 int l = 0; 59 while ((1UL << l) < v) 60 l++; 61 return l; 62 } 63 64 void show(unsigned long ps) 65 { 66 char buf[100]; 67 68 if (ps == getpagesize()) 69 return; 70 71 ksft_print_msg("%luMB: ", ps >> 20); 72 73 fflush(stdout); 74 snprintf(buf, sizeof buf, 75 "cat /sys/kernel/mm/hugepages/hugepages-%lukB/free_hugepages", 76 ps >> 10); 77 system(buf); 78 } 79 80 unsigned long read_free(unsigned long ps) 81 { 82 unsigned long val = 0; 83 char buf[100]; 84 85 snprintf(buf, sizeof(buf), 86 "/sys/kernel/mm/hugepages/hugepages-%lukB/free_hugepages", 87 ps >> 10); 88 if (read_sysfs(buf, &val) && ps != getpagesize()) 89 ksft_print_msg("missing %s\n", buf); 90 91 return val; 92 } 93 94 void test_mmap(unsigned long size, unsigned flags) 95 { 96 char *map; 97 unsigned long before, after; 98 99 before = read_free(size); 100 map = mmap(NULL, size*NUM_PAGES, PROT_READ|PROT_WRITE, 101 MAP_PRIVATE|MAP_ANONYMOUS|MAP_HUGETLB|flags, -1, 0); 102 if (map == MAP_FAILED) 103 ksft_exit_fail_msg("mmap: %s\n", strerror(errno)); 104 105 memset(map, 0xff, size*NUM_PAGES); 106 after = read_free(size); 107 108 show(size); 109 ksft_test_result(size == getpagesize() || (before - after) == NUM_PAGES, 110 "%s mmap %lu %x\n", __func__, size, flags); 111 112 if (munmap(map, size * NUM_PAGES)) 113 ksft_exit_fail_msg("%s: unmap %s\n", __func__, strerror(errno)); 114 } 115 116 void test_shmget(unsigned long size, unsigned flags) 117 { 118 int id; 119 unsigned long before, after; 120 struct shm_info i; 121 char *map; 122 123 before = read_free(size); 124 id = shmget(IPC_PRIVATE, size * NUM_PAGES, IPC_CREAT|0600|flags); 125 if (id < 0) { 126 if (errno == EPERM) { 127 ksft_test_result_skip("shmget requires root privileges: %s\n", 128 strerror(errno)); 129 return; 130 } 131 ksft_exit_fail_msg("shmget: %s\n", strerror(errno)); 132 } 133 134 if (shmctl(id, SHM_INFO, (void *)&i) < 0) 135 ksft_exit_fail_msg("shmctl: %s\n", strerror(errno)); 136 137 map = shmat(id, NULL, 0600); 138 if (map == MAP_FAILED) 139 ksft_exit_fail_msg("shmat: %s\n", strerror(errno)); 140 141 shmctl(id, IPC_RMID, NULL); 142 143 memset(map, 0xff, size*NUM_PAGES); 144 after = read_free(size); 145 146 show(size); 147 ksft_test_result(size == getpagesize() || (before - after) == NUM_PAGES, 148 "%s: mmap %lu %x\n", __func__, size, flags); 149 if (shmdt(map)) 150 ksft_exit_fail_msg("%s: shmdt: %s\n", __func__, strerror(errno)); 151 } 152 153 void find_pagesizes(void) 154 { 155 unsigned long largest = getpagesize(); 156 unsigned long shmmax_val = 0; 157 int i; 158 glob_t g; 159 160 glob("/sys/kernel/mm/hugepages/hugepages-*kB", 0, NULL, &g); 161 assert(g.gl_pathc <= NUM_PAGESIZES); 162 for (i = 0; (i < g.gl_pathc) && (num_page_sizes < NUM_PAGESIZES); i++) { 163 sscanf(g.gl_pathv[i], "/sys/kernel/mm/hugepages/hugepages-%lukB", 164 &page_sizes[num_page_sizes]); 165 page_sizes[num_page_sizes] <<= 10; 166 ksft_print_msg("Found %luMB\n", page_sizes[i] >> 20); 167 168 if (page_sizes[num_page_sizes] > largest) 169 largest = page_sizes[i]; 170 171 if (read_free(page_sizes[num_page_sizes]) >= NUM_PAGES) 172 num_page_sizes++; 173 else 174 ksft_print_msg("SKIP for size %lu MB as not enough huge pages, need %u\n", 175 page_sizes[num_page_sizes] >> 20, NUM_PAGES); 176 } 177 globfree(&g); 178 179 read_sysfs("/proc/sys/kernel/shmmax", &shmmax_val); 180 if (shmmax_val < NUM_PAGES * largest) { 181 ksft_print_msg("WARNING: shmmax is too small to run this test.\n"); 182 ksft_print_msg("Please run the following command to increase shmmax:\n"); 183 ksft_print_msg("echo %lu > /proc/sys/kernel/shmmax\n", largest * NUM_PAGES); 184 ksft_exit_skip("Test skipped due to insufficient shmmax value.\n"); 185 } 186 187 #if defined(__x86_64__) 188 if (largest != 1U<<30) { 189 ksft_exit_skip("No GB pages available on x86-64\n" 190 "Please boot with hugepagesz=1G hugepages=%d\n", NUM_PAGES); 191 } 192 #endif 193 } 194 195 int main(void) 196 { 197 unsigned default_hps = default_huge_page_size(); 198 int i; 199 200 ksft_print_header(); 201 202 find_pagesizes(); 203 204 if (!num_page_sizes) 205 ksft_finished(); 206 207 ksft_set_plan(2 * num_page_sizes + 3); 208 209 for (i = 0; i < num_page_sizes; i++) { 210 unsigned long ps = page_sizes[i]; 211 int arg = ilog2(ps) << MAP_HUGE_SHIFT; 212 213 ksft_print_msg("Testing %luMB mmap with shift %x\n", ps >> 20, arg); 214 test_mmap(ps, MAP_HUGETLB | arg); 215 } 216 217 ksft_print_msg("Testing default huge mmap\n"); 218 test_mmap(default_hps, MAP_HUGETLB); 219 220 ksft_print_msg("Testing non-huge shmget\n"); 221 test_shmget(getpagesize(), 0); 222 223 for (i = 0; i < num_page_sizes; i++) { 224 unsigned long ps = page_sizes[i]; 225 int arg = ilog2(ps) << SHM_HUGE_SHIFT; 226 ksft_print_msg("Testing %luMB shmget with shift %x\n", ps >> 20, arg); 227 test_shmget(ps, SHM_HUGETLB | arg); 228 } 229 230 ksft_print_msg("default huge shmget\n"); 231 test_shmget(default_hps, SHM_HUGETLB); 232 233 ksft_finished(); 234 } 235