1 // SPDX-License-Identifier: GPL-2.0-or-later
2
3 /*
4 * Copyright (c) Zilogic Systems Pvt. Ltd., 2018
5 * Email: [email protected]
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
check_huge_mmapable(int fd,unsigned long size)35 static void *check_huge_mmapable(int fd, unsigned long size)
36 {
37 void *mem;
38
39 mem = SAFE_MMAP(NULL, size, PROT_WRITE, MAP_PRIVATE, fd, 0);
40
41 memset((char *)mem, 0, 1);
42
43 tst_res(TINFO,
44 "mmap(%p, %lu, %d, %d, %d, %d) succeeded",
45 NULL, size, PROT_WRITE, MAP_PRIVATE, fd, 0);
46
47 return mem;
48 }
49
test_write_protect(int fd)50 static void test_write_protect(int fd)
51 {
52 ssize_t ret;
53 char test_str[] = "LTP";
54
55 ret = write(fd, test_str, strlen(test_str));
56 if (ret < 0) {
57 if (errno != EINVAL) {
58 tst_res(TFAIL | TERRNO,
59 "write(%d, \"%s\", %zu) didn't fail as expected\n",
60 fd, test_str, strlen(test_str));
61 return;
62 }
63 } else {
64 tst_res(TFAIL,
65 "write(%d, \"%s\", %zu) succeeded unexpectedly\n",
66 fd, test_str, strlen(test_str));
67 return;
68 }
69
70 tst_res(TPASS,
71 "write(%d, \"%s\", %zu) failed as expected\n",
72 fd, test_str, strlen(test_str));
73 }
74
test_def_pagesize(int fd)75 static void test_def_pagesize(int fd)
76 {
77 unsigned int i;
78 int unmap_size;
79 int ret;
80 long hps;
81 void *mem;
82
83 hps = SAFE_READ_MEMINFO("Hugepagesize:");
84 hps = hps << 10;
85 unmap_size = hps / 4;
86 mem = check_huge_mmapable(fd, hps);
87
88 for (i = unmap_size; i < hps; i += unmap_size) {
89 ret = munmap(mem, i);
90 if (ret == -1) {
91 if (errno == EINVAL) {
92 tst_res(TINFO,
93 "munmap(%p, %dkB) failed as expected",
94 mem, i/1024);
95 } else {
96 tst_res(TFAIL | TERRNO,
97 "munmap(%p, %dkB) failed unexpectedly",
98 mem, i/1024);
99 return;
100 }
101 } else {
102 tst_res(TFAIL,
103 "munmap(%p, %dkB) suceeded unexpectedly\n",
104 mem, i/1024);
105 return;
106 }
107 }
108
109 SAFE_MUNMAP(mem, hps);
110
111 tst_res(TPASS,
112 "munmap() fails for page sizes less than %ldkB\n", hps/1024);
113 }
114
test_max_hugepages(int fd)115 static void test_max_hugepages(int fd)
116 {
117 int new_fd;
118 long hps;
119 long free_pages;
120 void *mem;
121 void *new_mem;
122
123 free_pages = SAFE_READ_MEMINFO("HugePages_Free:");
124 hps = SAFE_READ_MEMINFO("Hugepagesize:");
125 hps = hps << 10;
126 mem = check_huge_mmapable(fd, free_pages * hps);
127
128 new_fd = sys_memfd_create("new_file", MFD_HUGETLB);
129 if (new_fd < 0)
130 tst_brk(TFAIL | TERRNO, "memfd_create() failed");
131 tst_res(TINFO, "memfd_create() succeeded");
132
133 new_mem = mmap(NULL, hps, 0, MAP_PRIVATE, new_fd, 0);
134 if (new_mem == MAP_FAILED) {
135 if (errno == ENOMEM)
136 tst_res(TPASS,
137 "mmap(%p, %lu, %d, %d, %d, %d) failed as expected",
138 NULL, hps, 0, MAP_PRIVATE, new_fd, 0);
139 else
140 tst_res(TFAIL | TERRNO,
141 "mmap(%p, %lu, %d, %d, %d, %d) failed unexpectedly",
142 NULL, hps, 0, MAP_PRIVATE, new_fd, 0);
143 } else {
144 tst_res(TFAIL,
145 "mmap(%p, %lu, %d, %d, %d, %d) succeeded",
146 NULL, hps, 0, MAP_PRIVATE, new_fd, 0);
147 SAFE_MUNMAP(new_mem, hps);
148 }
149
150 SAFE_CLOSE(new_fd);
151
152 SAFE_MUNMAP(mem, free_pages * hps);
153 }
154
155 static const struct tcase {
156 void (*func)(int fd);
157 const char *desc;
158 } tcases[] = {
159 {&test_write_protect, "--TESTING WRITE CALL IN HUGEPAGES--"},
160 {&test_def_pagesize, "--TESTING PAGE SIZE OF CREATED FILE--"},
161 {&test_max_hugepages, "--TESTING HUGEPAGE ALLOCATION LIMIT--"},
162 };
163
memfd_huge_controller(unsigned int n)164 static void memfd_huge_controller(unsigned int n)
165 {
166 int fd;
167 const struct tcase *tc;
168
169 tc = &tcases[n];
170
171 tst_res(TINFO, "%s", tc->desc);
172
173 fd = sys_memfd_create("test_file", MFD_HUGETLB);
174 if (fd < 0)
175 tst_brk(TFAIL | TERRNO, "memfd_create() failed");
176 tst_res(TINFO, "memfd_create() succeeded");
177
178 tc->func(fd);
179
180 SAFE_CLOSE(fd);
181 }
182
183 static struct tst_test test = {
184 .test = memfd_huge_controller,
185 .tcnt = ARRAY_SIZE(tcases),
186 .needs_root = 1,
187 .min_kver = "4.14",
188 .hugepages = {1, TST_NEEDS},
189 };
190