1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2005 David Xu <davidxu@freebsd.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice unmodified, this list of conditions, and the following
12 *    disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * $FreeBSD$
29 *
30 */
31
32#include <sys/cdefs.h>
33#include <sys/types.h>
34#include <sys/syscall.h>
35#include <sys/aio.h>
36
37#include "namespace.h"
38#include <errno.h>
39#include <stddef.h>
40#include <signal.h>
41#include "sigev_thread.h"
42#include "un-namespace.h"
43
44__weak_reference(__aio_read, aio_read);
45__weak_reference(__aio_write, aio_write);
46__weak_reference(__aio_return, aio_return);
47__weak_reference(__aio_waitcomplete, aio_waitcomplete);
48__weak_reference(__aio_fsync, aio_fsync);
49__weak_reference(__lio_listio, lio_listio);
50
51typedef void (*aio_func)(union sigval val, struct aiocb *iocb);
52
53extern int __sys_aio_read(struct aiocb *iocb);
54extern int __sys_aio_write(struct aiocb *iocb);
55extern ssize_t __sys_aio_waitcomplete(struct aiocb **iocbp, struct timespec *timeout);
56extern ssize_t __sys_aio_return(struct aiocb *iocb);
57extern int __sys_aio_error(struct aiocb *iocb);
58extern int __sys_aio_fsync(int op, struct aiocb *iocb);
59extern int __sys_lio_listio(int mode, struct aiocb * const list[], int nent,
60    struct sigevent *sig);
61
62static void
63aio_dispatch(struct sigev_node *sn)
64{
65	aio_func f = sn->sn_func;
66
67	f(sn->sn_value, (struct aiocb *)sn->sn_id);
68}
69
70static int
71aio_sigev_alloc(sigev_id_t id, struct sigevent *sigevent,
72    struct sigev_node **sn, struct sigevent *saved_ev)
73{
74	if (__sigev_check_init()) {
75		/* This might be that thread library is not enabled. */
76		errno = EINVAL;
77		return (-1);
78	}
79
80	*sn = __sigev_alloc(SI_ASYNCIO, sigevent, NULL, 1);
81	if (*sn == NULL) {
82		errno = EAGAIN;
83		return (-1);
84	}
85
86	*saved_ev = *sigevent;
87	(*sn)->sn_id = id;
88	__sigev_get_sigevent(*sn, sigevent, (*sn)->sn_id);
89	(*sn)->sn_dispatch = aio_dispatch;
90
91	__sigev_list_lock();
92	__sigev_register(*sn);
93	__sigev_list_unlock();
94
95	return (0);
96}
97
98static int
99aio_io(struct aiocb *iocb, int (*sysfunc)(struct aiocb *iocb))
100{
101	struct sigev_node *sn;
102	struct sigevent saved_ev;
103	int ret, err;
104
105	if (iocb->aio_sigevent.sigev_notify != SIGEV_THREAD) {
106		ret = sysfunc(iocb);
107		return (ret);
108	}
109
110	ret = aio_sigev_alloc((sigev_id_t)iocb, &iocb->aio_sigevent, &sn,
111			      &saved_ev);
112	if (ret)
113		return (ret);
114	ret = sysfunc(iocb);
115	iocb->aio_sigevent = saved_ev;
116	if (ret != 0) {
117		err = errno;
118		__sigev_list_lock();
119		__sigev_delete_node(sn);
120		__sigev_list_unlock();
121		errno = err;
122	}
123	return (ret);
124}
125
126int
127__aio_read(struct aiocb *iocb)
128{
129
130	return aio_io(iocb, &__sys_aio_read);
131}
132
133int
134__aio_write(struct aiocb *iocb)
135{
136
137	return aio_io(iocb, &__sys_aio_write);
138}
139
140ssize_t
141__aio_waitcomplete(struct aiocb **iocbp, struct timespec *timeout)
142{
143	ssize_t ret;
144	int err;
145
146	ret = __sys_aio_waitcomplete(iocbp, timeout);
147	if (*iocbp) {
148		if ((*iocbp)->aio_sigevent.sigev_notify == SIGEV_THREAD) {
149			err = errno;
150			__sigev_list_lock();
151			__sigev_delete(SI_ASYNCIO, (sigev_id_t)(*iocbp));
152			__sigev_list_unlock();
153			errno = err;
154		}
155	}
156
157	return (ret);
158}
159
160ssize_t
161__aio_return(struct aiocb *iocb)
162{
163
164	if (iocb->aio_sigevent.sigev_notify == SIGEV_THREAD) {
165		if (__sys_aio_error(iocb) == EINPROGRESS) {
166			/*
167			 * Fail with EINVAL to match the semantics of
168			 * __sys_aio_return() for an in-progress
169			 * request.
170			 */
171			errno = EINVAL;
172			return (-1);
173		}
174		__sigev_list_lock();
175		__sigev_delete(SI_ASYNCIO, (sigev_id_t)iocb);
176		__sigev_list_unlock();
177	}
178
179	return __sys_aio_return(iocb);
180}
181
182int
183__aio_fsync(int op, struct aiocb *iocb)
184{
185	struct sigev_node *sn;
186	struct sigevent saved_ev;
187	int ret, err;
188
189	if (iocb->aio_sigevent.sigev_notify != SIGEV_THREAD)
190		return __sys_aio_fsync(op, iocb);
191
192	ret = aio_sigev_alloc((sigev_id_t)iocb, &iocb->aio_sigevent, &sn,
193			      &saved_ev);
194	if (ret)
195		return (ret);
196	ret = __sys_aio_fsync(op, iocb);
197	iocb->aio_sigevent = saved_ev;
198	if (ret != 0) {
199		err = errno;
200		__sigev_list_lock();
201		__sigev_delete_node(sn);
202		__sigev_list_unlock();
203		errno = err;
204	}
205	return (ret);
206}
207
208int
209__lio_listio(int mode, struct aiocb * const list[], int nent,
210    struct sigevent *sig)
211{
212	struct sigev_node *sn;
213	struct sigevent saved_ev;
214	int ret, err;
215
216	if (sig == NULL || sig->sigev_notify != SIGEV_THREAD)
217		return (__sys_lio_listio(mode, list, nent, sig));
218
219	ret = aio_sigev_alloc((sigev_id_t)list, sig, &sn, &saved_ev);
220	if (ret)
221		return (ret);
222	ret = __sys_lio_listio(mode, list, nent, sig);
223	*sig = saved_ev;
224	if (ret != 0) {
225		err = errno;
226		__sigev_list_lock();
227		__sigev_delete_node(sn);
228		__sigev_list_unlock();
229		errno = err;
230	}
231	return (ret);
232}
233