aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/xfs/libxfs/xfs_bmap.c9
-rw-r--r--fs/xfs/libxfs/xfs_format.h3
-rw-r--r--fs/xfs/libxfs/xfs_fs.h3
-rw-r--r--fs/xfs/libxfs/xfs_inode_buf.c4
-rw-r--r--fs/xfs/libxfs/xfs_inode_buf.h1
-rw-r--r--fs/xfs/libxfs/xfs_log_format.h3
-rw-r--r--fs/xfs/xfs_bmap_util.c9
-rw-r--r--fs/xfs/xfs_inode.c33
-rw-r--r--fs/xfs/xfs_inode.h1
-rw-r--r--fs/xfs/xfs_inode_item.c2
-rw-r--r--fs/xfs/xfs_ioctl.c67
-rw-r--r--fs/xfs/xfs_iomap.c2
-rw-r--r--fs/xfs/xfs_iomap.h1
-rw-r--r--fs/xfs/xfs_itable.c8
-rw-r--r--fs/xfs/xfs_reflink.c41
15 files changed, 167 insertions, 20 deletions
diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index e4bf2c406e3b..9702f4c112bf 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -3666,7 +3666,9 @@ xfs_bmap_btalloc(
3666 else if (mp->m_dalign) 3666 else if (mp->m_dalign)
3667 stripe_align = mp->m_dalign; 3667 stripe_align = mp->m_dalign;
3668 3668
3669 if (xfs_alloc_is_userdata(ap->datatype)) 3669 if (ap->flags & XFS_BMAPI_COWFORK)
3670 align = xfs_get_cowextsz_hint(ap->ip);
3671 else if (xfs_alloc_is_userdata(ap->datatype))
3670 align = xfs_get_extsz_hint(ap->ip); 3672 align = xfs_get_extsz_hint(ap->ip);
3671 if (unlikely(align)) { 3673 if (unlikely(align)) {
3672 error = xfs_bmap_extsize_align(mp, &ap->got, &ap->prev, 3674 error = xfs_bmap_extsize_align(mp, &ap->got, &ap->prev,
@@ -4192,7 +4194,10 @@ xfs_bmapi_reserve_delalloc(
4192 alen = XFS_FILBLKS_MIN(alen, got->br_startoff - aoff); 4194 alen = XFS_FILBLKS_MIN(alen, got->br_startoff - aoff);
4193 4195
4194 /* Figure out the extent size, adjust alen */ 4196 /* Figure out the extent size, adjust alen */
4195 extsz = xfs_get_extsz_hint(ip); 4197 if (whichfork == XFS_COW_FORK)
4198 extsz = xfs_get_cowextsz_hint(ip);
4199 else
4200 extsz = xfs_get_extsz_hint(ip);
4196 if (extsz) { 4201 if (extsz) {
4197 error = xfs_bmap_extsize_align(mp, got, prev, extsz, rt, eof, 4202 error = xfs_bmap_extsize_align(mp, got, prev, extsz, rt, eof,
4198 1, 0, &aoff, &alen); 4203 1, 0, &aoff, &alen);
diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h
index 777fa974a319..5e04ea2aa902 100644
--- a/fs/xfs/libxfs/xfs_format.h
+++ b/fs/xfs/libxfs/xfs_format.h
@@ -901,7 +901,8 @@ typedef struct xfs_dinode {
901 __be64 di_changecount; /* number of attribute changes */ 901 __be64 di_changecount; /* number of attribute changes */
902 __be64 di_lsn; /* flush sequence */ 902 __be64 di_lsn; /* flush sequence */
903 __be64 di_flags2; /* more random flags */ 903 __be64 di_flags2; /* more random flags */
904 __u8 di_pad2[16]; /* more padding for future expansion */ 904 __be32 di_cowextsize; /* basic cow extent size for file */
905 __u8 di_pad2[12]; /* more padding for future expansion */
905 906
906 /* fields only written to during inode creation */ 907 /* fields only written to during inode creation */
907 xfs_timestamp_t di_crtime; /* time created */ 908 xfs_timestamp_t di_crtime; /* time created */
diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h
index 3d1efe5bf37b..b72dc821d78b 100644
--- a/fs/xfs/libxfs/xfs_fs.h
+++ b/fs/xfs/libxfs/xfs_fs.h
@@ -278,7 +278,8 @@ typedef struct xfs_bstat {
278#define bs_projid bs_projid_lo /* (previously just bs_projid) */ 278#define bs_projid bs_projid_lo /* (previously just bs_projid) */
279 __u16 bs_forkoff; /* inode fork offset in bytes */ 279 __u16 bs_forkoff; /* inode fork offset in bytes */
280 __u16 bs_projid_hi; /* higher part of project id */ 280 __u16 bs_projid_hi; /* higher part of project id */
281 unsigned char bs_pad[10]; /* pad space, unused */ 281 unsigned char bs_pad[6]; /* pad space, unused */
282 __u32 bs_cowextsize; /* cow extent size */
282 __u32 bs_dmevmask; /* DMIG event mask */ 283 __u32 bs_dmevmask; /* DMIG event mask */
283 __u16 bs_dmstate; /* DMIG state info */ 284 __u16 bs_dmstate; /* DMIG state info */
284 __u16 bs_aextents; /* attribute number of extents */ 285 __u16 bs_aextents; /* attribute number of extents */
diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c
index 4b9769e23c83..a3e803823a19 100644
--- a/fs/xfs/libxfs/xfs_inode_buf.c
+++ b/fs/xfs/libxfs/xfs_inode_buf.c
@@ -256,6 +256,7 @@ xfs_inode_from_disk(
256 to->di_crtime.t_sec = be32_to_cpu(from->di_crtime.t_sec); 256 to->di_crtime.t_sec = be32_to_cpu(from->di_crtime.t_sec);
257 to->di_crtime.t_nsec = be32_to_cpu(from->di_crtime.t_nsec); 257 to->di_crtime.t_nsec = be32_to_cpu(from->di_crtime.t_nsec);
258 to->di_flags2 = be64_to_cpu(from->di_flags2); 258 to->di_flags2 = be64_to_cpu(from->di_flags2);
259 to->di_cowextsize = be32_to_cpu(from->di_cowextsize);
259 } 260 }
260} 261}
261 262
@@ -305,7 +306,7 @@ xfs_inode_to_disk(
305 to->di_crtime.t_sec = cpu_to_be32(from->di_crtime.t_sec); 306 to->di_crtime.t_sec = cpu_to_be32(from->di_crtime.t_sec);
306 to->di_crtime.t_nsec = cpu_to_be32(from->di_crtime.t_nsec); 307 to->di_crtime.t_nsec = cpu_to_be32(from->di_crtime.t_nsec);
307 to->di_flags2 = cpu_to_be64(from->di_flags2); 308 to->di_flags2 = cpu_to_be64(from->di_flags2);
308 309 to->di_cowextsize = cpu_to_be32(from->di_cowextsize);
309 to->di_ino = cpu_to_be64(ip->i_ino); 310 to->di_ino = cpu_to_be64(ip->i_ino);
310 to->di_lsn = cpu_to_be64(lsn); 311 to->di_lsn = cpu_to_be64(lsn);
311 memset(to->di_pad2, 0, sizeof(to->di_pad2)); 312 memset(to->di_pad2, 0, sizeof(to->di_pad2));
@@ -357,6 +358,7 @@ xfs_log_dinode_to_disk(
357 to->di_crtime.t_sec = cpu_to_be32(from->di_crtime.t_sec); 358 to->di_crtime.t_sec = cpu_to_be32(from->di_crtime.t_sec);
358 to->di_crtime.t_nsec = cpu_to_be32(from->di_crtime.t_nsec); 359 to->di_crtime.t_nsec = cpu_to_be32(from->di_crtime.t_nsec);
359 to->di_flags2 = cpu_to_be64(from->di_flags2); 360 to->di_flags2 = cpu_to_be64(from->di_flags2);
361 to->di_cowextsize = cpu_to_be32(from->di_cowextsize);
360 to->di_ino = cpu_to_be64(from->di_ino); 362 to->di_ino = cpu_to_be64(from->di_ino);
361 to->di_lsn = cpu_to_be64(from->di_lsn); 363 to->di_lsn = cpu_to_be64(from->di_lsn);
362 memcpy(to->di_pad2, from->di_pad2, sizeof(to->di_pad2)); 364 memcpy(to->di_pad2, from->di_pad2, sizeof(to->di_pad2));
diff --git a/fs/xfs/libxfs/xfs_inode_buf.h b/fs/xfs/libxfs/xfs_inode_buf.h
index 7c4dd321b215..62d9d4681c8c 100644
--- a/fs/xfs/libxfs/xfs_inode_buf.h
+++ b/fs/xfs/libxfs/xfs_inode_buf.h
@@ -47,6 +47,7 @@ struct xfs_icdinode {
47 __uint16_t di_flags; /* random flags, XFS_DIFLAG_... */ 47 __uint16_t di_flags; /* random flags, XFS_DIFLAG_... */
48 48
49 __uint64_t di_flags2; /* more random flags */ 49 __uint64_t di_flags2; /* more random flags */
50 __uint32_t di_cowextsize; /* basic cow extent size for file */
50 51
51 xfs_ictimestamp_t di_crtime; /* time created */ 52 xfs_ictimestamp_t di_crtime; /* time created */
52}; 53};
diff --git a/fs/xfs/libxfs/xfs_log_format.h b/fs/xfs/libxfs/xfs_log_format.h
index 5dd7c2e8f439..364ce6fcc213 100644
--- a/fs/xfs/libxfs/xfs_log_format.h
+++ b/fs/xfs/libxfs/xfs_log_format.h
@@ -423,7 +423,8 @@ struct xfs_log_dinode {
423 __uint64_t di_changecount; /* number of attribute changes */ 423 __uint64_t di_changecount; /* number of attribute changes */
424 xfs_lsn_t di_lsn; /* flush sequence */ 424 xfs_lsn_t di_lsn; /* flush sequence */
425 __uint64_t di_flags2; /* more random flags */ 425 __uint64_t di_flags2; /* more random flags */
426 __uint8_t di_pad2[16]; /* more padding for future expansion */ 426 __uint32_t di_cowextsize; /* basic cow extent size for file */
427 __uint8_t di_pad2[12]; /* more padding for future expansion */
427 428
428 /* fields only written to during inode creation */ 429 /* fields only written to during inode creation */
429 xfs_ictimestamp_t di_crtime; /* time created */ 430 xfs_ictimestamp_t di_crtime; /* time created */
diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c
index e2f46e645612..4a807f05e460 100644
--- a/fs/xfs/xfs_bmap_util.c
+++ b/fs/xfs/xfs_bmap_util.c
@@ -582,8 +582,13 @@ xfs_getbmap(
582 if (ip->i_cformat != XFS_DINODE_FMT_EXTENTS) 582 if (ip->i_cformat != XFS_DINODE_FMT_EXTENTS)
583 return -EINVAL; 583 return -EINVAL;
584 584
585 prealloced = 0; 585 if (xfs_get_cowextsz_hint(ip)) {
586 fixlen = XFS_ISIZE(ip); 586 prealloced = 1;
587 fixlen = mp->m_super->s_maxbytes;
588 } else {
589 prealloced = 0;
590 fixlen = XFS_ISIZE(ip);
591 }
587 break; 592 break;
588 default: 593 default:
589 if (ip->i_d.di_format != XFS_DINODE_FMT_EXTENTS && 594 if (ip->i_d.di_format != XFS_DINODE_FMT_EXTENTS &&
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index f072f48b3c04..09640bdd3e44 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -78,6 +78,27 @@ xfs_get_extsz_hint(
78} 78}
79 79
80/* 80/*
81 * Helper function to extract CoW extent size hint from inode.
82 * Between the extent size hint and the CoW extent size hint, we
83 * return the greater of the two.
84 */
85xfs_extlen_t
86xfs_get_cowextsz_hint(
87 struct xfs_inode *ip)
88{
89 xfs_extlen_t a, b;
90
91 a = 0;
92 if (ip->i_d.di_flags2 & XFS_DIFLAG2_COWEXTSIZE)
93 a = ip->i_d.di_cowextsize;
94 b = xfs_get_extsz_hint(ip);
95
96 if (a > b)
97 return a;
98 return b;
99}
100
101/*
81 * These two are wrapper routines around the xfs_ilock() routine used to 102 * These two are wrapper routines around the xfs_ilock() routine used to
82 * centralize some grungy code. They are used in places that wish to lock the 103 * centralize some grungy code. They are used in places that wish to lock the
83 * inode solely for reading the extents. The reason these places can't just 104 * inode solely for reading the extents. The reason these places can't just
@@ -652,6 +673,8 @@ _xfs_dic2xflags(
652 if (di_flags2 & XFS_DIFLAG2_ANY) { 673 if (di_flags2 & XFS_DIFLAG2_ANY) {
653 if (di_flags2 & XFS_DIFLAG2_DAX) 674 if (di_flags2 & XFS_DIFLAG2_DAX)
654 flags |= FS_XFLAG_DAX; 675 flags |= FS_XFLAG_DAX;
676 if (di_flags2 & XFS_DIFLAG2_COWEXTSIZE)
677 flags |= FS_XFLAG_COWEXTSIZE;
655 } 678 }
656 679
657 if (has_attr) 680 if (has_attr)
@@ -835,6 +858,7 @@ xfs_ialloc(
835 if (ip->i_d.di_version == 3) { 858 if (ip->i_d.di_version == 3) {
836 inode->i_version = 1; 859 inode->i_version = 1;
837 ip->i_d.di_flags2 = 0; 860 ip->i_d.di_flags2 = 0;
861 ip->i_d.di_cowextsize = 0;
838 ip->i_d.di_crtime.t_sec = (__int32_t)tv.tv_sec; 862 ip->i_d.di_crtime.t_sec = (__int32_t)tv.tv_sec;
839 ip->i_d.di_crtime.t_nsec = (__int32_t)tv.tv_nsec; 863 ip->i_d.di_crtime.t_nsec = (__int32_t)tv.tv_nsec;
840 } 864 }
@@ -897,6 +921,15 @@ xfs_ialloc(
897 ip->i_d.di_flags |= di_flags; 921 ip->i_d.di_flags |= di_flags;
898 ip->i_d.di_flags2 |= di_flags2; 922 ip->i_d.di_flags2 |= di_flags2;
899 } 923 }
924 if (pip &&
925 (pip->i_d.di_flags2 & XFS_DIFLAG2_ANY) &&
926 pip->i_d.di_version == 3 &&
927 ip->i_d.di_version == 3) {
928 if (pip->i_d.di_flags2 & XFS_DIFLAG2_COWEXTSIZE) {
929 ip->i_d.di_flags2 |= XFS_DIFLAG2_COWEXTSIZE;
930 ip->i_d.di_cowextsize = pip->i_d.di_cowextsize;
931 }
932 }
900 /* FALLTHROUGH */ 933 /* FALLTHROUGH */
901 case S_IFLNK: 934 case S_IFLNK:
902 ip->i_d.di_format = XFS_DINODE_FMT_EXTENTS; 935 ip->i_d.di_format = XFS_DINODE_FMT_EXTENTS;
diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h
index ff6fc03c0092..6d63dc031127 100644
--- a/fs/xfs/xfs_inode.h
+++ b/fs/xfs/xfs_inode.h
@@ -426,6 +426,7 @@ int xfs_iflush(struct xfs_inode *, struct xfs_buf **);
426void xfs_lock_two_inodes(xfs_inode_t *, xfs_inode_t *, uint); 426void xfs_lock_two_inodes(xfs_inode_t *, xfs_inode_t *, uint);
427 427
428xfs_extlen_t xfs_get_extsz_hint(struct xfs_inode *ip); 428xfs_extlen_t xfs_get_extsz_hint(struct xfs_inode *ip);
429xfs_extlen_t xfs_get_cowextsz_hint(struct xfs_inode *ip);
429 430
430int xfs_dir_ialloc(struct xfs_trans **, struct xfs_inode *, umode_t, 431int xfs_dir_ialloc(struct xfs_trans **, struct xfs_inode *, umode_t,
431 xfs_nlink_t, xfs_dev_t, prid_t, int, 432 xfs_nlink_t, xfs_dev_t, prid_t, int,
diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c
index 892c2aced207..9610e9c00952 100644
--- a/fs/xfs/xfs_inode_item.c
+++ b/fs/xfs/xfs_inode_item.c
@@ -368,7 +368,7 @@ xfs_inode_to_log_dinode(
368 to->di_crtime.t_sec = from->di_crtime.t_sec; 368 to->di_crtime.t_sec = from->di_crtime.t_sec;
369 to->di_crtime.t_nsec = from->di_crtime.t_nsec; 369 to->di_crtime.t_nsec = from->di_crtime.t_nsec;
370 to->di_flags2 = from->di_flags2; 370 to->di_flags2 = from->di_flags2;
371 371 to->di_cowextsize = from->di_cowextsize;
372 to->di_ino = ip->i_ino; 372 to->di_ino = ip->i_ino;
373 to->di_lsn = lsn; 373 to->di_lsn = lsn;
374 memset(to->di_pad2, 0, sizeof(to->di_pad2)); 374 memset(to->di_pad2, 0, sizeof(to->di_pad2));
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index 96a70fd1f5d6..1388a1275dc8 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -903,6 +903,8 @@ xfs_ioc_fsgetxattr(
903 xfs_ilock(ip, XFS_ILOCK_SHARED); 903 xfs_ilock(ip, XFS_ILOCK_SHARED);
904 fa.fsx_xflags = xfs_ip2xflags(ip); 904 fa.fsx_xflags = xfs_ip2xflags(ip);
905 fa.fsx_extsize = ip->i_d.di_extsize << ip->i_mount->m_sb.sb_blocklog; 905 fa.fsx_extsize = ip->i_d.di_extsize << ip->i_mount->m_sb.sb_blocklog;
906 fa.fsx_cowextsize = ip->i_d.di_cowextsize <<
907 ip->i_mount->m_sb.sb_blocklog;
906 fa.fsx_projid = xfs_get_projid(ip); 908 fa.fsx_projid = xfs_get_projid(ip);
907 909
908 if (attr) { 910 if (attr) {
@@ -973,12 +975,13 @@ xfs_set_diflags(
973 if (ip->i_d.di_version < 3) 975 if (ip->i_d.di_version < 3)
974 return; 976 return;
975 977
976 di_flags2 = 0; 978 di_flags2 = (ip->i_d.di_flags2 & XFS_DIFLAG2_REFLINK);
977 if (xflags & FS_XFLAG_DAX) 979 if (xflags & FS_XFLAG_DAX)
978 di_flags2 |= XFS_DIFLAG2_DAX; 980 di_flags2 |= XFS_DIFLAG2_DAX;
981 if (xflags & FS_XFLAG_COWEXTSIZE)
982 di_flags2 |= XFS_DIFLAG2_COWEXTSIZE;
979 983
980 ip->i_d.di_flags2 = di_flags2; 984 ip->i_d.di_flags2 = di_flags2;
981
982} 985}
983 986
984STATIC void 987STATIC void
@@ -1219,6 +1222,56 @@ xfs_ioctl_setattr_check_extsize(
1219 return 0; 1222 return 0;
1220} 1223}
1221 1224
1225/*
1226 * CoW extent size hint validation rules are:
1227 *
1228 * 1. CoW extent size hint can only be set if reflink is enabled on the fs.
1229 * The inode does not have to have any shared blocks, but it must be a v3.
1230 * 2. FS_XFLAG_COWEXTSIZE is only valid for directories and regular files;
1231 * for a directory, the hint is propagated to new files.
1232 * 3. Can be changed on files & directories at any time.
1233 * 4. CoW extsize hint of 0 turns off hints, clears inode flags.
1234 * 5. Extent size must be a multiple of the appropriate block size.
1235 * 6. The extent size hint must be limited to half the AG size to avoid
1236 * alignment extending the extent beyond the limits of the AG.
1237 */
1238static int
1239xfs_ioctl_setattr_check_cowextsize(
1240 struct xfs_inode *ip,
1241 struct fsxattr *fa)
1242{
1243 struct xfs_mount *mp = ip->i_mount;
1244
1245 if (!(fa->fsx_xflags & FS_XFLAG_COWEXTSIZE))
1246 return 0;
1247
1248 if (!xfs_sb_version_hasreflink(&ip->i_mount->m_sb) ||
1249 ip->i_d.di_version != 3)
1250 return -EINVAL;
1251
1252 if (!S_ISREG(VFS_I(ip)->i_mode) && !S_ISDIR(VFS_I(ip)->i_mode))
1253 return -EINVAL;
1254
1255 if (fa->fsx_cowextsize != 0) {
1256 xfs_extlen_t size;
1257 xfs_fsblock_t cowextsize_fsb;
1258
1259 cowextsize_fsb = XFS_B_TO_FSB(mp, fa->fsx_cowextsize);
1260 if (cowextsize_fsb > MAXEXTLEN)
1261 return -EINVAL;
1262
1263 size = mp->m_sb.sb_blocksize;
1264 if (cowextsize_fsb > mp->m_sb.sb_agblocks / 2)
1265 return -EINVAL;
1266
1267 if (fa->fsx_cowextsize % size)
1268 return -EINVAL;
1269 } else
1270 fa->fsx_xflags &= ~FS_XFLAG_COWEXTSIZE;
1271
1272 return 0;
1273}
1274
1222static int 1275static int
1223xfs_ioctl_setattr_check_projid( 1276xfs_ioctl_setattr_check_projid(
1224 struct xfs_inode *ip, 1277 struct xfs_inode *ip,
@@ -1311,6 +1364,10 @@ xfs_ioctl_setattr(
1311 if (code) 1364 if (code)
1312 goto error_trans_cancel; 1365 goto error_trans_cancel;
1313 1366
1367 code = xfs_ioctl_setattr_check_cowextsize(ip, fa);
1368 if (code)
1369 goto error_trans_cancel;
1370
1314 code = xfs_ioctl_setattr_xflags(tp, ip, fa); 1371 code = xfs_ioctl_setattr_xflags(tp, ip, fa);
1315 if (code) 1372 if (code)
1316 goto error_trans_cancel; 1373 goto error_trans_cancel;
@@ -1346,6 +1403,12 @@ xfs_ioctl_setattr(
1346 ip->i_d.di_extsize = fa->fsx_extsize >> mp->m_sb.sb_blocklog; 1403 ip->i_d.di_extsize = fa->fsx_extsize >> mp->m_sb.sb_blocklog;
1347 else 1404 else
1348 ip->i_d.di_extsize = 0; 1405 ip->i_d.di_extsize = 0;
1406 if (ip->i_d.di_version == 3 &&
1407 (ip->i_d.di_flags2 & XFS_DIFLAG2_COWEXTSIZE))
1408 ip->i_d.di_cowextsize = fa->fsx_cowextsize >>
1409 mp->m_sb.sb_blocklog;
1410 else
1411 ip->i_d.di_cowextsize = 0;
1349 1412
1350 code = xfs_trans_commit(tp); 1413 code = xfs_trans_commit(tp);
1351 1414
diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
index 765849ed9b70..d907eb9f8ef3 100644
--- a/fs/xfs/xfs_iomap.c
+++ b/fs/xfs/xfs_iomap.c
@@ -71,7 +71,7 @@ xfs_bmbt_to_iomap(
71 iomap->bdev = xfs_find_bdev_for_inode(VFS_I(ip)); 71 iomap->bdev = xfs_find_bdev_for_inode(VFS_I(ip));
72} 72}
73 73
74static xfs_extlen_t 74xfs_extlen_t
75xfs_eof_alignment( 75xfs_eof_alignment(
76 struct xfs_inode *ip, 76 struct xfs_inode *ip,
77 xfs_extlen_t extsize) 77 xfs_extlen_t extsize)
diff --git a/fs/xfs/xfs_iomap.h b/fs/xfs/xfs_iomap.h
index a16b9567e76b..6d45cf01fcff 100644
--- a/fs/xfs/xfs_iomap.h
+++ b/fs/xfs/xfs_iomap.h
@@ -31,6 +31,7 @@ int xfs_iomap_write_unwritten(struct xfs_inode *, xfs_off_t, xfs_off_t);
31 31
32void xfs_bmbt_to_iomap(struct xfs_inode *, struct iomap *, 32void xfs_bmbt_to_iomap(struct xfs_inode *, struct iomap *,
33 struct xfs_bmbt_irec *); 33 struct xfs_bmbt_irec *);
34xfs_extlen_t xfs_eof_alignment(struct xfs_inode *ip, xfs_extlen_t extsize);
34 35
35extern struct iomap_ops xfs_iomap_ops; 36extern struct iomap_ops xfs_iomap_ops;
36extern struct iomap_ops xfs_xattr_iomap_ops; 37extern struct iomap_ops xfs_xattr_iomap_ops;
diff --git a/fs/xfs/xfs_itable.c b/fs/xfs/xfs_itable.c
index ce73eb34620d..66e881790c17 100644
--- a/fs/xfs/xfs_itable.c
+++ b/fs/xfs/xfs_itable.c
@@ -66,7 +66,7 @@ xfs_bulkstat_one_int(
66 if (!buffer || xfs_internal_inum(mp, ino)) 66 if (!buffer || xfs_internal_inum(mp, ino))
67 return -EINVAL; 67 return -EINVAL;
68 68
69 buf = kmem_alloc(sizeof(*buf), KM_SLEEP | KM_MAYFAIL); 69 buf = kmem_zalloc(sizeof(*buf), KM_SLEEP | KM_MAYFAIL);
70 if (!buf) 70 if (!buf)
71 return -ENOMEM; 71 return -ENOMEM;
72 72
@@ -111,6 +111,12 @@ xfs_bulkstat_one_int(
111 buf->bs_aextents = dic->di_anextents; 111 buf->bs_aextents = dic->di_anextents;
112 buf->bs_forkoff = XFS_IFORK_BOFF(ip); 112 buf->bs_forkoff = XFS_IFORK_BOFF(ip);
113 113
114 if (dic->di_version == 3) {
115 if (dic->di_flags2 & XFS_DIFLAG2_COWEXTSIZE)
116 buf->bs_cowextsize = dic->di_cowextsize <<
117 mp->m_sb.sb_blocklog;
118 }
119
114 switch (dic->di_format) { 120 switch (dic->di_format) {
115 case XFS_DINODE_FMT_DEV: 121 case XFS_DINODE_FMT_DEV:
116 buf->bs_rdev = ip->i_df.if_u2.if_rdev; 122 buf->bs_rdev = ip->i_df.if_u2.if_rdev;
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(
238 int nimaps, eof = 0, error = 0; 238 int nimaps, eof = 0, error = 0;
239 bool shared = false, trimmed = false; 239 bool shared = false, trimmed = false;
240 xfs_extnum_t idx; 240 xfs_extnum_t idx;
241 xfs_extlen_t align;
241 242
242 /* Already reserved? Skip the refcount btree access. */ 243 /* Already reserved? Skip the refcount btree access. */
243 xfs_bmap_search_extents(ip, *offset_fsb, XFS_COW_FORK, &eof, &idx, 244 xfs_bmap_search_extents(ip, *offset_fsb, XFS_COW_FORK, &eof, &idx,
@@ -277,6 +278,10 @@ __xfs_reflink_reserve_cow(
277 if (error) 278 if (error)
278 goto out_unlock; 279 goto out_unlock;
279 280
281 align = xfs_eof_alignment(ip, xfs_get_cowextsz_hint(ip));
282 if (align)
283 end_fsb = roundup_64(end_fsb, align);
284
280retry: 285retry:
281 error = xfs_bmapi_reserve_delalloc(ip, XFS_COW_FORK, *offset_fsb, 286 error = xfs_bmapi_reserve_delalloc(ip, XFS_COW_FORK, *offset_fsb,
282 end_fsb - *offset_fsb, &got, 287 end_fsb - *offset_fsb, &got,
@@ -927,18 +932,19 @@ out_error:
927} 932}
928 933
929/* 934/*
930 * Update destination inode size, if necessary. 935 * Update destination inode size & cowextsize hint, if necessary.
931 */ 936 */
932STATIC int 937STATIC int
933xfs_reflink_update_dest( 938xfs_reflink_update_dest(
934 struct xfs_inode *dest, 939 struct xfs_inode *dest,
935 xfs_off_t newlen) 940 xfs_off_t newlen,
941 xfs_extlen_t cowextsize)
936{ 942{
937 struct xfs_mount *mp = dest->i_mount; 943 struct xfs_mount *mp = dest->i_mount;
938 struct xfs_trans *tp; 944 struct xfs_trans *tp;
939 int error; 945 int error;
940 946
941 if (newlen <= i_size_read(VFS_I(dest))) 947 if (newlen <= i_size_read(VFS_I(dest)) && cowextsize == 0)
942 return 0; 948 return 0;
943 949
944 error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, 0, 0, &tp); 950 error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, 0, 0, &tp);
@@ -948,9 +954,17 @@ xfs_reflink_update_dest(
948 xfs_ilock(dest, XFS_ILOCK_EXCL); 954 xfs_ilock(dest, XFS_ILOCK_EXCL);
949 xfs_trans_ijoin(tp, dest, XFS_ILOCK_EXCL); 955 xfs_trans_ijoin(tp, dest, XFS_ILOCK_EXCL);
950 956
951 trace_xfs_reflink_update_inode_size(dest, newlen); 957 if (newlen > i_size_read(VFS_I(dest))) {
952 i_size_write(VFS_I(dest), newlen); 958 trace_xfs_reflink_update_inode_size(dest, newlen);
953 dest->i_d.di_size = newlen; 959 i_size_write(VFS_I(dest), newlen);
960 dest->i_d.di_size = newlen;
961 }
962
963 if (cowextsize) {
964 dest->i_d.di_cowextsize = cowextsize;
965 dest->i_d.di_flags2 |= XFS_DIFLAG2_COWEXTSIZE;
966 }
967
954 xfs_trans_log_inode(tp, dest, XFS_ILOG_CORE); 968 xfs_trans_log_inode(tp, dest, XFS_ILOG_CORE);
955 969
956 error = xfs_trans_commit(tp); 970 error = xfs_trans_commit(tp);
@@ -1270,6 +1284,7 @@ xfs_reflink_remap_range(
1270 xfs_fileoff_t sfsbno, dfsbno; 1284 xfs_fileoff_t sfsbno, dfsbno;
1271 xfs_filblks_t fsblen; 1285 xfs_filblks_t fsblen;
1272 int error; 1286 int error;
1287 xfs_extlen_t cowextsize;
1273 bool is_same; 1288 bool is_same;
1274 1289
1275 if (!xfs_sb_version_hasreflink(&mp->m_sb)) 1290 if (!xfs_sb_version_hasreflink(&mp->m_sb))
@@ -1330,7 +1345,19 @@ xfs_reflink_remap_range(
1330 if (error) 1345 if (error)
1331 goto out_error; 1346 goto out_error;
1332 1347
1333 error = xfs_reflink_update_dest(dest, destoff + len); 1348 /*
1349 * Carry the cowextsize hint from src to dest if we're sharing the
1350 * entire source file to the entire destination file, the source file
1351 * has a cowextsize hint, and the destination file does not.
1352 */
1353 cowextsize = 0;
1354 if (srcoff == 0 && len == i_size_read(VFS_I(src)) &&
1355 (src->i_d.di_flags2 & XFS_DIFLAG2_COWEXTSIZE) &&
1356 destoff == 0 && len >= i_size_read(VFS_I(dest)) &&
1357 !(dest->i_d.di_flags2 & XFS_DIFLAG2_COWEXTSIZE))
1358 cowextsize = src->i_d.di_cowextsize;
1359
1360 error = xfs_reflink_update_dest(dest, destoff + len, cowextsize);
1334 if (error) 1361 if (error)
1335 goto out_error; 1362 goto out_error;
1336 1363