Merge branch 'xfs-dax-fixes-4.6' into for-next

This commit is contained in:
Dave Chinner 2016-03-07 09:34:31 +11:00
commit 85a9f38d38
2 changed files with 102 additions and 9 deletions

View file

@ -1059,24 +1059,87 @@ xfs_ioctl_setattr_xflags(
return 0;
}
/*
* If we are changing DAX flags, we have to ensure the file is clean and any
* cached objects in the address space are invalidated and removed. This
* requires us to lock out other IO and page faults similar to a truncate
* operation. The locks need to be held until the transaction has been committed
* so that the cache invalidation is atomic with respect to the DAX flag
* manipulation.
*/
static int
xfs_ioctl_setattr_dax_invalidate(
struct xfs_inode *ip,
struct fsxattr *fa,
int *join_flags)
{
struct inode *inode = VFS_I(ip);
int error;
*join_flags = 0;
/*
* It is only valid to set the DAX flag on regular files and
* directories on filesystems where the block size is equal to the page
* size. On directories it serves as an inherit hint.
*/
if (fa->fsx_xflags & FS_XFLAG_DAX) {
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
return -EINVAL;
if (ip->i_mount->m_sb.sb_blocksize != PAGE_SIZE)
return -EINVAL;
}
/* If the DAX state is not changing, we have nothing to do here. */
if ((fa->fsx_xflags & FS_XFLAG_DAX) && IS_DAX(inode))
return 0;
if (!(fa->fsx_xflags & FS_XFLAG_DAX) && !IS_DAX(inode))
return 0;
/* lock, flush and invalidate mapping in preparation for flag change */
xfs_ilock(ip, XFS_MMAPLOCK_EXCL | XFS_IOLOCK_EXCL);
error = filemap_write_and_wait(inode->i_mapping);
if (error)
goto out_unlock;
error = invalidate_inode_pages2(inode->i_mapping);
if (error)
goto out_unlock;
*join_flags = XFS_MMAPLOCK_EXCL | XFS_IOLOCK_EXCL;
return 0;
out_unlock:
xfs_iunlock(ip, XFS_MMAPLOCK_EXCL | XFS_IOLOCK_EXCL);
return error;
}
/*
* Set up the transaction structure for the setattr operation, checking that we
* have permission to do so. On success, return a clean transaction and the
* inode locked exclusively ready for further operation specific checks. On
* failure, return an error without modifying or locking the inode.
*
* The inode might already be IO locked on call. If this is the case, it is
* indicated in @join_flags and we take full responsibility for ensuring they
* are unlocked from now on. Hence if we have an error here, we still have to
* unlock them. Otherwise, once they are joined to the transaction, they will
* be unlocked on commit/cancel.
*/
static struct xfs_trans *
xfs_ioctl_setattr_get_trans(
struct xfs_inode *ip)
struct xfs_inode *ip,
int join_flags)
{
struct xfs_mount *mp = ip->i_mount;
struct xfs_trans *tp;
int error;
int error = -EROFS;
if (mp->m_flags & XFS_MOUNT_RDONLY)
return ERR_PTR(-EROFS);
goto out_unlock;
error = -EIO;
if (XFS_FORCED_SHUTDOWN(mp))
return ERR_PTR(-EIO);
goto out_unlock;
tp = xfs_trans_alloc(mp, XFS_TRANS_SETATTR_NOT_SIZE);
error = xfs_trans_reserve(tp, &M_RES(mp)->tr_ichange, 0, 0);
@ -1084,7 +1147,8 @@ xfs_ioctl_setattr_get_trans(
goto out_cancel;
xfs_ilock(ip, XFS_ILOCK_EXCL);
xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL | join_flags);
join_flags = 0;
/*
* CAP_FOWNER overrides the following restrictions:
@ -1104,6 +1168,9 @@ xfs_ioctl_setattr_get_trans(
out_cancel:
xfs_trans_cancel(tp);
out_unlock:
if (join_flags)
xfs_iunlock(ip, join_flags);
return ERR_PTR(error);
}
@ -1202,6 +1269,7 @@ xfs_ioctl_setattr(
struct xfs_dquot *pdqp = NULL;
struct xfs_dquot *olddquot = NULL;
int code;
int join_flags = 0;
trace_xfs_ioctl_setattr(ip);
@ -1225,7 +1293,18 @@ xfs_ioctl_setattr(
return code;
}
tp = xfs_ioctl_setattr_get_trans(ip);
/*
* Changing DAX config may require inode locking for mapping
* invalidation. These need to be held all the way to transaction commit
* or cancel time, so need to be passed through to
* xfs_ioctl_setattr_get_trans() so it can apply them to the join call
* appropriately.
*/
code = xfs_ioctl_setattr_dax_invalidate(ip, fa, &join_flags);
if (code)
goto error_free_dquots;
tp = xfs_ioctl_setattr_get_trans(ip, join_flags);
if (IS_ERR(tp)) {
code = PTR_ERR(tp);
goto error_free_dquots;
@ -1341,6 +1420,7 @@ xfs_ioc_setxflags(
struct xfs_trans *tp;
struct fsxattr fa;
unsigned int flags;
int join_flags = 0;
int error;
if (copy_from_user(&flags, arg, sizeof(flags)))
@ -1357,7 +1437,18 @@ xfs_ioc_setxflags(
if (error)
return error;
tp = xfs_ioctl_setattr_get_trans(ip);
/*
* Changing DAX config may require inode locking for mapping
* invalidation. These need to be held all the way to transaction commit
* or cancel time, so need to be passed through to
* xfs_ioctl_setattr_get_trans() so it can apply them to the join call
* appropriately.
*/
error = xfs_ioctl_setattr_dax_invalidate(ip, &fa, &join_flags);
if (error)
goto out_drop_write;
tp = xfs_ioctl_setattr_get_trans(ip, join_flags);
if (IS_ERR(tp)) {
error = PTR_ERR(tp);
goto out_drop_write;

View file

@ -1185,8 +1185,10 @@ xfs_diflags_to_iflags(
inode->i_flags |= S_SYNC;
if (flags & XFS_DIFLAG_NOATIME)
inode->i_flags |= S_NOATIME;
if (ip->i_mount->m_flags & XFS_MOUNT_DAX ||
ip->i_d.di_flags2 & XFS_DIFLAG2_DAX)
if (S_ISREG(inode->i_mode) &&
ip->i_mount->m_sb.sb_blocksize == PAGE_SIZE &&
(ip->i_mount->m_flags & XFS_MOUNT_DAX ||
ip->i_d.di_flags2 & XFS_DIFLAG2_DAX))
inode->i_flags |= S_DAX;
}