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

Commit cbd56e7d authored by Jaegeuk Kim's avatar Jaegeuk Kim
Browse files

f2fs: fix handling orphan inodes



This patch fixes mishandling of the sbi->n_orphans variable.

If users request lots of f2fs_unlink(), check_orphan_space() could be contended.
In such the case, sbi->n_orphans can be read incorrectly so that f2fs_unlink()
would fall into the wrong state which results in the failure of
add_orphan_inode().

So, let's increment sbi->n_orphans virtually prior to the actual orphan inode
stuffs. After that, let's release sbi->n_orphans by calling release_orphan_inode
or remove_orphan_inode.

Signed-off-by: default avatarJaegeuk Kim <jaegeuk.kim@samsung.com>
parent d8207f69
Loading
Loading
Loading
Loading
+10 −3
Original line number Diff line number Diff line
@@ -182,7 +182,7 @@ const struct address_space_operations f2fs_meta_aops = {
	.set_page_dirty	= f2fs_set_meta_page_dirty,
};

int check_orphan_space(struct f2fs_sb_info *sbi)
int acquire_orphan_inode(struct f2fs_sb_info *sbi)
{
	unsigned int max_orphans;
	int err = 0;
@@ -197,10 +197,19 @@ int check_orphan_space(struct f2fs_sb_info *sbi)
	mutex_lock(&sbi->orphan_inode_mutex);
	if (sbi->n_orphans >= max_orphans)
		err = -ENOSPC;
	else
		sbi->n_orphans++;
	mutex_unlock(&sbi->orphan_inode_mutex);
	return err;
}

void release_orphan_inode(struct f2fs_sb_info *sbi)
{
	mutex_lock(&sbi->orphan_inode_mutex);
	sbi->n_orphans--;
	mutex_unlock(&sbi->orphan_inode_mutex);
}

void add_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino)
{
	struct list_head *head, *this;
@@ -229,8 +238,6 @@ void add_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino)
		list_add(&new->list, this->prev);
	else
		list_add_tail(&new->list, head);

	sbi->n_orphans++;
out:
	mutex_unlock(&sbi->orphan_inode_mutex);
}
+2 −0
Original line number Diff line number Diff line
@@ -572,6 +572,8 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,

		if (inode->i_nlink == 0)
			add_orphan_inode(sbi, inode->i_ino);
		else
			release_orphan_inode(sbi);
	}

	if (bit_pos == NR_DENTRY_IN_BLOCK) {
+2 −1
Original line number Diff line number Diff line
@@ -1044,7 +1044,8 @@ void destroy_segment_manager(struct f2fs_sb_info *);
struct page *grab_meta_page(struct f2fs_sb_info *, pgoff_t);
struct page *get_meta_page(struct f2fs_sb_info *, pgoff_t);
long sync_meta_pages(struct f2fs_sb_info *, enum page_type, long);
int check_orphan_space(struct f2fs_sb_info *);
int acquire_orphan_inode(struct f2fs_sb_info *);
void release_orphan_inode(struct f2fs_sb_info *);
void add_orphan_inode(struct f2fs_sb_info *, nid_t);
void remove_orphan_inode(struct f2fs_sb_info *, nid_t);
int recover_orphan_inodes(struct f2fs_sb_info *);
+14 −5
Original line number Diff line number Diff line
@@ -239,7 +239,7 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry)
	if (!de)
		goto fail;

	err = check_orphan_space(sbi);
	err = acquire_orphan_inode(sbi);
	if (err) {
		kunmap(page);
		f2fs_put_page(page, 0);
@@ -393,7 +393,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
	struct inode *old_inode = old_dentry->d_inode;
	struct inode *new_inode = new_dentry->d_inode;
	struct page *old_dir_page;
	struct page *old_page;
	struct page *old_page, *new_page;
	struct f2fs_dir_entry *old_dir_entry = NULL;
	struct f2fs_dir_entry *old_entry;
	struct f2fs_dir_entry *new_entry;
@@ -415,7 +415,6 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
	ilock = mutex_lock_op(sbi);

	if (new_inode) {
		struct page *new_page;

		err = -ENOTEMPTY;
		if (old_dir_entry && !f2fs_empty_dir(new_inode))
@@ -427,9 +426,13 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
		if (!new_entry)
			goto out_dir;

		err = acquire_orphan_inode(sbi);
		if (err)
			goto put_out_dir;

		if (update_dent_inode(old_inode, &new_dentry->d_name)) {
			f2fs_put_page(new_page, 1);
			goto out_dir;
			release_orphan_inode(sbi);
			goto put_out_dir;
		}

		f2fs_set_link(new_dir, new_entry, new_page, old_inode);
@@ -438,8 +441,12 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
		if (old_dir_entry)
			drop_nlink(new_inode);
		drop_nlink(new_inode);

		if (!new_inode->i_nlink)
			add_orphan_inode(sbi, new_inode->i_ino);
		else
			release_orphan_inode(sbi);

		update_inode_page(new_inode);
	} else {
		err = f2fs_add_link(new_dentry, old_inode);
@@ -472,6 +479,8 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
	mutex_unlock_op(sbi, ilock);
	return 0;

put_out_dir:
	f2fs_put_page(new_page, 1);
out_dir:
	if (old_dir_entry) {
		kunmap(old_dir_page);