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