Merge branch 'linux-4.7' of git://github.com/skeggsb/linux into drm-fixes
[linux-2.6-block.git] / fs / f2fs / recovery.c
index 011942f94d6494ff5da99a36967b3e8ba8ff6140..3d7216d7a28847090a1d2bcef934fe7a14afae80 100644 (file)
@@ -49,8 +49,9 @@ static struct kmem_cache *fsync_entry_slab;
 
 bool space_for_roll_forward(struct f2fs_sb_info *sbi)
 {
-       if (sbi->last_valid_block_count + sbi->alloc_valid_block_count
-                       > sbi->user_block_count)
+       s64 nalloc = percpu_counter_sum_positive(&sbi->alloc_valid_block_count);
+
+       if (sbi->last_valid_block_count + nalloc > sbi->user_block_count)
                return false;
        return true;
 }
@@ -67,7 +68,30 @@ static struct fsync_inode_entry *get_fsync_inode(struct list_head *head,
        return NULL;
 }
 
-static int recover_dentry(struct inode *inode, struct page *ipage)
+static struct fsync_inode_entry *add_fsync_inode(struct list_head *head,
+                                                       struct inode *inode)
+{
+       struct fsync_inode_entry *entry;
+
+       entry = kmem_cache_alloc(fsync_entry_slab, GFP_F2FS_ZERO);
+       if (!entry)
+               return NULL;
+
+       entry->inode = inode;
+       list_add_tail(&entry->list, head);
+
+       return entry;
+}
+
+static void del_fsync_inode(struct fsync_inode_entry *entry)
+{
+       iput(entry->inode);
+       list_del(&entry->list);
+       kmem_cache_free(fsync_entry_slab, entry);
+}
+
+static int recover_dentry(struct inode *inode, struct page *ipage,
+                                               struct list_head *dir_list)
 {
        struct f2fs_inode *raw_inode = F2FS_INODE(ipage);
        nid_t pino = le32_to_cpu(raw_inode->i_pino);
@@ -75,18 +99,29 @@ static int recover_dentry(struct inode *inode, struct page *ipage)
        struct qstr name;
        struct page *page;
        struct inode *dir, *einode;
+       struct fsync_inode_entry *entry;
        int err = 0;
 
-       dir = f2fs_iget(inode->i_sb, pino);
-       if (IS_ERR(dir)) {
-               err = PTR_ERR(dir);
-               goto out;
+       entry = get_fsync_inode(dir_list, pino);
+       if (!entry) {
+               dir = f2fs_iget(inode->i_sb, pino);
+               if (IS_ERR(dir)) {
+                       err = PTR_ERR(dir);
+                       goto out;
+               }
+
+               entry = add_fsync_inode(dir_list, dir);
+               if (!entry) {
+                       err = -ENOMEM;
+                       iput(dir);
+                       goto out;
+               }
        }
 
-       if (file_enc_name(inode)) {
-               iput(dir);
+       dir = entry->inode;
+
+       if (file_enc_name(inode))
                return 0;
-       }
 
        name.len = le32_to_cpu(raw_inode->i_namelen);
        name.name = raw_inode->i_name;
@@ -94,7 +129,7 @@ static int recover_dentry(struct inode *inode, struct page *ipage)
        if (unlikely(name.len > F2FS_NAME_LEN)) {
                WARN_ON(1);
                err = -ENAMETOOLONG;
-               goto out_err;
+               goto out;
        }
 retry:
        de = f2fs_find_entry(dir, &name, &page);
@@ -120,23 +155,12 @@ retry:
                goto retry;
        }
        err = __f2fs_add_link(dir, &name, inode, inode->i_ino, inode->i_mode);
-       if (err)
-               goto out_err;
-
-       if (is_inode_flag_set(F2FS_I(dir), FI_DELAY_IPUT)) {
-               iput(dir);
-       } else {
-               add_dirty_dir_inode(dir);
-               set_inode_flag(F2FS_I(dir), FI_DELAY_IPUT);
-       }
 
        goto out;
 
 out_unmap_put:
        f2fs_dentry_kunmap(dir, page);
        f2fs_put_page(page, 0);
-out_err:
-       iput(dir);
 out:
        f2fs_msg(inode->i_sb, KERN_NOTICE,
                        "%s: ino = %x, name = %s, dir = %lx, err = %d",
@@ -198,6 +222,7 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
 {
        unsigned long long cp_ver = cur_cp_version(F2FS_CKPT(sbi));
        struct curseg_info *curseg;
+       struct inode *inode;
        struct page *page = NULL;
        block_t blkaddr;
        int err = 0;
@@ -206,8 +231,6 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
        curseg = CURSEG_I(sbi, CURSEG_WARM_NODE);
        blkaddr = NEXT_FREE_BLKADDR(sbi, curseg);
 
-       ra_meta_pages(sbi, blkaddr, 1, META_POR, true);
-
        while (1) {
                struct fsync_inode_entry *entry;
 
@@ -233,35 +256,32 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
                                        break;
                        }
 
-                       /* add this fsync inode to the list */
-                       entry = kmem_cache_alloc(fsync_entry_slab, GFP_F2FS_ZERO);
-                       if (!entry) {
-                               err = -ENOMEM;
-                               break;
-                       }
                        /*
                         * CP | dnode(F) | inode(DF)
                         * For this case, we should not give up now.
                         */
-                       entry->inode = f2fs_iget(sbi->sb, ino_of_node(page));
-                       if (IS_ERR(entry->inode)) {
-                               err = PTR_ERR(entry->inode);
-                               kmem_cache_free(fsync_entry_slab, entry);
+                       inode = f2fs_iget(sbi->sb, ino_of_node(page));
+                       if (IS_ERR(inode)) {
+                               err = PTR_ERR(inode);
                                if (err == -ENOENT) {
                                        err = 0;
                                        goto next;
                                }
                                break;
                        }
-                       list_add_tail(&entry->list, head);
+
+                       /* add this fsync inode to the list */
+                       entry = add_fsync_inode(head, inode);
+                       if (!entry) {
+                               err = -ENOMEM;
+                               iput(inode);
+                               break;
+                       }
                }
                entry->blkaddr = blkaddr;
 
-               if (IS_INODE(page)) {
-                       entry->last_inode = blkaddr;
-                       if (is_dent_dnode(page))
-                               entry->last_dentry = blkaddr;
-               }
+               if (IS_INODE(page) && is_dent_dnode(page))
+                       entry->last_dentry = blkaddr;
 next:
                /* check next segment */
                blkaddr = next_blkaddr_of_node(page);
@@ -277,11 +297,8 @@ static void destroy_fsync_dnodes(struct list_head *head)
 {
        struct fsync_inode_entry *entry, *tmp;
 
-       list_for_each_entry_safe(entry, tmp, head, list) {
-               iput(entry->inode);
-               list_del(&entry->list);
-               kmem_cache_free(fsync_entry_slab, entry);
-       }
+       list_for_each_entry_safe(entry, tmp, head, list)
+               del_fsync_inode(entry);
 }
 
 static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi,
@@ -444,8 +461,7 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
                 */
                if (dest == NEW_ADDR) {
                        truncate_data_blocks_range(&dn, 1);
-                       err = reserve_new_block(&dn);
-                       f2fs_bug_on(sbi, err);
+                       reserve_new_block(&dn);
                        continue;
                }
 
@@ -454,6 +470,10 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
 
                        if (src == NULL_ADDR) {
                                err = reserve_new_block(&dn);
+#ifdef CONFIG_F2FS_FAULT_INJECTION
+                               while (err)
+                                       err = reserve_new_block(&dn);
+#endif
                                /* We should not get -ENOSPC */
                                f2fs_bug_on(sbi, err);
                        }
@@ -486,7 +506,8 @@ out:
        return err;
 }
 
-static int recover_data(struct f2fs_sb_info *sbi, struct list_head *head)
+static int recover_data(struct f2fs_sb_info *sbi, struct list_head *inode_list,
+                                               struct list_head *dir_list)
 {
        unsigned long long cp_ver = cur_cp_version(F2FS_CKPT(sbi));
        struct curseg_info *curseg;
@@ -513,7 +534,7 @@ static int recover_data(struct f2fs_sb_info *sbi, struct list_head *head)
                        break;
                }
 
-               entry = get_fsync_inode(head, ino_of_node(page));
+               entry = get_fsync_inode(inode_list, ino_of_node(page));
                if (!entry)
                        goto next;
                /*
@@ -521,10 +542,10 @@ static int recover_data(struct f2fs_sb_info *sbi, struct list_head *head)
                 * In this case, we can lose the latest inode(x).
                 * So, call recover_inode for the inode update.
                 */
-               if (entry->last_inode == blkaddr)
+               if (IS_INODE(page))
                        recover_inode(entry->inode, page);
                if (entry->last_dentry == blkaddr) {
-                       err = recover_dentry(entry->inode, page);
+                       err = recover_dentry(entry->inode, page, dir_list);
                        if (err) {
                                f2fs_put_page(page, 1);
                                break;
@@ -536,11 +557,8 @@ static int recover_data(struct f2fs_sb_info *sbi, struct list_head *head)
                        break;
                }
 
-               if (entry->blkaddr == blkaddr) {
-                       iput(entry->inode);
-                       list_del(&entry->list);
-                       kmem_cache_free(fsync_entry_slab, entry);
-               }
+               if (entry->blkaddr == blkaddr)
+                       del_fsync_inode(entry);
 next:
                /* check next segment */
                blkaddr = next_blkaddr_of_node(page);
@@ -551,12 +569,14 @@ next:
        return err;
 }
 
-int recover_fsync_data(struct f2fs_sb_info *sbi)
+int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only)
 {
        struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_WARM_NODE);
        struct list_head inode_list;
+       struct list_head dir_list;
        block_t blkaddr;
        int err;
+       int ret = 0;
        bool need_writecp = false;
 
        fsync_entry_slab = f2fs_kmem_cache_create("f2fs_fsync_inode_entry",
@@ -565,6 +585,7 @@ int recover_fsync_data(struct f2fs_sb_info *sbi)
                return -ENOMEM;
 
        INIT_LIST_HEAD(&inode_list);
+       INIT_LIST_HEAD(&dir_list);
 
        /* prevent checkpoint */
        mutex_lock(&sbi->cp_mutex);
@@ -573,21 +594,22 @@ int recover_fsync_data(struct f2fs_sb_info *sbi)
 
        /* step #1: find fsynced inode numbers */
        err = find_fsync_dnodes(sbi, &inode_list);
-       if (err)
+       if (err || list_empty(&inode_list))
                goto out;
 
-       if (list_empty(&inode_list))
+       if (check_only) {
+               ret = 1;
                goto out;
+       }
 
        need_writecp = true;
 
        /* step #2: recover data */
-       err = recover_data(sbi, &inode_list);
+       err = recover_data(sbi, &inode_list, &dir_list);
        if (!err)
                f2fs_bug_on(sbi, !list_empty(&inode_list));
 out:
        destroy_fsync_dnodes(&inode_list);
-       kmem_cache_destroy(fsync_entry_slab);
 
        /* truncate meta pages to be used by the recovery */
        truncate_inode_pages_range(META_MAPPING(sbi),
@@ -625,5 +647,8 @@ out:
        } else {
                mutex_unlock(&sbi->cp_mutex);
        }
-       return err;
+
+       destroy_fsync_dnodes(&dir_list);
+       kmem_cache_destroy(fsync_entry_slab);
+       return ret ? ret: err;
 }