From 6a1cddf41c39f9b297cb5428a8ef19edc68337a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Jacke?= Date: Thu, 19 Jan 2017 22:13:03 +0100 Subject: wscript: check for copy_file_range syscall this one came with Linux kernel 4.5 Signed-off-by: Bjoern Jacke --- source3/wscript | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/source3/wscript b/source3/wscript index b09e372..de0b1da 100644 --- a/source3/wscript +++ b/source3/wscript @@ -1204,6 +1204,15 @@ ssize_t err = posix_fadvise(0,0,0x80000,POSIX_FADV_WILLNEED); msg='Checking whether sysconf(%s) is available' % v) conf.CHECK_CODE(''' +#include +#include +#include +syscall(SYS_copy_file_range,0,NULL,0,NULL,0,0); + ''', + 'HAVE_SYSCALL_COPY_FILE_RANGE', + msg='Checking whether we have copy_file_range system call') + + conf.CHECK_CODE(''' #include #include syscall(SYS_initgroups, 16, NULL, NULL, 0); -- 2.7.4 From e9921beeacbeef904c91e57c9f3d3bfd02432a29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Jacke?= Date: Fri, 27 Jan 2017 15:27:44 +0100 Subject: vfs_default: add copy_file_range support for copy chunk we try to use the copy_file_range syscall first, which can ideally be a zero-copy operation. We fall back to userspace read/write if copy_file_range is not available. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12033 Signed-off-by: Bjoern Jacke --- source3/modules/vfs_default.c | 102 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 88 insertions(+), 14 deletions(-) diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c index d4610f7..c9ad0be 100644 --- a/source3/modules/vfs_default.c +++ b/source3/modules/vfs_default.c @@ -33,6 +33,10 @@ #include "lib/util/tevent_ntstatus.h" #include "lib/util/sys_rw.h" #include "lib/pthreadpool/pthreadpool_tevent.h" +#ifdef HAVE_SYS_SYSCALL_H +#include +#endif + #undef DBGC_CLASS #define DBGC_CLASS DBGC_VFS @@ -1607,21 +1611,15 @@ static struct tevent_req *vfswrap_copy_chunk_send(struct vfs_handle_struct *hand struct tevent_req *req; struct vfs_cc_state *vfs_cc_state; NTSTATUS status; - - DEBUG(10, ("performing server side copy chunk of length %lu\n", - (unsigned long)num)); + ssize_t ret; + struct lock_struct lck; + int saved_errno; req = tevent_req_create(mem_ctx, &vfs_cc_state, struct vfs_cc_state); if (req == NULL) { return NULL; } - vfs_cc_state->buf = talloc_array(vfs_cc_state, uint8_t, - MIN(num, 8*1024*1024)); - if (tevent_req_nomem(vfs_cc_state->buf, req)) { - return tevent_req_post(req, ev); - } - status = vfs_stat_fsp(src_fsp); if (tevent_req_nterror(req, status)) { return tevent_req_post(req, ev); @@ -1640,13 +1638,89 @@ static struct tevent_req *vfswrap_copy_chunk_send(struct vfs_handle_struct *hand tevent_req_nterror(req, NT_STATUS_INVALID_VIEW_SIZE); return tevent_req_post(req, ev); } +#ifdef HAVE_SYSCALL_COPY_FILE_RANGE + /* copy_file_range optimized call */ + DEBUG(5, ("performing server side copy chunk " + "(copy_file_range) of length %lu at offsets " + "%lu/%lu\n", (unsigned long)num, + (unsigned long)src_off, (unsigned long)dest_off)); + if (src_fsp->op == NULL || dest_fsp->op == NULL) { + tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); + return tevent_req_post(req, ev); + } - /* could use 2.6.33+ sendfile here to do this in kernel */ - while (vfs_cc_state->copied < num) { - ssize_t ret; - struct lock_struct lck; - int saved_errno; + init_strict_lock_struct(src_fsp, + src_fsp->op->global->open_persistent_id, + src_off, + num, + READ_LOCK, + &lck); + + if (!SMB_VFS_STRICT_LOCK(src_fsp->conn, src_fsp, &lck)) { + tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT); + return tevent_req_post(req, ev); + } + + init_strict_lock_struct(dest_fsp, + dest_fsp->op->global->open_persistent_id, + dest_off, + num, + WRITE_LOCK, + &lck); + + if (!SMB_VFS_STRICT_LOCK(dest_fsp->conn, dest_fsp, &lck)) { + tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT); + return tevent_req_post(req, ev); + } + + ret = syscall(SYS_copy_file_range, + src_fsp->fh->fd, &src_off, + dest_fsp->fh->fd, &dest_off, + num, 0); + + if (ret == -1) { + saved_errno = errno; + } + SMB_VFS_STRICT_UNLOCK(src_fsp->conn, src_fsp, &lck); + SMB_VFS_STRICT_UNLOCK(dest_fsp->conn, dest_fsp, &lck); + + if (ret == -1) { + DEBUG(1, ("failed copy_file_range: %s\n", + strerror(errno) )); + errno = saved_errno; + if (errno != EXDEV && + errno != ENOTSUP && + errno != ENOSYS) { + /* fail for sure - no rw fallback path */ + tevent_req_nterror(req, map_nt_error_from_unix(errno)); + return tevent_req_post(req, ev); + } else { + /* continue rw fallback path */ + goto manualcopy; + } + } + + if (ret != num) { + /* zero tolerance for short copies */ + tevent_req_nterror(req, NT_STATUS_IO_DEVICE_ERROR); + return tevent_req_post(req, ev); + } + /* all done well if we ended up here */ + tevent_req_done(req); + return tevent_req_post(req, ev); +#endif +manualcopy: + DEBUG(5, ("performing server side copy chunk " + "(rw) of length %lu at offsets " + "%lu/%lu\n", (unsigned long)num, + (unsigned long)src_off, (unsigned long)dest_off)); + vfs_cc_state->buf = talloc_array(vfs_cc_state, uint8_t, + MIN(num, 8*1024*1024)); + if (tevent_req_nomem(vfs_cc_state->buf, req)) { + return tevent_req_post(req, ev); + } + while (vfs_cc_state->copied < num) { off_t this_num = MIN(talloc_array_length(vfs_cc_state->buf), num - vfs_cc_state->copied); -- 2.7.4