f2fs: flush inode metadata when checkpoint is doing
authorJaegeuk Kim <jaegeuk@kernel.org>
Fri, 20 May 2016 18:10:10 +0000 (11:10 -0700)
committerJaegeuk Kim <jaegeuk@kernel.org>
Fri, 3 Jun 2016 01:05:11 +0000 (18:05 -0700)
This patch registers all the inodes which have dirty metadata to sync when
checkpoint is doing.

Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
fs/f2fs/checkpoint.c
fs/f2fs/debug.c
fs/f2fs/f2fs.h
fs/f2fs/inode.c
fs/f2fs/node.c
fs/f2fs/segment.h
fs/f2fs/super.c

index 02e0522beccfba8900636f2387d257fb7e31e5d1..5ddd15cd6c6cbdf4976e81cc9288069909e5cff0 100644 (file)
@@ -859,6 +859,34 @@ retry:
        goto retry;
 }
 
+int f2fs_sync_inode_meta(struct f2fs_sb_info *sbi)
+{
+       struct list_head *head = &sbi->inode_list[DIRTY_META];
+       struct inode *inode;
+       struct f2fs_inode_info *fi;
+       s64 total = get_pages(sbi, F2FS_DIRTY_IMETA);
+
+       while (total--) {
+               if (unlikely(f2fs_cp_error(sbi)))
+                       return -EIO;
+
+               spin_lock(&sbi->inode_lock[DIRTY_META]);
+               if (list_empty(head)) {
+                       spin_unlock(&sbi->inode_lock[DIRTY_META]);
+                       return 0;
+               }
+               fi = list_entry(head->next, struct f2fs_inode_info,
+                                                       gdirty_list);
+               inode = igrab(&fi->vfs_inode);
+               spin_unlock(&sbi->inode_lock[DIRTY_META]);
+               if (inode) {
+                       update_inode_page(inode);
+                       iput(inode);
+               }
+       };
+       return 0;
+}
+
 /*
  * Freeze all the FS-operations for checkpoint.
  */
@@ -885,6 +913,14 @@ retry_flush_dents:
                goto retry_flush_dents;
        }
 
+       if (get_pages(sbi, F2FS_DIRTY_IMETA)) {
+               f2fs_unlock_all(sbi);
+               err = f2fs_sync_inode_meta(sbi);
+               if (err)
+                       goto out;
+               goto retry_flush_dents;
+       }
+
        /*
         * POR: we should ensure that there are no dirty node pages
         * until finishing nat/sit flush.
index d89a425055d0131b9c4be5a1f19527ef5091362f..badd407bb622a5db00630bb1a0fdb994cbb015cb 100644 (file)
@@ -47,6 +47,7 @@ static void update_general_status(struct f2fs_sb_info *sbi)
        si->ndirty_data = get_pages(sbi, F2FS_DIRTY_DATA);
        si->ndirty_dirs = sbi->ndirty_inode[DIR_INODE];
        si->ndirty_files = sbi->ndirty_inode[FILE_INODE];
+       si->ndirty_all = sbi->ndirty_inode[DIRTY_META];
        si->inmem_pages = get_pages(sbi, F2FS_INMEM_PAGES);
        si->wb_bios = atomic_read(&sbi->nr_wb_bios);
        si->total_count = (int)sbi->user_block_count / sbi->blocks_per_seg;
@@ -304,8 +305,8 @@ static int stat_show(struct seq_file *s, void *v)
                           si->inmem_pages, si->wb_bios);
                seq_printf(s, "  - nodes: %4lld in %4d\n",
                           si->ndirty_node, si->node_pages);
-               seq_printf(s, "  - dents: %4lld in dirs:%4d\n",
-                          si->ndirty_dent, si->ndirty_dirs);
+               seq_printf(s, "  - dents: %4lld in dirs:%4d (%4d)\n",
+                          si->ndirty_dent, si->ndirty_dirs, si->ndirty_all);
                seq_printf(s, "  - datas: %4lld in files:%4d\n",
                           si->ndirty_data, si->ndirty_files);
                seq_printf(s, "  - meta: %4lld in %4d\n",
index 0534d7a18b352c0594adda9c8a8b7722f46928d9..b541164ce5af5ba4e6605752dcdfc3e02c0f269a 100644 (file)
@@ -442,7 +442,8 @@ struct f2fs_inode_info {
        nid_t i_xattr_nid;              /* node id that contains xattrs */
        unsigned long long xattr_ver;   /* cp version of xattr modification */
 
-       struct list_head dirty_list;    /* linked in global dirty list */
+       struct list_head dirty_list;    /* dirty list for dirs and files */
+       struct list_head gdirty_list;   /* linked in global dirty list */
        struct list_head inmem_pages;   /* inmemory pages managed by f2fs */
        struct mutex inmem_lock;        /* lock for inmemory pages */
        struct extent_tree *extent_tree;        /* cached extent_tree entry */
@@ -657,6 +658,7 @@ enum count_type {
        F2FS_DIRTY_NODES,
        F2FS_DIRTY_META,
        F2FS_INMEM_PAGES,
+       F2FS_DIRTY_IMETA,
        NR_COUNT_TYPE,
 };
 
@@ -707,6 +709,7 @@ struct f2fs_bio_info {
 enum inode_type {
        DIR_INODE,                      /* for dirty dir inode */
        FILE_INODE,                     /* for dirty regular/symlink inode */
+       DIRTY_META,                     /* for all dirtied inode metadata */
        NR_INODE_TYPE,
 };
 
@@ -1899,6 +1902,7 @@ static inline int f2fs_add_link(struct dentry *dentry, struct inode *inode)
 /*
  * super.c
  */
+void f2fs_inode_synced(struct inode *);
 int f2fs_commit_super(struct f2fs_sb_info *, bool);
 int f2fs_sync_fs(struct super_block *, int);
 extern __printf(3, 4)
@@ -2010,6 +2014,7 @@ void add_ino_entry(struct f2fs_sb_info *, nid_t, int type);
 void remove_ino_entry(struct f2fs_sb_info *, nid_t, int type);
 void release_ino_entry(struct f2fs_sb_info *, bool);
 bool exist_written_data(struct f2fs_sb_info *, nid_t, int);
+int f2fs_sync_inode_meta(struct f2fs_sb_info *);
 int acquire_orphan_inode(struct f2fs_sb_info *);
 void release_orphan_inode(struct f2fs_sb_info *);
 void add_orphan_inode(struct f2fs_sb_info *, nid_t);
@@ -2078,7 +2083,7 @@ struct f2fs_stat_info {
        unsigned long long hit_total, total_ext;
        int ext_tree, zombie_tree, ext_node;
        s64 ndirty_node, ndirty_dent, ndirty_meta, ndirty_data, inmem_pages;
-       unsigned int ndirty_dirs, ndirty_files;
+       unsigned int ndirty_dirs, ndirty_files, ndirty_all;
        int nats, dirty_nats, sits, dirty_sits, fnids;
        int total_count, utilization;
        int bg_gc, wb_bios;
index 34aa0949e48c2599e559bf60840c5f651b82205c..2d892b6d5632f7a20479267cf5051747465b89c9 100644 (file)
@@ -262,7 +262,7 @@ int update_inode(struct inode *inode, struct page *node_page)
 
        __set_inode_rdev(inode, ri);
        set_cold_node(inode, node_page);
-       clear_inode_flag(inode, FI_DIRTY_INODE);
+       f2fs_inode_synced(inode);
 
        /* deleted inode */
        if (inode->i_nlink == 0)
@@ -286,6 +286,7 @@ retry:
                } else if (err != -ENOENT) {
                        f2fs_stop_checkpoint(sbi, false);
                }
+               f2fs_inode_synced(inode);
                return 0;
        }
        ret = update_inode(inode, node_page);
@@ -360,6 +361,8 @@ retry:
                goto retry;
        }
 
+       if (err)
+               update_inode_page(inode);
        sb_end_intwrite(inode->i_sb);
 no_delete:
        stat_dec_inline_xattr(inode);
@@ -381,6 +384,8 @@ no_delete:
                !exist_written_data(sbi, inode->i_ino, ORPHAN_INO));
 out_clear:
        fscrypt_put_encryption_info(inode, NULL);
+
+       f2fs_bug_on(sbi, is_inode_flag_set(inode, FI_DIRTY_INODE));
        clear_inode(inode);
 }
 
index 0635304c50ac5010d7af3c92fe2b8aef2770f70f..1965351b644c16ad53d5955831bd7739a2b14628 100644 (file)
@@ -670,6 +670,7 @@ static void truncate_node(struct dnode_of_data *dn)
        if (dn->nid == dn->inode->i_ino) {
                remove_orphan_inode(sbi, dn->nid);
                dec_valid_inode_count(sbi);
+               f2fs_inode_synced(dn->inode);
        } else {
                sync_inode_page(dn);
        }
index fcdd7310b9614d9f869e388addc26dac9922525d..5d016a1edf21bdc66182912e3c0ed5d7588e66c8 100644 (file)
@@ -479,6 +479,8 @@ static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi, int freed)
        int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES);
        int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS);
 
+       node_secs += get_blocktype_secs(sbi, F2FS_DIRTY_IMETA);
+
        if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING)))
                return false;
 
index d832bf4051b5d7ab1ae8d0d08f640c24a2e94423..b5144b81e4c40cdd319bb3b9468414a09b30fb01 100644 (file)
@@ -537,6 +537,7 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb)
        fi->i_advise = 0;
        init_rwsem(&fi->i_sem);
        INIT_LIST_HEAD(&fi->dirty_list);
+       INIT_LIST_HEAD(&fi->gdirty_list);
        INIT_LIST_HEAD(&fi->inmem_pages);
        mutex_init(&fi->inmem_lock);
 
@@ -547,6 +548,8 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb)
 
 static int f2fs_drop_inode(struct inode *inode)
 {
+       int ret;
+
        /*
         * This is to avoid a deadlock condition like below.
         * writeback_single_inode(inode)
@@ -554,7 +557,7 @@ static int f2fs_drop_inode(struct inode *inode)
         *    - f2fs_gc -> iput -> evict
         *       - inode_wait_for_writeback(inode)
         */
-       if (!inode_unhashed(inode) && inode->i_state & I_SYNC) {
+       if ((!inode_unhashed(inode) && inode->i_state & I_SYNC)) {
                if (!inode->i_nlink && !is_bad_inode(inode)) {
                        /* to avoid evict_inode call simultaneously */
                        atomic_inc(&inode->i_count);
@@ -581,7 +584,20 @@ static int f2fs_drop_inode(struct inode *inode)
                }
                return 0;
        }
-       return generic_drop_inode(inode);
+
+       ret = generic_drop_inode(inode);
+       if (is_inode_flag_set(inode, FI_DIRTY_INODE)) {
+               if (ret)
+                       inode->i_state |= I_WILL_FREE;
+               spin_unlock(&inode->i_lock);
+
+               update_inode_page(inode);
+
+               spin_lock(&inode->i_lock);
+               if (ret)
+                       inode->i_state &= ~I_WILL_FREE;
+       }
+       return ret;
 }
 
 /*
@@ -591,7 +607,40 @@ static int f2fs_drop_inode(struct inode *inode)
  */
 static void f2fs_dirty_inode(struct inode *inode, int flags)
 {
+       struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
+
+       if (inode->i_ino == F2FS_NODE_INO(sbi) ||
+                       inode->i_ino == F2FS_META_INO(sbi))
+               return;
+
+       spin_lock(&sbi->inode_lock[DIRTY_META]);
+       if (is_inode_flag_set(inode, FI_DIRTY_INODE)) {
+               spin_unlock(&sbi->inode_lock[DIRTY_META]);
+               return;
+       }
+
        set_inode_flag(inode, FI_DIRTY_INODE);
+       list_add_tail(&F2FS_I(inode)->gdirty_list,
+                               &sbi->inode_list[DIRTY_META]);
+       inc_page_count(sbi, F2FS_DIRTY_IMETA);
+       spin_unlock(&sbi->inode_lock[DIRTY_META]);
+       stat_inc_dirty_inode(sbi, DIRTY_META);
+}
+
+void f2fs_inode_synced(struct inode *inode)
+{
+       struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
+
+       spin_lock(&sbi->inode_lock[DIRTY_META]);
+       if (!is_inode_flag_set(inode, FI_DIRTY_INODE)) {
+               spin_unlock(&sbi->inode_lock[DIRTY_META]);
+               return;
+       }
+       list_del_init(&F2FS_I(inode)->gdirty_list);
+       clear_inode_flag(inode, FI_DIRTY_INODE);
+       dec_page_count(sbi, F2FS_DIRTY_IMETA);
+       spin_unlock(&sbi->inode_lock[DIRTY_META]);
+       stat_dec_dirty_inode(F2FS_I_SB(inode), DIRTY_META);
 }
 
 static void f2fs_i_callback(struct rcu_head *head)
@@ -1757,6 +1806,7 @@ try_onemore:
        return 0;
 
 free_kobj:
+       f2fs_sync_inode_meta(sbi);
        kobject_del(&sbi->s_kobj);
        kobject_put(&sbi->s_kobj);
        wait_for_completion(&sbi->s_kobj_unregister);