xfstests 240: test non-aligned AIO hole-filling
[xfstests-dev.git] / src / aio-dio-regress / aiodio_sparse2.c
1 /* gcc -g -Wall -O2 aiodio_sparse.c -o aiodio_sparse -laio */
2
3 /*
4  * From http://developer.osdl.org/daniel/AIO/TESTS/aiodio_sparse.c
5  * With patch from https://bugzilla.redhat.com/attachment.cgi?id=142124
6  * (Bug https://bugzilla.redhat.com/show_bug.cgi?id=217098)
7  */
8
9 #ifndef _GNU_SOURCE
10 #define _GNU_SOURCE
11 #endif
12 #include <sys/types.h>
13 #include <errno.h>
14 #include <signal.h>
15 #include <fcntl.h>
16 #include <stdio.h>
17 #include <unistd.h>
18 #include <stdlib.h>
19 #include <limits.h>
20 #include <sys/mman.h>
21 #include <sys/wait.h>
22
23 #include <libaio.h>
24
25 int debug;
26
27 /*
28  * aiodio_sparse - issue async O_DIRECT writes to holes is a file while
29  *      concurrently reading the file and checking that the read never reads
30  *      uninitailized data.
31  */
32
33 unsigned char *check_zero(unsigned char *buf, int size)
34 {
35         unsigned char *p;
36
37         p = buf;
38
39         while (size > 0) {
40                 if (*buf != 1) {
41                         fprintf(stderr, "non one buffer at buf[%ld] => 0x%02x,%02x,%02x,%02x\n",
42                                 (long)(buf - p), (unsigned int)buf[0],
43                                 size > 1 ? (unsigned int)buf[1] : 0,
44                                 size > 2 ? (unsigned int)buf[2] : 0,
45                                 size > 3 ? (unsigned int)buf[3] : 0);
46                         if (debug)
47                                 fprintf(stderr, "buf %p, p %p\n", buf, p);
48                         return buf;
49                 }
50                 buf++;
51                 size--;
52         }
53         return 0;       /* all zeros */
54 }
55
56 volatile int got_signal;
57
58 void
59 sig_term_func(int i, siginfo_t *si, void *p)
60 {
61         if (debug)
62                 fprintf(stderr, "sig(%d, %p, %p)\n", i, si, p);
63         got_signal++;
64 }
65
66 /*
67  * do async DIO writes to a sparse file
68  */
69 void aiodio_sparse(char *filename, int align, int writesize, int startoffset, int filesize,
70                         int num_aio, int step, int sparse, int direct, int keep)
71 {
72         int fd;
73         void *bufptr;
74         int i;
75         int w;
76         static struct sigaction s;
77         struct iocb **iocbs;
78         off_t offset;
79         io_context_t myctx;
80         struct io_event event;
81         int aio_inflight;
82
83         s.sa_sigaction = sig_term_func;
84         s.sa_flags = SA_SIGINFO;
85         sigaction(SIGTERM, &s, 0);
86
87         if ((num_aio * step) > filesize) {
88                 num_aio = filesize / step;
89         }
90         memset(&myctx, 0, sizeof(myctx));
91         io_queue_init(num_aio, &myctx);
92
93         iocbs = (struct iocb **)calloc(num_aio, sizeof(struct iocb *));
94         for (i = 0; i < num_aio; i++) {
95                 if ((iocbs[i] = (struct iocb *)calloc(1, sizeof(struct iocb))) == 0) {
96                         perror("cannot malloc iocb");
97                         return;
98                 }
99         }
100
101         fd = open(filename, direct|O_WRONLY|O_CREAT, 0666);
102
103         if (fd < 0) {
104                 perror("cannot create file");
105                 return;
106         }
107
108         if (sparse)
109                 ftruncate(fd, filesize);
110
111         /*
112          * allocate the iocbs array and iocbs with buffers
113          */
114         offset = startoffset;
115         for (i = 0; i < num_aio; i++) {
116                 void *bufptr;
117
118                 if (posix_memalign(&bufptr, align, writesize)) {
119                         perror("cannot malloc aligned memory");
120                         close(fd);
121                         unlink(filename);
122                         return;
123                 }
124                 memset(bufptr, 1, writesize);
125                 io_prep_pwrite(iocbs[i], fd, bufptr, writesize, offset);
126                 offset += step;
127         }
128
129         /*
130          * start the 1st num_aio write requests
131          */
132         if ((w = io_submit(myctx, num_aio, iocbs)) < 0) {
133                 perror("io_submit failed");
134                 close(fd);
135                 unlink(filename);
136                 return;
137         }
138         if (debug)
139                 fprintf(stderr, "io_submit() return %d\n", w);
140
141         /*
142          * As AIO requests finish, keep issuing more AIO until done.
143          */
144         aio_inflight = num_aio;
145         if (debug)
146                 fprintf(stderr, "aiodio_sparse: %d i/o in flight\n", aio_inflight);
147         while (offset < filesize)  {
148                 int n;
149                 struct iocb *iocbp;
150
151                 if (debug)
152                         fprintf(stderr, "aiodio_sparse: offset %lld filesize %d inflight %d\n",
153                                 (long long)offset, filesize, aio_inflight);
154
155                 if ((n = io_getevents(myctx, 1, 1, &event, 0)) != 1) {
156                         if (-n != EINTR)
157                                 fprintf(stderr, "io_getevents() returned %d\n", n);
158                         break;
159                 }
160                 if (debug)
161                         fprintf(stderr, "aiodio_sparse: io_getevent() returned %d\n", n);
162                 aio_inflight--;
163                 if (got_signal)
164                         break;          /* told to stop */
165                 /*
166                  * check if write succeeded.
167                  */
168                 iocbp = event.obj;
169                 if (event.res2 != 0 || event.res != iocbp->u.c.nbytes) {
170                         fprintf(stderr,
171                                 "AIO write offset %lld expected %ld got %ld\n",
172                                 iocbp->u.c.offset, iocbp->u.c.nbytes,
173                                 event.res);
174                         break;
175                 }
176                 if (debug)
177                         fprintf(stderr, "aiodio_sparse: io_getevent() res %ld res2 %ld\n",
178                                 event.res, event.res2);
179
180                 /* start next write */
181                 io_prep_pwrite(iocbp, fd, iocbp->u.c.buf, writesize, offset);
182                 offset += step;
183                 if ((w = io_submit(myctx, 1, &iocbp)) < 0) {
184                         fprintf(stderr, "io_submit failed at offset %lld\n",
185                                 (long long)offset);
186                         perror("");
187                         break;
188                 }
189                 if (debug)
190                         fprintf(stderr, "io_submit() return %d\n", w);
191                 aio_inflight++;
192         }
193
194         /*
195          * wait for AIO requests in flight.
196          */
197         while (aio_inflight > 0) {
198                 int n;
199                 struct iocb *iocbp;
200
201                 if ((n = io_getevents(myctx, 1, 1, &event, 0)) != 1) {
202                         perror("io_getevents failed");
203                         break;
204                 }
205                 aio_inflight--;
206                 /*
207                  * check if write succeeded.
208                  */
209                 iocbp = event.obj;
210                 if (event.res2 != 0 || event.res != iocbp->u.c.nbytes) {
211                         fprintf(stderr,
212                                 "AIO write offset %lld expected %ld got %ld\n",
213                                 iocbp->u.c.offset, iocbp->u.c.nbytes,
214                                 event.res);
215                 }
216         }
217         if (debug)
218                 fprintf(stderr, "AIO DIO write done\n");
219         close(fd);
220         if ((fd = open(filename, O_RDONLY)) < 0)
221                 exit(1);
222
223         bufptr = malloc(writesize);
224         for (offset = startoffset; offset < filesize; offset += step)  {
225                 unsigned char *badbuf;
226
227                 if (debug)
228                         fprintf(stderr, "seek to %ld and read %d\n", offset, writesize);
229                 lseek(fd, offset, SEEK_SET);
230                 if (read(fd, bufptr, writesize) < writesize) {
231                         fprintf(stderr, "short read() at offset %lld\n",
232                                 (long long) offset);
233                         exit(11);
234                 }
235                 if ((badbuf = check_zero(bufptr, writesize))) {
236                         fprintf(stderr, "non-one read at offset %lld\n",
237                                 (long long)(offset + badbuf - (unsigned char*)bufptr));
238                         fprintf(stderr, "*** WARNING *** %s has not been unlinked; if you don't rm it manually first, it may influence the next run\n", filename);
239                         exit(10);
240                 }
241         }
242         close(fd);
243         if (!keep)
244                 unlink(filename);
245         else
246                 fprintf(stderr, "*** WARNING *** You requested %s not to be unlinked; if you don't rm it manually first, it may influence the next run\n", filename);
247 }
248
249 void dirty_freeblocks(char *filename, int size)
250 {
251         int fd;
252         void *p;
253         int pg;
254         char filename2[PATH_MAX];
255
256         pg = getpagesize();
257         size = ((size + pg - 1) / pg) * pg;
258         sprintf(filename2, "%s.xx.%d", filename, getpid());
259         fd = open(filename2, O_CREAT|O_RDWR, 0666);
260         if (fd < 0) {
261                 perror("cannot open file");
262                 exit(2);
263         }
264         ftruncate(fd, size);
265         p = mmap(0, size, PROT_WRITE|PROT_READ, MAP_SHARED|MAP_FILE, fd, 0);
266         if (p == MAP_FAILED) {
267                 perror("cannot mmap");
268                 exit(2);
269         }
270         memset(p, 0xaa, size);
271         msync(p, size, MS_SYNC);
272         munmap(p, size);
273         close(fd);
274         unlink(filename2);
275 }
276
277 void usage()
278 {
279         fprintf(stderr, "usage: dio_sparse [-n step] [-s filesize]"
280                 " [-w writesize] [-o startoffset] [-r readsize] filename\n");
281         exit(1);
282 }
283
284 /*
285  * Scale value by kilo, mega, or giga.
286  */
287 long long scale_by_kmg(long long value, char scale)
288 {
289         switch (scale) {
290         case 'g':
291         case 'G':
292                 value *= 1024;
293         case 'm':
294         case 'M':
295                 value *= 1024;
296         case 'k':
297         case 'K':
298                 value *= 1024;
299                 break;
300         case '\0':
301                 break;
302         default:
303                 usage();
304                 break;
305         }
306         return value;
307 }
308
309 /*
310  *      usage:
311  * aiodio_sparse [-r readsize] [-w writesize] [-o startoffset] [-n step] [-a align] [-i num_aio] filename
312  */
313
314 int main(int argc, char **argv)
315 {
316         char filename[PATH_MAX];
317         long alignment = 512;
318         int readsize = 65536;
319         int writesize = 65536;
320         int startoffset = 0;
321         int filesize = 100*1024*1024;
322         int num_aio = 16;
323         int step = 5*1024*1024;
324         int c, direct = O_DIRECT, keep = 0, sparse = 1;
325         extern char *optarg;
326         extern int optind, optopt, opterr;
327
328         while ((c = getopt(argc, argv, "dr:w:n:o:a:s:i:DkS")) != -1) {
329                 char *endp;
330                 switch (c) {
331                 case 'D':
332                         direct = 0;
333                         break;
334                 case 'k':
335                         keep = 1;
336                         break;
337                 case 'S':
338                         sparse = 0;
339                         break;
340                 case 'd':
341                         debug++;
342                         break;
343                 case 'i':
344                         num_aio = atoi(optarg);
345                         break;
346                 case 'a':
347                         alignment = strtol(optarg, &endp, 0);
348                         alignment = (int)scale_by_kmg((long long)alignment,
349                                                         *endp);
350                         break;
351                 case 'r':
352                         readsize = strtol(optarg, &endp, 0);
353                         readsize = (int)scale_by_kmg((long long)readsize, *endp);
354                         break;
355                 case 'w':
356                         writesize = strtol(optarg, &endp, 0);
357                         writesize = (int)scale_by_kmg((long long)writesize, *endp);
358                         break;
359                 case 's':
360                         filesize = strtol(optarg, &endp, 0);
361                         filesize = (int)scale_by_kmg((long long)filesize, *endp);
362                         break;
363                 case 'n':
364                         step = strtol(optarg, &endp, 0);
365                         step = (int)scale_by_kmg((long long)step, *endp);
366                         break;
367                 case 'o':
368                         startoffset = strtol(optarg, &endp, 0);
369                         startoffset = (int)scale_by_kmg((long long)startoffset, *endp);
370                         break;
371                 case '?':
372                         usage();
373                         break;
374                 }
375         }
376
377         strncpy(filename, argv[argc-1], PATH_MAX);
378
379         /*
380          * Create some dirty free blocks by allocating, writing, syncing,
381          * and then unlinking and freeing.
382          */
383         dirty_freeblocks(filename, filesize);
384
385         /*
386          * Parent write to a hole in a file using async direct i/o
387          */
388
389         aiodio_sparse(filename, alignment, writesize, startoffset, filesize, num_aio, step, sparse, direct, keep);
390
391         return 0;
392 }