~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/mm/hugetlb-read-hwpoison.c

Version: ~ [ linux-6.11-rc3 ] ~ [ linux-6.10.4 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.45 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.104 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.164 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.223 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.281 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.319 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0
  2 
  3 #define _GNU_SOURCE
  4 #include <stdlib.h>
  5 #include <stdio.h>
  6 #include <string.h>
  7 
  8 #include <linux/magic.h>
  9 #include <sys/mman.h>
 10 #include <sys/statfs.h>
 11 #include <errno.h>
 12 #include <stdbool.h>
 13 
 14 #include "../kselftest.h"
 15 
 16 #define PREFIX " ... "
 17 #define ERROR_PREFIX " !!! "
 18 
 19 #define MAX_WRITE_READ_CHUNK_SIZE (getpagesize() * 16)
 20 #define MAX(a, b) (((a) > (b)) ? (a) : (b))
 21 
 22 enum test_status {
 23         TEST_PASSED = 0,
 24         TEST_FAILED = 1,
 25         TEST_SKIPPED = 2,
 26 };
 27 
 28 static char *status_to_str(enum test_status status)
 29 {
 30         switch (status) {
 31         case TEST_PASSED:
 32                 return "TEST_PASSED";
 33         case TEST_FAILED:
 34                 return "TEST_FAILED";
 35         case TEST_SKIPPED:
 36                 return "TEST_SKIPPED";
 37         default:
 38                 return "TEST_???";
 39         }
 40 }
 41 
 42 static int setup_filemap(char *filemap, size_t len, size_t wr_chunk_size)
 43 {
 44         char iter = 0;
 45 
 46         for (size_t offset = 0; offset < len;
 47              offset += wr_chunk_size) {
 48                 iter++;
 49                 memset(filemap + offset, iter, wr_chunk_size);
 50         }
 51 
 52         return 0;
 53 }
 54 
 55 static bool verify_chunk(char *buf, size_t len, char val)
 56 {
 57         size_t i;
 58 
 59         for (i = 0; i < len; ++i) {
 60                 if (buf[i] != val) {
 61                         printf(PREFIX ERROR_PREFIX "check fail: buf[%lu] = %u != %u\n",
 62                                 i, buf[i], val);
 63                         return false;
 64                 }
 65         }
 66 
 67         return true;
 68 }
 69 
 70 static bool seek_read_hugepage_filemap(int fd, size_t len, size_t wr_chunk_size,
 71                                        off_t offset, size_t expected)
 72 {
 73         char buf[MAX_WRITE_READ_CHUNK_SIZE];
 74         ssize_t ret_count = 0;
 75         ssize_t total_ret_count = 0;
 76         char val = offset / wr_chunk_size + offset % wr_chunk_size;
 77 
 78         printf(PREFIX PREFIX "init val=%u with offset=0x%lx\n", val, offset);
 79         printf(PREFIX PREFIX "expect to read 0x%lx bytes of data in total\n",
 80                expected);
 81         if (lseek(fd, offset, SEEK_SET) < 0) {
 82                 perror(PREFIX ERROR_PREFIX "seek failed");
 83                 return false;
 84         }
 85 
 86         while (offset + total_ret_count < len) {
 87                 ret_count = read(fd, buf, wr_chunk_size);
 88                 if (ret_count == 0) {
 89                         printf(PREFIX PREFIX "read reach end of the file\n");
 90                         break;
 91                 } else if (ret_count < 0) {
 92                         perror(PREFIX ERROR_PREFIX "read failed");
 93                         break;
 94                 }
 95                 ++val;
 96                 if (!verify_chunk(buf, ret_count, val))
 97                         return false;
 98 
 99                 total_ret_count += ret_count;
100         }
101         printf(PREFIX PREFIX "actually read 0x%lx bytes of data in total\n",
102                total_ret_count);
103 
104         return total_ret_count == expected;
105 }
106 
107 static bool read_hugepage_filemap(int fd, size_t len,
108                                   size_t wr_chunk_size, size_t expected)
109 {
110         char buf[MAX_WRITE_READ_CHUNK_SIZE];
111         ssize_t ret_count = 0;
112         ssize_t total_ret_count = 0;
113         char val = 0;
114 
115         printf(PREFIX PREFIX "expect to read 0x%lx bytes of data in total\n",
116                expected);
117         while (total_ret_count < len) {
118                 ret_count = read(fd, buf, wr_chunk_size);
119                 if (ret_count == 0) {
120                         printf(PREFIX PREFIX "read reach end of the file\n");
121                         break;
122                 } else if (ret_count < 0) {
123                         perror(PREFIX ERROR_PREFIX "read failed");
124                         break;
125                 }
126                 ++val;
127                 if (!verify_chunk(buf, ret_count, val))
128                         return false;
129 
130                 total_ret_count += ret_count;
131         }
132         printf(PREFIX PREFIX "actually read 0x%lx bytes of data in total\n",
133                total_ret_count);
134 
135         return total_ret_count == expected;
136 }
137 
138 static enum test_status
139 test_hugetlb_read(int fd, size_t len, size_t wr_chunk_size)
140 {
141         enum test_status status = TEST_SKIPPED;
142         char *filemap = NULL;
143 
144         if (ftruncate(fd, len) < 0) {
145                 perror(PREFIX ERROR_PREFIX "ftruncate failed");
146                 return status;
147         }
148 
149         filemap = mmap(NULL, len, PROT_READ | PROT_WRITE,
150                        MAP_SHARED | MAP_POPULATE, fd, 0);
151         if (filemap == MAP_FAILED) {
152                 perror(PREFIX ERROR_PREFIX "mmap for primary mapping failed");
153                 goto done;
154         }
155 
156         setup_filemap(filemap, len, wr_chunk_size);
157         status = TEST_FAILED;
158 
159         if (read_hugepage_filemap(fd, len, wr_chunk_size, len))
160                 status = TEST_PASSED;
161 
162         munmap(filemap, len);
163 done:
164         if (ftruncate(fd, 0) < 0) {
165                 perror(PREFIX ERROR_PREFIX "ftruncate back to 0 failed");
166                 status = TEST_FAILED;
167         }
168 
169         return status;
170 }
171 
172 static enum test_status
173 test_hugetlb_read_hwpoison(int fd, size_t len, size_t wr_chunk_size,
174                            bool skip_hwpoison_page)
175 {
176         enum test_status status = TEST_SKIPPED;
177         char *filemap = NULL;
178         char *hwp_addr = NULL;
179         const unsigned long pagesize = getpagesize();
180 
181         if (ftruncate(fd, len) < 0) {
182                 perror(PREFIX ERROR_PREFIX "ftruncate failed");
183                 return status;
184         }
185 
186         filemap = mmap(NULL, len, PROT_READ | PROT_WRITE,
187                        MAP_SHARED | MAP_POPULATE, fd, 0);
188         if (filemap == MAP_FAILED) {
189                 perror(PREFIX ERROR_PREFIX "mmap for primary mapping failed");
190                 goto done;
191         }
192 
193         setup_filemap(filemap, len, wr_chunk_size);
194         status = TEST_FAILED;
195 
196         /*
197          * Poisoned hugetlb page layout (assume hugepagesize=2MB):
198          * |<---------------------- 1MB ---------------------->|
199          * |<---- healthy page ---->|<---- HWPOISON page ----->|
200          * |<------------------- (1MB - 8KB) ----------------->|
201          */
202         hwp_addr = filemap + len / 2 + pagesize;
203         if (madvise(hwp_addr, pagesize, MADV_HWPOISON) < 0) {
204                 perror(PREFIX ERROR_PREFIX "MADV_HWPOISON failed");
205                 goto unmap;
206         }
207 
208         if (!skip_hwpoison_page) {
209                 /*
210                  * Userspace should be able to read (1MB + 1 page) from
211                  * the beginning of the HWPOISONed hugepage.
212                  */
213                 if (read_hugepage_filemap(fd, len, wr_chunk_size,
214                                           len / 2 + pagesize))
215                         status = TEST_PASSED;
216         } else {
217                 /*
218                  * Userspace should be able to read (1MB - 2 pages) from
219                  * HWPOISONed hugepage.
220                  */
221                 if (seek_read_hugepage_filemap(fd, len, wr_chunk_size,
222                                                len / 2 + MAX(2 * pagesize, wr_chunk_size),
223                                                len / 2 - MAX(2 * pagesize, wr_chunk_size)))
224                         status = TEST_PASSED;
225         }
226 
227 unmap:
228         munmap(filemap, len);
229 done:
230         if (ftruncate(fd, 0) < 0) {
231                 perror(PREFIX ERROR_PREFIX "ftruncate back to 0 failed");
232                 status = TEST_FAILED;
233         }
234 
235         return status;
236 }
237 
238 static int create_hugetlbfs_file(struct statfs *file_stat)
239 {
240         int fd;
241 
242         fd = memfd_create("hugetlb_tmp", MFD_HUGETLB);
243         if (fd < 0) {
244                 perror(PREFIX ERROR_PREFIX "could not open hugetlbfs file");
245                 return -1;
246         }
247 
248         memset(file_stat, 0, sizeof(*file_stat));
249         if (fstatfs(fd, file_stat)) {
250                 perror(PREFIX ERROR_PREFIX "fstatfs failed");
251                 goto close;
252         }
253         if (file_stat->f_type != HUGETLBFS_MAGIC) {
254                 printf(PREFIX ERROR_PREFIX "not hugetlbfs file\n");
255                 goto close;
256         }
257 
258         return fd;
259 close:
260         close(fd);
261         return -1;
262 }
263 
264 int main(void)
265 {
266         int fd;
267         struct statfs file_stat;
268         enum test_status status;
269         /* Test read() in different granularity. */
270         size_t wr_chunk_sizes[] = {
271                 getpagesize() / 2, getpagesize(),
272                 getpagesize() * 2, getpagesize() * 4
273         };
274         size_t i;
275 
276         for (i = 0; i < ARRAY_SIZE(wr_chunk_sizes); ++i) {
277                 printf("Write/read chunk size=0x%lx\n",
278                        wr_chunk_sizes[i]);
279 
280                 fd = create_hugetlbfs_file(&file_stat);
281                 if (fd < 0)
282                         goto create_failure;
283                 printf(PREFIX "HugeTLB read regression test...\n");
284                 status = test_hugetlb_read(fd, file_stat.f_bsize,
285                                            wr_chunk_sizes[i]);
286                 printf(PREFIX "HugeTLB read regression test...%s\n",
287                        status_to_str(status));
288                 close(fd);
289                 if (status == TEST_FAILED)
290                         return -1;
291 
292                 fd = create_hugetlbfs_file(&file_stat);
293                 if (fd < 0)
294                         goto create_failure;
295                 printf(PREFIX "HugeTLB read HWPOISON test...\n");
296                 status = test_hugetlb_read_hwpoison(fd, file_stat.f_bsize,
297                                                     wr_chunk_sizes[i], false);
298                 printf(PREFIX "HugeTLB read HWPOISON test...%s\n",
299                        status_to_str(status));
300                 close(fd);
301                 if (status == TEST_FAILED)
302                         return -1;
303 
304                 fd = create_hugetlbfs_file(&file_stat);
305                 if (fd < 0)
306                         goto create_failure;
307                 printf(PREFIX "HugeTLB seek then read HWPOISON test...\n");
308                 status = test_hugetlb_read_hwpoison(fd, file_stat.f_bsize,
309                                                     wr_chunk_sizes[i], true);
310                 printf(PREFIX "HugeTLB seek then read HWPOISON test...%s\n",
311                        status_to_str(status));
312                 close(fd);
313                 if (status == TEST_FAILED)
314                         return -1;
315         }
316 
317         return 0;
318 
319 create_failure:
320         printf(ERROR_PREFIX "Abort test: failed to create hugetlbfs file\n");
321         return -1;
322 }
323 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php