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