aiotest.c revision 205224
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: head/tools/regression/aio/aiotest/aiotest.c 205224 2010-03-16 20:41:45Z kib $ 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/types.h> 42#include <sys/socket.h> 43#include <sys/stat.h> 44#include <sys/mdioctl.h> 45 46#include <aio.h> 47#include <err.h> 48#include <errno.h> 49#include <fcntl.h> 50#include <libutil.h> 51#include <limits.h> 52#include <stdio.h> 53#include <stdlib.h> 54#include <string.h> 55#include <termios.h> 56#include <unistd.h> 57 58#define PATH_TEMPLATE "/tmp/aio.XXXXXXXXXX" 59 60/* 61 * GLOBAL_MAX sets the largest usable buffer size to be read and written, as 62 * it sizes ac_buffer in the aio_context structure. It is also the default 63 * size for file I/O. For other types, we use smaller blocks or we risk 64 * blocking (and we run in a single process/thread so that would be bad). 65 */ 66#define GLOBAL_MAX 16384 67 68#define BUFFER_MAX GLOBAL_MAX 69struct aio_context { 70 const char *ac_test; 71 int ac_read_fd, ac_write_fd; 72 long ac_seed; 73 char ac_buffer[GLOBAL_MAX]; 74 int ac_buflen; 75 int ac_seconds; 76 void (*ac_cleanup)(void *arg); 77 void *ac_cleanup_arg; 78}; 79 80static int aio_timedout; 81static int aio_notpresent; 82 83/* 84 * Attempt to provide a cleaner failure mode in the event AIO support is not 85 * present by catching and reporting SIGSYS. 86 */ 87static void 88aio_sigsys(int sig) 89{ 90 91 aio_notpresent = 1; 92} 93 94static void 95aio_sigsys_setup(void) 96{ 97 98 if (signal(SIGSYS, aio_sigsys) == SIG_ERR) 99 errx(-1, "FAIL: signal(SIGSYS): %s", strerror(errno)); 100} 101 102/* 103 * Each test run specifies a timeout in seconds. Use the somewhat obsoleted 104 * signal(3) and alarm(3) APIs to set this up. 105 */ 106static void 107aio_timeout_signal(int sig) 108{ 109 110 aio_timedout = 1; 111} 112 113static void 114aio_timeout_start(const char *string1, const char *string2, int seconds) 115{ 116 117 aio_timedout = 0; 118 if (signal(SIGALRM, aio_timeout_signal) == SIG_ERR) 119 errx(-1, "FAIL: %s: %s: aio_timeout_set: signal(SIGALRM): %s", 120 string1, string2, strerror(errno)); 121 alarm(seconds); 122} 123 124static void 125aio_timeout_stop(const char *string1, const char *string2) 126{ 127 128 if (signal(SIGALRM, NULL) == SIG_ERR) 129 errx(-1, "FAIL: %s: %s: aio_timeout_stop: signal(NULL): %s", 130 string1, string2, strerror(errno)); 131 alarm(0); 132} 133 134/* 135 * Fill a buffer given a seed that can be fed into srandom() to initialize 136 * the PRNG in a repeatable manner. 137 */ 138static void 139aio_fill_buffer(char *buffer, int len, long seed) 140{ 141 char ch; 142 int i; 143 144 srandom(seed); 145 for (i = 0; i < len; i++) { 146 ch = random() & 0xff; 147 buffer[i] = ch; 148 } 149} 150 151/* 152 * Test that a buffer matches a given seed. See aio_fill_buffer(). Return 153 * (1) on a match, (0) on a mismatch. 154 */ 155static int 156aio_test_buffer(char *buffer, int len, long seed) 157{ 158 char ch; 159 int i; 160 161 srandom(seed); 162 for (i = 0; i < len; i++) { 163 ch = random() & 0xff; 164 if (buffer[i] != ch) 165 return (0); 166 } 167 return (1); 168} 169 170/* 171 * Initialize a testing context given the file descriptors provided by the 172 * test setup. 173 */ 174static void 175aio_context_init(struct aio_context *ac, const char *test, int read_fd, 176 int write_fd, int buflen, int seconds, void (*cleanup)(void *), 177 void *cleanup_arg) 178{ 179 180 if (buflen > BUFFER_MAX) 181 errx(-1, "FAIL: %s: aio_context_init: buffer too large", 182 test); 183 bzero(ac, sizeof(*ac)); 184 ac->ac_test = test; 185 ac->ac_read_fd = read_fd; 186 ac->ac_write_fd = write_fd; 187 ac->ac_buflen = buflen; 188 srandomdev(); 189 ac->ac_seed = random(); 190 aio_fill_buffer(ac->ac_buffer, buflen, ac->ac_seed); 191 if (aio_test_buffer(ac->ac_buffer, buflen, ac->ac_seed) == 0) 192 errx(-1, "%s: aio_context_init: aio_test_buffer: internal " 193 "error", test); 194 ac->ac_seconds = seconds; 195 ac->ac_cleanup = cleanup; 196 ac->ac_cleanup_arg = cleanup_arg; 197} 198 199/* 200 * Each tester can register a callback to clean up in the event the test 201 * fails. Preserve the value of errno so that subsequent calls to errx() 202 * work properly. 203 */ 204static void 205aio_cleanup(struct aio_context *ac) 206{ 207 int error; 208 209 if (ac->ac_cleanup == NULL) 210 return; 211 error = errno; 212 (ac->ac_cleanup)(ac->ac_cleanup_arg); 213 errno = error; 214} 215 216/* 217 * Perform a simple write test of our initialized data buffer to the provided 218 * file descriptor. 219 */ 220static void 221aio_write_test(struct aio_context *ac) 222{ 223 struct aiocb aio, *aiop; 224 ssize_t len; 225 int error; 226 227 bzero(&aio, sizeof(aio)); 228 aio.aio_buf = ac->ac_buffer; 229 aio.aio_nbytes = ac->ac_buflen; 230 aio.aio_fildes = ac->ac_write_fd; 231 aio.aio_offset = 0; 232 233 aio_timeout_start(ac->ac_test, "aio_write_test", ac->ac_seconds); 234 235 if (aio_write(&aio) < 0) { 236 if (errno == EINTR) { 237 if (aio_notpresent) 238 errno = EOPNOTSUPP; 239 if (aio_timedout) { 240 aio_cleanup(ac); 241 errx(-1, "FAIL: %s: aio_write_test: " 242 "aio_write: timed out", ac->ac_test); 243 } 244 } 245 aio_cleanup(ac); 246 errx(-1, "FAIL: %s: aio_write_test: aio_write: %s", 247 ac->ac_test, strerror(errno)); 248 } 249 250 len = aio_waitcomplete(&aiop, NULL); 251 if (len < 0) { 252 if (errno == EINTR) { 253 if (aio_notpresent) 254 errno = EOPNOTSUPP; 255 if (aio_timedout) { 256 aio_cleanup(ac); 257 errx(-1, "FAIL: %s: aio_write_test: " 258 "aio_waitcomplete: timed out", 259 ac->ac_test); 260 } 261 } 262 aio_cleanup(ac); 263 errx(-1, "FAIL: %s: aio_write_test: aio_waitcomplete: %s", 264 ac->ac_test, strerror(errno)); 265 } 266 267 aio_timeout_stop(ac->ac_test, "aio_write_test"); 268 269 if (len != ac->ac_buflen) { 270 aio_cleanup(ac); 271 errx(-1, "FAIL: %s: aio_write_test: aio_waitcomplete: short " 272 "write (%d)", ac->ac_test, len); 273 } 274} 275 276/* 277 * Perform a simple read test of our initialized data buffer from the 278 * provided file descriptor. 279 */ 280static void 281aio_read_test(struct aio_context *ac) 282{ 283 struct aiocb aio, *aiop; 284 ssize_t len; 285 286 bzero(ac->ac_buffer, ac->ac_buflen); 287 bzero(&aio, sizeof(aio)); 288 aio.aio_buf = ac->ac_buffer; 289 aio.aio_nbytes = ac->ac_buflen; 290 aio.aio_fildes = ac->ac_read_fd; 291 aio.aio_offset = 0; 292 293 aio_timeout_start(ac->ac_test, "aio_read_test", ac->ac_seconds); 294 295 if (aio_read(&aio) < 0) { 296 if (errno == EINTR) { 297 if (aio_notpresent) 298 errno = EOPNOTSUPP; 299 if (aio_timedout) { 300 aio_cleanup(ac); 301 errx(-1, "FAIL: %s: aio_read_test: " 302 "aio_read: timed out", ac->ac_test); 303 } 304 } 305 aio_cleanup(ac); 306 errx(-1, "FAIL: %s: aio_read_test: aio_read %s", ac->ac_test, 307 strerror(errno)); 308 } 309 310 len = aio_waitcomplete(&aiop, NULL); 311 if (len < 0) { 312 if (errno == EINTR) { 313 if (aio_notpresent) 314 errno = EOPNOTSUPP; 315 if (aio_timedout) { 316 aio_cleanup(ac); 317 errx(-1, "FAIL: %s: aio_read_test: " 318 "aio_waitcomplete: timed out", 319 ac->ac_test); 320 } 321 } 322 aio_cleanup(ac); 323 errx(-1, "FAIL: %s: aio_read_test: aio_waitcomplete: %s", 324 ac->ac_test, strerror(errno)); 325 } 326 327 aio_timeout_stop(ac->ac_test, "aio_read_test"); 328 329 if (len != ac->ac_buflen) { 330 aio_cleanup(ac); 331 errx(-1, "FAIL: %s: aio_read_test: aio_waitcomplete: short " 332 "read (%d)", ac->ac_test, len); 333 } 334 335 if (aio_test_buffer(ac->ac_buffer, ac->ac_buflen, ac->ac_seed) == 0) { 336 aio_cleanup(ac); 337 errx(-1, "FAIL: %s: aio_read_test: buffer mismatch", 338 ac->ac_test); 339 } 340} 341 342/* 343 * Series of type-specific tests for AIO. For now, we just make sure we can 344 * issue a write and then a read to each type. We assume that once a write 345 * is issued, a read can follow. 346 */ 347 348/* 349 * Test with a classic file. Assumes we can create a moderate size temporary 350 * file. 351 */ 352struct aio_file_arg { 353 int afa_fd; 354 char *afa_pathname; 355}; 356 357static void 358aio_file_cleanup(void *arg) 359{ 360 struct aio_file_arg *afa; 361 362 afa = arg; 363 close(afa->afa_fd); 364 unlink(afa->afa_pathname); 365} 366 367#define FILE_LEN GLOBAL_MAX 368#define FILE_TIMEOUT 30 369static int 370aio_file_test(void) 371{ 372 char pathname[PATH_MAX]; 373 struct aio_file_arg arg; 374 struct aio_context ac; 375 int fd; 376 377 strcpy(pathname, PATH_TEMPLATE); 378 fd = mkstemp(pathname); 379 if (fd == -1) 380 errx(-1, "FAIL: aio_file_test: mkstemp: %s", 381 strerror(errno)); 382 383 arg.afa_fd = fd; 384 arg.afa_pathname = pathname; 385 386 aio_context_init(&ac, "aio_file_test", fd, fd, FILE_LEN, 387 FILE_TIMEOUT, aio_file_cleanup, &arg); 388 aio_write_test(&ac); 389 aio_read_test(&ac); 390 391 aio_file_cleanup(&arg); 392 393 fprintf(stderr, "PASS: aio_file_test\n"); 394} 395 396struct aio_fifo_arg { 397 int afa_read_fd; 398 int afa_write_fd; 399 char *afa_pathname; 400}; 401 402static void 403aio_fifo_cleanup(void *arg) 404{ 405 struct aio_fifo_arg *afa; 406 407 afa = arg; 408 if (afa->afa_read_fd != -1) 409 close(afa->afa_read_fd); 410 if (afa->afa_write_fd != -1) 411 close(afa->afa_write_fd); 412 unlink(afa->afa_pathname); 413} 414 415#define FIFO_LEN 256 416#define FIFO_TIMEOUT 30 417static int 418aio_fifo_test(void) 419{ 420 int error, read_fd = -1, write_fd = -1; 421 struct aio_fifo_arg arg; 422 char pathname[PATH_MAX]; 423 struct aio_context ac; 424 425 /* 426 * In theory, mktemp() can return a name that is then collided with. 427 * Because this is a regression test, we treat that as a test failure 428 * rather than retrying. 429 */ 430 strcpy(pathname, PATH_TEMPLATE); 431 mktemp(pathname); 432 if (mkfifo(pathname, 0600) == -1) 433 errx(-1, "FAIL: aio_fifo_test: mkfifo: %s", strerror(errno)); 434 arg.afa_pathname = pathname; 435 arg.afa_read_fd = -1; 436 arg.afa_write_fd = -1; 437 438 read_fd = open(pathname, O_RDONLY | O_NONBLOCK); 439 if (read_fd == -1) { 440 error = errno; 441 aio_fifo_cleanup(&arg); 442 errno = error; 443 errx(-1, "FAIL: aio_fifo_test: read_fd open: %s", 444 strerror(errno)); 445 } 446 arg.afa_read_fd = read_fd; 447 448 write_fd = open(pathname, O_WRONLY); 449 if (write_fd == -1) { 450 error = errno; 451 aio_fifo_cleanup(&arg); 452 errno = error; 453 errx(-1, "FAIL: aio_fifo_test: write_fd open: %s", 454 strerror(errno)); 455 } 456 arg.afa_write_fd = write_fd; 457 458 aio_context_init(&ac, "aio_fifo_test", read_fd, write_fd, FIFO_LEN, 459 FIFO_TIMEOUT, aio_fifo_cleanup, &arg); 460 aio_write_test(&ac); 461 aio_read_test(&ac); 462 463 aio_fifo_cleanup(&arg); 464 465 fprintf(stderr, "PASS: aio_fifo_test\n"); 466} 467 468struct aio_unix_socketpair_arg { 469 int asa_sockets[2]; 470}; 471 472static void 473aio_unix_socketpair_cleanup(void *arg) 474{ 475 struct aio_unix_socketpair_arg *asa; 476 477 asa = arg; 478 close(asa->asa_sockets[0]); 479 close(asa->asa_sockets[1]); 480} 481 482#define UNIX_SOCKETPAIR_LEN 256 483#define UNIX_SOCKETPAIR_TIMEOUT 30 484static int 485aio_unix_socketpair_test(void) 486{ 487 struct aio_unix_socketpair_arg arg; 488 struct aio_context ac; 489 int sockets[2]; 490 491 if (socketpair(PF_UNIX, SOCK_STREAM, 0, sockets) < 0) 492 errx(-1, "FAIL: aio_socketpair_test: socketpair: %s", 493 strerror(errno)); 494 495 arg.asa_sockets[0] = sockets[0]; 496 arg.asa_sockets[1] = sockets[1]; 497 aio_context_init(&ac, "aio_unix_socketpair_test", sockets[0], 498 sockets[1], UNIX_SOCKETPAIR_LEN, UNIX_SOCKETPAIR_TIMEOUT, 499 aio_unix_socketpair_cleanup, &arg); 500 aio_write_test(&ac); 501 aio_read_test(&ac); 502 503 aio_unix_socketpair_cleanup(&arg); 504 505 fprintf(stderr, "PASS: aio_unix_socketpair_test\n"); 506} 507 508struct aio_pty_arg { 509 int apa_read_fd; 510 int apa_write_fd; 511}; 512 513static void 514aio_pty_cleanup(void *arg) 515{ 516 struct aio_pty_arg *apa; 517 518 close(apa->apa_read_fd); 519 close(apa->apa_write_fd); 520}; 521 522#define PTY_LEN 256 523#define PTY_TIMEOUT 30 524static int 525aio_pty_test(void) 526{ 527 struct aio_pty_arg arg; 528 struct aio_context ac; 529 int read_fd, write_fd; 530 struct termios ts; 531 int error; 532 533 if (openpty(&read_fd, &write_fd, NULL, NULL, NULL) < 0) 534 errx(-1, "FAIL: aio_pty_test: openpty: %s", strerror(errno)); 535 536 arg.apa_read_fd = read_fd; 537 arg.apa_write_fd = write_fd; 538 539 if (tcgetattr(write_fd, &ts) < 0) { 540 error = errno; 541 aio_pty_cleanup(&arg); 542 errno = error; 543 errx(-1, "FAIL: aio_pty_test: tcgetattr: %s", 544 strerror(errno)); 545 } 546 cfmakeraw(&ts); 547 if (tcsetattr(write_fd, TCSANOW, &ts) < 0) { 548 error = errno; 549 aio_pty_cleanup(&arg); 550 errno = error; 551 errx(-1, "FAIL: aio_pty_test: tcsetattr: %s", 552 strerror(errno)); 553 } 554 555 aio_context_init(&ac, "aio_pty_test", read_fd, write_fd, PTY_LEN, 556 PTY_TIMEOUT, aio_pty_cleanup, &arg); 557 aio_write_test(&ac); 558 aio_read_test(&ac); 559 560 aio_pty_cleanup(&arg); 561 562 fprintf(stderr, "PASS: aio_pty_test\n"); 563} 564 565static void 566aio_pipe_cleanup(void *arg) 567{ 568 int *pipes = arg; 569 570 close(pipes[0]); 571 close(pipes[1]); 572} 573 574#define PIPE_LEN 256 575#define PIPE_TIMEOUT 30 576static int 577aio_pipe_test(void) 578{ 579 struct aio_context ac; 580 int pipes[2]; 581 582 if (pipe(pipes) < 0) 583 errx(-1, "FAIL: aio_pipe_test: pipe: %s", strerror(errno)); 584 585 aio_context_init(&ac, "aio_file_test", pipes[0], pipes[1], PIPE_LEN, 586 PIPE_TIMEOUT, aio_pipe_cleanup, pipes); 587 aio_write_test(&ac); 588 aio_read_test(&ac); 589 590 aio_pipe_cleanup(pipes); 591 592 fprintf(stderr, "PASS: aio_pipe_test\n"); 593} 594 595struct aio_md_arg { 596 int ama_mdctl_fd; 597 int ama_unit; 598 int ama_fd; 599}; 600 601static void 602aio_md_cleanup(void *arg) 603{ 604 struct aio_md_arg *ama; 605 struct md_ioctl mdio; 606 int error; 607 608 ama = arg; 609 610 if (ama->ama_fd != -1) 611 close(ama->ama_fd); 612 613 if (ama->ama_unit != -1) { 614 bzero(&mdio, sizeof(mdio)); 615 mdio.md_version = MDIOVERSION; 616 mdio.md_unit = ama->ama_unit; 617 if (ioctl(ama->ama_mdctl_fd, MDIOCDETACH, &mdio) < 0) { 618 error = errno; 619 close(ama->ama_mdctl_fd); 620 errno = error; 621 warnx("FAIL: aio_md_test: MDIOCDETACH: %s", 622 strerror(errno)); 623 } 624 } 625 626 close(ama->ama_mdctl_fd); 627} 628 629#define MD_LEN GLOBAL_MAX 630#define MD_TIMEOUT 30 631static int 632aio_md_test(void) 633{ 634 int error, fd, i, mdctl_fd, unit; 635 char pathname[PATH_MAX]; 636 struct aio_md_arg arg; 637 struct aio_context ac; 638 struct md_ioctl mdio; 639 640 mdctl_fd = open("/dev/" MDCTL_NAME, O_RDWR, 0); 641 if (mdctl_fd < 0) 642 errx(-1, "FAIL: aio_md_test: open(/dev/%s): %s", MDCTL_NAME, 643 strerror(errno)); 644 645 bzero(&mdio, sizeof(mdio)); 646 mdio.md_version = MDIOVERSION; 647 mdio.md_type = MD_MALLOC; 648 mdio.md_options = MD_AUTOUNIT | MD_COMPRESS; 649 mdio.md_mediasize = GLOBAL_MAX; 650 mdio.md_sectorsize = 512; 651 652 arg.ama_mdctl_fd = mdctl_fd; 653 arg.ama_unit = -1; 654 arg.ama_fd = -1; 655 if (ioctl(mdctl_fd, MDIOCATTACH, &mdio) < 0) { 656 error = errno; 657 aio_md_cleanup(&arg); 658 errno = error; 659 errx(-1, "FAIL: aio_md_test: MDIOCATTACH: %s", 660 strerror(errno)); 661 } 662 663 arg.ama_unit = unit = mdio.md_unit; 664 snprintf(pathname, PATH_MAX, "/dev/md%d", unit); 665 fd = open(pathname, O_RDWR); 666 if (fd < 0) { 667 error = errno; 668 aio_md_cleanup(&arg); 669 errno = error; 670 errx(-1, "FAIL: aio_md_test: open(%s): %s", pathname, 671 strerror(errno)); 672 } 673 arg.ama_fd = fd; 674 675 aio_context_init(&ac, "aio_md_test", fd, fd, MD_LEN, MD_TIMEOUT, 676 aio_md_cleanup, &arg); 677 aio_write_test(&ac); 678 aio_read_test(&ac); 679 680 aio_md_cleanup(&arg); 681 682 fprintf(stderr, "PASS: aio_md_test\n"); 683} 684 685int 686main(int argc, char *argv[]) 687{ 688 689 aio_sigsys_setup(); 690 aio_file_test(); 691 aio_fifo_test(); 692 aio_unix_socketpair_test(); 693 aio_pty_test(); 694 aio_pipe_test(); 695 if (geteuid() == 0) 696 aio_md_test(); 697 else 698 fprintf(stderr, "WARNING: aio_md_test: skipped as euid " 699 "!= 0\n"); 700} 701