1 // SPDX-License-Identifier: GPL-2.0 2 /* Test selecting other page sizes for mmap/shmget. */ 3 4 #define _GNU_SOURCE 5 #include <sys/mman.h> 6 #include <linux/mman.h> 7 #include <stdlib.h> 8 #include <stdio.h> 9 #include <sys/ipc.h> 10 #include <sys/shm.h> 11 #include <sys/stat.h> 12 #include <unistd.h> 13 #include <stdarg.h> 14 #include <string.h> 15 #include "vm_util.h" 16 #include "kselftest.h" 17 #include "hugepage_settings.h" 18 19 #if !defined(MAP_HUGETLB) 20 #define MAP_HUGETLB 0x40000 21 #endif 22 23 #define SHM_HUGETLB 04000 /* segment will use huge TLB pages */ 24 #ifndef SHM_HUGE_SHIFT 25 #define SHM_HUGE_SHIFT 26 26 #endif 27 28 #define NUM_PAGESIZES 5 29 #define NUM_PAGES 4 30 31 unsigned long page_sizes[NUM_PAGESIZES]; 32 int num_page_sizes; 33 34 int ilog2(unsigned long v) 35 { 36 int l = 0; 37 while ((1UL << l) < v) 38 l++; 39 return l; 40 } 41 42 void show(unsigned long ps) 43 { 44 if (ps == getpagesize()) 45 return; 46 47 ksft_print_msg("%luMB: %lu\n", ps >> 20, hugetlb_free_pages(ps)); 48 } 49 50 void test_mmap(unsigned long size, unsigned flags) 51 { 52 char *map; 53 unsigned long before, after; 54 55 before = hugetlb_free_pages(size); 56 map = mmap(NULL, size*NUM_PAGES, PROT_READ|PROT_WRITE, 57 MAP_PRIVATE|MAP_ANONYMOUS|MAP_HUGETLB|flags, -1, 0); 58 if (map == MAP_FAILED) 59 ksft_exit_fail_msg("mmap: %s\n", strerror(errno)); 60 61 memset(map, 0xff, size*NUM_PAGES); 62 after = hugetlb_free_pages(size); 63 64 show(size); 65 ksft_test_result(size == getpagesize() || (before - after) == NUM_PAGES, 66 "%s mmap %lu %x\n", __func__, size, flags); 67 68 if (munmap(map, size * NUM_PAGES)) 69 ksft_exit_fail_msg("%s: unmap %s\n", __func__, strerror(errno)); 70 } 71 72 void test_shmget(unsigned long size, unsigned flags) 73 { 74 /* values for PAGE_SIZE test */ 75 unsigned long before = NUM_PAGES; 76 unsigned long after = 0; 77 struct shm_info i; 78 char *map; 79 int id; 80 81 if (size != getpagesize()) 82 before = hugetlb_free_pages(size); 83 84 id = shmget(IPC_PRIVATE, size * NUM_PAGES, IPC_CREAT|0600|flags); 85 if (id < 0) { 86 if (errno == EPERM) { 87 ksft_test_result_skip("shmget requires root privileges: %s\n", 88 strerror(errno)); 89 return; 90 } 91 ksft_exit_fail_msg("shmget: %s\n", strerror(errno)); 92 } 93 94 if (shmctl(id, SHM_INFO, (void *)&i) < 0) 95 ksft_exit_fail_msg("shmctl: %s\n", strerror(errno)); 96 97 map = shmat(id, NULL, 0600); 98 if (map == MAP_FAILED) 99 ksft_exit_fail_msg("shmat: %s\n", strerror(errno)); 100 101 shmctl(id, IPC_RMID, NULL); 102 103 memset(map, 0xff, size*NUM_PAGES); 104 if (size != getpagesize()) 105 after = hugetlb_free_pages(size); 106 107 show(size); 108 ksft_test_result((before - after) == NUM_PAGES, 109 "%s: mmap %lu %x\n", __func__, size, flags); 110 if (shmdt(map)) 111 ksft_exit_fail_msg("%s: shmdt: %s\n", __func__, strerror(errno)); 112 } 113 114 void find_pagesizes(void) 115 { 116 unsigned long largest = getpagesize(); 117 int i; 118 119 num_page_sizes = hugetlb_setup(NUM_PAGES, page_sizes, ARRAY_SIZE(page_sizes)); 120 121 for (i = 0; i < num_page_sizes; i++) 122 if (page_sizes[i] > largest) 123 largest = page_sizes[i]; 124 125 shm_limits_prepare(NUM_PAGES * largest); 126 } 127 128 int main(void) 129 { 130 unsigned default_hps = default_huge_page_size(); 131 int i; 132 133 ksft_print_header(); 134 135 find_pagesizes(); 136 137 if (!num_page_sizes) 138 ksft_finished(); 139 140 ksft_set_plan(2 * num_page_sizes + 3); 141 142 for (i = 0; i < num_page_sizes; i++) { 143 unsigned long ps = page_sizes[i]; 144 int arg = ilog2(ps) << MAP_HUGE_SHIFT; 145 146 ksft_print_msg("Testing %luMB mmap with shift %x\n", ps >> 20, arg); 147 test_mmap(ps, MAP_HUGETLB | arg); 148 } 149 150 ksft_print_msg("Testing default huge mmap\n"); 151 test_mmap(default_hps, MAP_HUGETLB); 152 153 ksft_print_msg("Testing non-huge shmget\n"); 154 test_shmget(getpagesize(), 0); 155 156 for (i = 0; i < num_page_sizes; i++) { 157 unsigned long ps = page_sizes[i]; 158 int arg = ilog2(ps) << SHM_HUGE_SHIFT; 159 ksft_print_msg("Testing %luMB shmget with shift %x\n", ps >> 20, arg); 160 test_shmget(ps, SHM_HUGETLB | arg); 161 } 162 163 ksft_print_msg("default huge shmget\n"); 164 test_shmget(default_hps, SHM_HUGETLB); 165 166 ksft_finished(); 167 } 168 169 SHM_LIMITS_RESTORE() 170