// SPDX-License-Identifier: GPL-2.0 /* * This program tests for hugepage leaks after DIO writes to a file using a * hugepage as the user buffer. During DIO, the user buffer is pinned and * should be properly unpinned upon completion. This patch verifies that the * kernel correctly unpins the buffer at DIO completion for both aligned and * unaligned user buffer offsets (w.r.t page boundary), ensuring the hugepage * is freed upon unmapping. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "vm_util.h" #include "../kselftest.h" void run_dio_using_hugetlb(unsigned int start_off, unsigned int end_off) { int fd; char *buffer = NULL; char *orig_buffer = NULL; size_t h_pagesize = 0; size_t writesize; int free_hpage_b = 0; int free_hpage_a = 0; const int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB; const int mmap_prot = PROT_READ | PROT_WRITE; writesize = end_off - start_off; /* Get the default huge page size */ h_pagesize = default_huge_page_size(); if (!h_pagesize) ksft_exit_fail_msg("Unable to determine huge page size\n"); /* Open the file to DIO */ fd = open("/tmp", O_TMPFILE | O_RDWR | O_DIRECT, 0664); if (fd < 0) ksft_exit_fail_perror("Error opening file\n"); /* Get the free huge pages before allocation */ free_hpage_b = get_free_hugepages(); if (free_hpage_b == 0) { close(fd); ksft_exit_skip("No free hugepage, exiting!\n"); } /* Allocate a hugetlb page */ orig_buffer = mmap(NULL, h_pagesize, mmap_prot, mmap_flags, -1, 0); if (orig_buffer == MAP_FAILED) { close(fd); ksft_exit_fail_perror("Error mapping memory\n"); } buffer = orig_buffer; buffer += start_off; memset(buffer, 'A', writesize); /* Write the buffer to the file */ if (write(fd, buffer, writesize) != (writesize)) { munmap(orig_buffer, h_pagesize); close(fd); ksft_exit_fail_perror("Error writing to file\n"); } /* unmap the huge page */ munmap(orig_buffer, h_pagesize); close(fd); /* Get the free huge pages after unmap*/ free_hpage_a = get_free_hugepages(); /* * If the no. of free hugepages before allocation and after unmap does * not match - that means there could still be a page which is pinned. */ if (free_hpage_a != free_hpage_b) { ksft_print_msg("No. Free pages before allocation : %d\n", free_hpage_b); ksft_print_msg("No. Free pages after munmap : %d\n", free_hpage_a); ksft_test_result_fail(": Huge pages not freed!\n"); } else { ksft_print_msg("No. Free pages before allocation : %d\n", free_hpage_b); ksft_print_msg("No. Free pages after munmap : %d\n", free_hpage_a); ksft_test_result_pass(": Huge pages freed successfully !\n"); } } int main(void) { size_t pagesize = 0; ksft_print_header(); ksft_set_plan(4); /* Get base page size */ pagesize = psize(); /* start and end is aligned to pagesize */ run_dio_using_hugetlb(0, (pagesize * 3)); /* start is aligned but end is not aligned */ run_dio_using_hugetlb(0, (pagesize * 3) - (pagesize / 2)); /* start is unaligned and end is aligned */ run_dio_using_hugetlb(pagesize / 2, (pagesize * 3)); /* both start and end are unaligned */ run_dio_using_hugetlb(pagesize / 2, (pagesize * 3) + (pagesize / 2)); ksft_finished(); }