• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2004 Stephen Hemminger <shemminger@osdl.org>
4  * Copyright (c) 2004 Daniel McNeil <daniel@osdl.org>
5  * Copyright (c) 2004 Marty Ridgeway <mridge@us.ibm.com>
6  * Copyright (C) 2021 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
7  */
8 
9 /*\
10  * [Description]
11  *
12  * Copy file by using an async I/O state machine.
13  *
14  * - Start read request
15  * - When read completes turn it into a write request
16  * - When write completes decrement counter and free up resources
17  */
18 
19 #define _GNU_SOURCE
20 
21 #include "tst_test.h"
22 
23 #ifdef HAVE_LIBAIO
24 #include <libaio.h>
25 #include <string.h>
26 #include <limits.h>
27 #include <sys/stat.h>
28 #include "common.h"
29 
30 #include "tst_rand_data.h"
31 
32 static const char *srcname = "srcfile.bin";
33 static const char *dstname = "dstfile.bin";
34 
35 static char *str_aio_blksize;
36 static char *str_filesize;
37 static char *str_aionum;
38 static char *str_oflag;
39 
40 static long long aio_blksize = 64 * 1024;
41 static long long filesize = 1 * 1024 * 1024;
42 static long long alignment;
43 static int aionum = 16;
44 static int srcflags = O_RDONLY;
45 static int dstflags = O_WRONLY;
46 
47 static int srcfd;
48 static int dstfd;
49 static long long busy;
50 static long long tocopy;
51 static struct iocb **iocb_free;
52 static int iocb_free_count;
53 
54 #ifndef howmany
55 # define howmany(x, y)  (((x) + ((y) - 1)) / (y))
56 #endif
57 
fill_with_rand_data(int fd,long long size)58 static void fill_with_rand_data(int fd, long long size)
59 {
60 	long long i = size;
61 
62 	while (i > 0) {
63 		SAFE_WRITE(1, fd, tst_rand_data,
64 			   MIN((long long)tst_rand_data_len, i));
65 		i -= tst_rand_data_len;
66 	}
67 	SAFE_FSYNC(fd);
68 }
69 
async_init(void)70 static void async_init(void)
71 {
72 	int i;
73 	char *buff;
74 
75 	iocb_free = SAFE_MALLOC(aionum * sizeof(struct iocb *));
76 	for (i = 0; i < aionum; i++) {
77 		iocb_free[i] = SAFE_MALLOC(sizeof(struct iocb));
78 		buff = SAFE_MEMALIGN(alignment, aio_blksize);
79 
80 		io_prep_pread(iocb_free[i], -1, buff, aio_blksize, 0);
81 	}
82 
83 	iocb_free_count = i;
84 }
85 
get_iocb(void)86 static struct iocb *get_iocb(void)
87 {
88 	if (!iocb_free_count)
89 		return 0;
90 
91 	return iocb_free[--iocb_free_count];
92 }
93 
put_iocb(struct iocb * io)94 static void put_iocb(struct iocb *io)
95 {
96 	iocb_free[iocb_free_count++] = io;
97 }
98 
async_write_done(LTP_ATTRIBUTE_UNUSED io_context_t ctx,struct iocb * iocb,long res,long res2)99 static void async_write_done(LTP_ATTRIBUTE_UNUSED io_context_t ctx, struct iocb *iocb, long res, long res2)
100 {
101 	int iosize = iocb->u.c.nbytes;
102 
103 	if (res != iosize)
104 		tst_brk(TBROK, "Write missing bytes expect %d got %ld", iosize, res);
105 
106 	if (res2 != 0)
107 		tst_brk(TBROK, "Write error: %s", tst_strerrno(-res2));
108 
109 	put_iocb(iocb);
110 
111 	--busy;
112 	--tocopy;
113 
114 	if (dstflags & O_DIRECT)
115 		SAFE_FSYNC(dstfd);
116 
117 	if (!tst_remaining_runtime())
118 		tst_brk(TCONF, "Out of runtime!");
119 }
120 
async_copy(io_context_t ctx,struct iocb * iocb,long res,long res2)121 static void async_copy(io_context_t ctx, struct iocb *iocb, long res, long res2)
122 {
123 	int iosize = iocb->u.c.nbytes;
124 	char *buf = iocb->u.c.buf;
125 	off_t offset = iocb->u.c.offset;
126 	int w;
127 
128 	if (res != iosize)
129 		tst_brk(TBROK, "Read missing bytes expect %d got %ld", iosize, res);
130 
131 	if (res2 != 0)
132 		tst_brk(TBROK, "Read error: %s", tst_strerrno(-res2));
133 
134 	io_prep_pwrite(iocb, dstfd, buf, iosize, offset);
135 	io_set_callback(iocb, async_write_done);
136 
137 	w = io_submit(ctx, 1, &iocb);
138 	if (w < 0)
139 		tst_brk(TBROK, "io_submit error: %s", tst_strerrno(-w));
140 }
141 
io_wait_run(io_context_t ctx,struct timespec * to)142 static void io_wait_run(io_context_t ctx, struct timespec *to)
143 {
144 	struct io_event events[aionum];
145 	struct io_event *ep;
146 	int n;
147 
148 	n = io_getevents(ctx, 1, aionum, events, to);
149 	if (n < 0)
150 		tst_brk(TBROK, "io_getevents() failed: %s", tst_strerrno(-n));
151 
152 	for (ep = events; n-- > 0; ep++) {
153 		io_callback_t cb = (io_callback_t) ep->data;
154 		struct iocb *iocb = ep->obj;
155 
156 		cb(ctx, iocb, ep->res, ep->res2);
157 	}
158 }
159 
async_run(io_context_t ctx,int fd,io_callback_t cb)160 static void async_run(io_context_t ctx, int fd, io_callback_t cb)
161 {
162 	long long offset = 0;
163 	int rc, i, n;
164 	int iosize;
165 
166 	tocopy = filesize / aio_blksize;
167 	busy = 0;
168 
169 	while (tocopy > 0) {
170 		n = MIN(aionum - busy, tocopy);
171 
172 		if (n > 0) {
173 			struct iocb *ioq[n];
174 
175 			for (i = 0; i < n; i++) {
176 				struct iocb *io = get_iocb();
177 
178 				iosize = MIN(filesize - offset, aio_blksize);
179 
180 				/* If we don't have any byte to write, exit */
181 				if (iosize <= 0)
182 					break;
183 
184 				io_prep_pread(io, fd, io->u.c.buf, iosize, offset);
185 				io_set_callback(io, cb);
186 
187 				ioq[i] = io;
188 				offset += iosize;
189 			}
190 
191 			rc = io_submit(ctx, i, ioq);
192 			if (rc < 0)
193 				tst_brk(TBROK, "io_submit write error: %s", tst_strerrno(-rc));
194 
195 			busy += n;
196 		}
197 
198 		io_wait_run(ctx, 0);
199 	}
200 }
201 
setup(void)202 static void setup(void)
203 {
204 	int maxaio;
205 	long long leftover;
206 
207 	if (tst_parse_int(str_aionum, &aionum, 1, INT_MAX))
208 		tst_brk(TBROK, "Invalid number of I/O '%s'", str_aionum);
209 
210 	SAFE_FILE_SCANF("/proc/sys/fs/aio-max-nr", "%d", &maxaio);
211 	tst_res(TINFO, "Maximum AIO blocks: %d", maxaio);
212 
213 	if (aionum > maxaio)
214 		tst_res(TCONF, "Number of async IO blocks passed the maximum (%d)", maxaio);
215 
216 	if (tst_parse_filesize(str_aio_blksize, &aio_blksize, 1, LLONG_MAX))
217 		tst_brk(TBROK, "Invalid write blocks size '%s'", str_aio_blksize);
218 
219 	if (str_oflag) {
220 		if (strncmp(str_oflag, "SYNC", 4) == 0) {
221 			dstflags |= O_SYNC;
222 		} else if (strncmp(str_oflag, "DIRECT", 6) == 0) {
223 			if (tst_fs_type(".") == TST_TMPFS_MAGIC)
224 				tst_brk(TCONF, "O_DIRECT not supported on tmpfs");
225 
226 			srcflags |= O_DIRECT;
227 			dstflags |= O_DIRECT;
228 		}
229 	}
230 
231 	if (tst_fs_type(".") == TST_TMPFS_MAGIC)
232 		alignment = getpagesize();
233 	else
234 		alignment = tst_dev_block_size(".");
235 
236 	if (dstflags & O_DIRECT && aio_blksize % alignment)
237 		tst_brk(TCONF, "Block size is not multiple of drive block size");
238 
239 	if (tst_parse_filesize(str_filesize, &filesize, 1, LLONG_MAX))
240 		tst_brk(TBROK, "Invalid file size '%s'", str_filesize);
241 
242 	leftover = filesize % aio_blksize;
243 
244 	if (leftover > 0) {
245 		filesize = filesize - leftover + aio_blksize;
246 
247 		tst_res(TINFO, "Rounded filesize to the next block size multiple: %llu", filesize);
248 	}
249 
250 	tst_res(TINFO, "Fill %s with random data", srcname);
251 
252 	srcfd = SAFE_OPEN(srcname, (srcflags & ~O_DIRECT) | O_RDWR | O_CREAT, 0666);
253 	fill_with_rand_data(srcfd, filesize);
254 	SAFE_CLOSE(srcfd);
255 }
256 
cleanup(void)257 static void cleanup(void)
258 {
259 	if (srcfd > 0)
260 		SAFE_CLOSE(srcfd);
261 
262 	if (dstfd > 0)
263 		SAFE_CLOSE(dstfd);
264 }
265 
run(void)266 static void run(void)
267 {
268 	const int buffsize = 4096;
269 	io_context_t myctx;
270 	struct stat st;
271 	char srcbuff[buffsize];
272 	char dstbuff[buffsize];
273 	int reads = 0;
274 	int i, r;
275 
276 	srcfd = SAFE_OPEN(srcname, srcflags | O_RDWR | O_CREAT, 0666);
277 	dstfd = SAFE_OPEN(dstname, dstflags | O_WRONLY | O_CREAT, 0666);
278 
279 	tst_res(TINFO, "Copy %s -> %s", srcname, dstname);
280 
281 	memset(&myctx, 0, sizeof(myctx));
282 	io_queue_init(aionum, &myctx);
283 
284 	async_init();
285 	async_run(myctx, srcfd, async_copy);
286 
287 	io_destroy(myctx);
288 	SAFE_CLOSE(srcfd);
289 	SAFE_CLOSE(dstfd);
290 
291 	tst_res(TINFO, "Comparing %s with %s", srcname, dstname);
292 
293 	SAFE_STAT(dstname, &st);
294 	if (st.st_size != filesize) {
295 		tst_res(TFAIL, "Expected destination file size %lld but it's %ld", filesize, st.st_size);
296 		/* no need to compare files */
297 		return;
298 	}
299 
300 	srcfd = SAFE_OPEN(srcname, srcflags | O_RDONLY, 0666);
301 	dstfd = SAFE_OPEN(dstname, srcflags | O_RDONLY, 0666);
302 
303 	reads = howmany(filesize, buffsize);
304 
305 	for (i = 0; i < reads; i++) {
306 		r = SAFE_READ(0, srcfd, srcbuff, buffsize);
307 		SAFE_READ(0, dstfd, dstbuff, buffsize);
308 		if (memcmp(srcbuff, dstbuff, r)) {
309 			tst_res(TFAIL, "Files are not identical");
310 			return;
311 		}
312 	}
313 
314 	tst_res(TPASS, "Files are identical");
315 
316 	SAFE_CLOSE(srcfd);
317 	SAFE_CLOSE(dstfd);
318 }
319 
320 static struct tst_test test = {
321 	.test_all = run,
322 	.setup = setup,
323 	.cleanup = cleanup,
324 	.needs_tmpdir = 1,
325 	.max_runtime = 1800,
326 	.needs_root = 1,
327 	.options = (struct tst_option[]) {
328 		{"b:", &str_aio_blksize, "Size of writing blocks (default 1K)"},
329 		{"s:", &str_filesize, "Size of file (default 10M)"},
330 		{"n:", &str_aionum, "Number of Async IO blocks (default 16)"},
331 		{"f:", &str_oflag, "Open flag: SYNC | DIRECT (default O_CREAT only)"},
332 		{},
333 	},
334 };
335 #else
336 TST_TEST_TCONF("test requires libaio and its development packages");
337 #endif
338