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 51 typedef void (*aio_func)(union sigval val, struct aiocb *iocb); 52 53 extern int __sys_aio_read(struct aiocb *iocb); 54 extern int __sys_aio_write(struct aiocb *iocb); 55 extern ssize_t __sys_aio_waitcomplete(struct aiocb **iocbp, struct timespec *timeout); 56 extern ssize_t __sys_aio_return(struct aiocb *iocb); 57 extern int __sys_aio_error(struct aiocb *iocb); 58 extern int __sys_aio_fsync(int op, struct aiocb *iocb); 59 extern int __sys_lio_listio(int mode, struct aiocb * const list[], int nent, 60 struct sigevent *sig); 61 62 static void 63 aio_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 70 static int 71 aio_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 98 static int 99 aio_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 126 int 127 __aio_read(struct aiocb *iocb) 128 { 129 130 return aio_io(iocb, &__sys_aio_read); 131 } 132 133 int 134 __aio_write(struct aiocb *iocb) 135 { 136 137 return aio_io(iocb, &__sys_aio_write); 138 } 139 140 ssize_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 160 ssize_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 182 int 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 208 int 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