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