[PATCH] Implement file syncing as data direction

Instead of defining a seperate ->sync() operation for the io engine,
reuse the io_u infrastructure for committing and reaping sync
events as well. It's a nice cleanup as well.

Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
diff --git a/engines/fio-engine-libaio.c b/engines/fio-engine-libaio.c
index 57daf1b..5e394e3 100644
--- a/engines/fio-engine-libaio.c
+++ b/engines/fio-engine-libaio.c
@@ -17,20 +17,18 @@
 	struct io_event *aio_events;
 };
 
-static int fio_libaio_sync(struct thread_data fio_unused *td,
-			   struct fio_file *f)
-{
-	return fsync(f->fd);
-}
-
 static int fio_libaio_prep(struct thread_data fio_unused *td, struct io_u *io_u)
 {
 	struct fio_file *f = io_u->file;
 
 	if (io_u->ddir == DDIR_READ)
 		io_prep_pread(&io_u->iocb, f->fd, io_u->buf, io_u->buflen, io_u->offset);
-	else
+	else if (io_u->ddir == DDIR_WRITE)
 		io_prep_pwrite(&io_u->iocb, f->fd, io_u->buf, io_u->buflen, io_u->offset);
+	else if (io_u->ddir == DDIR_SYNC)
+		io_prep_fsync(&io_u->iocb, f->fd);
+	else
+		return 1;
 
 	return 0;
 }
@@ -59,7 +57,7 @@
 			break;
 	} while (1);
 
-	return (int) r;
+	return (int) -r;
 }
 
 static int fio_libaio_queue(struct thread_data *td, struct io_u *io_u)
@@ -82,7 +80,7 @@
 
 	assert(ret);
 
-	return (int) ret;
+	return (int) -ret;
 }
 
 static int fio_libaio_cancel(struct thread_data *td, struct io_u *io_u)
@@ -132,5 +130,4 @@
 	.getevents	= fio_libaio_getevents,
 	.event		= fio_libaio_event,
 	.cleanup	= fio_libaio_cleanup,
-	.sync		= fio_libaio_sync,
 };
diff --git a/engines/fio-engine-mmap.c b/engines/fio-engine-mmap.c
index d203d6a..c85f661 100644
--- a/engines/fio-engine-mmap.c
+++ b/engines/fio-engine-mmap.c
@@ -48,8 +48,10 @@
 
 	if (io_u->ddir == DDIR_READ)
 		memcpy(io_u->buf, f->mmap + real_off, io_u->buflen);
-	else
+	else if (io_u->ddir == DDIR_WRITE)
 		memcpy(f->mmap + real_off, io_u->buf, io_u->buflen);
+	else if (io_u->ddir == DDIR_SYNC)
+		return msync(f->mmap, f->file_size, MS_SYNC);
 
 	/*
 	 * not really direct, but should drop the pages from the cache
@@ -67,12 +69,6 @@
 	return io_u->error;
 }
 
-static int fio_mmapio_sync(struct thread_data fio_unused *td,
-			   struct fio_file *f)
-{
-	return msync(f->mmap, f->file_size, MS_SYNC);
-}
-
 static void fio_mmapio_cleanup(struct thread_data *td)
 {
 	if (td->io_ops->data) {
@@ -98,6 +94,5 @@
 	.getevents	= fio_mmapio_getevents,
 	.event		= fio_mmapio_event,
 	.cleanup	= fio_mmapio_cleanup,
-	.sync		= fio_mmapio_sync,
 	.flags		= FIO_SYNCIO | FIO_MMAPIO,
 };
diff --git a/engines/fio-engine-posixaio.c b/engines/fio-engine-posixaio.c
index 260551a..2d0fd31 100644
--- a/engines/fio-engine-posixaio.c
+++ b/engines/fio-engine-posixaio.c
@@ -45,12 +45,6 @@
 	return sec + nsec;
 }
 
-static int fio_posixaio_sync(struct thread_data fio_unused *td,
-			     struct fio_file *f)
-{
-	return fsync(f->fd);
-}
-
 static int fio_posixaio_cancel(struct thread_data fio_unused *td,
 			       struct io_u *io_u)
 {
@@ -149,8 +143,10 @@
 
 	if (io_u->ddir == DDIR_READ)
 		ret = aio_read(aiocb);
-	else
+	else if (io_u->ddir == DDIR_WRITE)
 		ret = aio_write(aiocb);
+	else
+		ret = aio_fsync(O_SYNC, aiocb);
 
 	if (ret)
 		io_u->error = errno;
@@ -189,5 +185,4 @@
 	.getevents	= fio_posixaio_getevents,
 	.event		= fio_posixaio_event,
 	.cleanup	= fio_posixaio_cleanup,
-	.sync		= fio_posixaio_sync,
 };
diff --git a/engines/fio-engine-sg.c b/engines/fio-engine-sg.c
index 01eba8d..112f027 100644
--- a/engines/fio-engine-sg.c
+++ b/engines/fio-engine-sg.c
@@ -151,28 +151,6 @@
 	return fio_sgio_rw_doio(f, io_u, sync);
 }
 
-static int fio_sgio_sync(struct thread_data *td, struct fio_file fio_unused *f)
-{
-	struct sgio_data *sd = td->io_ops->data;
-	struct sg_io_hdr *hdr;
-	struct io_u *io_u;
-	int ret;
-
-	io_u = __get_io_u(td);
-	if (!io_u)
-		return ENOMEM;
-
-	hdr = &io_u->hdr;
-	sgio_hdr_init(sd, hdr, io_u, 0);
-	hdr->dxfer_direction = SG_DXFER_NONE;
-
-	hdr->cmdp[0] = 0x35;
-
-	ret = fio_sgio_doio(td, io_u, 1);
-	put_io_u(td, io_u);
-	return ret;
-}
-
 static int fio_sgio_prep(struct thread_data *td, struct io_u *io_u)
 {
 	struct sg_io_hdr *hdr = &io_u->hdr;
@@ -184,24 +162,34 @@
 		return EINVAL;
 	}
 
-	sgio_hdr_init(sd, hdr, io_u, 1);
-
 	if (io_u->ddir == DDIR_READ) {
+		sgio_hdr_init(sd, hdr, io_u, 1);
+
 		hdr->dxfer_direction = SG_DXFER_FROM_DEV;
 		hdr->cmdp[0] = 0x28;
-	} else {
+	} else if (io_u->ddir == DDIR_WRITE) {
+		sgio_hdr_init(sd, hdr, io_u, 1);
+
 		hdr->dxfer_direction = SG_DXFER_TO_DEV;
 		hdr->cmdp[0] = 0x2a;
+	} else {
+		sgio_hdr_init(sd, hdr, io_u, 0);
+
+		hdr->dxfer_direction = SG_DXFER_NONE;
+		hdr->cmdp[0] = 0x35;
 	}
 
-	nr_blocks = io_u->buflen / sd->bs;
-	lba = io_u->offset / sd->bs;
-	hdr->cmdp[2] = (lba >> 24) & 0xff;
-	hdr->cmdp[3] = (lba >> 16) & 0xff;
-	hdr->cmdp[4] = (lba >>  8) & 0xff;
-	hdr->cmdp[5] = lba & 0xff;
-	hdr->cmdp[7] = (nr_blocks >> 8) & 0xff;
-	hdr->cmdp[8] = nr_blocks & 0xff;
+	if (hdr->dxfer_direction != SG_DXFER_NONE) {
+		nr_blocks = io_u->buflen / sd->bs;
+		lba = io_u->offset / sd->bs;
+		hdr->cmdp[2] = (lba >> 24) & 0xff;
+		hdr->cmdp[3] = (lba >> 16) & 0xff;
+		hdr->cmdp[4] = (lba >>  8) & 0xff;
+		hdr->cmdp[5] = lba & 0xff;
+		hdr->cmdp[7] = (nr_blocks >> 8) & 0xff;
+		hdr->cmdp[8] = nr_blocks & 0xff;
+	}
+
 	return 0;
 }
 
@@ -210,7 +198,7 @@
 	struct sg_io_hdr *hdr = &io_u->hdr;
 	int ret;
 
-	ret = fio_sgio_doio(td, io_u, 0);
+	ret = fio_sgio_doio(td, io_u, io_u->ddir == DDIR_SYNC);
 
 	if (ret < 0)
 		io_u->error = errno;
@@ -324,6 +312,5 @@
 	.getevents	= fio_sgio_getevents,
 	.event		= fio_sgio_event,
 	.cleanup	= fio_sgio_cleanup,
-	.sync		= fio_sgio_sync,
 	.flags		= FIO_SYNCIO | FIO_RAWIO,
 };
diff --git a/engines/fio-engine-splice.c b/engines/fio-engine-splice.c
index cb39b72..3553d64 100644
--- a/engines/fio-engine-splice.c
+++ b/engines/fio-engine-splice.c
@@ -16,12 +16,6 @@
 	int pipe[2];
 };
 
-static int fio_spliceio_sync(struct thread_data fio_unused *td,
-			     struct fio_file *f)
-{
-	return fsync(f->fd);
-}
-
 static int fio_spliceio_getevents(struct thread_data *td, int fio_unused min,
 				  int max, struct timespec fio_unused *t)
 {
@@ -139,8 +133,10 @@
 
 	if (io_u->ddir == DDIR_READ)
 		ret = fio_splice_read(td, io_u);
-	else
+	else if (io_u->ddir == DDIR_WRITE)
 		ret = fio_splice_write(td, io_u);
+	else
+		ret = fsync(io_u->file->fd);
 
 	if ((unsigned int) ret != io_u->buflen) {
 		if (ret > 0) {
@@ -191,6 +187,5 @@
 	.getevents	= fio_spliceio_getevents,
 	.event		= fio_spliceio_event,
 	.cleanup	= fio_spliceio_cleanup,
-	.sync		= fio_spliceio_sync,
 	.flags		= FIO_SYNCIO,
 };
diff --git a/engines/fio-engine-sync.c b/engines/fio-engine-sync.c
index d5be4c8..8bc990d 100644
--- a/engines/fio-engine-sync.c
+++ b/engines/fio-engine-sync.c
@@ -14,12 +14,6 @@
 	struct io_u *last_io_u;
 };
 
-static int fio_syncio_sync(struct thread_data fio_unused *td,
-			   struct fio_file *f)
-{
-	return fsync(f->fd);
-}
-
 static int fio_syncio_getevents(struct thread_data *td, int fio_unused min,
 				int max, struct timespec fio_unused *t)
 {
@@ -48,6 +42,9 @@
 {
 	struct fio_file *f = io_u->file;
 
+	if (io_u->ddir == DDIR_SYNC)
+		return 0;
+
 	if (lseek(f->fd, io_u->offset, SEEK_SET) == -1) {
 		td_verror(td, errno);
 		return 1;
@@ -64,8 +61,10 @@
 
 	if (io_u->ddir == DDIR_READ)
 		ret = read(f->fd, io_u->buf, io_u->buflen);
-	else
+	else if (io_u->ddir == DDIR_WRITE)
 		ret = write(f->fd, io_u->buf, io_u->buflen);
+	else
+		ret = fsync(f->fd);
 
 	if ((unsigned int) ret != io_u->buflen) {
 		if (ret > 0) {
@@ -107,6 +106,5 @@
 	.getevents	= fio_syncio_getevents,
 	.event		= fio_syncio_event,
 	.cleanup	= fio_syncio_cleanup,
-	.sync		= fio_syncio_sync,
 	.flags		= FIO_SYNCIO,
 };
diff --git a/fio.c b/fio.c
index 5673d9e..fc653b3 100644
--- a/fio.c
+++ b/fio.c
@@ -45,8 +45,6 @@
 int temp_stall_ts;
 char *fio_inst_prefix = _INST_PREFIX;
 
-#define should_fsync(td)	((td_write(td) || td_rw(td)) && (!(td)->odirect || (td)->override_sync))
-
 static volatile int startup_sem;
 
 #define TERMINATE_ALL		(-1)
@@ -372,7 +370,7 @@
 
 		ret = td_io_getevents(td, min_evts, td->cur_depth, timeout);
 		if (ret < 0) {
-			td_verror(td, -ret);
+			td_verror(td, ret);
 			break;
 		} else if (!ret)
 			continue;
@@ -406,10 +404,6 @@
 
 		if (td->thinktime)
 			usec_sleep(td, td->thinktime);
-
-		if (should_fsync(td) && td->fsync_blocks &&
-		    (td->io_blocks[DDIR_WRITE] % td->fsync_blocks) == 0)
-			td_io_sync(td, f);
 	}
 
 	if (!ret) {
diff --git a/fio.h b/fio.h
index 921282c..967c42e 100644
--- a/fio.h
+++ b/fio.h
@@ -113,6 +113,7 @@
 enum fio_ddir {
 	DDIR_READ = 0,
 	DDIR_WRITE,
+	DDIR_SYNC,
 };
 
 /*
@@ -184,6 +185,7 @@
 	enum fio_ddir ddir;
 	unsigned int iomix;
 	unsigned int ioprio;
+	unsigned int last_was_sync;
 
 	unsigned char sequential;
 	unsigned char odirect;
@@ -365,6 +367,18 @@
 
 #define MAX_JOBS	(1024)
 
+static inline int should_fsync(struct thread_data *td)
+{
+	if (td->last_was_sync)
+		return 0;
+	if (td->odirect)
+		return 0;
+	if (td_write(td) || td_rw(td) || td->override_sync)
+		return 1;
+
+	return 0;
+}
+
 struct disk_util_stat {
 	unsigned ios[2];
 	unsigned merges[2];
@@ -554,12 +568,11 @@
 	struct io_u *(*event)(struct thread_data *, int);
 	int (*cancel)(struct thread_data *, struct io_u *);
 	void (*cleanup)(struct thread_data *);
-	int (*sync)(struct thread_data *, struct fio_file *);
 	void *data;
 	void *dlhandle;
 };
 
-#define FIO_IOOPS_VERSION	2
+#define FIO_IOOPS_VERSION	3
 
 extern struct ioengine_ops *load_ioengine(struct thread_data *, char *);
 extern void close_ioengine(struct thread_data *);
diff --git a/io_u.c b/io_u.c
index 962c17a..738f3e2 100644
--- a/io_u.c
+++ b/io_u.c
@@ -190,6 +190,16 @@
 		return read_iolog_get(td, io_u);
 
 	/*
+	 * see if it's time to sync
+	 */
+	if (td->fsync_blocks && !(td->io_blocks[DDIR_WRITE] % td->fsync_blocks)
+	    && should_fsync(td)) {
+		io_u->ddir = DDIR_SYNC;
+		io_u->file = f;
+		return 0;
+	}
+
+	/*
 	 * No log, let the seq/rand engine retrieve the next position.
 	 */
 	if (!get_next_offset(td, f, &io_u->offset)) {
@@ -260,19 +270,21 @@
 		io_u->buflen = f->file_size - io_u->offset;
 	}
 
-	if (!io_u->buflen) {
-		put_io_u(td, io_u);
-		return NULL;
+	if (io_u->ddir != DDIR_SYNC) {
+		if (!io_u->buflen) {
+			put_io_u(td, io_u);
+			return NULL;
+		}
+
+		if (!td->read_iolog && !td->sequential)
+			mark_random_map(td, f, io_u);
+
+		f->last_pos += io_u->buflen;
+
+		if (td->verify != VERIFY_NONE)
+			populate_verify_io_u(td, io_u);
 	}
 
-	if (!td->read_iolog && !td->sequential)
-		mark_random_map(td, f, io_u);
-
-	f->last_pos += io_u->buflen;
-
-	if (td->verify != VERIFY_NONE)
-		populate_verify_io_u(td, io_u);
-
 	if (td_io_prep(td, io_u)) {
 		put_io_u(td, io_u);
 		return NULL;
@@ -288,6 +300,13 @@
 	struct timeval e;
 	unsigned long msec;
 
+	if (io_u->ddir == DDIR_SYNC) {
+		td->last_was_sync = 1;
+		return;
+	}
+
+	td->last_was_sync = 0;
+
 	gettimeofday(&e, NULL);
 
 	if (!io_u->error) {
diff --git a/ioengines.c b/ioengines.c
index 9b1ad60..bb46c29 100644
--- a/ioengines.c
+++ b/ioengines.c
@@ -113,8 +113,40 @@
 
 int td_io_sync(struct thread_data *td, struct fio_file *f)
 {
-	if (td->io_ops->sync)
-		return td->io_ops->sync(td, f);
+	struct io_u *io_u = __get_io_u(td);
+	struct io_completion_data icd;
+	int ret;
+
+	if (!io_u)
+		return 1;
+
+	io_u->ddir = DDIR_SYNC;
+	io_u->file = f;
+
+	if (td_io_prep(td, io_u)) {
+		put_io_u(td, io_u);
+		return 1;
+	}
+
+	ret = td_io_queue(td, io_u);
+	if (ret) {
+		put_io_u(td, io_u);
+		td_verror(td, ret);
+		return 1;
+	}
+
+	ret = td_io_getevents(td, 1, td->cur_depth, NULL);
+	if (ret < 0) {
+		td_verror(td, -ret);
+		return 1;
+	}
+
+	icd.nr = ret;
+	ios_completed(td, &icd);
+	if (icd.error) {
+		td_verror(td, icd.error);
+		return 1;
+	}
 
 	return 0;
 }