1a78009eb18aa91d2815a63abbee8bb317b693f2
[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 static void *writer(struct io_data *data)
74 {
75         int ret;
76
77         while (!reader_ready)
78                 usleep(1);
79
80         if (data->use_aio) {
81                 struct io_context *ctx = NULL;
82                 struct io_event evs[1];
83                 struct iocb iocb;
84                 struct iocb *iocbs[] = { &iocb };
85
86                 ret = io_setup(1, &ctx);
87                 if (ret) {
88                         fprintf(stderr, "error %s during io_setup\n",
89                                 strerror(ret));
90                         return NULL;
91                 }
92                 io_prep_pwrite(&iocb, data->fd, data->buf, data->blksize, data->offset);
93                 ret = io_submit(ctx, 1, iocbs);
94                 if (ret != 1) {
95                         fprintf(stderr, "error %s during io_submit\n",
96                                 strerror(ret));
97                         return NULL;
98                 }
99                 ret = io_getevents(ctx, 1, 1, evs, NULL);
100                 if (ret != 1) {
101                         fprintf(stderr, "error %s during io_getevents\n",
102                                 strerror(ret));
103                         return NULL;
104                 }
105         } else {
106                 ret = pwrite(data->fd, data->buf, data->blksize, data->offset);
107                 if (ret < 0)
108                         perror("write file failed");
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                 fprintf(stderr, "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                 fprintf(stderr, "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                         fprintf(stderr, "create reader thread failed: %s\n",
193                                 strerror(ret));
194                         ret = 1;
195                         goto err;
196                 }
197
198                 writer(&wdata);
199
200                 ret = pthread_join(tid, NULL);
201                 if (ret) {
202                         fprintf(stderr, "pthread join reader failed: %s\n",
203                                 strerror(ret));
204                         ret = 1;
205                         goto err;
206                 }
207
208                 for (j = 0; j < blksize; j++) {
209                         if (rdata.buf[j] != 'a') {
210                                 fprintf(stderr, "encounter an error: "
211                                         "block %d offset %d, content %x\n",
212                                         i, j, rbuf[j]);
213                                 ret = 1;
214                                 goto err;
215                         }
216                 }
217         }
218
219 err:
220         if (rfd)
221                 close(rfd);
222         if (wfd)
223                 close(wfd);
224         if (rbuf)
225                 free(rbuf);
226         if (wbuf)
227                 free(wbuf);
228
229         exit(ret);
230 }