mremap_test.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright 2020 Google LLC
  4. */
  5. #define _GNU_SOURCE
  6. #include <errno.h>
  7. #include <stdlib.h>
  8. #include <stdio.h>
  9. #include <string.h>
  10. #include <sys/mman.h>
  11. #include <time.h>
  12. #include <stdbool.h>
  13. #include "../kselftest.h"
  14. #define EXPECT_SUCCESS 0
  15. #define EXPECT_FAILURE 1
  16. #define NON_OVERLAPPING 0
  17. #define OVERLAPPING 1
  18. #define NS_PER_SEC 1000000000ULL
  19. #define VALIDATION_DEFAULT_THRESHOLD 4 /* 4MB */
  20. #define VALIDATION_NO_THRESHOLD 0 /* Verify the entire region */
  21. #define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
  22. struct config {
  23. unsigned long long src_alignment;
  24. unsigned long long dest_alignment;
  25. unsigned long long region_size;
  26. int overlapping;
  27. };
  28. struct test {
  29. const char *name;
  30. struct config config;
  31. int expect_failure;
  32. };
  33. enum {
  34. _1KB = 1ULL << 10, /* 1KB -> not page aligned */
  35. _4KB = 4ULL << 10,
  36. _8KB = 8ULL << 10,
  37. _1MB = 1ULL << 20,
  38. _2MB = 2ULL << 20,
  39. _4MB = 4ULL << 20,
  40. _1GB = 1ULL << 30,
  41. _2GB = 2ULL << 30,
  42. PMD = _2MB,
  43. PUD = _1GB,
  44. };
  45. #define PTE page_size
  46. #define MAKE_TEST(source_align, destination_align, size, \
  47. overlaps, should_fail, test_name) \
  48. (struct test){ \
  49. .name = test_name, \
  50. .config = { \
  51. .src_alignment = source_align, \
  52. .dest_alignment = destination_align, \
  53. .region_size = size, \
  54. .overlapping = overlaps, \
  55. }, \
  56. .expect_failure = should_fail \
  57. }
  58. /*
  59. * Returns false if the requested remap region overlaps with an
  60. * existing mapping (e.g text, stack) else returns true.
  61. */
  62. static bool is_remap_region_valid(void *addr, unsigned long long size)
  63. {
  64. void *remap_addr = NULL;
  65. bool ret = true;
  66. /* Use MAP_FIXED_NOREPLACE flag to ensure region is not mapped */
  67. remap_addr = mmap(addr, size, PROT_READ | PROT_WRITE,
  68. MAP_FIXED_NOREPLACE | MAP_ANONYMOUS | MAP_SHARED,
  69. -1, 0);
  70. if (remap_addr == MAP_FAILED) {
  71. if (errno == EEXIST)
  72. ret = false;
  73. } else {
  74. munmap(remap_addr, size);
  75. }
  76. return ret;
  77. }
  78. /* Returns mmap_min_addr sysctl tunable from procfs */
  79. static unsigned long long get_mmap_min_addr(void)
  80. {
  81. FILE *fp;
  82. int n_matched;
  83. static unsigned long long addr;
  84. if (addr)
  85. return addr;
  86. fp = fopen("/proc/sys/vm/mmap_min_addr", "r");
  87. if (fp == NULL) {
  88. ksft_print_msg("Failed to open /proc/sys/vm/mmap_min_addr: %s\n",
  89. strerror(errno));
  90. exit(KSFT_SKIP);
  91. }
  92. n_matched = fscanf(fp, "%llu", &addr);
  93. if (n_matched != 1) {
  94. ksft_print_msg("Failed to read /proc/sys/vm/mmap_min_addr: %s\n",
  95. strerror(errno));
  96. fclose(fp);
  97. exit(KSFT_SKIP);
  98. }
  99. fclose(fp);
  100. return addr;
  101. }
  102. /*
  103. * This test validates that merge is called when expanding a mapping.
  104. * Mapping containing three pages is created, middle page is unmapped
  105. * and then the mapping containing the first page is expanded so that
  106. * it fills the created hole. The two parts should merge creating
  107. * single mapping with three pages.
  108. */
  109. static void mremap_expand_merge(unsigned long page_size)
  110. {
  111. char *test_name = "mremap expand merge";
  112. FILE *fp;
  113. char *line = NULL;
  114. size_t len = 0;
  115. bool success = false;
  116. char *start = mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE,
  117. MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  118. munmap(start + page_size, page_size);
  119. mremap(start, page_size, 2 * page_size, 0);
  120. fp = fopen("/proc/self/maps", "r");
  121. if (fp == NULL) {
  122. ksft_test_result_fail("%s\n", test_name);
  123. return;
  124. }
  125. while (getline(&line, &len, fp) != -1) {
  126. char *first = strtok(line, "- ");
  127. void *first_val = (void *)strtol(first, NULL, 16);
  128. char *second = strtok(NULL, "- ");
  129. void *second_val = (void *) strtol(second, NULL, 16);
  130. if (first_val == start && second_val == start + 3 * page_size) {
  131. success = true;
  132. break;
  133. }
  134. }
  135. if (success)
  136. ksft_test_result_pass("%s\n", test_name);
  137. else
  138. ksft_test_result_fail("%s\n", test_name);
  139. fclose(fp);
  140. }
  141. /*
  142. * Returns the start address of the mapping on success, else returns
  143. * NULL on failure.
  144. */
  145. static void *get_source_mapping(struct config c)
  146. {
  147. unsigned long long addr = 0ULL;
  148. void *src_addr = NULL;
  149. unsigned long long mmap_min_addr;
  150. mmap_min_addr = get_mmap_min_addr();
  151. retry:
  152. addr += c.src_alignment;
  153. if (addr < mmap_min_addr)
  154. goto retry;
  155. src_addr = mmap((void *) addr, c.region_size, PROT_READ | PROT_WRITE,
  156. MAP_FIXED_NOREPLACE | MAP_ANONYMOUS | MAP_SHARED,
  157. -1, 0);
  158. if (src_addr == MAP_FAILED) {
  159. if (errno == EPERM || errno == EEXIST)
  160. goto retry;
  161. goto error;
  162. }
  163. /*
  164. * Check that the address is aligned to the specified alignment.
  165. * Addresses which have alignments that are multiples of that
  166. * specified are not considered valid. For instance, 1GB address is
  167. * 2MB-aligned, however it will not be considered valid for a
  168. * requested alignment of 2MB. This is done to reduce coincidental
  169. * alignment in the tests.
  170. */
  171. if (((unsigned long long) src_addr & (c.src_alignment - 1)) ||
  172. !((unsigned long long) src_addr & c.src_alignment)) {
  173. munmap(src_addr, c.region_size);
  174. goto retry;
  175. }
  176. if (!src_addr)
  177. goto error;
  178. return src_addr;
  179. error:
  180. ksft_print_msg("Failed to map source region: %s\n",
  181. strerror(errno));
  182. return NULL;
  183. }
  184. /* Returns the time taken for the remap on success else returns -1. */
  185. static long long remap_region(struct config c, unsigned int threshold_mb,
  186. char pattern_seed)
  187. {
  188. void *addr, *src_addr, *dest_addr;
  189. unsigned long long i;
  190. struct timespec t_start = {0, 0}, t_end = {0, 0};
  191. long long start_ns, end_ns, align_mask, ret, offset;
  192. unsigned long long threshold;
  193. if (threshold_mb == VALIDATION_NO_THRESHOLD)
  194. threshold = c.region_size;
  195. else
  196. threshold = MIN(threshold_mb * _1MB, c.region_size);
  197. src_addr = get_source_mapping(c);
  198. if (!src_addr) {
  199. ret = -1;
  200. goto out;
  201. }
  202. /* Set byte pattern */
  203. srand(pattern_seed);
  204. for (i = 0; i < threshold; i++)
  205. memset((char *) src_addr + i, (char) rand(), 1);
  206. /* Mask to zero out lower bits of address for alignment */
  207. align_mask = ~(c.dest_alignment - 1);
  208. /* Offset of destination address from the end of the source region */
  209. offset = (c.overlapping) ? -c.dest_alignment : c.dest_alignment;
  210. addr = (void *) (((unsigned long long) src_addr + c.region_size
  211. + offset) & align_mask);
  212. /* See comment in get_source_mapping() */
  213. if (!((unsigned long long) addr & c.dest_alignment))
  214. addr = (void *) ((unsigned long long) addr | c.dest_alignment);
  215. /* Don't destroy existing mappings unless expected to overlap */
  216. while (!is_remap_region_valid(addr, c.region_size) && !c.overlapping) {
  217. /* Check for unsigned overflow */
  218. if (addr + c.dest_alignment < addr) {
  219. ksft_print_msg("Couldn't find a valid region to remap to\n");
  220. ret = -1;
  221. goto out;
  222. }
  223. addr += c.dest_alignment;
  224. }
  225. clock_gettime(CLOCK_MONOTONIC, &t_start);
  226. dest_addr = mremap(src_addr, c.region_size, c.region_size,
  227. MREMAP_MAYMOVE|MREMAP_FIXED, (char *) addr);
  228. clock_gettime(CLOCK_MONOTONIC, &t_end);
  229. if (dest_addr == MAP_FAILED) {
  230. ksft_print_msg("mremap failed: %s\n", strerror(errno));
  231. ret = -1;
  232. goto clean_up_src;
  233. }
  234. /* Verify byte pattern after remapping */
  235. srand(pattern_seed);
  236. for (i = 0; i < threshold; i++) {
  237. char c = (char) rand();
  238. if (((char *) dest_addr)[i] != c) {
  239. ksft_print_msg("Data after remap doesn't match at offset %d\n",
  240. i);
  241. ksft_print_msg("Expected: %#x\t Got: %#x\n", c & 0xff,
  242. ((char *) dest_addr)[i] & 0xff);
  243. ret = -1;
  244. goto clean_up_dest;
  245. }
  246. }
  247. start_ns = t_start.tv_sec * NS_PER_SEC + t_start.tv_nsec;
  248. end_ns = t_end.tv_sec * NS_PER_SEC + t_end.tv_nsec;
  249. ret = end_ns - start_ns;
  250. /*
  251. * Since the destination address is specified using MREMAP_FIXED, subsequent
  252. * mremap will unmap any previous mapping at the address range specified by
  253. * dest_addr and region_size. This significantly affects the remap time of
  254. * subsequent tests. So we clean up mappings after each test.
  255. */
  256. clean_up_dest:
  257. munmap(dest_addr, c.region_size);
  258. clean_up_src:
  259. munmap(src_addr, c.region_size);
  260. out:
  261. return ret;
  262. }
  263. static void run_mremap_test_case(struct test test_case, int *failures,
  264. unsigned int threshold_mb,
  265. unsigned int pattern_seed)
  266. {
  267. long long remap_time = remap_region(test_case.config, threshold_mb,
  268. pattern_seed);
  269. if (remap_time < 0) {
  270. if (test_case.expect_failure)
  271. ksft_test_result_xfail("%s\n\tExpected mremap failure\n",
  272. test_case.name);
  273. else {
  274. ksft_test_result_fail("%s\n", test_case.name);
  275. *failures += 1;
  276. }
  277. } else {
  278. /*
  279. * Comparing mremap time is only applicable if entire region
  280. * was faulted in.
  281. */
  282. if (threshold_mb == VALIDATION_NO_THRESHOLD ||
  283. test_case.config.region_size <= threshold_mb * _1MB)
  284. ksft_test_result_pass("%s\n\tmremap time: %12lldns\n",
  285. test_case.name, remap_time);
  286. else
  287. ksft_test_result_pass("%s\n", test_case.name);
  288. }
  289. }
  290. static void usage(const char *cmd)
  291. {
  292. fprintf(stderr,
  293. "Usage: %s [[-t <threshold_mb>] [-p <pattern_seed>]]\n"
  294. "-t\t only validate threshold_mb of the remapped region\n"
  295. " \t if 0 is supplied no threshold is used; all tests\n"
  296. " \t are run and remapped regions validated fully.\n"
  297. " \t The default threshold used is 4MB.\n"
  298. "-p\t provide a seed to generate the random pattern for\n"
  299. " \t validating the remapped region.\n", cmd);
  300. }
  301. static int parse_args(int argc, char **argv, unsigned int *threshold_mb,
  302. unsigned int *pattern_seed)
  303. {
  304. const char *optstr = "t:p:";
  305. int opt;
  306. while ((opt = getopt(argc, argv, optstr)) != -1) {
  307. switch (opt) {
  308. case 't':
  309. *threshold_mb = atoi(optarg);
  310. break;
  311. case 'p':
  312. *pattern_seed = atoi(optarg);
  313. break;
  314. default:
  315. usage(argv[0]);
  316. return -1;
  317. }
  318. }
  319. if (optind < argc) {
  320. usage(argv[0]);
  321. return -1;
  322. }
  323. return 0;
  324. }
  325. #define MAX_TEST 13
  326. #define MAX_PERF_TEST 3
  327. int main(int argc, char **argv)
  328. {
  329. int failures = 0;
  330. int i, run_perf_tests;
  331. unsigned int threshold_mb = VALIDATION_DEFAULT_THRESHOLD;
  332. unsigned int pattern_seed;
  333. int num_expand_tests = 1;
  334. struct test test_cases[MAX_TEST];
  335. struct test perf_test_cases[MAX_PERF_TEST];
  336. int page_size;
  337. time_t t;
  338. pattern_seed = (unsigned int) time(&t);
  339. if (parse_args(argc, argv, &threshold_mb, &pattern_seed) < 0)
  340. exit(EXIT_FAILURE);
  341. ksft_print_msg("Test configs:\n\tthreshold_mb=%u\n\tpattern_seed=%u\n\n",
  342. threshold_mb, pattern_seed);
  343. page_size = sysconf(_SC_PAGESIZE);
  344. /* Expected mremap failures */
  345. test_cases[0] = MAKE_TEST(page_size, page_size, page_size,
  346. OVERLAPPING, EXPECT_FAILURE,
  347. "mremap - Source and Destination Regions Overlapping");
  348. test_cases[1] = MAKE_TEST(page_size, page_size/4, page_size,
  349. NON_OVERLAPPING, EXPECT_FAILURE,
  350. "mremap - Destination Address Misaligned (1KB-aligned)");
  351. test_cases[2] = MAKE_TEST(page_size/4, page_size, page_size,
  352. NON_OVERLAPPING, EXPECT_FAILURE,
  353. "mremap - Source Address Misaligned (1KB-aligned)");
  354. /* Src addr PTE aligned */
  355. test_cases[3] = MAKE_TEST(PTE, PTE, PTE * 2,
  356. NON_OVERLAPPING, EXPECT_SUCCESS,
  357. "8KB mremap - Source PTE-aligned, Destination PTE-aligned");
  358. /* Src addr 1MB aligned */
  359. test_cases[4] = MAKE_TEST(_1MB, PTE, _2MB, NON_OVERLAPPING, EXPECT_SUCCESS,
  360. "2MB mremap - Source 1MB-aligned, Destination PTE-aligned");
  361. test_cases[5] = MAKE_TEST(_1MB, _1MB, _2MB, NON_OVERLAPPING, EXPECT_SUCCESS,
  362. "2MB mremap - Source 1MB-aligned, Destination 1MB-aligned");
  363. /* Src addr PMD aligned */
  364. test_cases[6] = MAKE_TEST(PMD, PTE, _4MB, NON_OVERLAPPING, EXPECT_SUCCESS,
  365. "4MB mremap - Source PMD-aligned, Destination PTE-aligned");
  366. test_cases[7] = MAKE_TEST(PMD, _1MB, _4MB, NON_OVERLAPPING, EXPECT_SUCCESS,
  367. "4MB mremap - Source PMD-aligned, Destination 1MB-aligned");
  368. test_cases[8] = MAKE_TEST(PMD, PMD, _4MB, NON_OVERLAPPING, EXPECT_SUCCESS,
  369. "4MB mremap - Source PMD-aligned, Destination PMD-aligned");
  370. /* Src addr PUD aligned */
  371. test_cases[9] = MAKE_TEST(PUD, PTE, _2GB, NON_OVERLAPPING, EXPECT_SUCCESS,
  372. "2GB mremap - Source PUD-aligned, Destination PTE-aligned");
  373. test_cases[10] = MAKE_TEST(PUD, _1MB, _2GB, NON_OVERLAPPING, EXPECT_SUCCESS,
  374. "2GB mremap - Source PUD-aligned, Destination 1MB-aligned");
  375. test_cases[11] = MAKE_TEST(PUD, PMD, _2GB, NON_OVERLAPPING, EXPECT_SUCCESS,
  376. "2GB mremap - Source PUD-aligned, Destination PMD-aligned");
  377. test_cases[12] = MAKE_TEST(PUD, PUD, _2GB, NON_OVERLAPPING, EXPECT_SUCCESS,
  378. "2GB mremap - Source PUD-aligned, Destination PUD-aligned");
  379. perf_test_cases[0] = MAKE_TEST(page_size, page_size, _1GB, NON_OVERLAPPING, EXPECT_SUCCESS,
  380. "1GB mremap - Source PTE-aligned, Destination PTE-aligned");
  381. /*
  382. * mremap 1GB region - Page table level aligned time
  383. * comparison.
  384. */
  385. perf_test_cases[1] = MAKE_TEST(PMD, PMD, _1GB, NON_OVERLAPPING, EXPECT_SUCCESS,
  386. "1GB mremap - Source PMD-aligned, Destination PMD-aligned");
  387. perf_test_cases[2] = MAKE_TEST(PUD, PUD, _1GB, NON_OVERLAPPING, EXPECT_SUCCESS,
  388. "1GB mremap - Source PUD-aligned, Destination PUD-aligned");
  389. run_perf_tests = (threshold_mb == VALIDATION_NO_THRESHOLD) ||
  390. (threshold_mb * _1MB >= _1GB);
  391. ksft_set_plan(ARRAY_SIZE(test_cases) + (run_perf_tests ?
  392. ARRAY_SIZE(perf_test_cases) : 0) + num_expand_tests);
  393. for (i = 0; i < ARRAY_SIZE(test_cases); i++)
  394. run_mremap_test_case(test_cases[i], &failures, threshold_mb,
  395. pattern_seed);
  396. mremap_expand_merge(page_size);
  397. if (run_perf_tests) {
  398. ksft_print_msg("\n%s\n",
  399. "mremap HAVE_MOVE_PMD/PUD optimization time comparison for 1GB region:");
  400. for (i = 0; i < ARRAY_SIZE(perf_test_cases); i++)
  401. run_mremap_test_case(perf_test_cases[i], &failures,
  402. threshold_mb, pattern_seed);
  403. }
  404. if (failures > 0)
  405. ksft_exit_fail();
  406. else
  407. ksft_exit_pass();
  408. }