From ca10e361498dc55669dc0e630ca4481f2d74eca9 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Wed, 26 Jun 2019 11:51:07 +0800 Subject: [PATCH] linux: use copy_file_range for uv_fs_copyfile when possible Refs: https://github.com/libuv/libuv/issues/925#issuecomment-234696227 PR-URL: https://github.com/libuv/libuv/pull/2352 Reviewed-By: Santiago Gimeno Reviewed-By: Jameson Nash --- src/unix/fs.c | 19 +++++++++++++++++++ src/unix/linux-syscalls.c | 40 +++++++++++++++++++++++++++++++++++++++ src/unix/linux-syscalls.h | 7 +++++++ 3 files changed, 66 insertions(+) diff --git a/src/unix/fs.c b/src/unix/fs.c index dd08ea541..a06f15b9f 100644 --- a/src/unix/fs.c +++ b/src/unix/fs.c @@ -883,8 +883,27 @@ static ssize_t uv__fs_sendfile(uv_fs_t* req) { ssize_t r; off = req->off; + +#ifdef __linux__ + { + static int copy_file_range_support = 1; + + if (copy_file_range_support) { + r = uv__fs_copy_file_range(in_fd, NULL, out_fd, &off, req->bufsml[0].len, 0); + + if (r == -1 && errno == ENOSYS) { + errno = 0; + copy_file_range_support = 0; + } else { + goto ok; + } + } + } +#endif + r = sendfile(out_fd, in_fd, &off, req->bufsml[0].len); +ok: /* sendfile() on SunOS returns EINVAL if the target fd is not a socket but * it still writes out data. Fortunately, we can detect it by checking if * the offset has been updated. diff --git a/src/unix/linux-syscalls.c b/src/unix/linux-syscalls.c index 742f26ada..160056b46 100644 --- a/src/unix/linux-syscalls.c +++ b/src/unix/linux-syscalls.c @@ -94,6 +94,24 @@ # endif #endif /* __NR_pwritev */ +#ifndef __NR_copy_file_range +# if defined(__x86_64__) +# define __NR_copy_file_range 326 +# elif defined(__i386__) +# define __NR_copy_file_range 377 +# elif defined(__s390__) +# define __NR_copy_file_range 375 +# elif defined(__arm__) +# define __NR_copy_file_range (UV_SYSCALL_BASE + 391) +# elif defined(__aarch64__) +# define __NR_copy_file_range 285 +# elif defined(__powerpc__) +# define __NR_copy_file_range 379 +# elif defined(__arc__) +# define __NR_copy_file_range 285 +# endif +#endif /* __NR_copy_file_range */ + #ifndef __NR_statx # if defined(__x86_64__) # define __NR_statx 332 @@ -180,6 +198,28 @@ int uv__dup3(int oldfd, int newfd, int flags) { } +ssize_t +uv__fs_copy_file_range(int fd_in, + ssize_t* off_in, + int fd_out, + ssize_t* off_out, + size_t len, + unsigned int flags) +{ +#ifdef __NR_copy_file_range + return syscall(__NR_copy_file_range, + fd_in, + off_in, + fd_out, + off_out, + len, + flags); +#else + return errno = ENOSYS, -1; +#endif +} + + int uv__statx(int dirfd, const char* path, int flags, diff --git a/src/unix/linux-syscalls.h b/src/unix/linux-syscalls.h index 2e8fa2a51..761ff32e2 100644 --- a/src/unix/linux-syscalls.h +++ b/src/unix/linux-syscalls.h @@ -64,6 +64,13 @@ struct uv__statx { ssize_t uv__preadv(int fd, const struct iovec *iov, int iovcnt, int64_t offset); ssize_t uv__pwritev(int fd, const struct iovec *iov, int iovcnt, int64_t offset); int uv__dup3(int oldfd, int newfd, int flags); +ssize_t +uv__fs_copy_file_range(int fd_in, + ssize_t* off_in, + int fd_out, + ssize_t* off_out, + size_t len, + unsigned int flags); int uv__statx(int dirfd, const char* path, int flags,