diff options
author | Jannik Glückert <jannik.glueckert@gmail.com> | 2023-03-08 19:37:43 +0100 |
---|---|---|
committer | Jonathan Wakely <jwakely@redhat.com> | 2023-06-06 12:31:57 +0100 |
commit | d87caacf8e2df563afda85f3a5b7b852e08b6b2c (patch) | |
tree | 4191571e0d3db7dd3bc34d442c8737f4a40633b0 | |
parent | f80a8b42296265bb868a48592a2bd1fdaa2a3d8a (diff) | |
download | gcc-d87caacf8e2df563afda85f3a5b7b852e08b6b2c.zip gcc-d87caacf8e2df563afda85f3a5b7b852e08b6b2c.tar.gz gcc-d87caacf8e2df563afda85f3a5b7b852e08b6b2c.tar.bz2 |
libstdc++: Use copy_file_range for filesystem::copy_file
copy_file_range is a recent-ish syscall for copying files. It is similar
to sendfile but allows filesystem-specific optimizations. Common are:
Reflinks: BTRFS, XFS, ZFS (does not implement the syscall yet)
Server-side copy: NFS, SMB, Ceph
If copy_file_range is not available for the given files, fall back to
sendfile / userspace copy.
libstdc++-v3/ChangeLog:
* acinclude.m4 (_GLIBCXX_USE_COPY_FILE_RANGE): Define.
* config.h.in: Regenerate.
* configure: Regenerate.
* src/filesystem/ops-common.h (copy_file_copy_file_range):
Define new function.
(do_copy_file): Use it.
Signed-off-by: Jannik Glückert <jannik.glueckert@gmail.com>
-rw-r--r-- | libstdc++-v3/acinclude.m4 | 20 | ||||
-rw-r--r-- | libstdc++-v3/config.h.in | 3 | ||||
-rwxr-xr-x | libstdc++-v3/configure | 62 | ||||
-rw-r--r-- | libstdc++-v3/src/filesystem/ops-common.h | 56 |
4 files changed, 141 insertions, 0 deletions
diff --git a/libstdc++-v3/acinclude.m4 b/libstdc++-v3/acinclude.m4 index 1920444..13c3966 100644 --- a/libstdc++-v3/acinclude.m4 +++ b/libstdc++-v3/acinclude.m4 @@ -4954,6 +4954,7 @@ dnl _GLIBCXX_USE_UTIMENSAT dnl _GLIBCXX_USE_ST_MTIM dnl _GLIBCXX_USE_FCHMOD dnl _GLIBCXX_USE_FCHMODAT +dnl _GLIBCXX_USE_COPY_FILE_RANGE dnl _GLIBCXX_USE_SENDFILE dnl HAVE_LINK dnl HAVE_LSEEK @@ -5153,6 +5154,25 @@ dnl AC_DEFINE(HAVE_TRUNCATE, 1, [Define if truncate is available in <unistd.h>.]) fi dnl + AC_CACHE_CHECK([for copy_file_range that can copy files], + glibcxx_cv_copy_file_range, [dnl + case "${target_os}" in + linux*) + GCC_TRY_COMPILE_OR_LINK( + [#include <unistd.h>], + [copy_file_range(1, nullptr, 2, nullptr, 1, 0);], + [glibcxx_cv_copy_file_range=yes], + [glibcxx_cv_copy_file_range=no]) + ;; + *) + glibcxx_cv_copy_file_range=no + ;; + esac + ]) + if test $glibcxx_cv_copy_file_range = yes; then + AC_DEFINE(_GLIBCXX_USE_COPY_FILE_RANGE, 1, [Define if copy_file_range is available in <unistd.h>.]) + fi +dnl AC_CACHE_CHECK([for sendfile that can copy files], glibcxx_cv_sendfile, [dnl case "${target_os}" in diff --git a/libstdc++-v3/config.h.in b/libstdc++-v3/config.h.in index 99ce682..e156650 100644 --- a/libstdc++-v3/config.h.in +++ b/libstdc++-v3/config.h.in @@ -971,6 +971,9 @@ /* Defined if clock_gettime has realtime clock support. */ #undef _GLIBCXX_USE_CLOCK_REALTIME +/* Define if copy_file_range is available in <unistd.h>. */ +#undef _GLIBCXX_USE_COPY_FILE_RANGE + /* Define if ISO/IEC TR 24733 decimal floating point types are supported on this host. */ #undef _GLIBCXX_USE_DECIMAL_FLOAT diff --git a/libstdc++-v3/configure b/libstdc++-v3/configure index 50a7c30..47db64f 100755 --- a/libstdc++-v3/configure +++ b/libstdc++-v3/configure @@ -71280,6 +71280,68 @@ $as_echo "$glibcxx_cv_truncate" >&6; } $as_echo "#define HAVE_TRUNCATE 1" >>confdefs.h fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for copy_file_range that can copy files" >&5 +$as_echo_n "checking for copy_file_range that can copy files... " >&6; } +if ${glibcxx_cv_copy_file_range+:} false; then : + $as_echo_n "(cached) " >&6 +else + case "${target_os}" in + linux*) + if test x$gcc_no_link = xyes; then + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <unistd.h> +int +main () +{ +copy_file_range(1, nullptr, 2, nullptr, 1, 0); + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + glibcxx_cv_copy_file_range=yes +else + glibcxx_cv_copy_file_range=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +else + if test x$gcc_no_link = xyes; then + as_fn_error $? "Link tests are not allowed after GCC_NO_EXECUTABLES." "$LINENO" 5 +fi +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <unistd.h> +int +main () +{ +copy_file_range(1, nullptr, 2, nullptr, 1, 0); + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + glibcxx_cv_copy_file_range=yes +else + glibcxx_cv_copy_file_range=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi + ;; + *) + glibcxx_cv_copy_file_range=no + ;; + esac + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $glibcxx_cv_copy_file_range" >&5 +$as_echo "$glibcxx_cv_copy_file_range" >&6; } + if test $glibcxx_cv_copy_file_range = yes; then + +$as_echo "#define _GLIBCXX_USE_COPY_FILE_RANGE 1" >>confdefs.h + + fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sendfile that can copy files" >&5 $as_echo_n "checking for sendfile that can copy files... " >&6; } if ${glibcxx_cv_sendfile+:} false; then : diff --git a/libstdc++-v3/src/filesystem/ops-common.h b/libstdc++-v3/src/filesystem/ops-common.h index 3644438..7c8d3a6 100644 --- a/libstdc++-v3/src/filesystem/ops-common.h +++ b/libstdc++-v3/src/filesystem/ops-common.h @@ -49,6 +49,9 @@ #ifdef NEED_DO_COPY_FILE # include <filesystem> # include <ext/stdio_filebuf.h> +# ifdef _GLIBCXX_USE_COPY_FILE_RANGE +# include <unistd.h> // copy_file_range +# endif # ifdef _GLIBCXX_USE_SENDFILE # include <sys/sendfile.h> // sendfile # include <unistd.h> // lseek @@ -359,6 +362,32 @@ _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM } #ifdef NEED_DO_COPY_FILE +#ifdef _GLIBCXX_USE_COPY_FILE_RANGE + bool + copy_file_copy_file_range(int fd_in, int fd_out, size_t length) noexcept + { + // a zero-length file is either empty, or not copyable by this syscall + // return early to avoid the syscall cost + if (length == 0) + { + errno = EINVAL; + return false; + } + size_t bytes_left = length; + off64_t off_in = 0, off_out = 0; + ssize_t bytes_copied; + do + { + bytes_copied = ::copy_file_range(fd_in, &off_in, fd_out, &off_out, + bytes_left, 0); + bytes_left -= bytes_copied; + } + while (bytes_left > 0 && bytes_copied > 0); + if (bytes_copied < 0) + return false; + return true; + } +#endif #if defined _GLIBCXX_USE_SENDFILE && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS bool copy_file_sendfile(int fd_in, int fd_out, size_t length) noexcept @@ -529,6 +558,33 @@ _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM bool has_copied = false; +#ifdef _GLIBCXX_USE_COPY_FILE_RANGE + if (!has_copied) + has_copied = copy_file_copy_file_range(in.fd, out.fd, from_st->st_size); + if (!has_copied) + { + // EINVAL: src and dst are the same file (this is not cheaply + // detectable from userspace) + // EINVAL: copy_file_range is unsupported for this file type by the + // underlying filesystem + // ENOTSUP: undocumented, can arise with old kernels and NFS + // EOPNOTSUPP: filesystem does not implement copy_file_range + // ETXTBSY: src or dst is an active swapfile (nonsensical, but allowed + // with normal copying) + // EXDEV: src and dst are on different filesystems that do not support + // cross-fs copy_file_range + // ENOENT: undocumented, can arise with CIFS + // ENOSYS: unsupported by kernel or blocked by seccomp + if (errno != EINVAL && errno != ENOTSUP && errno != EOPNOTSUPP + && errno != ETXTBSY && errno != EXDEV && errno != ENOENT + && errno != ENOSYS) + { + ec.assign(errno, std::generic_category()); + return false; + } + } +#endif + #if defined _GLIBCXX_USE_SENDFILE && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS if (!has_copied) has_copied = copy_file_sendfile(in.fd, out.fd, from_st->st_size); |