gup_longterm.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * GUP long-term page pinning tests.
  4. *
  5. * Copyright 2023, Red Hat, Inc.
  6. *
  7. * Author(s): David Hildenbrand <david@redhat.com>
  8. */
  9. #define _GNU_SOURCE
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <stdbool.h>
  13. #include <stdint.h>
  14. #include <unistd.h>
  15. #include <errno.h>
  16. #include <fcntl.h>
  17. #include <assert.h>
  18. #include <sys/mman.h>
  19. #include <sys/ioctl.h>
  20. #include <sys/vfs.h>
  21. #include <linux/magic.h>
  22. #include <linux/memfd.h>
  23. #include "local_config.h"
  24. #ifdef LOCAL_CONFIG_HAVE_LIBURING
  25. #include <liburing.h>
  26. #endif /* LOCAL_CONFIG_HAVE_LIBURING */
  27. #include "../../../../mm/gup_test.h"
  28. #include "../kselftest.h"
  29. #include "vm_util.h"
  30. static size_t pagesize;
  31. static int nr_hugetlbsizes;
  32. static size_t hugetlbsizes[10];
  33. static int gup_fd;
  34. static __fsword_t get_fs_type(int fd)
  35. {
  36. struct statfs fs;
  37. int ret;
  38. do {
  39. ret = fstatfs(fd, &fs);
  40. } while (ret && errno == EINTR);
  41. return ret ? 0 : fs.f_type;
  42. }
  43. static bool fs_is_unknown(__fsword_t fs_type)
  44. {
  45. /*
  46. * We only support some filesystems in our tests when dealing with
  47. * R/W long-term pinning. For these filesystems, we can be fairly sure
  48. * whether they support it or not.
  49. */
  50. switch (fs_type) {
  51. case TMPFS_MAGIC:
  52. case HUGETLBFS_MAGIC:
  53. case BTRFS_SUPER_MAGIC:
  54. case EXT4_SUPER_MAGIC:
  55. case XFS_SUPER_MAGIC:
  56. return false;
  57. default:
  58. return true;
  59. }
  60. }
  61. static bool fs_supports_writable_longterm_pinning(__fsword_t fs_type)
  62. {
  63. assert(!fs_is_unknown(fs_type));
  64. switch (fs_type) {
  65. case TMPFS_MAGIC:
  66. case HUGETLBFS_MAGIC:
  67. return true;
  68. default:
  69. return false;
  70. }
  71. }
  72. enum test_type {
  73. TEST_TYPE_RO,
  74. TEST_TYPE_RO_FAST,
  75. TEST_TYPE_RW,
  76. TEST_TYPE_RW_FAST,
  77. #ifdef LOCAL_CONFIG_HAVE_LIBURING
  78. TEST_TYPE_IOURING,
  79. #endif /* LOCAL_CONFIG_HAVE_LIBURING */
  80. };
  81. static void do_test(int fd, size_t size, enum test_type type, bool shared)
  82. {
  83. __fsword_t fs_type = get_fs_type(fd);
  84. bool should_work;
  85. char *mem;
  86. int ret;
  87. if (ftruncate(fd, size)) {
  88. ksft_test_result_fail("ftruncate() failed\n");
  89. return;
  90. }
  91. if (fallocate(fd, 0, 0, size)) {
  92. if (size == pagesize)
  93. ksft_test_result_fail("fallocate() failed\n");
  94. else
  95. ksft_test_result_skip("need more free huge pages\n");
  96. return;
  97. }
  98. mem = mmap(NULL, size, PROT_READ | PROT_WRITE,
  99. shared ? MAP_SHARED : MAP_PRIVATE, fd, 0);
  100. if (mem == MAP_FAILED) {
  101. if (size == pagesize || shared)
  102. ksft_test_result_fail("mmap() failed\n");
  103. else
  104. ksft_test_result_skip("need more free huge pages\n");
  105. return;
  106. }
  107. /* Fault in the page such that GUP-fast can pin it directly. */
  108. memset(mem, 0, size);
  109. switch (type) {
  110. case TEST_TYPE_RO:
  111. case TEST_TYPE_RO_FAST:
  112. /*
  113. * Cover more cases regarding unsharing decisions when
  114. * long-term R/O pinning by mapping the page R/O.
  115. */
  116. ret = mprotect(mem, size, PROT_READ);
  117. if (ret) {
  118. ksft_test_result_fail("mprotect() failed\n");
  119. goto munmap;
  120. }
  121. /* FALLTHROUGH */
  122. case TEST_TYPE_RW:
  123. case TEST_TYPE_RW_FAST: {
  124. struct pin_longterm_test args;
  125. const bool fast = type == TEST_TYPE_RO_FAST ||
  126. type == TEST_TYPE_RW_FAST;
  127. const bool rw = type == TEST_TYPE_RW ||
  128. type == TEST_TYPE_RW_FAST;
  129. if (gup_fd < 0) {
  130. ksft_test_result_skip("gup_test not available\n");
  131. break;
  132. }
  133. if (rw && shared && fs_is_unknown(fs_type)) {
  134. ksft_test_result_skip("Unknown filesystem\n");
  135. return;
  136. }
  137. /*
  138. * R/O pinning or pinning in a private mapping is always
  139. * expected to work. Otherwise, we expect long-term R/W pinning
  140. * to only succeed for special fielesystems.
  141. */
  142. should_work = !shared || !rw ||
  143. fs_supports_writable_longterm_pinning(fs_type);
  144. args.addr = (__u64)(uintptr_t)mem;
  145. args.size = size;
  146. args.flags = fast ? PIN_LONGTERM_TEST_FLAG_USE_FAST : 0;
  147. args.flags |= rw ? PIN_LONGTERM_TEST_FLAG_USE_WRITE : 0;
  148. ret = ioctl(gup_fd, PIN_LONGTERM_TEST_START, &args);
  149. if (ret && errno == EINVAL) {
  150. ksft_test_result_skip("PIN_LONGTERM_TEST_START failed\n");
  151. break;
  152. } else if (ret && errno == EFAULT) {
  153. ksft_test_result(!should_work, "Should have failed\n");
  154. break;
  155. } else if (ret) {
  156. ksft_test_result_fail("PIN_LONGTERM_TEST_START failed\n");
  157. break;
  158. }
  159. if (ioctl(gup_fd, PIN_LONGTERM_TEST_STOP))
  160. ksft_print_msg("[INFO] PIN_LONGTERM_TEST_STOP failed\n");
  161. /*
  162. * TODO: if the kernel ever supports long-term R/W pinning on
  163. * some previously unsupported filesystems, we might want to
  164. * perform some additional tests for possible data corruptions.
  165. */
  166. ksft_test_result(should_work, "Should have worked\n");
  167. break;
  168. }
  169. #ifdef LOCAL_CONFIG_HAVE_LIBURING
  170. case TEST_TYPE_IOURING: {
  171. struct io_uring ring;
  172. struct iovec iov;
  173. /* io_uring always pins pages writable. */
  174. if (shared && fs_is_unknown(fs_type)) {
  175. ksft_test_result_skip("Unknown filesystem\n");
  176. return;
  177. }
  178. should_work = !shared ||
  179. fs_supports_writable_longterm_pinning(fs_type);
  180. /* Skip on errors, as we might just lack kernel support. */
  181. ret = io_uring_queue_init(1, &ring, 0);
  182. if (ret < 0) {
  183. ksft_test_result_skip("io_uring_queue_init() failed\n");
  184. break;
  185. }
  186. /*
  187. * Register the range as a fixed buffer. This will FOLL_WRITE |
  188. * FOLL_PIN | FOLL_LONGTERM the range.
  189. */
  190. iov.iov_base = mem;
  191. iov.iov_len = size;
  192. ret = io_uring_register_buffers(&ring, &iov, 1);
  193. /* Only new kernels return EFAULT. */
  194. if (ret && (errno == ENOSPC || errno == EOPNOTSUPP ||
  195. errno == EFAULT)) {
  196. ksft_test_result(!should_work, "Should have failed\n");
  197. } else if (ret) {
  198. /*
  199. * We might just lack support or have insufficient
  200. * MEMLOCK limits.
  201. */
  202. ksft_test_result_skip("io_uring_register_buffers() failed\n");
  203. } else {
  204. ksft_test_result(should_work, "Should have worked\n");
  205. io_uring_unregister_buffers(&ring);
  206. }
  207. io_uring_queue_exit(&ring);
  208. break;
  209. }
  210. #endif /* LOCAL_CONFIG_HAVE_LIBURING */
  211. default:
  212. assert(false);
  213. }
  214. munmap:
  215. munmap(mem, size);
  216. }
  217. typedef void (*test_fn)(int fd, size_t size);
  218. static void run_with_memfd(test_fn fn, const char *desc)
  219. {
  220. int fd;
  221. ksft_print_msg("[RUN] %s ... with memfd\n", desc);
  222. fd = memfd_create("test", 0);
  223. if (fd < 0) {
  224. ksft_test_result_fail("memfd_create() failed\n");
  225. return;
  226. }
  227. fn(fd, pagesize);
  228. close(fd);
  229. }
  230. static void run_with_tmpfile(test_fn fn, const char *desc)
  231. {
  232. FILE *file;
  233. int fd;
  234. ksft_print_msg("[RUN] %s ... with tmpfile\n", desc);
  235. file = tmpfile();
  236. if (!file) {
  237. ksft_test_result_fail("tmpfile() failed\n");
  238. return;
  239. }
  240. fd = fileno(file);
  241. if (fd < 0) {
  242. ksft_test_result_fail("fileno() failed\n");
  243. goto close;
  244. }
  245. fn(fd, pagesize);
  246. close:
  247. fclose(file);
  248. }
  249. static void run_with_local_tmpfile(test_fn fn, const char *desc)
  250. {
  251. char filename[] = __FILE__"_tmpfile_XXXXXX";
  252. int fd;
  253. ksft_print_msg("[RUN] %s ... with local tmpfile\n", desc);
  254. fd = mkstemp(filename);
  255. if (fd < 0) {
  256. ksft_test_result_fail("mkstemp() failed\n");
  257. return;
  258. }
  259. if (unlink(filename)) {
  260. ksft_test_result_fail("unlink() failed\n");
  261. goto close;
  262. }
  263. fn(fd, pagesize);
  264. close:
  265. close(fd);
  266. }
  267. static void run_with_memfd_hugetlb(test_fn fn, const char *desc,
  268. size_t hugetlbsize)
  269. {
  270. int flags = MFD_HUGETLB;
  271. int fd;
  272. ksft_print_msg("[RUN] %s ... with memfd hugetlb (%zu kB)\n", desc,
  273. hugetlbsize / 1024);
  274. flags |= __builtin_ctzll(hugetlbsize) << MFD_HUGE_SHIFT;
  275. fd = memfd_create("test", flags);
  276. if (fd < 0) {
  277. ksft_test_result_skip("memfd_create() failed\n");
  278. return;
  279. }
  280. fn(fd, hugetlbsize);
  281. close(fd);
  282. }
  283. struct test_case {
  284. const char *desc;
  285. test_fn fn;
  286. };
  287. static void test_shared_rw_pin(int fd, size_t size)
  288. {
  289. do_test(fd, size, TEST_TYPE_RW, true);
  290. }
  291. static void test_shared_rw_fast_pin(int fd, size_t size)
  292. {
  293. do_test(fd, size, TEST_TYPE_RW_FAST, true);
  294. }
  295. static void test_shared_ro_pin(int fd, size_t size)
  296. {
  297. do_test(fd, size, TEST_TYPE_RO, true);
  298. }
  299. static void test_shared_ro_fast_pin(int fd, size_t size)
  300. {
  301. do_test(fd, size, TEST_TYPE_RO_FAST, true);
  302. }
  303. static void test_private_rw_pin(int fd, size_t size)
  304. {
  305. do_test(fd, size, TEST_TYPE_RW, false);
  306. }
  307. static void test_private_rw_fast_pin(int fd, size_t size)
  308. {
  309. do_test(fd, size, TEST_TYPE_RW_FAST, false);
  310. }
  311. static void test_private_ro_pin(int fd, size_t size)
  312. {
  313. do_test(fd, size, TEST_TYPE_RO, false);
  314. }
  315. static void test_private_ro_fast_pin(int fd, size_t size)
  316. {
  317. do_test(fd, size, TEST_TYPE_RO_FAST, false);
  318. }
  319. #ifdef LOCAL_CONFIG_HAVE_LIBURING
  320. static void test_shared_iouring(int fd, size_t size)
  321. {
  322. do_test(fd, size, TEST_TYPE_IOURING, true);
  323. }
  324. static void test_private_iouring(int fd, size_t size)
  325. {
  326. do_test(fd, size, TEST_TYPE_IOURING, false);
  327. }
  328. #endif /* LOCAL_CONFIG_HAVE_LIBURING */
  329. static const struct test_case test_cases[] = {
  330. {
  331. "R/W longterm GUP pin in MAP_SHARED file mapping",
  332. test_shared_rw_pin,
  333. },
  334. {
  335. "R/W longterm GUP-fast pin in MAP_SHARED file mapping",
  336. test_shared_rw_fast_pin,
  337. },
  338. {
  339. "R/O longterm GUP pin in MAP_SHARED file mapping",
  340. test_shared_ro_pin,
  341. },
  342. {
  343. "R/O longterm GUP-fast pin in MAP_SHARED file mapping",
  344. test_shared_ro_fast_pin,
  345. },
  346. {
  347. "R/W longterm GUP pin in MAP_PRIVATE file mapping",
  348. test_private_rw_pin,
  349. },
  350. {
  351. "R/W longterm GUP-fast pin in MAP_PRIVATE file mapping",
  352. test_private_rw_fast_pin,
  353. },
  354. {
  355. "R/O longterm GUP pin in MAP_PRIVATE file mapping",
  356. test_private_ro_pin,
  357. },
  358. {
  359. "R/O longterm GUP-fast pin in MAP_PRIVATE file mapping",
  360. test_private_ro_fast_pin,
  361. },
  362. #ifdef LOCAL_CONFIG_HAVE_LIBURING
  363. {
  364. "io_uring fixed buffer with MAP_SHARED file mapping",
  365. test_shared_iouring,
  366. },
  367. {
  368. "io_uring fixed buffer with MAP_PRIVATE file mapping",
  369. test_private_iouring,
  370. },
  371. #endif /* LOCAL_CONFIG_HAVE_LIBURING */
  372. };
  373. static void run_test_case(struct test_case const *test_case)
  374. {
  375. int i;
  376. run_with_memfd(test_case->fn, test_case->desc);
  377. run_with_tmpfile(test_case->fn, test_case->desc);
  378. run_with_local_tmpfile(test_case->fn, test_case->desc);
  379. for (i = 0; i < nr_hugetlbsizes; i++)
  380. run_with_memfd_hugetlb(test_case->fn, test_case->desc,
  381. hugetlbsizes[i]);
  382. }
  383. static int tests_per_test_case(void)
  384. {
  385. return 3 + nr_hugetlbsizes;
  386. }
  387. int main(int argc, char **argv)
  388. {
  389. int i, err;
  390. pagesize = getpagesize();
  391. nr_hugetlbsizes = detect_hugetlb_page_sizes(hugetlbsizes,
  392. ARRAY_SIZE(hugetlbsizes));
  393. ksft_print_header();
  394. ksft_set_plan(ARRAY_SIZE(test_cases) * tests_per_test_case());
  395. gup_fd = open("/sys/kernel/debug/gup_test", O_RDWR);
  396. for (i = 0; i < ARRAY_SIZE(test_cases); i++)
  397. run_test_case(&test_cases[i]);
  398. err = ksft_get_fail_cnt();
  399. if (err)
  400. ksft_exit_fail_msg("%d out of %d tests failed\n",
  401. err, ksft_test_num());
  402. ksft_exit_pass();
  403. }