24f3e3c660b2f77517b464871e20ccd7cf8ed16d
[xfstests-dev.git] / src / aio-dio-regress / aio-dio-invalidate-failure.c
1 /*
2  *   aio-dio-invalidate-failure - test race in read cache invalidation
3  *   Copyright (C) 2007 Zach Brown
4  *
5  *   This program is free software; you can redistribute it and/or modify
6  *   it under the terms of the GNU General Public License as published by
7  *   the Free Software Foundation; either version 2 of the License, or
8  *   (at your option) any later version.
9  *
10  *   This program is distributed in the hope that it will 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 to the Free Software
17  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19  
20 #define _XOPEN_SOURCE 500 /* pwrite */
21 #include <unistd.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <signal.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28 #include <libaio.h>
29 #include <errno.h>
30 #include <time.h>
31 #include <sys/types.h>
32 #include <sys/wait.h>
33
34 /*
35  * DIO invalidates the read cache after it writes.  At one point it tried to
36  * return EIO if this failed.  When called from AIO, though, this EIO return
37  * would clobber EIOCBQUEUED and cause fs/aio.c and fs/direct-io.c to complete
38  * an iocb twice.  This typically references freed memory from an interrupt
39  * handler and oopses.
40  *
41  * This test hits the race after at most two minutes on a single spindle.  It
42  * spins performing large dio writes.  It also spins racing buffered writes.
43  * It assumes it's on ext3 using ordered writes.  The ordered write bhs can be
44  * pinned by jbd as a transaction commits.  If invalidate_inode_pages2_range()
45  * hits pages backed by those buffers ->releasepage will fail and it'll try to
46  * return -EIO.
47  */
48 #ifndef O_DIRECT
49 #define O_DIRECT         040000 /* direct disk access hint */
50 #endif
51
52 #define GINORMOUS (32 * 1024 * 1024)
53
54
55 /* This test never survived to 180 seconds on a single spindle */
56 #define SECONDS 200
57
58 static unsigned char buf[GINORMOUS] __attribute((aligned (4096)));
59
60 #define fail(fmt , args...) do {\
61         printf(fmt , ##args);   \
62         exit(1);                \
63 } while (0)
64
65 void spin_dio(int fd)
66 {
67         io_context_t ctx;
68         struct iocb iocb;
69         struct iocb *iocbs[1] = { &iocb };
70         struct io_event event;
71         int ret;
72
73         io_prep_pwrite(&iocb, fd, buf, GINORMOUS, 0);
74
75         ret = io_queue_init(1, &ctx);
76         if (ret)
77                 fail("io_queue_init returned %d", ret);
78
79         while (1) {
80                 ret = io_submit(ctx, 1, iocbs);
81                 if (ret != 1)
82                         fail("io_submit returned %d instead of 1", ret);
83
84                 ret = io_getevents(ctx, 1, 1, &event, NULL);
85                 if (ret != 1)
86                         fail("io_getevents returned %d instead of 1", ret);
87
88                 if (event.res == -EIO) {
89                         printf("invalidation returned -EIO, OK\n");
90                         exit(0);
91                 }
92
93                 if (event.res != GINORMOUS)
94                         fail("event res %ld\n", event.res);
95         }
96 }
97
98 void spin_buffered(int fd)
99 {
100         int ret;
101
102         while (1) {
103                 ret = pwrite(fd, buf, GINORMOUS, 0);
104                 if (ret != GINORMOUS)
105                         fail("buffered write returned %d", ret);
106         }
107 }
108
109 static void alarm_handler(int signum)
110 {
111 }
112
113 int main(int argc, char **argv)
114 {
115         pid_t buffered_pid;
116         pid_t dio_pid;
117         pid_t pid;
118         int fd;
119         int fd2;
120         int status;
121         struct sigaction sa;
122
123         if (argc != 2)
124                 fail("only arg should be file name");
125
126         fd = open(argv[1], O_DIRECT|O_CREAT|O_RDWR, 0644);
127         if (fd < 0)
128                 fail("open dio failed: %d\n", errno);
129
130         fd2 = open(argv[1], O_RDWR, 0644);
131         if (fd < 0)
132                 fail("open failed: %d\n", errno);
133
134         buffered_pid = fork();
135         if (buffered_pid < 0)
136                 fail("fork failed: %d\n", errno);
137
138         if (buffered_pid == 0) {
139                 spin_buffered(fd2);
140                 exit(0);
141         }
142
143         dio_pid = fork();
144         if (dio_pid < 0) {
145                 kill(buffered_pid, SIGKILL);
146                 waitpid(buffered_pid, NULL, 0);
147                 fail("fork failed: %d\n", errno);
148         }
149
150         if (dio_pid == 0) {
151                 spin_dio(fd);
152                 exit(0);
153         }
154
155         memset(&sa, 0, sizeof(sa));
156         sa.sa_handler = alarm_handler;
157         sigemptyset(&sa.sa_mask);
158         if (sigaction(SIGALRM, &sa, NULL) == -1)
159                 fail("sigaction: %d\n", errno);
160
161         alarm(SECONDS);
162
163         pid = wait(&status);
164         if (pid < 0 && errno == EINTR) {
165                 /* if we timed out then we're done */
166                 kill(buffered_pid, SIGKILL);
167                 kill(dio_pid, SIGKILL);
168
169                 waitpid(buffered_pid, NULL, 0);
170                 waitpid(dio_pid, NULL, 0);
171
172                 printf("ran for %d seconds without error, passing\n", SECONDS);
173                 exit(0);
174         }
175
176         if (pid == dio_pid) {
177                 kill(buffered_pid, SIGKILL);
178                 waitpid(buffered_pid, NULL, 0);
179         } else {
180                 kill(dio_pid, SIGKILL);
181                 waitpid(dio_pid, NULL, 0);
182         }
183
184         /* 
185          * pass on the child's pass/fail return code or fail if the child 
186          * didn't exit cleanly.
187          */
188         exit(WIFEXITED(status) ? WEXITSTATUS(status) : 1);
189 }