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 /* Get the free huge pages before allocation */ 48 free_hpage_b = get_free_hugepages(); 49 if (free_hpage_b == 0) { 50 close(fd); 51 ksft_exit_skip("No free hugepage, exiting!\n"); 52 } 53 54 /* Allocate a hugetlb page */ 55 orig_buffer = mmap(NULL, h_pagesize, mmap_prot, mmap_flags, -1, 0); 56 if (orig_buffer == MAP_FAILED) { 57 close(fd); 58 ksft_exit_fail_perror("Error mapping memory\n"); 59 } 60 buffer = orig_buffer; 61 buffer += start_off; 62 63 memset(buffer, 'A', writesize); 64 65 /* Write the buffer to the file */ 66 if (write(fd, buffer, writesize) != (writesize)) { 67 munmap(orig_buffer, h_pagesize); 68 close(fd); 69 ksft_exit_fail_perror("Error writing to file\n"); 70 } 71 72 /* unmap the huge page */ 73 munmap(orig_buffer, h_pagesize); 74 close(fd); 75 76 /* Get the free huge pages after unmap*/ 77 free_hpage_a = get_free_hugepages(); 78 79 /* 80 * If the no. of free hugepages before allocation and after unmap does 81 * not match - that means there could still be a page which is pinned. 82 */ 83 if (free_hpage_a != free_hpage_b) { 84 ksft_print_msg("No. Free pages before allocation : %d\n", free_hpage_b); 85 ksft_print_msg("No. Free pages after munmap : %d\n", free_hpage_a); 86 ksft_test_result_fail(": Huge pages not freed!\n"); 87 } else { 88 ksft_print_msg("No. Free pages before allocation : %d\n", free_hpage_b); 89 ksft_print_msg("No. Free pages after munmap : %d\n", free_hpage_a); 90 ksft_test_result_pass(": Huge pages freed successfully !\n"); 91 } 92 } 93 94 int main(void) 95 { 96 size_t pagesize = 0; 97 int fd; 98 99 ksft_print_header(); 100 101 /* Open the file to DIO */ 102 fd = open("/tmp", O_TMPFILE | O_RDWR | O_DIRECT, 0664); 103 if (fd < 0) 104 ksft_exit_skip("Unable to allocate file: %s\n", strerror(errno)); 105 close(fd); 106 107 /* Check if huge pages are free */ 108 if (!get_free_hugepages()) 109 ksft_exit_skip("No free hugepage, exiting\n"); 110 111 ksft_set_plan(4); 112 113 /* Get base page size */ 114 pagesize = psize(); 115 116 /* start and end is aligned to pagesize */ 117 run_dio_using_hugetlb(0, (pagesize * 3)); 118 119 /* start is aligned but end is not aligned */ 120 run_dio_using_hugetlb(0, (pagesize * 3) - (pagesize / 2)); 121 122 /* start is unaligned and end is aligned */ 123 run_dio_using_hugetlb(pagesize / 2, (pagesize * 3)); 124 125 /* both start and end are unaligned */ 126 run_dio_using_hugetlb(pagesize / 2, (pagesize * 3) + (pagesize / 2)); 127 128 ksft_finished(); 129 } 130