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