]> Git Repo - linux.git/commitdiff
Merge branch 'xfs-dax-fixes-4.6' into for-next
authorDave Chinner <[email protected]>
Sun, 6 Mar 2016 22:34:31 +0000 (09:34 +1100)
committerDave Chinner <[email protected]>
Sun, 6 Mar 2016 22:34:31 +0000 (09:34 +1100)
1  2 
fs/xfs/xfs_ioctl.c
fs/xfs/xfs_iops.c

diff --combined fs/xfs/xfs_ioctl.c
index 81d6d62188037ba6f5f6ccc3dcc68fdeab2ec8c3,388b5b5b67c9e01336f4dadd79f09b350c3ef288..bcb6c19ce3ea4fea69c536343c07ea222de3155d
@@@ -114,7 -114,7 +114,7 @@@ xfs_find_handle
                handle.ha_fid.fid_len = sizeof(xfs_fid_t) -
                                        sizeof(handle.ha_fid.fid_len);
                handle.ha_fid.fid_pad = 0;
 -              handle.ha_fid.fid_gen = ip->i_d.di_gen;
 +              handle.ha_fid.fid_gen = inode->i_generation;
                handle.ha_fid.fid_ino = ip->i_ino;
  
                hsize = XFS_HSIZE(handle);
@@@ -963,7 -963,7 +963,7 @@@ xfs_set_diflags
                di_flags |= XFS_DIFLAG_NODEFRAG;
        if (xflags & FS_XFLAG_FILESTREAM)
                di_flags |= XFS_DIFLAG_FILESTREAM;
 -      if (S_ISDIR(ip->i_d.di_mode)) {
 +      if (S_ISDIR(VFS_I(ip)->i_mode)) {
                if (xflags & FS_XFLAG_RTINHERIT)
                        di_flags |= XFS_DIFLAG_RTINHERIT;
                if (xflags & FS_XFLAG_NOSYMLINKS)
                        di_flags |= XFS_DIFLAG_EXTSZINHERIT;
                if (xflags & FS_XFLAG_PROJINHERIT)
                        di_flags |= XFS_DIFLAG_PROJINHERIT;
 -      } else if (S_ISREG(ip->i_d.di_mode)) {
 +      } else if (S_ISREG(VFS_I(ip)->i_mode)) {
                if (xflags & FS_XFLAG_REALTIME)
                        di_flags |= XFS_DIFLAG_REALTIME;
                if (xflags & FS_XFLAG_EXTSIZE)
@@@ -1059,24 -1059,87 +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);
                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:
  
  out_cancel:
        xfs_trans_cancel(tp);
+ out_unlock:
+       if (join_flags)
+               xfs_iunlock(ip, join_flags);
        return ERR_PTR(error);
  }
  
@@@ -1128,14 -1195,14 +1195,14 @@@ xfs_ioctl_setattr_check_extsize
  {
        struct xfs_mount        *mp = ip->i_mount;
  
 -      if ((fa->fsx_xflags & FS_XFLAG_EXTSIZE) && !S_ISREG(ip->i_d.di_mode))
 +      if ((fa->fsx_xflags & FS_XFLAG_EXTSIZE) && !S_ISREG(VFS_I(ip)->i_mode))
                return -EINVAL;
  
        if ((fa->fsx_xflags & FS_XFLAG_EXTSZINHERIT) &&
 -          !S_ISDIR(ip->i_d.di_mode))
 +          !S_ISDIR(VFS_I(ip)->i_mode))
                return -EINVAL;
  
 -      if (S_ISREG(ip->i_d.di_mode) && ip->i_d.di_nextents &&
 +      if (S_ISREG(VFS_I(ip)->i_mode) && ip->i_d.di_nextents &&
            ((ip->i_d.di_extsize << mp->m_sb.sb_blocklog) != fa->fsx_extsize))
                return -EINVAL;
  
@@@ -1202,6 -1269,7 +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);
  
                        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;
         * successful return from chown()
         */
  
 -      if ((ip->i_d.di_mode & (S_ISUID|S_ISGID)) &&
 +      if ((VFS_I(ip)->i_mode & (S_ISUID|S_ISGID)) &&
            !capable_wrt_inode_uidgid(VFS_I(ip), CAP_FSETID))
 -              ip->i_d.di_mode &= ~(S_ISUID|S_ISGID);
 +              VFS_I(ip)->i_mode &= ~(S_ISUID|S_ISGID);
  
        /* Change the ownerships and register project quota modifications */
        if (xfs_get_projid(ip) != fa->fsx_projid) {
@@@ -1341,6 -1420,7 +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)))
        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;
diff --combined fs/xfs/xfs_iops.c
index 0d38b1d2c420fcc75ffa0cbc28fe3f0db8de17b4,f7386dc10a205a79386472d62b0f549e172c6ffe..fb7dc61f4a29d7cee3d4683c675c4551e7669e52
@@@ -459,8 -459,8 +459,8 @@@ xfs_vn_getattr
  
        stat->size = XFS_ISIZE(ip);
        stat->dev = inode->i_sb->s_dev;
 -      stat->mode = ip->i_d.di_mode;
 -      stat->nlink = ip->i_d.di_nlink;
 +      stat->mode = inode->i_mode;
 +      stat->nlink = inode->i_nlink;
        stat->uid = inode->i_uid;
        stat->gid = inode->i_gid;
        stat->ino = ip->i_ino;
@@@ -506,6 -506,9 +506,6 @@@ xfs_setattr_mode
  
        ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
  
 -      ip->i_d.di_mode &= S_IFMT;
 -      ip->i_d.di_mode |= mode & ~S_IFMT;
 -
        inode->i_mode &= S_IFMT;
        inode->i_mode |= mode & ~S_IFMT;
  }
@@@ -519,12 -522,21 +519,12 @@@ xfs_setattr_time
  
        ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
  
 -      if (iattr->ia_valid & ATTR_ATIME) {
 +      if (iattr->ia_valid & ATTR_ATIME)
                inode->i_atime = iattr->ia_atime;
 -              ip->i_d.di_atime.t_sec = iattr->ia_atime.tv_sec;
 -              ip->i_d.di_atime.t_nsec = iattr->ia_atime.tv_nsec;
 -      }
 -      if (iattr->ia_valid & ATTR_CTIME) {
 +      if (iattr->ia_valid & ATTR_CTIME)
                inode->i_ctime = iattr->ia_ctime;
 -              ip->i_d.di_ctime.t_sec = iattr->ia_ctime.tv_sec;
 -              ip->i_d.di_ctime.t_nsec = iattr->ia_ctime.tv_nsec;
 -      }
 -      if (iattr->ia_valid & ATTR_MTIME) {
 +      if (iattr->ia_valid & ATTR_MTIME)
                inode->i_mtime = iattr->ia_mtime;
 -              ip->i_d.di_mtime.t_sec = iattr->ia_mtime.tv_sec;
 -              ip->i_d.di_mtime.t_nsec = iattr->ia_mtime.tv_nsec;
 -      }
  }
  
  int
@@@ -649,9 -661,9 +649,9 @@@ xfs_setattr_nonsize
                 * The set-user-ID and set-group-ID bits of a file will be
                 * cleared upon successful return from chown()
                 */
 -              if ((ip->i_d.di_mode & (S_ISUID|S_ISGID)) &&
 +              if ((inode->i_mode & (S_ISUID|S_ISGID)) &&
                    !capable(CAP_FSETID))
 -                      ip->i_d.di_mode &= ~(S_ISUID|S_ISGID);
 +                      inode->i_mode &= ~(S_ISUID|S_ISGID);
  
                /*
                 * Change the ownerships and register quota modifications
@@@ -761,7 -773,7 +761,7 @@@ xfs_setattr_size
  
        ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL));
        ASSERT(xfs_isilocked(ip, XFS_MMAPLOCK_EXCL));
 -      ASSERT(S_ISREG(ip->i_d.di_mode));
 +      ASSERT(S_ISREG(inode->i_mode));
        ASSERT((iattr->ia_valid & (ATTR_UID|ATTR_GID|ATTR_ATIME|ATTR_ATIME_SET|
                ATTR_MTIME_SET|ATTR_KILL_PRIV|ATTR_TIMES_SET)) == 0);
  
@@@ -979,13 -991,21 +979,13 @@@ xfs_vn_update_time
        }
  
        xfs_ilock(ip, XFS_ILOCK_EXCL);
 -      if (flags & S_CTIME) {
 +      if (flags & S_CTIME)
                inode->i_ctime = *now;
 -              ip->i_d.di_ctime.t_sec = (__int32_t)now->tv_sec;
 -              ip->i_d.di_ctime.t_nsec = (__int32_t)now->tv_nsec;
 -      }
 -      if (flags & S_MTIME) {
 +      if (flags & S_MTIME)
                inode->i_mtime = *now;
 -              ip->i_d.di_mtime.t_sec = (__int32_t)now->tv_sec;
 -              ip->i_d.di_mtime.t_nsec = (__int32_t)now->tv_nsec;
 -      }
 -      if (flags & S_ATIME) {
 +      if (flags & S_ATIME)
                inode->i_atime = *now;
 -              ip->i_d.di_atime.t_sec = (__int32_t)now->tv_sec;
 -              ip->i_d.di_atime.t_nsec = (__int32_t)now->tv_nsec;
 -      }
 +
        xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
        xfs_trans_log_inode(tp, ip, XFS_ILOG_TIMESTAMP);
        return xfs_trans_commit(tp);
@@@ -1185,8 -1205,10 +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;
  }
  
@@@ -1212,6 -1234,8 +1214,6 @@@ xfs_setup_inode
        /* make the inode look hashed for the writeback code */
        hlist_add_fake(&inode->i_hash);
  
 -      inode->i_mode   = ip->i_d.di_mode;
 -      set_nlink(inode, ip->i_d.di_nlink);
        inode->i_uid    = xfs_uid_to_kuid(ip->i_d.di_uid);
        inode->i_gid    = xfs_gid_to_kgid(ip->i_d.di_gid);
  
                break;
        }
  
 -      inode->i_generation = ip->i_d.di_gen;
        i_size_write(inode, ip->i_d.di_size);
 -      inode->i_atime.tv_sec   = ip->i_d.di_atime.t_sec;
 -      inode->i_atime.tv_nsec  = ip->i_d.di_atime.t_nsec;
 -      inode->i_mtime.tv_sec   = ip->i_d.di_mtime.t_sec;
 -      inode->i_mtime.tv_nsec  = ip->i_d.di_mtime.t_nsec;
 -      inode->i_ctime.tv_sec   = ip->i_d.di_ctime.t_sec;
 -      inode->i_ctime.tv_nsec  = ip->i_d.di_ctime.t_nsec;
        xfs_diflags_to_iflags(inode, ip);
  
        ip->d_ops = ip->i_mount->m_nondir_inode_ops;
This page took 0.08492 seconds and 4 git commands to generate.