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: Jaegeuk Kim <jaegeuk.kim@samsung.com>
This commit is contained in:
@@ -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)
|
|||||||
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 @@ retry:
|
|||||||
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,
|
|||||||
|
|
||||||
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 *);
|
|||||||
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)
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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);
|
||||||
|
Reference in New Issue
Block a user