Btrfs: Add delayed iput
iput() can trigger new transactions if we are dropping the final reference, so calling it in btrfs_commit_transaction may end up deadlock. This patch adds delayed iput to avoid the issue. Signed-off-by: Yan Zheng <zheng.yan@oracle.com> Signed-off-by: Chris Mason <chris.mason@oracle.com>
This commit is contained in:
@@ -2022,6 +2022,54 @@ zeroit:
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
struct delayed_iput {
|
||||
struct list_head list;
|
||||
struct inode *inode;
|
||||
};
|
||||
|
||||
void btrfs_add_delayed_iput(struct inode *inode)
|
||||
{
|
||||
struct btrfs_fs_info *fs_info = BTRFS_I(inode)->root->fs_info;
|
||||
struct delayed_iput *delayed;
|
||||
|
||||
if (atomic_add_unless(&inode->i_count, -1, 1))
|
||||
return;
|
||||
|
||||
delayed = kmalloc(sizeof(*delayed), GFP_NOFS | __GFP_NOFAIL);
|
||||
delayed->inode = inode;
|
||||
|
||||
spin_lock(&fs_info->delayed_iput_lock);
|
||||
list_add_tail(&delayed->list, &fs_info->delayed_iputs);
|
||||
spin_unlock(&fs_info->delayed_iput_lock);
|
||||
}
|
||||
|
||||
void btrfs_run_delayed_iputs(struct btrfs_root *root)
|
||||
{
|
||||
LIST_HEAD(list);
|
||||
struct btrfs_fs_info *fs_info = root->fs_info;
|
||||
struct delayed_iput *delayed;
|
||||
int empty;
|
||||
|
||||
spin_lock(&fs_info->delayed_iput_lock);
|
||||
empty = list_empty(&fs_info->delayed_iputs);
|
||||
spin_unlock(&fs_info->delayed_iput_lock);
|
||||
if (empty)
|
||||
return;
|
||||
|
||||
down_read(&root->fs_info->cleanup_work_sem);
|
||||
spin_lock(&fs_info->delayed_iput_lock);
|
||||
list_splice_init(&fs_info->delayed_iputs, &list);
|
||||
spin_unlock(&fs_info->delayed_iput_lock);
|
||||
|
||||
while (!list_empty(&list)) {
|
||||
delayed = list_entry(list.next, struct delayed_iput, list);
|
||||
list_del(&delayed->list);
|
||||
iput(delayed->inode);
|
||||
kfree(delayed);
|
||||
}
|
||||
up_read(&root->fs_info->cleanup_work_sem);
|
||||
}
|
||||
|
||||
/*
|
||||
* This creates an orphan entry for the given inode in case something goes
|
||||
* wrong in the middle of an unlink/truncate.
|
||||
@@ -5568,7 +5616,7 @@ out_fail:
|
||||
* some fairly slow code that needs optimization. This walks the list
|
||||
* of all the inodes with pending delalloc and forces them to disk.
|
||||
*/
|
||||
int btrfs_start_delalloc_inodes(struct btrfs_root *root)
|
||||
int btrfs_start_delalloc_inodes(struct btrfs_root *root, int delay_iput)
|
||||
{
|
||||
struct list_head *head = &root->fs_info->delalloc_inodes;
|
||||
struct btrfs_inode *binode;
|
||||
@@ -5587,7 +5635,10 @@ int btrfs_start_delalloc_inodes(struct btrfs_root *root)
|
||||
spin_unlock(&root->fs_info->delalloc_lock);
|
||||
if (inode) {
|
||||
filemap_flush(inode->i_mapping);
|
||||
iput(inode);
|
||||
if (delay_iput)
|
||||
btrfs_add_delayed_iput(inode);
|
||||
else
|
||||
iput(inode);
|
||||
}
|
||||
cond_resched();
|
||||
spin_lock(&root->fs_info->delalloc_lock);
|
||||
|
Reference in New Issue
Block a user