f2fs: fix iget/iput of dir during recovery
authorJaegeuk Kim <jaegeuk.kim@samsung.com>
Wed, 5 Jun 2013 08:42:45 +0000 (17:42 +0900)
committerJaegeuk Kim <jaegeuk.kim@samsung.com>
Fri, 7 Jun 2013 04:21:37 +0000 (13:21 +0900)
It is possible that iput is skipped after iget during the recovery.

In recover_dentry(),
 dir = f2fs_iget();
 ...
 if (de && inode->i_ino == le32_to_cpu(de->ino))
goto out;

In this case, this dir is not able to be added in dirty_dir_inode_list.
The actual linking is done only when set_page_dirty() is called.

So let's add this newly got inode into the list explicitly, and put it at the
end of the recovery routine.

Signed-off-by: Jaegeuk Kim <jaegeuk.kim@samsung.com>
fs/f2fs/checkpoint.c
fs/f2fs/f2fs.h
fs/f2fs/recovery.c

index 6f56e57..9a77509 100644 (file)
@@ -450,13 +450,30 @@ fail_no_cp:
        return -EINVAL;
 }
 
-void set_dirty_dir_page(struct inode *inode, struct page *page)
+static int __add_dirty_inode(struct inode *inode, struct dir_inode_entry *new)
 {
        struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
        struct list_head *head = &sbi->dir_inode_list;
-       struct dir_inode_entry *new;
        struct list_head *this;
 
+       list_for_each(this, head) {
+               struct dir_inode_entry *entry;
+               entry = list_entry(this, struct dir_inode_entry, list);
+               if (entry->inode == inode)
+                       return -EEXIST;
+       }
+       list_add_tail(&new->list, head);
+#ifdef CONFIG_F2FS_STAT_FS
+       sbi->n_dirty_dirs++;
+#endif
+       return 0;
+}
+
+void set_dirty_dir_page(struct inode *inode, struct page *page)
+{
+       struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
+       struct dir_inode_entry *new;
+
        if (!S_ISDIR(inode->i_mode))
                return;
 retry:
@@ -469,25 +486,31 @@ retry:
        INIT_LIST_HEAD(&new->list);
 
        spin_lock(&sbi->dir_inode_lock);
-       list_for_each(this, head) {
-               struct dir_inode_entry *entry;
-               entry = list_entry(this, struct dir_inode_entry, list);
-               if (entry->inode == inode) {
-                       kmem_cache_free(inode_entry_slab, new);
-                       goto out;
-               }
-       }
-       list_add_tail(&new->list, head);
-#ifdef CONFIG_F2FS_STAT_FS
-       sbi->n_dirty_dirs++;
-#endif
+       if (__add_dirty_inode(inode, new))
+               kmem_cache_free(inode_entry_slab, new);
 
-       BUG_ON(!S_ISDIR(inode->i_mode));
-out:
        inc_page_count(sbi, F2FS_DIRTY_DENTS);
        inode_inc_dirty_dents(inode);
        SetPagePrivate(page);
+       spin_unlock(&sbi->dir_inode_lock);
+}
+
+void add_dirty_dir_inode(struct inode *inode)
+{
+       struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
+       struct dir_inode_entry *new;
+retry:
+       new = kmem_cache_alloc(inode_entry_slab, GFP_NOFS);
+       if (!new) {
+               cond_resched();
+               goto retry;
+       }
+       new->inode = inode;
+       INIT_LIST_HEAD(&new->list);
 
+       spin_lock(&sbi->dir_inode_lock);
+       if (__add_dirty_inode(inode, new))
+               kmem_cache_free(inode_entry_slab, new);
        spin_unlock(&sbi->dir_inode_lock);
 }
 
index 40b137a..d6e63da 100644 (file)
@@ -1030,6 +1030,7 @@ void remove_orphan_inode(struct f2fs_sb_info *, nid_t);
 int recover_orphan_inodes(struct f2fs_sb_info *);
 int get_valid_checkpoint(struct f2fs_sb_info *);
 void set_dirty_dir_page(struct inode *, struct page *);
+void add_dirty_dir_inode(struct inode *);
 void remove_dirty_dir_inode(struct inode *);
 struct inode *check_dirty_dir_inode(struct f2fs_sb_info *, nid_t);
 void sync_dirty_dir_inodes(struct f2fs_sb_info *);
index 539ca32..ddde14f 100644 (file)
@@ -58,6 +58,7 @@ static int recover_dentry(struct page *ipage, struct inode *inode)
                        goto out;
                }
                set_inode_flag(F2FS_I(dir), FI_DELAY_IPUT);
+               add_dirty_dir_inode(dir);
        }
 
        name.len = le32_to_cpu(raw_inode->i_namelen);