[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [RFC] Add copy_file_range syscall support
From: |
Luis Henriques |
Subject: |
Re: [RFC] Add copy_file_range syscall support |
Date: |
Mon, 20 Jul 2020 11:50:53 +0100 |
Hi Sergey,
I forgot to include you on CC for this RFC patch. Explicitly pinging you
now. Please let me know your thoughts about adding support for this on
cpio.
Cheers,
--
Luis
Luis Henriques <lhenriques@suse.de> writes:
> Hi!
>
> I'm sharing a quick hack to cpio that adds support for using the linux
> copy_file_range(2) syscall when doing a disk_to_disk copy ('pass-through'
> mode).
>
> My very limited testing on running this patch on a filesystem that
> supports this syscall (btrfs) showed a nice performance improvement, but
> this is just a very early hack to try to get some feedback on whether this
> would be something other people would like to see merged in.
>
> Cheers,
> --
> Luís
>
> ---
> configure.ac | 2 +-
> src/copypass.c | 4 ++++
> src/extern.h | 2 ++
> src/util.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 58 insertions(+), 1 deletion(-)
>
> diff --git a/configure.ac b/configure.ac
> index 9f81a4730cd1..79c1533a7a1c 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -48,7 +48,7 @@ AC_HEADER_DIRENT
> AC_COMPILE_CHECK_RETTYPE([major], [0])
> AC_COMPILE_CHECK_RETTYPE([minor], [0])
>
> -AC_CHECK_FUNCS([fchmod fchown])
> +AC_CHECK_FUNCS([fchmod fchown copy_file_range])
> # This is needed for mingw build
> AC_CHECK_FUNCS([setmode getpwuid getpwnam getgrgid getgrnam pipe fork getuid
> geteuid])
>
> diff --git a/src/copypass.c b/src/copypass.c
> index 3b0104fef784..9987a58bd4f8 100644
> --- a/src/copypass.c
> +++ b/src/copypass.c
> @@ -188,8 +188,12 @@ process_copy_pass ()
> continue;
> }
>
> +#ifdef HAVE_COPY_FILE_RANGE
> + copy_files_range (in_file_des, out_file_des,
> in_file_stat.st_size, input_name.ds_string);
> +#else
> copy_files_disk_to_disk (in_file_des, out_file_des,
> in_file_stat.st_size, input_name.ds_string);
> disk_empty_output_buffer (out_file_des, true);
> +#endif /* HAVE_COPY_FILE_RANGE */
>
> set_copypass_perms (out_file_des,
> output_name.ds_string, &in_file_stat);
> diff --git a/src/extern.h b/src/extern.h
> index ad05e78a1ae9..981045ef93ef 100644
> --- a/src/extern.h
> +++ b/src/extern.h
> @@ -170,6 +170,8 @@ void tape_toss_input (int in_des, off_t num_bytes);
> void copy_files_tape_to_disk (int in_des, int out_des, off_t num_bytes);
> void copy_files_disk_to_tape (int in_des, int out_des, off_t num_bytes, char
> *filename);
> void copy_files_disk_to_disk (int in_des, int out_des, off_t num_bytes, char
> *filename);
> +void copy_files_range (int in_des, int out_des, off_t num_bytes, char
> *filename);
> +
> void warn_if_file_changed (char *file_name, off_t old_file_size,
> time_t old_file_mtime);
> void create_all_directories (char const *name);
> diff --git a/src/util.c b/src/util.c
> index c44a17b2cd07..9dd0be537294 100644
> --- a/src/util.c
> +++ b/src/util.c
> @@ -516,6 +516,57 @@ copy_files_disk_to_tape (int in_des, int out_des, off_t
> num_bytes,
> in_buff += size;
> }
> }
> +
> +#ifdef HAVE_COPY_FILE_RANGE
> +void
> +copy_files_range (int in_des, int out_des, off_t num_bytes,
> + char *filename)
> +{
> + loff_t in_off = 0, out_off = 0;
> + off_t next_start, next_end;
> + ssize_t total = 0;
> + ssize_t ret;
> +
> + while (in_off < num_bytes)
> + {
> + next_start = lseek (in_des, in_off, SEEK_DATA);
> + if (next_start < 0)
> + {
> + /* Hole at the end of the file */
> + if (errno == ENXIO)
> + {
> + if (ftruncate (out_des, num_bytes) < 0)
> + error (PAXEXIT_FAILURE, errno, _("Failed ftruncate"));
> + in_off = num_bytes;
> + break;
> + }
> + error (PAXEXIT_FAILURE, errno, _("Can't seek to data in file"));
> + }
> + next_end = lseek (in_des, next_start, SEEK_HOLE);
> + if (next_end < 0)
> + error (PAXEXIT_FAILURE, errno, _("Can't seek to hole in file"));
> + in_off = out_off = next_start;
> + ret = copy_file_range (in_des, &in_off, out_des, &out_off,
> + (next_end - next_start), 0);
> + if (ret < 0)
> + {
> + error (0, errno, _("copy_file_range failed."));
> + break;
> + }
> + total += ret;
> + }
> +
> + if (in_off < num_bytes)
> + {
> + lseek (in_des, in_off, SEEK_SET);
> + lseek (out_des, in_off, SEEK_SET);
> + copy_files_disk_to_disk (in_des, out_des, (num_bytes - in_off),
> + filename);
> + disk_empty_output_buffer (out_des, true);
> + }
> +}
> +#endif /* HAVE_COPY_FILE_RANGE */
> +
> /* Copy a file using the input and output buffers, which may start out
> partly full. After the copy, the files are not closed nor the last
> block flushed to output, and the input buffer may still be partly
>