• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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