2 * Copyright (c) 2004 Robert N. M. Watson
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
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.
41 #include <sys/param.h>
42 #include <sys/module.h>
43 #include <sys/socket.h>
45 #include <sys/mdioctl.h>
60 #define PATH_TEMPLATE "/tmp/aio.XXXXXXXXXX"
63 * GLOBAL_MAX sets the largest usable buffer size to be read and written, as
64 * it sizes ac_buffer in the aio_context structure. It is also the default
65 * size for file I/O. For other types, we use smaller blocks or we risk
66 * blocking (and we run in a single process/thread so that would be bad).
68 #define GLOBAL_MAX 16384
70 #define BUFFER_MAX GLOBAL_MAX
73 int ac_read_fd, ac_write_fd;
75 char ac_buffer[GLOBAL_MAX];
78 void (*ac_cleanup)(void *arg);
82 static int aio_timedout;
88 if (modfind("aio") == -1)
90 "aio support not available in the kernel; skipping "
95 * Each test run specifies a timeout in seconds. Use the somewhat obsoleted
96 * signal(3) and alarm(3) APIs to set this up.
99 aio_timeout_signal(int sig __unused)
106 aio_timeout_start(const char *string1, const char *string2, int seconds)
110 if (signal(SIGALRM, aio_timeout_signal) == SIG_ERR)
111 errx(1, "FAIL: %s: %s: aio_timeout_set: signal(SIGALRM): %s",
112 string1, string2, strerror(errno));
117 aio_timeout_stop(const char *string1, const char *string2)
120 if (signal(SIGALRM, NULL) == SIG_ERR)
121 errx(1, "FAIL: %s: %s: aio_timeout_stop: signal(NULL): %s",
122 string1, string2, strerror(errno));
127 * Fill a buffer given a seed that can be fed into srandom() to initialize
128 * the PRNG in a repeatable manner.
131 aio_fill_buffer(char *buffer, int len, long seed)
137 for (i = 0; i < len; i++) {
138 ch = random() & 0xff;
144 * Test that a buffer matches a given seed. See aio_fill_buffer(). Return
145 * (1) on a match, (0) on a mismatch.
148 aio_test_buffer(char *buffer, int len, long seed)
154 for (i = 0; i < len; i++) {
155 ch = random() & 0xff;
163 * Initialize a testing context given the file descriptors provided by the
167 aio_context_init(struct aio_context *ac, const char *test, int read_fd,
168 int write_fd, int buflen, int seconds, void (*cleanup)(void *),
172 if (buflen > BUFFER_MAX)
173 errx(1, "FAIL: %s: aio_context_init: buffer too large",
175 bzero(ac, sizeof(*ac));
177 ac->ac_read_fd = read_fd;
178 ac->ac_write_fd = write_fd;
179 ac->ac_buflen = buflen;
181 ac->ac_seed = random();
182 aio_fill_buffer(ac->ac_buffer, buflen, ac->ac_seed);
183 if (aio_test_buffer(ac->ac_buffer, buflen, ac->ac_seed) == 0)
184 errx(1, "%s: aio_context_init: aio_test_buffer: internal "
186 ac->ac_seconds = seconds;
187 ac->ac_cleanup = cleanup;
188 ac->ac_cleanup_arg = cleanup_arg;
192 * Each tester can register a callback to clean up in the event the test
193 * fails. Preserve the value of errno so that subsequent calls to errx()
197 aio_cleanup(struct aio_context *ac)
201 if (ac->ac_cleanup == NULL)
204 (ac->ac_cleanup)(ac->ac_cleanup_arg);
209 * Perform a simple write test of our initialized data buffer to the provided
213 aio_write_test(struct aio_context *ac)
215 struct aiocb aio, *aiop;
220 bzero(&aio, sizeof(aio));
221 aio.aio_buf = ac->ac_buffer;
222 aio.aio_nbytes = ac->ac_buflen;
223 aio.aio_fildes = ac->ac_write_fd;
226 aio_timeout_start(ac->ac_test, "aio_write_test", ac->ac_seconds);
228 if (aio_write(&aio) < 0) {
229 if (errno == EINTR) {
232 errx(1, "FAIL: %s: aio_write_test: "
233 "aio_write: timed out", ac->ac_test);
237 errx(1, "FAIL: %s: aio_write_test: aio_write: %s",
238 ac->ac_test, strerror(errno));
241 len = aio_waitcomplete(&aiop, NULL);
243 if (errno == EINTR) {
246 errx(1, "FAIL: %s: aio_write_test: "
247 "aio_waitcomplete: timed out",
252 errx(1, "FAIL: %s: aio_write_test: aio_waitcomplete: %s",
253 ac->ac_test, strerror(errno));
256 aio_timeout_stop(ac->ac_test, "aio_write_test");
258 if (len != ac->ac_buflen) {
260 errx(1, "FAIL: %s: aio_write_test: aio_waitcomplete: short "
261 "write (%jd)", ac->ac_test, (intmax_t)len);
266 * Perform a simple read test of our initialized data buffer from the
267 * provided file descriptor.
270 aio_read_test(struct aio_context *ac)
272 struct aiocb aio, *aiop;
277 bzero(ac->ac_buffer, ac->ac_buflen);
278 bzero(&aio, sizeof(aio));
279 aio.aio_buf = ac->ac_buffer;
280 aio.aio_nbytes = ac->ac_buflen;
281 aio.aio_fildes = ac->ac_read_fd;
284 aio_timeout_start(ac->ac_test, "aio_read_test", ac->ac_seconds);
286 if (aio_read(&aio) < 0) {
287 if (errno == EINTR) {
290 errx(1, "FAIL: %s: aio_read_test: "
291 "aio_read: timed out", ac->ac_test);
295 errx(1, "FAIL: %s: aio_read_test: aio_read %s", ac->ac_test,
299 len = aio_waitcomplete(&aiop, NULL);
301 if (errno == EINTR) {
304 errx(1, "FAIL: %s: aio_read_test: "
305 "aio_waitcomplete: timed out",
310 errx(1, "FAIL: %s: aio_read_test: aio_waitcomplete: %s",
311 ac->ac_test, strerror(errno));
314 aio_timeout_stop(ac->ac_test, "aio_read_test");
316 if (len != ac->ac_buflen) {
318 errx(1, "FAIL: %s: aio_read_test: aio_waitcomplete: short "
319 "read (%jd)", ac->ac_test, (intmax_t)len);
322 if (aio_test_buffer(ac->ac_buffer, ac->ac_buflen, ac->ac_seed) == 0) {
324 errx(1, "FAIL: %s: aio_read_test: buffer mismatch",
330 * Series of type-specific tests for AIO. For now, we just make sure we can
331 * issue a write and then a read to each type. We assume that once a write
332 * is issued, a read can follow.
336 * Test with a classic file. Assumes we can create a moderate size temporary
339 struct aio_file_arg {
345 aio_file_cleanup(void *arg)
347 struct aio_file_arg *afa;
351 unlink(afa->afa_pathname);
354 #define FILE_LEN GLOBAL_MAX
355 #define FILE_TIMEOUT 30
359 char pathname[PATH_MAX];
360 struct aio_file_arg arg;
361 struct aio_context ac;
366 strcpy(pathname, PATH_TEMPLATE);
367 fd = mkstemp(pathname);
369 errx(1, "FAIL: aio_file_test: mkstemp: %s",
373 arg.afa_pathname = pathname;
375 aio_context_init(&ac, "aio_file_test", fd, fd, FILE_LEN,
376 FILE_TIMEOUT, aio_file_cleanup, &arg);
380 aio_file_cleanup(&arg);
382 fprintf(stderr, "PASS: aio_file_test\n");
385 struct aio_fifo_arg {
392 aio_fifo_cleanup(void *arg)
394 struct aio_fifo_arg *afa;
397 if (afa->afa_read_fd != -1)
398 close(afa->afa_read_fd);
399 if (afa->afa_write_fd != -1)
400 close(afa->afa_write_fd);
401 unlink(afa->afa_pathname);
405 #define FIFO_TIMEOUT 30
409 int error, read_fd = -1, write_fd = -1;
410 struct aio_fifo_arg arg;
411 char pathname[PATH_MAX];
412 struct aio_context ac;
417 * In theory, mkstemp() can return a name that is then collided with.
418 * Because this is a regression test, we treat that as a test failure
419 * rather than retrying.
421 strcpy(pathname, PATH_TEMPLATE);
422 if (mkstemp(pathname) == -1)
423 err(1, "FAIL: aio_fifo_test: mkstemp failed");
424 if (unlink(pathname) == -1)
425 err(1, "FAIL: aio_fifo_test: unlink failed");
426 if (mkfifo(pathname, 0600) == -1)
427 errx(1, "FAIL: aio_fifo_test: mkfifo: %s", strerror(errno));
428 arg.afa_pathname = pathname;
429 arg.afa_read_fd = -1;
430 arg.afa_write_fd = -1;
432 read_fd = open(pathname, O_RDONLY | O_NONBLOCK);
435 aio_fifo_cleanup(&arg);
437 errx(1, "FAIL: aio_fifo_test: read_fd open: %s",
440 arg.afa_read_fd = read_fd;
442 write_fd = open(pathname, O_WRONLY);
443 if (write_fd == -1) {
445 aio_fifo_cleanup(&arg);
447 errx(1, "FAIL: aio_fifo_test: write_fd open: %s",
450 arg.afa_write_fd = write_fd;
452 aio_context_init(&ac, "aio_fifo_test", read_fd, write_fd, FIFO_LEN,
453 FIFO_TIMEOUT, aio_fifo_cleanup, &arg);
457 aio_fifo_cleanup(&arg);
459 fprintf(stderr, "PASS: aio_fifo_test\n");
462 struct aio_unix_socketpair_arg {
467 aio_unix_socketpair_cleanup(void *arg)
469 struct aio_unix_socketpair_arg *asa;
472 close(asa->asa_sockets[0]);
473 close(asa->asa_sockets[1]);
476 #define UNIX_SOCKETPAIR_LEN 256
477 #define UNIX_SOCKETPAIR_TIMEOUT 30
479 aio_unix_socketpair_test(void)
481 struct aio_unix_socketpair_arg arg;
482 struct aio_context ac;
487 if (socketpair(PF_UNIX, SOCK_STREAM, 0, sockets) < 0)
488 errx(1, "FAIL: aio_socketpair_test: socketpair: %s",
491 arg.asa_sockets[0] = sockets[0];
492 arg.asa_sockets[1] = sockets[1];
493 aio_context_init(&ac, "aio_unix_socketpair_test", sockets[0],
494 sockets[1], UNIX_SOCKETPAIR_LEN, UNIX_SOCKETPAIR_TIMEOUT,
495 aio_unix_socketpair_cleanup, &arg);
499 aio_unix_socketpair_cleanup(&arg);
501 fprintf(stderr, "PASS: aio_unix_socketpair_test\n");
510 aio_pty_cleanup(void *arg)
512 struct aio_pty_arg *apa;
515 close(apa->apa_read_fd);
516 close(apa->apa_write_fd);
520 #define PTY_TIMEOUT 30
524 struct aio_pty_arg arg;
525 struct aio_context ac;
526 int read_fd, write_fd;
532 if (openpty(&read_fd, &write_fd, NULL, NULL, NULL) < 0)
533 errx(1, "FAIL: aio_pty_test: openpty: %s", strerror(errno));
535 arg.apa_read_fd = read_fd;
536 arg.apa_write_fd = write_fd;
538 if (tcgetattr(write_fd, &ts) < 0) {
540 aio_pty_cleanup(&arg);
542 errx(1, "FAIL: aio_pty_test: tcgetattr: %s",
546 if (tcsetattr(write_fd, TCSANOW, &ts) < 0) {
548 aio_pty_cleanup(&arg);
550 errx(1, "FAIL: aio_pty_test: tcsetattr: %s",
554 aio_context_init(&ac, "aio_pty_test", read_fd, write_fd, PTY_LEN,
555 PTY_TIMEOUT, aio_pty_cleanup, &arg);
559 aio_pty_cleanup(&arg);
561 fprintf(stderr, "PASS: aio_pty_test\n");
565 aio_pipe_cleanup(void *arg)
574 #define PIPE_TIMEOUT 30
578 struct aio_context ac;
584 errx(1, "FAIL: aio_pipe_test: pipe: %s", strerror(errno));
586 aio_context_init(&ac, "aio_file_test", pipes[0], pipes[1], PIPE_LEN,
587 PIPE_TIMEOUT, aio_pipe_cleanup, pipes);
591 aio_pipe_cleanup(pipes);
593 fprintf(stderr, "PASS: aio_pipe_test\n");
603 aio_md_cleanup(void *arg)
605 struct aio_md_arg *ama;
606 struct md_ioctl mdio;
611 if (ama->ama_fd != -1)
614 if (ama->ama_unit != -1) {
615 bzero(&mdio, sizeof(mdio));
616 mdio.md_version = MDIOVERSION;
617 mdio.md_unit = ama->ama_unit;
618 if (ioctl(ama->ama_mdctl_fd, MDIOCDETACH, &mdio) < 0) {
620 close(ama->ama_mdctl_fd);
622 warnx("FAIL: aio_md_test: MDIOCDETACH: %s",
627 close(ama->ama_mdctl_fd);
630 #define MD_LEN GLOBAL_MAX
631 #define MD_TIMEOUT 30
635 int error, fd, mdctl_fd, unit;
636 char pathname[PATH_MAX];
637 struct aio_md_arg arg;
638 struct aio_context ac;
639 struct md_ioctl mdio;
643 if (geteuid() != 0) {
644 fprintf(stderr, "WARNING: aio_md_test: skipped as euid "
649 mdctl_fd = open("/dev/" MDCTL_NAME, O_RDWR, 0);
651 errx(1, "FAIL: aio_md_test: open(/dev/%s): %s", MDCTL_NAME,
654 bzero(&mdio, sizeof(mdio));
655 mdio.md_version = MDIOVERSION;
656 mdio.md_type = MD_MALLOC;
657 mdio.md_options = MD_AUTOUNIT | MD_COMPRESS;
658 mdio.md_mediasize = GLOBAL_MAX;
659 mdio.md_sectorsize = 512;
661 arg.ama_mdctl_fd = mdctl_fd;
664 if (ioctl(mdctl_fd, MDIOCATTACH, &mdio) < 0) {
666 aio_md_cleanup(&arg);
668 errx(1, "FAIL: aio_md_test: MDIOCATTACH: %s",
672 arg.ama_unit = unit = mdio.md_unit;
673 snprintf(pathname, PATH_MAX, "/dev/md%d", unit);
674 fd = open(pathname, O_RDWR);
677 aio_md_cleanup(&arg);
679 errx(1, "FAIL: aio_md_test: open(%s): %s", pathname,
684 aio_context_init(&ac, "aio_md_test", fd, fd, MD_LEN, MD_TIMEOUT,
685 aio_md_cleanup, &arg);
689 aio_md_cleanup(&arg);
691 fprintf(stderr, "PASS: aio_md_test\n");
700 aio_unix_socketpair_test();