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