summaryrefslogtreecommitdiff
path: root/fs/xfs/xfs_reflink.c
diff options
context:
space:
mode:
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;