gup_longterm.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  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 result = KSFT_PASS;
  87. int ret;
  88. if (fd < 0) {
  89. result = KSFT_FAIL;
  90. goto report;
  91. }
  92. if (ftruncate(fd, size)) {
  93. if (errno == ENOENT) {
  94. skip_test_dodgy_fs("ftruncate()");
  95. } else {
  96. ksft_print_msg("ftruncate() failed (%s)\n",
  97. strerror(errno));
  98. result = KSFT_FAIL;
  99. goto report;
  100. }
  101. return;
  102. }
  103. if (fallocate(fd, 0, 0, size)) {
  104. /*
  105. * Some filesystems (eg, NFSv3) don't support
  106. * fallocate(), report this as a skip rather than a
  107. * test failure.
  108. */
  109. if (errno == EOPNOTSUPP) {
  110. ksft_print_msg("fallocate() not supported by filesystem\n");
  111. result = KSFT_SKIP;
  112. } else if (size == pagesize) {
  113. ksft_print_msg("fallocate() failed (%s)\n", strerror(errno));
  114. result = KSFT_FAIL;
  115. } else {
  116. ksft_print_msg("need more free huge pages\n");
  117. result = KSFT_SKIP;
  118. }
  119. goto report;
  120. }
  121. mem = mmap(NULL, size, PROT_READ | PROT_WRITE,
  122. shared ? MAP_SHARED : MAP_PRIVATE, fd, 0);
  123. if (mem == MAP_FAILED) {
  124. if (size == pagesize || shared) {
  125. ksft_print_msg("mmap() failed (%s)\n", strerror(errno));
  126. result = KSFT_FAIL;
  127. } else {
  128. ksft_print_msg("need more free huge pages\n");
  129. result = KSFT_SKIP;
  130. }
  131. goto report;
  132. }
  133. /* Fault in the page such that GUP-fast can pin it directly. */
  134. memset(mem, 0, size);
  135. switch (type) {
  136. case TEST_TYPE_RO:
  137. case TEST_TYPE_RO_FAST:
  138. /*
  139. * Cover more cases regarding unsharing decisions when
  140. * long-term R/O pinning by mapping the page R/O.
  141. */
  142. ret = mprotect(mem, size, PROT_READ);
  143. if (ret) {
  144. ksft_print_msg("mprotect() failed (%s)\n", strerror(errno));
  145. result = KSFT_FAIL;
  146. goto munmap;
  147. }
  148. /* FALLTHROUGH */
  149. case TEST_TYPE_RW:
  150. case TEST_TYPE_RW_FAST: {
  151. struct pin_longterm_test args;
  152. const bool fast = type == TEST_TYPE_RO_FAST ||
  153. type == TEST_TYPE_RW_FAST;
  154. const bool rw = type == TEST_TYPE_RW ||
  155. type == TEST_TYPE_RW_FAST;
  156. if (gup_fd < 0) {
  157. ksft_print_msg("gup_test not available\n");
  158. result = KSFT_SKIP;
  159. break;
  160. }
  161. if (rw && shared && fs_is_unknown(fs_type)) {
  162. ksft_print_msg("Unknown filesystem\n");
  163. result = KSFT_SKIP;
  164. break;
  165. }
  166. /*
  167. * R/O pinning or pinning in a private mapping is always
  168. * expected to work. Otherwise, we expect long-term R/W pinning
  169. * to only succeed for special filesystems.
  170. */
  171. should_work = !shared || !rw ||
  172. fs_supports_writable_longterm_pinning(fs_type);
  173. args.addr = (__u64)(uintptr_t)mem;
  174. args.size = size;
  175. args.flags = fast ? PIN_LONGTERM_TEST_FLAG_USE_FAST : 0;
  176. args.flags |= rw ? PIN_LONGTERM_TEST_FLAG_USE_WRITE : 0;
  177. ret = ioctl(gup_fd, PIN_LONGTERM_TEST_START, &args);
  178. if (ret && errno == EINVAL) {
  179. ksft_print_msg("PIN_LONGTERM_TEST_START failed (EINVAL)n");
  180. result = KSFT_SKIP;
  181. break;
  182. } else if (ret && errno == EFAULT) {
  183. if (should_work)
  184. result = KSFT_FAIL;
  185. else
  186. result = KSFT_PASS;
  187. break;
  188. } else if (ret) {
  189. ksft_print_msg("PIN_LONGTERM_TEST_START failed (%s)\n",
  190. strerror(errno));
  191. result = KSFT_FAIL;
  192. break;
  193. }
  194. if (ioctl(gup_fd, PIN_LONGTERM_TEST_STOP))
  195. ksft_print_msg("[INFO] PIN_LONGTERM_TEST_STOP failed (%s)\n",
  196. strerror(errno));
  197. /*
  198. * TODO: if the kernel ever supports long-term R/W pinning on
  199. * some previously unsupported filesystems, we might want to
  200. * perform some additional tests for possible data corruptions.
  201. */
  202. if (should_work)
  203. result = KSFT_PASS;
  204. else
  205. result = KSFT_FAIL;
  206. break;
  207. }
  208. #ifdef LOCAL_CONFIG_HAVE_LIBURING
  209. case TEST_TYPE_IOURING: {
  210. struct io_uring ring;
  211. struct iovec iov;
  212. /* io_uring always pins pages writable. */
  213. if (shared && fs_is_unknown(fs_type)) {
  214. ksft_print_msg("Unknown filesystem\n");
  215. result = KSFT_SKIP;
  216. goto report;
  217. }
  218. should_work = !shared ||
  219. fs_supports_writable_longterm_pinning(fs_type);
  220. /* Skip on errors, as we might just lack kernel support. */
  221. ret = io_uring_queue_init(1, &ring, 0);
  222. if (ret < 0) {
  223. ksft_print_msg("io_uring_queue_init() failed (%s)\n",
  224. strerror(-ret));
  225. result = KSFT_SKIP;
  226. break;
  227. }
  228. /*
  229. * Register the range as a fixed buffer. This will FOLL_WRITE |
  230. * FOLL_PIN | FOLL_LONGTERM the range.
  231. */
  232. iov.iov_base = mem;
  233. iov.iov_len = size;
  234. ret = io_uring_register_buffers(&ring, &iov, 1);
  235. /* Only new kernels return EFAULT. */
  236. if (ret && (errno == ENOSPC || errno == EOPNOTSUPP ||
  237. errno == EFAULT)) {
  238. if (should_work) {
  239. ksft_print_msg("Should have failed (%s)\n",
  240. strerror(errno));
  241. result = KSFT_FAIL;
  242. } else {
  243. result = KSFT_PASS;
  244. }
  245. } else if (ret) {
  246. /*
  247. * We might just lack support or have insufficient
  248. * MEMLOCK limits.
  249. */
  250. ksft_print_msg("io_uring_register_buffers() failed (%s)\n",
  251. strerror(-ret));
  252. result = KSFT_SKIP;
  253. } else {
  254. if (should_work) {
  255. result = KSFT_PASS;
  256. } else {
  257. ksft_print_msg("Should have worked\n");
  258. result = KSFT_FAIL;
  259. }
  260. io_uring_unregister_buffers(&ring);
  261. }
  262. io_uring_queue_exit(&ring);
  263. break;
  264. }
  265. #endif /* LOCAL_CONFIG_HAVE_LIBURING */
  266. default:
  267. assert(false);
  268. }
  269. munmap:
  270. munmap(mem, size);
  271. report:
  272. log_test_result(result);
  273. }
  274. typedef void (*test_fn)(int fd, size_t size);
  275. static void run_with_memfd(test_fn fn, const char *desc)
  276. {
  277. int fd;
  278. log_test_start("%s ... with memfd", desc);
  279. fd = memfd_create("test", 0);
  280. if (fd < 0) {
  281. ksft_print_msg("memfd_create() failed (%s)\n", strerror(errno));
  282. log_test_result(KSFT_SKIP);
  283. return;
  284. }
  285. fn(fd, pagesize);
  286. close(fd);
  287. }
  288. static void run_with_tmpfile(test_fn fn, const char *desc)
  289. {
  290. FILE *file;
  291. int fd;
  292. log_test_start("%s ... with tmpfile", desc);
  293. file = tmpfile();
  294. if (!file) {
  295. ksft_print_msg("tmpfile() failed (%s)\n", strerror(errno));
  296. fd = -1;
  297. } else {
  298. fd = fileno(file);
  299. if (fd < 0) {
  300. ksft_print_msg("fileno() failed (%s)\n", strerror(errno));
  301. }
  302. }
  303. fn(fd, pagesize);
  304. if (file)
  305. fclose(file);
  306. }
  307. static void run_with_local_tmpfile(test_fn fn, const char *desc)
  308. {
  309. char filename[] = __FILE__"_tmpfile_XXXXXX";
  310. int fd;
  311. log_test_start("%s ... with local tmpfile", desc);
  312. fd = mkstemp(filename);
  313. if (fd < 0)
  314. ksft_print_msg("mkstemp() failed (%s)\n", strerror(errno));
  315. if (unlink(filename)) {
  316. ksft_print_msg("unlink() failed (%s)\n", strerror(errno));
  317. close(fd);
  318. fd = -1;
  319. }
  320. fn(fd, pagesize);
  321. if (fd >= 0)
  322. close(fd);
  323. }
  324. static void run_with_memfd_hugetlb(test_fn fn, const char *desc,
  325. size_t hugetlbsize)
  326. {
  327. int flags = MFD_HUGETLB;
  328. int fd;
  329. log_test_start("%s ... with memfd hugetlb (%zu kB)", desc,
  330. hugetlbsize / 1024);
  331. flags |= __builtin_ctzll(hugetlbsize) << MFD_HUGE_SHIFT;
  332. fd = memfd_create("test", flags);
  333. if (fd < 0) {
  334. ksft_print_msg("memfd_create() failed (%s)\n", strerror(errno));
  335. log_test_result(KSFT_SKIP);
  336. return;
  337. }
  338. fn(fd, hugetlbsize);
  339. close(fd);
  340. }
  341. struct test_case {
  342. const char *desc;
  343. test_fn fn;
  344. };
  345. static void test_shared_rw_pin(int fd, size_t size)
  346. {
  347. do_test(fd, size, TEST_TYPE_RW, true);
  348. }
  349. static void test_shared_rw_fast_pin(int fd, size_t size)
  350. {
  351. do_test(fd, size, TEST_TYPE_RW_FAST, true);
  352. }
  353. static void test_shared_ro_pin(int fd, size_t size)
  354. {
  355. do_test(fd, size, TEST_TYPE_RO, true);
  356. }
  357. static void test_shared_ro_fast_pin(int fd, size_t size)
  358. {
  359. do_test(fd, size, TEST_TYPE_RO_FAST, true);
  360. }
  361. static void test_private_rw_pin(int fd, size_t size)
  362. {
  363. do_test(fd, size, TEST_TYPE_RW, false);
  364. }
  365. static void test_private_rw_fast_pin(int fd, size_t size)
  366. {
  367. do_test(fd, size, TEST_TYPE_RW_FAST, false);
  368. }
  369. static void test_private_ro_pin(int fd, size_t size)
  370. {
  371. do_test(fd, size, TEST_TYPE_RO, false);
  372. }
  373. static void test_private_ro_fast_pin(int fd, size_t size)
  374. {
  375. do_test(fd, size, TEST_TYPE_RO_FAST, false);
  376. }
  377. #ifdef LOCAL_CONFIG_HAVE_LIBURING
  378. static void test_shared_iouring(int fd, size_t size)
  379. {
  380. do_test(fd, size, TEST_TYPE_IOURING, true);
  381. }
  382. static void test_private_iouring(int fd, size_t size)
  383. {
  384. do_test(fd, size, TEST_TYPE_IOURING, false);
  385. }
  386. #endif /* LOCAL_CONFIG_HAVE_LIBURING */
  387. static const struct test_case test_cases[] = {
  388. {
  389. "R/W longterm GUP pin in MAP_SHARED file mapping",
  390. test_shared_rw_pin,
  391. },
  392. {
  393. "R/W longterm GUP-fast pin in MAP_SHARED file mapping",
  394. test_shared_rw_fast_pin,
  395. },
  396. {
  397. "R/O longterm GUP pin in MAP_SHARED file mapping",
  398. test_shared_ro_pin,
  399. },
  400. {
  401. "R/O longterm GUP-fast pin in MAP_SHARED file mapping",
  402. test_shared_ro_fast_pin,
  403. },
  404. {
  405. "R/W longterm GUP pin in MAP_PRIVATE file mapping",
  406. test_private_rw_pin,
  407. },
  408. {
  409. "R/W longterm GUP-fast pin in MAP_PRIVATE file mapping",
  410. test_private_rw_fast_pin,
  411. },
  412. {
  413. "R/O longterm GUP pin in MAP_PRIVATE file mapping",
  414. test_private_ro_pin,
  415. },
  416. {
  417. "R/O longterm GUP-fast pin in MAP_PRIVATE file mapping",
  418. test_private_ro_fast_pin,
  419. },
  420. #ifdef LOCAL_CONFIG_HAVE_LIBURING
  421. {
  422. "io_uring fixed buffer with MAP_SHARED file mapping",
  423. test_shared_iouring,
  424. },
  425. {
  426. "io_uring fixed buffer with MAP_PRIVATE file mapping",
  427. test_private_iouring,
  428. },
  429. #endif /* LOCAL_CONFIG_HAVE_LIBURING */
  430. };
  431. static void run_test_case(struct test_case const *test_case)
  432. {
  433. int i;
  434. run_with_memfd(test_case->fn, test_case->desc);
  435. run_with_tmpfile(test_case->fn, test_case->desc);
  436. run_with_local_tmpfile(test_case->fn, test_case->desc);
  437. for (i = 0; i < nr_hugetlbsizes; i++)
  438. run_with_memfd_hugetlb(test_case->fn, test_case->desc,
  439. hugetlbsizes[i]);
  440. }
  441. static int tests_per_test_case(void)
  442. {
  443. return 3 + nr_hugetlbsizes;
  444. }
  445. int main(int argc, char **argv)
  446. {
  447. int i;
  448. pagesize = getpagesize();
  449. nr_hugetlbsizes = detect_hugetlb_page_sizes(hugetlbsizes,
  450. ARRAY_SIZE(hugetlbsizes));
  451. ksft_print_header();
  452. ksft_set_plan(ARRAY_SIZE(test_cases) * tests_per_test_case());
  453. gup_fd = open("/sys/kernel/debug/gup_test", O_RDWR);
  454. for (i = 0; i < ARRAY_SIZE(test_cases); i++)
  455. run_test_case(&test_cases[i]);
  456. ksft_finished();
  457. }