]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - tools/regression/aio/aiotest/aiotest.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.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/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
69 struct 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
80 static int      aio_timedout;
81 static 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  */
87 static void
88 aio_sigsys(int sig)
89 {
90
91         aio_notpresent = 1;
92 }
93
94 static void
95 aio_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  */
106 static void
107 aio_timeout_signal(int sig)
108 {
109
110         aio_timedout = 1;
111 }
112
113 static void
114 aio_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
124 static void
125 aio_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  */
138 static void
139 aio_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  */
155 static int
156 aio_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  */
174 static void
175 aio_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  */
204 static void
205 aio_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  */
220 static void
221 aio_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  */
280 static void
281 aio_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  */
352 struct aio_file_arg {
353         int      afa_fd;
354         char    *afa_pathname;
355 };
356
357 static void
358 aio_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
369 static int
370 aio_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
396 struct aio_fifo_arg {
397         int      afa_read_fd;
398         int      afa_write_fd;
399         char    *afa_pathname;
400 };
401
402 static void
403 aio_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
417 static int
418 aio_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
468 struct aio_unix_socketpair_arg {
469         int     asa_sockets[2];
470 };
471
472 static void
473 aio_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
484 static int
485 aio_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
508 struct aio_pty_arg {
509         int     apa_read_fd;
510         int     apa_write_fd;
511 };
512
513 static void
514 aio_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
524 static int
525 aio_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
565 static void
566 aio_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
576 static int
577 aio_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
595 struct aio_md_arg {
596         int     ama_mdctl_fd;
597         int     ama_unit;
598         int     ama_fd;
599 };
600
601 static void
602 aio_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
631 static int
632 aio_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
685 int
686 main(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 }