From patchwork Fri Sep 11 20:30:21 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Schumaker, Anna" X-Patchwork-Id: 7163701 Return-Path: X-Original-To: patchwork-linux-btrfs@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id A8C88BEEC1 for ; Fri, 11 Sep 2015 20:31:23 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id A4211207F3 for ; Fri, 11 Sep 2015 20:31:22 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id F153D207CE for ; Fri, 11 Sep 2015 20:31:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754138AbbIKUbK (ORCPT ); Fri, 11 Sep 2015 16:31:10 -0400 Received: from mx143.netapp.com ([216.240.21.24]:3501 "EHLO mx143.netapp.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751841AbbIKUan (ORCPT ); Fri, 11 Sep 2015 16:30:43 -0400 X-IronPort-AV: E=Sophos;i="5.17,513,1437462000"; d="scan'208";a="66009772" Received: from vmwexchts04-prd.hq.netapp.com ([10.122.105.32]) by mx143-out.netapp.com with ESMTP; 11 Sep 2015 13:30:43 -0700 Received: from smtp2.corp.netapp.com (10.57.159.114) by VMWEXCHTS04-PRD.hq.netapp.com (10.122.105.32) with Microsoft SMTP Server id 15.0.1076.9; Fri, 11 Sep 2015 13:30:41 -0700 Received: from davros.com ([10.63.231.129]) by smtp2.corp.netapp.com (8.13.1/8.13.1/NTAP-1.6) with ESMTP id t8BKUOKj001605; Fri, 11 Sep 2015 13:30:40 -0700 (PDT) From: Anna Schumaker To: , , , , , , , , , , Subject: [PATCH v2 8/9] vfs: copy_file_range() can do a pagecache copy with splice Date: Fri, 11 Sep 2015 16:30:21 -0400 Message-ID: <1442003423-6884-9-git-send-email-Anna.Schumaker@Netapp.com> X-Mailer: git-send-email 2.5.1 In-Reply-To: <1442003423-6884-1-git-send-email-Anna.Schumaker@Netapp.com> References: <1442003423-6884-1-git-send-email-Anna.Schumaker@Netapp.com> MIME-Version: 1.0 Sender: linux-btrfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The NFS server will need some kind offallback for filesystems that don't have any kind of copy acceleration, and it should be generally useful to have an in-kernel copy to avoid lots of switches between kernel and user space. I make this configurable by adding two new flags. Users who only want a reflink can pass COPY_FR_REFLINK, and users who want a full data copy can pass COPY_FR_COPY. The default (flags=0) means to first attempt a reflink, but use the pagecache if that fails. I moved the rw_verify_area() calls into the fallback code since some filesystems can handle reflinking a large range. Signed-off-by: Anna Schumaker --- v2: - Rename COPY_REFLINK -> COPY_FR_REFLINK - Introduce COPY_FR_COPY flag - Flags == 0 is really COPY_FR_COPY|COPY_FR_REFLINK - Drop check for invalid flags - Move call to do_splice_direct() into a new function - Move rw_verify_area() checks into the new fallback function --- fs/read_write.c | 56 ++++++++++++++++++++++++++++------------------- include/linux/copy.h | 6 +++++ include/uapi/linux/Kbuild | 1 + include/uapi/linux/copy.h | 7 ++++++ 4 files changed, 48 insertions(+), 22 deletions(-) create mode 100644 include/linux/copy.h create mode 100644 include/uapi/linux/copy.h diff --git a/fs/read_write.c b/fs/read_write.c index 363bd3e..ba24884 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -1329,6 +1330,29 @@ COMPAT_SYSCALL_DEFINE4(sendfile64, int, out_fd, int, in_fd, } #endif +static ssize_t vfs_copy_file_pagecache(struct file *file_in, loff_t pos_in, + struct file *file_out, loff_t pos_out, + size_t len) +{ + ssize_t ret; + + ret = rw_verify_area(READ, file_in, &pos_in, len); + if (ret >= 0) { + len = ret; + ret = rw_verify_area(WRITE, file_out, &pos_out, len); + if (ret >= 0) + len = ret; + } + if (ret < 0) + return ret; + + file_start_write(file_out); + ret = do_splice_direct(file_in, &pos_in, file_out, &pos_out, len, 0); + file_end_write(file_out); + + return ret; +} + /* * copy_file_range() differs from regular file read and write in that it * specifically allows return partial success. When it does so is up to @@ -1338,34 +1362,17 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in, struct file *file_out, loff_t pos_out, size_t len, int flags) { - struct inode *inode_in; - struct inode *inode_out; ssize_t ret; - if (flags) - return -EINVAL; - - /* copy_file_range allows full ssize_t len, ignoring MAX_RW_COUNT */ - ret = rw_verify_area(READ, file_in, &pos_in, len); - if (ret >= 0) - ret = rw_verify_area(WRITE, file_out, &pos_out, len); - if (ret < 0) - return ret; + if (flags == 0) + flags = COPY_FR_COPY | COPY_FR_REFLINK; if (!(file_in->f_mode & FMODE_READ) || !(file_out->f_mode & FMODE_WRITE) || (file_out->f_flags & O_APPEND) || - !file_out->f_op || !file_out->f_op->copy_file_range) + !file_in->f_op) return -EBADF; - inode_in = file_inode(file_in); - inode_out = file_inode(file_out); - - /* make sure offsets don't wrap and the input is inside i_size */ - if (pos_in + len < pos_in || pos_out + len < pos_out || - pos_in + len > i_size_read(inode_in)) - return -EINVAL; - if (len == 0) return 0; @@ -1373,8 +1380,13 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in, if (ret) return ret; - ret = file_out->f_op->copy_file_range(file_in, pos_in, file_out, pos_out, - len, flags); + ret = -EOPNOTSUPP; + if (file_out->f_op->copy_file_range) + ret = file_out->f_op->copy_file_range(file_in, pos_in, file_out, + pos_out, len, flags); + if ((ret < 0) && (flags & COPY_FR_COPY)) + ret = vfs_copy_file_pagecache(file_in, pos_in, file_out, + pos_out, len); if (ret > 0) { fsnotify_access(file_in); add_rchar(current, ret); diff --git a/include/linux/copy.h b/include/linux/copy.h new file mode 100644 index 0000000..fd54543 --- /dev/null +++ b/include/linux/copy.h @@ -0,0 +1,6 @@ +#ifndef _LINUX_COPY_H +#define _LINUX_COPY_H + +#include + +#endif /* _LINUX_COPY_H */ diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index 70ff1d9..d46830a 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -90,6 +90,7 @@ header-y += coda_psdev.h header-y += coff.h header-y += connector.h header-y += const.h +header-y += copy.h header-y += cramfs_fs.h header-y += cuda.h header-y += cyclades.h diff --git a/include/uapi/linux/copy.h b/include/uapi/linux/copy.h new file mode 100644 index 0000000..2da59a8 --- /dev/null +++ b/include/uapi/linux/copy.h @@ -0,0 +1,7 @@ +#ifndef _UAPI_LINUX_COPY_H +#define _UAPI_LINUX_COPY_H + +#define COPY_FR_COPY (1 << 0) /* Only do a pagecache copy. */ +#define COPY_FR_REFLINK (1 << 1) /* Only make a reflink. */ + +#endif /* _UAPI_LINUX_COPY_H */