split_huge_page_test.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * A test of splitting PMD THPs and PTE-mapped THPs from a specified virtual
  4. * address range in a process via <debugfs>/split_huge_pages interface.
  5. */
  6. #define _GNU_SOURCE
  7. #include <assert.h>
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <stdarg.h>
  11. #include <unistd.h>
  12. #include <inttypes.h>
  13. #include <string.h>
  14. #include <fcntl.h>
  15. #include <sys/mman.h>
  16. #include <sys/mount.h>
  17. #include <sys/param.h>
  18. #include <malloc.h>
  19. #include <stdbool.h>
  20. #include <time.h>
  21. #include "vm_util.h"
  22. #include "kselftest.h"
  23. uint64_t pagesize;
  24. unsigned int pageshift;
  25. uint64_t pmd_pagesize;
  26. unsigned int pmd_order;
  27. int *expected_orders;
  28. #define SPLIT_DEBUGFS "/sys/kernel/debug/split_huge_pages"
  29. #define SMAP_PATH "/proc/self/smaps"
  30. #define INPUT_MAX 80
  31. #define PID_FMT "%d,0x%lx,0x%lx,%d"
  32. #define PID_FMT_OFFSET "%d,0x%lx,0x%lx,%d,%d"
  33. #define PATH_FMT "%s,0x%lx,0x%lx,%d"
  34. const char *pagemap_proc = "/proc/self/pagemap";
  35. const char *kpageflags_proc = "/proc/kpageflags";
  36. int pagemap_fd;
  37. int kpageflags_fd;
  38. static bool is_backed_by_folio(char *vaddr, int order, int pagemap_fd,
  39. int kpageflags_fd)
  40. {
  41. const uint64_t folio_head_flags = KPF_THP | KPF_COMPOUND_HEAD;
  42. const uint64_t folio_tail_flags = KPF_THP | KPF_COMPOUND_TAIL;
  43. const unsigned long nr_pages = 1UL << order;
  44. unsigned long pfn_head;
  45. uint64_t pfn_flags;
  46. unsigned long pfn;
  47. unsigned long i;
  48. pfn = pagemap_get_pfn(pagemap_fd, vaddr);
  49. /* non present page */
  50. if (pfn == -1UL)
  51. return false;
  52. if (pageflags_get(pfn, kpageflags_fd, &pfn_flags))
  53. goto fail;
  54. /* check for order-0 pages */
  55. if (!order) {
  56. if (pfn_flags & (folio_head_flags | folio_tail_flags))
  57. return false;
  58. return true;
  59. }
  60. /* non THP folio */
  61. if (!(pfn_flags & KPF_THP))
  62. return false;
  63. pfn_head = pfn & ~(nr_pages - 1);
  64. if (pageflags_get(pfn_head, kpageflags_fd, &pfn_flags))
  65. goto fail;
  66. /* head PFN has no compound_head flag set */
  67. if ((pfn_flags & folio_head_flags) != folio_head_flags)
  68. return false;
  69. /* check all tail PFN flags */
  70. for (i = 1; i < nr_pages; i++) {
  71. if (pageflags_get(pfn_head + i, kpageflags_fd, &pfn_flags))
  72. goto fail;
  73. if ((pfn_flags & folio_tail_flags) != folio_tail_flags)
  74. return false;
  75. }
  76. /*
  77. * check the PFN after this folio, but if its flags cannot be obtained,
  78. * assume this folio has the expected order
  79. */
  80. if (pageflags_get(pfn_head + nr_pages, kpageflags_fd, &pfn_flags))
  81. return true;
  82. /* If we find another tail page, then the folio is larger. */
  83. return (pfn_flags & folio_tail_flags) != folio_tail_flags;
  84. fail:
  85. ksft_exit_fail_msg("Failed to get folio info\n");
  86. return false;
  87. }
  88. static int vaddr_pageflags_get(char *vaddr, int pagemap_fd, int kpageflags_fd,
  89. uint64_t *flags)
  90. {
  91. unsigned long pfn;
  92. pfn = pagemap_get_pfn(pagemap_fd, vaddr);
  93. /* non-present PFN */
  94. if (pfn == -1UL)
  95. return 1;
  96. if (pageflags_get(pfn, kpageflags_fd, flags))
  97. return -1;
  98. return 0;
  99. }
  100. /*
  101. * gather_after_split_folio_orders - scan through [vaddr_start, len) and record
  102. * folio orders
  103. *
  104. * @vaddr_start: start vaddr
  105. * @len: range length
  106. * @pagemap_fd: file descriptor to /proc/<pid>/pagemap
  107. * @kpageflags_fd: file descriptor to /proc/kpageflags
  108. * @orders: output folio order array
  109. * @nr_orders: folio order array size
  110. *
  111. * gather_after_split_folio_orders() scan through [vaddr_start, len) and check
  112. * all folios within the range and record their orders. All order-0 pages will
  113. * be recorded. Non-present vaddr is skipped.
  114. *
  115. * NOTE: the function is used to check folio orders after a split is performed,
  116. * so it assumes [vaddr_start, len) fully maps to after-split folios within that
  117. * range.
  118. *
  119. * Return: 0 - no error, -1 - unhandled cases
  120. */
  121. static int gather_after_split_folio_orders(char *vaddr_start, size_t len,
  122. int pagemap_fd, int kpageflags_fd, int orders[], int nr_orders)
  123. {
  124. uint64_t page_flags = 0;
  125. int cur_order = -1;
  126. char *vaddr;
  127. if (pagemap_fd == -1 || kpageflags_fd == -1)
  128. return -1;
  129. if (!orders)
  130. return -1;
  131. if (nr_orders <= 0)
  132. return -1;
  133. for (vaddr = vaddr_start; vaddr < vaddr_start + len;) {
  134. char *next_folio_vaddr;
  135. int status;
  136. status = vaddr_pageflags_get(vaddr, pagemap_fd, kpageflags_fd,
  137. &page_flags);
  138. if (status < 0)
  139. return -1;
  140. /* skip non present vaddr */
  141. if (status == 1) {
  142. vaddr += psize();
  143. continue;
  144. }
  145. /* all order-0 pages with possible false postive (non folio) */
  146. if (!(page_flags & (KPF_COMPOUND_HEAD | KPF_COMPOUND_TAIL))) {
  147. orders[0]++;
  148. vaddr += psize();
  149. continue;
  150. }
  151. /* skip non thp compound pages */
  152. if (!(page_flags & KPF_THP)) {
  153. vaddr += psize();
  154. continue;
  155. }
  156. /* vpn points to part of a THP at this point */
  157. if (page_flags & KPF_COMPOUND_HEAD)
  158. cur_order = 1;
  159. else {
  160. vaddr += psize();
  161. continue;
  162. }
  163. next_folio_vaddr = vaddr + (1UL << (cur_order + pshift()));
  164. if (next_folio_vaddr >= vaddr_start + len)
  165. break;
  166. while ((status = vaddr_pageflags_get(next_folio_vaddr,
  167. pagemap_fd, kpageflags_fd,
  168. &page_flags)) >= 0) {
  169. /*
  170. * non present vaddr, next compound head page, or
  171. * order-0 page
  172. */
  173. if (status == 1 ||
  174. (page_flags & KPF_COMPOUND_HEAD) ||
  175. !(page_flags & (KPF_COMPOUND_HEAD | KPF_COMPOUND_TAIL))) {
  176. if (cur_order < nr_orders) {
  177. orders[cur_order]++;
  178. cur_order = -1;
  179. vaddr = next_folio_vaddr;
  180. }
  181. break;
  182. }
  183. cur_order++;
  184. next_folio_vaddr = vaddr + (1UL << (cur_order + pshift()));
  185. }
  186. if (status < 0)
  187. return status;
  188. }
  189. if (cur_order > 0 && cur_order < nr_orders)
  190. orders[cur_order]++;
  191. return 0;
  192. }
  193. static int check_after_split_folio_orders(char *vaddr_start, size_t len,
  194. int pagemap_fd, int kpageflags_fd, int orders[], int nr_orders)
  195. {
  196. int *vaddr_orders;
  197. int status;
  198. int i;
  199. vaddr_orders = (int *)malloc(sizeof(int) * nr_orders);
  200. if (!vaddr_orders)
  201. ksft_exit_fail_msg("Cannot allocate memory for vaddr_orders");
  202. memset(vaddr_orders, 0, sizeof(int) * nr_orders);
  203. status = gather_after_split_folio_orders(vaddr_start, len, pagemap_fd,
  204. kpageflags_fd, vaddr_orders, nr_orders);
  205. if (status)
  206. ksft_exit_fail_msg("gather folio info failed\n");
  207. for (i = 0; i < nr_orders; i++)
  208. if (vaddr_orders[i] != orders[i]) {
  209. ksft_print_msg("order %d: expected: %d got %d\n", i,
  210. orders[i], vaddr_orders[i]);
  211. status = -1;
  212. }
  213. free(vaddr_orders);
  214. return status;
  215. }
  216. static void write_file(const char *path, const char *buf, size_t buflen)
  217. {
  218. int fd;
  219. ssize_t numwritten;
  220. fd = open(path, O_WRONLY);
  221. if (fd == -1)
  222. ksft_exit_fail_msg("%s open failed: %s\n", path, strerror(errno));
  223. numwritten = write(fd, buf, buflen - 1);
  224. close(fd);
  225. if (numwritten < 1)
  226. ksft_exit_fail_msg("Write failed\n");
  227. }
  228. static void write_debugfs(const char *fmt, ...)
  229. {
  230. char input[INPUT_MAX];
  231. int ret;
  232. va_list argp;
  233. va_start(argp, fmt);
  234. ret = vsnprintf(input, INPUT_MAX, fmt, argp);
  235. va_end(argp);
  236. if (ret >= INPUT_MAX)
  237. ksft_exit_fail_msg("%s: Debugfs input is too long\n", __func__);
  238. write_file(SPLIT_DEBUGFS, input, ret + 1);
  239. }
  240. static char *allocate_zero_filled_hugepage(size_t len)
  241. {
  242. char *result;
  243. size_t i;
  244. result = memalign(pmd_pagesize, len);
  245. if (!result) {
  246. printf("Fail to allocate memory\n");
  247. exit(EXIT_FAILURE);
  248. }
  249. madvise(result, len, MADV_HUGEPAGE);
  250. for (i = 0; i < len; i++)
  251. result[i] = (char)0;
  252. return result;
  253. }
  254. static void verify_rss_anon_split_huge_page_all_zeroes(char *one_page, int nr_hpages, size_t len)
  255. {
  256. unsigned long rss_anon_before, rss_anon_after;
  257. size_t i;
  258. if (!check_huge_anon(one_page, nr_hpages, pmd_pagesize))
  259. ksft_exit_fail_msg("No THP is allocated\n");
  260. rss_anon_before = rss_anon();
  261. if (!rss_anon_before)
  262. ksft_exit_fail_msg("No RssAnon is allocated before split\n");
  263. /* split all THPs */
  264. write_debugfs(PID_FMT, getpid(), (uint64_t)one_page,
  265. (uint64_t)one_page + len, 0);
  266. for (i = 0; i < len; i++)
  267. if (one_page[i] != (char)0)
  268. ksft_exit_fail_msg("%ld byte corrupted\n", i);
  269. if (!check_huge_anon(one_page, 0, pmd_pagesize))
  270. ksft_exit_fail_msg("Still AnonHugePages not split\n");
  271. rss_anon_after = rss_anon();
  272. if (rss_anon_after >= rss_anon_before)
  273. ksft_exit_fail_msg("Incorrect RssAnon value. Before: %ld After: %ld\n",
  274. rss_anon_before, rss_anon_after);
  275. }
  276. static void split_pmd_zero_pages(void)
  277. {
  278. char *one_page;
  279. int nr_hpages = 4;
  280. size_t len = nr_hpages * pmd_pagesize;
  281. one_page = allocate_zero_filled_hugepage(len);
  282. verify_rss_anon_split_huge_page_all_zeroes(one_page, nr_hpages, len);
  283. ksft_test_result_pass("Split zero filled huge pages successful\n");
  284. free(one_page);
  285. }
  286. static void split_pmd_thp_to_order(int order)
  287. {
  288. char *one_page;
  289. size_t len = 4 * pmd_pagesize;
  290. size_t i;
  291. one_page = memalign(pmd_pagesize, len);
  292. if (!one_page)
  293. ksft_exit_fail_msg("Fail to allocate memory: %s\n", strerror(errno));
  294. madvise(one_page, len, MADV_HUGEPAGE);
  295. for (i = 0; i < len; i++)
  296. one_page[i] = (char)i;
  297. if (!check_huge_anon(one_page, 4, pmd_pagesize))
  298. ksft_exit_fail_msg("No THP is allocated\n");
  299. /* split all THPs */
  300. write_debugfs(PID_FMT, getpid(), (uint64_t)one_page,
  301. (uint64_t)one_page + len, order);
  302. for (i = 0; i < len; i++)
  303. if (one_page[i] != (char)i)
  304. ksft_exit_fail_msg("%ld byte corrupted\n", i);
  305. memset(expected_orders, 0, sizeof(int) * (pmd_order + 1));
  306. expected_orders[order] = 4 << (pmd_order - order);
  307. if (check_after_split_folio_orders(one_page, len, pagemap_fd,
  308. kpageflags_fd, expected_orders,
  309. (pmd_order + 1)))
  310. ksft_exit_fail_msg("Unexpected THP split\n");
  311. if (!check_huge_anon(one_page, 0, pmd_pagesize))
  312. ksft_exit_fail_msg("Still AnonHugePages not split\n");
  313. ksft_test_result_pass("Split huge pages to order %d successful\n", order);
  314. free(one_page);
  315. }
  316. static void split_pte_mapped_thp(void)
  317. {
  318. const size_t nr_thps = 4;
  319. const size_t thp_area_size = nr_thps * pmd_pagesize;
  320. const size_t page_area_size = nr_thps * pagesize;
  321. char *thp_area, *tmp, *page_area = MAP_FAILED;
  322. size_t i;
  323. thp_area = mmap((void *)(1UL << 30), thp_area_size, PROT_READ | PROT_WRITE,
  324. MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
  325. if (thp_area == MAP_FAILED) {
  326. ksft_test_result_fail("Fail to allocate memory: %s\n", strerror(errno));
  327. return;
  328. }
  329. madvise(thp_area, thp_area_size, MADV_HUGEPAGE);
  330. for (i = 0; i < thp_area_size; i++)
  331. thp_area[i] = (char)i;
  332. if (!check_huge_anon(thp_area, nr_thps, pmd_pagesize)) {
  333. ksft_test_result_skip("Not all THPs allocated\n");
  334. goto out;
  335. }
  336. /*
  337. * To challenge spitting code, we will mremap a single page of each
  338. * THP (page[i] of thp[i]) in the thp_area into page_area. This will
  339. * replace the PMD mappings in the thp_area by PTE mappings first,
  340. * but leaving the THP unsplit, to then create a page-sized hole in
  341. * the thp_area.
  342. * We will then manually trigger splitting of all THPs through the
  343. * single mremap'ed pages of each THP in the page_area.
  344. */
  345. page_area = mmap(NULL, page_area_size, PROT_READ | PROT_WRITE,
  346. MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
  347. if (page_area == MAP_FAILED) {
  348. ksft_test_result_fail("Fail to allocate memory: %s\n", strerror(errno));
  349. goto out;
  350. }
  351. for (i = 0; i < nr_thps; i++) {
  352. tmp = mremap(thp_area + pmd_pagesize * i + pagesize * i,
  353. pagesize, pagesize, MREMAP_MAYMOVE|MREMAP_FIXED,
  354. page_area + pagesize * i);
  355. if (tmp != MAP_FAILED)
  356. continue;
  357. ksft_test_result_fail("mremap failed: %s\n", strerror(errno));
  358. goto out;
  359. }
  360. /*
  361. * Verify that our THPs were not split yet. Note that
  362. * check_huge_anon() cannot be used as it checks for PMD mappings.
  363. */
  364. for (i = 0; i < nr_thps; i++) {
  365. if (is_backed_by_folio(page_area + i * pagesize, pmd_order,
  366. pagemap_fd, kpageflags_fd))
  367. continue;
  368. ksft_test_result_fail("THP %zu missing after mremap\n", i);
  369. goto out;
  370. }
  371. /* Split all THPs through the remapped pages. */
  372. write_debugfs(PID_FMT, getpid(), (uint64_t)page_area,
  373. (uint64_t)page_area + page_area_size, 0);
  374. /* Corruption during mremap or split? */
  375. for (i = 0; i < page_area_size; i++) {
  376. if (page_area[i] == (char)i)
  377. continue;
  378. ksft_test_result_fail("%zu byte corrupted\n", i);
  379. goto out;
  380. }
  381. /* Split failed? */
  382. for (i = 0; i < nr_thps; i++) {
  383. if (is_backed_by_folio(page_area + i * pagesize, 0,
  384. pagemap_fd, kpageflags_fd))
  385. continue;
  386. ksft_test_result_fail("THP %zu not split\n", i);
  387. }
  388. ksft_test_result_pass("Split PTE-mapped huge pages successful\n");
  389. out:
  390. munmap(thp_area, thp_area_size);
  391. if (page_area != MAP_FAILED)
  392. munmap(page_area, page_area_size);
  393. }
  394. static void split_file_backed_thp(int order)
  395. {
  396. int status;
  397. int fd;
  398. char tmpfs_template[] = "/tmp/thp_split_XXXXXX";
  399. const char *tmpfs_loc = mkdtemp(tmpfs_template);
  400. char testfile[INPUT_MAX];
  401. ssize_t num_written, num_read;
  402. char *file_buf1, *file_buf2;
  403. uint64_t pgoff_start = 0, pgoff_end = 1024;
  404. int i;
  405. ksft_print_msg("Please enable pr_debug in split_huge_pages_in_file() for more info.\n");
  406. file_buf1 = (char *)malloc(pmd_pagesize);
  407. file_buf2 = (char *)malloc(pmd_pagesize);
  408. if (!file_buf1 || !file_buf2) {
  409. ksft_print_msg("cannot allocate file buffers\n");
  410. goto out;
  411. }
  412. for (i = 0; i < pmd_pagesize; i++)
  413. file_buf1[i] = (char)i;
  414. memset(file_buf2, 0, pmd_pagesize);
  415. status = mount("tmpfs", tmpfs_loc, "tmpfs", 0, "huge=always,size=4m");
  416. if (status)
  417. ksft_exit_fail_msg("Unable to create a tmpfs for testing\n");
  418. status = snprintf(testfile, INPUT_MAX, "%s/thp_file", tmpfs_loc);
  419. if (status >= INPUT_MAX) {
  420. ksft_print_msg("Fail to create file-backed THP split testing file\n");
  421. goto cleanup;
  422. }
  423. fd = open(testfile, O_CREAT|O_RDWR, 0664);
  424. if (fd == -1) {
  425. ksft_perror("Cannot open testing file");
  426. goto cleanup;
  427. }
  428. /* write pmd size data to the file, so a file-backed THP can be allocated */
  429. num_written = write(fd, file_buf1, pmd_pagesize);
  430. if (num_written == -1 || num_written != pmd_pagesize) {
  431. ksft_perror("Failed to write data to testing file");
  432. goto close_file;
  433. }
  434. /* split the file-backed THP */
  435. write_debugfs(PATH_FMT, testfile, pgoff_start, pgoff_end, order);
  436. /* check file content after split */
  437. status = lseek(fd, 0, SEEK_SET);
  438. if (status == -1) {
  439. ksft_perror("Cannot lseek file");
  440. goto close_file;
  441. }
  442. num_read = read(fd, file_buf2, num_written);
  443. if (num_read == -1 || num_read != num_written) {
  444. ksft_perror("Cannot read file content back");
  445. goto close_file;
  446. }
  447. if (strncmp(file_buf1, file_buf2, pmd_pagesize) != 0) {
  448. ksft_print_msg("File content changed\n");
  449. goto close_file;
  450. }
  451. close(fd);
  452. status = unlink(testfile);
  453. if (status) {
  454. ksft_perror("Cannot remove testing file");
  455. goto cleanup;
  456. }
  457. status = umount(tmpfs_loc);
  458. if (status) {
  459. rmdir(tmpfs_loc);
  460. ksft_exit_fail_msg("Unable to umount %s\n", tmpfs_loc);
  461. }
  462. status = rmdir(tmpfs_loc);
  463. if (status)
  464. ksft_exit_fail_msg("cannot remove tmp dir: %s\n", strerror(errno));
  465. ksft_print_msg("Please check dmesg for more information\n");
  466. ksft_test_result_pass("File-backed THP split to order %d test done\n", order);
  467. return;
  468. close_file:
  469. close(fd);
  470. cleanup:
  471. umount(tmpfs_loc);
  472. rmdir(tmpfs_loc);
  473. out:
  474. ksft_exit_fail_msg("Error occurred\n");
  475. }
  476. static bool prepare_thp_fs(const char *xfs_path, char *thp_fs_template,
  477. const char **thp_fs_loc)
  478. {
  479. if (xfs_path) {
  480. *thp_fs_loc = xfs_path;
  481. return false;
  482. }
  483. *thp_fs_loc = mkdtemp(thp_fs_template);
  484. if (!*thp_fs_loc)
  485. ksft_exit_fail_msg("cannot create temp folder\n");
  486. return true;
  487. }
  488. static void cleanup_thp_fs(const char *thp_fs_loc, bool created_tmp)
  489. {
  490. int status;
  491. if (!created_tmp)
  492. return;
  493. status = rmdir(thp_fs_loc);
  494. if (status)
  495. ksft_exit_fail_msg("cannot remove tmp dir: %s\n",
  496. strerror(errno));
  497. }
  498. static int create_pagecache_thp_and_fd(const char *testfile, size_t fd_size,
  499. int *fd, char **addr)
  500. {
  501. size_t i;
  502. unsigned char buf[1024];
  503. srand(time(NULL));
  504. *fd = open(testfile, O_CREAT | O_RDWR, 0664);
  505. if (*fd == -1)
  506. ksft_exit_fail_msg("Failed to create a file at %s\n", testfile);
  507. assert(fd_size % sizeof(buf) == 0);
  508. for (i = 0; i < sizeof(buf); i++)
  509. buf[i] = (unsigned char)i;
  510. for (i = 0; i < fd_size; i += sizeof(buf))
  511. write(*fd, buf, sizeof(buf));
  512. close(*fd);
  513. sync();
  514. *fd = open("/proc/sys/vm/drop_caches", O_WRONLY);
  515. if (*fd == -1) {
  516. ksft_perror("open drop_caches");
  517. goto err_out_unlink;
  518. }
  519. if (write(*fd, "3", 1) != 1) {
  520. ksft_perror("write to drop_caches");
  521. goto err_out_unlink;
  522. }
  523. close(*fd);
  524. *fd = open(testfile, O_RDWR);
  525. if (*fd == -1) {
  526. ksft_perror("Failed to open testfile\n");
  527. goto err_out_unlink;
  528. }
  529. *addr = mmap(NULL, fd_size, PROT_READ|PROT_WRITE, MAP_SHARED, *fd, 0);
  530. if (*addr == (char *)-1) {
  531. ksft_perror("cannot mmap");
  532. goto err_out_close;
  533. }
  534. madvise(*addr, fd_size, MADV_HUGEPAGE);
  535. force_read_pages(*addr, fd_size / pmd_pagesize, pmd_pagesize);
  536. if (!check_huge_file(*addr, fd_size / pmd_pagesize, pmd_pagesize)) {
  537. ksft_print_msg("No large pagecache folio generated, please provide a filesystem supporting large folio\n");
  538. munmap(*addr, fd_size);
  539. close(*fd);
  540. unlink(testfile);
  541. ksft_test_result_skip("Pagecache folio split skipped\n");
  542. return -2;
  543. }
  544. return 0;
  545. err_out_close:
  546. close(*fd);
  547. err_out_unlink:
  548. unlink(testfile);
  549. ksft_exit_fail_msg("Failed to create large pagecache folios\n");
  550. return -1;
  551. }
  552. static void split_thp_in_pagecache_to_order_at(size_t fd_size,
  553. const char *fs_loc, int order, int offset)
  554. {
  555. int fd;
  556. char *split_addr;
  557. char *addr;
  558. size_t i;
  559. char testfile[INPUT_MAX];
  560. int err = 0;
  561. err = snprintf(testfile, INPUT_MAX, "%s/test", fs_loc);
  562. if (err < 0)
  563. ksft_exit_fail_msg("cannot generate right test file name\n");
  564. err = create_pagecache_thp_and_fd(testfile, fd_size, &fd, &addr);
  565. if (err)
  566. return;
  567. err = 0;
  568. memset(expected_orders, 0, sizeof(int) * (pmd_order + 1));
  569. /*
  570. * use [split_addr, split_addr + pagesize) range to split THPs, since
  571. * the debugfs function always split a range with pagesize step and
  572. * providing a full [addr, addr + fd_size) range can trigger multiple
  573. * splits, complicating after-split result checking.
  574. */
  575. if (offset == -1) {
  576. for (split_addr = addr; split_addr < addr + fd_size; split_addr += pmd_pagesize)
  577. write_debugfs(PID_FMT, getpid(), (uint64_t)split_addr,
  578. (uint64_t)split_addr + pagesize, order);
  579. expected_orders[order] = fd_size / (pagesize << order);
  580. } else {
  581. int times = fd_size / pmd_pagesize;
  582. for (split_addr = addr; split_addr < addr + fd_size; split_addr += pmd_pagesize)
  583. write_debugfs(PID_FMT_OFFSET, getpid(), (uint64_t)split_addr,
  584. (uint64_t)split_addr + pagesize, order, offset);
  585. for (i = order + 1; i < pmd_order; i++)
  586. expected_orders[i] = times;
  587. expected_orders[order] = 2 * times;
  588. }
  589. for (i = 0; i < fd_size; i++)
  590. if (*(addr + i) != (char)i) {
  591. ksft_print_msg("%lu byte corrupted in the file\n", i);
  592. err = EXIT_FAILURE;
  593. goto out;
  594. }
  595. if (check_after_split_folio_orders(addr, fd_size, pagemap_fd,
  596. kpageflags_fd, expected_orders,
  597. (pmd_order + 1))) {
  598. ksft_print_msg("Unexpected THP split\n");
  599. err = 1;
  600. goto out;
  601. }
  602. if (!check_huge_file(addr, 0, pmd_pagesize)) {
  603. ksft_print_msg("Still FilePmdMapped not split\n");
  604. err = EXIT_FAILURE;
  605. goto out;
  606. }
  607. out:
  608. munmap(addr, fd_size);
  609. close(fd);
  610. unlink(testfile);
  611. if (offset == -1) {
  612. if (err)
  613. ksft_exit_fail_msg("Split PMD-mapped pagecache folio to order %d failed\n", order);
  614. ksft_test_result_pass("Split PMD-mapped pagecache folio to order %d passed\n", order);
  615. } else {
  616. if (err)
  617. ksft_exit_fail_msg("Split PMD-mapped pagecache folio to order %d at in-folio offset %d failed\n", order, offset);
  618. ksft_test_result_pass("Split PMD-mapped pagecache folio to order %d at in-folio offset %d passed\n", order, offset);
  619. }
  620. }
  621. int main(int argc, char **argv)
  622. {
  623. int i;
  624. size_t fd_size;
  625. char *optional_xfs_path = NULL;
  626. char fs_loc_template[] = "/tmp/thp_fs_XXXXXX";
  627. const char *fs_loc;
  628. bool created_tmp;
  629. int offset;
  630. unsigned int nr_pages;
  631. unsigned int tests;
  632. ksft_print_header();
  633. if (geteuid() != 0) {
  634. ksft_print_msg("Please run the benchmark as root\n");
  635. ksft_finished();
  636. }
  637. if (argc > 1)
  638. optional_xfs_path = argv[1];
  639. pagesize = getpagesize();
  640. pageshift = ffs(pagesize) - 1;
  641. pmd_pagesize = read_pmd_pagesize();
  642. if (!pmd_pagesize)
  643. ksft_exit_fail_msg("Reading PMD pagesize failed\n");
  644. nr_pages = pmd_pagesize / pagesize;
  645. pmd_order = sz2ord(pmd_pagesize, pagesize);
  646. expected_orders = (int *)malloc(sizeof(int) * (pmd_order + 1));
  647. if (!expected_orders)
  648. ksft_exit_fail_msg("Fail to allocate memory: %s\n", strerror(errno));
  649. tests = 2 + (pmd_order - 1) + (2 * pmd_order) + (pmd_order - 1) * 4 + 2;
  650. ksft_set_plan(tests);
  651. pagemap_fd = open(pagemap_proc, O_RDONLY);
  652. if (pagemap_fd == -1)
  653. ksft_exit_fail_msg("read pagemap: %s\n", strerror(errno));
  654. kpageflags_fd = open(kpageflags_proc, O_RDONLY);
  655. if (kpageflags_fd == -1)
  656. ksft_exit_fail_msg("read kpageflags: %s\n", strerror(errno));
  657. fd_size = 2 * pmd_pagesize;
  658. split_pmd_zero_pages();
  659. for (i = 0; i < pmd_order; i++)
  660. if (i != 1)
  661. split_pmd_thp_to_order(i);
  662. split_pte_mapped_thp();
  663. for (i = 0; i < pmd_order; i++)
  664. split_file_backed_thp(i);
  665. created_tmp = prepare_thp_fs(optional_xfs_path, fs_loc_template,
  666. &fs_loc);
  667. for (i = pmd_order - 1; i >= 0; i--)
  668. split_thp_in_pagecache_to_order_at(fd_size, fs_loc, i, -1);
  669. for (i = 0; i < pmd_order; i++)
  670. for (offset = 0;
  671. offset < nr_pages;
  672. offset += MAX(nr_pages / 4, 1 << i))
  673. split_thp_in_pagecache_to_order_at(fd_size, fs_loc, i, offset);
  674. cleanup_thp_fs(fs_loc, created_tmp);
  675. close(pagemap_fd);
  676. close(kpageflags_fd);
  677. free(expected_orders);
  678. ksft_finished();
  679. return 0;
  680. }