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