xref: /freebsd/lib/librt/aio.c (revision edf8578117e8844e02c0121147f45e4609b30680)
1  /*-
2   * SPDX-License-Identifier: BSD-2-Clause
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   */
29  
30  #include <sys/cdefs.h>
31  #include <sys/types.h>
32  #include <sys/syscall.h>
33  #include <sys/aio.h>
34  
35  #include "namespace.h"
36  #include <errno.h>
37  #include <stddef.h>
38  #include <signal.h>
39  #include "sigev_thread.h"
40  #include "un-namespace.h"
41  
42  __weak_reference(__aio_read, aio_read);
43  __weak_reference(__aio_readv, aio_readv);
44  __weak_reference(__aio_write, aio_write);
45  __weak_reference(__aio_writev, aio_writev);
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_readv(struct aiocb *iocb);
55  extern int __sys_aio_write(struct aiocb *iocb);
56  extern int __sys_aio_writev(struct aiocb *iocb);
57  extern ssize_t __sys_aio_waitcomplete(struct aiocb **iocbp, struct timespec *timeout);
58  extern ssize_t __sys_aio_return(struct aiocb *iocb);
59  extern int __sys_aio_error(struct aiocb *iocb);
60  extern int __sys_aio_fsync(int op, struct aiocb *iocb);
61  extern int __sys_lio_listio(int mode, struct aiocb * const list[], int nent,
62      struct sigevent *sig);
63  
64  static void
65  aio_dispatch(struct sigev_node *sn)
66  {
67  	aio_func f = sn->sn_func;
68  
69  	f(sn->sn_value, (struct aiocb *)sn->sn_id);
70  }
71  
72  static int
73  aio_sigev_alloc(sigev_id_t id, struct sigevent *sigevent,
74      struct sigev_node **sn, struct sigevent *saved_ev)
75  {
76  	if (__sigev_check_init()) {
77  		/* This might be that thread library is not enabled. */
78  		errno = EINVAL;
79  		return (-1);
80  	}
81  
82  	*sn = __sigev_alloc(SI_ASYNCIO, sigevent, NULL, 1);
83  	if (*sn == NULL) {
84  		errno = EAGAIN;
85  		return (-1);
86  	}
87  
88  	*saved_ev = *sigevent;
89  	(*sn)->sn_id = id;
90  	__sigev_get_sigevent(*sn, sigevent, (*sn)->sn_id);
91  	(*sn)->sn_dispatch = aio_dispatch;
92  
93  	__sigev_list_lock();
94  	__sigev_register(*sn);
95  	__sigev_list_unlock();
96  
97  	return (0);
98  }
99  
100  static int
101  aio_io(struct aiocb *iocb, int (*sysfunc)(struct aiocb *iocb))
102  {
103  	struct sigev_node *sn;
104  	struct sigevent saved_ev;
105  	int ret, err;
106  
107  	if (iocb->aio_sigevent.sigev_notify != SIGEV_THREAD) {
108  		ret = sysfunc(iocb);
109  		return (ret);
110  	}
111  
112  	ret = aio_sigev_alloc((sigev_id_t)iocb, &iocb->aio_sigevent, &sn,
113  			      &saved_ev);
114  	if (ret)
115  		return (ret);
116  	ret = sysfunc(iocb);
117  	iocb->aio_sigevent = saved_ev;
118  	if (ret != 0) {
119  		err = errno;
120  		__sigev_list_lock();
121  		__sigev_delete_node(sn);
122  		__sigev_list_unlock();
123  		errno = err;
124  	}
125  	return (ret);
126  }
127  
128  int
129  __aio_read(struct aiocb *iocb)
130  {
131  
132  	return aio_io(iocb, &__sys_aio_read);
133  }
134  
135  int
136  __aio_readv(struct aiocb *iocb)
137  {
138  
139  	return aio_io(iocb, &__sys_aio_readv);
140  }
141  
142  int
143  __aio_write(struct aiocb *iocb)
144  {
145  
146  	return aio_io(iocb, &__sys_aio_write);
147  }
148  
149  int
150  __aio_writev(struct aiocb *iocb)
151  {
152  
153  	return aio_io(iocb, &__sys_aio_writev);
154  }
155  
156  ssize_t
157  __aio_waitcomplete(struct aiocb **iocbp, struct timespec *timeout)
158  {
159  	ssize_t ret;
160  	int err;
161  
162  	ret = __sys_aio_waitcomplete(iocbp, timeout);
163  	if (*iocbp) {
164  		if ((*iocbp)->aio_sigevent.sigev_notify == SIGEV_THREAD) {
165  			err = errno;
166  			__sigev_list_lock();
167  			__sigev_delete(SI_ASYNCIO, (sigev_id_t)(*iocbp));
168  			__sigev_list_unlock();
169  			errno = err;
170  		}
171  	}
172  
173  	return (ret);
174  }
175  
176  ssize_t
177  __aio_return(struct aiocb *iocb)
178  {
179  
180  	if (iocb->aio_sigevent.sigev_notify == SIGEV_THREAD) {
181  		if (__sys_aio_error(iocb) == EINPROGRESS) {
182  			/*
183  			 * Fail with EINVAL to match the semantics of
184  			 * __sys_aio_return() for an in-progress
185  			 * request.
186  			 */
187  			errno = EINVAL;
188  			return (-1);
189  		}
190  		__sigev_list_lock();
191  		__sigev_delete(SI_ASYNCIO, (sigev_id_t)iocb);
192  		__sigev_list_unlock();
193  	}
194  
195  	return __sys_aio_return(iocb);
196  }
197  
198  int
199  __aio_fsync(int op, struct aiocb *iocb)
200  {
201  	struct sigev_node *sn;
202  	struct sigevent saved_ev;
203  	int ret, err;
204  
205  	if (iocb->aio_sigevent.sigev_notify != SIGEV_THREAD)
206  		return __sys_aio_fsync(op, iocb);
207  
208  	ret = aio_sigev_alloc((sigev_id_t)iocb, &iocb->aio_sigevent, &sn,
209  			      &saved_ev);
210  	if (ret)
211  		return (ret);
212  	ret = __sys_aio_fsync(op, iocb);
213  	iocb->aio_sigevent = saved_ev;
214  	if (ret != 0) {
215  		err = errno;
216  		__sigev_list_lock();
217  		__sigev_delete_node(sn);
218  		__sigev_list_unlock();
219  		errno = err;
220  	}
221  	return (ret);
222  }
223  
224  int
225  __lio_listio(int mode, struct aiocb * const list[], int nent,
226      struct sigevent *sig)
227  {
228  	struct sigev_node *sn;
229  	struct sigevent saved_ev;
230  	int ret, err;
231  
232  	if (sig == NULL || sig->sigev_notify != SIGEV_THREAD)
233  		return (__sys_lio_listio(mode, list, nent, sig));
234  
235  	ret = aio_sigev_alloc((sigev_id_t)list, sig, &sn, &saved_ev);
236  	if (ret)
237  		return (ret);
238  	ret = __sys_lio_listio(mode, list, nent, sig);
239  	*sig = saved_ev;
240  	if (ret != 0) {
241  		err = errno;
242  		__sigev_list_lock();
243  		__sigev_delete_node(sn);
244  		__sigev_list_unlock();
245  		errno = err;
246  	}
247  	return (ret);
248  }
249