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
run_dio_using_hugetlb(unsigned int start_off,unsigned int end_off)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
main(void)94 int main(void)
95 {
96 size_t pagesize = 0;
97
98 ksft_print_header();
99 ksft_set_plan(4);
100
101 /* Get base page size */
102 pagesize = psize();
103
104 /* start and end is aligned to pagesize */
105 run_dio_using_hugetlb(0, (pagesize * 3));
106
107 /* start is aligned but end is not aligned */
108 run_dio_using_hugetlb(0, (pagesize * 3) - (pagesize / 2));
109
110 /* start is unaligned and end is aligned */
111 run_dio_using_hugetlb(pagesize / 2, (pagesize * 3));
112
113 /* both start and end are unaligned */
114 run_dio_using_hugetlb(pagesize / 2, (pagesize * 3) + (pagesize / 2));
115
116 ksft_finished();
117 }
118