From 5f9099ea8adf423d0db01274ef0d7e65629c0e1c Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 16 Jun 2009 22:40:26 +0200 Subject: [PATCH] Add support for fdatasync() Adds a new option, fdatasync=. It's identical to the fsync= option, but uses fdatasync() instead. Signed-off-by: Jens Axboe --- HOWTO | 3 +++ engines/guasi.c | 2 +- engines/libaio.c | 9 ++++++++- engines/mmap.c | 4 ++-- engines/sg.c | 2 +- engines/solarisaio.c | 9 +++++++++ engines/sync.c | 10 +++++++--- engines/syslet-rw.c | 8 ++++++++ fio.1 | 4 ++++ fio.h | 1 + io_ddir.h | 6 ++++++ io_u.c | 34 +++++++++++++++++++++++----------- ioengines.c | 6 +++--- log.c | 6 ++++-- options.c | 7 +++++++ 15 files changed, 87 insertions(+), 24 deletions(-) diff --git a/HOWTO b/HOWTO index 0eab6e11..f323c204 100644 --- a/HOWTO +++ b/HOWTO @@ -530,6 +530,9 @@ fsync=int If writing to a file, issue a sync of the dirty data not sync the file. The exception is the sg io engine, which synchronizes the disk cache anyway. +fsyncdata=int Like fsync= but uses fdatasync() to only sync data and not + metadata blocks. + overwrite=bool If true, writes to a file will always overwrite existing data. If the file doesn't already exist, it will be created before the write phase begins. If the file exists diff --git a/engines/guasi.c b/engines/guasi.c index 3802f2c2..15d4801a 100644 --- a/engines/guasi.c +++ b/engines/guasi.c @@ -174,7 +174,7 @@ static int fio_guasi_commit(struct thread_data *td) io_u->greq = guasi__pwrite(ld->hctx, ld, io_u, 0, f->fd, io_u->xfer_buf, io_u->xfer_buflen, io_u->offset); - else if (io_u->ddir == DDIR_SYNC) + else if (ddir_sync(io_u->ddir)) io_u->greq = guasi__fsync(ld->hctx, ld, io_u, 0, f->fd); else { log_err("fio_guasi_commit() FAILED: unknow request %d\n", diff --git a/engines/libaio.c b/engines/libaio.c index e452f1ca..bd8ebb8b 100644 --- a/engines/libaio.c +++ b/engines/libaio.c @@ -32,7 +32,7 @@ static int fio_libaio_prep(struct thread_data fio_unused *td, struct io_u *io_u) io_prep_pread(&io_u->iocb, f->fd, io_u->xfer_buf, io_u->xfer_buflen, io_u->offset); else if (io_u->ddir == DDIR_WRITE) io_prep_pwrite(&io_u->iocb, f->fd, io_u->xfer_buf, io_u->xfer_buflen, io_u->offset); - else if (io_u->ddir == DDIR_SYNC) + else if (ddir_sync(io_u->ddir)) io_prep_fsync(&io_u->iocb, f->fd); else return 1; @@ -103,6 +103,13 @@ static int fio_libaio_queue(struct thread_data *td, struct io_u *io_u) if (fsync(io_u->file->fd) < 0) io_u->error = errno; + return FIO_Q_COMPLETED; + } else if (io_u->ddir == DDIR_DATASYNC) { + if (ld->iocbs_nr) + return FIO_Q_BUSY; + if (fdatasync(io_u->file->fd) < 0) + io_u->error = errno; + return FIO_Q_COMPLETED; } diff --git a/engines/mmap.c b/engines/mmap.c index fde68f1d..6671fc0e 100644 --- a/engines/mmap.c +++ b/engines/mmap.c @@ -113,7 +113,7 @@ static int fio_mmapio_queue(struct thread_data *td, struct io_u *io_u) memcpy(io_u->xfer_buf, io_u->mmap_data, io_u->xfer_buflen); else if (io_u->ddir == DDIR_WRITE) memcpy(io_u->mmap_data, io_u->xfer_buf, io_u->xfer_buflen); - else if (io_u->ddir == DDIR_SYNC) { + else if (ddir_sync(io_u->ddir)) { if (msync(f->mmap_ptr, f->mmap_sz, MS_SYNC)) { io_u->error = errno; td_verror(td, io_u->error, "msync"); @@ -123,7 +123,7 @@ static int fio_mmapio_queue(struct thread_data *td, struct io_u *io_u) /* * not really direct, but should drop the pages from the cache */ - if (td->o.odirect && io_u->ddir != DDIR_SYNC) { + if (td->o.odirect && !ddir_sync(io_u->ddir)) { if (msync(io_u->mmap_data, io_u->xfer_buflen, MS_SYNC) < 0) { io_u->error = errno; td_verror(td, io_u->error, "msync"); diff --git a/engines/sg.c b/engines/sg.c index 39f99d8d..0ed39b3d 100644 --- a/engines/sg.c +++ b/engines/sg.c @@ -244,7 +244,7 @@ static int fio_sgio_queue(struct thread_data *td, struct io_u *io_u) fio_ro_check(td, io_u); - ret = fio_sgio_doio(td, io_u, io_u->ddir == DDIR_SYNC); + ret = fio_sgio_doio(td, io_u, ddir_sync(io_u->ddir)); if (ret < 0) io_u->error = errno; diff --git a/engines/solarisaio.c b/engines/solarisaio.c index a48ec418..17b7e22c 100644 --- a/engines/solarisaio.c +++ b/engines/solarisaio.c @@ -126,6 +126,15 @@ static int fio_solarisaio_queue(struct thread_data fio_unused *td, return FIO_Q_COMPLETED; } + if (io_u->ddir == DDIR_DATASYNC) { + if (sd->nr) + return FIO_Q_BUSY; + if (fdatasync(f->fd) < 0) + io_u->error = errno; + + return FIO_Q_COMPLETED; + } + if (sd->nr == sd->max_depth) return FIO_Q_BUSY; diff --git a/engines/sync.c b/engines/sync.c index 842c6c0b..7c1cca63 100644 --- a/engines/sync.c +++ b/engines/sync.c @@ -30,7 +30,7 @@ static int fio_syncio_prep(struct thread_data *td, struct io_u *io_u) { struct fio_file *f = io_u->file; - if (io_u->ddir == DDIR_SYNC) + if (ddir_sync(io_u->ddir)) return 0; if (lseek(f->fd, io_u->offset, SEEK_SET) == -1) { @@ -120,7 +120,7 @@ static int fio_vsyncio_append(struct thread_data *td, struct io_u *io_u) { struct syncio_data *sd = td->io_ops->data; - if (io_u->ddir == DDIR_SYNC) + if (ddir_sync(io_u->ddir)) return 0; if (io_u->offset == sd->last_offset && io_u->file == sd->last_file && @@ -161,8 +161,12 @@ static int fio_vsyncio_queue(struct thread_data *td, struct io_u *io_u) int ret = fsync(io_u->file->fd); return fio_io_end(td, io_u, ret); - } + } else if (io_u->ddir == DDIR_DATASYNC) { + int ret = fdatasync(io_u->file->fd); + return fio_io_end(td, io_u, ret); + } + sd->queued = 0; sd->queued_bytes = 0; fio_vsyncio_set_iov(sd, io_u, 0); diff --git a/engines/syslet-rw.c b/engines/syslet-rw.c index ad9cb359..c11e4f21 100644 --- a/engines/syslet-rw.c +++ b/engines/syslet-rw.c @@ -131,6 +131,12 @@ static void fio_syslet_prep_sync(struct fio_file *f, FILL_IN(*regs, __NR_fsync, (long) f->fd); } +static void fio_syslet_prep_datasync(struct fio_file *f, + struct indirect_registers *regs) +{ + FILL_IN(*regs, __NR_fdatasync, (long) f->fd); +} + static void fio_syslet_prep_rw(struct io_u *io_u, struct fio_file *f, struct indirect_registers *regs) { @@ -154,6 +160,8 @@ static void fio_syslet_prep(struct io_u *io_u, struct indirect_registers *regs) if (io_u->ddir == DDIR_SYNC) fio_syslet_prep_sync(f, regs); + else if (io_u->ddir == DDIR_DATASYNC) + fio_syslet_prep_datasync(f, regs); else fio_syslet_prep_rw(io_u, f, regs); } diff --git a/fio.1 b/fio.1 index 637304e5..fc055e4d 100644 --- a/fio.1 +++ b/fio.1 @@ -365,6 +365,10 @@ Offset in the file to start I/O. Data before the offset will not be touched. How many I/Os to perform before issuing an \fBfsync\fR\|(2) of dirty data. If 0, don't sync. Default: 0. .TP +.BI fdatasync \fR=\fPint +Like \fBfsync\fR, but uses \fBfdatasync\fR\|(2) instead to only sync the +data parts of the file. Default: 0. +.TP .BI overwrite \fR=\fPbool If writing, setup the file first and do overwrites. Default: false. .TP diff --git a/fio.h b/fio.h index 7c059d10..71ae71db 100644 --- a/fio.h +++ b/fio.h @@ -192,6 +192,7 @@ struct thread_options { unsigned int thinktime_spin; unsigned int thinktime_blocks; unsigned int fsync_blocks; + unsigned int fdatasync_blocks; unsigned int start_delay; unsigned long long timeout; unsigned long long ramp_time; diff --git a/io_ddir.h b/io_ddir.h index d2ee40ce..620a9ee3 100644 --- a/io_ddir.h +++ b/io_ddir.h @@ -5,6 +5,7 @@ enum fio_ddir { DDIR_READ = 0, DDIR_WRITE, DDIR_SYNC, + DDIR_DATASYNC, DDIR_INVAL = -1, }; @@ -24,4 +25,9 @@ enum td_ddir { #define td_random(td) ((td)->o.td_ddir & TD_DDIR_RAND) #define file_randommap(td, f) (!(td)->o.norandommap && (f)->file_map) +static inline int ddir_sync(enum fio_ddir ddir) +{ + return ddir == DDIR_SYNC || ddir == DDIR_DATASYNC; +} + #endif diff --git a/io_u.c b/io_u.c index 276f3b0c..41b5cdbb 100644 --- a/io_u.c +++ b/io_u.c @@ -363,6 +363,22 @@ static enum fio_ddir get_rw_ddir(struct thread_data *td) { enum fio_ddir ddir; + /* + * see if it's time to fsync + */ + if (td->o.fsync_blocks && + !(td->io_issues[DDIR_WRITE] % td->o.fsync_blocks) && + td->io_issues[DDIR_WRITE] && should_fsync(td)) + return DDIR_SYNC; + + /* + * see if it's time to fdatasync + */ + if (td->o.fdatasync_blocks && + !(td->io_issues[DDIR_WRITE] % td->o.fdatasync_blocks) && + td->io_issues[DDIR_WRITE] && should_fsync(td)) + return DDIR_DATASYNC; + if (td_rw(td)) { /* * Check if it's time to seed a new data direction. @@ -425,7 +441,7 @@ void requeue_io_u(struct thread_data *td, struct io_u **io_u) dprint(FD_IO, "requeue %p\n", __io_u); __io_u->flags |= IO_U_F_FREE; - if ((__io_u->flags & IO_U_F_FLIGHT) && (__io_u->ddir != DDIR_SYNC)) + if ((__io_u->flags & IO_U_F_FLIGHT) && !ddir_sync(__io_u->ddir)) td->io_issues[__io_u->ddir]--; __io_u->flags &= ~IO_U_F_FLIGHT; @@ -441,17 +457,13 @@ static int fill_io_u(struct thread_data *td, struct io_u *io_u) if (td->io_ops->flags & FIO_NOIO) goto out; + io_u->ddir = get_rw_ddir(td); + /* - * see if it's time to sync + * fsync() or fdatasync(), we are done */ - if (td->o.fsync_blocks && - !(td->io_issues[DDIR_WRITE] % td->o.fsync_blocks) && - td->io_issues[DDIR_WRITE] && should_fsync(td)) { - io_u->ddir = DDIR_SYNC; + if (ddir_sync(io_u->ddir)) goto out; - } - - io_u->ddir = get_rw_ddir(td); /* * See if it's time to switch to a new zone @@ -878,7 +890,7 @@ struct io_u *get_io_u(struct thread_data *td) f = io_u->file; assert(fio_file_open(f)); - if (io_u->ddir != DDIR_SYNC) { + if (!ddir_sync(io_u->ddir)) { if (!io_u->buflen && !(td->io_ops->flags & FIO_NOIO)) { dprint(FD_IO, "get_io_u: zero buflen on %p\n", io_u); goto err_put; @@ -942,7 +954,7 @@ static void io_completed(struct thread_data *td, struct io_u *io_u, assert(io_u->flags & IO_U_F_FLIGHT); io_u->flags &= ~IO_U_F_FLIGHT; - if (io_u->ddir == DDIR_SYNC) { + if (ddir_sync(io_u->ddir)) { td->last_was_sync = 1; return; } diff --git a/ioengines.c b/ioengines.c index c143b338..4c97d038 100644 --- a/ioengines.c +++ b/ioengines.c @@ -238,7 +238,7 @@ int td_io_queue(struct thread_data *td, struct io_u *io_u) sizeof(struct timeval)); } - if (io_u->ddir != DDIR_SYNC) + if (!ddir_sync(io_u->ddir)) td->io_issues[io_u->ddir]++; ret = td->io_ops->queue(td, io_u); @@ -251,14 +251,14 @@ int td_io_queue(struct thread_data *td, struct io_u *io_u) } if (ret == FIO_Q_COMPLETED) { - if (io_u->ddir != DDIR_SYNC) { + if (!ddir_sync(io_u->ddir)) { io_u_mark_depth(td, 1); td->ts.total_io_u[io_u->ddir]++; } } else if (ret == FIO_Q_QUEUED) { int r; - if (io_u->ddir != DDIR_SYNC) { + if (!ddir_sync(io_u->ddir)) { td->io_u_queued++; td->ts.total_io_u[io_u->ddir]++; } diff --git a/log.c b/log.c index 5014b484..c44d8221 100644 --- a/log.c +++ b/log.c @@ -20,7 +20,7 @@ void queue_io_piece(struct thread_data *td, struct io_piece *ipo) void log_io_u(struct thread_data *td, struct io_u *io_u) { - const char *act[] = { "read", "write", "sync" }; + const char *act[] = { "read", "write", "sync", "datasync" }; assert(io_u->ddir < 3); @@ -273,6 +273,8 @@ static int read_iolog2(struct thread_data *td, FILE *f) rw = DDIR_WRITE; else if (!strcmp(act, "sync")) rw = DDIR_SYNC; + else if (!strcmp(act, "datasync")) + rw = DDIR_DATASYNC; else { log_err("fio: bad iolog file action: %s\n", act); @@ -310,7 +312,7 @@ static int read_iolog2(struct thread_data *td, FILE *f) if (read_only) continue; writes++; - } else if (rw != DDIR_SYNC && rw != DDIR_INVAL) { + } else if (!ddir_sync(rw)) { log_err("bad ddir: %d\n", rw); continue; } diff --git a/options.c b/options.c index 9606ab20..394472a4 100644 --- a/options.c +++ b/options.c @@ -931,6 +931,13 @@ static struct fio_option options[] = { .help = "Issue fsync for writes every given number of blocks", .def = "0", }, + { + .name = "fdatasync", + .type = FIO_OPT_INT, + .off1 = td_var_offset(fdatasync_blocks), + .help = "Issue fdatasync for writes every given number of blocks", + .def = "0", + }, { .name = "direct", .type = FIO_OPT_BOOL, -- 2.25.1