aio-dio-append-write-fallocate-race: fix directio buffer alignment bugs
[xfstests-dev.git] / src / aio-dio-regress / aio-dio-append-write-fallocate-race.c
1 // SPDX-License-Identifier: GPL-2.0-or-newer
2 /*
3  * Copyright (c) 2019 Oracle.
4  * All Rights Reserved.
5  *
6  * Race appending aio dio and fallocate to make sure we get the correct file
7  * size afterwards.
8  */
9 #include <stdio.h>
10 #include <pthread.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <fcntl.h>
14 #include <unistd.h>
15 #include <string.h>
16 #include <errno.h>
17 #include <libaio.h>
18 #include <stdlib.h>
19 #include <stdbool.h>
20 #include <limits.h>
21
22 static int fd;
23 static int blocksize;
24
25 static void *
26 falloc_thread(
27         void            *p)
28 {
29         int             ret;
30
31         ret = fallocate(fd, 0, 0, blocksize);
32         if (ret)
33                 perror("falloc");
34
35         return NULL;
36 }
37
38 static int
39 test(
40         const char      *fname,
41         unsigned int    iteration,
42         unsigned int    *passed)
43 {
44         struct stat     sbuf;
45         pthread_t       thread;
46         io_context_t    ioctx = 0;
47         struct iocb     iocb;
48         struct iocb     *iocbp = &iocb;
49         struct io_event event;
50         char            *buf;
51         bool            wait_thread = false;
52         int             ret;
53
54         /* Truncate file, allocate resources for doing IO. */
55         fd = open(fname, O_DIRECT | O_RDWR | O_TRUNC | O_CREAT, 0644);
56         if (fd < 0) {
57                 perror(fname);
58                 return -1;
59         }
60
61         ret = fstat(fd, &sbuf);
62         if (ret) {
63                 perror(fname);
64                 goto out;
65         }
66         blocksize = sbuf.st_blksize;
67
68         ret = posix_memalign((void **)&buf, sysconf(_SC_PAGESIZE), blocksize);
69         if (ret) {
70                 errno = ret;
71                 perror("buffer");
72                 goto out;
73         }
74         memset(buf, 'X', blocksize);
75         memset(&event, 0, sizeof(event));
76
77         ret = io_queue_init(1, &ioctx);
78         if (ret) {
79                 errno = -ret;
80                 perror("io_queue_init");
81                 goto out_buf;
82         }
83
84         /*
85          * Set ourselves up to race fallocate(0..blocksize) with aio dio
86          * pwrite(blocksize..blocksize * 2).  This /should/ give us a file
87          * with length (2 * blocksize).
88          */
89         io_prep_pwrite(&iocb, fd, buf, blocksize, blocksize);
90
91         ret = pthread_create(&thread, NULL, falloc_thread, NULL);
92         if (ret) {
93                 errno = ret;
94                 perror("pthread");
95                 goto out_io;
96         }
97         wait_thread = true;
98
99         ret = io_submit(ioctx, 1, &iocbp);
100         if (ret != 1) {
101                 errno = -ret;
102                 perror("io_submit");
103                 goto out_join;
104         }
105
106         ret = io_getevents(ioctx, 1, 1, &event, NULL);
107         if (ret != 1) {
108                 errno = -ret;
109                 perror("io_getevents");
110                 goto out_join;
111         }
112
113         if (event.res < 0) {
114                 errno = -event.res;
115                 perror("io_event.res");
116                 goto out_join;
117         }
118
119         if (event.res2 < 0) {
120                 errno = -event.res2;
121                 perror("io_event.res2");
122                 goto out_join;
123         }
124
125         wait_thread = false;
126         ret = pthread_join(thread, NULL);
127         if (ret) {
128                 errno = ret;
129                 perror("join");
130                 goto out_io;
131         }
132
133         /* Make sure we actually got a file of size (2 * blocksize). */
134         ret = fstat(fd, &sbuf);
135         if (ret) {
136                 perror(fname);
137                 goto out_buf;
138         }
139
140         if (sbuf.st_size != 2 * blocksize) {
141                 fprintf(stderr, "[%u]: sbuf.st_size=%llu, expected %llu.\n",
142                                 iteration,
143                                 (unsigned long long)sbuf.st_size,
144                                 (unsigned long long)2 * blocksize);
145         } else {
146                 printf("[%u]: passed.\n", iteration);
147                 (*passed)++;
148         }
149
150 out_join:
151         if (wait_thread) {
152                 ret = pthread_join(thread, NULL);
153                 if (ret) {
154                         errno = ret;
155                         perror("join");
156                         goto out_io;
157                 }
158         }
159 out_io:
160         ret = io_queue_release(ioctx);
161         if (ret) {
162                 errno = -ret;
163                 perror("io_queue_release");
164         }
165
166 out_buf:
167         free(buf);
168 out:
169         ret = close(fd);
170         fd = -1;
171         if (ret) {
172                 perror("close");
173                 return -1;
174         }
175
176         return 0;
177 }
178
179 int main(int argc, char *argv[])
180 {
181         int             ret;
182         long            l;
183         unsigned int    i;
184         unsigned int    passed = 0;
185
186         if (argc != 3) {
187                 printf("Usage: %s filename iterations\n", argv[0]);
188                 return 1;
189         }
190
191         errno = 0;
192         l = strtol(argv[2], NULL, 0);
193         if (errno) {
194                 perror(argv[2]);
195                 return 1;
196         }
197         if (l < 1 || l > UINT_MAX) {
198                 fprintf(stderr, "%ld: must be between 1 and %u.\n",
199                                 l, UINT_MAX);
200                 return 1;
201         }
202
203         for (i = 0; i < l; i++) {
204                 ret = test(argv[1], i, &passed);
205                 if (ret)
206                         return 1;
207         }
208
209         printf("pass rate: %u/%u (%.2f%%)\n", passed, i, 100.0 * passed / i);
210
211         return 0;
212 }