hugetlb_dio.c 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * This program tests for hugepage leaks after DIO writes to a file using a
  4. * hugepage as the user buffer. During DIO, the user buffer is pinned and
  5. * should be properly unpinned upon completion. This patch verifies that the
  6. * kernel correctly unpins the buffer at DIO completion for both aligned and
  7. * unaligned user buffer offsets (w.r.t page boundary), ensuring the hugepage
  8. * is freed upon unmapping.
  9. */
  10. #define _GNU_SOURCE
  11. #include <stdio.h>
  12. #include <sys/stat.h>
  13. #include <stdlib.h>
  14. #include <fcntl.h>
  15. #include <stdint.h>
  16. #include <unistd.h>
  17. #include <string.h>
  18. #include <sys/mman.h>
  19. #include "vm_util.h"
  20. #include "../kselftest.h"
  21. void run_dio_using_hugetlb(unsigned int start_off, unsigned int end_off)
  22. {
  23. int fd;
  24. char *buffer = NULL;
  25. char *orig_buffer = NULL;
  26. size_t h_pagesize = 0;
  27. size_t writesize;
  28. int free_hpage_b = 0;
  29. int free_hpage_a = 0;
  30. const int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB;
  31. const int mmap_prot = PROT_READ | PROT_WRITE;
  32. writesize = end_off - start_off;
  33. /* Get the default huge page size */
  34. h_pagesize = default_huge_page_size();
  35. if (!h_pagesize)
  36. ksft_exit_fail_msg("Unable to determine huge page size\n");
  37. /* Open the file to DIO */
  38. fd = open("/tmp", O_TMPFILE | O_RDWR | O_DIRECT, 0664);
  39. if (fd < 0)
  40. ksft_exit_fail_perror("Error opening file\n");
  41. /* Get the free huge pages before allocation */
  42. free_hpage_b = get_free_hugepages();
  43. if (free_hpage_b == 0) {
  44. close(fd);
  45. ksft_exit_skip("No free hugepage, exiting!\n");
  46. }
  47. /* Allocate a hugetlb page */
  48. orig_buffer = mmap(NULL, h_pagesize, mmap_prot, mmap_flags, -1, 0);
  49. if (orig_buffer == MAP_FAILED) {
  50. close(fd);
  51. ksft_exit_fail_perror("Error mapping memory\n");
  52. }
  53. buffer = orig_buffer;
  54. buffer += start_off;
  55. memset(buffer, 'A', writesize);
  56. /* Write the buffer to the file */
  57. if (write(fd, buffer, writesize) != (writesize)) {
  58. munmap(orig_buffer, h_pagesize);
  59. close(fd);
  60. ksft_exit_fail_perror("Error writing to file\n");
  61. }
  62. /* unmap the huge page */
  63. munmap(orig_buffer, h_pagesize);
  64. close(fd);
  65. /* Get the free huge pages after unmap*/
  66. free_hpage_a = get_free_hugepages();
  67. ksft_print_msg("No. Free pages before allocation : %d\n", free_hpage_b);
  68. ksft_print_msg("No. Free pages after munmap : %d\n", free_hpage_a);
  69. /*
  70. * If the no. of free hugepages before allocation and after unmap does
  71. * not match - that means there could still be a page which is pinned.
  72. */
  73. ksft_test_result(free_hpage_a == free_hpage_b,
  74. "free huge pages from %u-%u\n", start_off, end_off);
  75. }
  76. int main(void)
  77. {
  78. size_t pagesize = 0;
  79. int fd;
  80. ksft_print_header();
  81. /* Open the file to DIO */
  82. fd = open("/tmp", O_TMPFILE | O_RDWR | O_DIRECT, 0664);
  83. if (fd < 0)
  84. ksft_exit_skip("Unable to allocate file: %s\n", strerror(errno));
  85. close(fd);
  86. /* Check if huge pages are free */
  87. if (!get_free_hugepages())
  88. ksft_exit_skip("No free hugepage, exiting\n");
  89. ksft_set_plan(4);
  90. /* Get base page size */
  91. pagesize = psize();
  92. /* start and end is aligned to pagesize */
  93. run_dio_using_hugetlb(0, (pagesize * 3));
  94. /* start is aligned but end is not aligned */
  95. run_dio_using_hugetlb(0, (pagesize * 3) - (pagesize / 2));
  96. /* start is unaligned and end is aligned */
  97. run_dio_using_hugetlb(pagesize / 2, (pagesize * 3));
  98. /* both start and end are unaligned */
  99. run_dio_using_hugetlb(pagesize / 2, (pagesize * 3) + (pagesize / 2));
  100. ksft_finished();
  101. }