xref: /freebsd/lib/librt/aio.c (revision 3904e7966eb353c636c6aa638a6fdf1489ee514c)
15e53a4f9SPedro F. Giffuni /*-
25e53a4f9SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
35e53a4f9SPedro F. Giffuni  *
44acaec8fSDavid Xu  * Copyright (c) 2005 David Xu <davidxu@freebsd.org>
54acaec8fSDavid Xu  * All rights reserved.
64acaec8fSDavid Xu  *
74acaec8fSDavid Xu  * Redistribution and use in source and binary forms, with or without
84acaec8fSDavid Xu  * modification, are permitted provided that the following conditions
94acaec8fSDavid Xu  * are met:
104acaec8fSDavid Xu  * 1. Redistributions of source code must retain the above copyright
114acaec8fSDavid Xu  *    notice unmodified, this list of conditions, and the following
124acaec8fSDavid Xu  *    disclaimer.
134acaec8fSDavid Xu  * 2. Redistributions in binary form must reproduce the above copyright
144acaec8fSDavid Xu  *    notice, this list of conditions and the following disclaimer in the
154acaec8fSDavid Xu  *    documentation and/or other materials provided with the distribution.
164acaec8fSDavid Xu  *
174acaec8fSDavid Xu  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
184acaec8fSDavid Xu  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
194acaec8fSDavid Xu  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
204acaec8fSDavid Xu  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
214acaec8fSDavid Xu  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
224acaec8fSDavid Xu  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
234acaec8fSDavid Xu  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
244acaec8fSDavid Xu  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
254acaec8fSDavid Xu  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
264acaec8fSDavid Xu  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
274acaec8fSDavid Xu  *
284acaec8fSDavid Xu  * $FreeBSD$
294acaec8fSDavid Xu  *
304acaec8fSDavid Xu  */
314acaec8fSDavid Xu 
324acaec8fSDavid Xu #include <sys/cdefs.h>
334acaec8fSDavid Xu #include <sys/types.h>
344acaec8fSDavid Xu #include <sys/syscall.h>
354acaec8fSDavid Xu #include <sys/aio.h>
364acaec8fSDavid Xu 
374acaec8fSDavid Xu #include "namespace.h"
384acaec8fSDavid Xu #include <errno.h>
394acaec8fSDavid Xu #include <stddef.h>
404acaec8fSDavid Xu #include <signal.h>
414acaec8fSDavid Xu #include "sigev_thread.h"
424acaec8fSDavid Xu #include "un-namespace.h"
434acaec8fSDavid Xu 
444acaec8fSDavid Xu __weak_reference(__aio_read, aio_read);
45*3904e796SThomas Munro __weak_reference(__aio_readv, aio_readv);
464acaec8fSDavid Xu __weak_reference(__aio_write, aio_write);
47*3904e796SThomas Munro __weak_reference(__aio_writev, aio_writev);
484acaec8fSDavid Xu __weak_reference(__aio_return, aio_return);
494acaec8fSDavid Xu __weak_reference(__aio_waitcomplete, aio_waitcomplete);
503c778728SDavid Xu __weak_reference(__aio_fsync, aio_fsync);
5118ddf67cSAlan Somers __weak_reference(__lio_listio, lio_listio);
524acaec8fSDavid Xu 
534acaec8fSDavid Xu typedef void (*aio_func)(union sigval val, struct aiocb *iocb);
544acaec8fSDavid Xu 
554acaec8fSDavid Xu extern int __sys_aio_read(struct aiocb *iocb);
56*3904e796SThomas Munro extern int __sys_aio_readv(struct aiocb *iocb);
574acaec8fSDavid Xu extern int __sys_aio_write(struct aiocb *iocb);
58*3904e796SThomas Munro extern int __sys_aio_writev(struct aiocb *iocb);
5976793d4dSJohn Baldwin extern ssize_t __sys_aio_waitcomplete(struct aiocb **iocbp, struct timespec *timeout);
6076793d4dSJohn Baldwin extern ssize_t __sys_aio_return(struct aiocb *iocb);
614acaec8fSDavid Xu extern int __sys_aio_error(struct aiocb *iocb);
623c778728SDavid Xu extern int __sys_aio_fsync(int op, struct aiocb *iocb);
6318ddf67cSAlan Somers extern int __sys_lio_listio(int mode, struct aiocb * const list[], int nent,
6418ddf67cSAlan Somers     struct sigevent *sig);
654acaec8fSDavid Xu 
664acaec8fSDavid Xu static void
676348ace8SDavid Xu aio_dispatch(struct sigev_node *sn)
684acaec8fSDavid Xu {
694acaec8fSDavid Xu 	aio_func f = sn->sn_func;
704acaec8fSDavid Xu 
714acaec8fSDavid Xu 	f(sn->sn_value, (struct aiocb *)sn->sn_id);
724acaec8fSDavid Xu }
734acaec8fSDavid Xu 
744acaec8fSDavid Xu static int
7518ddf67cSAlan Somers aio_sigev_alloc(sigev_id_t id, struct sigevent *sigevent,
7618ddf67cSAlan Somers     struct sigev_node **sn, struct sigevent *saved_ev)
773c778728SDavid Xu {
783c778728SDavid Xu 	if (__sigev_check_init()) {
793c778728SDavid Xu 		/* This might be that thread library is not enabled. */
803c778728SDavid Xu 		errno = EINVAL;
813c778728SDavid Xu 		return (-1);
823c778728SDavid Xu 	}
833c778728SDavid Xu 
8418ddf67cSAlan Somers 	*sn = __sigev_alloc(SI_ASYNCIO, sigevent, NULL, 1);
853c778728SDavid Xu 	if (*sn == NULL) {
863c778728SDavid Xu 		errno = EAGAIN;
873c778728SDavid Xu 		return (-1);
883c778728SDavid Xu 	}
893c778728SDavid Xu 
9018ddf67cSAlan Somers 	*saved_ev = *sigevent;
9118ddf67cSAlan Somers 	(*sn)->sn_id = id;
9218ddf67cSAlan Somers 	__sigev_get_sigevent(*sn, sigevent, (*sn)->sn_id);
933c778728SDavid Xu 	(*sn)->sn_dispatch = aio_dispatch;
943c778728SDavid Xu 
953c778728SDavid Xu 	__sigev_list_lock();
963c778728SDavid Xu 	__sigev_register(*sn);
973c778728SDavid Xu 	__sigev_list_unlock();
983c778728SDavid Xu 
993c778728SDavid Xu 	return (0);
1003c778728SDavid Xu }
1013c778728SDavid Xu 
1023c778728SDavid Xu static int
1034acaec8fSDavid Xu aio_io(struct aiocb *iocb, int (*sysfunc)(struct aiocb *iocb))
1044acaec8fSDavid Xu {
1054acaec8fSDavid Xu 	struct sigev_node *sn;
1064acaec8fSDavid Xu 	struct sigevent saved_ev;
1071b4610feSDavid Xu 	int ret, err;
1084acaec8fSDavid Xu 
1094acaec8fSDavid Xu 	if (iocb->aio_sigevent.sigev_notify != SIGEV_THREAD) {
1104acaec8fSDavid Xu 		ret = sysfunc(iocb);
1114acaec8fSDavid Xu 		return (ret);
1124acaec8fSDavid Xu 	}
1134acaec8fSDavid Xu 
11418ddf67cSAlan Somers 	ret = aio_sigev_alloc((sigev_id_t)iocb, &iocb->aio_sigevent, &sn,
11518ddf67cSAlan Somers 			      &saved_ev);
1163c778728SDavid Xu 	if (ret)
1173c778728SDavid Xu 		return (ret);
1184acaec8fSDavid Xu 	ret = sysfunc(iocb);
1194acaec8fSDavid Xu 	iocb->aio_sigevent = saved_ev;
1204acaec8fSDavid Xu 	if (ret != 0) {
1211b4610feSDavid Xu 		err = errno;
1224acaec8fSDavid Xu 		__sigev_list_lock();
1234acaec8fSDavid Xu 		__sigev_delete_node(sn);
1244acaec8fSDavid Xu 		__sigev_list_unlock();
1251b4610feSDavid Xu 		errno = err;
1264acaec8fSDavid Xu 	}
1274acaec8fSDavid Xu 	return (ret);
1284acaec8fSDavid Xu }
1294acaec8fSDavid Xu 
1304acaec8fSDavid Xu int
1314acaec8fSDavid Xu __aio_read(struct aiocb *iocb)
1324acaec8fSDavid Xu {
1331b4610feSDavid Xu 
1344acaec8fSDavid Xu 	return aio_io(iocb, &__sys_aio_read);
1354acaec8fSDavid Xu }
1364acaec8fSDavid Xu 
1374acaec8fSDavid Xu int
138*3904e796SThomas Munro __aio_readv(struct aiocb *iocb)
139*3904e796SThomas Munro {
140*3904e796SThomas Munro 
141*3904e796SThomas Munro 	return aio_io(iocb, &__sys_aio_readv);
142*3904e796SThomas Munro }
143*3904e796SThomas Munro 
144*3904e796SThomas Munro int
1454acaec8fSDavid Xu __aio_write(struct aiocb *iocb)
1464acaec8fSDavid Xu {
1471b4610feSDavid Xu 
1484acaec8fSDavid Xu 	return aio_io(iocb, &__sys_aio_write);
1494acaec8fSDavid Xu }
1504acaec8fSDavid Xu 
151*3904e796SThomas Munro int
152*3904e796SThomas Munro __aio_writev(struct aiocb *iocb)
153*3904e796SThomas Munro {
154*3904e796SThomas Munro 
155*3904e796SThomas Munro 	return aio_io(iocb, &__sys_aio_writev);
156*3904e796SThomas Munro }
157*3904e796SThomas Munro 
15876793d4dSJohn Baldwin ssize_t
1594acaec8fSDavid Xu __aio_waitcomplete(struct aiocb **iocbp, struct timespec *timeout)
1604acaec8fSDavid Xu {
16176793d4dSJohn Baldwin 	ssize_t ret;
1621b4610feSDavid Xu 	int err;
1634acaec8fSDavid Xu 
16476793d4dSJohn Baldwin 	ret = __sys_aio_waitcomplete(iocbp, timeout);
1654acaec8fSDavid Xu 	if (*iocbp) {
1664acaec8fSDavid Xu 		if ((*iocbp)->aio_sigevent.sigev_notify == SIGEV_THREAD) {
1671b4610feSDavid Xu 			err = errno;
1684acaec8fSDavid Xu 			__sigev_list_lock();
1694acaec8fSDavid Xu 			__sigev_delete(SI_ASYNCIO, (sigev_id_t)(*iocbp));
1704acaec8fSDavid Xu 			__sigev_list_unlock();
1711b4610feSDavid Xu 			errno = err;
1724acaec8fSDavid Xu 		}
1734acaec8fSDavid Xu 	}
1744acaec8fSDavid Xu 
1754acaec8fSDavid Xu 	return (ret);
1764acaec8fSDavid Xu }
1774acaec8fSDavid Xu 
17876793d4dSJohn Baldwin ssize_t
1794acaec8fSDavid Xu __aio_return(struct aiocb *iocb)
1804acaec8fSDavid Xu {
1814acaec8fSDavid Xu 
1821b4610feSDavid Xu 	if (iocb->aio_sigevent.sigev_notify == SIGEV_THREAD) {
18376793d4dSJohn Baldwin 		if (__sys_aio_error(iocb) == EINPROGRESS) {
18476793d4dSJohn Baldwin 			/*
18576793d4dSJohn Baldwin 			 * Fail with EINVAL to match the semantics of
18676793d4dSJohn Baldwin 			 * __sys_aio_return() for an in-progress
18776793d4dSJohn Baldwin 			 * request.
18876793d4dSJohn Baldwin 			 */
18976793d4dSJohn Baldwin 			errno = EINVAL;
19076793d4dSJohn Baldwin 			return (-1);
19176793d4dSJohn Baldwin 		}
1924acaec8fSDavid Xu 		__sigev_list_lock();
1934acaec8fSDavid Xu 		__sigev_delete(SI_ASYNCIO, (sigev_id_t)iocb);
1944acaec8fSDavid Xu 		__sigev_list_unlock();
1954acaec8fSDavid Xu 	}
1964acaec8fSDavid Xu 
1971b4610feSDavid Xu 	return __sys_aio_return(iocb);
1984acaec8fSDavid Xu }
1993c778728SDavid Xu 
2003c778728SDavid Xu int
2013c778728SDavid Xu __aio_fsync(int op, struct aiocb *iocb)
2023c778728SDavid Xu {
2033c778728SDavid Xu 	struct sigev_node *sn;
2043c778728SDavid Xu 	struct sigevent saved_ev;
2053c778728SDavid Xu 	int ret, err;
2063c778728SDavid Xu 
2073c778728SDavid Xu 	if (iocb->aio_sigevent.sigev_notify != SIGEV_THREAD)
2083c778728SDavid Xu 		return __sys_aio_fsync(op, iocb);
2093c778728SDavid Xu 
21018ddf67cSAlan Somers 	ret = aio_sigev_alloc((sigev_id_t)iocb, &iocb->aio_sigevent, &sn,
21118ddf67cSAlan Somers 			      &saved_ev);
2123c778728SDavid Xu 	if (ret)
2133c778728SDavid Xu 		return (ret);
2143c778728SDavid Xu 	ret = __sys_aio_fsync(op, iocb);
2153c778728SDavid Xu 	iocb->aio_sigevent = saved_ev;
2163c778728SDavid Xu 	if (ret != 0) {
2173c778728SDavid Xu 		err = errno;
2183c778728SDavid Xu 		__sigev_list_lock();
2193c778728SDavid Xu 		__sigev_delete_node(sn);
2203c778728SDavid Xu 		__sigev_list_unlock();
2213c778728SDavid Xu 		errno = err;
2223c778728SDavid Xu 	}
2233c778728SDavid Xu 	return (ret);
2243c778728SDavid Xu }
22518ddf67cSAlan Somers 
22618ddf67cSAlan Somers int
22718ddf67cSAlan Somers __lio_listio(int mode, struct aiocb * const list[], int nent,
22818ddf67cSAlan Somers     struct sigevent *sig)
22918ddf67cSAlan Somers {
23018ddf67cSAlan Somers 	struct sigev_node *sn;
23118ddf67cSAlan Somers 	struct sigevent saved_ev;
23218ddf67cSAlan Somers 	int ret, err;
23318ddf67cSAlan Somers 
23418ddf67cSAlan Somers 	if (sig == NULL || sig->sigev_notify != SIGEV_THREAD)
23518ddf67cSAlan Somers 		return (__sys_lio_listio(mode, list, nent, sig));
23618ddf67cSAlan Somers 
23718ddf67cSAlan Somers 	ret = aio_sigev_alloc((sigev_id_t)list, sig, &sn, &saved_ev);
23818ddf67cSAlan Somers 	if (ret)
23918ddf67cSAlan Somers 		return (ret);
24018ddf67cSAlan Somers 	ret = __sys_lio_listio(mode, list, nent, sig);
24118ddf67cSAlan Somers 	*sig = saved_ev;
24218ddf67cSAlan Somers 	if (ret != 0) {
24318ddf67cSAlan Somers 		err = errno;
24418ddf67cSAlan Somers 		__sigev_list_lock();
24518ddf67cSAlan Somers 		__sigev_delete_node(sn);
24618ddf67cSAlan Somers 		__sigev_list_unlock();
24718ddf67cSAlan Somers 		errno = err;
24818ddf67cSAlan Somers 	}
24918ddf67cSAlan Somers 	return (ret);
25018ddf67cSAlan Somers }
251