1 /*
2  * Copyright (c) 2017, NVIDIA CORPORATION.  All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 
18 /*
19  * The external routines in this module are:
20  *
21  * Fio_asy_open - called from open
22  * Fio_asy_enable - enable async i/o, disable stdio
23  * Fio_asy_read - async read
24  * Fio_asy_write - async write
25  * Fio_asy_start - for vectored i/o, start reads or writes
26  * Fio_asy_disable - disable async i/o, enable stdio
27  * Fio_asy_close - called from close
28  */
29 
30 #if !defined(TARGET_WIN_X8664)
31 #include <unistd.h>
32 #include <stdlib.h>
33 #include <errno.h>
34 #include <string.h>
35 #include <aio.h>
36 #include <signal.h>
37 #else
38 #include <windows.h>
39 #include <errno.h>
40 #endif
41 
42 #include "stdioInterf.h"
43 #include "async.h"
44 
45 
46 #define FIO_MAX_ASYNC_TRANSACTIONS 16
47 
48 /* one struct per file */
49 
50 struct asy_transaction_data {
51   long len;
52   seekoffx_t off;
53 };
54 
55 #if defined(TARGET_WIN_X8664)
56 struct asy {
57   FILE *fp;
58   int fd;
59   HANDLE handle;
60   int flags;
61   int outstanding_transactions;
62   struct asy_transaction_data atd[FIO_MAX_ASYNC_TRANSACTIONS];
63   OVERLAPPED overlap[FIO_MAX_ASYNC_TRANSACTIONS];
64 };
65 
66 union Converter {
67   struct {
68     DWORD wOffset;
69     DWORD wOffsetHigh;
70   };
71   seekoffx_t offset;
72 };
73 
74 #else
75 
76 struct asy {
77   FILE *fp;
78   int fd;
79   int flags;
80   int outstanding_transactions;
81   struct asy_transaction_data atd[FIO_MAX_ASYNC_TRANSACTIONS];
82   struct aiocb aiocb[FIO_MAX_ASYNC_TRANSACTIONS];
83 };
84 #endif
85 
86 /* flags */
87 
88 #define ASY_FDACT 0x01 /* fd is active, not fp */
89 #define ASY_IOACT 0x02 /* asynch i/o is active */
90 
91 static int slime;
92 
93 /* internal wait for asynch i/o */
94 
95 #if defined(TARGET_WIN_X8664)
96 static int
asy_wait(struct asy * asy)97 asy_wait(struct asy *asy)
98 {
99   long len;
100   int s;
101   int tn;
102   BOOL aio_complete;
103 
104   if (!(asy->flags & ASY_IOACT)) {
105     return (0);
106   }
107   asy->flags &= ~ASY_IOACT;
108 
109   for (tn = 0; tn < asy->outstanding_transactions; tn++) {
110     do {
111       aio_complete =
112           GetOverlappedResult(asy->handle, &(asy->overlap[tn]), &len, FALSE);
113     } while ((aio_complete == FALSE));
114 
115     if (slime)
116       printf("---Fio_asy_wait %d\n", asy->fd);
117 
118     if (asy->atd[tn].len != len) {
119       __io_set_errno(FIO_EEOF);
120       printf("Wait FIO_EEOF error forthcoming.\n");
121       return (-1);
122     }
123   }
124   asy->atd[0].off = asy->atd[asy->outstanding_transactions].off;
125   asy->outstanding_transactions = 0;
126   return (0);
127 }
128 #else
129 static int
asy_wait(struct asy * asy)130 asy_wait(struct asy *asy)
131 {
132   long len;
133   int s;
134   int tn;
135   int offset;
136   int n;
137 
138   struct aiocb *p[1];
139 
140   if (!(asy->flags & ASY_IOACT)) { /* i/o active? */
141     return (0);
142   }
143   asy->flags &= ~ASY_IOACT;
144 
145   for (tn = 0; tn < asy->outstanding_transactions; tn++) {
146     p[0] = &(asy->aiocb[tn]);
147     do {
148       s = aio_suspend((const struct aiocb * const *)p, 1,
149                       (const struct timespec *)0);
150     } while ((s == -1) && (__io_errno() == EINTR));
151     if (s == -1) {
152       return (-1);
153     }
154     if (slime)
155       printf("---Fio_asy_wait %d\n", asy->fd);
156     len = aio_return(&(asy->aiocb[tn]));
157     if (len == -1) {
158       s = aio_error(&(asy->aiocb[tn]));
159       __io_set_errno(s);
160       return (-1);
161     }
162     if (asy->atd[tn].len != len) { /* incomplete transfer? */
163       __io_set_errno(FIO_EEOF);  /* ..yes */
164       return (-1);
165     }
166   }
167   /* Reset the number of outstanding transactions back to 0,
168    * and set the offset to the end of the last transaction list
169    */
170   asy->atd[0].off = asy->atd[asy->outstanding_transactions].off;
171   asy->outstanding_transactions = 0;
172   return (0);
173 }
174 #endif
175 
176 int
Fio_asy_fseek(struct asy * asy,long offset,int whence)177 Fio_asy_fseek(struct asy *asy, long offset, int whence)
178 {
179   if (slime)
180     printf("--Fio_asy_seek %d %ld\n", asy->fd, offset);
181 
182   if (whence == SEEK_CUR) {
183     asy->atd[asy->outstanding_transactions].off += offset;
184   } else {
185     asy->atd[asy->outstanding_transactions].off = offset;
186   }
187   return (0);
188 }
189 
190 /* enable fd, disable fp */
191 
192 int
Fio_asy_enable(struct asy * asy)193 Fio_asy_enable(struct asy *asy)
194 {
195   int n;
196 
197   if (slime)
198     printf("--Fio_asy_enable %d\n", asy->fd);
199   if (asy->flags & ASY_IOACT) { /* i/o active? */
200     if (asy_wait(asy) == -1) {
201       return (-1);
202     }
203   }
204   if (asy->flags & ASY_FDACT) { /* fd already active? */
205     return (0);
206   }
207 
208   asy->atd[0].off = __io_ftellx(asy->fp);
209   asy->outstanding_transactions = 0;
210   if (asy->atd[0].off == -1) {
211     return (-1);
212   }
213   n = __io_fflush(asy->fp);
214   if (n != 0) {
215     return (-1);
216   }
217   asy->flags |= ASY_FDACT; /* fd is now active */
218   return (0);
219 }
220 
221 /* disable fd, enable fp */
222 
223 int
Fio_asy_disable(struct asy * asy)224 Fio_asy_disable(struct asy *asy)
225 {
226   int n;
227   int offset;
228 
229   if (slime)
230     printf("--Fio_asy_disable %d\n", asy->fd);
231   if (asy->flags & ASY_IOACT) { /* i/o active? */
232     if (asy_wait(asy) == -1) {
233       return (-1);
234     }
235   }
236   if (!(asy->flags & ASY_FDACT)) { /* fd not active? */
237     return (0);
238   }
239   /* Seek to the end of the the list. */
240   offset = asy->atd[asy->outstanding_transactions].off;
241   n = __io_fseekx(asy->fp, offset, 0);
242   if (n == -1) {
243     return (-1);
244   }
245   asy->flags &= ~ASY_FDACT; /* fd is now inactive */
246   return (0);
247 }
248 
249 /* init file for asynch i/o, called from open */
250 
251 int
Fio_asy_open(FILE * fp,struct asy ** pasy)252 Fio_asy_open(FILE *fp, struct asy **pasy)
253 {
254   struct asy *asy;
255   char *p;
256 #if defined(TARGET_WIN_X8664)
257   HANDLE temp_handle;
258 #endif
259   asy = (struct asy *)calloc(sizeof(struct asy), 1);
260   if (asy == (struct asy *)0) {
261     __io_set_errno(ENOMEM);
262     return (-1);
263   }
264   asy->fp = fp;
265   asy->fd = __io_getfd(fp);
266 #if defined(TARGET_WIN_X8664)
267   temp_handle = _get_osfhandle(asy->fd);
268   asy->handle =
269       ReOpenFile(temp_handle, GENERIC_READ | GENERIC_WRITE,
270                  FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_FLAG_OVERLAPPED);
271   if (asy->handle == INVALID_HANDLE_VALUE) {
272     __io_set_errno(EBADF);
273     return (-1);
274   }
275 #endif
276   if (slime)
277     printf("--Fio_asy_open %d\n", asy->fd);
278   *pasy = asy;
279   return (0);
280 }
281 
282 /* start an asynch read */
283 
284 int
Fio_asy_read(struct asy * asy,void * adr,long len)285 Fio_asy_read(struct asy *asy, void *adr, long len)
286 {
287   int n;
288   int tn;
289 
290 #if defined(TARGET_WIN_X8664)
291   union Converter converter;
292 #endif
293   if (slime)
294     printf("--Fio_asy_read %d %p %ld\n", asy->fd, adr, len);
295 
296 #if defined(TARGET_WIN_X8664)
297   if (asy->flags & ASY_IOACT) { /* i/o active? */
298     if (asy_wait(asy) == -1) {  /* ..yes, wait */
299       return (-1);
300     }
301   }
302   tn = asy->outstanding_transactions;
303   asy->overlap[tn].Internal = 0;
304   asy->overlap[tn].InternalHigh = 0;
305   asy->overlap[tn].u.Pointer = 0;
306   /* Load asy->off into OffsetHigh/Offset */
307   converter.offset = asy->atd[tn].off;
308   asy->overlap[tn].u.s.Offset = converter.wOffset;
309   asy->overlap[tn].u.s.OffsetHigh = converter.wOffsetHigh;
310   asy->overlap[tn].hEvent = 0;
311   if (ReadFile(asy->handle, adr, len, NULL, &(asy->overlap[tn])) == FALSE &&
312       GetLastError() != ERROR_IO_PENDING) {
313     n = -1;
314   }
315 #else
316   tn = asy->outstanding_transactions;
317   asy->aiocb[tn].aio_fildes = asy->fd;
318   asy->aiocb[tn].aio_reqprio = 0;
319   asy->aiocb[tn].aio_buf = adr;
320   asy->aiocb[tn].aio_nbytes = len;
321   memset(&(asy->aiocb[tn].aio_sigevent), 0, sizeof(struct sigevent));
322   asy->aiocb[tn].aio_offset = asy->atd[tn].off;
323   n = aio_read(&(asy->aiocb[tn]));
324 #endif
325 
326   if (n == -1) {
327     return (-1);
328   }
329   asy->atd[tn].len = len;
330   asy->atd[tn + 1].off = asy->atd[tn].off + len;
331   asy->flags |= ASY_IOACT; /* i/o now active */
332   asy->outstanding_transactions += 1;
333   return (0);
334 }
335 
336 /* start an asynch write */
337 
338 int
Fio_asy_write(struct asy * asy,void * adr,long len)339 Fio_asy_write(struct asy *asy, void *adr, long len)
340 {
341   int n;
342   int tn;
343 #if defined(TARGET_WIN_X8664)
344   union Converter converter;
345 #endif
346 
347   if (slime)
348     printf("--Fio_asy_write %d %p %ld\n", asy->fd, adr, len);
349 
350 #if defined(TARGET_WIN_X8664)
351   if (asy->flags & ASY_IOACT) { /* i/o active? */
352     if (asy_wait(asy) == -1) {  /* ..yes, wait */
353       return (-1);
354     }
355   }
356   tn = asy->outstanding_transactions;
357   asy->overlap[tn].Internal = 0;
358   asy->overlap[tn].InternalHigh = 0;
359   asy->overlap[tn].u.Pointer = 0;
360   /* Load asy->off into OffsetHigh/Offset. */
361   converter.offset = asy->atd[0].off;
362   asy->overlap[tn].u.s.Offset = converter.wOffset;
363   asy->overlap[tn].u.s.OffsetHigh = converter.wOffsetHigh;
364   asy->overlap[tn].hEvent = 0;
365   if (WriteFile(asy->handle, adr, len, NULL, &(asy->overlap[tn])) == FALSE &&
366       GetLastError() != ERROR_IO_PENDING) {
367     n = -1;
368   }
369 #else
370   tn = asy->outstanding_transactions;
371   asy->aiocb[tn].aio_fildes = asy->fd;
372   asy->aiocb[tn].aio_reqprio = 0;
373   asy->aiocb[tn].aio_buf = adr;
374   asy->aiocb[tn].aio_nbytes = len;
375   memset(&(asy->aiocb[tn].aio_sigevent), 0, sizeof(struct sigevent));
376   asy->aiocb[tn].aio_offset = asy->atd[tn].off;
377   n = aio_write(&(asy->aiocb[tn]));
378 #endif
379 
380   if (n == -1) {
381     return (-1);
382   }
383   asy->atd[tn].len = len;
384   asy->atd[tn + 1].off = asy->atd[tn].off + len;
385   asy->outstanding_transactions += 1;
386   asy->flags |= ASY_IOACT; /* i/o now active */
387   return (0);
388 }
389 
390 int
Fio_asy_start(struct asy * asy)391 Fio_asy_start(struct asy *asy)
392 {
393   if (slime)
394     printf("--Fio_asy_start %d\n", asy->fd);
395   return (0);
396 }
397 
398 /* close asynch i/o called from close */
399 
400 int
Fio_asy_close(struct asy * asy)401 Fio_asy_close(struct asy *asy)
402 {
403   int n;
404 
405   if (slime)
406     printf("--Fio_asy_close %d\n", asy->fd);
407   n = 0;
408   if (asy->flags & ASY_IOACT) { /* i/o active? */
409     n = asy_wait(asy);
410   }
411 #if defined(TARGET_WIN_X8664)
412   /* Close the Re-opened handle that we created. */
413   CloseHandle(asy->handle);
414 #endif
415   free(asy);
416   return (n);
417 }
418 
419