From: james rizzo Date: Thu, 16 Dec 2021 22:35:45 +0000 (-0700) Subject: Added a new windows only IO engine option “no_completion_thread”. X-Git-Tag: fio-3.30~78^2 X-Git-Url: https://git.kernel.dk/?a=commitdiff_plain;h=1388e47301d6ea788542f8e5da4fee94ce2b0137;p=fio.git Added a new windows only IO engine option “no_completion_thread”. Without this option, Windows FIO creates a completion polling thread for each worker thread. This also requires an event queue for the completion thread to forward completions to the worker thread. Polling directly improves performance and better matches the linuxaio engine model. Signed-off-by: james rizzo --- diff --git a/engines/windowsaio.c b/engines/windowsaio.c index 9868e816..d82c8053 100644 --- a/engines/windowsaio.c +++ b/engines/windowsaio.c @@ -11,6 +11,7 @@ #include #include "../fio.h" +#include "../optgroup.h" typedef BOOL (WINAPI *CANCELIOEX)(HANDLE hFile, LPOVERLAPPED lpOverlapped); @@ -35,6 +36,26 @@ struct thread_ctx { struct windowsaio_data *wd; }; +struct windowsaio_options { + struct thread_data *td; + unsigned int no_completion_thread; +}; + +static struct fio_option options[] = { + { + .name = "no_completion_thread", + .lname = "No completion polling thread", + .type = FIO_OPT_STR_SET, + .off1 = offsetof(struct windowsaio_options, no_completion_thread), + .help = "Use to avoid separate completion polling thread", + .category = FIO_OPT_C_ENGINE, + .group = FIO_OPT_G_WINDOWSAIO, + }, + { + .name = NULL, + }, +}; + static DWORD WINAPI IoCompletionRoutine(LPVOID lpParameter); static int fio_windowsaio_init(struct thread_data *td) @@ -80,6 +101,7 @@ static int fio_windowsaio_init(struct thread_data *td) struct thread_ctx *ctx; struct windowsaio_data *wd; HANDLE hFile; + struct windowsaio_options *o = td->eo; hFile = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); if (hFile == INVALID_HANDLE_VALUE) { @@ -91,29 +113,30 @@ static int fio_windowsaio_init(struct thread_data *td) wd->iothread_running = TRUE; wd->iocp = hFile; - if (!rc) - ctx = malloc(sizeof(struct thread_ctx)); + if (o->no_completion_thread == 0) { + if (!rc) + ctx = malloc(sizeof(struct thread_ctx)); - if (!rc && ctx == NULL) { - log_err("windowsaio: failed to allocate memory for thread context structure\n"); - CloseHandle(hFile); - rc = 1; - } + if (!rc && ctx == NULL) { + log_err("windowsaio: failed to allocate memory for thread context structure\n"); + CloseHandle(hFile); + rc = 1; + } - if (!rc) { - DWORD threadid; + if (!rc) { + DWORD threadid; - ctx->iocp = hFile; - ctx->wd = wd; - wd->iothread = CreateThread(NULL, 0, IoCompletionRoutine, ctx, 0, &threadid); - if (!wd->iothread) - log_err("windowsaio: failed to create io completion thread\n"); - else if (fio_option_is_set(&td->o, cpumask)) - fio_setaffinity(threadid, td->o.cpumask); + ctx->iocp = hFile; + ctx->wd = wd; + wd->iothread = CreateThread(NULL, 0, IoCompletionRoutine, ctx, 0, &threadid); + if (!wd->iothread) + log_err("windowsaio: failed to create io completion thread\n"); + else if (fio_option_is_set(&td->o, cpumask)) + fio_setaffinity(threadid, td->o.cpumask); + } + if (rc || wd->iothread == NULL) + rc = 1; } - - if (rc || wd->iothread == NULL) - rc = 1; } return rc; @@ -302,9 +325,63 @@ static struct io_u* fio_windowsaio_event(struct thread_data *td, int event) return wd->aio_events[event]; } -static int fio_windowsaio_getevents(struct thread_data *td, unsigned int min, - unsigned int max, - const struct timespec *t) +/* dequeue completion entrees directly (no separate completion thread) */ +static int fio_windowsaio_getevents_nothread(struct thread_data *td, unsigned int min, + unsigned int max, const struct timespec *t) +{ + struct windowsaio_data *wd = td->io_ops_data; + unsigned int dequeued = 0; + struct io_u *io_u; + DWORD start_count = 0; + DWORD end_count = 0; + DWORD mswait = 250; + struct fio_overlapped *fov; + + if (t != NULL) { + mswait = (t->tv_sec * 1000) + (t->tv_nsec / 1000000); + start_count = GetTickCount(); + end_count = start_count + (t->tv_sec * 1000) + (t->tv_nsec / 1000000); + } + + do { + BOOL ret; + OVERLAPPED *ovl; + + ULONG entries = min(16, max-dequeued); + OVERLAPPED_ENTRY oe[16]; + ret = GetQueuedCompletionStatusEx(wd->iocp, oe, 16, &entries, mswait, 0); + if (ret && entries) { + int entry_num; + + for (entry_num=0; entry_numio_u; + + 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 = win_to_posix_error(GetLastError()); + } + + fov->io_complete = FALSE; + wd->aio_events[dequeued] = io_u; + dequeued++; + } + } + + if (dequeued >= min || + (t != NULL && timeout_expired(start_count, end_count))) + break; + } while (1); + return dequeued; +} + +/* dequeue completion entrees creates by separate IoCompletionRoutine thread */ +static int fio_windowaio_getevents_thread(struct thread_data *td, unsigned int min, + unsigned int max, const struct timespec *t) { struct windowsaio_data *wd = td->io_ops_data; unsigned int dequeued = 0; @@ -334,7 +411,6 @@ static int fio_windowsaio_getevents(struct thread_data *td, unsigned int min, wd->aio_events[dequeued] = io_u; dequeued++; } - } if (dequeued >= min) break; @@ -353,6 +429,16 @@ static int fio_windowsaio_getevents(struct thread_data *td, unsigned int min, return dequeued; } +static int fio_windowsaio_getevents(struct thread_data *td, unsigned int min, + unsigned int max, const struct timespec *t) +{ + struct windowsaio_options *o = td->eo; + + if (o->no_completion_thread) + return fio_windowsaio_getevents_nothread(td, min, max, t); + return fio_windowaio_getevents_thread(td, min, max, t); +} + static enum fio_q_status fio_windowsaio_queue(struct thread_data *td, struct io_u *io_u) { @@ -484,6 +570,8 @@ static struct ioengine_ops ioengine = { .get_file_size = generic_get_file_size, .io_u_init = fio_windowsaio_io_u_init, .io_u_free = fio_windowsaio_io_u_free, + .options = options, + .option_struct_size = sizeof(struct windowsaio_options), }; static void fio_init fio_windowsaio_register(void) diff --git a/optgroup.h b/optgroup.h index 1fb84a29..3ac8f62a 100644 --- a/optgroup.h +++ b/optgroup.h @@ -71,6 +71,7 @@ enum opt_category_group { __FIO_OPT_G_LIBCUFILE, __FIO_OPT_G_DFS, __FIO_OPT_G_NFS, + __FIO_OPT_G_WINDOWSAIO, FIO_OPT_G_RATE = (1ULL << __FIO_OPT_G_RATE), FIO_OPT_G_ZONE = (1ULL << __FIO_OPT_G_ZONE), @@ -116,6 +117,7 @@ enum opt_category_group { FIO_OPT_G_FILESTAT = (1ULL << __FIO_OPT_G_FILESTAT), FIO_OPT_G_LIBCUFILE = (1ULL << __FIO_OPT_G_LIBCUFILE), FIO_OPT_G_DFS = (1ULL << __FIO_OPT_G_DFS), + FIO_OPT_G_WINDOWSAIO = (1ULL << __FIO_OPT_G_WINDOWSAIO), }; extern const struct opt_group *opt_group_from_mask(uint64_t *mask);