X-Git-Url: https://git.kernel.dk/?a=blobdiff_plain;f=engines%2Fwindowsaio.c;h=f66b9a2ec3465dee7c2d6b5b7f4c0e23cb9f9828;hb=e18941aab394d4a68df10379fec7184b52f95b3f;hp=5c9d85e11b528c19dccfc1337a7fdf27d2982df4;hpb=ecc314ba7c5f02b7e90ac1dfbce1a74cd4e6d6fe;p=fio.git diff --git a/engines/windowsaio.c b/engines/windowsaio.c index 5c9d85e1..f66b9a2e 100644 --- a/engines/windowsaio.c +++ b/engines/windowsaio.c @@ -3,7 +3,6 @@ * Copyright (C) 2010 Bruce Cran */ - #include #include #include @@ -13,31 +12,34 @@ #include "../fio.h" -BOOL windowsaio_debug = FALSE; - -struct windowsaio_data { - struct io_u **aio_events; - unsigned int ioFinished; - BOOL running; - BOOL stopped; - HANDLE hThread; -}; - typedef struct { OVERLAPPED o; struct io_u *io_u; } FIO_OVERLAPPED; +struct windowsaio_data { + HANDLE *io_handles; + unsigned int io_index; + FIO_OVERLAPPED *ovls; + + HANDLE iothread; + HANDLE iothread_stopped; + BOOL iothread_running; + + struct io_u **aio_events; + HANDLE iocomplete_event; + BOOL have_cancelioex; +}; + struct thread_ctx { - HANDLE ioCP; + HANDLE iocp; struct windowsaio_data *wd; }; static void PrintError(LPCSTR lpszFunction); static int fio_windowsaio_cancel(struct thread_data *td, struct io_u *io_u); -static DWORD GetEndCount(DWORD startCount, struct timespec *t); -static BOOL TimedOut(DWORD startCount, DWORD endCount); +static BOOL TimedOut(DWORD start_count, DWORD end_count); static int fio_windowsaio_getevents(struct thread_data *td, unsigned int min, unsigned int max, struct timespec *t); static struct io_u *fio_windowsaio_event(struct thread_data *td, int event); @@ -49,6 +51,21 @@ static int fio_windowsaio_init(struct thread_data *td); static int fio_windowsaio_open_file(struct thread_data *td, struct fio_file *f); static int fio_windowsaio_close_file(struct thread_data fio_unused *td, struct fio_file *f); +/* CancelIoEx isn't in Cygwin's w32api */ +BOOL WINAPI CancelIoEx( + HANDLE hFile, + LPOVERLAPPED lpOverlapped +); + + + +int sync_file_range(int fd, off64_t offset, off64_t nbytes, + unsigned int flags) +{ + errno = ENOSYS; + return -1; +} + static void PrintError(LPCSTR lpszFunction) { // Retrieve the system error message for the last-error code @@ -73,46 +90,33 @@ static void PrintError(LPCSTR lpszFunction) static int fio_windowsaio_cancel(struct thread_data *td, struct io_u *io_u) { - BOOL bSuccess; int rc = 0; - bSuccess = CancelIo(io_u->file->hFile); + struct windowsaio_data *wd = td->io_ops->data; - if (!bSuccess) + /* If we're running on Vista, we can cancel individual IO requests */ + if (wd->have_cancelioex) { + FIO_OVERLAPPED *ovl = io_u->engine_data; + if (!CancelIoEx(io_u->file->hFile, &ovl->o)) + rc = 1; + } else rc = 1; return rc; } -static DWORD GetEndCount(DWORD startCount, struct timespec *t) -{ - DWORD endCount = startCount; - - if (t == NULL) - return 0; - - endCount += (t->tv_sec * 1000) + (t->tv_nsec / 1000000); - return endCount; -} - -static BOOL TimedOut(DWORD startCount, DWORD endCount) +static BOOL TimedOut(DWORD start_count, DWORD end_count) { BOOL expired = FALSE; - DWORD currentTime; - - if (startCount == 0 || endCount == 0) - return FALSE; + DWORD current_time; - currentTime = GetTickCount(); + current_time = GetTickCount(); - if ((endCount > startCount) && currentTime >= endCount) + if ((end_count > start_count) && current_time >= end_count) expired = TRUE; - else if (currentTime < startCount && currentTime > endCount) + else if (current_time < start_count && current_time > end_count) expired = TRUE; - if (windowsaio_debug) - printf("windowsaio: timedout = %d\n", expired); - return expired; } @@ -123,49 +127,38 @@ static int fio_windowsaio_getevents(struct thread_data *td, unsigned int min, struct flist_head *entry; unsigned int dequeued = 0; struct io_u *io_u; - DWORD startCount = 0, endCount = 0; + DWORD start_count = 0, end_count = 0; BOOL timedout = FALSE; - unsigned int r = 0; - - if (windowsaio_debug) - printf("getevents (min %d, max %d)\n", min, max); + unsigned int mswait = 100; if (t != NULL) { - startCount = GetTickCount(); - endCount = GetEndCount(startCount, t); + mswait = (t->tv_sec * 1000) + (t->tv_nsec / 1000000); + start_count = GetTickCount(); + end_count = start_count + (t->tv_sec * 1000) + (t->tv_nsec / 1000000); } while (dequeued < min && !timedout) { - flist_for_each(entry, &td->io_u_busylist) { io_u = flist_entry(entry, struct io_u, list); - if (io_u->seen == 0) - continue; - - dequeued++; - - wd->ioFinished--; - wd->aio_events[r] = io_u; - r++; - - if (windowsaio_debug) - printf("dequeued %d\n", dequeued); + if (io_u->seen == 1) { + io_u->seen = 2; + wd->aio_events[dequeued] = io_u; + dequeued++; + } if (dequeued == max) break; } - if (TimedOut(startCount, endCount)) - timedout = TRUE; + if (dequeued < min) { + WaitForSingleObject(wd->iocomplete_event, mswait); + } - if (dequeued < min && !timedout) - Sleep(250); + if (t != NULL && TimedOut(start_count, end_count)) + timedout = TRUE; } - if (windowsaio_debug) - printf("leave getevents (%d)\n", dequeued); - return dequeued; } @@ -178,36 +171,33 @@ static struct io_u *fio_windowsaio_event(struct thread_data *td, int event) static int fio_windowsaio_queue(struct thread_data *td, struct io_u *io_u) { - FIO_OVERLAPPED *fov; - DWORD ioBytes; - BOOL bSuccess = TRUE; + struct windowsaio_data *wd; + DWORD iobytes; + BOOL success = TRUE; + int ind; int rc; fio_ro_check(td, io_u); - if (windowsaio_debug) - printf("enqueue enter\n"); + wd = td->io_ops->data; + ind = wd->io_index; - fov = malloc(sizeof(FIO_OVERLAPPED)); - ZeroMemory(fov, sizeof(FIO_OVERLAPPED)); + ResetEvent(wd->io_handles[ind]); + wd->ovls[ind].o.Internal = 0; + wd->ovls[ind].o.InternalHigh = 0; + wd->ovls[ind].o.Offset = io_u->offset & 0xFFFFFFFF; + wd->ovls[ind].o.OffsetHigh = io_u->offset >> 32; + wd->ovls[ind].o.hEvent = wd->io_handles[ind]; + wd->ovls[ind].io_u = io_u; + io_u->engine_data = &wd->ovls[ind]; io_u->seen = 0; - fov->o.Offset = io_u->offset & 0xFFFFFFFF; - fov->o.OffsetHigh = io_u->offset >> 32; - fov->o.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - fov->io_u = io_u; - - if (fov->o.hEvent == NULL) { - PrintError(__func__); - return 1; - } - - if (io_u->ddir == DDIR_WRITE) - bSuccess = WriteFile(io_u->file->hFile, io_u->xfer_buf, io_u->xfer_buflen, &ioBytes, &fov->o); - else if (io_u->ddir == DDIR_READ) - bSuccess = ReadFile(io_u->file->hFile, io_u->xfer_buf, io_u->xfer_buflen, &ioBytes, &fov->o); - else if (io_u->ddir == DDIR_SYNC || + if (io_u->ddir == DDIR_WRITE) { + success = WriteFile(io_u->file->hFile, io_u->xfer_buf, io_u->xfer_buflen, &iobytes, &wd->ovls[ind].o); + } else if (io_u->ddir == DDIR_READ) { + success = ReadFile(io_u->file->hFile, io_u->xfer_buf, io_u->xfer_buflen, &iobytes, &wd->ovls[ind].o); + } else if (io_u->ddir == DDIR_SYNC || io_u->ddir == DDIR_DATASYNC || io_u->ddir == DDIR_SYNC_FILE_RANGE) { @@ -216,14 +206,16 @@ static int fio_windowsaio_queue(struct thread_data *td, } else if (io_u->ddir == DDIR_TRIM) { log_info("explicit TRIM isn't supported on Windows"); return FIO_Q_COMPLETED; - } + } else + assert(0); - if (bSuccess) { + if (success) { io_u->seen = 1; - io_u->resid = io_u->xfer_buflen - fov->o.InternalHigh; + io_u->resid = io_u->xfer_buflen - iobytes; io_u->error = 0; rc = FIO_Q_COMPLETED; - } else if (!bSuccess && GetLastError() == ERROR_IO_PENDING) { + } else if (!success && GetLastError() == ERROR_IO_PENDING) { + wd->io_index = (wd->io_index + 1) % (2 * td->o.iodepth); rc = FIO_Q_QUEUED; } else { PrintError(__func__); @@ -232,128 +224,142 @@ static int fio_windowsaio_queue(struct thread_data *td, rc = FIO_Q_COMPLETED; } - if (windowsaio_debug) - printf("enqueue - leave (offset %llu)\n", io_u->offset); - return rc; } static void fio_windowsaio_cleanup(struct thread_data *td) { + int i; struct windowsaio_data *wd; - if (windowsaio_debug) - printf("windowsaio: cleanup - enter\n"); - wd = td->io_ops->data; - wd->running = FALSE; - while (wd->stopped == FALSE) - Sleep(5); + WaitForSingleObject(wd->iothread_stopped, INFINITE); if (wd != NULL) { - CloseHandle(wd->hThread); + CloseHandle(wd->iothread); + CloseHandle(wd->iothread_stopped); + CloseHandle(wd->iocomplete_event); + + for (i = 0; i < 2 * td->o.iodepth; i++) { + CloseHandle(wd->io_handles[i]); + } + free(wd->aio_events); - wd->aio_events = NULL; + free(wd->io_handles); + free(wd->ovls); free(wd); + td->io_ops->data = NULL; } - - if (windowsaio_debug) - printf("windowsaio: cleanup - leave\n"); } +/* Runs as a thread and waits for queued IO to complete */ static DWORD WINAPI IoCompletionRoutine(LPVOID lpParameter) { OVERLAPPED *ovl; FIO_OVERLAPPED *fov; struct io_u *io_u; struct windowsaio_data *wd; - struct thread_ctx *ctx; ULONG_PTR ulKey = 0; - BOOL bSuccess; DWORD bytes; - ctx = (struct thread_ctx*)lpParameter; wd = ctx->wd; - bSuccess = TRUE; - - if (windowsaio_debug) - printf("windowsaio: IoCompletionRoutine - enter\n"); - - while (ctx->wd->running) { - bSuccess = GetQueuedCompletionStatus(ctx->ioCP, &bytes, &ulKey, &ovl, 500); - if (windowsaio_debug) - printf("GetQueuedCompletionStatus returned %d\n", bSuccess); - - if (!bSuccess) { - if (GetLastError() == WAIT_TIMEOUT) { - continue; - } else { - PrintError(__func__); - continue; - } - } + while (ctx->wd->iothread_running) { + if (!GetQueuedCompletionStatus(ctx->iocp, &bytes, &ulKey, &ovl, 250)) + continue; fov = CONTAINING_RECORD(ovl, FIO_OVERLAPPED, o); io_u = fov->io_u; - if (windowsaio_debug) { - if (io_u->seen == 1) - printf("IoCompletionRoutine - got already completed IO\n"); - else - printf("IoCompletionRoutine - completed %d IO\n", ctx->wd->ioFinished); - } - - if (io_u->seen == 1) + if (io_u->seen != 0) continue; - ctx->wd->ioFinished++; - if (ovl->Internal == ERROR_SUCCESS) { io_u->resid = io_u->xfer_buflen - ovl->InternalHigh; io_u->error = 0; } else { io_u->resid = io_u->xfer_buflen; - io_u->error = 1; + io_u->error = ovl->Internal; } io_u->seen = 1; - CloseHandle(ovl->hEvent); - free(ovl); + SetEvent(wd->iocomplete_event); } - bSuccess = CloseHandle(ctx->ioCP); - if (!bSuccess) - PrintError(__func__); - - if (windowsaio_debug) - printf("windowsaio: IoCompletionRoutine - leave\n"); - - ctx->wd->stopped = TRUE; + CloseHandle(ctx->iocp); + SetEvent(ctx->wd->iothread_stopped); free(ctx); + return 0; } static int fio_windowsaio_init(struct thread_data *td) { - int rc = 0; struct windowsaio_data *wd; - - if (windowsaio_debug) - printf("windowsaio: init\n"); + OSVERSIONINFO osInfo; + int rc = 0; wd = malloc(sizeof(struct windowsaio_data)); + if (wd != NULL) + ZeroMemory(wd, sizeof(struct windowsaio_data)); + else + rc = 1; - ZeroMemory(wd, sizeof(*wd)); - wd->aio_events = malloc((td->o.iodepth + 1) * sizeof(struct io_u *)); - ZeroMemory(wd->aio_events, (td->o.iodepth + 1) * sizeof(struct io_u *)); + if (!rc) { + wd->aio_events = malloc(td->o.iodepth * sizeof(struct io_u*)); + if (wd->aio_events == NULL) + rc = 1; + } + + if (!rc) { + wd->io_handles = malloc(2 * td->o.iodepth * sizeof(HANDLE)); + if (wd->io_handles == NULL) + rc = 1; + } + + if (!rc) { + wd->ovls = malloc(2 * td->o.iodepth * sizeof(FIO_OVERLAPPED)); + if (wd->ovls == NULL) + rc = 1; + } + + if (!rc) { + /* Create an auto-reset event */ + wd->iocomplete_event = CreateEvent(NULL, FALSE, FALSE, NULL); + if (wd->iocomplete_event == NULL) + rc = 1; + } + + if (!rc) { + osInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&osInfo); + + if (osInfo.dwMajorVersion >= 6) + wd->have_cancelioex = TRUE; + else + wd->have_cancelioex = FALSE; + } + + if (rc) { + PrintError(__func__); + if (wd != NULL) { + if (wd->ovls != NULL) + free(wd->ovls); + if (wd->io_handles != NULL) + free(wd->io_handles); + if (wd->aio_events != NULL) + free(wd->aio_events); + + free(wd); + } + } td->io_ops->data = wd; - return rc; + return 0; } static int fio_windowsaio_open_file(struct thread_data *td, struct fio_file *f) @@ -364,12 +370,10 @@ static int fio_windowsaio_open_file(struct thread_data *td, struct fio_file *f) DWORD sharemode = FILE_SHARE_READ | FILE_SHARE_WRITE; DWORD openmode = OPEN_ALWAYS; DWORD access; + int i; dprint(FD_FILE, "fd open %s\n", f->file_name); - if (windowsaio_debug) - printf("windowsaio: open file %s - enter\n", f->file_name); - if (f->filetype == FIO_TYPE_PIPE) { log_err("fio: windowsaio doesn't support pipes\n"); return 1; @@ -411,7 +415,6 @@ static int fio_windowsaio_open_file(struct thread_data *td, struct fio_file *f) NULL, openmode, flags, NULL); if (f->hFile == INVALID_HANDLE_VALUE) { - log_err("Failed to open %s\n", f->file_name); PrintError(__func__); rc = 1; } @@ -424,37 +427,58 @@ static int fio_windowsaio_open_file(struct thread_data *td, struct fio_file *f) hFile = CreateIoCompletionPort(f->hFile, NULL, 0, 0); wd = td->io_ops->data; - wd->running = TRUE; - wd->stopped = FALSE; - ctx = malloc(sizeof(struct thread_ctx)); - ctx->ioCP = hFile; - ctx->wd = wd; + wd->io_index = 0; + wd->iothread_running = TRUE; + /* Create a manual-reset event */ + wd->iothread_stopped = CreateEvent(NULL, TRUE, FALSE, NULL); - wd->hThread = CreateThread(NULL, 0, IoCompletionRoutine, ctx, 0, NULL); + if (wd->iothread_stopped == NULL) + rc = 1; - if (wd->hThread == NULL) { + if (!rc) { + for (i = 0; i < 2 * td->o.iodepth; i++) { + /* Create a manual-reset event for putting in OVERLAPPED */ + wd->io_handles[i] = CreateEvent(NULL, TRUE, FALSE, NULL); + if (wd->io_handles[i] == NULL) { + PrintError(__func__); + rc = 1; + break; + } + } + } + + if (!rc) { + ctx = malloc(sizeof(struct thread_ctx)); + ctx->iocp = hFile; + ctx->wd = wd; + + wd->iothread = CreateThread(NULL, 0, IoCompletionRoutine, ctx, 0, NULL); + } + + if (rc || wd->iothread == NULL) { PrintError(__func__); rc = 1; } } - if (windowsaio_debug) - printf("windowsaio: open file - leave (%d)\n", rc); - return rc; } static int fio_windowsaio_close_file(struct thread_data fio_unused *td, struct fio_file *f) { - BOOL bSuccess; + struct windowsaio_data *wd; - if (windowsaio_debug) - printf("windowsaio: close file\n"); + dprint(FD_FILE, "fd close %s\n", f->file_name); + + if (td->io_ops->data != NULL) { + wd = td->io_ops->data; + wd->iothread_running = FALSE; + WaitForSingleObject(wd->iothread_stopped, INFINITE); + } if (f->hFile != INVALID_HANDLE_VALUE) { - bSuccess = CloseHandle(f->hFile); - if (!bSuccess) + if (!CloseHandle(f->hFile)) PrintError(__func__); }