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