Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.9-rc8 312 lines 10 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2 3/* 4 * Tests for mremap w/ MREMAP_DONTUNMAP. 5 * 6 * Copyright 2020, Brian Geffon <bgeffon@google.com> 7 */ 8#define _GNU_SOURCE 9#include <sys/mman.h> 10#include <errno.h> 11#include <stdio.h> 12#include <stdlib.h> 13#include <string.h> 14#include <unistd.h> 15 16#include "../kselftest.h" 17 18#ifndef MREMAP_DONTUNMAP 19#define MREMAP_DONTUNMAP 4 20#endif 21 22unsigned long page_size; 23char *page_buffer; 24 25static void dump_maps(void) 26{ 27 char cmd[32]; 28 29 snprintf(cmd, sizeof(cmd), "cat /proc/%d/maps", getpid()); 30 system(cmd); 31} 32 33#define BUG_ON(condition, description) \ 34 do { \ 35 if (condition) { \ 36 fprintf(stderr, "[FAIL]\t%s():%d\t%s:%s\n", __func__, \ 37 __LINE__, (description), strerror(errno)); \ 38 dump_maps(); \ 39 exit(1); \ 40 } \ 41 } while (0) 42 43// Try a simple operation for to "test" for kernel support this prevents 44// reporting tests as failed when it's run on an older kernel. 45static int kernel_support_for_mremap_dontunmap() 46{ 47 int ret = 0; 48 unsigned long num_pages = 1; 49 void *source_mapping = mmap(NULL, num_pages * page_size, PROT_NONE, 50 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 51 BUG_ON(source_mapping == MAP_FAILED, "mmap"); 52 53 // This simple remap should only fail if MREMAP_DONTUNMAP isn't 54 // supported. 55 void *dest_mapping = 56 mremap(source_mapping, num_pages * page_size, num_pages * page_size, 57 MREMAP_DONTUNMAP | MREMAP_MAYMOVE, 0); 58 if (dest_mapping == MAP_FAILED) { 59 ret = errno; 60 } else { 61 BUG_ON(munmap(dest_mapping, num_pages * page_size) == -1, 62 "unable to unmap destination mapping"); 63 } 64 65 BUG_ON(munmap(source_mapping, num_pages * page_size) == -1, 66 "unable to unmap source mapping"); 67 return ret; 68} 69 70// This helper will just validate that an entire mapping contains the expected 71// byte. 72static int check_region_contains_byte(void *addr, unsigned long size, char byte) 73{ 74 BUG_ON(size & (page_size - 1), 75 "check_region_contains_byte expects page multiples"); 76 BUG_ON((unsigned long)addr & (page_size - 1), 77 "check_region_contains_byte expects page alignment"); 78 79 memset(page_buffer, byte, page_size); 80 81 unsigned long num_pages = size / page_size; 82 unsigned long i; 83 84 // Compare each page checking that it contains our expected byte. 85 for (i = 0; i < num_pages; ++i) { 86 int ret = 87 memcmp(addr + (i * page_size), page_buffer, page_size); 88 if (ret) { 89 return ret; 90 } 91 } 92 93 return 0; 94} 95 96// this test validates that MREMAP_DONTUNMAP moves the pagetables while leaving 97// the source mapping mapped. 98static void mremap_dontunmap_simple() 99{ 100 unsigned long num_pages = 5; 101 102 void *source_mapping = 103 mmap(NULL, num_pages * page_size, PROT_READ | PROT_WRITE, 104 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 105 BUG_ON(source_mapping == MAP_FAILED, "mmap"); 106 107 memset(source_mapping, 'a', num_pages * page_size); 108 109 // Try to just move the whole mapping anywhere (not fixed). 110 void *dest_mapping = 111 mremap(source_mapping, num_pages * page_size, num_pages * page_size, 112 MREMAP_DONTUNMAP | MREMAP_MAYMOVE, NULL); 113 BUG_ON(dest_mapping == MAP_FAILED, "mremap"); 114 115 // Validate that the pages have been moved, we know they were moved if 116 // the dest_mapping contains a's. 117 BUG_ON(check_region_contains_byte 118 (dest_mapping, num_pages * page_size, 'a') != 0, 119 "pages did not migrate"); 120 BUG_ON(check_region_contains_byte 121 (source_mapping, num_pages * page_size, 0) != 0, 122 "source should have no ptes"); 123 124 BUG_ON(munmap(dest_mapping, num_pages * page_size) == -1, 125 "unable to unmap destination mapping"); 126 BUG_ON(munmap(source_mapping, num_pages * page_size) == -1, 127 "unable to unmap source mapping"); 128} 129 130// This test validates MREMAP_DONTUNMAP will move page tables to a specific 131// destination using MREMAP_FIXED, also while validating that the source 132// remains intact. 133static void mremap_dontunmap_simple_fixed() 134{ 135 unsigned long num_pages = 5; 136 137 // Since we want to guarantee that we can remap to a point, we will 138 // create a mapping up front. 139 void *dest_mapping = 140 mmap(NULL, num_pages * page_size, PROT_READ | PROT_WRITE, 141 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 142 BUG_ON(dest_mapping == MAP_FAILED, "mmap"); 143 memset(dest_mapping, 'X', num_pages * page_size); 144 145 void *source_mapping = 146 mmap(NULL, num_pages * page_size, PROT_READ | PROT_WRITE, 147 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 148 BUG_ON(source_mapping == MAP_FAILED, "mmap"); 149 memset(source_mapping, 'a', num_pages * page_size); 150 151 void *remapped_mapping = 152 mremap(source_mapping, num_pages * page_size, num_pages * page_size, 153 MREMAP_FIXED | MREMAP_DONTUNMAP | MREMAP_MAYMOVE, 154 dest_mapping); 155 BUG_ON(remapped_mapping == MAP_FAILED, "mremap"); 156 BUG_ON(remapped_mapping != dest_mapping, 157 "mremap should have placed the remapped mapping at dest_mapping"); 158 159 // The dest mapping will have been unmap by mremap so we expect the Xs 160 // to be gone and replaced with a's. 161 BUG_ON(check_region_contains_byte 162 (dest_mapping, num_pages * page_size, 'a') != 0, 163 "pages did not migrate"); 164 165 // And the source mapping will have had its ptes dropped. 166 BUG_ON(check_region_contains_byte 167 (source_mapping, num_pages * page_size, 0) != 0, 168 "source should have no ptes"); 169 170 BUG_ON(munmap(dest_mapping, num_pages * page_size) == -1, 171 "unable to unmap destination mapping"); 172 BUG_ON(munmap(source_mapping, num_pages * page_size) == -1, 173 "unable to unmap source mapping"); 174} 175 176// This test validates that we can MREMAP_DONTUNMAP for a portion of an 177// existing mapping. 178static void mremap_dontunmap_partial_mapping() 179{ 180 /* 181 * source mapping: 182 * -------------- 183 * | aaaaaaaaaa | 184 * -------------- 185 * to become: 186 * -------------- 187 * | aaaaa00000 | 188 * -------------- 189 * With the destination mapping containing 5 pages of As. 190 * --------- 191 * | aaaaa | 192 * --------- 193 */ 194 unsigned long num_pages = 10; 195 void *source_mapping = 196 mmap(NULL, num_pages * page_size, PROT_READ | PROT_WRITE, 197 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 198 BUG_ON(source_mapping == MAP_FAILED, "mmap"); 199 memset(source_mapping, 'a', num_pages * page_size); 200 201 // We will grab the last 5 pages of the source and move them. 202 void *dest_mapping = 203 mremap(source_mapping + (5 * page_size), 5 * page_size, 204 5 * page_size, 205 MREMAP_DONTUNMAP | MREMAP_MAYMOVE, NULL); 206 BUG_ON(dest_mapping == MAP_FAILED, "mremap"); 207 208 // We expect the first 5 pages of the source to contain a's and the 209 // final 5 pages to contain zeros. 210 BUG_ON(check_region_contains_byte(source_mapping, 5 * page_size, 'a') != 211 0, "first 5 pages of source should have original pages"); 212 BUG_ON(check_region_contains_byte 213 (source_mapping + (5 * page_size), 5 * page_size, 0) != 0, 214 "final 5 pages of source should have no ptes"); 215 216 // Finally we expect the destination to have 5 pages worth of a's. 217 BUG_ON(check_region_contains_byte(dest_mapping, 5 * page_size, 'a') != 218 0, "dest mapping should contain ptes from the source"); 219 220 BUG_ON(munmap(dest_mapping, 5 * page_size) == -1, 221 "unable to unmap destination mapping"); 222 BUG_ON(munmap(source_mapping, num_pages * page_size) == -1, 223 "unable to unmap source mapping"); 224} 225 226// This test validates that we can remap over only a portion of a mapping. 227static void mremap_dontunmap_partial_mapping_overwrite(void) 228{ 229 /* 230 * source mapping: 231 * --------- 232 * |aaaaa| 233 * --------- 234 * dest mapping initially: 235 * ----------- 236 * |XXXXXXXXXX| 237 * ------------ 238 * Source to become: 239 * --------- 240 * |00000| 241 * --------- 242 * With the destination mapping containing 5 pages of As. 243 * ------------ 244 * |aaaaaXXXXX| 245 * ------------ 246 */ 247 void *source_mapping = 248 mmap(NULL, 5 * page_size, PROT_READ | PROT_WRITE, 249 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 250 BUG_ON(source_mapping == MAP_FAILED, "mmap"); 251 memset(source_mapping, 'a', 5 * page_size); 252 253 void *dest_mapping = 254 mmap(NULL, 10 * page_size, PROT_READ | PROT_WRITE, 255 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 256 BUG_ON(dest_mapping == MAP_FAILED, "mmap"); 257 memset(dest_mapping, 'X', 10 * page_size); 258 259 // We will grab the last 5 pages of the source and move them. 260 void *remapped_mapping = 261 mremap(source_mapping, 5 * page_size, 262 5 * page_size, 263 MREMAP_DONTUNMAP | MREMAP_MAYMOVE | MREMAP_FIXED, dest_mapping); 264 BUG_ON(dest_mapping == MAP_FAILED, "mremap"); 265 BUG_ON(dest_mapping != remapped_mapping, "expected to remap to dest_mapping"); 266 267 BUG_ON(check_region_contains_byte(source_mapping, 5 * page_size, 0) != 268 0, "first 5 pages of source should have no ptes"); 269 270 // Finally we expect the destination to have 5 pages worth of a's. 271 BUG_ON(check_region_contains_byte(dest_mapping, 5 * page_size, 'a') != 0, 272 "dest mapping should contain ptes from the source"); 273 274 // Finally the last 5 pages shouldn't have been touched. 275 BUG_ON(check_region_contains_byte(dest_mapping + (5 * page_size), 276 5 * page_size, 'X') != 0, 277 "dest mapping should have retained the last 5 pages"); 278 279 BUG_ON(munmap(dest_mapping, 10 * page_size) == -1, 280 "unable to unmap destination mapping"); 281 BUG_ON(munmap(source_mapping, 5 * page_size) == -1, 282 "unable to unmap source mapping"); 283} 284 285int main(void) 286{ 287 page_size = sysconf(_SC_PAGE_SIZE); 288 289 // test for kernel support for MREMAP_DONTUNMAP skipping the test if 290 // not. 291 if (kernel_support_for_mremap_dontunmap() != 0) { 292 printf("No kernel support for MREMAP_DONTUNMAP\n"); 293 return KSFT_SKIP; 294 } 295 296 // Keep a page sized buffer around for when we need it. 297 page_buffer = 298 mmap(NULL, page_size, PROT_READ | PROT_WRITE, 299 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 300 BUG_ON(page_buffer == MAP_FAILED, "unable to mmap a page."); 301 302 mremap_dontunmap_simple(); 303 mremap_dontunmap_simple_fixed(); 304 mremap_dontunmap_partial_mapping(); 305 mremap_dontunmap_partial_mapping_overwrite(); 306 307 BUG_ON(munmap(page_buffer, page_size) == -1, 308 "unable to unmap page buffer"); 309 310 printf("OK\n"); 311 return 0; 312}