Makefile: Fix android compilation
[fio.git] / engines / windowsaio.c
1 /*
2  * windowsaio engine
3  *
4  * IO engine using Windows IO Completion Ports.
5  */
6
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <signal.h>
11 #include <errno.h>
12
13 #include "../fio.h"
14
15 typedef BOOL (WINAPI *CANCELIOEX)(HANDLE hFile, LPOVERLAPPED lpOverlapped);
16
17 int geterrno_from_win_error (DWORD code, int deferrno);
18
19 struct fio_overlapped {
20         OVERLAPPED o;
21         struct io_u *io_u;
22         BOOL io_complete;
23 };
24
25 struct windowsaio_data {
26         struct io_u **aio_events;
27         HANDLE iocp;
28         HANDLE iothread;
29         HANDLE iocomplete_event;
30         BOOL iothread_running;
31 };
32
33 struct thread_ctx {
34         HANDLE iocp;
35         struct windowsaio_data *wd;
36 };
37
38 static DWORD WINAPI IoCompletionRoutine(LPVOID lpParameter);
39
40 static int fio_windowsaio_init(struct thread_data *td)
41 {
42         struct windowsaio_data *wd;
43         int rc = 0;
44
45         wd = calloc(1, sizeof(struct windowsaio_data));
46         if (wd == NULL) {
47                  log_err("windowsaio: failed to allocate memory for engine data\n");
48                 rc = 1;
49         }
50
51         if (!rc) {
52                 wd->aio_events = malloc(td->o.iodepth * sizeof(struct io_u*));
53                 if (wd->aio_events == NULL) {
54                         log_err("windowsaio: failed to allocate memory for aio events list\n");
55                         rc = 1;
56                 }
57         }
58
59         if (!rc) {
60                 /* Create an auto-reset event */
61                 wd->iocomplete_event = CreateEvent(NULL, FALSE, FALSE, NULL);
62                 if (wd->iocomplete_event == NULL) {
63                         log_err("windowsaio: failed to create io complete event handle\n");
64                         rc = 1;
65                 }
66         }
67
68         if (rc) {
69                 if (wd != NULL) {
70                         if (wd->aio_events != NULL)
71                                 free(wd->aio_events);
72
73                         free(wd);
74                 }
75         }
76
77         td->io_ops_data = wd;
78
79         if (!rc) {
80                 struct thread_ctx *ctx;
81                 struct windowsaio_data *wd;
82                 HANDLE hFile;
83
84                 hFile = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
85                 if (hFile == INVALID_HANDLE_VALUE) {
86                         log_err("windowsaio: failed to create io completion port\n");
87                         rc = 1;
88                 }
89
90                 wd = td->io_ops_data;
91                 wd->iothread_running = TRUE;
92                 wd->iocp = hFile;
93
94                 if (!rc)
95                         ctx = malloc(sizeof(struct thread_ctx));
96
97                 if (!rc && ctx == NULL) {
98                         log_err("windowsaio: failed to allocate memory for thread context structure\n");
99                         CloseHandle(hFile);
100                         rc = 1;
101                 }
102
103                 if (!rc) {
104                         DWORD threadid;
105
106                         ctx->iocp = hFile;
107                         ctx->wd = wd;
108                         wd->iothread = CreateThread(NULL, 0, IoCompletionRoutine, ctx, 0, &threadid);
109                         if (!wd->iothread)
110                                 log_err("windowsaio: failed to create io completion thread\n");
111                         else if (fio_option_is_set(&td->o, cpumask))
112                                 fio_setaffinity(threadid, td->o.cpumask);
113                 }
114
115                 if (rc || wd->iothread == NULL)
116                         rc = 1;
117         }
118
119         return rc;
120 }
121
122 static void fio_windowsaio_cleanup(struct thread_data *td)
123 {
124         struct windowsaio_data *wd;
125
126         wd = td->io_ops_data;
127
128         if (wd != NULL) {
129                 wd->iothread_running = FALSE;
130                 WaitForSingleObject(wd->iothread, INFINITE);
131
132                 CloseHandle(wd->iothread);
133                 CloseHandle(wd->iocomplete_event);
134
135                 free(wd->aio_events);
136                 free(wd);
137
138                 td->io_ops_data = NULL;
139         }
140 }
141
142 static int windowsaio_invalidate_cache(struct fio_file *f)
143 {
144         DWORD error;
145         DWORD isharemode = (FILE_SHARE_DELETE | FILE_SHARE_READ |
146                                 FILE_SHARE_WRITE);
147         HANDLE ihFile;
148         int rc = 0;
149
150         /*
151          * Encourage Windows to drop cached parts of a file by temporarily
152          * opening it for non-buffered access. Note: this will only work when
153          * the following is the only thing with the file open on the whole
154          * system.
155          */
156         dprint(FD_IO, "windowaio: attempt invalidate cache for %s\n",
157                         f->file_name);
158         ihFile = CreateFile(f->file_name, 0, isharemode, NULL, OPEN_EXISTING,
159                         FILE_FLAG_NO_BUFFERING, NULL);
160
161         if (ihFile != INVALID_HANDLE_VALUE) {
162                 if (!CloseHandle(ihFile)) {
163                         error = GetLastError();
164                         log_info("windowsaio: invalidation fd close %s failed: error %lu\n",
165                                  f->file_name, error);
166                         rc = 1;
167                 }
168         } else {
169                 error = GetLastError();
170                 if (error != ERROR_FILE_NOT_FOUND) {
171                         log_info("windowsaio: cache invalidation of %s failed: error %lu\n",
172                                  f->file_name, error);
173                         rc = 1;
174                 }
175         }
176
177         return rc;
178 }
179
180 static int fio_windowsaio_open_file(struct thread_data *td, struct fio_file *f)
181 {
182         int rc = 0;
183         DWORD flags = FILE_FLAG_POSIX_SEMANTICS | FILE_FLAG_OVERLAPPED;
184         DWORD sharemode = FILE_SHARE_READ | FILE_SHARE_WRITE;
185         DWORD openmode = OPEN_ALWAYS;
186         DWORD access;
187
188         dprint(FD_FILE, "fd open %s\n", f->file_name);
189
190         if (f->filetype == FIO_TYPE_PIPE) {
191                 log_err("windowsaio: pipes are not supported\n");
192                 return 1;
193         }
194
195         if (!strcmp(f->file_name, "-")) {
196                 log_err("windowsaio: can't read/write to stdin/out\n");
197                 return 1;
198         }
199
200         if (td->o.odirect)
201                 flags |= FILE_FLAG_NO_BUFFERING;
202         if (td->o.sync_io)
203                 flags |= FILE_FLAG_WRITE_THROUGH;
204
205         /*
206          * Inform Windows whether we're going to be doing sequential or
207          * random IO so it can tune the Cache Manager
208          */
209         switch (td->o.fadvise_hint) {
210         case F_ADV_TYPE:
211                 if (td_random(td))
212                         flags |= FILE_FLAG_RANDOM_ACCESS;
213                 else
214                         flags |= FILE_FLAG_SEQUENTIAL_SCAN;
215                 break;
216         case F_ADV_RANDOM:
217                 flags |= FILE_FLAG_RANDOM_ACCESS;
218                 break;
219         case F_ADV_SEQUENTIAL:
220                 flags |= FILE_FLAG_SEQUENTIAL_SCAN;
221                 break;
222         case F_ADV_NONE:
223                 break;
224         default:
225                 log_err("fio: unknown fadvise type %d\n", td->o.fadvise_hint);
226         }
227
228         if (!td_write(td) || read_only)
229                 access = GENERIC_READ;
230         else
231                 access = (GENERIC_READ | GENERIC_WRITE);
232
233         if (td->o.create_on_open)
234                 openmode = OPEN_ALWAYS;
235         else
236                 openmode = OPEN_EXISTING;
237
238         /* If we're going to use direct I/O, Windows will try and invalidate
239          * its cache at that point so there's no need to do it here */
240         if (td->o.invalidate_cache && !td->o.odirect)
241                 windowsaio_invalidate_cache(f);
242
243         f->hFile = CreateFile(f->file_name, access, sharemode,
244                 NULL, openmode, flags, NULL);
245
246         if (f->hFile == INVALID_HANDLE_VALUE) {
247                 log_err("windowsaio: failed to open file \"%s\"\n", f->file_name);
248                 rc = 1;
249         }
250
251         /* Only set up the completion port and thread if we're not just
252          * querying the device size */
253         if (!rc && td->io_ops_data != NULL) {
254                 struct windowsaio_data *wd;
255
256                 wd = td->io_ops_data;
257
258                 if (CreateIoCompletionPort(f->hFile, wd->iocp, 0, 0) == NULL) {
259                         log_err("windowsaio: failed to create io completion port\n");
260                         rc = 1;
261                 }
262         }
263
264         return rc;
265 }
266
267 static int fio_windowsaio_close_file(struct thread_data fio_unused *td, struct fio_file *f)
268 {
269         int rc = 0;
270
271         dprint(FD_FILE, "fd close %s\n", f->file_name);
272
273         if (f->hFile != INVALID_HANDLE_VALUE) {
274                 if (!CloseHandle(f->hFile)) {
275                         log_info("windowsaio: failed to close file handle for \"%s\"\n", f->file_name);
276                         rc = 1;
277                 }
278         }
279
280         f->hFile = INVALID_HANDLE_VALUE;
281         return rc;
282 }
283
284 static BOOL timeout_expired(DWORD start_count, DWORD end_count)
285 {
286         BOOL expired = FALSE;
287         DWORD current_time;
288
289         current_time = GetTickCount();
290
291         if ((end_count > start_count) && current_time >= end_count)
292                 expired = TRUE;
293         else if (current_time < start_count && current_time > end_count)
294                 expired = TRUE;
295
296         return expired;
297 }
298
299 static struct io_u* fio_windowsaio_event(struct thread_data *td, int event)
300 {
301         struct windowsaio_data *wd = td->io_ops_data;
302         return wd->aio_events[event];
303 }
304
305 static int fio_windowsaio_getevents(struct thread_data *td, unsigned int min,
306                                     unsigned int max,
307                                     const struct timespec *t)
308 {
309         struct windowsaio_data *wd = td->io_ops_data;
310         unsigned int dequeued = 0;
311         struct io_u *io_u;
312         int i;
313         struct fio_overlapped *fov;
314         DWORD start_count = 0;
315         DWORD end_count = 0;
316         DWORD status;
317         DWORD mswait = 250;
318
319         if (t != NULL) {
320                 mswait = (t->tv_sec * 1000) + (t->tv_nsec / 1000000);
321                 start_count = GetTickCount();
322                 end_count = start_count + (t->tv_sec * 1000) + (t->tv_nsec / 1000000);
323         }
324
325         do {
326                 io_u_qiter(&td->io_u_all, io_u, i) {
327                         if (!(io_u->flags & IO_U_F_FLIGHT))
328                                 continue;
329
330                         fov = (struct fio_overlapped*)io_u->engine_data;
331
332                         if (fov->io_complete) {
333                                 fov->io_complete = FALSE;
334                                 wd->aio_events[dequeued] = io_u;
335                                 dequeued++;
336                         }
337
338                 }
339                 if (dequeued >= min)
340                         break;
341
342                 if (dequeued < min) {
343                         status = WaitForSingleObject(wd->iocomplete_event, mswait);
344                         if (status != WAIT_OBJECT_0 && dequeued >= min)
345                                 break;
346                 }
347
348                 if (dequeued >= min ||
349                     (t != NULL && timeout_expired(start_count, end_count)))
350                         break;
351         } while (1);
352
353         return dequeued;
354 }
355
356 static enum fio_q_status fio_windowsaio_queue(struct thread_data *td,
357                                               struct io_u *io_u)
358 {
359         struct fio_overlapped *o = io_u->engine_data;
360         LPOVERLAPPED lpOvl = &o->o;
361         BOOL success = FALSE;
362         int rc = FIO_Q_COMPLETED;
363
364         fio_ro_check(td, io_u);
365
366         lpOvl->Internal = 0;
367         lpOvl->InternalHigh = 0;
368         lpOvl->Offset = io_u->offset & 0xFFFFFFFF;
369         lpOvl->OffsetHigh = io_u->offset >> 32;
370
371         switch (io_u->ddir) {
372         case DDIR_WRITE:
373                 success = WriteFile(io_u->file->hFile, io_u->xfer_buf,
374                                         io_u->xfer_buflen, NULL, lpOvl);
375                 break;
376         case DDIR_READ:
377                 success = ReadFile(io_u->file->hFile, io_u->xfer_buf,
378                                         io_u->xfer_buflen, NULL, lpOvl);
379                 break;
380         case DDIR_SYNC:
381         case DDIR_DATASYNC:
382         case DDIR_SYNC_FILE_RANGE:
383                 success = FlushFileBuffers(io_u->file->hFile);
384                 if (!success) {
385                         log_err("windowsaio: failed to flush file buffers\n");
386                         io_u->error = win_to_posix_error(GetLastError());
387                 }
388
389                 return FIO_Q_COMPLETED;
390         case DDIR_TRIM:
391                 log_err("windowsaio: manual TRIM isn't supported on Windows\n");
392                 io_u->error = 1;
393                 io_u->resid = io_u->xfer_buflen;
394                 return FIO_Q_COMPLETED;
395         default:
396                 assert(0);
397                 break;
398         }
399
400         if (success || GetLastError() == ERROR_IO_PENDING)
401                 rc = FIO_Q_QUEUED;
402         else {
403                 io_u->error = win_to_posix_error(GetLastError());
404                 io_u->resid = io_u->xfer_buflen;
405         }
406
407         return rc;
408 }
409
410 /* Runs as a thread and waits for queued IO to complete */
411 static DWORD WINAPI IoCompletionRoutine(LPVOID lpParameter)
412 {
413         OVERLAPPED *ovl;
414         struct fio_overlapped *fov;
415         struct io_u *io_u;
416         struct windowsaio_data *wd;
417         struct thread_ctx *ctx;
418         ULONG_PTR ulKey = 0;
419         DWORD bytes;
420
421         ctx = (struct thread_ctx*)lpParameter;
422         wd = ctx->wd;
423
424         do {
425                 BOOL ret;
426
427                 ret = GetQueuedCompletionStatus(ctx->iocp, &bytes, &ulKey,
428                                                 &ovl, 250);
429                 if (!ret && ovl == NULL)
430                         continue;
431
432                 fov = CONTAINING_RECORD(ovl, struct fio_overlapped, o);
433                 io_u = fov->io_u;
434
435                 if (ovl->Internal == ERROR_SUCCESS) {
436                         io_u->resid = io_u->xfer_buflen - ovl->InternalHigh;
437                         io_u->error = 0;
438                 } else {
439                         io_u->resid = io_u->xfer_buflen;
440                         io_u->error = win_to_posix_error(GetLastError());
441                 }
442
443                 fov->io_complete = TRUE;
444                 SetEvent(wd->iocomplete_event);
445         } while (ctx->wd->iothread_running);
446
447         CloseHandle(ctx->iocp);
448         free(ctx);
449         return 0;
450 }
451
452 static void fio_windowsaio_io_u_free(struct thread_data *td, struct io_u *io_u)
453 {
454         struct fio_overlapped *o = io_u->engine_data;
455
456         if (o) {
457                 io_u->engine_data = NULL;
458                 free(o);
459         }
460 }
461
462 static int fio_windowsaio_io_u_init(struct thread_data *td, struct io_u *io_u)
463 {
464         struct fio_overlapped *o;
465
466         o = malloc(sizeof(*o));
467         o->io_complete = FALSE;
468         o->io_u = io_u;
469         o->o.hEvent = NULL;
470         io_u->engine_data = o;
471         return 0;
472 }
473
474 static struct ioengine_ops ioengine = {
475         .name           = "windowsaio",
476         .version        = FIO_IOOPS_VERSION,
477         .init           = fio_windowsaio_init,
478         .queue          = fio_windowsaio_queue,
479         .getevents      = fio_windowsaio_getevents,
480         .event          = fio_windowsaio_event,
481         .cleanup        = fio_windowsaio_cleanup,
482         .open_file      = fio_windowsaio_open_file,
483         .close_file     = fio_windowsaio_close_file,
484         .get_file_size  = generic_get_file_size,
485         .io_u_init      = fio_windowsaio_io_u_init,
486         .io_u_free      = fio_windowsaio_io_u_free,
487 };
488
489 static void fio_init fio_windowsaio_register(void)
490 {
491         register_ioengine(&ioengine);
492 }
493
494 static void fio_exit fio_windowsaio_unregister(void)
495 {
496         unregister_ioengine(&ioengine);
497 }