size=x Set file size to x bytes (x string can include k/m/g)
ioengine=x 'x' may be: aio/libaio/linuxaio for Linux aio,
posixaio for POSIX aio, sync for regular read/write io,
- mmap for mmap'ed io, or sgio for direct SG_IO io. The
- latter only works on Linux on SCSI (or SCSI-like
- devices, such as usb-storage or sata/libata driven)
- devices.
+ mmap for mmap'ed io, splice for using splice/vmsplice,
+ or sgio for direct SG_IO io. The latter only works on
+ Linux on SCSI (or SCSI-like devices, such as
+ usb-storage or sata/libata driven) devices.
iodepth=x For async io, allow 'x' ios in flight
overwrite=x If 'x', layout a write file first.
prio=x Run io at prio X, 0-7 is the kernel allowed range
#define __NR_fadvise64 1234
#endif
+#ifndef __NR_sys_splice
+#define __NR_sys_splice 1297
+#define __NR_sys_tee 1301
+#define __NR_sys_vmsplice 1302
+#endif
+
#define nop asm volatile ("hint @pause" ::: "memory");
#define ia64_popcnt(x) \
#define __NR_fadvise64 233
#endif
+#ifndef __NR_sys_splice
+#define __NR_sys_splice 283
+#define __NR_sys_tee 284
+#define __NR_sys_vmsplice 285
+#endif
+
#define nop do { } while (0)
static inline int __ilog2(unsigned long bitmask)
#define __NR_fadvise64 250
#endif
+#ifndef __NR_sys_splice
+#define __NR_sys_splice 313
+#define __NR_sys_tee 315
+#define __NR_sys_vmsplice 316
+#endif
+
#define nop __asm__ __volatile__("rep;nop": : :"memory")
static inline unsigned long ffz(unsigned long bitmask)
#define __NR_fadvise64 221
#endif
+#ifndef __NR_sys_splice
+#define __NR_sys_splice 275
+#define __NR_sys_tee 276
+#define __NR_sys_vmsplice 278
+#endif
+
#define nop __asm__ __volatile__("rep;nop": : :"memory")
static inline unsigned long ffz(unsigned long bitmask)
thread_number--;
}
-static int add_job(struct thread_data *td, const char *jobname, int prioclass,
- int prio)
+static int add_job(struct thread_data *td, const char *jobname)
{
char *ddir_str[] = { "read", "write", "randread", "randwrite",
"rw", NULL, "randrw" };
return 1;
}
#endif
-#ifdef FIO_HAVE_IOPRIO
- td->ioprio = (prioclass << IOPRIO_CLASS_SHIFT) | prio;
-#endif
/*
* the def_thread is just for options, it's not a real job
setup_log(&td->bw_log);
ddir = td->ddir + (!td->sequential << 1) + (td->iomix << 2);
- printf("Client%d (g=%d): rw=%s, prio=%d/%d, odir=%d, bs=%d-%d, rate=%d, ioengine=%s, iodepth=%d\n", td->thread_number, td->groupid, ddir_str[ddir], prioclass, prio, td->odirect, td->min_bs, td->max_bs, td->rate, td->io_engine_name, td->iodepth);
+ printf("Client%d (g=%d): rw=%s, odir=%d, bs=%d-%d, rate=%d, ioengine=%s, iodepth=%d\n", td->thread_number, td->groupid, ddir_str[ddir], td->odirect, td->min_bs, td->max_bs, td->rate, td->io_engine_name, td->iodepth);
/*
* recurse add identical jobs, clear numjobs and stonewall options
td_new->stonewall = 0;
td_new->jobnum = numjobs;
- if (add_job(td_new, jobname, prioclass, prio))
+ if (add_job(td_new, jobname))
goto err;
}
return 0;
strcpy(td->io_engine_name, "sgio");
td->io_engine = FIO_SGIO;
return 0;
+ } else if (!strncmp(str, "splice", 6)) {
+ strcpy(td->io_engine_name, "splice");
+ td->io_engine = FIO_SPLICEIO;
+ return 0;
}
fprintf(stderr, "bad ioengine type: %s\n", str);
if (!td)
return 1;
- prioclass = 2;
- prio = 4;
-
fgetpos(f, &off);
while ((p = fgets(string, 4096, f)) != NULL) {
if (is_empty_or_comment(p))
fprintf(stderr, "io priorities not available\n");
return 1;
#endif
+ td->ioprio |= prio;
fgetpos(f, &off);
continue;
}
fprintf(stderr, "io priorities not available\n");
return 1;
#endif
+ td->ioprio |= prioclass << IOPRIO_CLASS_SHIFT;
fgetpos(f, &off);
continue;
}
}
fsetpos(f, &off);
- if (add_job(td, name, prioclass, prio))
+ if (add_job(td, name))
return 1;
}
}
#endif /* FIO_HAVE_SGIO */
+
+#ifdef FIO_HAVE_SPLICE
+struct spliceio_data {
+ struct io_u *last_io_u;
+ int pipe[2];
+};
+
+static struct io_u *fio_spliceio_event(struct thread_data *td, int event)
+{
+ struct syncio_data *sd = td->io_data;
+
+ assert(event == 0);
+
+ return sd->last_io_u;
+}
+
+/*
+ * For splice reading, we unfortunately cannot (yet) vmsplice the other way.
+ * So just splice the data from the file into the pipe, and use regular
+ * read to fill the buffer. Doesn't make a lot of sense, but...
+ */
+static int fio_splice_read(struct thread_data *td, struct io_u *io_u)
+{
+ struct spliceio_data *sd = td->io_data;
+ int ret, ret2, buflen;
+ void *p;
+
+ buflen = io_u->buflen;
+ p = io_u->buf;
+ do {
+ off_t off = io_u->offset;
+
+ ret = splice(td->fd, &off, sd->pipe[1], NULL, buflen, 0);
+ if (ret < 0)
+ return errno;
+
+ buflen -= ret;
+
+ while (ret) {
+ ret2 = read(sd->pipe[0], p, ret);
+ if (ret2 < 0)
+ return errno;
+
+ ret -= ret2;
+ p += ret2;
+ }
+ } while (buflen);
+
+ return io_u->buflen;
+}
+
+/*
+ * For splice writing, we can vmsplice our data buffer directly into a
+ * pipe and then splice that to a file.
+ */
+static int fio_splice_write(struct thread_data *td, struct io_u *io_u)
+{
+ struct spliceio_data *sd = td->io_data;
+ struct iovec iov[1] = {
+ {
+ .iov_base = io_u->buf,
+ .iov_len = io_u->buflen,
+ }
+ };
+ struct pollfd pfd = { .fd = sd->pipe[1], .events = POLLOUT, };
+ int ret, ret2;
+
+ while (iov[0].iov_len) {
+ if (poll(&pfd, 1, -1) < 0)
+ return errno;
+
+ ret = vmsplice(sd->pipe[1], iov, 1, SPLICE_F_NONBLOCK);
+ if (ret < 0)
+ return errno;
+
+ iov[0].iov_len -= ret;
+ iov[0].iov_base += ret;
+
+ while (ret) {
+ off_t off = io_u->offset;
+
+ ret2 = splice(sd->pipe[0], NULL, td->fd, &off, ret, 0);
+ if (ret2 < 0)
+ return errno;
+
+ ret -= ret2;
+ }
+ }
+
+ return io_u->buflen;
+}
+
+static int fio_spliceio_queue(struct thread_data *td, struct io_u *io_u)
+{
+ struct spliceio_data *sd = td->io_data;
+ int ret;
+
+ if (io_u->ddir == DDIR_READ)
+ ret = fio_splice_read(td, io_u);
+ else
+ ret = fio_splice_write(td, io_u);
+
+ if ((unsigned int) ret != io_u->buflen) {
+ if (ret > 0) {
+ io_u->resid = io_u->buflen - ret;
+ io_u->error = ENODATA;
+ } else
+ io_u->error = errno;
+ }
+
+ if (!io_u->error)
+ sd->last_io_u = io_u;
+
+ return io_u->error;
+}
+
+static void fio_spliceio_cleanup(struct thread_data *td)
+{
+ struct spliceio_data *sd = td->io_data;
+
+ if (sd) {
+ close(sd->pipe[0]);
+ close(sd->pipe[1]);
+ free(sd);
+ td->io_data = NULL;
+ }
+}
+
+int fio_spliceio_init(struct thread_data *td)
+{
+ struct spliceio_data *sd = malloc(sizeof(*sd));
+
+ td->io_queue = fio_spliceio_queue;
+ td->io_getevents = fio_syncio_getevents;
+ td->io_event = fio_spliceio_event;
+ td->io_cancel = NULL;
+ td->io_cleanup = fio_spliceio_cleanup;
+ td->io_sync = fio_io_sync;
+
+ sd->last_io_u = NULL;
+ if (pipe(sd->pipe) < 0) {
+ td_verror(td, errno);
+ free(sd);
+ return 1;
+ }
+
+ td->io_data = sd;
+ return 0;
+}
+
+#else /* FIO_HAVE_SPLICE */
+
+int fio_spliceio_init(struct thread_data *td)
+{
+ return EINVAL;
+}
+
+#endif /* FIO_HAVE_SPLICE */
return fio_posixaio_init(td);
else if (td->io_engine == FIO_SGIO)
return fio_sgio_init(td);
+ else if (td->io_engine == FIO_SPLICEIO)
+ return fio_spliceio_init(td);
else {
fprintf(stderr, "bad io_engine %d\n", td->io_engine);
return 1;
FIO_LIBAIO = 1 << 2,
FIO_POSIXAIO = 1 << 3,
FIO_SGIO = 1 << 4,
+ FIO_SPLICEIO = 1 << 5 | FIO_SYNCIO,
};
#define td_read(td) ((td)->ddir == DDIR_READ)
#define FIO_OS_LINUX_H
#include <sys/ioctl.h>
+#include <sys/uio.h>
+#include <asm/unistd.h>
#define FIO_HAVE_LIBAIO
#define FIO_HAVE_POSIXAIO
#define FIO_HAVE_DISK_UTIL
#define FIO_HAVE_SGIO
#define FIO_HAVE_IOPRIO
+#define FIO_HAVE_SPLICE
#define OS_MAP_ANON (MAP_ANONYMOUS)
return syscall(__NR_ioprio_set, which, who, ioprio);
}
+static _syscall6(int, sys_splice, int, fdin, loff_t *, off_in, int, fdout, loff_t *, off_out, size_t, len, unsigned int, flags);
+static _syscall4(int, sys_vmsplice, int, fd, const struct iovec *, iov, unsigned long, nr_segs, unsigned int, flags);
+static _syscall4(int, sys_tee, int, fdin, int, fdout, size_t, len, unsigned int, flags);
+
+static inline int splice(int fdin, loff_t *off_in, int fdout, loff_t *off_out,
+ size_t len, unsigned long flags)
+{
+ return sys_splice(fdin, off_in, fdout, off_out, len, flags);
+}
+
+static inline int tee(int fdin, int fdout, size_t len, unsigned int flags)
+{
+ return sys_tee(fdin, fdout, len, flags);
+}
+
+static inline int vmsplice(int fd, const struct iovec *iov,
+ unsigned long nr_segs, unsigned int flags)
+{
+ return sys_vmsplice(fd, iov, nr_segs, flags);
+}
+
+#define SPLICE_F_MOVE (0x01) /* move pages instead of copying */
+#define SPLICE_F_NONBLOCK (0x02) /* don't block on the pipe splicing (but */
+ /* we may still block on the fd we splice */
+ /* from/to, of course */
+#define SPLICE_F_MORE (0x04) /* expect more data */
+#define SPLICE_F_GIFT (0x08) /* pages passed in are a gift */
+
enum {
IOPRIO_WHO_PROCESS = 1,
IOPRIO_WHO_PGRP,
extern int fio_syncio_init(struct thread_data *);
extern int fio_mmapio_init(struct thread_data *);
extern int fio_sgio_init(struct thread_data *);
+extern int fio_spliceio_init(struct thread_data *);
#endif