if (attr->ia_valid & ATTR_SIZE) {
                handle_t *handle;
                loff_t oldsize = inode->i_size;
-               int shrink = (attr->ia_size <= inode->i_size);
+               int shrink = (attr->ia_size < inode->i_size);
 
                if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))) {
                        struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
                if (IS_I_VERSION(inode) && attr->ia_size != inode->i_size)
                        inode_inc_iversion(inode);
 
-               if (ext4_should_order_data(inode) &&
-                   (attr->ia_size < inode->i_size)) {
-                       error = ext4_begin_ordered_truncate(inode,
+               if (shrink) {
+                       if (ext4_should_order_data(inode)) {
+                               error = ext4_begin_ordered_truncate(inode,
                                                            attr->ia_size);
-                       if (error)
-                               goto err_out;
+                               if (error)
+                                       goto err_out;
+                       }
+                       /*
+                        * Blocks are going to be removed from the inode. Wait
+                        * for dio in flight.
+                        */
+                       inode_dio_wait(inode);
+               }
+
+               down_write(&EXT4_I(inode)->i_mmap_sem);
+
+               rc = ext4_break_layouts(inode);
+               if (rc) {
+                       up_write(&EXT4_I(inode)->i_mmap_sem);
+                       return rc;
                }
+
                if (attr->ia_size != inode->i_size) {
                        handle = ext4_journal_start(inode, EXT4_HT_INODE, 3);
                        if (IS_ERR(handle)) {
                                error = PTR_ERR(handle);
-                               goto err_out;
+                               goto out_mmap_sem;
                        }
                        if (ext4_handle_valid(handle) && shrink) {
                                error = ext4_orphan_add(handle, inode);
                                i_size_write(inode, attr->ia_size);
                        up_write(&EXT4_I(inode)->i_data_sem);
                        ext4_journal_stop(handle);
-                       if (error) {
-                               if (orphan && inode->i_nlink)
-                                       ext4_orphan_del(NULL, inode);
-                               goto err_out;
+                       if (error)
+                               goto out_mmap_sem;
+                       if (!shrink) {
+                               pagecache_isize_extended(inode, oldsize,
+                                                        inode->i_size);
+                       } else if (ext4_should_journal_data(inode)) {
+                               ext4_wait_for_tail_page_commit(inode);
                        }
                }
-               if (!shrink) {
-                       pagecache_isize_extended(inode, oldsize, inode->i_size);
-               } else {
-                       /*
-                        * Blocks are going to be removed from the inode. Wait
-                        * for dio in flight.
-                        */
-                       inode_dio_wait(inode);
-               }
-               if (orphan && ext4_should_journal_data(inode))
-                       ext4_wait_for_tail_page_commit(inode);
-               down_write(&EXT4_I(inode)->i_mmap_sem);
-
-               rc = ext4_break_layouts(inode);
-               if (rc) {
-                       up_write(&EXT4_I(inode)->i_mmap_sem);
-                       error = rc;
-                       goto err_out;
-               }
 
                /*
                 * Truncate pagecache after we've waited for commit
                 * in data=journal mode to make pages freeable.
                 */
                truncate_pagecache(inode, inode->i_size);
-               if (shrink) {
+               /*
+                * Call ext4_truncate() even if i_size didn't change to
+                * truncate possible preallocated blocks.
+                */
+               if (attr->ia_size <= oldsize) {
                        rc = ext4_truncate(inode);
                        if (rc)
                                error = rc;
                }
+out_mmap_sem:
                up_write(&EXT4_I(inode)->i_mmap_sem);
        }