f2fs: avoid unnecessary updating inode during fsync
authorJaegeuk Kim <jaegeuk@kernel.org>
Sat, 21 May 2016 03:42:37 +0000 (20:42 -0700)
committerJaegeuk Kim <jaegeuk@kernel.org>
Fri, 3 Jun 2016 01:05:13 +0000 (18:05 -0700)
If roll-forward recovery can recover i_size, we don't need to update inode's
metadata during fsync.

Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
fs/f2fs/data.c
fs/f2fs/f2fs.h
fs/f2fs/file.c
fs/f2fs/inode.c
fs/f2fs/node.c
fs/f2fs/recovery.c
fs/f2fs/super.c

index a3dea51f4702c25d5d2af68db04469b33d971d1c..287582e12f8fd25a1b8b6286564e45a43e5cf508 100644 (file)
@@ -1204,6 +1204,7 @@ static int f2fs_write_data_page(struct page *page,
        loff_t i_size = i_size_read(inode);
        const pgoff_t end_index = ((unsigned long long) i_size)
                                                        >> PAGE_SHIFT;
+       loff_t psize = (page->index + 1) << PAGE_SHIFT;
        unsigned offset = 0;
        bool need_balance_fs = false;
        int err = 0;
@@ -1265,6 +1266,8 @@ write:
                err = f2fs_write_inline_data(inode, page);
        if (err == -EAGAIN)
                err = do_write_data_page(&fio);
+       if (F2FS_I(inode)->last_disk_size < psize)
+               F2FS_I(inode)->last_disk_size = psize;
        f2fs_unlock_op(sbi);
 done:
        if (err && err != -ENOENT)
index 2adef0e58461b39fad332d72137e78ff9c88f3a8..bf1c8b0ef6c7386f13944d3135b0cc75037785fb 100644 (file)
@@ -441,6 +441,7 @@ struct f2fs_inode_info {
        unsigned int clevel;            /* maximum level of given file name */
        nid_t i_xattr_nid;              /* node id that contains xattrs */
        unsigned long long xattr_ver;   /* cp version of xattr modification */
+       loff_t  last_disk_size;         /* lastly written file size */
 
        struct list_head dirty_list;    /* dirty list for dirs and files */
        struct list_head gdirty_list;   /* linked in global dirty list */
@@ -1516,6 +1517,7 @@ static inline void f2fs_change_bit(unsigned int nr, char *addr)
 enum {
        FI_NEW_INODE,           /* indicate newly allocated inode */
        FI_DIRTY_INODE,         /* indicate inode is dirty or not */
+       FI_AUTO_RECOVER,        /* indicate inode is recoverable */
        FI_DIRTY_DIR,           /* indicate directory has dirty pages */
        FI_INC_LINK,            /* need to increment i_nlink */
        FI_ACL_MODE,            /* indicate acl mode */
@@ -1591,18 +1593,35 @@ static inline void f2fs_i_links_write(struct inode *inode, bool inc)
 static inline void f2fs_i_blocks_write(struct inode *inode,
                                        blkcnt_t diff, bool add)
 {
+       bool clean = !is_inode_flag_set(inode, FI_DIRTY_INODE);
+       bool recover = is_inode_flag_set(inode, FI_AUTO_RECOVER);
+
        inode->i_blocks = add ? inode->i_blocks + diff :
                                inode->i_blocks - diff;
        mark_inode_dirty_sync(inode);
+       if (clean || recover)
+               set_inode_flag(inode, FI_AUTO_RECOVER);
 }
 
 static inline void f2fs_i_size_write(struct inode *inode, loff_t i_size)
 {
+       bool clean = !is_inode_flag_set(inode, FI_DIRTY_INODE);
+       bool recover = is_inode_flag_set(inode, FI_AUTO_RECOVER);
+
        if (i_size_read(inode) == i_size)
                return;
 
        i_size_write(inode, i_size);
        mark_inode_dirty_sync(inode);
+       if (clean || recover)
+               set_inode_flag(inode, FI_AUTO_RECOVER);
+}
+
+static inline bool f2fs_skip_inode_update(struct inode *inode)
+{
+       if (!is_inode_flag_set(inode, FI_AUTO_RECOVER))
+               return false;
+       return F2FS_I(inode)->last_disk_size == i_size_read(inode);
 }
 
 static inline void f2fs_i_depth_write(struct inode *inode, unsigned int depth)
@@ -1936,8 +1955,8 @@ void ra_node_page(struct f2fs_sb_info *, nid_t);
 struct page *get_node_page(struct f2fs_sb_info *, pgoff_t);
 struct page *get_node_page_ra(struct page *, int);
 void move_node_page(struct page *, int);
-int fsync_node_pages(struct f2fs_sb_info *, nid_t, struct writeback_control *,
-                                                               bool);
+int fsync_node_pages(struct f2fs_sb_info *, struct inode *,
+                       struct writeback_control *, bool);
 int sync_node_pages(struct f2fs_sb_info *, struct writeback_control *);
 bool alloc_nid(struct f2fs_sb_info *, nid_t *);
 void alloc_nid_done(struct f2fs_sb_info *, nid_t);
index 73bc946974adc939766e78328c37bd06814bf525..23decf050236dbbb46e250872ab285565da47e97 100644 (file)
@@ -208,7 +208,7 @@ static int f2fs_do_sync_file(struct file *file, loff_t start, loff_t end,
        }
 
        /* if the inode is dirty, let's recover all the time */
-       if (!datasync) {
+       if (!datasync && !f2fs_skip_inode_update(inode)) {
                f2fs_write_inode(inode, NULL);
                goto go_write;
        }
@@ -251,7 +251,7 @@ go_write:
                goto out;
        }
 sync_nodes:
-       ret = fsync_node_pages(sbi, ino, &wbc, atomic);
+       ret = fsync_node_pages(sbi, inode, &wbc, atomic);
        if (ret)
                goto out;
 
index 2d892b6d5632f7a20479267cf5051747465b89c9..bdd814db883eb538e98b98c29bbf69c47f1c99cb 100644 (file)
@@ -154,6 +154,9 @@ static int do_read_inode(struct inode *inode)
        if (__written_first_block(ri))
                set_inode_flag(inode, FI_FIRST_BLOCK_WRITTEN);
 
+       if (!need_inode_block_update(sbi, inode->i_ino))
+               fi->last_disk_size = inode->i_size;
+
        f2fs_put_page(node_page, 1);
 
        stat_inc_inline_xattr(inode);
index 82f0f833151e95006e715f9dff1646dd113925f8..641d60392bfffd972dd7a280f5aa17a82e2ce9f5 100644 (file)
@@ -1293,7 +1293,7 @@ continue_unlock:
        return last_page;
 }
 
-int fsync_node_pages(struct f2fs_sb_info *sbi, nid_t ino,
+int fsync_node_pages(struct f2fs_sb_info *sbi, struct inode *inode,
                        struct writeback_control *wbc, bool atomic)
 {
        pgoff_t index, end;
@@ -1301,6 +1301,7 @@ int fsync_node_pages(struct f2fs_sb_info *sbi, nid_t ino,
        int ret = 0;
        struct page *last_page = NULL;
        bool marked = false;
+       nid_t ino = inode->i_ino;
 
        if (atomic) {
                last_page = last_fsync_dnode(sbi, ino);
@@ -1354,9 +1355,13 @@ continue_unlock:
 
                        if (!atomic || page == last_page) {
                                set_fsync_mark(page, 1);
-                               if (IS_INODE(page))
+                               if (IS_INODE(page)) {
+                                       if (is_inode_flag_set(inode,
+                                                               FI_DIRTY_INODE))
+                                               update_inode(inode, page);
                                        set_dentry_mark(page,
                                                need_dentry_mark(sbi, ino));
+                               }
                                /*  may be written by other thread */
                                if (!PageDirty(page))
                                        set_page_dirty(page);
index 68c433f17ab50e41006b8b4575f09ba235d30315..b568b28c74f2a677d800a8a102018d7b3909212e 100644 (file)
@@ -455,6 +455,9 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
                        continue;
                }
 
+               if ((start + 1) << PAGE_SHIFT > i_size_read(inode))
+                       f2fs_i_size_write(inode, (start + 1) << PAGE_SHIFT);
+
                /*
                 * dest is reserved block, invalidate src block
                 * and then reserve one new block in dnode page.
index b5144b81e4c40cdd319bb3b9468414a09b30fb01..6fa4ec8ea1f701f5b81b70ab86a15eda12297b04 100644 (file)
@@ -613,6 +613,9 @@ static void f2fs_dirty_inode(struct inode *inode, int flags)
                        inode->i_ino == F2FS_META_INO(sbi))
                return;
 
+       if (is_inode_flag_set(inode, FI_AUTO_RECOVER))
+               clear_inode_flag(inode, FI_AUTO_RECOVER);
+
        spin_lock(&sbi->inode_lock[DIRTY_META]);
        if (is_inode_flag_set(inode, FI_DIRTY_INODE)) {
                spin_unlock(&sbi->inode_lock[DIRTY_META]);
@@ -638,6 +641,7 @@ void f2fs_inode_synced(struct inode *inode)
        }
        list_del_init(&F2FS_I(inode)->gdirty_list);
        clear_inode_flag(inode, FI_DIRTY_INODE);
+       clear_inode_flag(inode, FI_AUTO_RECOVER);
        dec_page_count(sbi, F2FS_DIRTY_IMETA);
        spin_unlock(&sbi->inode_lock[DIRTY_META]);
        stat_dec_dirty_inode(F2FS_I_SB(inode), DIRTY_META);