1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) International Business Machines Corp., 2003
4 * Copyright (c) Linux Test Project, 2004-2019
5 *
6 * AUTHORS
7 * Kai Zhao (ltcd3@cn.ibm.com)
8 */
9
10 #include "config.h"
11 #include "tst_test.h"
12
13 #ifdef HAVE_LIBAIO
14 #include <errno.h>
15 #include <stdlib.h>
16 #include <libaio.h>
17
18 #define AIO_MAXIO 32
19 #define AIO_BLKSIZE (64*1024)
20
21 static int wait_count;
22
23 #define DESC_FLAGS_OPR(x, y) .desc = (x == IO_CMD_PWRITE ? "WRITE: " #y: "READ : " #y), \
24 .flags = y, .operation = x
25
26 struct testcase {
27 const char *desc;
28 int flags;
29 int operation;
30 } testcases[] = {
31 {
32 DESC_FLAGS_OPR(IO_CMD_PWRITE, O_WRONLY | O_TRUNC | O_DIRECT | O_LARGEFILE | O_CREAT),
33 },
34 {
35 DESC_FLAGS_OPR(IO_CMD_PREAD, O_RDONLY | O_DIRECT | O_LARGEFILE),
36 },
37 {
38 DESC_FLAGS_OPR(IO_CMD_PWRITE, O_RDWR | O_TRUNC),
39 },
40 {
41 DESC_FLAGS_OPR(IO_CMD_PREAD, O_RDWR),
42 },
43 {
44 DESC_FLAGS_OPR(IO_CMD_PWRITE, O_WRONLY | O_TRUNC),
45 },
46 {
47 DESC_FLAGS_OPR(IO_CMD_PREAD, O_RDONLY),
48 },
49 };
50
51 /*
52 * Fatal error handler
53 */
io_error(const char * func,int rc)54 static void io_error(const char *func, int rc)
55 {
56 if (rc == -ENOSYS)
57 tst_brk(TCONF, "AIO not in this kernel");
58 else if (rc < 0)
59 tst_brk(TFAIL, "%s: %s", func, strerror(-rc));
60 else
61 tst_brk(TFAIL, "%s: error %d", func, rc);
62 }
63
64 /*
65 * write work done
66 */
work_done(io_context_t ctx,struct iocb * iocb,long res,long res2)67 static void work_done(io_context_t ctx, struct iocb *iocb, long res, long res2)
68 {
69 (void) ctx; // silence compiler warning (-Wunused)
70
71 if (res2 != 0)
72 io_error("aio write", res2);
73
74 if (res != (long)iocb->u.c.nbytes)
75 tst_brk(TFAIL, "write missed bytes expect %lu got %ld",
76 iocb->u.c.nbytes, res);
77
78 wait_count--;
79 }
80
81 /*
82 * io_wait_run() - wait for an io_event and then call the callback.
83 */
io_wait_run(io_context_t ctx,struct timespec * to)84 static int io_wait_run(io_context_t ctx, struct timespec *to)
85 {
86 struct io_event events[AIO_MAXIO];
87 struct io_event *ep;
88 int ret, n;
89
90 /*
91 * get up to aio_maxio events at a time.
92 */
93 ret = n = io_getevents(ctx, 1, AIO_MAXIO, events, to);
94
95 /*
96 * Call the callback functions for each event.
97 */
98 for (ep = events; n-- > 0; ep++) {
99 io_callback_t cb = (io_callback_t) ep->data;
100 struct iocb *iocb = ep->obj;
101 cb(ctx, iocb, ep->res, ep->res2);
102 }
103 return ret;
104 }
105
io_tio(char * pathname,int flag,int operation)106 static int io_tio(char *pathname, int flag, int operation)
107 {
108 int res, fd = 0, i = 0;
109 void *bufptr = NULL;
110 off_t offset = 0;
111 struct timespec timeout;
112 struct stat fi_stat;
113 size_t alignment;
114
115 io_context_t myctx;
116 struct iocb iocb_array[AIO_MAXIO];
117 struct iocb *iocbps[AIO_MAXIO];
118
119 fd = SAFE_OPEN(pathname, flag, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
120
121 /* determine the alignment from the blksize of the underlying device */
122 SAFE_FSTAT(fd, &fi_stat);
123 alignment = fi_stat.st_blksize;
124
125 res = io_queue_init(AIO_MAXIO, &myctx);
126
127 for (i = 0; i < AIO_MAXIO; i++) {
128
129 switch (operation) {
130 case IO_CMD_PWRITE:
131 if (posix_memalign(&bufptr, alignment, AIO_BLKSIZE)) {
132 tst_brk(TBROK | TERRNO, "posix_memalign failed");
133 return -1;
134 }
135 memset(bufptr, 0, AIO_BLKSIZE);
136
137 io_prep_pwrite(&iocb_array[i], fd, bufptr,
138 AIO_BLKSIZE, offset);
139 io_set_callback(&iocb_array[i], work_done);
140 iocbps[i] = &iocb_array[i];
141 offset += AIO_BLKSIZE;
142
143 break;
144 case IO_CMD_PREAD:
145 if (posix_memalign(&bufptr, alignment, AIO_BLKSIZE)) {
146 tst_brk(TBROK | TERRNO, "posix_memalign failed");
147 return -1;
148 }
149 memset(bufptr, 0, AIO_BLKSIZE);
150
151 io_prep_pread(&iocb_array[i], fd, bufptr,
152 AIO_BLKSIZE, offset);
153 io_set_callback(&iocb_array[i], work_done);
154 iocbps[i] = &iocb_array[i];
155 offset += AIO_BLKSIZE;
156 break;
157 default:
158 tst_res(TFAIL, "Command failed; opcode returned: %d", operation);
159 return -1;
160 break;
161 }
162 }
163
164 do {
165 res = io_submit(myctx, AIO_MAXIO, iocbps);
166 } while (res == -EAGAIN);
167
168 if (res < 0)
169 io_error("io_submit tio", res);
170
171 /*
172 * We have submitted all the i/o requests. Wait for them to complete and
173 * call the callbacks.
174 */
175 wait_count = AIO_MAXIO;
176
177 timeout.tv_sec = 30;
178 timeout.tv_nsec = 0;
179
180 switch (operation) {
181 case IO_CMD_PREAD:
182 case IO_CMD_PWRITE:
183 {
184 while (wait_count) {
185 res = io_wait_run(myctx, &timeout);
186 if (res < 0)
187 io_error("io_wait_run", res);
188 }
189 }
190 break;
191 }
192
193 SAFE_CLOSE(fd);
194
195 for (i = 0; i < AIO_MAXIO; i++)
196 if (iocb_array[i].u.c.buf != NULL)
197 free(iocb_array[i].u.c.buf);
198
199 io_queue_release(myctx);
200
201 return 0;
202 }
203
test_io(unsigned int n)204 static void test_io(unsigned int n)
205 {
206 int status, new_flags;
207 struct testcase *tc = testcases + n;
208
209 new_flags = tc->flags;
210
211 if ((tst_fs_type(".") == TST_TMPFS_MAGIC) && (tc->flags & O_DIRECT)) {
212 tst_res(TINFO, "Drop O_DIRECT flag for tmpfs");
213 new_flags &= ~O_DIRECT;
214 }
215
216 status = io_tio("file", new_flags, tc->operation);
217 if (status)
218 tst_res(TFAIL, "%s, status = %d", tc->desc, status);
219 else
220 tst_res(TPASS, "%s", tc->desc);
221 }
222
223 static struct tst_test test = {
224 .needs_tmpdir = 1,
225 .test = test_io,
226 .tcnt = ARRAY_SIZE(testcases),
227 };
228
229 #else
230 TST_TEST_TCONF("test requires libaio and its development packages");
231 #endif
232