[PATCH] fio: new engine based on copy_file_range() system call
Dmitry Antipov <[email protected]> Fri, 31 Jul 2026 14:20:16 +0300
| Newsgroups | org.kernel.vger.fio |
|---|---|
| Message-ID | <[email protected]> |
Simple engine based on copy_file_range(2) Linux-specific system call. Signed-off-by: Dmitry Antipov <[email protected]> --- Makefile | 3 + configure | 21 +++++++ engines/copy_file_range.c | 107 +++++++++++++++++++++++++++++++++++ examples/copy-file-range.fio | 13 +++++ file.h | 2 + filesetup.c | 7 ++- 6 files changed, 150 insertions(+), 3 deletions(-) create mode 100644 engines/copy_file_range.c create mode 100644 examples/copy-file-range.fio diff --git a/Makefile b/Makefile index 4f87adba..98d71700 100644 --- a/Makefile +++ b/Makefile @@ -124,6 +124,9 @@ endif ifdef CONFIG_LINUX_SPLICE SOURCE += engines/splice.c endif +ifdef CONFIG_LINUX_COPY_FILE_RANGE + SOURCE += engines/copy_file_range.c +endif ifdef CONFIG_SOLARISAIO SOURCE += engines/solarisaio.c endif diff --git a/configure b/configure index cdfefa59..2bdeac88 100755 --- a/configure +++ b/configure @@ -1409,6 +1409,24 @@ if compile_prog "" "" "linux splice"; then fi print_config "Linux splice(2)" "$linux_splice" +########################################## +# copy_file_range probe +if test "$linux_copy_file_range" != "yes" ; then + linux_copy_file_range="no" +fi +cat > $TMPC << EOF +#include <stdio.h> +#include <unistd.h> +int main(int argc, char **argv) +{ + return copy_file_range(0, NULL, 0, NULL, 0, 0); +} +EOF +if compile_prog "" "" "linux copy_file_range"; then + linux_copy_file_range="yes" +fi +print_config "Linux copy_file_range(2)" "$linux_copy_file_range" + ########################################## # libnuma probe if test "$libnuma" != "yes" ; then @@ -3228,6 +3246,9 @@ fi if test "$linux_splice" = "yes" ; then output_sym "CONFIG_LINUX_SPLICE" fi +if test "$linux_copy_file_range" = "yes" ; then + output_sym "CONFIG_LINUX_COPY_FILE_RANGE" +fi if test "$libnuma_v2" = "yes" ; then output_sym "CONFIG_LIBNUMA" fi diff --git a/engines/copy_file_range.c b/engines/copy_file_range.c new file mode 100644 index 00000000..2e9c1b05 --- /dev/null +++ b/engines/copy_file_range.c @@ -0,0 +1,107 @@ +#include <stdlib.h> +#include <unistd.h> +#include <limits.h> +#include <sys/user.h> + +#include "../fio.h" + +#ifndef MAX_RW_COUNT +#define MAX_RW_COUNT (INT_MAX & PAGE_MASK) +#endif + +static int +fio_copy_file_range_prep(struct thread_data *td, struct io_u *io_u) +{ + return io_u->xfer_buflen > MAX_RW_COUNT ? EINVAL : 0; +} + +static int +fio_copy_file_range_close(struct thread_data *td, struct fio_file *f) +{ + int ret = generic_close_file(td, f); + + if (!ret && td->o.unlink) { + struct fio_file *pf = f->engine_data; + + if (fio_file_open(pf)) + td_io_close_file(td, pf); + td_io_unlink_file(td, pf); + fio_file_free(pf); + + f->engine_data = NULL; + } + return ret; +} + +static int +fio_copy_file_range_open(struct thread_data *td, struct fio_file *f) +{ + int ret = generic_open_file(td, f); + + if (!ret) { + char fname[PATH_MAX]; + struct fio_file *pf; + + snprintf(fname, PATH_MAX, "%s.p", f->file_name); + pf = alloc_new_file(td); + pf->file_name = smalloc_strdup(fname); + pf->real_file_size = f->real_file_size; + extend_file(td, pf); + + f->engine_data = pf; + } + return ret; +} + +static enum fio_q_status +fio_copy_file_range_queue(struct thread_data *td, struct io_u *io_u) +{ + struct fio_file *f = io_u->file, *pf = f->engine_data; + off_t off_in = io_u->offset, off_out = io_u->offset; + ssize_t ret; + + fio_ro_check(td, io_u); + + if (io_u->ddir == DDIR_READ) + ret = copy_file_range(f->fd, &off_in, pf->fd, + &off_out, io_u->xfer_buflen, 0); + else if (io_u->ddir == DDIR_WRITE) + ret = copy_file_range(pf->fd, &off_in, f->fd, + &off_out, io_u->xfer_buflen, 0); + else { + io_u->error = EINVAL; + return FIO_Q_COMPLETED; + } + + if (ret != io_u->xfer_buflen) { + if (ret >= 0) { + io_u->resid = io_u->xfer_buflen - ret; + io_u->offset += ret; + io_u->error = 0; + } else + io_u->error = errno; + } else + io_u->offset += ret; + + return FIO_Q_COMPLETED; +} + +static struct ioengine_ops ioengine = { + .name = "copy_file_range", + .version = FIO_IOOPS_VERSION, + .prep = fio_copy_file_range_prep, + .queue = fio_copy_file_range_queue, + .open_file = fio_copy_file_range_open, + .close_file = fio_copy_file_range_close, + .flags = FIO_SYNCIO, +}; + +static void fio_init fio_copy_file_range_register(void) +{ + register_ioengine(&ioengine); +} + +static void fio_exit fio_copy_file_range_unregister(void) +{ + unregister_ioengine(&ioengine); +} diff --git a/examples/copy-file-range.fio b/examples/copy-file-range.fio new file mode 100644 index 00000000..2d4684c1 --- /dev/null +++ b/examples/copy-file-range.fio @@ -0,0 +1,13 @@ +[global] +ioengine=copy_file_range +thread=1 +buffered=0 +rw=randrw +bs=128k +size=128m +directory=/tmp +unlink=1 + +[file1] +numjobs=1 +iodepth=1 diff --git a/file.h b/file.h index 6fcf409b..a195970c 100644 --- a/file.h +++ b/file.h @@ -229,6 +229,8 @@ extern int __must_check generic_prepopulate_file(struct thread_data *, struct fi extern int __must_check file_lookup_open(struct fio_file *f, int flags); extern bool __must_check pre_read_files(struct thread_data *); extern unsigned long long get_rand_file_size(struct thread_data *td); +extern struct fio_file *alloc_new_file(struct thread_data *); +extern int extend_file(struct thread_data *, struct fio_file *); extern int add_file(struct thread_data *, const char *, int, int); extern int add_file_exclusive(struct thread_data *, const char *); extern void get_file(struct fio_file *); diff --git a/filesetup.c b/filesetup.c index 740a2f47..6438a74e 100644 --- a/filesetup.c +++ b/filesetup.c @@ -117,7 +117,7 @@ static void fallocate_file(struct thread_data *td, struct fio_file *f) /* * Leaves f->fd open on success, caller must close */ -static int extend_file(struct thread_data *td, struct fio_file *f) +int extend_file(struct thread_data *td, struct fio_file *f) { int new_layout = 0, unlink_file = 0, flags; unsigned long long left; @@ -153,7 +153,8 @@ static int extend_file(struct thread_data *td, struct fio_file *f) } } - flags = O_WRONLY; + /* FIXME: read is required by copy_file_range(2) engine */ + flags = O_RDWR; if (td->o.allow_create) flags |= O_CREAT; if (new_layout) @@ -1814,7 +1815,7 @@ static void free_already_allocated(void) fio_file_hash_unlock(); } -static struct fio_file *alloc_new_file(struct thread_data *td) +struct fio_file *alloc_new_file(struct thread_data *td) { struct fio_file *f; -- 2.55.0