Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at master 172 lines 4.8 kB view raw
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 <sys/syscall.h> 21#include "vm_util.h" 22#include "kselftest.h" 23 24#ifndef STATX_DIOALIGN 25#define STATX_DIOALIGN 0x00002000U 26#endif 27 28static int get_dio_alignment(int fd) 29{ 30 struct statx stx; 31 int ret; 32 33 ret = syscall(__NR_statx, fd, "", AT_EMPTY_PATH, STATX_DIOALIGN, &stx); 34 if (ret < 0) 35 return -1; 36 37 /* 38 * If STATX_DIOALIGN is unsupported, assume no alignment 39 * constraint and let the test proceed. 40 */ 41 if (!(stx.stx_mask & STATX_DIOALIGN) || !stx.stx_dio_offset_align) 42 return 1; 43 44 return stx.stx_dio_offset_align; 45} 46 47static bool check_dio_alignment(unsigned int start_off, 48 unsigned int end_off, unsigned int align) 49{ 50 unsigned int writesize = end_off - start_off; 51 52 /* 53 * The kernel's DIO path checks that file offset, length, and 54 * buffer address are all multiples of dio_offset_align. When 55 * this test case's parameters don't satisfy that, the write 56 * would fail with -EINVAL before exercising the hugetlb unpin 57 * path, so skip. 58 */ 59 if (start_off % align != 0 || writesize % align != 0) { 60 ksft_test_result_skip("DIO align=%u incompatible with offset %u writesize %u\n", 61 align, start_off, writesize); 62 return false; 63 } 64 65 return true; 66} 67 68static void run_dio_using_hugetlb(int fd, unsigned int start_off, 69 unsigned int end_off, unsigned int align) 70{ 71 char *buffer = NULL; 72 char *orig_buffer = NULL; 73 size_t h_pagesize = 0; 74 size_t writesize; 75 int free_hpage_b = 0; 76 int free_hpage_a = 0; 77 const int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB; 78 const int mmap_prot = PROT_READ | PROT_WRITE; 79 80 if (!check_dio_alignment(start_off, end_off, align)) 81 return; 82 83 writesize = end_off - start_off; 84 85 /* Get the default huge page size */ 86 h_pagesize = default_huge_page_size(); 87 if (!h_pagesize) 88 ksft_exit_fail_msg("Unable to determine huge page size\n"); 89 90 /* Reset file position since fd is shared across tests */ 91 if (lseek(fd, 0, SEEK_SET) < 0) 92 ksft_exit_fail_perror("lseek failed\n"); 93 94 /* Get the free huge pages before allocation */ 95 free_hpage_b = get_free_hugepages(); 96 if (free_hpage_b == 0) { 97 close(fd); 98 ksft_exit_skip("No free hugepage, exiting!\n"); 99 } 100 101 /* Allocate a hugetlb page */ 102 orig_buffer = mmap(NULL, h_pagesize, mmap_prot, mmap_flags, -1, 0); 103 if (orig_buffer == MAP_FAILED) { 104 close(fd); 105 ksft_exit_fail_perror("Error mapping memory\n"); 106 } 107 buffer = orig_buffer; 108 buffer += start_off; 109 110 memset(buffer, 'A', writesize); 111 112 /* Write the buffer to the file */ 113 if (write(fd, buffer, writesize) != (writesize)) { 114 munmap(orig_buffer, h_pagesize); 115 close(fd); 116 ksft_exit_fail_perror("Error writing to file\n"); 117 } 118 119 /* unmap the huge page */ 120 munmap(orig_buffer, h_pagesize); 121 122 /* Get the free huge pages after unmap*/ 123 free_hpage_a = get_free_hugepages(); 124 125 ksft_print_msg("No. Free pages before allocation : %d\n", free_hpage_b); 126 ksft_print_msg("No. Free pages after munmap : %d\n", free_hpage_a); 127 128 /* 129 * If the no. of free hugepages before allocation and after unmap does 130 * not match - that means there could still be a page which is pinned. 131 */ 132 ksft_test_result(free_hpage_a == free_hpage_b, 133 "free huge pages from %u-%u\n", start_off, end_off); 134} 135 136int main(void) 137{ 138 int fd, align; 139 const size_t pagesize = psize(); 140 141 ksft_print_header(); 142 143 /* Check if huge pages are free */ 144 if (!get_free_hugepages()) 145 ksft_exit_skip("No free hugepage, exiting\n"); 146 147 fd = open("/tmp", O_TMPFILE | O_RDWR | O_DIRECT, 0664); 148 if (fd < 0) 149 ksft_exit_skip("Unable to allocate file: %s\n", strerror(errno)); 150 151 align = get_dio_alignment(fd); 152 if (align < 0) 153 ksft_exit_skip("Unable to obtain DIO alignment: %s\n", 154 strerror(errno)); 155 ksft_set_plan(4); 156 157 /* start and end is aligned to pagesize */ 158 run_dio_using_hugetlb(fd, 0, (pagesize * 3), align); 159 160 /* start is aligned but end is not aligned */ 161 run_dio_using_hugetlb(fd, 0, (pagesize * 3) - (pagesize / 2), align); 162 163 /* start is unaligned and end is aligned */ 164 run_dio_using_hugetlb(fd, pagesize / 2, (pagesize * 3), align); 165 166 /* both start and end are unaligned */ 167 run_dio_using_hugetlb(fd, pagesize / 2, (pagesize * 3) + (pagesize / 2), align); 168 169 close(fd); 170 171 ksft_finished(); 172}