Skip to content

Commit

Permalink
linux: work around copy_file_range() cephfs bug
Browse files Browse the repository at this point in the history
Pre-4.20 kernels have a bug where CephFS uses the RADOS copy-from
command when it shouldn't. Fall back to a regular file copy.

Fixes: libuv#3117
Refs: torvalds/linux@6f9718f
  • Loading branch information
bnoordhuis committed Feb 28, 2021
1 parent 285a5ea commit 00c8196
Showing 1 changed file with 42 additions and 0 deletions.
42 changes: 42 additions & 0 deletions src/unix/fs.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@
# define HAVE_PREADV 0
#endif

#if defined(__linux__)
# include "sys/utsname.h"
#endif

#if defined(__linux__) || defined(__sun)
# include <sys/sendfile.h>
# include <sys/sysmacros.h>
Expand Down Expand Up @@ -901,6 +905,41 @@ static ssize_t uv__fs_sendfile_emul(uv_fs_t* req) {
}


#ifdef __linux__
static unsigned uv__kernel_version(void) {
struct utsname u;
unsigned major;
unsigned minor;
unsigned patch;

if (-1 == uname(&u))
return 0;

if (3 != sscanf(u.release, "%u.%u.%u", &major, &minor, &patch))
return 0;

return major * 65536 + minor * 256 + patch;
}


/* Pre-4.20 kernels have a bug where CephFS uses the RADOS copy-from command
* in copy_file_range() when it shouldn't. There is no workaround except to
* fall back to a regular copy.
*/
static int uv__is_buggy_cephfs(int fd) {
struct statfs s;

if (-1 == fstatfs(fd, &s))
return 0;

if (s.f_type != /* CephFS */ 0xC36400)
return 0;

return uv__kernel_version() < /* 4.20.0 */ 0x041400;
}
#endif /* __linux__ */


static ssize_t uv__fs_sendfile(uv_fs_t* req) {
int in_fd;
int out_fd;
Expand All @@ -926,6 +965,9 @@ static ssize_t uv__fs_sendfile(uv_fs_t* req) {
/* ENOSYS - it will never work */
errno = 0;
copy_file_range_support = 0;
} else if (r == -1 && errno == EACCES && uv__is_buggy_cephfs(in_fd)) {
errno = 0;
copy_file_range_support = 0;
} else if (r == -1 && (errno == ENOTSUP || errno == EXDEV)) {
/* ENOTSUP - it could work on another file system type */
/* EXDEV - it will not work when in_fd and out_fd are not on the same
Expand Down

0 comments on commit 00c8196

Please sign in to comment.