• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 = 0;
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\n");
58 	else if (rc < 0)
59 		tst_brk(TFAIL, "%s: %s\n", func, strerror(-rc));
60 	else
61 		tst_brk(TFAIL, "%s: error %d\n", 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\n",
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\n", 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;
207 	struct testcase *tc = testcases + n;
208 
209 	status = io_tio("file", tc->flags, tc->operation);
210 	if (status)
211 		tst_res(TFAIL, "%s, status = %d", tc->desc, status);
212 	else
213 		tst_res(TPASS, "%s", tc->desc);
214 }
215 
216 static struct tst_test test = {
217 	.needs_tmpdir = 1,
218 	.test = test_io,
219 	.tcnt = ARRAY_SIZE(testcases),
220 };
221 
222 #else
223 TST_TEST_TCONF("test requires libaio and its development packages");
224 #endif
225