From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2181) id BFB493857020; Tue, 6 Jun 2023 11:35:57 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org BFB493857020 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1686051357; bh=tib4ZxVq6seaNeXuIGl5zt9mK23/JHy9SOusS7U6uZ0=; h=From:To:Subject:Date:From; b=ZYHTwywX6XkXpYyiRGNzwwjisX4hq17Y13TOgY0c0xKA0j9NxAZ8WE3bT1E78TzZ0 kYjl9YNmjQtbi0W3/DNVXUN0u/V6n1YVsKjS8zBllbH3RvYTsJuCbM8G1Frhxl/XSB ykGHctVEHhmNIS1DqumWQvO6bG379lpBQxnGv4lo= MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="utf-8" From: Jonathan Wakely To: gcc-cvs@gcc.gnu.org, libstdc++-cvs@gcc.gnu.org Subject: [gcc r14-1569] libstdc++: Use copy_file_range for filesystem::copy_file X-Act-Checkin: gcc X-Git-Author: =?utf-8?q?Jannik_Gl=C3=BCckert?= X-Git-Refname: refs/heads/master X-Git-Oldrev: f80a8b42296265bb868a48592a2bd1fdaa2a3d8a X-Git-Newrev: d87caacf8e2df563afda85f3a5b7b852e08b6b2c Message-Id: <20230606113557.BFB493857020@sourceware.org> Date: Tue, 6 Jun 2023 11:35:57 +0000 (GMT) List-Id: https://gcc.gnu.org/g:d87caacf8e2df563afda85f3a5b7b852e08b6b2c commit r14-1569-gd87caacf8e2df563afda85f3a5b7b852e08b6b2c Author: Jannik Glückert Date: Wed Mar 8 19:37:43 2023 +0100 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 Diff: --- libstdc++-v3/acinclude.m4 | 20 +++++++++++ libstdc++-v3/config.h.in | 3 ++ libstdc++-v3/configure | 62 ++++++++++++++++++++++++++++++++ libstdc++-v3/src/filesystem/ops-common.h | 56 +++++++++++++++++++++++++++++ 4 files changed, 141 insertions(+) diff --git a/libstdc++-v3/acinclude.m4 b/libstdc++-v3/acinclude.m4 index 1920444e5cd..13c3966b317 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 @@ -5152,6 +5153,25 @@ dnl if test $glibcxx_cv_truncate = yes; then AC_DEFINE(HAVE_TRUNCATE, 1, [Define if truncate is available in .]) 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 ], + [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 .]) + fi dnl AC_CACHE_CHECK([for sendfile that can copy files], glibcxx_cv_sendfile, [dnl diff --git a/libstdc++-v3/config.h.in b/libstdc++-v3/config.h.in index 99ce682670e..e156650792e 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 . */ +#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 50a7c30665b..47db64ff8fb 100755 --- a/libstdc++-v3/configure +++ b/libstdc++-v3/configure @@ -71279,6 +71279,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 +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 +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; } diff --git a/libstdc++-v3/src/filesystem/ops-common.h b/libstdc++-v3/src/filesystem/ops-common.h index 36444388208..7c8d3a694e2 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 # include +# ifdef _GLIBCXX_USE_COPY_FILE_RANGE +# include // copy_file_range +# endif # ifdef _GLIBCXX_USE_SENDFILE # include // sendfile # include // 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);