1 #define __USE_GNU
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <unistd.h>
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #include <libaio.h>
8 #include <malloc.h>
9 #include <fcntl.h>
10 #include <pthread.h>
11 #include <errno.h>
12
13 #ifndef O_DIRECT
14 #define O_DIRECT 040000 /* direct disk access hint */
15 #endif
16
17
18 /*
19 * This was originally submitted to
20 * http://bugzilla.kernel.org/show_bug.cgi?id=6831 by
21 * Rafal Wijata <wijata@nec-labs.com>. It caught a race in dio aio completion
22 * that would call aio_complete() before the dio callers would update i_size.
23 * A stat after io_getevents() would not see the new file size.
24 *
25 * The bug was fixed in the fs/direct-io.c completion reworking that appeared
26 * in 2.6.20. This test should fail on 2.6.19.
27 */
28
29 #define BUFSIZE 1024
30
31 static unsigned char buf[BUFSIZE] __attribute((aligned (512)));
32
33 /*
34 * this was arbitrarily chosen to take about two seconds on a dual athlon in a
35 * debugging kernel.. it trips up long before that.
36 */
37 #define MAX_AIO_EVENTS 4000
38
39 #define fail(fmt , args...) do {\
40 printf(fmt , ##args); \
41 exit(1); \
42 } while (0)
43
44 void fun_write1(void* ptr);
45 void fun_writeN(void* ptr);
46 void fun_read(void* ptr);
47
48 int handle = 0;
49 io_context_t ctxp;
50 struct iocb *iocbs[MAX_AIO_EVENTS];
51 struct io_event ioevents[MAX_AIO_EVENTS];
52
53 volatile int submittedSize = 0; //synchronization
54
main(int argc,char ** argv)55 int main(int argc, char **argv)
56 {
57 pthread_t thread_read;
58 pthread_t thread_write;
59 int i;
60 int ret;
61
62 if (argc != 2)
63 fail("only arg should be file name\n");
64
65 for (i = 0; i < BUFSIZE; ++i)
66 buf[i] = 'A' + (char)(i % ('Z'-'A'+1));
67
68 buf[BUFSIZE-1] = '\n';
69
70 handle = open(argv[1], O_CREAT | O_TRUNC | O_DIRECT | O_RDWR, 0600);
71 if (handle == -1)
72 fail("failed to open test file %s, errno: %d\n",
73 argv[1], errno);
74
75 memset(&ctxp, 0, sizeof(ctxp));
76 ret = io_setup(MAX_AIO_EVENTS, &ctxp);
77 if (ret)
78 fail("io_setup returned %d\n", ret);
79
80 for (i = 0; i < MAX_AIO_EVENTS; ++i) {
81
82 iocbs[i] = calloc(1, sizeof(struct iocb));
83 if (iocbs[i] == NULL)
84 fail("failed to allocate an iocb\n");
85
86 /* iocbs[i]->data = i; */
87 iocbs[i]->aio_fildes = handle;
88 iocbs[i]->aio_lio_opcode = IO_CMD_PWRITE;
89 iocbs[i]->aio_reqprio = 0;
90 iocbs[i]->u.c.buf = buf;
91 iocbs[i]->u.c.nbytes = BUFSIZE;
92 iocbs[i]->u.c.offset = BUFSIZE*i;
93 }
94
95 pthread_create(&thread_read, NULL, (void*)&fun_read, NULL);
96 pthread_create(&thread_write, NULL, (void*)&fun_writeN, NULL);
97
98 pthread_join(thread_read, NULL);
99 pthread_join(thread_write, NULL);
100
101 io_destroy(ctxp);
102 close(handle);
103
104 printf("%u iterations of racing extensions and collection passed\n",
105 MAX_AIO_EVENTS);
106
107 return 0;
108 }
109
fun_read(void * ptr)110 void fun_read(void *ptr)
111 {
112 long n = MAX_AIO_EVENTS;
113 struct stat filestat;
114 long long exSize;
115 long i;
116 long r;
117
118 while (n > 0) {
119 r = io_getevents(ctxp, 1, MAX_AIO_EVENTS, ioevents, NULL);
120 if (r < 0)
121 fail("io_getevents returned %ld\n", r);
122
123 n -= r;
124 for (i = 0; i < r; ++i) {
125 if (ioevents[i].obj->u.c.nbytes != BUFSIZE)
126 fail("error in block: expacted %d bytes, "
127 "receiced %ld\n", BUFSIZE,
128 ioevents[i].obj->u.c.nbytes);
129
130 exSize = ioevents[i].obj->u.c.offset +
131 ioevents[i].obj->u.c.nbytes;
132 fstat(handle, &filestat);
133 if (filestat.st_size < exSize)
134 fail("write of %lu bytes @%llu finished, "
135 "expected filesize at least %llu, but "
136 "got %ld\n", ioevents[i].obj->u.c.nbytes,
137 ioevents[i].obj->u.c.offset, exSize,
138 filestat.st_size);
139 }
140 }
141 }
142
fun_writeN(void * ptr)143 void fun_writeN(void *ptr)
144 {
145 int i;
146 int ret;
147
148 for(i = 0; i < MAX_AIO_EVENTS; ++i) {
149 ret = io_submit(ctxp, 1, &(iocbs[i]));
150 if (ret != 1)
151 fail("io_subit returned %d instead of 1\n", ret);
152 }
153 }
154
fun_write1(void * ptr)155 void fun_write1(void *ptr)
156 {
157 int ret;
158
159 ret = io_submit(ctxp, MAX_AIO_EVENTS, iocbs);
160 if (ret != MAX_AIO_EVENTS)
161 fail("io_subit returned %d instead of %u\n", ret,
162 MAX_AIO_EVENTS);
163 }
164