xref: /freebsd/tests/sys/aio/aio_test.c (revision f5e9c916afed4a948fe5c03bfaee038d165e12ab)
1 /*-
2  * Copyright (c) 2004 Robert N. M. Watson
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 /*
30  * Regression test to do some very basic AIO exercising on several types of
31  * file descriptors.  Currently, the tests consist of initializing a fixed
32  * size buffer with pseudo-random data, writing it to one fd using AIO, then
33  * reading it from a second descriptor using AIO.  For some targets, the same
34  * fd is used for write and read (i.e., file, md device), but for others the
35  * operation is performed on a peer (pty, socket, fifo, etc).  A timeout is
36  * initiated to detect undo blocking.  This test does not attempt to exercise
37  * error cases or more subtle asynchronous behavior, just make sure that the
38  * basic operations work on some basic object types.
39  */
40 
41 #include <sys/param.h>
42 #include <sys/module.h>
43 #include <sys/socket.h>
44 #include <sys/stat.h>
45 #include <sys/mdioctl.h>
46 
47 #include <aio.h>
48 #include <err.h>
49 #include <errno.h>
50 #include <fcntl.h>
51 #include <libutil.h>
52 #include <limits.h>
53 #include <stdint.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <termios.h>
58 #include <unistd.h>
59 
60 #include <atf-c.h>
61 
62 #include "freebsd_test_suite/macros.h"
63 #include "local.h"
64 
65 #define	PATH_TEMPLATE	"aio.XXXXXXXXXX"
66 
67 /*
68  * GLOBAL_MAX sets the largest usable buffer size to be read and written, as
69  * it sizes ac_buffer in the aio_context structure.  It is also the default
70  * size for file I/O.  For other types, we use smaller blocks or we risk
71  * blocking (and we run in a single process/thread so that would be bad).
72  */
73 #define	GLOBAL_MAX	16384
74 
75 #define	BUFFER_MAX	GLOBAL_MAX
76 struct aio_context {
77 	int		 ac_read_fd, ac_write_fd;
78 	long		 ac_seed;
79 	char		 ac_buffer[GLOBAL_MAX];
80 	int		 ac_buflen;
81 	int		 ac_seconds;
82 	void		 (*ac_cleanup)(void *arg);
83 	void		*ac_cleanup_arg;
84 };
85 
86 static int	aio_timedout;
87 
88 /*
89  * Each test run specifies a timeout in seconds.  Use the somewhat obsoleted
90  * signal(3) and alarm(3) APIs to set this up.
91  */
92 static void
93 aio_timeout_signal(int sig __unused)
94 {
95 
96 	aio_timedout = 1;
97 }
98 
99 static void
100 aio_timeout_start(int seconds)
101 {
102 
103 	aio_timedout = 0;
104 	ATF_REQUIRE_MSG(signal(SIGALRM, aio_timeout_signal) != SIG_ERR,
105 	    "failed to set SIGALRM handler: %s", strerror(errno));
106 	alarm(seconds);
107 }
108 
109 static void
110 aio_timeout_stop(void)
111 {
112 
113 	ATF_REQUIRE_MSG(signal(SIGALRM, NULL) != SIG_ERR,
114 	    "failed to reset SIGALRM handler to default: %s", strerror(errno));
115 	alarm(0);
116 }
117 
118 /*
119  * Fill a buffer given a seed that can be fed into srandom() to initialize
120  * the PRNG in a repeatable manner.
121  */
122 static void
123 aio_fill_buffer(char *buffer, int len, long seed)
124 {
125 	char ch;
126 	int i;
127 
128 	srandom(seed);
129 	for (i = 0; i < len; i++) {
130 		ch = random() & 0xff;
131 		buffer[i] = ch;
132 	}
133 }
134 
135 /*
136  * Test that a buffer matches a given seed.  See aio_fill_buffer().  Return
137  * (1) on a match, (0) on a mismatch.
138  */
139 static int
140 aio_test_buffer(char *buffer, int len, long seed)
141 {
142 	char ch;
143 	int i;
144 
145 	srandom(seed);
146 	for (i = 0; i < len; i++) {
147 		ch = random() & 0xff;
148 		if (buffer[i] != ch)
149 			return (0);
150 	}
151 	return (1);
152 }
153 
154 /*
155  * Initialize a testing context given the file descriptors provided by the
156  * test setup.
157  */
158 static void
159 aio_context_init(struct aio_context *ac, int read_fd,
160     int write_fd, int buflen, int seconds, void (*cleanup)(void *),
161     void *cleanup_arg)
162 {
163 
164 	ATF_REQUIRE_MSG(buflen <= BUFFER_MAX,
165 	    "aio_context_init: buffer too large (%d > %d)",
166 	    buflen, BUFFER_MAX);
167 	bzero(ac, sizeof(*ac));
168 	ac->ac_read_fd = read_fd;
169 	ac->ac_write_fd = write_fd;
170 	ac->ac_buflen = buflen;
171 	srandomdev();
172 	ac->ac_seed = random();
173 	aio_fill_buffer(ac->ac_buffer, buflen, ac->ac_seed);
174 	ATF_REQUIRE_MSG(aio_test_buffer(ac->ac_buffer, buflen,
175 	    ac->ac_seed) != 0, "aio_test_buffer: internal error");
176 	ac->ac_seconds = seconds;
177 	ac->ac_cleanup = cleanup;
178 	ac->ac_cleanup_arg = cleanup_arg;
179 }
180 
181 /*
182  * Each tester can register a callback to clean up in the event the test
183  * fails.  Preserve the value of errno so that subsequent calls to errx()
184  * work properly.
185  */
186 static void
187 aio_cleanup(struct aio_context *ac)
188 {
189 	int error;
190 
191 	if (ac->ac_cleanup == NULL)
192 		return;
193 	error = errno;
194 	(ac->ac_cleanup)(ac->ac_cleanup_arg);
195 	errno = error;
196 }
197 
198 /*
199  * Perform a simple write test of our initialized data buffer to the provided
200  * file descriptor.
201  */
202 static void
203 aio_write_test(struct aio_context *ac)
204 {
205 	struct aiocb aio, *aiop;
206 	ssize_t len;
207 
208 	ATF_REQUIRE_KERNEL_MODULE("aio");
209 
210 	bzero(&aio, sizeof(aio));
211 	aio.aio_buf = ac->ac_buffer;
212 	aio.aio_nbytes = ac->ac_buflen;
213 	aio.aio_fildes = ac->ac_write_fd;
214 	aio.aio_offset = 0;
215 
216 	aio_timeout_start(ac->ac_seconds);
217 
218 	if (aio_write(&aio) < 0) {
219 		if (errno == EINTR) {
220 			if (aio_timedout) {
221 				aio_cleanup(ac);
222 				atf_tc_fail("aio_write timed out");
223 			}
224 		}
225 		aio_cleanup(ac);
226 		atf_tc_fail("aio_write failed: %s", strerror(errno));
227 	}
228 
229 	len = aio_waitcomplete(&aiop, NULL);
230 	if (len < 0) {
231 		if (errno == EINTR) {
232 			if (aio_timedout) {
233 				aio_cleanup(ac);
234 				atf_tc_fail("aio_waitcomplete timed out");
235 			}
236 		}
237 		aio_cleanup(ac);
238 		atf_tc_fail("aio_waitcomplete failed: %s", strerror(errno));
239 	}
240 
241 	aio_timeout_stop();
242 
243 	if (len != ac->ac_buflen) {
244 		aio_cleanup(ac);
245 		atf_tc_fail("aio_waitcomplete short write (%jd)",
246 		    (intmax_t)len);
247 	}
248 }
249 
250 /*
251  * Perform a simple read test of our initialized data buffer from the
252  * provided file descriptor.
253  */
254 static void
255 aio_read_test(struct aio_context *ac)
256 {
257 	struct aiocb aio, *aiop;
258 	ssize_t len;
259 
260 	ATF_REQUIRE_KERNEL_MODULE("aio");
261 
262 	bzero(ac->ac_buffer, ac->ac_buflen);
263 	bzero(&aio, sizeof(aio));
264 	aio.aio_buf = ac->ac_buffer;
265 	aio.aio_nbytes = ac->ac_buflen;
266 	aio.aio_fildes = ac->ac_read_fd;
267 	aio.aio_offset = 0;
268 
269 	aio_timeout_start(ac->ac_seconds);
270 
271 	if (aio_read(&aio) < 0) {
272 		if (errno == EINTR) {
273 			if (aio_timedout) {
274 				aio_cleanup(ac);
275 				atf_tc_fail("aio_write timed out");
276 			}
277 		}
278 		aio_cleanup(ac);
279 		atf_tc_fail("aio_read failed: %s", strerror(errno));
280 	}
281 
282 	len = aio_waitcomplete(&aiop, NULL);
283 	if (len < 0) {
284 		if (errno == EINTR) {
285 			if (aio_timedout) {
286 				aio_cleanup(ac);
287 				atf_tc_fail("aio_waitcomplete timed out");
288 			}
289 		}
290 		aio_cleanup(ac);
291 		atf_tc_fail("aio_waitcomplete failed: %s", strerror(errno));
292 	}
293 
294 	aio_timeout_stop();
295 
296 	if (len != ac->ac_buflen) {
297 		aio_cleanup(ac);
298 		atf_tc_fail("aio_waitcomplete short read (%jd)",
299 		    (intmax_t)len);
300 	}
301 
302 	if (aio_test_buffer(ac->ac_buffer, ac->ac_buflen, ac->ac_seed) == 0) {
303 		aio_cleanup(ac);
304 		atf_tc_fail("buffer mismatched");
305 	}
306 }
307 
308 /*
309  * Series of type-specific tests for AIO.  For now, we just make sure we can
310  * issue a write and then a read to each type.  We assume that once a write
311  * is issued, a read can follow.
312  */
313 
314 /*
315  * Test with a classic file.  Assumes we can create a moderate size temporary
316  * file.
317  */
318 struct aio_file_arg {
319 	int	 afa_fd;
320 	char	*afa_pathname;
321 };
322 
323 static void
324 aio_file_cleanup(void *arg)
325 {
326 	struct aio_file_arg *afa;
327 
328 	afa = arg;
329 	close(afa->afa_fd);
330 	unlink(afa->afa_pathname);
331 }
332 
333 #define	FILE_LEN	GLOBAL_MAX
334 #define	FILE_TIMEOUT	30
335 ATF_TC_WITHOUT_HEAD(aio_file_test);
336 ATF_TC_BODY(aio_file_test, tc)
337 {
338 	char pathname[PATH_MAX];
339 	struct aio_file_arg arg;
340 	struct aio_context ac;
341 	int fd;
342 
343 	ATF_REQUIRE_KERNEL_MODULE("aio");
344 	ATF_REQUIRE_UNSAFE_AIO();
345 
346 	strcpy(pathname, PATH_TEMPLATE);
347 	fd = mkstemp(pathname);
348 	ATF_REQUIRE_MSG(fd != -1, "mkstemp failed: %s", strerror(errno));
349 
350 	arg.afa_fd = fd;
351 	arg.afa_pathname = pathname;
352 
353 	aio_context_init(&ac, fd, fd, FILE_LEN,
354 	    FILE_TIMEOUT, aio_file_cleanup, &arg);
355 	aio_write_test(&ac);
356 	aio_read_test(&ac);
357 
358 	aio_file_cleanup(&arg);
359 }
360 
361 struct aio_fifo_arg {
362 	int	 afa_read_fd;
363 	int	 afa_write_fd;
364 	char	*afa_pathname;
365 };
366 
367 static void
368 aio_fifo_cleanup(void *arg)
369 {
370 	struct aio_fifo_arg *afa;
371 
372 	afa = arg;
373 	if (afa->afa_read_fd != -1)
374 		close(afa->afa_read_fd);
375 	if (afa->afa_write_fd != -1)
376 		close(afa->afa_write_fd);
377 	unlink(afa->afa_pathname);
378 }
379 
380 #define	FIFO_LEN	256
381 #define	FIFO_TIMEOUT	30
382 ATF_TC_WITHOUT_HEAD(aio_fifo_test);
383 ATF_TC_BODY(aio_fifo_test, tc)
384 {
385 	int error, read_fd = -1, write_fd = -1;
386 	struct aio_fifo_arg arg;
387 	char pathname[PATH_MAX];
388 	struct aio_context ac;
389 
390 	ATF_REQUIRE_KERNEL_MODULE("aio");
391 	ATF_REQUIRE_UNSAFE_AIO();
392 
393 	/*
394 	 * In theory, mkstemp() can return a name that is then collided with.
395 	 * Because this is a regression test, we treat that as a test failure
396 	 * rather than retrying.
397 	 */
398 	strcpy(pathname, PATH_TEMPLATE);
399 	ATF_REQUIRE_MSG(mkstemp(pathname) != -1,
400 	    "mkstemp failed: %s", strerror(errno));
401 	ATF_REQUIRE_MSG(unlink(pathname) == 0,
402 	    "unlink failed: %s", strerror(errno));
403 	ATF_REQUIRE_MSG(mkfifo(pathname, 0600) != -1,
404 	    "mkfifo failed: %s", strerror(errno));
405 	arg.afa_pathname = pathname;
406 	arg.afa_read_fd = -1;
407 	arg.afa_write_fd = -1;
408 
409 	read_fd = open(pathname, O_RDONLY | O_NONBLOCK);
410 	if (read_fd == -1) {
411 		error = errno;
412 		aio_fifo_cleanup(&arg);
413 		errno = error;
414 		atf_tc_fail("read_fd open failed: %s",
415 		    strerror(errno));
416 	}
417 	arg.afa_read_fd = read_fd;
418 
419 	write_fd = open(pathname, O_WRONLY);
420 	if (write_fd == -1) {
421 		error = errno;
422 		aio_fifo_cleanup(&arg);
423 		errno = error;
424 		atf_tc_fail("write_fd open failed: %s",
425 		    strerror(errno));
426 	}
427 	arg.afa_write_fd = write_fd;
428 
429 	aio_context_init(&ac, read_fd, write_fd, FIFO_LEN,
430 	    FIFO_TIMEOUT, aio_fifo_cleanup, &arg);
431 	aio_write_test(&ac);
432 	aio_read_test(&ac);
433 
434 	aio_fifo_cleanup(&arg);
435 }
436 
437 struct aio_unix_socketpair_arg {
438 	int	asa_sockets[2];
439 };
440 
441 static void
442 aio_unix_socketpair_cleanup(void *arg)
443 {
444 	struct aio_unix_socketpair_arg *asa;
445 
446 	asa = arg;
447 	close(asa->asa_sockets[0]);
448 	close(asa->asa_sockets[1]);
449 }
450 
451 #define	UNIX_SOCKETPAIR_LEN	256
452 #define	UNIX_SOCKETPAIR_TIMEOUT	30
453 ATF_TC_WITHOUT_HEAD(aio_unix_socketpair_test);
454 ATF_TC_BODY(aio_unix_socketpair_test, tc)
455 {
456 	struct aio_unix_socketpair_arg arg;
457 	struct aio_context ac;
458 	int sockets[2];
459 
460 	ATF_REQUIRE_KERNEL_MODULE("aio");
461 
462 	ATF_REQUIRE_MSG(socketpair(PF_UNIX, SOCK_STREAM, 0, sockets) != -1,
463 	    "socketpair failed: %s", strerror(errno));
464 
465 	arg.asa_sockets[0] = sockets[0];
466 	arg.asa_sockets[1] = sockets[1];
467 	aio_context_init(&ac, sockets[0],
468 	    sockets[1], UNIX_SOCKETPAIR_LEN, UNIX_SOCKETPAIR_TIMEOUT,
469 	    aio_unix_socketpair_cleanup, &arg);
470 	aio_write_test(&ac);
471 	aio_read_test(&ac);
472 
473 	aio_unix_socketpair_cleanup(&arg);
474 }
475 
476 struct aio_pty_arg {
477 	int	apa_read_fd;
478 	int	apa_write_fd;
479 };
480 
481 static void
482 aio_pty_cleanup(void *arg)
483 {
484 	struct aio_pty_arg *apa;
485 
486 	apa = arg;
487 	close(apa->apa_read_fd);
488 	close(apa->apa_write_fd);
489 };
490 
491 #define	PTY_LEN		256
492 #define	PTY_TIMEOUT	30
493 ATF_TC_WITHOUT_HEAD(aio_pty_test);
494 ATF_TC_BODY(aio_pty_test, tc)
495 {
496 	struct aio_pty_arg arg;
497 	struct aio_context ac;
498 	int read_fd, write_fd;
499 	struct termios ts;
500 	int error;
501 
502 	ATF_REQUIRE_KERNEL_MODULE("aio");
503 	ATF_REQUIRE_UNSAFE_AIO();
504 
505 	ATF_REQUIRE_MSG(openpty(&read_fd, &write_fd, NULL, NULL, NULL) == 0,
506 	    "openpty failed: %s", strerror(errno));
507 
508 	arg.apa_read_fd = read_fd;
509 	arg.apa_write_fd = write_fd;
510 
511 	if (tcgetattr(write_fd, &ts) < 0) {
512 		error = errno;
513 		aio_pty_cleanup(&arg);
514 		errno = error;
515 		atf_tc_fail("tcgetattr failed: %s", strerror(errno));
516 	}
517 	cfmakeraw(&ts);
518 	if (tcsetattr(write_fd, TCSANOW, &ts) < 0) {
519 		error = errno;
520 		aio_pty_cleanup(&arg);
521 		errno = error;
522 		atf_tc_fail("tcsetattr failed: %s", strerror(errno));
523 	}
524 	aio_context_init(&ac, read_fd, write_fd, PTY_LEN,
525 	    PTY_TIMEOUT, aio_pty_cleanup, &arg);
526 
527 	aio_write_test(&ac);
528 	aio_read_test(&ac);
529 
530 	aio_pty_cleanup(&arg);
531 }
532 
533 static void
534 aio_pipe_cleanup(void *arg)
535 {
536 	int *pipes = arg;
537 
538 	close(pipes[0]);
539 	close(pipes[1]);
540 }
541 
542 #define	PIPE_LEN	256
543 #define	PIPE_TIMEOUT	30
544 ATF_TC_WITHOUT_HEAD(aio_pipe_test);
545 ATF_TC_BODY(aio_pipe_test, tc)
546 {
547 	struct aio_context ac;
548 	int pipes[2];
549 
550 	ATF_REQUIRE_KERNEL_MODULE("aio");
551 	ATF_REQUIRE_UNSAFE_AIO();
552 
553 	ATF_REQUIRE_MSG(pipe(pipes) != -1,
554 	    "pipe failed: %s", strerror(errno));
555 
556 	aio_context_init(&ac, pipes[0], pipes[1], PIPE_LEN,
557 	    PIPE_TIMEOUT, aio_pipe_cleanup, pipes);
558 	aio_write_test(&ac);
559 	aio_read_test(&ac);
560 
561 	aio_pipe_cleanup(pipes);
562 }
563 
564 struct aio_md_arg {
565 	int	ama_mdctl_fd;
566 	int	ama_unit;
567 	int	ama_fd;
568 };
569 
570 static void
571 aio_md_cleanup(void *arg)
572 {
573 	struct aio_md_arg *ama;
574 	struct md_ioctl mdio;
575 	int error;
576 
577 	ama = arg;
578 
579 	if (ama->ama_fd != -1)
580 		close(ama->ama_fd);
581 
582 	if (ama->ama_unit != -1) {
583 		bzero(&mdio, sizeof(mdio));
584 		mdio.md_version = MDIOVERSION;
585 		mdio.md_unit = ama->ama_unit;
586 		if (ioctl(ama->ama_mdctl_fd, MDIOCDETACH, &mdio) == -1) {
587 			error = errno;
588 			close(ama->ama_mdctl_fd);
589 			errno = error;
590 			atf_tc_fail("ioctl MDIOCDETACH failed: %s",
591 			    strerror(errno));
592 		}
593 	}
594 
595 	close(ama->ama_mdctl_fd);
596 }
597 
598 #define	MD_LEN		GLOBAL_MAX
599 #define	MD_TIMEOUT	30
600 ATF_TC(aio_md_test);
601 ATF_TC_HEAD(aio_md_test, tc)
602 {
603 
604 	atf_tc_set_md_var(tc, "require.user", "root");
605 }
606 ATF_TC_BODY(aio_md_test, tc)
607 {
608 	int error, fd, mdctl_fd, unit;
609 	char pathname[PATH_MAX];
610 	struct aio_md_arg arg;
611 	struct aio_context ac;
612 	struct md_ioctl mdio;
613 
614 	ATF_REQUIRE_KERNEL_MODULE("aio");
615 
616 	mdctl_fd = open("/dev/" MDCTL_NAME, O_RDWR, 0);
617 	ATF_REQUIRE_MSG(mdctl_fd != -1,
618 	    "opening /dev/%s failed: %s", MDCTL_NAME, strerror(errno));
619 
620 	bzero(&mdio, sizeof(mdio));
621 	mdio.md_version = MDIOVERSION;
622 	mdio.md_type = MD_MALLOC;
623 	mdio.md_options = MD_AUTOUNIT | MD_COMPRESS;
624 	mdio.md_mediasize = GLOBAL_MAX;
625 	mdio.md_sectorsize = 512;
626 
627 	arg.ama_mdctl_fd = mdctl_fd;
628 	arg.ama_unit = -1;
629 	arg.ama_fd = -1;
630 	if (ioctl(mdctl_fd, MDIOCATTACH, &mdio) < 0) {
631 		error = errno;
632 		aio_md_cleanup(&arg);
633 		errno = error;
634 		atf_tc_fail("ioctl MDIOCATTACH failed: %s", strerror(errno));
635 	}
636 
637 	arg.ama_unit = unit = mdio.md_unit;
638 	snprintf(pathname, PATH_MAX, "/dev/md%d", unit);
639 	fd = open(pathname, O_RDWR);
640 	ATF_REQUIRE_MSG(fd != -1,
641 	    "opening %s failed: %s", pathname, strerror(errno));
642 	arg.ama_fd = fd;
643 
644 	aio_context_init(&ac, fd, fd, MD_LEN, MD_TIMEOUT,
645 	    aio_md_cleanup, &arg);
646 	aio_write_test(&ac);
647 	aio_read_test(&ac);
648 
649 	aio_md_cleanup(&arg);
650 }
651 
652 ATF_TP_ADD_TCS(tp)
653 {
654 
655 	ATF_TP_ADD_TC(tp, aio_file_test);
656 	ATF_TP_ADD_TC(tp, aio_fifo_test);
657 	ATF_TP_ADD_TC(tp, aio_unix_socketpair_test);
658 	ATF_TP_ADD_TC(tp, aio_pty_test);
659 	ATF_TP_ADD_TC(tp, aio_pipe_test);
660 	ATF_TP_ADD_TC(tp, aio_md_test);
661 
662 	return (atf_no_error());
663 }
664