summaryrefslogtreecommitdiff
path: root/fs/xfs/xfs_reflink.c
diff options
context:
space:
mode:
authorDarrick J. Wong <darrick.wong@oracle.com>2016-10-03 19:11:43 +0300
committerDarrick J. Wong <darrick.wong@oracle.com>2016-10-06 02:26:26 +0300
commitf7ca35227253dc8244fd908140b06010e67a31e5 (patch)
tree2e2731cafe2a8e8f6679d2a58eef031c1a84ffba /fs/xfs/xfs_reflink.c
parent98cc2db5b8b2c9f38aebf54a7b03657406b6de26 (diff)
downloadlinux-f7ca35227253dc8244fd908140b06010e67a31e5.tar.xz
xfs: create a separate cow extent size hint for the allocator
Create a per-inode extent size allocator hint for copy-on-write. This hint is separate from the existing extent size hint so that CoW can take advantage of the fragmentation-reducing properties of extent size hints without disabling delalloc for regular writes. The extent size hint that's fed to the allocator during a copy on write operation is the greater of the cowextsize and regular extsize hint. During reflink, if we're sharing the entire source file to the entire destination file and the destination file doesn't already have a cowextsize hint, propagate the source file's cowextsize hint to the destination file. Furthermore, zero the bulkstat buffer prior to setting the fields so that we don't copy kernel memory contents into userspace. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Reviewed-by: Christoph Hellwig <hch@lst.de>
Diffstat (limited to 'fs/xfs/xfs_reflink.c')
-rw-r--r--fs/xfs/xfs_reflink.c41
1 files changed, 34 insertions, 7 deletions
diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c
index d4707e627a74..8e894118295f 100644
--- a/fs/xfs/xfs_reflink.c
+++ b/fs/xfs/xfs_reflink.c
@@ -238,6 +238,7 @@ __xfs_reflink_reserve_cow(
int nimaps, eof = 0, error = 0;
bool shared = false, trimmed = false;
xfs_extnum_t idx;
+ xfs_extlen_t align;
/* Already reserved? Skip the refcount btree access. */
xfs_bmap_search_extents(ip, *offset_fsb, XFS_COW_FORK, &eof, &idx,
@@ -277,6 +278,10 @@ __xfs_reflink_reserve_cow(
if (error)
goto out_unlock;
+ align = xfs_eof_alignment(ip, xfs_get_cowextsz_hint(ip));
+ if (align)
+ end_fsb = roundup_64(end_fsb, align);
+
retry:
error = xfs_bmapi_reserve_delalloc(ip, XFS_COW_FORK, *offset_fsb,
end_fsb - *offset_fsb, &got,
@@ -927,18 +932,19 @@ out_error:
}
/*
- * Update destination inode size, if necessary.
+ * Update destination inode size & cowextsize hint, if necessary.
*/
STATIC int
xfs_reflink_update_dest(
struct xfs_inode *dest,
- xfs_off_t newlen)
+ xfs_off_t newlen,
+ xfs_extlen_t cowextsize)
{
struct xfs_mount *mp = dest->i_mount;
struct xfs_trans *tp;
int error;
- if (newlen <= i_size_read(VFS_I(dest)))
+ if (newlen <= i_size_read(VFS_I(dest)) && cowextsize == 0)
return 0;
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, 0, 0, &tp);
@@ -948,9 +954,17 @@ xfs_reflink_update_dest(
xfs_ilock(dest, XFS_ILOCK_EXCL);
xfs_trans_ijoin(tp, dest, XFS_ILOCK_EXCL);
- trace_xfs_reflink_update_inode_size(dest, newlen);
- i_size_write(VFS_I(dest), newlen);
- dest->i_d.di_size = newlen;
+ if (newlen > i_size_read(VFS_I(dest))) {
+ trace_xfs_reflink_update_inode_size(dest, newlen);
+ i_size_write(VFS_I(dest), newlen);
+ dest->i_d.di_size = newlen;
+ }
+
+ if (cowextsize) {
+ dest->i_d.di_cowextsize = cowextsize;
+ dest->i_d.di_flags2 |= XFS_DIFLAG2_COWEXTSIZE;
+ }
+
xfs_trans_log_inode(tp, dest, XFS_ILOG_CORE);
error = xfs_trans_commit(tp);
@@ -1270,6 +1284,7 @@ xfs_reflink_remap_range(
xfs_fileoff_t sfsbno, dfsbno;
xfs_filblks_t fsblen;
int error;
+ xfs_extlen_t cowextsize;
bool is_same;
if (!xfs_sb_version_hasreflink(&mp->m_sb))
@@ -1330,7 +1345,19 @@ xfs_reflink_remap_range(
if (error)
goto out_error;
- error = xfs_reflink_update_dest(dest, destoff + len);
+ /*
+ * Carry the cowextsize hint from src to dest if we're sharing the
+ * entire source file to the entire destination file, the source file
+ * has a cowextsize hint, and the destination file does not.
+ */
+ cowextsize = 0;
+ if (srcoff == 0 && len == i_size_read(VFS_I(src)) &&
+ (src->i_d.di_flags2 & XFS_DIFLAG2_COWEXTSIZE) &&
+ destoff == 0 && len >= i_size_read(VFS_I(dest)) &&
+ !(dest->i_d.di_flags2 & XFS_DIFLAG2_COWEXTSIZE))
+ cowextsize = src->i_d.di_cowextsize;
+
+ error = xfs_reflink_update_dest(dest, destoff + len, cowextsize);
if (error)
goto out_error;