summaryrefslogtreecommitdiffstats
path: root/fs/xfs/xfs_ioctl.c
diff options
context:
space:
mode:
authorDarrick J. Wong <darrick.wong@oracle.com>2016-10-03 18:11:43 +0200
committerDarrick J. Wong <darrick.wong@oracle.com>2016-10-06 01:26:26 +0200
commitf7ca35227253dc8244fd908140b06010e67a31e5 (patch)
tree2e2731cafe2a8e8f6679d2a58eef031c1a84ffba /fs/xfs/xfs_ioctl.c
parentxfs: unshare a range of blocks via fallocate (diff)
downloadlinux-f7ca35227253dc8244fd908140b06010e67a31e5.tar.xz
linux-f7ca35227253dc8244fd908140b06010e67a31e5.zip
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_ioctl.c')
-rw-r--r--fs/xfs/xfs_ioctl.c67
1 files changed, 65 insertions, 2 deletions
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(
xfs_ilock(ip, XFS_ILOCK_SHARED);
fa.fsx_xflags = xfs_ip2xflags(ip);
fa.fsx_extsize = ip->i_d.di_extsize << ip->i_mount->m_sb.sb_blocklog;
+ fa.fsx_cowextsize = ip->i_d.di_cowextsize <<
+ ip->i_mount->m_sb.sb_blocklog;
fa.fsx_projid = xfs_get_projid(ip);
if (attr) {
@@ -973,12 +975,13 @@ xfs_set_diflags(
if (ip->i_d.di_version < 3)
return;
- di_flags2 = 0;
+ di_flags2 = (ip->i_d.di_flags2 & XFS_DIFLAG2_REFLINK);
if (xflags & FS_XFLAG_DAX)
di_flags2 |= XFS_DIFLAG2_DAX;
+ if (xflags & FS_XFLAG_COWEXTSIZE)
+ di_flags2 |= XFS_DIFLAG2_COWEXTSIZE;
ip->i_d.di_flags2 = di_flags2;
-
}
STATIC void
@@ -1219,6 +1222,56 @@ xfs_ioctl_setattr_check_extsize(
return 0;
}
+/*
+ * CoW extent size hint validation rules are:
+ *
+ * 1. CoW extent size hint can only be set if reflink is enabled on the fs.
+ * The inode does not have to have any shared blocks, but it must be a v3.
+ * 2. FS_XFLAG_COWEXTSIZE is only valid for directories and regular files;
+ * for a directory, the hint is propagated to new files.
+ * 3. Can be changed on files & directories at any time.
+ * 4. CoW extsize hint of 0 turns off hints, clears inode flags.
+ * 5. Extent size must be a multiple of the appropriate block size.
+ * 6. The extent size hint must be limited to half the AG size to avoid
+ * alignment extending the extent beyond the limits of the AG.
+ */
+static int
+xfs_ioctl_setattr_check_cowextsize(
+ struct xfs_inode *ip,
+ struct fsxattr *fa)
+{
+ struct xfs_mount *mp = ip->i_mount;
+
+ if (!(fa->fsx_xflags & FS_XFLAG_COWEXTSIZE))
+ return 0;
+
+ if (!xfs_sb_version_hasreflink(&ip->i_mount->m_sb) ||
+ ip->i_d.di_version != 3)
+ return -EINVAL;
+
+ if (!S_ISREG(VFS_I(ip)->i_mode) && !S_ISDIR(VFS_I(ip)->i_mode))
+ return -EINVAL;
+
+ if (fa->fsx_cowextsize != 0) {
+ xfs_extlen_t size;
+ xfs_fsblock_t cowextsize_fsb;
+
+ cowextsize_fsb = XFS_B_TO_FSB(mp, fa->fsx_cowextsize);
+ if (cowextsize_fsb > MAXEXTLEN)
+ return -EINVAL;
+
+ size = mp->m_sb.sb_blocksize;
+ if (cowextsize_fsb > mp->m_sb.sb_agblocks / 2)
+ return -EINVAL;
+
+ if (fa->fsx_cowextsize % size)
+ return -EINVAL;
+ } else
+ fa->fsx_xflags &= ~FS_XFLAG_COWEXTSIZE;
+
+ return 0;
+}
+
static int
xfs_ioctl_setattr_check_projid(
struct xfs_inode *ip,
@@ -1311,6 +1364,10 @@ xfs_ioctl_setattr(
if (code)
goto error_trans_cancel;
+ code = xfs_ioctl_setattr_check_cowextsize(ip, fa);
+ if (code)
+ goto error_trans_cancel;
+
code = xfs_ioctl_setattr_xflags(tp, ip, fa);
if (code)
goto error_trans_cancel;
@@ -1346,6 +1403,12 @@ xfs_ioctl_setattr(
ip->i_d.di_extsize = fa->fsx_extsize >> mp->m_sb.sb_blocklog;
else
ip->i_d.di_extsize = 0;
+ if (ip->i_d.di_version == 3 &&
+ (ip->i_d.di_flags2 & XFS_DIFLAG2_COWEXTSIZE))
+ ip->i_d.di_cowextsize = fa->fsx_cowextsize >>
+ mp->m_sb.sb_blocklog;
+ else
+ ip->i_d.di_cowextsize = 0;
code = xfs_trans_commit(tp);