From ecc314ba7c5f02b7e90ac1dfbce1a74cd4e6d6fe Mon Sep 17 00:00:00 2001 From: Bruce Cran Date: Tue, 4 Jan 2011 10:59:30 +0100 Subject: [PATCH] FIO Windows update Signed-off-by: Jens Axboe --- HOWTO | 10 +- README | 16 +- engines/windowsaio.c | 487 ++++++++++++++++++++++++++++++++++++++++++ file.h | 2 + filesetup.c | 13 +- fio.c | 7 +- fio.h | 2 + init.c | 4 +- ioengines.c | 3 +- os/os-aix.h | 7 +- os/os-freebsd.h | 13 +- os/os-linux.h | 9 +- os/os-mac.h | 3 +- os/os-netbsd.h | 4 +- os/os-solaris.h | 3 +- os/os-windows.h | 81 ++++--- os/os.h | 13 +- os/windows/version.h | 4 + os/windows/version.rc | 10 +- 19 files changed, 600 insertions(+), 91 deletions(-) create mode 100644 engines/windowsaio.c create mode 100644 os/windows/version.h diff --git a/HOWTO b/HOWTO index 3b1eeb06..35dccfb1 100644 --- a/HOWTO +++ b/HOWTO @@ -272,11 +272,11 @@ filename=str Fio normally makes up a filename based on the job name, ':' colon. So if you wanted a job to open /dev/sda and /dev/sdb as the two working files, you would use filename=/dev/sda:/dev/sdb. On Windows, disk devices are accessed - as /dev/sda for the first device (i.e. \Device\HardDisk0\Partition0, - /dev/sda1 for the first partition on the first disk etc. If the - wanted filename does need to include a colon, then escape that with - a '\' character. For instance, if the filename is - "/dev/dsk/foo@3,0:c", then you would use filename="/dev/dsk/foo@3,0\:c". + as \\.\PhysicalDrive0 for the first device, \\.\PhysicalDrive1 + for the second etc. If the wanted filename does need to + include a colon, then escape that with a '\' character. + For instance, if the filename is "/dev/dsk/foo@3,0:c", + then you would use filename="/dev/dsk/foo@3,0\:c". '-' is a reserved name, meaning stdin or stdout. Which of the two depends on the read/write direction set. diff --git a/README b/README index f1ee9aa7..f136bb5e 100644 --- a/README +++ b/README @@ -54,6 +54,10 @@ Packages for Solaris are available from OpenCSW. Install their pkgutil tool (http://www.opencsw.org/get-it/pkgutil/) and then install fio via 'pkgutil -i fio'. +Windows: +Bruce Cran has fio packages for Windows at +http://www.bluestop.org/fio . + Mailing list ------------ @@ -79,10 +83,10 @@ http://maillist.kernel.dk/fio-devel/ Building -------- -Just type 'make' and 'make install'. If on FreeBSD, for now you have to -specify the FreeBSD Makefile with -f and use gmake (not make), eg: +Just type 'make' and 'make install'. If on BSD, for now you have to +specify the BSD Makefile with -f and use gmake (not make), eg: -$ gmake -f Makefile.Freebsd && gmake -f Makefile.FreeBSD install +$ gmake -f Makefile.FreeBSD && gmake -f Makefile.FreeBSD install Same goes for AIX: @@ -132,7 +136,7 @@ unless they match a job file parameter. You can add as many as you want, each job file will be regarded as a separate group and fio will stonewall its execution. -The --readonly switch is an extra safety guard to prevent accidentically +The --readonly switch is an extra safety guard to prevent accidentally turning on a write setting when that is not desired. Fio will only write if rw=write/randwrite/rw/randrw is given, but this extra safety net can be used as an extra precaution. It will also enable a write check in the @@ -294,8 +298,8 @@ The job file parameters are: Platforms --------- -Fio works on (at least) Linux, Solaris, AIX, OSX, NetBSD, and FreeBSD. Some -features and/or options may only be available on some of the platforms, +Fio works on (at least) Linux, Solaris, AIX, OSX, NetBSD, Windows and FreeBSD. +Some features and/or options may only be available on some of the platforms, typically because those features only apply to that platform (like the solarisaio engine, or the splice engine on Linux). diff --git a/engines/windowsaio.c b/engines/windowsaio.c new file mode 100644 index 00000000..5c9d85e1 --- /dev/null +++ b/engines/windowsaio.c @@ -0,0 +1,487 @@ +/* + * Native Windows async IO engine + * Copyright (C) 2010 Bruce Cran + */ + + +#include +#include +#include +#include +#include +#include + +#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 thread_ctx { + 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 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); +static int fio_windowsaio_queue(struct thread_data *td, + struct io_u *io_u); +static void fio_windowsaio_cleanup(struct thread_data *td); +static DWORD WINAPI IoCompletionRoutine(LPVOID lpParameter); +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); + +static void PrintError(LPCSTR lpszFunction) +{ + // Retrieve the system error message for the last-error code + + LPSTR lpMsgBuf; + DWORD dw = GetLastError(); + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + dw, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&lpMsgBuf, + 0, NULL ); + + log_err("%s - %s", lpszFunction, lpMsgBuf); + LocalFree(lpMsgBuf); +} + +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); + + if (!bSuccess) + 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) +{ + BOOL expired = FALSE; + DWORD currentTime; + + if (startCount == 0 || endCount == 0) + return FALSE; + + currentTime = GetTickCount(); + + if ((endCount > startCount) && currentTime >= endCount) + expired = TRUE; + else if (currentTime < startCount && currentTime > endCount) + expired = TRUE; + + if (windowsaio_debug) + printf("windowsaio: timedout = %d\n", expired); + + return expired; +} + +static int fio_windowsaio_getevents(struct thread_data *td, unsigned int min, + unsigned int max, struct timespec *t) +{ + struct windowsaio_data *wd = td->io_ops->data; + struct flist_head *entry; + unsigned int dequeued = 0; + struct io_u *io_u; + DWORD startCount = 0, endCount = 0; + BOOL timedout = FALSE; + unsigned int r = 0; + + if (windowsaio_debug) + printf("getevents (min %d, max %d)\n", min, max); + + if (t != NULL) { + startCount = GetTickCount(); + endCount = GetEndCount(startCount, t); + } + + 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 (dequeued == max) + break; + } + + if (TimedOut(startCount, endCount)) + timedout = TRUE; + + if (dequeued < min && !timedout) + Sleep(250); + } + + if (windowsaio_debug) + printf("leave getevents (%d)\n", dequeued); + + return dequeued; +} + +static struct io_u *fio_windowsaio_event(struct thread_data *td, int event) +{ + struct windowsaio_data *wd = td->io_ops->data; + return wd->aio_events[event]; +} + +static int fio_windowsaio_queue(struct thread_data *td, + struct io_u *io_u) +{ + FIO_OVERLAPPED *fov; + DWORD ioBytes; + BOOL bSuccess = TRUE; + int rc; + + fio_ro_check(td, io_u); + + if (windowsaio_debug) + printf("enqueue enter\n"); + + fov = malloc(sizeof(FIO_OVERLAPPED)); + ZeroMemory(fov, sizeof(FIO_OVERLAPPED)); + + 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 || + io_u->ddir == DDIR_DATASYNC || + io_u->ddir == DDIR_SYNC_FILE_RANGE) + { + FlushFileBuffers(io_u->file->hFile); + return FIO_Q_COMPLETED; + } else if (io_u->ddir == DDIR_TRIM) { + log_info("explicit TRIM isn't supported on Windows"); + return FIO_Q_COMPLETED; + } + + if (bSuccess) { + io_u->seen = 1; + io_u->resid = io_u->xfer_buflen - fov->o.InternalHigh; + io_u->error = 0; + rc = FIO_Q_COMPLETED; + } else if (!bSuccess && GetLastError() == ERROR_IO_PENDING) { + rc = FIO_Q_QUEUED; + } else { + PrintError(__func__); + io_u->error = GetLastError(); + io_u->resid = io_u->xfer_buflen; + 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) +{ + 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); + + if (wd != NULL) { + CloseHandle(wd->hThread); + free(wd->aio_events); + wd->aio_events = NULL; + free(wd); + td->io_ops->data = NULL; + } + + if (windowsaio_debug) + printf("windowsaio: cleanup - leave\n"); +} + +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; + } + } + + 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) + 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->seen = 1; + CloseHandle(ovl->hEvent); + free(ovl); + } + + bSuccess = CloseHandle(ctx->ioCP); + if (!bSuccess) + PrintError(__func__); + + if (windowsaio_debug) + printf("windowsaio: IoCompletionRoutine - leave\n"); + + ctx->wd->stopped = TRUE; + 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"); + + wd = malloc(sizeof(struct windowsaio_data)); + + 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 *)); + + td->io_ops->data = wd; + return rc; +} + +static int fio_windowsaio_open_file(struct thread_data *td, struct fio_file *f) +{ + int rc = 0; + HANDLE hFile; + DWORD flags = FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_POSIX_SEMANTICS | FILE_FLAG_OVERLAPPED; + DWORD sharemode = FILE_SHARE_READ | FILE_SHARE_WRITE; + DWORD openmode = OPEN_ALWAYS; + DWORD access; + + 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; + } + + if (!strcmp(f->file_name, "-")) { + log_err("fio: can't read/write to stdin/out\n"); + return 1; + } + + if (td->o.odirect) + flags |= FILE_FLAG_NO_BUFFERING; + if (td->o.sync_io) + flags |= FILE_FLAG_WRITE_THROUGH; + + + if (td->o.td_ddir == TD_DDIR_READ || + td->o.td_ddir == TD_DDIR_WRITE || + td->o.td_ddir == TD_DDIR_RANDRW) + { + flags |= FILE_FLAG_SEQUENTIAL_SCAN; + } + else + { + flags |= FILE_FLAG_RANDOM_ACCESS; + } + + if (td_read(td) || read_only) + access = GENERIC_READ; + else + access = (GENERIC_READ | GENERIC_WRITE); + + if (td->o.create_on_open > 0) + openmode = OPEN_ALWAYS; + else + openmode = OPEN_EXISTING; + + f->hFile = CreateFile(f->file_name, access, sharemode, + NULL, openmode, flags, NULL); + + if (f->hFile == INVALID_HANDLE_VALUE) { + log_err("Failed to open %s\n", f->file_name); + PrintError(__func__); + rc = 1; + } + + /* Only set up the competion port and thread if we're not just + * querying the device size */ + if (!rc && td->io_ops->data != NULL) { + struct windowsaio_data *wd; + struct thread_ctx *ctx; + 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->hThread = CreateThread(NULL, 0, IoCompletionRoutine, ctx, 0, NULL); + + if (wd->hThread == 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; + + if (windowsaio_debug) + printf("windowsaio: close file\n"); + + if (f->hFile != INVALID_HANDLE_VALUE) { + bSuccess = CloseHandle(f->hFile); + if (!bSuccess) + PrintError(__func__); + } + + f->hFile = INVALID_HANDLE_VALUE; + return 0; +} + +static struct ioengine_ops ioengine = { + .name = "windowsaio", + .version = FIO_IOOPS_VERSION, + .init = fio_windowsaio_init, + .queue = fio_windowsaio_queue, + .cancel = fio_windowsaio_cancel, + .getevents = fio_windowsaio_getevents, + .event = fio_windowsaio_event, + .cleanup = fio_windowsaio_cleanup, + .open_file = fio_windowsaio_open_file, + .close_file = fio_windowsaio_close_file, + .get_file_size = generic_get_file_size +}; + +static void fio_init fio_posixaio_register(void) +{ + register_ioengine(&ioengine); +} + +static void fio_exit fio_posixaio_unregister(void) +{ + unregister_ioengine(&ioengine); +} diff --git a/file.h b/file.h index 994d5fd0..10c5379c 100644 --- a/file.h +++ b/file.h @@ -1,7 +1,9 @@ #ifndef FIO_FILE_H #define FIO_FILE_H +#include "compiler/compiler.h" #include "io_ddir.h" +#include "flist.h" /* * The type of object we are working on diff --git a/filesetup.c b/filesetup.c index 5b96c66f..9d42deb0 100644 --- a/filesetup.c +++ b/filesetup.c @@ -11,6 +11,7 @@ #include "fio.h" #include "smalloc.h" #include "filehash.h" +#include "os/os.h" static int root_warn; @@ -241,7 +242,7 @@ static int bdev_size(struct thread_data *td, struct fio_file *f) return 1; } - r = blockdev_size(f->fd, &bytes); + r = blockdev_size(f, &bytes); if (r) { td_verror(td, r, "blockdev_size"); printf("fd is %d\n", f->fd); @@ -273,7 +274,7 @@ static int char_size(struct thread_data *td, struct fio_file *f) return 1; } - r = chardev_size(f->fd, &bytes); + r = chardev_size(f, &bytes); if (r) { td_verror(td, r, "chardev_size"); goto err; @@ -351,9 +352,9 @@ static int __file_invalidate_cache(struct thread_data *td, struct fio_file *f, (void) posix_madvise(f->mmap_ptr, f->mmap_sz, FIO_MADV_FREE); #endif } else if (f->filetype == FIO_TYPE_FILE) { - ret = fadvise(f->fd, off, len, POSIX_FADV_DONTNEED); + ret = posix_fadvise(f->fd, off, len, POSIX_FADV_DONTNEED); } else if (f->filetype == FIO_TYPE_BD) { - ret = blockdev_invalidate_cache(f->fd); + ret = blockdev_invalidate_cache(f); if (ret < 0 && errno == EACCES && geteuid()) { if (!root_warn) { log_err("fio: only root may flush block " @@ -885,7 +886,9 @@ static void get_file_type(struct fio_file *f) f->filetype = FIO_TYPE_FILE; if (!stat(f->file_name, &sb)) { - if (S_ISBLK(sb.st_mode)) + /* \\.\ is the device namespace in Windows, where every file is + * a block device */ + if (S_ISBLK(sb.st_mode) || strncmp(f->file_name, "\\\\.\\", 4) == 0) f->filetype = FIO_TYPE_BD; else if (S_ISCHR(sb.st_mode)) f->filetype = FIO_TYPE_CHAR; diff --git a/fio.c b/fio.c index 2420d103..93482f52 100644 --- a/fio.c +++ b/fio.c @@ -161,13 +161,8 @@ static void posix_timer_setup(void) evt.sigev_notify = SIGEV_THREAD; evt.sigev_notify_function = ival_fn; -#ifndef __CYGWIN__ - if (timer_create(CLOCK_MONOTONIC, &evt, &ival_timer) < 0) + if (timer_create(FIO_TIMER_CLOCK, &evt, &ival_timer) < 0) perror("timer_create"); -#else /* Windows (and thus Cygwin) doesn't have a monotonic clock */ - if (timer_create(CLOCK_REALTIME, &evt, &ival_timer) < 0) - perror("timer_create"); -#endif } diff --git a/fio.h b/fio.h index c04ad8b2..95988584 100644 --- a/fio.h +++ b/fio.h @@ -14,6 +14,8 @@ #include #include +struct thread_data; + #include "compiler/compiler.h" #include "flist.h" #include "fifo.h" diff --git a/init.c b/init.c index bdbb3774..0a3529d0 100644 --- a/init.c +++ b/init.c @@ -431,7 +431,9 @@ static int exists_and_not_file(const char *filename) if (lstat(filename, &sb) == -1) return 0; - if (S_ISREG(sb.st_mode)) + /* \\.\ is the device namespace in Windows, where every file + * is a device node */ + if (S_ISREG(sb.st_mode) && strncmp(filename, "\\\\.\\", 4) != 0) return 0; return 1; diff --git a/ioengines.c b/ioengines.c index 35471ad1..6b677cb9 100644 --- a/ioengines.c +++ b/ioengines.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "fio.h" @@ -386,7 +387,7 @@ int td_io_open_file(struct thread_data *td, struct fio_file *f) else flags = POSIX_FADV_SEQUENTIAL; - if (fadvise(f->fd, f->file_offset, f->io_size, flags) < 0) { + if (posix_fadvise(f->fd, f->file_offset, f->io_size, flags) < 0) { td_verror(td, errno, "fadvise"); goto err; } diff --git a/os/os-aix.h b/os/os-aix.h index 09a0530f..b5ee5bb3 100644 --- a/os/os-aix.h +++ b/os/os-aix.h @@ -9,6 +9,7 @@ #define FIO_HAVE_POSIXAIO #define FIO_HAVE_ODIRECT #define FIO_USE_GENERIC_RAND +#define FIO_HAVE_CLOCK_MONOTONIC /* * This is broken on AIX if _LARGE_FILES is defined... @@ -22,16 +23,16 @@ #define OS_MAP_ANON MAP_ANON #define OS_MSG_DONTWAIT 0 -static inline int blockdev_invalidate_cache(int fd) +static inline int blockdev_invalidate_cache(struct fio_file fio_unused *f) { return EINVAL; } -static inline int blockdev_size(int fd, unsigned long long *bytes) +static inline int blockdev_size(struct fio_file *f, unsigned long long *bytes) { struct devinfo info; - if (!ioctl(fd, IOCINFO, &info)) { + if (!ioctl(f->fd, IOCINFO, &info)) { *bytes = (unsigned long long)info.un.scdk.numblks * info.un.scdk.blksize; return 0; diff --git a/os/os-freebsd.h b/os/os-freebsd.h index 4a50a3cf..0cb7ae5e 100644 --- a/os/os-freebsd.h +++ b/os/os-freebsd.h @@ -7,18 +7,21 @@ #define FIO_HAVE_POSIXAIO #define FIO_HAVE_ODIRECT +#define FIO_HAVE_IOPRIO +#define FIO_HAVE_STRSEP #define FIO_USE_GENERIC_RAND #define FIO_HAVE_CHARDEV_SIZE +#define FIO_HAVE_CLOCK_MONOTONIC #define OS_MAP_ANON MAP_ANON typedef off_t off64_t; -static inline int blockdev_size(int fd, unsigned long long *bytes) +static inline int blockdev_size(struct fio_file *f, unsigned long long *bytes) { off_t size; - if (!ioctl(fd, DIOCGMEDIASIZE, &size)) { + if (!ioctl(f->fd, DIOCGMEDIASIZE, &size)) { *bytes = size; return 0; } @@ -27,12 +30,12 @@ static inline int blockdev_size(int fd, unsigned long long *bytes) return errno; } -static inline int chardev_size(int fd, unsigned long long *bytes) +static inline int chardev_size(struct fio_file *f, unsigned long long *bytes) { - return blockdev_size(fd, bytes); + return blockdev_size(f->fd, bytes); } -static inline int blockdev_invalidate_cache(int fd) +static inline int blockdev_invalidate_cache(struct fio_file *f) { return EINVAL; } diff --git a/os/os-linux.h b/os/os-linux.h index 20f2a94e..aab74e58 100644 --- a/os/os-linux.h +++ b/os/os-linux.h @@ -39,6 +39,7 @@ #define FIO_HAVE_FS_STAT #define FIO_HAVE_TRIM #define FIO_HAVE_BINJECT +#define FIO_HAVE_CLOCK_MONOTONIC #ifdef SYNC_FILE_RANGE_WAIT_BEFORE #define FIO_HAVE_SYNC_FILE_RANGE @@ -192,14 +193,14 @@ enum { #define BLKDISCARD _IO(0x12,119) #endif -static inline int blockdev_invalidate_cache(int fd) +static inline int blockdev_invalidate_cache(struct fio_file *fd) { - return ioctl(fd, BLKFLSBUF); + return ioctl(f->fd, BLKFLSBUF); } -static inline int blockdev_size(int fd, unsigned long long *bytes) +static inline int blockdev_size(struct fio_file *f, unsigned long long *bytes) { - if (!ioctl(fd, BLKGETSIZE64, bytes)) + if (!ioctl(f->fd, BLKGETSIZE64, bytes)) return 0; return errno; diff --git a/os/os-mac.h b/os/os-mac.h index 1b786f83..39a2f827 100644 --- a/os/os-mac.h +++ b/os/os-mac.h @@ -13,6 +13,7 @@ #endif #define FIO_HAVE_POSIXAIO +#define FIO_HAVE_CLOCK_MONOTONIC #define FIO_USE_GENERIC_BDEV_SIZE #define FIO_USE_GENERIC_RAND @@ -21,7 +22,7 @@ typedef unsigned int clockid_t; typedef off_t off64_t; -static inline int blockdev_invalidate_cache(int fd) +static inline int blockdev_invalidate_cache(struct fio_file fio_unused *f) { return EINVAL; } diff --git a/os/os-netbsd.h b/os/os-netbsd.h index 70d2958b..ecfcddbb 100644 --- a/os/os-netbsd.h +++ b/os/os-netbsd.h @@ -12,10 +12,10 @@ #define FIO_HAVE_POSIXAIO #define FIO_HAVE_FADVISE -#define fadvise posix_fadvise /* XXX NetBSD doesn't have fadvise */ #define FIO_HAVE_ODIRECT #define FIO_HAVE_STRSEP #define FIO_HAVE_FDATASYNC +#define FIO_HAVE_CLOCK_MONOTONIC #define FIO_USE_GENERIC_BDEV_SIZE #define FIO_USE_GENERIC_RAND @@ -25,7 +25,7 @@ typedef off_t off64_t; -static inline int blockdev_invalidate_cache(int fd) +static inline int blockdev_invalidate_cache(struct fio_file fio_unused *f) { return EINVAL; } diff --git a/os/os-solaris.h b/os/os-solaris.h index f8ce1f31..14164b01 100644 --- a/os/os-solaris.h +++ b/os/os-solaris.h @@ -15,6 +15,7 @@ #define FIO_HAVE_PSHARED_MUTEX #define FIO_USE_GENERIC_BDEV_SIZE #define FIO_HAVE_FDATASYNC +#define FIO_HAVE_CLOCK_MONOTONIC #define OS_MAP_ANON MAP_ANON #define OS_RAND_MAX 2147483648UL @@ -26,7 +27,7 @@ struct solaris_rand_seed { typedef psetid_t os_cpu_mask_t; typedef struct solaris_rand_seed os_random_state_t; -static inline int blockdev_invalidate_cache(int fd) +static inline int blockdev_invalidate_cache(struct fio_file fio_unused *f) { return EINVAL; } diff --git a/os/os-windows.h b/os/os-windows.h index f5da6a5d..74c0f9e8 100644 --- a/os/os-windows.h +++ b/os/os-windows.h @@ -4,7 +4,11 @@ #include #include +#include +#include "../smalloc.h" +#include "../file.h" +#include "../log.h" #define FIO_HAVE_ODIRECT #define FIO_USE_GENERIC_RAND @@ -15,28 +19,11 @@ #define FIO_HAVE_FDATASYNC #define FIO_HAVE_WINDOWSAIO -/* TODO add support for FIO_HAVE_CPU_AFFINITY */ - #define OS_MAP_ANON MAP_ANON -typedef off_t off64_t; - - -#define FIO_NOTUNIX - -#include -#include +#define OS_CLOCK CLOCK_REALTIME -typedef void* HANDLE; - -BOOL WINAPI GetFileSizeEx( - HANDLE hFile, - PLARGE_INTEGER lpFileSize -); - -long _get_osfhandle( - int fd -); +typedef off_t off64_t; typedef struct { LARGE_INTEGER Length; @@ -44,42 +31,50 @@ typedef struct { #define IOCTL_DISK_GET_LENGTH_INFO 0x7405C - -static inline int blockdev_size(int fd, unsigned long long *bytes) +static inline int blockdev_size(struct fio_file *f, unsigned long long *bytes) { int rc = 0; - HANDLE hFile = (HANDLE)_get_osfhandle(fd); - if (hFile != INVALID_HANDLE_VALUE) - { - GET_LENGTH_INFORMATION info; - DWORD outBytes; - LARGE_INTEGER size; - size.QuadPart = 0; - if (DeviceIoControl(hFile, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &info, sizeof(info), &outBytes, NULL)) - *bytes = info.Length.QuadPart; - else - rc = EIO; + HANDLE hFile; + + if (f->hFile == NULL) { + hFile = CreateFile(f->file_name, (GENERIC_READ | GENERIC_WRITE), + (FILE_SHARE_READ | FILE_SHARE_WRITE), NULL, OPEN_EXISTING, 0, NULL); + } else { + hFile = f->hFile; } - return 0; + GET_LENGTH_INFORMATION info; + DWORD outBytes; + LARGE_INTEGER size; + size.QuadPart = 0; + if (DeviceIoControl(hFile, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &info, sizeof(info), &outBytes, NULL)) + *bytes = info.Length.QuadPart; + else + rc = EIO; + } + + /* If we were passed a POSIX fd, + * close the HANDLE we created via CreateFile */ + if (hFile != INVALID_HANDLE_VALUE && f->hFile == NULL) + CloseHandle(hFile); + + return rc; } -static inline int chardev_size(int fd, unsigned long long *bytes) +static inline int chardev_size(struct fio_file *f, unsigned long long *bytes) { - return blockdev_size(fd, bytes); + return blockdev_size(f, bytes); } -static inline int blockdev_invalidate_cache(int fd) { - int rc = 0; - HANDLE hFile = (HANDLE)_get_osfhandle(fd); - if (hFile != INVALID_HANDLE_VALUE) - FlushFileBuffers(hFile); - else - rc = EIO; +static inline int blockdev_invalidate_cache(struct fio_file *f) +{ + BOOL bSuccess = FlushFileBuffers(f->hFile); + if (!bSuccess) + log_info("blockdev_invalidate_cache - FlushFileBuffers failed\n"); - return rc; + return 0; } static inline unsigned long long os_phys_mem(void) diff --git a/os/os.h b/os/os.h index 95f0ff29..92b6950d 100644 --- a/os/os.h +++ b/os/os.h @@ -4,6 +4,7 @@ #include #include #include +#include #if defined(__linux__) #include "os-linux.h" @@ -45,7 +46,7 @@ #endif #ifndef FIO_HAVE_FADVISE -#define fadvise(fd, off, len, advice) (0) +#define posix_fadvise(fd, off, len, advice) (0) #ifndef POSIX_FADV_DONTNEED #define POSIX_FADV_DONTNEED (0) @@ -91,6 +92,12 @@ typedef unsigned long os_cpu_mask_t; #define OS_RAND_MAX RAND_MAX #endif +#ifdef FIO_HAVE_CLOCK_MONOTONIC +#define FIO_TIMER_CLOCK CLOCK_MONOTONIC +#else +#define FIO_TIMER_CLOCK CLOCK_REALTIME +#endif + #ifndef FIO_HAVE_RAWBIND #define fio_lookup_raw(dev, majdev, mindev) 1 #endif @@ -124,13 +131,13 @@ static inline int os_cache_line_size(void) } #ifdef FIO_USE_GENERIC_BDEV_SIZE -static inline int blockdev_size(int fd, unsigned long long *bytes) +static inline int blockdev_size(struct fio_file *f, unsigned long long *bytes) { off_t end; *bytes = 0; - end = lseek(fd, 0, SEEK_END); + end = lseek(f->fd, 0, SEEK_END); if (end < 0) return errno; diff --git a/os/windows/version.h b/os/windows/version.h new file mode 100644 index 00000000..3642342c --- /dev/null +++ b/os/windows/version.h @@ -0,0 +1,4 @@ +#define FIO_VERSION_MAJOR 1 +#define FIO_VERSION_MINOR 44 +#define FIO_VERSION_BUILD 3 +#define FIO_VERSION_STRING "1.44.3" diff --git a/os/windows/version.rc b/os/windows/version.rc index f13ba062..d699f097 100755 --- a/os/windows/version.rc +++ b/os/windows/version.rc @@ -1,7 +1,7 @@ -#include "../../version.h" +#include "version.h" 1 VERSIONINFO -FILEVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_BUILD,0 -PRODUCTVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_BUILD,0 +FILEVERSION FIO_VERSION_MAJOR,FIO_VERSION_MINOR,FIO_VERSION_BUILD,0 +PRODUCTVERSION FIO_VERSION_MAJOR,FIO_VERSION_MINOR,FIO_VERSION_BUILD,0 BEGIN BLOCK "StringFileInfo" BEGIN @@ -9,12 +9,12 @@ BEGIN BEGIN VALUE "CompanyName", "" VALUE "FileDescription", "Flexible IO Tester" - VALUE "FileVersion", "1.44.3" + VALUE "FileVersion", FIO_VERSION_STRING VALUE "InternalName", "fio" VALUE "LegalCopyright", "Copyright (C) 2010" VALUE "OriginalFilename", "fio.exe" VALUE "ProductName", "FIO" - VALUE "ProductVersion", VERSION_STR + VALUE "ProductVersion", FIO_VERSION_STRING END END BLOCK "VarFileInfo" -- 2.25.1