Merge tag 'arc-4.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vgupta/arc
[cascardo/linux.git] / fs / f2fs / file.c
index df6a059..a6f3f61 100644 (file)
@@ -241,6 +241,8 @@ go_write:
                 * will be used only for fsynced inodes after checkpoint.
                 */
                try_to_fix_pino(inode);
+               clear_inode_flag(fi, FI_APPEND_WRITE);
+               clear_inode_flag(fi, FI_UPDATE_WRITE);
                goto out;
        }
 sync_nodes:
@@ -433,8 +435,12 @@ int truncate_data_blocks_range(struct dnode_of_data *dn, int count)
                        continue;
 
                dn->data_blkaddr = NULL_ADDR;
-               update_extent_cache(dn);
+               set_data_blkaddr(dn);
+               f2fs_update_extent_cache(dn);
                invalidate_blocks(sbi, blkaddr);
+               if (dn->ofs_in_node == 0 && IS_INODE(dn->node_page))
+                       clear_inode_flag(F2FS_I(dn->inode),
+                                               FI_FIRST_BLOCK_WRITTEN);
                nr_free++;
        }
        if (nr_free) {
@@ -454,15 +460,16 @@ void truncate_data_blocks(struct dnode_of_data *dn)
        truncate_data_blocks_range(dn, ADDRS_PER_BLOCK);
 }
 
-static int truncate_partial_data_page(struct inode *inode, u64 from)
+static int truncate_partial_data_page(struct inode *inode, u64 from,
+                                                               bool force)
 {
        unsigned offset = from & (PAGE_CACHE_SIZE - 1);
        struct page *page;
 
-       if (!offset)
+       if (!offset && !force)
                return 0;
 
-       page = find_data_page(inode, from >> PAGE_CACHE_SHIFT, false);
+       page = find_data_page(inode, from >> PAGE_CACHE_SHIFT, force);
        if (IS_ERR(page))
                return 0;
 
@@ -473,7 +480,8 @@ static int truncate_partial_data_page(struct inode *inode, u64 from)
 
        f2fs_wait_on_page_writeback(page, DATA);
        zero_user(page, offset, PAGE_CACHE_SIZE - offset);
-       set_page_dirty(page);
+       if (!force)
+               set_page_dirty(page);
 out:
        f2fs_put_page(page, 1);
        return 0;
@@ -487,6 +495,7 @@ int truncate_blocks(struct inode *inode, u64 from, bool lock)
        pgoff_t free_from;
        int count = 0, err = 0;
        struct page *ipage;
+       bool truncate_page = false;
 
        trace_f2fs_truncate_blocks_enter(inode, from);
 
@@ -502,7 +511,10 @@ int truncate_blocks(struct inode *inode, u64 from, bool lock)
        }
 
        if (f2fs_has_inline_data(inode)) {
+               if (truncate_inline_inode(ipage, from))
+                       set_page_dirty(ipage);
                f2fs_put_page(ipage, 1);
+               truncate_page = true;
                goto out;
        }
 
@@ -533,7 +545,7 @@ out:
 
        /* lastly zero out the first data page */
        if (!err)
-               err = truncate_partial_data_page(inode, from);
+               err = truncate_partial_data_page(inode, from, truncate_page);
 
        trace_f2fs_truncate_blocks_exit(inode, err);
        return err;
@@ -997,6 +1009,9 @@ static int f2fs_ioc_release_volatile_write(struct file *filp)
        if (!f2fs_is_volatile_file(inode))
                return 0;
 
+       if (!f2fs_is_first_block_written(inode))
+               return truncate_partial_data_page(inode, 0, true);
+
        punch_hole(inode, 0, F2FS_BLKSIZE);
        return 0;
 }
@@ -1029,6 +1044,41 @@ static int f2fs_ioc_abort_volatile_write(struct file *filp)
        return ret;
 }
 
+static int f2fs_ioc_shutdown(struct file *filp, unsigned long arg)
+{
+       struct inode *inode = file_inode(filp);
+       struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
+       struct super_block *sb = sbi->sb;
+       __u32 in;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       if (get_user(in, (__u32 __user *)arg))
+               return -EFAULT;
+
+       switch (in) {
+       case F2FS_GOING_DOWN_FULLSYNC:
+               sb = freeze_bdev(sb->s_bdev);
+               if (sb && !IS_ERR(sb)) {
+                       f2fs_stop_checkpoint(sbi);
+                       thaw_bdev(sb->s_bdev, sb);
+               }
+               break;
+       case F2FS_GOING_DOWN_METASYNC:
+               /* do checkpoint only */
+               f2fs_sync_fs(sb, 1);
+               f2fs_stop_checkpoint(sbi);
+               break;
+       case F2FS_GOING_DOWN_NOSYNC:
+               f2fs_stop_checkpoint(sbi);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
 static int f2fs_ioc_fitrim(struct file *filp, unsigned long arg)
 {
        struct inode *inode = file_inode(filp);
@@ -1078,6 +1128,8 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
                return f2fs_ioc_release_volatile_write(filp);
        case F2FS_IOC_ABORT_VOLATILE_WRITE:
                return f2fs_ioc_abort_volatile_write(filp);
+       case F2FS_IOC_SHUTDOWN:
+               return f2fs_ioc_shutdown(filp, arg);
        case FITRIM:
                return f2fs_ioc_fitrim(filp, arg);
        default: