f2fs: introduce a new global lock scheme
[GitHub/exynos8895/android_kernel_samsung_universal8895.git] / fs / f2fs / file.c
index 3191b52aafb04cdce0ead1a5dbdb4a1ed6c7b082..07be88ddb9f8025df9d125ec24f7614ef2c58962 100644 (file)
 #include <linux/stat.h>
 #include <linux/buffer_head.h>
 #include <linux/writeback.h>
+#include <linux/blkdev.h>
 #include <linux/falloc.h>
 #include <linux/types.h>
+#include <linux/compat.h>
 #include <linux/uaccess.h>
 #include <linux/mount.h>
 
@@ -28,23 +30,22 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma,
                                                struct vm_fault *vmf)
 {
        struct page *page = vmf->page;
-       struct inode *inode = vma->vm_file->f_path.dentry->d_inode;
+       struct inode *inode = file_inode(vma->vm_file);
        struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
        block_t old_blk_addr;
        struct dnode_of_data dn;
-       int err;
+       int err, ilock;
 
        f2fs_balance_fs(sbi);
 
        sb_start_pagefault(inode->i_sb);
 
-       mutex_lock_op(sbi, DATA_NEW);
-
        /* block allocation */
+       ilock = mutex_lock_op(sbi);
        set_new_dnode(&dn, inode, NULL, NULL, 0);
-       err = get_dnode_of_data(&dn, page->index, 0);
+       err = get_dnode_of_data(&dn, page->index, ALLOC_NODE);
        if (err) {
-               mutex_unlock_op(sbi, DATA_NEW);
+               mutex_unlock_op(sbi, ilock);
                goto out;
        }
 
@@ -54,13 +55,12 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma,
                err = reserve_new_block(&dn);
                if (err) {
                        f2fs_put_dnode(&dn);
-                       mutex_unlock_op(sbi, DATA_NEW);
+                       mutex_unlock_op(sbi, ilock);
                        goto out;
                }
        }
        f2fs_put_dnode(&dn);
-
-       mutex_unlock_op(sbi, DATA_NEW);
+       mutex_unlock_op(sbi, ilock);
 
        lock_page(page);
        if (page->mapping != inode->i_mapping ||
@@ -101,28 +101,10 @@ static const struct vm_operations_struct f2fs_file_vm_ops = {
        .remap_pages    = generic_file_remap_pages,
 };
 
-static int need_to_sync_dir(struct f2fs_sb_info *sbi, struct inode *inode)
-{
-       struct dentry *dentry;
-       nid_t pino;
-
-       inode = igrab(inode);
-       dentry = d_find_any_alias(inode);
-       if (!dentry) {
-               iput(inode);
-               return 0;
-       }
-       pino = dentry->d_parent->d_inode->i_ino;
-       dput(dentry);
-       iput(inode);
-       return !is_checkpointed_node(sbi, pino);
-}
-
 int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
 {
        struct inode *inode = file->f_mapping->host;
        struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
-       unsigned long long cur_version;
        int ret = 0;
        bool need_cp = false;
        struct writeback_control wbc = {
@@ -146,28 +128,18 @@ int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
        if (datasync && !(inode->i_state & I_DIRTY_DATASYNC))
                goto out;
 
-       mutex_lock(&sbi->cp_mutex);
-       cur_version = le64_to_cpu(F2FS_CKPT(sbi)->checkpoint_ver);
-       mutex_unlock(&sbi->cp_mutex);
-
-       if (F2FS_I(inode)->data_version != cur_version &&
-                                       !(inode->i_state & I_DIRTY))
-               goto out;
-       F2FS_I(inode)->data_version--;
-
        if (!S_ISREG(inode->i_mode) || inode->i_nlink != 1)
                need_cp = true;
-       if (is_inode_flag_set(F2FS_I(inode), FI_NEED_CP))
+       else if (is_cp_file(inode))
                need_cp = true;
-       if (!space_for_roll_forward(sbi))
+       else if (!space_for_roll_forward(sbi))
                need_cp = true;
-       if (need_to_sync_dir(sbi, inode))
+       else if (!is_checkpointed_node(sbi, F2FS_I(inode)->i_pino))
                need_cp = true;
 
        if (need_cp) {
                /* all the dirty node pages should be flushed for POR */
                ret = f2fs_sync_fs(inode->i_sb, 1);
-               clear_inode_flag(F2FS_I(inode), FI_NEED_CP);
        } else {
                /* if there is no written node page, write its inode page */
                while (!sync_node_pages(sbi, inode->i_ino, &wbc)) {
@@ -177,6 +149,7 @@ int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
                }
                filemap_fdatawait_range(sbi->node_inode->i_mapping,
                                                        0, LONG_MAX);
+               ret = blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL);
        }
 out:
        mutex_unlock(&inode->i_mutex);
@@ -248,20 +221,19 @@ static int truncate_blocks(struct inode *inode, u64 from)
        unsigned int blocksize = inode->i_sb->s_blocksize;
        struct dnode_of_data dn;
        pgoff_t free_from;
-       int count = 0;
+       int count = 0, ilock = -1;
        int err;
 
        free_from = (pgoff_t)
                        ((from + blocksize - 1) >> (sbi->log_blocksize));
 
-       mutex_lock_op(sbi, DATA_TRUNC);
-
+       ilock = mutex_lock_op(sbi);
        set_new_dnode(&dn, inode, NULL, NULL, 0);
-       err = get_dnode_of_data(&dn, free_from, RDONLY_NODE);
+       err = get_dnode_of_data(&dn, free_from, LOOKUP_NODE);
        if (err) {
                if (err == -ENOENT)
                        goto free_next;
-               mutex_unlock_op(sbi, DATA_TRUNC);
+               mutex_unlock_op(sbi, ilock);
                return err;
        }
 
@@ -272,6 +244,7 @@ static int truncate_blocks(struct inode *inode, u64 from)
 
        count -= dn.ofs_in_node;
        BUG_ON(count < 0);
+
        if (dn.ofs_in_node || IS_INODE(dn.node_page)) {
                truncate_data_blocks_range(&dn, count);
                free_from += count;
@@ -280,7 +253,7 @@ static int truncate_blocks(struct inode *inode, u64 from)
        f2fs_put_dnode(&dn);
 free_next:
        err = truncate_inode_blocks(inode, free_from);
-       mutex_unlock_op(sbi, DATA_TRUNC);
+       mutex_unlock_op(sbi, ilock);
 
        /* lastly zero out the first data page */
        truncate_partial_data_page(inode, from);
@@ -298,8 +271,6 @@ void f2fs_truncate(struct inode *inode)
                inode->i_mtime = inode->i_ctime = CURRENT_TIME;
                mark_inode_dirty(inode);
        }
-
-       f2fs_balance_fs(F2FS_SB(inode->i_sb));
 }
 
 static int f2fs_getattr(struct vfsmount *mnt,
@@ -356,6 +327,7 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr)
                        attr->ia_size != i_size_read(inode)) {
                truncate_setsize(inode, attr->ia_size);
                f2fs_truncate(inode);
+               f2fs_balance_fs(F2FS_SB(inode->i_sb));
        }
 
        __setattr_copy(inode, attr);
@@ -387,12 +359,18 @@ const struct inode_operations f2fs_file_inode_operations = {
 static void fill_zero(struct inode *inode, pgoff_t index,
                                        loff_t start, loff_t len)
 {
+       struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
        struct page *page;
+       int ilock;
 
        if (!len)
                return;
 
+       f2fs_balance_fs(sbi);
+
+       ilock = mutex_lock_op(sbi);
        page = get_new_data_page(inode, index, false);
+       mutex_unlock_op(sbi, ilock);
 
        if (!IS_ERR(page)) {
                wait_on_page_writeback(page);
@@ -409,15 +387,10 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end)
 
        for (index = pg_start; index < pg_end; index++) {
                struct dnode_of_data dn;
-               struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
 
-               f2fs_balance_fs(sbi);
-
-               mutex_lock_op(sbi, DATA_TRUNC);
                set_new_dnode(&dn, inode, NULL, NULL, 0);
-               err = get_dnode_of_data(&dn, index, RDONLY_NODE);
+               err = get_dnode_of_data(&dn, index, LOOKUP_NODE);
                if (err) {
-                       mutex_unlock_op(sbi, DATA_TRUNC);
                        if (err == -ENOENT)
                                continue;
                        return err;
@@ -426,7 +399,6 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end)
                if (dn.data_blkaddr != NULL_ADDR)
                        truncate_data_blocks_range(&dn, 1);
                f2fs_put_dnode(&dn);
-               mutex_unlock_op(sbi, DATA_TRUNC);
        }
        return 0;
 }
@@ -456,12 +428,19 @@ static int punch_hole(struct inode *inode, loff_t offset, loff_t len, int mode)
                if (pg_start < pg_end) {
                        struct address_space *mapping = inode->i_mapping;
                        loff_t blk_start, blk_end;
+                       struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
+                       int ilock;
+
+                       f2fs_balance_fs(sbi);
 
                        blk_start = pg_start << PAGE_CACHE_SHIFT;
                        blk_end = pg_end << PAGE_CACHE_SHIFT;
                        truncate_inode_pages_range(mapping, blk_start,
                                        blk_end - 1);
+
+                       ilock = mutex_lock_op(sbi);
                        ret = truncate_hole(inode, pg_start, pg_end);
+                       mutex_unlock_op(sbi, ilock);
                }
        }
 
@@ -495,13 +474,13 @@ static int expand_inode_data(struct inode *inode, loff_t offset,
 
        for (index = pg_start; index <= pg_end; index++) {
                struct dnode_of_data dn;
+               int ilock;
 
-               mutex_lock_op(sbi, DATA_NEW);
-
+               ilock = mutex_lock_op(sbi);
                set_new_dnode(&dn, inode, NULL, NULL, 0);
-               ret = get_dnode_of_data(&dn, index, 0);
+               ret = get_dnode_of_data(&dn, index, ALLOC_NODE);
                if (ret) {
-                       mutex_unlock_op(sbi, DATA_NEW);
+                       mutex_unlock_op(sbi, ilock);
                        break;
                }
 
@@ -509,13 +488,12 @@ static int expand_inode_data(struct inode *inode, loff_t offset,
                        ret = reserve_new_block(&dn);
                        if (ret) {
                                f2fs_put_dnode(&dn);
-                               mutex_unlock_op(sbi, DATA_NEW);
+                               mutex_unlock_op(sbi, ilock);
                                break;
                        }
                }
                f2fs_put_dnode(&dn);
-
-               mutex_unlock_op(sbi, DATA_NEW);
+               mutex_unlock_op(sbi, ilock);
 
                if (pg_start == pg_end)
                        new_size = offset + len;
@@ -539,7 +517,7 @@ static int expand_inode_data(struct inode *inode, loff_t offset,
 static long f2fs_fallocate(struct file *file, int mode,
                                loff_t offset, loff_t len)
 {
-       struct inode *inode = file->f_path.dentry->d_inode;
+       struct inode *inode = file_inode(file);
        long ret;
 
        if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE))
@@ -572,7 +550,7 @@ static inline __u32 f2fs_mask_flags(umode_t mode, __u32 flags)
 
 long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
-       struct inode *inode = filp->f_dentry->d_inode;
+       struct inode *inode = file_inode(filp);
        struct f2fs_inode_info *fi = F2FS_I(inode);
        unsigned int flags;
        int ret;
@@ -630,6 +608,23 @@ out:
        }
 }
 
+#ifdef CONFIG_COMPAT
+long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+       switch (cmd) {
+       case F2FS_IOC32_GETFLAGS:
+               cmd = F2FS_IOC_GETFLAGS;
+               break;
+       case F2FS_IOC32_SETFLAGS:
+               cmd = F2FS_IOC_SETFLAGS;
+               break;
+       default:
+               return -ENOIOCTLCMD;
+       }
+       return f2fs_ioctl(file, cmd, (unsigned long) compat_ptr(arg));
+}
+#endif
+
 const struct file_operations f2fs_file_operations = {
        .llseek         = generic_file_llseek,
        .read           = do_sync_read,
@@ -641,6 +636,9 @@ const struct file_operations f2fs_file_operations = {
        .fsync          = f2fs_sync_file,
        .fallocate      = f2fs_fallocate,
        .unlocked_ioctl = f2fs_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl   = f2fs_compat_ioctl,
+#endif
        .splice_read    = generic_file_splice_read,
        .splice_write   = generic_file_splice_write,
 };