aio_kqueue_test.c revision 218938
1/*-
2 * Copyright (C) 2005 IronPort Systems, Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 *
25 * $FreeBSD: head/tools/regression/aio/kqueue/aio_kqueue.c 218938 2011-02-22 05:13:26Z miwi $
26 */
27
28/*
29 * Prerequisities:
30 * - AIO support must be compiled into the kernel (see sys/<arch>/NOTES for
31 *   more details).
32 *
33 * Note: it is a good idea to run this against a physical drive to
34 * exercise the physio fast path (ie. aio_kqueue /dev/<something safe>)
35 */
36
37#include <sys/types.h>
38#include <sys/event.h>
39#include <sys/time.h>
40#include <aio.h>
41#include <err.h>
42#include <errno.h>
43#include <fcntl.h>
44#include <stdlib.h>
45#include <stdio.h>
46#include <string.h>
47#include <unistd.h>
48
49#define PATH_TEMPLATE   "/tmp/aio.XXXXXXXXXX"
50
51#define MAX 128
52#define MAX_RUNS 300
53/* #define DEBUG */
54
55int
56main (int argc, char *argv[])
57{
58	int fd;
59	struct aiocb *iocb[MAX], *kq_iocb;
60	int i, result, run, error, j;
61	char buffer[32768];
62	int kq = kqueue();
63	struct kevent ke, kq_returned;
64	struct timespec ts;
65	int cancel, pending, tmp_file = 0, failed = 0;
66	char *file, pathname[sizeof(PATH_TEMPLATE)+1];
67
68	if (kq < 0) {
69		perror("No kqeueue\n");
70		exit(1);
71	}
72
73	if (argc == 1) {
74		strcpy(pathname, PATH_TEMPLATE);
75		fd = mkstemp(pathname);
76		file = pathname;
77		tmp_file = 1;
78	} else {
79		file = argv[1];
80		fd = open(file, O_RDWR|O_CREAT, 0666);
81	}
82	if (fd == -1)
83		err(1, "Can't open %s\n", file);
84
85	for (run = 0; run < MAX_RUNS; run++){
86#ifdef DEBUG
87		printf("Run %d\n", run);
88#endif
89		for (i = 0; i < MAX; i++) {
90			iocb[i] = (struct aiocb *)calloc(1,
91			    sizeof(struct aiocb));
92			if (iocb[i] == NULL)
93				err(1, "calloc");
94		}
95
96		pending = 0;
97		for (i = 0; i < MAX; i++) {
98			pending++;
99			iocb[i]->aio_nbytes = sizeof(buffer);
100			iocb[i]->aio_buf = buffer;
101			iocb[i]->aio_fildes = fd;
102			iocb[i]->aio_offset = iocb[i]->aio_nbytes * i * run;
103
104			iocb[i]->aio_sigevent.sigev_notify_kqueue = kq;
105			iocb[i]->aio_sigevent.sigev_value.sival_ptr = iocb[i];
106			iocb[i]->aio_sigevent.sigev_notify = SIGEV_KEVENT;
107
108			result = aio_write(iocb[i]);
109			if (result != 0) {
110				perror("aio_write");
111				printf("Result %d iteration %d\n", result, i);
112				exit(1);
113			}
114#ifdef DEBUG
115			printf("WRITE %d is at %p\n", i, iocb[i]);
116#endif
117			result = rand();
118			if (result < RAND_MAX/32) {
119				if (result > RAND_MAX/64) {
120					result = aio_cancel(fd, iocb[i]);
121#ifdef DEBUG
122					printf("Cancel %d %p result %d\n", i, iocb[i], result);
123#endif
124					if (result == AIO_CANCELED) {
125						aio_return(iocb[i]);
126						iocb[i] = NULL;
127						pending--;
128					}
129				}
130			}
131		}
132		cancel = MAX - pending;
133
134		i = 0;
135		while (pending) {
136
137			for (;;) {
138
139				bzero(&ke, sizeof(ke));
140				bzero(&kq_returned, sizeof(ke));
141				ts.tv_sec = 0;
142				ts.tv_nsec = 1;
143				result = kevent(kq, NULL, 0,
144						&kq_returned, 1, &ts);
145				error = errno;
146				if (result < 0)
147					perror("kevent error: ");
148				kq_iocb = kq_returned.udata;
149#ifdef DEBUG
150				printf("kevent %d %d errno %d return.ident %p "
151				       "return.data %p return.udata %p %p\n",
152				       i, result, error,
153				       kq_returned.ident, kq_returned.data,
154				       kq_returned.udata,
155				       kq_iocb);
156#endif
157
158				if (kq_iocb)
159					break;
160#ifdef DEBUG
161				printf("Try again left %d out of %d %d\n",
162				    pending, MAX, cancel);
163#endif
164			}
165
166			for (j = 0; j < MAX && iocb[j] != kq_iocb;
167			   j++) ;
168#ifdef DEBUG
169			printf("kq_iocb %p\n", kq_iocb);
170
171			printf("Error Result for %d is %d pending %d\n",
172			    j, result, pending);
173#endif
174			result = aio_return(kq_iocb);
175#ifdef DEBUG
176			printf("Return Result for %d is %d\n\n", j, result);
177#endif
178			if (result != sizeof(buffer)) {
179				printf("FAIL: run %d, operation %d, result %d "
180				    " (errno=%d) should be %d\n", run, pending,
181				    result, errno, sizeof(buffer));
182				failed++;
183			} else
184				printf("PASS: run %d, left %d\n", run,
185				    pending - 1);
186
187			free(kq_iocb);
188			iocb[j] = NULL;
189			pending--;
190			i++;
191		}
192
193		for (i = 0; i < MAX; i++)
194			free(iocb[i]);
195
196	}
197
198	if (tmp_file)
199		unlink(pathname);
200
201	if (failed != 0)
202		printf("FAIL: %d tests failed\n", failed);
203	else
204		printf("PASS: All tests passed\n");
205
206	exit (failed == 0 ? 0 : 1);
207}
208