diff options
-rw-r--r-- | fs/xfs/libxfs/xfs_bmap.c | 9 | ||||
-rw-r--r-- | fs/xfs/libxfs/xfs_format.h | 3 | ||||
-rw-r--r-- | fs/xfs/libxfs/xfs_fs.h | 3 | ||||
-rw-r--r-- | fs/xfs/libxfs/xfs_inode_buf.c | 4 | ||||
-rw-r--r-- | fs/xfs/libxfs/xfs_inode_buf.h | 1 | ||||
-rw-r--r-- | fs/xfs/libxfs/xfs_log_format.h | 3 | ||||
-rw-r--r-- | fs/xfs/xfs_bmap_util.c | 9 | ||||
-rw-r--r-- | fs/xfs/xfs_inode.c | 33 | ||||
-rw-r--r-- | fs/xfs/xfs_inode.h | 1 | ||||
-rw-r--r-- | fs/xfs/xfs_inode_item.c | 2 | ||||
-rw-r--r-- | fs/xfs/xfs_ioctl.c | 67 | ||||
-rw-r--r-- | fs/xfs/xfs_iomap.c | 2 | ||||
-rw-r--r-- | fs/xfs/xfs_iomap.h | 1 | ||||
-rw-r--r-- | fs/xfs/xfs_itable.c | 8 | ||||
-rw-r--r-- | fs/xfs/xfs_reflink.c | 41 |
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 | */ | ||
85 | xfs_extlen_t | ||
86 | xfs_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 **); | |||
426 | void xfs_lock_two_inodes(xfs_inode_t *, xfs_inode_t *, uint); | 426 | void xfs_lock_two_inodes(xfs_inode_t *, xfs_inode_t *, uint); |
427 | 427 | ||
428 | xfs_extlen_t xfs_get_extsz_hint(struct xfs_inode *ip); | 428 | xfs_extlen_t xfs_get_extsz_hint(struct xfs_inode *ip); |
429 | xfs_extlen_t xfs_get_cowextsz_hint(struct xfs_inode *ip); | ||
429 | 430 | ||
430 | int xfs_dir_ialloc(struct xfs_trans **, struct xfs_inode *, umode_t, | 431 | int 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 | ||
984 | STATIC void | 987 | STATIC 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 | */ | ||
1238 | static int | ||
1239 | xfs_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 | |||
1222 | static int | 1275 | static int |
1223 | xfs_ioctl_setattr_check_projid( | 1276 | xfs_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 | ||
74 | static xfs_extlen_t | 74 | xfs_extlen_t |
75 | xfs_eof_alignment( | 75 | xfs_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 | ||
32 | void xfs_bmbt_to_iomap(struct xfs_inode *, struct iomap *, | 32 | void xfs_bmbt_to_iomap(struct xfs_inode *, struct iomap *, |
33 | struct xfs_bmbt_irec *); | 33 | struct xfs_bmbt_irec *); |
34 | xfs_extlen_t xfs_eof_alignment(struct xfs_inode *ip, xfs_extlen_t extsize); | ||
34 | 35 | ||
35 | extern struct iomap_ops xfs_iomap_ops; | 36 | extern struct iomap_ops xfs_iomap_ops; |
36 | extern struct iomap_ops xfs_xattr_iomap_ops; | 37 | extern 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 | |||
280 | retry: | 285 | retry: |
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 | */ |
932 | STATIC int | 937 | STATIC int |
933 | xfs_reflink_update_dest( | 938 | xfs_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 | ||