xref: /freebsd/lib/librt/aio.c (revision a2f733abcff64628b7771a47089628b7327a88bd)
15e53a4f9SPedro F. Giffuni /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
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  */
294acaec8fSDavid Xu 
304acaec8fSDavid Xu #include <sys/types.h>
314acaec8fSDavid Xu #include <sys/syscall.h>
324acaec8fSDavid Xu #include <sys/aio.h>
334acaec8fSDavid Xu 
344acaec8fSDavid Xu #include "namespace.h"
354acaec8fSDavid Xu #include <errno.h>
364acaec8fSDavid Xu #include <stddef.h>
374acaec8fSDavid Xu #include <signal.h>
384acaec8fSDavid Xu #include "sigev_thread.h"
394acaec8fSDavid Xu #include "un-namespace.h"
404acaec8fSDavid Xu 
414acaec8fSDavid Xu __weak_reference(__aio_read, aio_read);
423904e796SThomas Munro __weak_reference(__aio_readv, aio_readv);
434acaec8fSDavid Xu __weak_reference(__aio_write, aio_write);
443904e796SThomas Munro __weak_reference(__aio_writev, aio_writev);
454acaec8fSDavid Xu __weak_reference(__aio_return, aio_return);
464acaec8fSDavid Xu __weak_reference(__aio_waitcomplete, aio_waitcomplete);
473c778728SDavid Xu __weak_reference(__aio_fsync, aio_fsync);
4818ddf67cSAlan Somers __weak_reference(__lio_listio, lio_listio);
494acaec8fSDavid Xu 
504acaec8fSDavid Xu typedef void (*aio_func)(union sigval val, struct aiocb *iocb);
514acaec8fSDavid Xu 
524acaec8fSDavid Xu extern int __sys_aio_read(struct aiocb *iocb);
533904e796SThomas Munro extern int __sys_aio_readv(struct aiocb *iocb);
544acaec8fSDavid Xu extern int __sys_aio_write(struct aiocb *iocb);
553904e796SThomas Munro extern int __sys_aio_writev(struct aiocb *iocb);
5676793d4dSJohn Baldwin extern ssize_t __sys_aio_waitcomplete(struct aiocb **iocbp, struct timespec *timeout);
5776793d4dSJohn Baldwin extern ssize_t __sys_aio_return(struct aiocb *iocb);
584acaec8fSDavid Xu extern int __sys_aio_error(struct aiocb *iocb);
593c778728SDavid Xu extern int __sys_aio_fsync(int op, struct aiocb *iocb);
6018ddf67cSAlan Somers extern int __sys_lio_listio(int mode, struct aiocb * const list[], int nent,
6118ddf67cSAlan Somers     struct sigevent *sig);
624acaec8fSDavid Xu 
634acaec8fSDavid Xu static void
aio_dispatch(struct sigev_node * sn)646348ace8SDavid Xu aio_dispatch(struct sigev_node *sn)
654acaec8fSDavid Xu {
664acaec8fSDavid Xu 	aio_func f = sn->sn_func;
674acaec8fSDavid Xu 
684acaec8fSDavid Xu 	f(sn->sn_value, (struct aiocb *)sn->sn_id);
694acaec8fSDavid Xu }
704acaec8fSDavid Xu 
714acaec8fSDavid Xu static int
aio_sigev_alloc(sigev_id_t id,struct sigevent * sigevent,struct sigev_node ** sn,struct sigevent * saved_ev)7218ddf67cSAlan Somers aio_sigev_alloc(sigev_id_t id, struct sigevent *sigevent,
7318ddf67cSAlan Somers     struct sigev_node **sn, struct sigevent *saved_ev)
743c778728SDavid Xu {
753c778728SDavid Xu 	if (__sigev_check_init()) {
763c778728SDavid Xu 		/* This might be that thread library is not enabled. */
773c778728SDavid Xu 		errno = EINVAL;
783c778728SDavid Xu 		return (-1);
793c778728SDavid Xu 	}
803c778728SDavid Xu 
8118ddf67cSAlan Somers 	*sn = __sigev_alloc(SI_ASYNCIO, sigevent, NULL, 1);
823c778728SDavid Xu 	if (*sn == NULL) {
833c778728SDavid Xu 		errno = EAGAIN;
843c778728SDavid Xu 		return (-1);
853c778728SDavid Xu 	}
863c778728SDavid Xu 
8718ddf67cSAlan Somers 	*saved_ev = *sigevent;
8818ddf67cSAlan Somers 	(*sn)->sn_id = id;
8918ddf67cSAlan Somers 	__sigev_get_sigevent(*sn, sigevent, (*sn)->sn_id);
903c778728SDavid Xu 	(*sn)->sn_dispatch = aio_dispatch;
913c778728SDavid Xu 
923c778728SDavid Xu 	__sigev_list_lock();
933c778728SDavid Xu 	__sigev_register(*sn);
943c778728SDavid Xu 	__sigev_list_unlock();
953c778728SDavid Xu 
963c778728SDavid Xu 	return (0);
973c778728SDavid Xu }
983c778728SDavid Xu 
993c778728SDavid Xu static int
aio_io(struct aiocb * iocb,int (* sysfunc)(struct aiocb * iocb))1004acaec8fSDavid Xu aio_io(struct aiocb *iocb, int (*sysfunc)(struct aiocb *iocb))
1014acaec8fSDavid Xu {
1024acaec8fSDavid Xu 	struct sigev_node *sn;
1034acaec8fSDavid Xu 	struct sigevent saved_ev;
1041b4610feSDavid Xu 	int ret, err;
1054acaec8fSDavid Xu 
1064acaec8fSDavid Xu 	if (iocb->aio_sigevent.sigev_notify != SIGEV_THREAD) {
1074acaec8fSDavid Xu 		ret = sysfunc(iocb);
1084acaec8fSDavid Xu 		return (ret);
1094acaec8fSDavid Xu 	}
1104acaec8fSDavid Xu 
11118ddf67cSAlan Somers 	ret = aio_sigev_alloc((sigev_id_t)iocb, &iocb->aio_sigevent, &sn,
11218ddf67cSAlan Somers 			      &saved_ev);
1133c778728SDavid Xu 	if (ret)
1143c778728SDavid Xu 		return (ret);
1154acaec8fSDavid Xu 	ret = sysfunc(iocb);
1164acaec8fSDavid Xu 	iocb->aio_sigevent = saved_ev;
1174acaec8fSDavid Xu 	if (ret != 0) {
1181b4610feSDavid Xu 		err = errno;
1194acaec8fSDavid Xu 		__sigev_list_lock();
1204acaec8fSDavid Xu 		__sigev_delete_node(sn);
1214acaec8fSDavid Xu 		__sigev_list_unlock();
1221b4610feSDavid Xu 		errno = err;
1234acaec8fSDavid Xu 	}
1244acaec8fSDavid Xu 	return (ret);
1254acaec8fSDavid Xu }
1264acaec8fSDavid Xu 
1274acaec8fSDavid Xu int
__aio_read(struct aiocb * iocb)1284acaec8fSDavid Xu __aio_read(struct aiocb *iocb)
1294acaec8fSDavid Xu {
1301b4610feSDavid Xu 
1314acaec8fSDavid Xu 	return aio_io(iocb, &__sys_aio_read);
1324acaec8fSDavid Xu }
1334acaec8fSDavid Xu 
1344acaec8fSDavid Xu int
__aio_readv(struct aiocb * iocb)1353904e796SThomas Munro __aio_readv(struct aiocb *iocb)
1363904e796SThomas Munro {
1373904e796SThomas Munro 
1383904e796SThomas Munro 	return aio_io(iocb, &__sys_aio_readv);
1393904e796SThomas Munro }
1403904e796SThomas Munro 
1413904e796SThomas Munro int
__aio_write(struct aiocb * iocb)1424acaec8fSDavid Xu __aio_write(struct aiocb *iocb)
1434acaec8fSDavid Xu {
1441b4610feSDavid Xu 
1454acaec8fSDavid Xu 	return aio_io(iocb, &__sys_aio_write);
1464acaec8fSDavid Xu }
1474acaec8fSDavid Xu 
1483904e796SThomas Munro int
__aio_writev(struct aiocb * iocb)1493904e796SThomas Munro __aio_writev(struct aiocb *iocb)
1503904e796SThomas Munro {
1513904e796SThomas Munro 
1523904e796SThomas Munro 	return aio_io(iocb, &__sys_aio_writev);
1533904e796SThomas Munro }
1543904e796SThomas Munro 
15576793d4dSJohn Baldwin ssize_t
__aio_waitcomplete(struct aiocb ** iocbp,struct timespec * timeout)1564acaec8fSDavid Xu __aio_waitcomplete(struct aiocb **iocbp, struct timespec *timeout)
1574acaec8fSDavid Xu {
15876793d4dSJohn Baldwin 	ssize_t ret;
1591b4610feSDavid Xu 	int err;
1604acaec8fSDavid Xu 
16176793d4dSJohn Baldwin 	ret = __sys_aio_waitcomplete(iocbp, timeout);
1624acaec8fSDavid Xu 	if (*iocbp) {
1634acaec8fSDavid Xu 		if ((*iocbp)->aio_sigevent.sigev_notify == SIGEV_THREAD) {
1641b4610feSDavid Xu 			err = errno;
1654acaec8fSDavid Xu 			__sigev_list_lock();
1664acaec8fSDavid Xu 			__sigev_delete(SI_ASYNCIO, (sigev_id_t)(*iocbp));
1674acaec8fSDavid Xu 			__sigev_list_unlock();
1681b4610feSDavid Xu 			errno = err;
1694acaec8fSDavid Xu 		}
1704acaec8fSDavid Xu 	}
1714acaec8fSDavid Xu 
1724acaec8fSDavid Xu 	return (ret);
1734acaec8fSDavid Xu }
1744acaec8fSDavid Xu 
17576793d4dSJohn Baldwin ssize_t
__aio_return(struct aiocb * iocb)1764acaec8fSDavid Xu __aio_return(struct aiocb *iocb)
1774acaec8fSDavid Xu {
1784acaec8fSDavid Xu 
1791b4610feSDavid Xu 	if (iocb->aio_sigevent.sigev_notify == SIGEV_THREAD) {
18076793d4dSJohn Baldwin 		if (__sys_aio_error(iocb) == EINPROGRESS) {
18176793d4dSJohn Baldwin 			/*
18276793d4dSJohn Baldwin 			 * Fail with EINVAL to match the semantics of
18376793d4dSJohn Baldwin 			 * __sys_aio_return() for an in-progress
18476793d4dSJohn Baldwin 			 * request.
18576793d4dSJohn Baldwin 			 */
18676793d4dSJohn Baldwin 			errno = EINVAL;
18776793d4dSJohn Baldwin 			return (-1);
18876793d4dSJohn Baldwin 		}
1894acaec8fSDavid Xu 		__sigev_list_lock();
1904acaec8fSDavid Xu 		__sigev_delete(SI_ASYNCIO, (sigev_id_t)iocb);
1914acaec8fSDavid Xu 		__sigev_list_unlock();
1924acaec8fSDavid Xu 	}
1934acaec8fSDavid Xu 
1941b4610feSDavid Xu 	return __sys_aio_return(iocb);
1954acaec8fSDavid Xu }
1963c778728SDavid Xu 
1973c778728SDavid Xu int
__aio_fsync(int op,struct aiocb * iocb)1983c778728SDavid Xu __aio_fsync(int op, struct aiocb *iocb)
1993c778728SDavid Xu {
2003c778728SDavid Xu 	struct sigev_node *sn;
2013c778728SDavid Xu 	struct sigevent saved_ev;
2023c778728SDavid Xu 	int ret, err;
2033c778728SDavid Xu 
2043c778728SDavid Xu 	if (iocb->aio_sigevent.sigev_notify != SIGEV_THREAD)
2053c778728SDavid Xu 		return __sys_aio_fsync(op, iocb);
2063c778728SDavid Xu 
20718ddf67cSAlan Somers 	ret = aio_sigev_alloc((sigev_id_t)iocb, &iocb->aio_sigevent, &sn,
20818ddf67cSAlan Somers 			      &saved_ev);
2093c778728SDavid Xu 	if (ret)
2103c778728SDavid Xu 		return (ret);
2113c778728SDavid Xu 	ret = __sys_aio_fsync(op, iocb);
2123c778728SDavid Xu 	iocb->aio_sigevent = saved_ev;
2133c778728SDavid Xu 	if (ret != 0) {
2143c778728SDavid Xu 		err = errno;
2153c778728SDavid Xu 		__sigev_list_lock();
2163c778728SDavid Xu 		__sigev_delete_node(sn);
2173c778728SDavid Xu 		__sigev_list_unlock();
2183c778728SDavid Xu 		errno = err;
2193c778728SDavid Xu 	}
2203c778728SDavid Xu 	return (ret);
2213c778728SDavid Xu }
22218ddf67cSAlan Somers 
22318ddf67cSAlan Somers int
__lio_listio(int mode,struct aiocb * const list[],int nent,struct sigevent * sig)22418ddf67cSAlan Somers __lio_listio(int mode, struct aiocb * const list[], int nent,
22518ddf67cSAlan Somers     struct sigevent *sig)
22618ddf67cSAlan Somers {
22718ddf67cSAlan Somers 	struct sigev_node *sn;
22818ddf67cSAlan Somers 	struct sigevent saved_ev;
22918ddf67cSAlan Somers 	int ret, err;
23018ddf67cSAlan Somers 
23118ddf67cSAlan Somers 	if (sig == NULL || sig->sigev_notify != SIGEV_THREAD)
23218ddf67cSAlan Somers 		return (__sys_lio_listio(mode, list, nent, sig));
23318ddf67cSAlan Somers 
23418ddf67cSAlan Somers 	ret = aio_sigev_alloc((sigev_id_t)list, sig, &sn, &saved_ev);
23518ddf67cSAlan Somers 	if (ret)
23618ddf67cSAlan Somers 		return (ret);
23718ddf67cSAlan Somers 	ret = __sys_lio_listio(mode, list, nent, sig);
23818ddf67cSAlan Somers 	*sig = saved_ev;
23918ddf67cSAlan Somers 	if (ret != 0) {
24018ddf67cSAlan Somers 		err = errno;
24118ddf67cSAlan Somers 		__sigev_list_lock();
24218ddf67cSAlan Somers 		__sigev_delete_node(sn);
24318ddf67cSAlan Somers 		__sigev_list_unlock();
24418ddf67cSAlan Somers 		errno = err;
24518ddf67cSAlan Somers 	}
24618ddf67cSAlan Somers 	return (ret);
24718ddf67cSAlan Somers }
248