xfs/178: fix mkfs success test
[xfstests-dev.git] / src / dio-invalidate-cache.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2017 Red Hat Inc. All Rights Reserved.
4  */
5
6 /*
7  * Fork N children, each child writes to and reads from its own region of the
8  * same test file, and check if what it reads is what it writes. The test
9  * region is determined by N * blksz. Write and read operation can be either
10  * direct or buffered.
11  */
12
13 #ifndef _GNU_SOURCE
14 #define _GNU_SOURCE
15 #endif
16 #include <sys/file.h>
17 #include <sys/types.h>
18 #include <sys/wait.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <signal.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26
27 #define DEF_BLKSZ 4096
28
29 int verbose = 0;
30
31 static void usage(const char *prog)
32 {
33         fprintf(stderr, "Usage: %s [-Fhptrwv] [-b blksz] [-n nr_child] [-i iterations] [-o offset] <-f filename>\n", prog);
34         fprintf(stderr, "\t-F\tPreallocate all blocks by writing them before test\n");
35         fprintf(stderr, "\t-p\tPreallocate all blocks using fallocate(2) before test\n");
36         fprintf(stderr, "\t-t\tTruncate test file to largest size before test\n");
37         fprintf(stderr, "\t-r\tDo direct read\n");
38         fprintf(stderr, "\t-w\tDo direct write\n");
39         fprintf(stderr, "\t-v\tBe verbose\n");
40         fprintf(stderr, "\t-h\tshow this help message\n");
41         exit(EXIT_FAILURE);
42 }
43
44 static int cmpbuf(char *b1, char *b2, int bsize)
45 {
46         int i;
47
48         for (i = 0; i < bsize; i++) {
49                 if (b1[i] != b2[i]) {
50                         fprintf(stderr, "cmpbuf: offset %d: Expected: 0x%x,"
51                                 " got 0x%x\n", i, b1[i], b2[i]);
52                         return 1;
53                 }
54         }
55         return 0;
56 }
57
58 static void kill_children(pid_t *pids, int nr_child)
59 {
60         int i;
61         pid_t pid;
62
63         for (i = 0; i < nr_child; i++) {
64                 pid = pids[i];
65                 if (pid == 0)
66                         continue;
67                 kill(pid, SIGTERM);
68         }
69 }
70
71 static int wait_children(pid_t *pids, int nr_child)
72 {
73         int i, status, ret = 0;
74         pid_t pid;
75
76         for (i = 0; i < nr_child; i++) {
77                 pid = pids[i];
78                 if (pid == 0)
79                         continue;
80                 waitpid(pid, &status, 0);
81                 ret += WEXITSTATUS(status);
82         }
83         return ret;
84 }
85
86 static void dumpbuf(char *buf, int size, int blksz)
87 {
88         int i;
89
90         printf("dumping buffer content\n");
91         for (i = 0; i < size; i++) {
92                 if (((i % blksz) == 0) || ((i % 64) == 0))
93                         putchar('\n');
94                 printf("%x", buf[i]);
95         }
96         putchar('\n');
97 }
98
99 static int run_test(const char *filename, int n_child, int blksz, off_t offset,
100                     int nr_iter, int flag_rd, int flag_wr)
101 {
102         char *buf_rd;
103         char *buf_wr;
104         off_t seekoff;
105         int fd_rd, fd_wr;
106         int i, ret;
107         long page_size;
108
109         seekoff = offset + blksz * n_child;
110
111         page_size = sysconf(_SC_PAGESIZE);
112         ret = posix_memalign((void **)&buf_rd, (size_t)page_size,
113                 blksz > page_size ? blksz : (size_t)page_size);
114         if (ret) {
115                 fprintf(stderr, "posix_memalign(buf_rd, %d, %d) failed: %d\n",
116                         blksz, blksz, ret);
117                 exit(EXIT_FAILURE);
118         }
119         memset(buf_rd, 0, blksz);
120         ret = posix_memalign((void **)&buf_wr, (size_t)page_size,
121                 blksz > page_size ? blksz : (size_t)page_size);
122         if (ret) {
123                 fprintf(stderr, "posix_memalign(buf_wr, %d, %d) failed: %d\n",
124                         blksz, blksz, ret);
125                 exit(EXIT_FAILURE);
126         }
127         memset(buf_wr, 0, blksz);
128
129         fd_rd = open(filename, flag_rd);
130         if (fd_rd < 0) {
131                 perror("open readonly for read");
132                 exit(EXIT_FAILURE);
133         }
134
135         fd_wr = open(filename, flag_wr);
136         if (fd_wr < 0) {
137                 perror("open writeonly for direct write");
138                 exit(EXIT_FAILURE);
139         }
140
141 #define log(format, ...)                        \
142         if (verbose) {                          \
143                 printf("[%d:%d] ", n_child, i); \
144                 printf(format, __VA_ARGS__);    \
145         }
146
147
148         /* seek, write, read and verify */
149         for (i = 0; i < nr_iter; i++) {
150                 memset(buf_wr, i + 1, blksz);
151                 log("pwrite(fd_wr, %p, %d, %lld)\n", buf_wr, blksz,
152                     (long long) seekoff);
153                 if (pwrite(fd_wr, buf_wr, blksz, seekoff) != blksz) {
154                         perror("direct write");
155                         exit(EXIT_FAILURE);
156                 }
157
158                 /* make sure buffer write hits disk before direct read */
159                 if (!(flag_wr & O_DIRECT)) {
160                         if (fsync(fd_wr) < 0) {
161                                 perror("fsync(fd_wr)");
162                                 exit(EXIT_FAILURE);
163                         }
164                 }
165
166                 log("pread(fd_rd, %p, %d, %lld)\n", buf_rd, blksz,
167                     (long long) seekoff);
168                 if (pread(fd_rd, buf_rd, blksz, seekoff) != blksz) {
169                         perror("buffer read");
170                         exit(EXIT_FAILURE);
171                 }
172                 if (cmpbuf(buf_wr, buf_rd, blksz) != 0) {
173                         fprintf(stderr, "[%d:%d] FAIL - comparison failed, "
174                                 "offset %d\n", n_child, i, (int)seekoff);
175                         if (verbose)
176                                 dumpbuf(buf_rd, blksz, blksz);
177                         exit(EXIT_FAILURE);
178                 }
179         }
180         exit(EXIT_SUCCESS);
181 }
182
183 int main(int argc, char *argv[])
184 {
185         int nr_iter = 1;
186         int nr_child = 1;
187         int blksz = DEF_BLKSZ;
188         int fd, i, ret = 0;
189         int flag_rd = O_RDONLY;
190         int flag_wr = O_WRONLY;
191         int do_trunc = 0;
192         int pre_fill = 0;
193         int pre_alloc = 0;
194         pid_t pid;
195         pid_t *pids;
196         off_t offset = 0;
197         char *filename = NULL;
198
199         while ((i = getopt(argc, argv, "b:i:n:f:Fpo:tvrw")) != -1) {
200                 switch (i) {
201                 case 'b':
202                         if ((blksz = atoi(optarg)) <= 0) {
203                                 fprintf(stderr, "blksz must be > 0\n");
204                                 exit(EXIT_FAILURE);
205                         }
206                         if (blksz % 512 != 0) {
207                                 fprintf(stderr, "blksz must be multiple of 512\n");
208                                 exit(EXIT_FAILURE);
209                         }
210                         break;
211                 case 'i':
212                         if ((nr_iter = atoi(optarg)) <= 0) {
213                                 fprintf(stderr, "iterations must be > 0\n");
214                                 exit(EXIT_FAILURE);
215                         }
216                         break;
217                 case 'n':
218                         if ((nr_child = atoi(optarg)) <= 0) {
219                                 fprintf(stderr, "no of children must be > 0\n");
220                                 exit(EXIT_FAILURE);
221                         }
222                         break;
223                 case 'f':
224                         filename = optarg;
225                         break;
226                 case 'F':
227                         pre_fill = 1;
228                         break;
229                 case 'p':
230                         pre_alloc = 1;
231                         break;
232                 case 'r':
233                         flag_rd |= O_DIRECT;
234                         break;
235                 case 'w':
236                         flag_wr |= O_DIRECT;
237                         break;
238                 case 't':
239                         do_trunc = 1;
240                         break;
241                 case 'o':
242                         if ((offset = atol(optarg)) < 0) {
243                                 fprintf(stderr, "offset must be >= 0\n");
244                                 exit(EXIT_FAILURE);
245                         }
246                         break;
247                 case 'v':
248                         verbose = 1;
249                         break;
250                 case 'h':       /* fall through */
251                 default:
252                         usage(argv[0]);
253                 }
254         }
255
256         if (filename == NULL)
257                 usage(argv[0]);
258         if (pre_fill && pre_alloc) {
259                 fprintf(stderr, "Error: -F and -p are both specified\n");
260                 exit(EXIT_FAILURE);
261         }
262
263         pids = malloc(nr_child * sizeof(pid_t));
264         if (!pids) {
265                 fprintf(stderr, "failed to malloc memory for pids\n");
266                 exit(EXIT_FAILURE);
267         }
268         memset(pids, 0, nr_child * sizeof(pid_t));
269
270         /* create & truncate testfile first */
271         fd = open(filename, O_CREAT | O_TRUNC | O_RDWR, 0600);
272         if (fd < 0) {
273                 perror("create & truncate testfile");
274                 free(pids);
275                 exit(EXIT_FAILURE);
276         }
277         if (do_trunc && (ftruncate(fd, blksz * nr_child) < 0)) {
278                 perror("ftruncate failed");
279                 free(pids);
280                 exit(EXIT_FAILURE);
281         }
282         if (pre_fill) {
283                 char *buf;
284                 buf = malloc(blksz * nr_child);
285                 memset(buf, 's', blksz * nr_child);
286                 write(fd, buf, blksz * nr_child);
287                 free(buf);
288         }
289         if (pre_alloc) {
290                 fallocate(fd, 0, 0, blksz * nr_child);
291         }
292         fsync(fd);
293         close(fd);
294
295         /* fork workers */
296         for (i = 0; i < nr_child; i++) {
297                 pid = fork();
298                 if (pid < 0) {
299                         perror("fork");
300                         kill_children(pids, nr_child);
301                         free(pids);
302                         exit(EXIT_FAILURE);
303                 } else if (pid == 0) {
304                         /* never returns */
305                         run_test(filename, i, blksz, offset, nr_iter,
306                                  flag_rd, flag_wr);
307                 } else {
308                         pids[i] = pid;
309                 }
310         }
311
312         ret = wait_children(pids, nr_child);
313         free(pids);
314         exit(ret);
315 }