1// SPDX-License-Identifier: LGPL-2.1-or-later 2/* 3 * Copyright (C) 2009 IBM Corporation. 4 * Author: David Gibson 5 */ 6 7/*\ 8 * [Description] 9 * 10 * Kernel has bug in mremap for some architecture. mremap() can cause 11 * crashes on architectures with holes in the address space (like ia64) 12 * and on powerpc with it's distict page size slices. 13 * 14 * This test perform mremap() with normal and hugepages around powerpc 15 * slice boundary. 16 */ 17 18#define _GNU_SOURCE 19#include "hugetlb.h" 20 21#define RANDOM_CONSTANT 0x1234ABCD 22#define MNTPOINT "hugetlbfs/" 23 24static int fd = -1; 25static unsigned long slice_boundary; 26static unsigned long hpage_size, page_size; 27 28static int init_slice_boundary(int fd) 29{ 30 unsigned long slice_size; 31 void *p, *heap; 32 int i; 33#if defined(__LP64__) && !defined(__aarch64__) 34 /* powerpc: 1TB slices starting at 1 TB */ 35 slice_boundary = 0x10000000000; 36 slice_size = 0x10000000000; 37#else 38 /* powerpc: 256MB slices up to 4GB */ 39 slice_boundary = 0x00000000; 40 slice_size = 0x10000000; 41#endif 42 43 /* dummy malloc so we know where is heap */ 44 heap = malloc(1); 45 free(heap); 46 47 /* Avoid underflow on systems with large huge pages. 48 * The additionally plus heap address is to reduce the possibility 49 * of MAP_FIXED stomp over existing mappings. 50 */ 51 while (slice_boundary + slice_size < (unsigned long)heap + 2*hpage_size) 52 slice_boundary += slice_size; 53 54 /* Find 2 neighbour slices with couple huge pages free 55 * around slice boundary. 56 * 16 is the maximum number of slices (low/high) 57 */ 58 for (i = 0; i < 16-1; i++) { 59 slice_boundary += slice_size; 60 p = mmap((void *)(slice_boundary-2*hpage_size), 4*hpage_size, 61 PROT_READ, MAP_SHARED | MAP_FIXED, fd, 0); 62 if (p == MAP_FAILED) { 63 tst_res(TINFO|TERRNO, "can't use slice_boundary: 0x%lx", 64 slice_boundary); 65 } else { 66 SAFE_MUNMAP(p, 4*hpage_size); 67 break; 68 } 69 } 70 71 if (p == MAP_FAILED) { 72 tst_res(TFAIL|TERRNO, "couldn't find 2 free neighbour slices"); 73 return -1; 74 } 75 76 tst_res(TINFO, "using slice_boundary: 0x%lx", slice_boundary); 77 78 return 0; 79} 80 81static void run_test(void) 82{ 83 void *p = NULL, *q = NULL, *r; 84 long p_size, q_size; 85 int ret; 86 87 fd = tst_creat_unlinked(MNTPOINT, 0); 88 ret = init_slice_boundary(fd); 89 if (ret) 90 goto cleanup; 91 92 /* First, hugepages above, normal below */ 93 tst_res(TINFO, "Testing with hpage above & normal below the slice_boundary"); 94 p_size = hpage_size; 95 p = SAFE_MMAP((void *)(slice_boundary + hpage_size), p_size, 96 PROT_READ | PROT_WRITE, 97 MAP_SHARED | MAP_FIXED, fd, 0); 98 99 ret = do_readback(p, p_size, "huge above"); 100 if (ret) 101 goto cleanup; 102 103 q_size = page_size; 104 q = SAFE_MMAP((void *)(slice_boundary - page_size), q_size, 105 PROT_READ | PROT_WRITE, 106 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); 107 108 ret = do_readback(q, q_size, "normal below"); 109 if (ret) 110 goto cleanup; 111 112 r = mremap(q, page_size, 2*page_size, 0); 113 if (r == MAP_FAILED) { 114 tst_res(TINFO, "mremap(%p, %lu, %lu, 0) disallowed", 115 q, page_size, 2*page_size); 116 } else { 117 q_size = 2*page_size; 118 if (r != q) { 119 tst_res(TFAIL, "mremap() moved without MREMAP_MAYMOVE!?"); 120 ret = -1; 121 } else 122 ret = do_readback(q, 2*page_size, "normal below expanded"); 123 } 124 125 SAFE_MUNMAP(p, p_size); 126 SAFE_MUNMAP(q, q_size); 127 if (ret) 128 goto cleanup_fd; 129 130 /* Next, normal pages above, huge below */ 131 tst_res(TINFO, "Testing with normal above & hpage below the slice_boundary"); 132 p_size = page_size; 133 p = SAFE_MMAP((void *)(slice_boundary + hpage_size), p_size, 134 PROT_READ|PROT_WRITE, 135 MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS, -1, 0); 136 137 ret = do_readback(p, p_size, "normal above"); 138 if (ret) 139 goto cleanup; 140 141 q_size = hpage_size; 142 q = SAFE_MMAP((void *)(slice_boundary - hpage_size), 143 q_size, PROT_READ | PROT_WRITE, 144 MAP_SHARED | MAP_FIXED, fd, 0); 145 146 ret = do_readback(q, q_size, "huge below"); 147 if (ret) 148 goto cleanup; 149 150 r = mremap(q, hpage_size, 2*hpage_size, 0); 151 if (r == MAP_FAILED) { 152 tst_res(TINFO, "mremap(%p, %lu, %lu, 0) disallowed", 153 q, hpage_size, 2*hpage_size); 154 } else { 155 q_size = 2*hpage_size; 156 if (r != q) { 157 tst_res(TFAIL, "mremap() moved without MREMAP_MAYMOVE!?"); 158 ret = -1; 159 } else 160 ret = do_readback(q, 2*hpage_size, "huge below expanded"); 161 } 162 if (ret) 163 goto cleanup; 164 165 tst_res(TPASS, "Successful"); 166 167cleanup: 168 if (p) 169 SAFE_MUNMAP(p, p_size); 170 if (q) 171 SAFE_MUNMAP(q, q_size); 172cleanup_fd: 173 SAFE_CLOSE(fd); 174} 175 176static void setup(void) 177{ 178 hpage_size = tst_get_hugepage_size(); 179 page_size = getpagesize(); 180} 181 182static void cleanup(void) 183{ 184 if (fd >= 0) 185 SAFE_CLOSE(fd); 186} 187 188static struct tst_test test = { 189 .needs_root = 1, 190 .mntpoint = MNTPOINT, 191 .needs_hugetlbfs = 1, 192 .needs_tmpdir = 1, 193 .setup = setup, 194 .cleanup = cleanup, 195 .test_all = run_test, 196 .hugepages = {4, TST_NEEDS}, 197}; 198