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