|
@@ -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;
|
|
|
|