1 // SPDX-License-Identifier: GPL-2.0-or-later
2 // SPDX-License-Identifier: GPL-2.0-or-later
3
4 /*
5 * Copyright (c) Zilogic Systems Pvt. Ltd., 2018
6 * Email: code@zilogic.com
7 */
8
9 /*
10 * Test: Validating memfd_create() with MFD_HUGETLB flag.
11 *
12 * Test case 1: --WRITE CALL IN HUGEPAGES TEST--
13 * Huge pages are write protected. Any writes to
14 * the file should return EINVAL error.
15 *
16 * Test case 2: --PAGE SIZE OF CREATED FILE TEST--
17 * Default huge page sized pages are created with
18 * MFD_HUGETLB flag. Any attempt to unmap memory-mapped
19 * huge pages with an unmapping length less than
20 * huge page size should return EINVAL error.
21 *
22 * Test case 3: --HUGEPAGE ALLOCATION LIMIT TEST--
23 * Number of huge pages currently available to use should be
24 * atmost total number of allowed huge pages. Memory-mapping
25 * more than allowed huge pages should return ENOMEM error.
26 */
27
28 #define _GNU_SOURCE
29
30 #include "tst_test.h"
31 #include "memfd_create_common.h"
32
33 #include <stdio.h>
34 #include <errno.h>
35
36 #define TOTAL_HP_PATH "/proc/sys/vm/nr_hugepages"
37 #define MEMINFO_PATH "/proc/meminfo"
38 #define FREE_HP "HugePages_Free:\t%ld"
39 #define DEFAULT_HPS "Hugepagesize:\t%ld kB"
40
41 static int hugepages_allocated;
42 static long og_total_pages;
43
check_huge_mmapable(int fd,unsigned long size)44 static void *check_huge_mmapable(int fd, unsigned long size)
45 {
46 void *mem;
47
48 mem = SAFE_MMAP(NULL, size, PROT_WRITE, MAP_PRIVATE, fd, 0);
49
50 memset((char *)mem, 0, 1);
51
52 tst_res(TINFO,
53 "mmap(%p, %lu, %d, %d, %d, %d) succeeded",
54 NULL, size, PROT_WRITE, MAP_PRIVATE, fd, 0);
55
56 return mem;
57 }
58
test_write_protect(int fd)59 static void test_write_protect(int fd)
60 {
61 ssize_t ret;
62 char test_str[] = "LTP";
63
64 ret = write(fd, test_str, strlen(test_str));
65 if (ret < 0) {
66 if (errno != EINVAL) {
67 tst_res(TFAIL | TERRNO,
68 "write(%d, \"%s\", %zu) didn't fail as expected\n",
69 fd, test_str, strlen(test_str));
70 return;
71 }
72 } else {
73 tst_res(TFAIL,
74 "write(%d, \"%s\", %zu) succeeded unexpectedly\n",
75 fd, test_str, strlen(test_str));
76 return;
77 }
78
79 tst_res(TPASS,
80 "write(%d, \"%s\", %zu) failed as expected\n",
81 fd, test_str, strlen(test_str));
82 }
83
test_def_pagesize(int fd)84 static void test_def_pagesize(int fd)
85 {
86 unsigned int i;
87 int unmap_size;
88 int ret;
89 long hps;
90 void *mem;
91
92 SAFE_FILE_LINES_SCANF(MEMINFO_PATH, DEFAULT_HPS, &hps);
93 hps = hps << 10;
94 unmap_size = hps / 4;
95 mem = check_huge_mmapable(fd, hps);
96
97 for (i = unmap_size; i < hps; i += unmap_size) {
98 ret = munmap(mem, i);
99 if (ret == -1) {
100 if (errno == EINVAL) {
101 tst_res(TINFO,
102 "munmap(%p, %dkB) failed as expected",
103 mem, i/1024);
104 } else {
105 tst_res(TFAIL | TERRNO,
106 "munmap(%p, %dkB) failed unexpectedly",
107 mem, i/1024);
108 return;
109 }
110 } else {
111 tst_res(TFAIL,
112 "munmap(%p, %dkB) suceeded unexpectedly\n",
113 mem, i);
114 return;
115 }
116 }
117
118 SAFE_MUNMAP(mem, hps);
119
120 tst_res(TPASS,
121 "munmap() fails for page sizes less than %ldkB\n", hps/1024);
122 }
123
test_max_hugepages(int fd)124 static void test_max_hugepages(int fd)
125 {
126 int new_fd;
127 long hps;
128 long free_pages;
129 void *mem;
130 void *new_mem;
131
132 SAFE_FILE_LINES_SCANF(MEMINFO_PATH, FREE_HP, &free_pages);
133 SAFE_FILE_LINES_SCANF(MEMINFO_PATH, DEFAULT_HPS, &hps);
134 hps = hps << 10;
135 mem = check_huge_mmapable(fd, free_pages * hps);
136
137 new_fd = sys_memfd_create("new_file", MFD_HUGETLB);
138 if (new_fd < 0)
139 tst_brk(TFAIL | TERRNO, "memfd_create() failed");
140 tst_res(TINFO, "memfd_create() succeeded");
141
142 new_mem = mmap(NULL, hps, 0, MAP_PRIVATE, new_fd, 0);
143 if (new_mem == MAP_FAILED) {
144 if (errno == ENOMEM)
145 tst_res(TPASS,
146 "mmap(%p, %lu, %d, %d, %d, %d) failed as expected",
147 NULL, hps, 0, MAP_PRIVATE, new_fd, 0);
148 else
149 tst_res(TFAIL | TERRNO,
150 "mmap(%p, %lu, %d, %d, %d, %d) failed unexpectedly",
151 NULL, hps, 0, MAP_PRIVATE, new_fd, 0);
152 } else {
153 tst_res(TFAIL,
154 "mmap(%p, %lu, %d, %d, %d, %d) succeeded",
155 NULL, hps, 0, MAP_PRIVATE, new_fd, 0);
156 SAFE_MUNMAP(new_mem, hps);
157 }
158
159 SAFE_CLOSE(new_fd);
160
161 SAFE_MUNMAP(mem, free_pages * hps);
162 }
163
164 static const struct tcase {
165 void (*func)(int fd);
166 const char *desc;
167 } tcases[] = {
168 {&test_write_protect, "--TESTING WRITE CALL IN HUGEPAGES--"},
169 {&test_def_pagesize, "--TESTING PAGE SIZE OF CREATED FILE--"},
170 {&test_max_hugepages, "--TESTING HUGEPAGE ALLOCATION LIMIT--"},
171 };
172
memfd_huge_controller(unsigned int n)173 static void memfd_huge_controller(unsigned int n)
174 {
175 int fd;
176 const struct tcase *tc;
177
178 tc = &tcases[n];
179
180 tst_res(TINFO, "%s", tc->desc);
181
182 fd = sys_memfd_create("test_file", MFD_HUGETLB);
183 if (fd < 0)
184 tst_brk(TFAIL | TERRNO, "memfd_create() failed");
185 tst_res(TINFO, "memfd_create() succeeded");
186
187 tc->func(fd);
188
189 SAFE_CLOSE(fd);
190 }
191
setup(void)192 static void setup(void)
193 {
194 char buf[8];
195 int fd;
196 long free_pages;
197 long total_pages;
198
199 if (access(MEMINFO_PATH, F_OK) ||
200 access("/sys/kernel/mm/hugepages", F_OK) ||
201 access(TOTAL_HP_PATH, F_OK))
202 tst_brk(TCONF, "Huge page is not supported");
203
204 SAFE_FILE_LINES_SCANF(MEMINFO_PATH, FREE_HP, &free_pages);
205 if (free_pages > 0)
206 return;
207
208 SAFE_FILE_LINES_SCANF(TOTAL_HP_PATH, "%ld", &og_total_pages);
209 sprintf(buf, "%ld", og_total_pages + 1);
210
211 fd = open(TOTAL_HP_PATH, O_RDWR | O_TRUNC);
212
213 if (write(fd, buf, strlen(buf)) == -1)
214 tst_brk(TCONF | TERRNO,
215 "write() fail: Hugepage allocation failed");
216
217 SAFE_CLOSE(fd);
218
219 SAFE_FILE_LINES_SCANF(TOTAL_HP_PATH, "%ld", &total_pages);
220 if (total_pages != (og_total_pages + 1))
221 tst_brk(TCONF, "Hugepage allocation failed");
222
223 hugepages_allocated = 1;
224 }
225
cleanup(void)226 static void cleanup(void)
227 {
228 char buf[8];
229 int fd;
230 long total_pages;
231
232 if (hugepages_allocated == 0)
233 return;
234
235 sprintf(buf, "%ld", og_total_pages);
236
237 fd = open(TOTAL_HP_PATH, O_RDWR | O_TRUNC);
238
239 if (write(fd, buf, strlen(buf)) == -1)
240 tst_brk(TCONF | TERRNO, "Clean-up failed: write() failed");
241
242 SAFE_CLOSE(fd);
243
244 SAFE_FILE_LINES_SCANF(TOTAL_HP_PATH, "%ld", &total_pages);
245 if (og_total_pages != total_pages)
246 tst_brk(TCONF, "Clean-up failed");
247 }
248
249 static struct tst_test test = {
250 .setup = setup,
251 .test = memfd_huge_controller,
252 .tcnt = ARRAY_SIZE(tcases),
253 .needs_root = 1,
254 .min_kver = "4.14",
255 .cleanup = cleanup,
256 };
257