Revert "Merge remote-tracking branch 'linux-2.6.32.y/master' into develop"
[firefly-linux-kernel-4.4.55.git] / fs / xfs / xfs_vnodeops.c
index b572f7e840e0b82a372225581d87c85c104dfec6..38a63244e75dda8d89fb1e17b0009ce1c491998b 100644 (file)
@@ -69,7 +69,6 @@ xfs_setattr(
        uint                    commit_flags=0;
        uid_t                   uid=0, iuid=0;
        gid_t                   gid=0, igid=0;
-       int                     timeflags = 0;
        struct xfs_dquot        *udqp, *gdqp, *olddquot1, *olddquot2;
        int                     need_iolock = 1;
 
@@ -134,16 +133,13 @@ xfs_setattr(
        if (flags & XFS_ATTR_NOLOCK)
                need_iolock = 0;
        if (!(mask & ATTR_SIZE)) {
-               if ((mask != (ATTR_CTIME|ATTR_ATIME|ATTR_MTIME)) ||
-                   (mp->m_flags & XFS_MOUNT_WSYNC)) {
-                       tp = xfs_trans_alloc(mp, XFS_TRANS_SETATTR_NOT_SIZE);
-                       commit_flags = 0;
-                       if ((code = xfs_trans_reserve(tp, 0,
-                                                    XFS_ICHANGE_LOG_RES(mp), 0,
-                                                    0, 0))) {
-                               lock_flags = 0;
-                               goto error_return;
-                       }
+               tp = xfs_trans_alloc(mp, XFS_TRANS_SETATTR_NOT_SIZE);
+               commit_flags = 0;
+               code = xfs_trans_reserve(tp, 0, XFS_ICHANGE_LOG_RES(mp),
+                                        0, 0, 0);
+               if (code) {
+                       lock_flags = 0;
+                       goto error_return;
                }
        } else {
                if (DM_EVENT_ENABLED(ip, DM_EVENT_TRUNCATE) &&
@@ -294,15 +290,23 @@ xfs_setattr(
                 * or we are explicitly asked to change it. This handles
                 * the semantic difference between truncate() and ftruncate()
                 * as implemented in the VFS.
+                *
+                * The regular truncate() case without ATTR_CTIME and ATTR_MTIME
+                * is a special case where we need to update the times despite
+                * not having these flags set.  For all other operations the
+                * VFS set these flags explicitly if it wants a timestamp
+                * update.
                 */
-               if (iattr->ia_size != ip->i_size || (mask & ATTR_CTIME))
-                       timeflags |= XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG;
+               if (iattr->ia_size != ip->i_size &&
+                   (!(mask & (ATTR_CTIME | ATTR_MTIME)))) {
+                       iattr->ia_ctime = iattr->ia_mtime =
+                               current_fs_time(inode->i_sb);
+                       mask |= ATTR_CTIME | ATTR_MTIME;
+               }
 
                if (iattr->ia_size > ip->i_size) {
                        ip->i_d.di_size = iattr->ia_size;
                        ip->i_size = iattr->ia_size;
-                       if (!(flags & XFS_ATTR_DMI))
-                               xfs_ichgtime(ip, XFS_ICHGTIME_CHG);
                        xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
                } else if (iattr->ia_size <= ip->i_size ||
                           (iattr->ia_size == 0 && ip->i_d.di_nextents)) {
@@ -373,9 +377,6 @@ xfs_setattr(
                        ip->i_d.di_gid = gid;
                        inode->i_gid = gid;
                }
-
-               xfs_trans_log_inode (tp, ip, XFS_ILOG_CORE);
-               timeflags |= XFS_ICHGTIME_CHG;
        }
 
        /*
@@ -392,51 +393,37 @@ xfs_setattr(
 
                inode->i_mode &= S_IFMT;
                inode->i_mode |= mode & ~S_IFMT;
-
-               xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
-               timeflags |= XFS_ICHGTIME_CHG;
        }
 
        /*
         * Change file access or modified times.
         */
-       if (mask & (ATTR_ATIME|ATTR_MTIME)) {
-               if (mask & 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;
-                       ip->i_update_core = 1;
-               }
-               if (mask & 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;
-                       timeflags &= ~XFS_ICHGTIME_MOD;
-                       timeflags |= XFS_ICHGTIME_CHG;
-               }
-               if (tp && (mask & (ATTR_MTIME_SET|ATTR_ATIME_SET)))
-                       xfs_trans_log_inode (tp, ip, XFS_ILOG_CORE);
+       if (mask & 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;
+               ip->i_update_core = 1;
        }
-
-       /*
-        * Change file inode change time only if ATTR_CTIME set
-        * AND we have been called by a DMI function.
-        */
-
-       if ((flags & XFS_ATTR_DMI) && (mask & ATTR_CTIME)) {
+       if (mask & 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;
                ip->i_update_core = 1;
-               timeflags &= ~XFS_ICHGTIME_CHG;
+       }
+       if (mask & 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;
+               ip->i_update_core = 1;
        }
 
        /*
-        * Send out timestamp changes that need to be set to the
-        * current time.  Not done when called by a DMI function.
+        * And finally, log the inode core if any attribute in it
+        * has been changed.
         */
-       if (timeflags && !(flags & XFS_ATTR_DMI))
-               xfs_ichgtime(ip, timeflags);
+       if (mask & (ATTR_UID|ATTR_GID|ATTR_MODE|
+                   ATTR_ATIME|ATTR_CTIME|ATTR_MTIME))
+               xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
 
        XFS_STATS_INC(xs_ig_attrchg);
 
@@ -451,12 +438,10 @@ xfs_setattr(
         * mix so this probably isn't worth the trouble to optimize.
         */
        code = 0;
-       if (tp) {
-               if (mp->m_flags & XFS_MOUNT_WSYNC)
-                       xfs_trans_set_sync(tp);
+       if (mp->m_flags & XFS_MOUNT_WSYNC)
+               xfs_trans_set_sync(tp);
 
-               code = xfs_trans_commit(tp, commit_flags);
-       }
+       code = xfs_trans_commit(tp, commit_flags);
 
        xfs_iunlock(ip, lock_flags);
 
@@ -612,7 +597,7 @@ xfs_fsync(
 {
        xfs_trans_t     *tp;
        int             error = 0;
-       int             log_flushed = 0, changed = 1;
+       int             log_flushed = 0;
 
        xfs_itrace_entry(ip);
 
@@ -642,19 +627,11 @@ xfs_fsync(
                 * disk yet, the inode will be still be pinned.  If it is,
                 * force the log.
                 */
-
                xfs_iunlock(ip, XFS_ILOCK_SHARED);
-
                if (xfs_ipincount(ip)) {
                        error = _xfs_log_force(ip->i_mount, (xfs_lsn_t)0,
                                      XFS_LOG_FORCE | XFS_LOG_SYNC,
                                      &log_flushed);
-               } else {
-                       /*
-                        * If the inode is not pinned and nothing has changed
-                        * we don't need to flush the cache.
-                        */
-                       changed = 0;
                }
        } else  {
                /*
@@ -689,7 +666,7 @@ xfs_fsync(
                xfs_iunlock(ip, XFS_ILOCK_EXCL);
        }
 
-       if ((ip->i_mount->m_flags & XFS_MOUNT_BARRIER) && changed) {
+       if (ip->i_mount->m_flags & XFS_MOUNT_BARRIER) {
                /*
                 * If the log write didn't issue an ordered tag we need
                 * to flush the disk cache for the data device now.
@@ -708,6 +685,11 @@ xfs_fsync(
        return error;
 }
 
+/*
+ * Flags for xfs_free_eofblocks
+ */
+#define XFS_FREE_EOF_TRYLOCK   (1<<0)
+
 /*
  * This is called by xfs_inactive to free any blocks beyond eof
  * when the link count isn't zero and by xfs_dm_punch_hole() when
@@ -726,7 +708,6 @@ xfs_free_eofblocks(
        xfs_filblks_t   map_len;
        int             nimaps;
        xfs_bmbt_irec_t imap;
-       int             use_iolock = (flags & XFS_FREE_EOF_LOCK);
 
        /*
         * Figure out if there are any blocks beyond the end
@@ -768,14 +749,19 @@ xfs_free_eofblocks(
                 * cache and we can't
                 * do that within a transaction.
                 */
-               if (use_iolock)
+               if (flags & XFS_FREE_EOF_TRYLOCK) {
+                       if (!xfs_ilock_nowait(ip, XFS_IOLOCK_EXCL)) {
+                               xfs_trans_cancel(tp, 0);
+                               return 0;
+                       }
+               } else {
                        xfs_ilock(ip, XFS_IOLOCK_EXCL);
+               }
                error = xfs_itruncate_start(ip, XFS_ITRUNC_DEFINITE,
                                    ip->i_size);
                if (error) {
                        xfs_trans_cancel(tp, 0);
-                       if (use_iolock)
-                               xfs_iunlock(ip, XFS_IOLOCK_EXCL);
+                       xfs_iunlock(ip, XFS_IOLOCK_EXCL);
                        return error;
                }
 
@@ -812,8 +798,7 @@ xfs_free_eofblocks(
                        error = xfs_trans_commit(tp,
                                                XFS_TRANS_RELEASE_LOG_RES);
                }
-               xfs_iunlock(ip, (use_iolock ? (XFS_IOLOCK_EXCL|XFS_ILOCK_EXCL)
-                                           : XFS_ILOCK_EXCL));
+               xfs_iunlock(ip, XFS_IOLOCK_EXCL|XFS_ILOCK_EXCL);
        }
        return error;
 }
@@ -1113,7 +1098,17 @@ xfs_release(
                     (ip->i_df.if_flags & XFS_IFEXTENTS))  &&
                    (!(ip->i_d.di_flags &
                                (XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND)))) {
-                       error = xfs_free_eofblocks(mp, ip, XFS_FREE_EOF_LOCK);
+
+                       /*
+                        * If we can't get the iolock just skip truncating
+                        * the blocks past EOF because we could deadlock
+                        * with the mmap_sem otherwise.  We'll get another
+                        * chance to drop them once the last reference to
+                        * the inode is dropped, so we'll never leak blocks
+                        * permanently.
+                        */
+                       error = xfs_free_eofblocks(mp, ip,
+                                                  XFS_FREE_EOF_TRYLOCK);
                        if (error)
                                return error;
                }
@@ -1184,7 +1179,7 @@ xfs_inactive(
                     (!(ip->i_d.di_flags &
                                (XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND)) ||
                      (ip->i_delayed_blks != 0)))) {
-                       error = xfs_free_eofblocks(mp, ip, XFS_FREE_EOF_LOCK);
+                       error = xfs_free_eofblocks(mp, ip, 0);
                        if (error)
                                return VN_INACTIVE_CACHE;
                }
@@ -2456,46 +2451,6 @@ xfs_set_dmattrs(
        return error;
 }
 
-int
-xfs_reclaim(
-       xfs_inode_t     *ip)
-{
-
-       xfs_itrace_entry(ip);
-
-       ASSERT(!VN_MAPPED(VFS_I(ip)));
-
-       /* bad inode, get out here ASAP */
-       if (is_bad_inode(VFS_I(ip))) {
-               xfs_ireclaim(ip);
-               return 0;
-       }
-
-       xfs_ioend_wait(ip);
-
-       ASSERT(XFS_FORCED_SHUTDOWN(ip->i_mount) || ip->i_delayed_blks == 0);
-
-       /*
-        * If we have nothing to flush with this inode then complete the
-        * teardown now, otherwise break the link between the xfs inode and the
-        * linux inode and clean up the xfs inode later. This avoids flushing
-        * the inode to disk during the delete operation itself.
-        *
-        * When breaking the link, we need to set the XFS_IRECLAIMABLE flag
-        * first to ensure that xfs_iunpin() will never see an xfs inode
-        * that has a linux inode being reclaimed. Synchronisation is provided
-        * by the i_flags_lock.
-        */
-       if (!ip->i_update_core && (ip->i_itemp == NULL)) {
-               xfs_ilock(ip, XFS_ILOCK_EXCL);
-               xfs_iflock(ip);
-               xfs_iflags_set(ip, XFS_IRECLAIMABLE);
-               return xfs_reclaim_inode(ip, 1, XFS_IFLUSH_DELWRI_ELSE_SYNC);
-       }
-       xfs_inode_set_reclaim_tag(ip);
-       return 0;
-}
-
 /*
  * xfs_alloc_file_space()
  *      This routine allocates disk space for the given file.