ext4: atomically set inode->i_flags in ext4_set_inode_flags()
[firefly-linux-kernel-4.4.55.git] / fs / ext4 / inode.c
index d6382b89ecbde3077720ebc6a9bb56254883e2d7..f9e11df768d595d1a01d1e60e72d630617d25656 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/slab.h>
 #include <linux/ratelimit.h>
 #include <linux/aio.h>
+#include <linux/bitops.h>
 
 #include "ext4_jbd2.h"
 #include "xattr.h"
@@ -1118,10 +1119,13 @@ static int ext4_write_end(struct file *file,
                }
        }
 
-       if (ext4_has_inline_data(inode))
-               copied = ext4_write_inline_data_end(inode, pos, len,
-                                                   copied, page);
-       else
+       if (ext4_has_inline_data(inode)) {
+               ret = ext4_write_inline_data_end(inode, pos, len,
+                                                copied, page);
+               if (ret < 0)
+                       goto errout;
+               copied = ret;
+       } else
                copied = block_write_end(file, mapping, pos,
                                         len, copied, page, fsdata);
 
@@ -1260,7 +1264,6 @@ static int ext4_journalled_write_end(struct file *file,
  */
 static int ext4_da_reserve_metadata(struct inode *inode, ext4_lblk_t lblock)
 {
-       int retries = 0;
        struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
        struct ext4_inode_info *ei = EXT4_I(inode);
        unsigned int md_needed;
@@ -1272,7 +1275,6 @@ static int ext4_da_reserve_metadata(struct inode *inode, ext4_lblk_t lblock)
         * in order to allocate nrblocks
         * worse case is one extent per block
         */
-repeat:
        spin_lock(&ei->i_block_reservation_lock);
        /*
         * ext4_calc_metadata_amount() has side effects, which we have
@@ -1292,10 +1294,6 @@ repeat:
                ei->i_da_metadata_calc_len = save_len;
                ei->i_da_metadata_calc_last_lblock = save_last_lblock;
                spin_unlock(&ei->i_block_reservation_lock);
-               if (ext4_should_retry_alloc(inode->i_sb, &retries)) {
-                       cond_resched();
-                       goto repeat;
-               }
                return -ENOSPC;
        }
        ei->i_reserved_meta_blocks += md_needed;
@@ -1309,7 +1307,6 @@ repeat:
  */
 static int ext4_da_reserve_space(struct inode *inode, ext4_lblk_t lblock)
 {
-       int retries = 0;
        struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
        struct ext4_inode_info *ei = EXT4_I(inode);
        unsigned int md_needed;
@@ -1331,7 +1328,6 @@ static int ext4_da_reserve_space(struct inode *inode, ext4_lblk_t lblock)
         * in order to allocate nrblocks
         * worse case is one extent per block
         */
-repeat:
        spin_lock(&ei->i_block_reservation_lock);
        /*
         * ext4_calc_metadata_amount() has side effects, which we have
@@ -1351,10 +1347,6 @@ repeat:
                ei->i_da_metadata_calc_len = save_len;
                ei->i_da_metadata_calc_last_lblock = save_last_lblock;
                spin_unlock(&ei->i_block_reservation_lock);
-               if (ext4_should_retry_alloc(inode->i_sb, &retries)) {
-                       cond_resched();
-                       goto repeat;
-               }
                dquot_release_reservation_block(inode, EXT4_C2B(sbi, 1));
                return -ENOSPC;
        }
@@ -4053,18 +4045,20 @@ int ext4_get_inode_loc(struct inode *inode, struct ext4_iloc *iloc)
 void ext4_set_inode_flags(struct inode *inode)
 {
        unsigned int flags = EXT4_I(inode)->i_flags;
+       unsigned int new_fl = 0;
 
-       inode->i_flags &= ~(S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC);
        if (flags & EXT4_SYNC_FL)
-               inode->i_flags |= S_SYNC;
+               new_fl |= S_SYNC;
        if (flags & EXT4_APPEND_FL)
-               inode->i_flags |= S_APPEND;
+               new_fl |= S_APPEND;
        if (flags & EXT4_IMMUTABLE_FL)
-               inode->i_flags |= S_IMMUTABLE;
+               new_fl |= S_IMMUTABLE;
        if (flags & EXT4_NOATIME_FL)
-               inode->i_flags |= S_NOATIME;
+               new_fl |= S_NOATIME;
        if (flags & EXT4_DIRSYNC_FL)
-               inode->i_flags |= S_DIRSYNC;
+               new_fl |= S_DIRSYNC;
+       set_mask_bits(&inode->i_flags,
+                     S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC, new_fl);
 }
 
 /* Propagate flags from i_flags to EXT4_I(inode)->i_flags */
@@ -4703,7 +4697,9 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
                ext4_journal_stop(handle);
        }
 
-       if (attr->ia_valid & ATTR_SIZE) {
+       if (attr->ia_valid & ATTR_SIZE && attr->ia_size != inode->i_size) {
+               handle_t *handle;
+               loff_t oldsize = inode->i_size;
 
                if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))) {
                        struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
@@ -4711,73 +4707,64 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
                        if (attr->ia_size > sbi->s_bitmap_maxbytes)
                                return -EFBIG;
                }
-       }
 
-       if (S_ISREG(inode->i_mode) &&
-           attr->ia_valid & ATTR_SIZE &&
-           (attr->ia_size < inode->i_size)) {
-               handle_t *handle;
+               if (IS_I_VERSION(inode) && attr->ia_size != inode->i_size)
+                       inode_inc_iversion(inode);
 
-               handle = ext4_journal_start(inode, EXT4_HT_INODE, 3);
-               if (IS_ERR(handle)) {
-                       error = PTR_ERR(handle);
-                       goto err_out;
-               }
-               if (ext4_handle_valid(handle)) {
-                       error = ext4_orphan_add(handle, inode);
-                       orphan = 1;
-               }
-               EXT4_I(inode)->i_disksize = attr->ia_size;
-               rc = ext4_mark_inode_dirty(handle, inode);
-               if (!error)
-                       error = rc;
-               ext4_journal_stop(handle);
-
-               if (ext4_should_order_data(inode)) {
-                       error = ext4_begin_ordered_truncate(inode,
+               if (S_ISREG(inode->i_mode) &&
+                   (attr->ia_size < inode->i_size)) {
+                       if (ext4_should_order_data(inode)) {
+                               error = ext4_begin_ordered_truncate(inode,
                                                            attr->ia_size);
-                       if (error) {
-                               /* Do as much error cleanup as possible */
-                               handle = ext4_journal_start(inode,
-                                                           EXT4_HT_INODE, 3);
-                               if (IS_ERR(handle)) {
-                                       ext4_orphan_del(NULL, inode);
+                               if (error)
                                        goto err_out;
-                               }
-                               ext4_orphan_del(handle, inode);
-                               orphan = 0;
-                               ext4_journal_stop(handle);
+                       }
+                       handle = ext4_journal_start(inode, EXT4_HT_INODE, 3);
+                       if (IS_ERR(handle)) {
+                               error = PTR_ERR(handle);
+                               goto err_out;
+                       }
+                       if (ext4_handle_valid(handle)) {
+                               error = ext4_orphan_add(handle, inode);
+                               orphan = 1;
+                       }
+                       EXT4_I(inode)->i_disksize = attr->ia_size;
+                       rc = ext4_mark_inode_dirty(handle, inode);
+                       if (!error)
+                               error = rc;
+                       ext4_journal_stop(handle);
+                       if (error) {
+                               ext4_orphan_del(NULL, inode);
                                goto err_out;
                        }
                }
-       }
 
-       if (attr->ia_valid & ATTR_SIZE) {
-               if (attr->ia_size != inode->i_size) {
-                       loff_t oldsize = inode->i_size;
-
-                       i_size_write(inode, attr->ia_size);
-                       /*
-                        * Blocks are going to be removed from the inode. Wait
-                        * for dio in flight.  Temporarily disable
-                        * dioread_nolock to prevent livelock.
-                        */
-                       if (orphan) {
-                               if (!ext4_should_journal_data(inode)) {
-                                       ext4_inode_block_unlocked_dio(inode);
-                                       inode_dio_wait(inode);
-                                       ext4_inode_resume_unlocked_dio(inode);
-                               } else
-                                       ext4_wait_for_tail_page_commit(inode);
-                       }
-                       /*
-                        * Truncate pagecache after we've waited for commit
-                        * in data=journal mode to make pages freeable.
-                        */
-                       truncate_pagecache(inode, oldsize, inode->i_size);
+               i_size_write(inode, attr->ia_size);
+               /*
+                * Blocks are going to be removed from the inode. Wait
+                * for dio in flight.  Temporarily disable
+                * dioread_nolock to prevent livelock.
+                */
+               if (orphan) {
+                       if (!ext4_should_journal_data(inode)) {
+                               ext4_inode_block_unlocked_dio(inode);
+                               inode_dio_wait(inode);
+                               ext4_inode_resume_unlocked_dio(inode);
+                       } else
+                               ext4_wait_for_tail_page_commit(inode);
                }
-               ext4_truncate(inode);
+               /*
+                * Truncate pagecache after we've waited for commit
+                * in data=journal mode to make pages freeable.
+                */
+               truncate_pagecache(inode, oldsize, inode->i_size);
        }
+       /*
+        * We want to call ext4_truncate() even if attr->ia_size ==
+        * inode->i_size for cases like truncation of fallocated space
+        */
+       if (attr->ia_valid & ATTR_SIZE)
+               ext4_truncate(inode);
 
        if (!rc) {
                setattr_copy(inode, attr);
@@ -4805,7 +4792,7 @@ int ext4_getattr(struct vfsmount *mnt, struct dentry *dentry,
                 struct kstat *stat)
 {
        struct inode *inode;
-       unsigned long delalloc_blocks;
+       unsigned long long delalloc_blocks;
 
        inode = dentry->d_inode;
        generic_fillattr(inode, stat);
@@ -4823,7 +4810,7 @@ int ext4_getattr(struct vfsmount *mnt, struct dentry *dentry,
        delalloc_blocks = EXT4_C2B(EXT4_SB(inode->i_sb),
                                EXT4_I(inode)->i_reserved_data_blocks);
 
-       stat->blocks += (delalloc_blocks << inode->i_sb->s_blocksize_bits)>>9;
+       stat->blocks += delalloc_blocks << (inode->i_sb->s_blocksize_bits-9);
        return 0;
 }