hugepage-mremap.c 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * hugepage-mremap:
  4. *
  5. * Example of remapping huge page memory in a user application using the
  6. * mremap system call. The path to a file in a hugetlbfs filesystem must
  7. * be passed as the last argument to this test. The amount of memory used
  8. * by this test in MBs can optionally be passed as an argument. If no memory
  9. * amount is passed, the default amount is 10MB.
  10. *
  11. * To make sure the test triggers pmd sharing and goes through the 'unshare'
  12. * path in the mremap code use 1GB (1024) or more.
  13. */
  14. #define _GNU_SOURCE
  15. #include <stdlib.h>
  16. #include <stdio.h>
  17. #include <unistd.h>
  18. #include <sys/mman.h>
  19. #include <errno.h>
  20. #include <fcntl.h> /* Definition of O_* constants */
  21. #include <sys/syscall.h> /* Definition of SYS_* constants */
  22. #include <linux/userfaultfd.h>
  23. #include <sys/ioctl.h>
  24. #define DEFAULT_LENGTH_MB 10UL
  25. #define MB_TO_BYTES(x) (x * 1024 * 1024)
  26. #define PROTECTION (PROT_READ | PROT_WRITE | PROT_EXEC)
  27. #define FLAGS (MAP_SHARED | MAP_ANONYMOUS)
  28. static void check_bytes(char *addr)
  29. {
  30. printf("First hex is %x\n", *((unsigned int *)addr));
  31. }
  32. static void write_bytes(char *addr, size_t len)
  33. {
  34. unsigned long i;
  35. for (i = 0; i < len; i++)
  36. *(addr + i) = (char)i;
  37. }
  38. static int read_bytes(char *addr, size_t len)
  39. {
  40. unsigned long i;
  41. check_bytes(addr);
  42. for (i = 0; i < len; i++)
  43. if (*(addr + i) != (char)i) {
  44. printf("Mismatch at %lu\n", i);
  45. return 1;
  46. }
  47. return 0;
  48. }
  49. static void register_region_with_uffd(char *addr, size_t len)
  50. {
  51. long uffd; /* userfaultfd file descriptor */
  52. struct uffdio_api uffdio_api;
  53. struct uffdio_register uffdio_register;
  54. /* Create and enable userfaultfd object. */
  55. uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
  56. if (uffd == -1) {
  57. perror("userfaultfd");
  58. exit(1);
  59. }
  60. uffdio_api.api = UFFD_API;
  61. uffdio_api.features = 0;
  62. if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1) {
  63. perror("ioctl-UFFDIO_API");
  64. exit(1);
  65. }
  66. /* Create a private anonymous mapping. The memory will be
  67. * demand-zero paged--that is, not yet allocated. When we
  68. * actually touch the memory, it will be allocated via
  69. * the userfaultfd.
  70. */
  71. addr = mmap(NULL, len, PROT_READ | PROT_WRITE,
  72. MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  73. if (addr == MAP_FAILED) {
  74. perror("mmap");
  75. exit(1);
  76. }
  77. printf("Address returned by mmap() = %p\n", addr);
  78. /* Register the memory range of the mapping we just created for
  79. * handling by the userfaultfd object. In mode, we request to track
  80. * missing pages (i.e., pages that have not yet been faulted in).
  81. */
  82. uffdio_register.range.start = (unsigned long)addr;
  83. uffdio_register.range.len = len;
  84. uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
  85. if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1) {
  86. perror("ioctl-UFFDIO_REGISTER");
  87. exit(1);
  88. }
  89. }
  90. int main(int argc, char *argv[])
  91. {
  92. size_t length = 0;
  93. if (argc != 2 && argc != 3) {
  94. printf("Usage: %s [length_in_MB] <hugetlb_file>\n", argv[0]);
  95. exit(1);
  96. }
  97. /* Read memory length as the first arg if valid, otherwise fallback to
  98. * the default length.
  99. */
  100. if (argc == 3)
  101. length = argc > 2 ? (size_t)atoi(argv[1]) : 0UL;
  102. length = length > 0 ? length : DEFAULT_LENGTH_MB;
  103. length = MB_TO_BYTES(length);
  104. int ret = 0;
  105. /* last arg is the hugetlb file name */
  106. int fd = open(argv[argc-1], O_CREAT | O_RDWR, 0755);
  107. if (fd < 0) {
  108. perror("Open failed");
  109. exit(1);
  110. }
  111. /* mmap to a PUD aligned address to hopefully trigger pmd sharing. */
  112. unsigned long suggested_addr = 0x7eaa40000000;
  113. void *haddr = mmap((void *)suggested_addr, length, PROTECTION,
  114. MAP_HUGETLB | MAP_SHARED | MAP_POPULATE, fd, 0);
  115. printf("Map haddr: Returned address is %p\n", haddr);
  116. if (haddr == MAP_FAILED) {
  117. perror("mmap1");
  118. exit(1);
  119. }
  120. /* mmap again to a dummy address to hopefully trigger pmd sharing. */
  121. suggested_addr = 0x7daa40000000;
  122. void *daddr = mmap((void *)suggested_addr, length, PROTECTION,
  123. MAP_HUGETLB | MAP_SHARED | MAP_POPULATE, fd, 0);
  124. printf("Map daddr: Returned address is %p\n", daddr);
  125. if (daddr == MAP_FAILED) {
  126. perror("mmap3");
  127. exit(1);
  128. }
  129. suggested_addr = 0x7faa40000000;
  130. void *vaddr =
  131. mmap((void *)suggested_addr, length, PROTECTION, FLAGS, -1, 0);
  132. printf("Map vaddr: Returned address is %p\n", vaddr);
  133. if (vaddr == MAP_FAILED) {
  134. perror("mmap2");
  135. exit(1);
  136. }
  137. register_region_with_uffd(haddr, length);
  138. void *addr = mremap(haddr, length, length,
  139. MREMAP_MAYMOVE | MREMAP_FIXED, vaddr);
  140. if (addr == MAP_FAILED) {
  141. perror("mremap");
  142. exit(1);
  143. }
  144. printf("Mremap: Returned address is %p\n", addr);
  145. check_bytes(addr);
  146. write_bytes(addr, length);
  147. ret = read_bytes(addr, length);
  148. munmap(addr, length);
  149. addr = mremap(addr, length, length, 0);
  150. if (addr != MAP_FAILED) {
  151. printf("mremap: Expected failure, but call succeeded\n");
  152. exit(1);
  153. }
  154. close(fd);
  155. unlink(argv[argc-1]);
  156. return ret;
  157. }