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 11 #define _GNU_SOURCE 12 #include <stdio.h> 13 #include <sys/stat.h> 14 #include <stdlib.h> 15 #include <fcntl.h> 16 #include <stdint.h> 17 #include <unistd.h> 18 #include <string.h> 19 #include <sys/mman.h> 20 #include "vm_util.h" 21 #include "../kselftest.h" 22 23 void run_dio_using_hugetlb(unsigned int start_off, unsigned int end_off) 24 { 25 int fd; 26 char *buffer = NULL; 27 char *orig_buffer = NULL; 28 size_t h_pagesize = 0; 29 size_t writesize; 30 int free_hpage_b = 0; 31 int free_hpage_a = 0; 32 const int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB; 33 const int mmap_prot = PROT_READ | PROT_WRITE; 34 35 writesize = end_off - start_off; 36 37 /* Get the default huge page size */ 38 h_pagesize = default_huge_page_size(); 39 if (!h_pagesize) 40 ksft_exit_fail_msg("Unable to determine huge page size\n"); 41 42 /* Open the file to DIO */ 43 fd = open("/tmp", O_TMPFILE | O_RDWR | O_DIRECT, 0664); 44 if (fd < 0) 45 ksft_exit_fail_perror("Error opening file\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 56 memset(buffer, 'A', writesize); 57 58 /* Write the buffer to the file */ 59 if (write(fd, buffer, writesize) != (writesize)) { 60 munmap(orig_buffer, h_pagesize); 61 close(fd); 62 ksft_exit_fail_perror("Error writing to file\n"); 63 } 64 65 /* unmap the huge page */ 66 munmap(orig_buffer, h_pagesize); 67 close(fd); 68 69 /* Get the free huge pages after unmap*/ 70 free_hpage_a = get_free_hugepages(); 71 72 /* 73 * If the no. of free hugepages before allocation and after unmap does 74 * not match - that means there could still be a page which is pinned. 75 */ 76 if (free_hpage_a != free_hpage_b) { 77 ksft_print_msg("No. Free pages before allocation : %d\n", free_hpage_b); 78 ksft_print_msg("No. Free pages after munmap : %d\n", free_hpage_a); 79 ksft_test_result_fail(": Huge pages not freed!\n"); 80 } else { 81 ksft_print_msg("No. Free pages before allocation : %d\n", free_hpage_b); 82 ksft_print_msg("No. Free pages after munmap : %d\n", free_hpage_a); 83 ksft_test_result_pass(": Huge pages freed successfully !\n"); 84 } 85 } 86 87 int main(void) 88 { 89 size_t pagesize = 0; 90 int fd; 91 92 ksft_print_header(); 93 94 /* Open the file to DIO */ 95 fd = open("/tmp", O_TMPFILE | O_RDWR | O_DIRECT, 0664); 96 if (fd < 0) 97 ksft_exit_skip("Unable to allocate file: %s\n", strerror(errno)); 98 close(fd); 99 100 /* Check if huge pages are free */ 101 if (!get_free_hugepages()) 102 ksft_exit_skip("No free hugepage, exiting\n"); 103 104 ksft_set_plan(4); 105 106 /* Get base page size */ 107 pagesize = psize(); 108 109 /* start and end is aligned to pagesize */ 110 run_dio_using_hugetlb(0, (pagesize * 3)); 111 112 /* start is aligned but end is not aligned */ 113 run_dio_using_hugetlb(0, (pagesize * 3) - (pagesize / 2)); 114 115 /* start is unaligned and end is aligned */ 116 run_dio_using_hugetlb(pagesize / 2, (pagesize * 3)); 117 118 /* both start and end are unaligned */ 119 run_dio_using_hugetlb(pagesize / 2, (pagesize * 3) + (pagesize / 2)); 120 121 ksft_finished(); 122 } 123