Merge tag 'f2fs-for-4.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeu...
[linux-2.6-block.git] / fs / f2fs / checkpoint.c
index 0bb8e2c022d3c7b39a0123312d75a9ca7a72abe8..dd2e73e10857a33428c6bfd35eba738d4d232e45 100644 (file)
@@ -29,7 +29,6 @@ struct kmem_cache *inode_entry_slab;
 void f2fs_stop_checkpoint(struct f2fs_sb_info *sbi, bool end_io)
 {
        set_ckpt_flags(sbi, CP_ERROR_FLAG);
-       sbi->sb->s_flags |= MS_RDONLY;
        if (!end_io)
                f2fs_flush_merged_writes(sbi);
 }
@@ -398,24 +397,23 @@ const struct address_space_operations f2fs_meta_aops = {
 #endif
 };
 
-static void __add_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type)
+static void __add_ino_entry(struct f2fs_sb_info *sbi, nid_t ino,
+                                               unsigned int devidx, int type)
 {
        struct inode_management *im = &sbi->im[type];
        struct ino_entry *e, *tmp;
 
        tmp = f2fs_kmem_cache_alloc(ino_entry_slab, GFP_NOFS);
-retry:
+
        radix_tree_preload(GFP_NOFS | __GFP_NOFAIL);
 
        spin_lock(&im->ino_lock);
        e = radix_tree_lookup(&im->ino_root, ino);
        if (!e) {
                e = tmp;
-               if (radix_tree_insert(&im->ino_root, ino, e)) {
-                       spin_unlock(&im->ino_lock);
-                       radix_tree_preload_end();
-                       goto retry;
-               }
+               if (unlikely(radix_tree_insert(&im->ino_root, ino, e)))
+                       f2fs_bug_on(sbi, 1);
+
                memset(e, 0, sizeof(struct ino_entry));
                e->ino = ino;
 
@@ -423,6 +421,10 @@ retry:
                if (type != ORPHAN_INO)
                        im->ino_num++;
        }
+
+       if (type == FLUSH_INO)
+               f2fs_set_bit(devidx, (char *)&e->dirty_device);
+
        spin_unlock(&im->ino_lock);
        radix_tree_preload_end();
 
@@ -451,7 +453,7 @@ static void __remove_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type)
 void add_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type)
 {
        /* add new dirty ino entry into list */
-       __add_ino_entry(sbi, ino, type);
+       __add_ino_entry(sbi, ino, 0, type);
 }
 
 void remove_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type)
@@ -477,7 +479,7 @@ void release_ino_entry(struct f2fs_sb_info *sbi, bool all)
        struct ino_entry *e, *tmp;
        int i;
 
-       for (i = all ? ORPHAN_INO: APPEND_INO; i <= UPDATE_INO; i++) {
+       for (i = all ? ORPHAN_INO : APPEND_INO; i < MAX_INO_ENTRY; i++) {
                struct inode_management *im = &sbi->im[i];
 
                spin_lock(&im->ino_lock);
@@ -491,6 +493,27 @@ void release_ino_entry(struct f2fs_sb_info *sbi, bool all)
        }
 }
 
+void set_dirty_device(struct f2fs_sb_info *sbi, nid_t ino,
+                                       unsigned int devidx, int type)
+{
+       __add_ino_entry(sbi, ino, devidx, type);
+}
+
+bool is_dirty_device(struct f2fs_sb_info *sbi, nid_t ino,
+                                       unsigned int devidx, int type)
+{
+       struct inode_management *im = &sbi->im[type];
+       struct ino_entry *e;
+       bool is_dirty = false;
+
+       spin_lock(&im->ino_lock);
+       e = radix_tree_lookup(&im->ino_root, ino);
+       if (e && f2fs_test_bit(devidx, (char *)&e->dirty_device))
+               is_dirty = true;
+       spin_unlock(&im->ino_lock);
+       return is_dirty;
+}
+
 int acquire_orphan_inode(struct f2fs_sb_info *sbi)
 {
        struct inode_management *im = &sbi->im[ORPHAN_INO];
@@ -527,7 +550,7 @@ void release_orphan_inode(struct f2fs_sb_info *sbi)
 void add_orphan_inode(struct inode *inode)
 {
        /* add new orphan ino entry into list */
-       __add_ino_entry(F2FS_I_SB(inode), inode->i_ino, ORPHAN_INO);
+       __add_ino_entry(F2FS_I_SB(inode), inode->i_ino, 0, ORPHAN_INO);
        update_inode_page(inode);
 }
 
@@ -551,7 +574,7 @@ static int recover_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino)
                return err;
        }
 
-       __add_ino_entry(sbi, ino, ORPHAN_INO);
+       __add_ino_entry(sbi, ino, 0, ORPHAN_INO);
 
        inode = f2fs_iget_retry(sbi->sb, ino);
        if (IS_ERR(inode)) {
@@ -587,6 +610,9 @@ int recover_orphan_inodes(struct f2fs_sb_info *sbi)
        block_t start_blk, orphan_blocks, i, j;
        unsigned int s_flags = sbi->sb->s_flags;
        int err = 0;
+#ifdef CONFIG_QUOTA
+       int quota_enabled;
+#endif
 
        if (!is_set_ckpt_flags(sbi, CP_ORPHAN_PRESENT_FLAG))
                return 0;
@@ -599,8 +625,9 @@ int recover_orphan_inodes(struct f2fs_sb_info *sbi)
 #ifdef CONFIG_QUOTA
        /* Needed for iput() to work correctly and not trash data */
        sbi->sb->s_flags |= MS_ACTIVE;
+
        /* Turn on quotas so that they are updated correctly */
-       f2fs_enable_quota_files(sbi);
+       quota_enabled = f2fs_enable_quota_files(sbi, s_flags & MS_RDONLY);
 #endif
 
        start_blk = __start_cp_addr(sbi) + 1 + __cp_payload(sbi);
@@ -628,7 +655,8 @@ int recover_orphan_inodes(struct f2fs_sb_info *sbi)
 out:
 #ifdef CONFIG_QUOTA
        /* Turn quotas off */
-       f2fs_quota_off_umount(sbi->sb);
+       if (quota_enabled)
+               f2fs_quota_off_umount(sbi->sb);
 #endif
        sbi->sb->s_flags = s_flags; /* Restore MS_RDONLY status */
 
@@ -983,7 +1011,7 @@ int f2fs_sync_inode_meta(struct f2fs_sb_info *sbi)
                                update_inode_page(inode);
                        iput(inode);
                }
-       };
+       }
        return 0;
 }
 
@@ -1143,6 +1171,7 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
        struct super_block *sb = sbi->sb;
        struct curseg_info *seg_i = CURSEG_I(sbi, CURSEG_HOT_NODE);
        u64 kbytes_written;
+       int err;
 
        /* Flush all the NAT/SIT pages */
        while (get_pages(sbi, F2FS_DIRTY_META)) {
@@ -1236,6 +1265,11 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
        if (unlikely(f2fs_cp_error(sbi)))
                return -EIO;
 
+       /* flush all device cache */
+       err = f2fs_flush_device_cache(sbi);
+       if (err)
+               return err;
+
        /* write out checkpoint buffer at block 0 */
        update_meta_page(sbi, ckpt, start_blk++);