f2fs: fix iget/iput of dir during recovery
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>
This commit is contained in:
parent
b2b3460a94
commit
5deb82671a
|
@ -450,13 +450,30 @@ fail_no_cp:
|
||||||
return -EINVAL;
|
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 f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
|
||||||
struct list_head *head = &sbi->dir_inode_list;
|
struct list_head *head = &sbi->dir_inode_list;
|
||||||
struct dir_inode_entry *new;
|
|
||||||
struct list_head *this;
|
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))
|
if (!S_ISDIR(inode->i_mode))
|
||||||
return;
|
return;
|
||||||
retry:
|
retry:
|
||||||
|
@ -469,25 +486,31 @@ retry:
|
||||||
INIT_LIST_HEAD(&new->list);
|
INIT_LIST_HEAD(&new->list);
|
||||||
|
|
||||||
spin_lock(&sbi->dir_inode_lock);
|
spin_lock(&sbi->dir_inode_lock);
|
||||||
list_for_each(this, head) {
|
if (__add_dirty_inode(inode, new))
|
||||||
struct dir_inode_entry *entry;
|
kmem_cache_free(inode_entry_slab, new);
|
||||||
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
|
|
||||||
|
|
||||||
BUG_ON(!S_ISDIR(inode->i_mode));
|
|
||||||
out:
|
|
||||||
inc_page_count(sbi, F2FS_DIRTY_DENTS);
|
inc_page_count(sbi, F2FS_DIRTY_DENTS);
|
||||||
inode_inc_dirty_dents(inode);
|
inode_inc_dirty_dents(inode);
|
||||||
SetPagePrivate(page);
|
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);
|
spin_unlock(&sbi->dir_inode_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1030,6 +1030,7 @@ void remove_orphan_inode(struct f2fs_sb_info *, nid_t);
|
||||||
int recover_orphan_inodes(struct f2fs_sb_info *);
|
int recover_orphan_inodes(struct f2fs_sb_info *);
|
||||||
int get_valid_checkpoint(struct f2fs_sb_info *);
|
int get_valid_checkpoint(struct f2fs_sb_info *);
|
||||||
void set_dirty_dir_page(struct inode *, struct page *);
|
void set_dirty_dir_page(struct inode *, struct page *);
|
||||||
|
void add_dirty_dir_inode(struct inode *);
|
||||||
void remove_dirty_dir_inode(struct inode *);
|
void remove_dirty_dir_inode(struct inode *);
|
||||||
struct inode *check_dirty_dir_inode(struct f2fs_sb_info *, nid_t);
|
struct inode *check_dirty_dir_inode(struct f2fs_sb_info *, nid_t);
|
||||||
void sync_dirty_dir_inodes(struct f2fs_sb_info *);
|
void sync_dirty_dir_inodes(struct f2fs_sb_info *);
|
||||||
|
|
|
@ -58,6 +58,7 @@ static int recover_dentry(struct page *ipage, struct inode *inode)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
set_inode_flag(F2FS_I(dir), FI_DELAY_IPUT);
|
set_inode_flag(F2FS_I(dir), FI_DELAY_IPUT);
|
||||||
|
add_dirty_dir_inode(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
name.len = le32_to_cpu(raw_inode->i_namelen);
|
name.len = le32_to_cpu(raw_inode->i_namelen);
|
||||||
|
|
Loading…
Reference in New Issue