Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5deb8267 authored by Jaegeuk Kim's avatar Jaegeuk Kim
Browse files

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: default avatarJaegeuk Kim <jaegeuk.kim@samsung.com>
parent b2b3460a
Loading
Loading
Loading
Loading
+39 −16
Original line number Diff line number Diff line
@@ -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) {
	if (__add_dirty_inode(inode, new))
		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);
	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);
}

+1 −0
Original line number Diff line number Diff line
@@ -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 *);
+1 −0
Original line number Diff line number Diff line
@@ -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);