8f94d503c955f0bb978db2423ed95f40a338d82e
[xfstests-dev.git] / src / aio-dio-regress / aio-dio-append-write-read-race.c
1 /*
2  * Copyright (c) 2013 Alibaba Group.
3  * Copyright (c) 2017 Red Hat Inc.
4  * All Rights Reserved.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it would be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write the Free Software Foundation,
17  * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19
20 /*
21  * This is a normal case that we do some append dio writes and meanwhile
22  * we do some dio reads.  Currently in vfs we don't ensure that i_size
23  * is updated properly.  Hence the reader will read some data with '0'.
24  * But we expect that the reader should read nothing or data with 'a'.
25  */
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include <unistd.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35 #include <errno.h>
36
37 #include <libaio.h>
38 #include <pthread.h>
39
40 struct io_data {
41         int fd;
42         size_t blksize;
43         off_t offset;
44         char *buf;
45         int use_aio;
46 };
47
48 int reader_ready = 0;
49
50 static void usage(const char *prog)
51 {
52         fprintf(stderr, "usage: %s [-a] <file>\n", prog);
53         fprintf(stderr, "\t-a\tuse aio-dio\n");
54         exit(EXIT_FAILURE);
55 }
56
57 static void *reader(void *arg)
58 {
59         struct io_data *data = (struct io_data *)arg;
60         int ret;
61
62         memset(data->buf, 'b', data->blksize);
63         reader_ready = 1;
64         do {
65                 ret = pread(data->fd, data->buf, data->blksize, data->offset);
66                 if (ret < 0)
67                         perror("read file");
68         } while (ret <= 0);
69
70         return NULL;
71 }
72
73 #define fail(fmt , args...) do {        \
74         fprintf(stderr, fmt , ##args);  \
75         exit(1);                        \
76 } while (0)
77
78 static void *writer(struct io_data *data)
79 {
80         int ret;
81
82         while (!reader_ready)
83                 usleep(1);
84
85         if (data->use_aio) {
86                 struct io_context *ctx = NULL;
87                 struct io_event evs[1];
88                 struct iocb iocb;
89                 struct iocb *iocbs[] = { &iocb };
90
91                 ret = io_setup(1, &ctx);
92                 if (ret)
93                         fail("error %s during io_setup\n", strerror(ret));
94                 io_prep_pwrite(&iocb, data->fd, data->buf, data->blksize, data->offset);
95                 ret = io_submit(ctx, 1, iocbs);
96                 if (ret != 1)
97                         fail("error %s during io_submit\n", strerror(ret));
98                 ret = io_getevents(ctx, 1, 1, evs, NULL);
99                 if (ret != 1)
100                         fail("error %s during io_getevents\n", strerror(ret));
101                 if ((signed)evs[0].res < 0)
102                         fail("error %s during write\n", strerror(-evs[0].res));
103                 if ((signed)evs[0].res2 < 0)
104                         fail("secondary error %s during write\n", strerror(-evs[0].res2));
105         } else {
106                 ret = pwrite(data->fd, data->buf, data->blksize, data->offset);
107                 if (ret < 0)
108                         fail("write file failed: %s", strerror(errno));
109         }
110
111         return NULL;
112 }
113
114 int main(int argc, char *argv[])
115 {
116         pthread_t tid;
117         struct io_data wdata;
118         struct io_data rdata;
119         size_t max_blocks = 128;                /* 128 */
120         size_t blksize = 1 * 1024 * 1024;       /* 1M */
121         char *rbuf = NULL, *wbuf = NULL;
122         int rfd = 0, wfd = 0;
123         int i, j, c;
124         int use_aio = 1;
125         int ret = 0;
126         int io_align = 4096;
127         char *prog;
128         char *testfile;
129
130
131         prog = basename(argv[0]);
132
133         while ((c = getopt(argc, argv, "a:d")) != -1) {
134                 switch (c) {
135                 case 'a':
136                         io_align = strtol(optarg, NULL, 0);
137                         break;
138                 case 'd':
139                         use_aio = 0;
140                         break;
141                 default:
142                         usage(prog);
143                 }
144         }
145         if (optind != argc - 1)
146                 usage(prog);
147         testfile = argv[optind];
148
149         wfd = open(testfile, O_CREAT|O_DIRECT|O_WRONLY|O_TRUNC, 0644);
150         if (wfd < 0) {
151                 perror("open for write");
152                 exit(1);
153         }
154
155         rfd = open(testfile, O_DIRECT|O_RDONLY, 0644);
156         if (rfd < 0) {
157                 perror("open for read");
158                 ret = 1;
159                 goto err;
160         }
161
162         ret = posix_memalign((void **)&wbuf, io_align, blksize);
163         if (ret) {
164                 fail("failed to alloc memory: %s\n", strerror(ret));
165                 ret = 1;
166                 goto err;
167         }
168
169         ret = posix_memalign((void **)&rbuf, io_align, blksize);
170         if (ret) {
171                 fail("failed to alloc memory: %s\n", strerror(ret));
172                 ret = 1;
173                 goto err;
174         }
175
176         memset(wbuf, 'a', blksize);
177         wdata.fd = wfd;
178         wdata.blksize = blksize;
179         wdata.buf = wbuf;
180         wdata.use_aio = use_aio;
181         rdata.fd = rfd;
182         rdata.blksize = blksize;
183         rdata.buf = rbuf;
184
185         for (i = 0; i < max_blocks; i++) {
186                 wdata.offset = rdata.offset = i * blksize;
187
188                 /* reset reader_ready, it will be set in reader thread */
189                 reader_ready = 0;
190                 ret = pthread_create(&tid, NULL, reader, &rdata);
191                 if (ret) {
192                         fail("create reader thread failed: %s\n", strerror(ret));
193                         ret = 1;
194                         goto err;
195                 }
196
197                 writer(&wdata);
198
199                 ret = pthread_join(tid, NULL);
200                 if (ret) {
201                         fail("pthread join reader failed: %s\n", strerror(ret));
202                         ret = 1;
203                         goto err;
204                 }
205
206                 for (j = 0; j < blksize; j++) {
207                         if (rdata.buf[j] != 'a') {
208                                 fail("encounter an error: "
209                                         "block %d offset %d, content %x\n",
210                                         i, j, rbuf[j]);
211                                 ret = 1;
212                                 goto err;
213                         }
214                 }
215         }
216
217 err:
218         if (rfd)
219                 close(rfd);
220         if (wfd)
221                 close(wfd);
222         if (rbuf)
223                 free(rbuf);
224         if (wbuf)
225                 free(wbuf);
226
227         exit(ret);
228 }