Commit cbd56e7d authored by Jaegeuk Kim's avatar Jaegeuk Kim

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
...@@ -182,7 +182,7 @@ const struct address_space_operations f2fs_meta_aops = { ...@@ -182,7 +182,7 @@ const struct address_space_operations f2fs_meta_aops = {
.set_page_dirty = f2fs_set_meta_page_dirty, .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; unsigned int max_orphans;
int err = 0; int err = 0;
...@@ -197,10 +197,19 @@ int check_orphan_space(struct f2fs_sb_info *sbi) ...@@ -197,10 +197,19 @@ int check_orphan_space(struct f2fs_sb_info *sbi)
mutex_lock(&sbi->orphan_inode_mutex); mutex_lock(&sbi->orphan_inode_mutex);
if (sbi->n_orphans >= max_orphans) if (sbi->n_orphans >= max_orphans)
err = -ENOSPC; err = -ENOSPC;
else
sbi->n_orphans++;
mutex_unlock(&sbi->orphan_inode_mutex); mutex_unlock(&sbi->orphan_inode_mutex);
return err; 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) void add_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino)
{ {
struct list_head *head, *this; struct list_head *head, *this;
...@@ -229,8 +238,6 @@ void add_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino) ...@@ -229,8 +238,6 @@ void add_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino)
list_add(&new->list, this->prev); list_add(&new->list, this->prev);
else else
list_add_tail(&new->list, head); list_add_tail(&new->list, head);
sbi->n_orphans++;
out: out:
mutex_unlock(&sbi->orphan_inode_mutex); mutex_unlock(&sbi->orphan_inode_mutex);
} }
......
...@@ -572,6 +572,8 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page, ...@@ -572,6 +572,8 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
if (inode->i_nlink == 0) if (inode->i_nlink == 0)
add_orphan_inode(sbi, inode->i_ino); add_orphan_inode(sbi, inode->i_ino);
else
release_orphan_inode(sbi);
} }
if (bit_pos == NR_DENTRY_IN_BLOCK) { if (bit_pos == NR_DENTRY_IN_BLOCK) {
......
...@@ -1044,7 +1044,8 @@ void destroy_segment_manager(struct f2fs_sb_info *); ...@@ -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 *grab_meta_page(struct f2fs_sb_info *, pgoff_t);
struct page *get_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); 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 add_orphan_inode(struct f2fs_sb_info *, nid_t);
void remove_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 *); int recover_orphan_inodes(struct f2fs_sb_info *);
......
...@@ -239,7 +239,7 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -239,7 +239,7 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry)
if (!de) if (!de)
goto fail; goto fail;
err = check_orphan_space(sbi); err = acquire_orphan_inode(sbi);
if (err) { if (err) {
kunmap(page); kunmap(page);
f2fs_put_page(page, 0); f2fs_put_page(page, 0);
...@@ -393,7 +393,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -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 *old_inode = old_dentry->d_inode;
struct inode *new_inode = new_dentry->d_inode; struct inode *new_inode = new_dentry->d_inode;
struct page *old_dir_page; 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_dir_entry = NULL;
struct f2fs_dir_entry *old_entry; struct f2fs_dir_entry *old_entry;
struct f2fs_dir_entry *new_entry; struct f2fs_dir_entry *new_entry;
...@@ -415,7 +415,6 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -415,7 +415,6 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
ilock = mutex_lock_op(sbi); ilock = mutex_lock_op(sbi);
if (new_inode) { if (new_inode) {
struct page *new_page;
err = -ENOTEMPTY; err = -ENOTEMPTY;
if (old_dir_entry && !f2fs_empty_dir(new_inode)) 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, ...@@ -427,9 +426,13 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (!new_entry) if (!new_entry)
goto out_dir; goto out_dir;
err = acquire_orphan_inode(sbi);
if (err)
goto put_out_dir;
if (update_dent_inode(old_inode, &new_dentry->d_name)) { if (update_dent_inode(old_inode, &new_dentry->d_name)) {
f2fs_put_page(new_page, 1); release_orphan_inode(sbi);
goto out_dir; goto put_out_dir;
} }
f2fs_set_link(new_dir, new_entry, new_page, old_inode); 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, ...@@ -438,8 +441,12 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (old_dir_entry) if (old_dir_entry)
drop_nlink(new_inode); drop_nlink(new_inode);
drop_nlink(new_inode); drop_nlink(new_inode);
if (!new_inode->i_nlink) if (!new_inode->i_nlink)
add_orphan_inode(sbi, new_inode->i_ino); add_orphan_inode(sbi, new_inode->i_ino);
else
release_orphan_inode(sbi);
update_inode_page(new_inode); update_inode_page(new_inode);
} else { } else {
err = f2fs_add_link(new_dentry, old_inode); err = f2fs_add_link(new_dentry, old_inode);
...@@ -472,6 +479,8 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -472,6 +479,8 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
mutex_unlock_op(sbi, ilock); mutex_unlock_op(sbi, ilock);
return 0; return 0;
put_out_dir:
f2fs_put_page(new_page, 1);
out_dir: out_dir:
if (old_dir_entry) { if (old_dir_entry) {
kunmap(old_dir_page); kunmap(old_dir_page);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment