1 /* Enqueue and list of read or write requests. Common code template.
2 Copyright (C) 1997-2022 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19 /* The following macros must be defined before including this file:
20
21 LIO_LISTIO The public symbol (lio_listio or lio_listio64).
22 AIOCB Struct tag used by LIO_LISTIO (aiocb or aiocb64).
23 LIO_LISTIO_OLD The internal symbol for the compat implementation.
24 LIO_LISTIO_NEW The internal symbol for the current implementation.
25 LIO_OPCODE_BASE Opcode shift for 64-bit version with 32-bit word size.
26
27 For __WORDSIZE == 64, LIO_LISTIO must always be lio_listio, and
28 lio_listio64 is automatically defined as well. */
29
30 #include <bits/wordsize.h>
31 #if __WORDSIZE == 64
32 # define lio_listio64 XXX
33 # include <aio.h>
34 /* And undo the hack. */
35 # undef lio_listio64
36 #else
37 # include <aio.h>
38 #endif
39
40 #include <assert.h>
41 #include <errno.h>
42 #include <stdlib.h>
43 #include <unistd.h>
44 #include <pthreadP.h>
45
46 #include <aio_misc.h>
47
48 #include <shlib-compat.h>
49
50
51 /* We need this special structure to handle asynchronous I/O. */
52 struct async_waitlist
53 {
54 unsigned int counter;
55 struct sigevent sigev;
56 struct waitlist list[0];
57 };
58
59
60 /* The code in glibc 2.1 to glibc 2.4 issued only one event when all
61 requests submitted with lio_listio finished. The existing practice
62 is to issue events for the individual requests as well. This is
63 what the new code does. */
64 #if SHLIB_COMPAT (librt, GLIBC_2_1, GLIBC_2_4)
65 # define LIO_MODE(mode) ((mode) & 127)
66 # define NO_INDIVIDUAL_EVENT_P(mode) ((mode) & 128)
67 #else
68 # define LIO_MODE(mode) mode
69 # define NO_INDIVIDUAL_EVENT_P(mode) 0
70 #endif
71
72
73 static int
lio_listio_internal(int mode,struct AIOCB * const list[],int nent,struct sigevent * sig)74 lio_listio_internal (int mode, struct AIOCB *const list[], int nent,
75 struct sigevent *sig)
76 {
77 struct sigevent defsigev;
78 struct requestlist *requests[nent];
79 int cnt;
80 volatile unsigned int total = 0;
81 int result = 0;
82
83 if (sig == NULL)
84 {
85 defsigev.sigev_notify = SIGEV_NONE;
86 sig = &defsigev;
87 }
88
89 /* Request the mutex. */
90 __pthread_mutex_lock (&__aio_requests_mutex);
91
92 /* Now we can enqueue all requests. Since we already acquired the
93 mutex the enqueue function need not do this. */
94 for (cnt = 0; cnt < nent; ++cnt)
95 if (list[cnt] != NULL && list[cnt]->aio_lio_opcode != LIO_NOP)
96 {
97 if (NO_INDIVIDUAL_EVENT_P (mode))
98 list[cnt]->aio_sigevent.sigev_notify = SIGEV_NONE;
99
100 requests[cnt] = __aio_enqueue_request ((aiocb_union *) list[cnt],
101 (list[cnt]->aio_lio_opcode
102 | LIO_OPCODE_BASE));
103
104 if (requests[cnt] != NULL)
105 /* Successfully enqueued. */
106 ++total;
107 else
108 /* Signal that we've seen an error. `errno' and the error code
109 of the aiocb will tell more. */
110 result = -1;
111 }
112 else
113 requests[cnt] = NULL;
114
115 if (total == 0)
116 {
117 /* We don't have anything to do except signalling if we work
118 asynchronously. */
119
120 /* Release the mutex. We do this before raising a signal since the
121 signal handler might do a `siglongjmp' and then the mutex is
122 locked forever. */
123 __pthread_mutex_unlock (&__aio_requests_mutex);
124
125 if (LIO_MODE (mode) == LIO_NOWAIT)
126 __aio_notify_only (sig);
127
128 return result;
129 }
130 else if (LIO_MODE (mode) == LIO_WAIT)
131 {
132 #ifndef DONT_NEED_AIO_MISC_COND
133 pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
134 int oldstate;
135 #endif
136 struct waitlist waitlist[nent];
137
138 total = 0;
139 for (cnt = 0; cnt < nent; ++cnt)
140 {
141 assert (requests[cnt] == NULL || list[cnt] != NULL);
142
143 if (requests[cnt] != NULL && list[cnt]->aio_lio_opcode != LIO_NOP)
144 {
145 #ifndef DONT_NEED_AIO_MISC_COND
146 waitlist[cnt].cond = &cond;
147 #endif
148 waitlist[cnt].result = &result;
149 waitlist[cnt].next = requests[cnt]->waiting;
150 waitlist[cnt].counterp = &total;
151 waitlist[cnt].sigevp = NULL;
152 requests[cnt]->waiting = &waitlist[cnt];
153 ++total;
154 }
155 }
156
157 #ifdef DONT_NEED_AIO_MISC_COND
158 AIO_MISC_WAIT (result, total, NULL, 0);
159 #else
160 /* Since `pthread_cond_wait'/`pthread_cond_timedwait' are cancellation
161 points we must be careful. We added entries to the waiting lists
162 which we must remove. So defer cancellation for now. */
163 pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &oldstate);
164
165 while (total > 0)
166 pthread_cond_wait (&cond, &__aio_requests_mutex);
167
168 /* Now it's time to restore the cancellation state. */
169 pthread_setcancelstate (oldstate, NULL);
170
171 /* Release the conditional variable. */
172 if (pthread_cond_destroy (&cond) != 0)
173 /* This must never happen. */
174 abort ();
175 #endif
176
177 /* If any of the I/O requests failed, return -1 and set errno. */
178 if (result != 0)
179 {
180 __set_errno (result == EINTR ? EINTR : EIO);
181 result = -1;
182 }
183 }
184 else
185 {
186 struct async_waitlist *waitlist;
187
188 waitlist = (struct async_waitlist *)
189 malloc (sizeof (struct async_waitlist)
190 + (nent * sizeof (struct waitlist)));
191
192 if (waitlist == NULL)
193 {
194 __set_errno (EAGAIN);
195 result = -1;
196 }
197 else
198 {
199 total = 0;
200
201 for (cnt = 0; cnt < nent; ++cnt)
202 {
203 assert (requests[cnt] == NULL || list[cnt] != NULL);
204
205 if (requests[cnt] != NULL
206 && list[cnt]->aio_lio_opcode != LIO_NOP)
207 {
208 #ifndef DONT_NEED_AIO_MISC_COND
209 waitlist->list[cnt].cond = NULL;
210 #endif
211 waitlist->list[cnt].result = NULL;
212 waitlist->list[cnt].next = requests[cnt]->waiting;
213 waitlist->list[cnt].counterp = &waitlist->counter;
214 waitlist->list[cnt].sigevp = &waitlist->sigev;
215 requests[cnt]->waiting = &waitlist->list[cnt];
216 ++total;
217 }
218 }
219
220 waitlist->counter = total;
221 waitlist->sigev = *sig;
222 }
223 }
224
225 /* Release the mutex. */
226 __pthread_mutex_unlock (&__aio_requests_mutex);
227
228 return result;
229 }
230
231
232 #if OTHER_SHLIB_COMPAT (librt, GLIBC_2_1, GLIBC_2_4)
233 int
234 attribute_compat_text_section
LIO_LISTIO_OLD(int mode,struct AIOCB * const list[],int nent,struct sigevent * sig)235 LIO_LISTIO_OLD (int mode, struct AIOCB *const list[], int nent,
236 struct sigevent *sig)
237 {
238 /* Check arguments. */
239 if (mode != LIO_WAIT && mode != LIO_NOWAIT)
240 {
241 __set_errno (EINVAL);
242 return -1;
243 }
244
245 return lio_listio_internal (mode | LIO_NO_INDIVIDUAL_EVENT, list, nent, sig);
246 }
247 compat_symbol (librt, LIO_LISTIO_OLD, LIO_LISTIO, GLIBC_2_1);
248 # if __WORDSIZE == 64
249 compat_symbol (librt, LIO_LISTIO_OLD, lio_listio64, GLIBC_2_1);
250 # endif
251 #endif /* OTHER_SHLIB_COMPAT */
252
253
254 int
LIO_LISTIO_NEW(int mode,struct AIOCB * const list[],int nent,struct sigevent * sig)255 LIO_LISTIO_NEW (int mode, struct AIOCB *const list[], int nent,
256 struct sigevent *sig)
257 {
258 /* Check arguments. */
259 if (mode != LIO_WAIT && mode != LIO_NOWAIT)
260 {
261 __set_errno (EINVAL);
262 return -1;
263 }
264
265 return lio_listio_internal (mode, list, nent, sig);
266 }
267
268 #if PTHREAD_IN_LIBC
269 versioned_symbol (libc, LIO_LISTIO_NEW, LIO_LISTIO, GLIBC_2_34);
270 # if __WORDSIZE == 64
271 versioned_symbol (libc, LIO_LISTIO_NEW, lio_listio64, GLIBC_2_34);
272 # endif
273 # if OTHER_SHLIB_COMPAT (librt, GLIBC_2_4, GLIBC_2_34)
274 compat_symbol (librt, LIO_LISTIO_NEW, LIO_LISTIO, GLIBC_2_4);
275 # if __WORDSIZE == 64
276 compat_symbol (librt, LIO_LISTIO_NEW, lio_listio64, GLIBC_2_4);
277 # endif
278 # endif /* OTHER_SHLIB_COMPAT */
279 #else /* !PTHREAD_IN_LIBC */
280 versioned_symbol (librt, LIO_LISTIO_NEW, LIO_LISTIO, GLIBC_2_4);
281 # if __WORDSIZE == 64
282 versioned_symbol (librt, LIO_LISTIO_NEW, lio_listio64, GLIBC_2_4);
283 # endif
284 #endif /* !PTHREAD_IN_LIBC */
285