aio: test closing the fd before destroying the ioctx
[xfstests-dev.git] / src / aio-dio-regress / aio-last-ref-held-by-io.c
1 /* Copyright (C) 2010, Matthew E. Cross <matt.cross@gmail.com>
2  *
3  *  This program is free software; you can redistribute it and/or modify
4  *  it under the terms of the GNU General Public License as published by
5  *  the Free Software Foundation; either version 2 of the License, or
6  *  (at your option) any later version.
7  *
8  *  This program is distributed in the hope that it will be useful,
9  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  *  GNU General Public License for more details.
12  *
13  *  You should have received a copy of the GNU General Public License along
14  *  with this program; if not, write to the Free Software Foundation, Inc.,
15  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16  */
17
18 /* Code to reproduce the aio lockup.
19  *
20  * Make a test file that is at least 4MB long.  Something like this:
21  * 'dd if=/dev/zero of=/tmp/testfile bs=1M count=10'
22  *
23  * Run this test as './aio_test 0 100 /tmp/testfile' to induce the
24  * failure.
25  *
26  * Run this test as './aio_test 1 100 /tmp/testfile' to demonstrate an
27  * incomplete workaround (close fd, then wait for all io to complete
28  * on an io context before calling io_destroy()).  This still induces
29  * the failure.
30  *
31  * This test was written several years ago by Matt Cross, and he has
32  * graciously allowed me to post it for inclusion in xfstests.
33  *
34  * Changelog
35  * - reduce output and make it consistent for integration into xfstests (JEM)
36  * - run for fixed amount of time instead of indefinitely (JEM)
37  * - change coding style to meet xfstests standards (JEM)
38  * - get rid of unused code (workaround 2 documented above) (JEM)
39  * - use posix_memalign (JEM)
40  */
41
42 #ifndef _GNU_SOURCE
43 #define _GNU_SOURCE /* to get definition of O_DIRECT flag. */
44 #endif
45
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <libgen.h>
49 #include <pthread.h>
50 #include <unistd.h>
51 #include <libaio.h>
52 #include <sys/types.h>
53 #include <sys/stat.h>
54 #include <sys/syscall.h>
55 #include <sys/time.h>
56 #include <fcntl.h>
57 #include <sched.h>
58
59 #undef DEBUG
60 #ifdef DEBUG
61 #define dprintf(fmt, args...) printf(fmt, ##args)
62 #else
63 #define dprintf(fmt, args...)
64 #endif
65
66 char *filename;
67 int wait_for_events = 0;
68
69 pthread_mutex_t count_mutex = PTHREAD_MUTEX_INITIALIZER;
70 unsigned long total_loop_count = 0;
71
72 #define NUM_IOS 16
73 #define IOSIZE (1024 * 64)
74
75 pid_t
76 gettid(void)
77 {
78         return (pid_t)syscall(SYS_gettid);
79 }
80
81 void *
82 aio_test_thread(void *data)
83 {
84         int fd = -1;
85         io_context_t ioctx;
86         int ioctx_initted;
87         int ios_submitted;
88         struct iocb iocbs[NUM_IOS];
89         int i;
90         static unsigned char *buffer;
91         int ret;
92         long mycpu = (long)data;
93         pid_t mytid = gettid();
94         cpu_set_t cpuset;
95
96         dprintf("setting thread %d to run on cpu %ld\n", mytid, mycpu);
97
98         /*
99          * Problems have been easier to trigger when spreading the
100          * workload over the available CPUs.
101          */
102         CPU_ZERO(&cpuset);
103         CPU_SET(mycpu, &cpuset);
104         if (sched_setaffinity(mytid, sizeof(cpuset), &cpuset)) {
105                 printf("FAILED to set thread %d to run on cpu %ld\n",
106                        mytid, mycpu);
107         }
108
109         ioctx_initted = 0;
110         ios_submitted = 0;
111
112         ret = posix_memalign((void **)&buffer, getpagesize(), IOSIZE);
113         if (ret != 0) {
114                 printf("%lu: Failed to allocate buffer for IO: %d\n",
115                        pthread_self(), ret);
116                 goto done;
117         }
118
119         while (1) {
120                 fd = open(filename, O_RDONLY | O_DIRECT);
121                 if (fd < 0) {
122                         printf("%lu: Failed to open file '%s'\n",
123                                pthread_self(), filename);
124                         goto done;
125                 }
126
127                 memset(&ioctx, 0, sizeof(ioctx));
128                 if (io_setup(NUM_IOS, &ioctx)) {
129                         printf("%lu: Failed to setup io context\n",
130                                pthread_self());
131                         goto done;
132                 }
133                 ioctx_initted = 1;
134
135                 if (mycpu != 0) {
136                         for (i = 0; i < NUM_IOS; i++) {
137                                 struct iocb *iocb = &iocbs[i];
138
139                                 memset(iocb, 0, sizeof(*iocb));
140                                 io_prep_pread(iocb, fd, buffer,
141                                               IOSIZE, i * IOSIZE);
142                                 if (io_submit(ioctx, 1, &iocb) != 1) {
143                                         printf("%lu: failed to submit io #%d\n",
144                                                 pthread_self(), i+1);
145                                 }
146                         }
147                         ios_submitted = 1;
148                 }
149
150 done:
151                 if (fd >= 0)
152                         close(fd);
153
154                 if (wait_for_events && ios_submitted) {
155                         struct io_event io_events[NUM_IOS];
156
157                         if (io_getevents(ioctx, NUM_IOS, NUM_IOS,
158                                          io_events, NULL) != NUM_IOS)
159                                 printf("io_getevents failed to wait for all IO\n");
160                 }
161
162                 if (ioctx_initted) {
163                         io_destroy(ioctx);
164                         ioctx_initted = 0;
165                 }
166
167                 if (ios_submitted) {
168                         pthread_mutex_lock(&count_mutex);
169                         total_loop_count++;
170                         pthread_mutex_unlock(&count_mutex);
171
172                         ios_submitted = 0;
173                 }
174         }
175 }
176
177 int
178 main(int argc, char **argv)
179 {
180         unsigned num_threads;
181         unsigned i;
182         int fd;
183         pthread_t *threads;
184         long ncpus = sysconf(_SC_NPROCESSORS_ONLN);
185         struct timeval start, now, delta = { 0, 0 };
186
187         if (argc != 4) {
188                 printf("Usage: aio_test [wait for events?] [# of threads] "
189                        "[filename]\n");
190                 return -1;
191         }
192
193         wait_for_events = strtoul(argv[1], NULL, 0);
194         num_threads = strtoul(argv[2], NULL, 0);
195         filename = argv[3];
196
197         printf("wait_for_events: %d\n", wait_for_events);
198         printf("num_threads: %u\n", num_threads);
199         printf("filename: '%s'\n", basename(filename));
200
201         if (num_threads < 1) {
202                 printf("Number of threads is invalid, must be at least 1\n");
203                 return -1;
204         }
205
206         fd = open(filename, O_RDONLY|O_DIRECT);
207         if (fd < 0) {
208                 printf("Failed to open filename '%s' for reading\n", filename);
209                 return -1;
210         }
211         close(fd);
212
213         threads = malloc(sizeof(pthread_t) * num_threads);
214         if (threads == NULL) {
215                 printf("Failed to allocate thread id storage\n");
216                 return -1;
217         }
218
219         for (i = 0; i < num_threads; i++) {
220                 if (pthread_create(&threads[i], NULL,
221                                    aio_test_thread, (void *)(i % ncpus))) {
222                         printf("Failed to create thread #%u\n", i+1);
223                         threads[i] = (pthread_t)-1;
224                 }
225         }
226
227         printf("All threads spawned\n");
228         gettimeofday(&start, NULL);
229
230         while (delta.tv_sec < 60) {
231                 sleep(1);
232                 gettimeofday(&now, NULL);
233                 timersub(&now, &start, &delta);
234                 dprintf("%lu loops completed in %ld seconds\n",
235                         total_loop_count, delta.tv_sec);
236         }
237
238         return 0;
239 }