1142971Sambrisko/*-
2142971Sambrisko * Copyright (C) 2005 IronPort Systems, Inc. All rights reserved.
3142971Sambrisko *
4142971Sambrisko * Redistribution and use in source and binary forms, with or without
5142971Sambrisko * modification, are permitted provided that the following conditions
6142971Sambrisko * are met:
7142971Sambrisko * 1. Redistributions of source code must retain the above copyright
8142971Sambrisko *    notice, this list of conditions and the following disclaimer.
9142971Sambrisko * 2. Redistributions in binary form must reproduce the above copyright
10142971Sambrisko *    notice, this list of conditions and the following disclaimer in the
11142971Sambrisko *    documentation and/or other materials provided with the distribution.
12142971Sambrisko *
13142971Sambrisko * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14142971Sambrisko * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15142971Sambrisko * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16142971Sambrisko * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17142971Sambrisko * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18142971Sambrisko * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19142971Sambrisko * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20142971Sambrisko * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21142971Sambrisko * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22142971Sambrisko * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23142971Sambrisko * SUCH DAMAGE.
24142971Sambrisko *
25142971Sambrisko * $FreeBSD: stable/11/tests/sys/aio/aio_kqueue_test.c 326677 2017-12-08 05:20:54Z asomers $
26142971Sambrisko */
27142971Sambrisko
28142971Sambrisko/*
29218938Smiwi * Prerequisities:
30218938Smiwi * - AIO support must be compiled into the kernel (see sys/<arch>/NOTES for
31218938Smiwi *   more details).
32218938Smiwi *
33142971Sambrisko * Note: it is a good idea to run this against a physical drive to
34142971Sambrisko * exercise the physio fast path (ie. aio_kqueue /dev/<something safe>)
35142971Sambrisko */
36142971Sambrisko
37218938Smiwi#include <sys/types.h>
38218938Smiwi#include <sys/event.h>
39218938Smiwi#include <sys/time.h>
40142971Sambrisko#include <aio.h>
41218938Smiwi#include <err.h>
42218938Smiwi#include <errno.h>
43142971Sambrisko#include <fcntl.h>
44142971Sambrisko#include <stdlib.h>
45142971Sambrisko#include <stdio.h>
46218938Smiwi#include <string.h>
47142971Sambrisko#include <unistd.h>
48142971Sambrisko
49282136Sngie#include "freebsd_test_suite/macros.h"
50296277Sjhb#include "local.h"
51142971Sambrisko
52282136Sngie#define PATH_TEMPLATE   "aio.XXXXXXXXXX"
53282136Sngie
54142971Sambrisko#define MAX_RUNS 300
55142971Sambrisko/* #define DEBUG */
56142971Sambrisko
57218938Smiwiint
58218938Smiwimain (int argc, char *argv[])
59218938Smiwi{
60326677Sasomers	struct aiocb **iocb, *kq_iocb;
61282136Sngie	char *file, pathname[sizeof(PATH_TEMPLATE)+1];
62142971Sambrisko	struct kevent ke, kq_returned;
63142971Sambrisko	struct timespec ts;
64282136Sngie	char buffer[32768];
65326677Sasomers	int max_queue_per_proc;
66326677Sasomers	size_t max_queue_per_proc_size;
67292816Sngie#ifdef DEBUG
68292816Sngie	int cancel, error;
69292816Sngie#endif
70292816Sngie	int failed = 0, fd, kq, pending, result, run;
71282136Sngie	int tmp_file = 0;
72326677Sasomers	int i, j;
73142971Sambrisko
74282136Sngie	PLAIN_REQUIRE_KERNEL_MODULE("aio", 0);
75296277Sjhb	PLAIN_REQUIRE_UNSAFE_AIO(0);
76282136Sngie
77326677Sasomers	max_queue_per_proc_size = sizeof(max_queue_per_proc);
78326677Sasomers	if (sysctlbyname("vfs.aio.max_aio_queue_per_proc",
79326677Sasomers	    &max_queue_per_proc, &max_queue_per_proc_size, NULL, 0) != 0)
80326677Sasomers		err(1, "sysctlbyname");
81326677Sasomers	iocb = calloc(max_queue_per_proc, sizeof(struct aiocb*));
82326677Sasomers	if (iocb == NULL)
83326677Sasomers		err(1, "calloc");
84326677Sasomers
85282136Sngie	kq = kqueue();
86142971Sambrisko	if (kq < 0) {
87142971Sambrisko		perror("No kqeueue\n");
88142971Sambrisko		exit(1);
89142971Sambrisko	}
90142971Sambrisko
91142971Sambrisko	if (argc == 1) {
92142971Sambrisko		strcpy(pathname, PATH_TEMPLATE);
93142971Sambrisko		fd = mkstemp(pathname);
94142971Sambrisko		file = pathname;
95142971Sambrisko		tmp_file = 1;
96142971Sambrisko	} else {
97142971Sambrisko		file = argv[1];
98142971Sambrisko		fd = open(file, O_RDWR|O_CREAT, 0666);
99142971Sambrisko	}
100218938Smiwi	if (fd == -1)
101218938Smiwi		err(1, "Can't open %s\n", file);
102142971Sambrisko
103142971Sambrisko	for (run = 0; run < MAX_RUNS; run++){
104142971Sambrisko#ifdef DEBUG
105142971Sambrisko		printf("Run %d\n", run);
106142971Sambrisko#endif
107326677Sasomers		for (i = 0; i < max_queue_per_proc; i++) {
108218938Smiwi			iocb[i] = (struct aiocb *)calloc(1,
109218938Smiwi			    sizeof(struct aiocb));
110218938Smiwi			if (iocb[i] == NULL)
111218938Smiwi				err(1, "calloc");
112142971Sambrisko		}
113292816Sngie
114292816Sngie		pending = 0;
115326677Sasomers		for (i = 0; i < max_queue_per_proc; i++) {
116142971Sambrisko			pending++;
117142971Sambrisko			iocb[i]->aio_nbytes = sizeof(buffer);
118142971Sambrisko			iocb[i]->aio_buf = buffer;
119142971Sambrisko			iocb[i]->aio_fildes = fd;
120142971Sambrisko			iocb[i]->aio_offset = iocb[i]->aio_nbytes * i * run;
121292816Sngie
122142971Sambrisko			iocb[i]->aio_sigevent.sigev_notify_kqueue = kq;
123154668Sdavidxu			iocb[i]->aio_sigevent.sigev_value.sival_ptr = iocb[i];
124142971Sambrisko			iocb[i]->aio_sigevent.sigev_notify = SIGEV_KEVENT;
125292816Sngie
126142971Sambrisko			result = aio_write(iocb[i]);
127142971Sambrisko			if (result != 0) {
128142971Sambrisko				perror("aio_write");
129218938Smiwi				printf("Result %d iteration %d\n", result, i);
130142971Sambrisko				exit(1);
131142971Sambrisko			}
132142971Sambrisko#ifdef DEBUG
133142971Sambrisko			printf("WRITE %d is at %p\n", i, iocb[i]);
134142971Sambrisko#endif
135142971Sambrisko			result = rand();
136142971Sambrisko			if (result < RAND_MAX/32) {
137142971Sambrisko				if (result > RAND_MAX/64) {
138142971Sambrisko					result = aio_cancel(fd, iocb[i]);
139142971Sambrisko#ifdef DEBUG
140142971Sambrisko					printf("Cancel %d %p result %d\n", i, iocb[i], result);
141142971Sambrisko#endif
142142971Sambrisko					if (result == AIO_CANCELED) {
143142971Sambrisko						aio_return(iocb[i]);
144218938Smiwi						iocb[i] = NULL;
145142971Sambrisko						pending--;
146142971Sambrisko					}
147142971Sambrisko				}
148142971Sambrisko			}
149142971Sambrisko		}
150292816Sngie#ifdef DEBUG
151326677Sasomers		cancel = max_queue_per_proc - pending;
152292816Sngie#endif
153282136Sngie
154142971Sambrisko		i = 0;
155218938Smiwi		while (pending) {
156218938Smiwi
157218938Smiwi			for (;;) {
158218938Smiwi
159142971Sambrisko				bzero(&ke, sizeof(ke));
160142971Sambrisko				bzero(&kq_returned, sizeof(ke));
161142971Sambrisko				ts.tv_sec = 0;
162142971Sambrisko				ts.tv_nsec = 1;
163292816Sngie				result = kevent(kq, NULL, 0,
164142971Sambrisko						&kq_returned, 1, &ts);
165292816Sngie#ifdef DEBUG
166142971Sambrisko				error = errno;
167292816Sngie#endif
168218938Smiwi				if (result < 0)
169142971Sambrisko					perror("kevent error: ");
170142971Sambrisko				kq_iocb = kq_returned.udata;
171142971Sambrisko#ifdef DEBUG
172142971Sambrisko				printf("kevent %d %d errno %d return.ident %p "
173292816Sngie				       "return.data %p return.udata %p %p\n",
174292816Sngie				       i, result, error,
175319218Sasomers				       (void*)kq_returned.ident,
176319218Sasomers				       (void*)kq_returned.data,
177292816Sngie				       kq_returned.udata,
178142971Sambrisko				       kq_iocb);
179142971Sambrisko#endif
180292816Sngie
181218938Smiwi				if (kq_iocb)
182142971Sambrisko					break;
183142971Sambrisko#ifdef DEBUG
184319218Sasomers				printf("Try again left %d out of %lu %d\n",
185326677Sasomers				    pending, max_queue_per_proc, cancel);
186142971Sambrisko#endif
187292816Sngie			}
188292816Sngie
189326677Sasomers			for (j = 0; j < max_queue_per_proc && iocb[j] != kq_iocb;
190218938Smiwi			   j++) ;
191142971Sambrisko#ifdef DEBUG
192142971Sambrisko			printf("kq_iocb %p\n", kq_iocb);
193292816Sngie
194218938Smiwi			printf("Error Result for %d is %d pending %d\n",
195218938Smiwi			    j, result, pending);
196142971Sambrisko#endif
197142971Sambrisko			result = aio_return(kq_iocb);
198142971Sambrisko#ifdef DEBUG
199218938Smiwi			printf("Return Result for %d is %d\n\n", j, result);
200142971Sambrisko#endif
201142971Sambrisko			if (result != sizeof(buffer)) {
202218938Smiwi				printf("FAIL: run %d, operation %d, result %d "
203280895Sngie				    " (errno=%d) should be %zu\n", run, pending,
204218938Smiwi				    result, errno, sizeof(buffer));
205218938Smiwi				failed++;
206218938Smiwi			} else
207218938Smiwi				printf("PASS: run %d, left %d\n", run,
208218938Smiwi				    pending - 1);
209142971Sambrisko
210142971Sambrisko			free(kq_iocb);
211142971Sambrisko			iocb[j] = NULL;
212142971Sambrisko			pending--;
213142971Sambrisko			i++;
214292816Sngie		}
215218938Smiwi
216326677Sasomers		for (i = 0; i < max_queue_per_proc; i++)
217218938Smiwi			free(iocb[i]);
218218938Smiwi
219142971Sambrisko	}
220142971Sambrisko
221218938Smiwi	if (tmp_file)
222142971Sambrisko		unlink(pathname);
223142971Sambrisko
224218938Smiwi	if (failed != 0)
225218938Smiwi		printf("FAIL: %d tests failed\n", failed);
226218938Smiwi	else
227218938Smiwi		printf("PASS: All tests passed\n");
228218938Smiwi
229218938Smiwi	exit (failed == 0 ? 0 : 1);
230142971Sambrisko}
231