mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-25 05:04:09 +08:00
Btrfs: fix unprotected defragable inode insertion
We forget to get the defrag lock when we re-add the defragable inode, Fix it. Signed-off-by: Miao Xie <miaox@cn.fujitsu.com> Signed-off-by: Chris Mason <chris.mason@fusionio.com>
This commit is contained in:
parent
9247f3170b
commit
8ddc473433
@ -91,7 +91,7 @@ static int __compare_inode_defrag(struct inode_defrag *defrag1,
|
||||
* If an existing record is found the defrag item you
|
||||
* pass in is freed
|
||||
*/
|
||||
static void __btrfs_add_inode_defrag(struct inode *inode,
|
||||
static int __btrfs_add_inode_defrag(struct inode *inode,
|
||||
struct inode_defrag *defrag)
|
||||
{
|
||||
struct btrfs_root *root = BTRFS_I(inode)->root;
|
||||
@ -119,18 +119,24 @@ static void __btrfs_add_inode_defrag(struct inode *inode,
|
||||
entry->transid = defrag->transid;
|
||||
if (defrag->last_offset > entry->last_offset)
|
||||
entry->last_offset = defrag->last_offset;
|
||||
goto exists;
|
||||
return -EEXIST;
|
||||
}
|
||||
}
|
||||
set_bit(BTRFS_INODE_IN_DEFRAG, &BTRFS_I(inode)->runtime_flags);
|
||||
rb_link_node(&defrag->rb_node, parent, p);
|
||||
rb_insert_color(&defrag->rb_node, &root->fs_info->defrag_inodes);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
exists:
|
||||
kmem_cache_free(btrfs_inode_defrag_cachep, defrag);
|
||||
return;
|
||||
static inline int __need_auto_defrag(struct btrfs_root *root)
|
||||
{
|
||||
if (!btrfs_test_opt(root, AUTO_DEFRAG))
|
||||
return 0;
|
||||
|
||||
if (btrfs_fs_closing(root->fs_info))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -143,11 +149,9 @@ int btrfs_add_inode_defrag(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root = BTRFS_I(inode)->root;
|
||||
struct inode_defrag *defrag;
|
||||
u64 transid;
|
||||
int ret;
|
||||
|
||||
if (!btrfs_test_opt(root, AUTO_DEFRAG))
|
||||
return 0;
|
||||
|
||||
if (btrfs_fs_closing(root->fs_info))
|
||||
if (!__need_auto_defrag(root))
|
||||
return 0;
|
||||
|
||||
if (test_bit(BTRFS_INODE_IN_DEFRAG, &BTRFS_I(inode)->runtime_flags))
|
||||
@ -167,14 +171,50 @@ int btrfs_add_inode_defrag(struct btrfs_trans_handle *trans,
|
||||
defrag->root = root->root_key.objectid;
|
||||
|
||||
spin_lock(&root->fs_info->defrag_inodes_lock);
|
||||
if (!test_bit(BTRFS_INODE_IN_DEFRAG, &BTRFS_I(inode)->runtime_flags))
|
||||
__btrfs_add_inode_defrag(inode, defrag);
|
||||
else
|
||||
if (!test_bit(BTRFS_INODE_IN_DEFRAG, &BTRFS_I(inode)->runtime_flags)) {
|
||||
/*
|
||||
* If we set IN_DEFRAG flag and evict the inode from memory,
|
||||
* and then re-read this inode, this new inode doesn't have
|
||||
* IN_DEFRAG flag. At the case, we may find the existed defrag.
|
||||
*/
|
||||
ret = __btrfs_add_inode_defrag(inode, defrag);
|
||||
if (ret)
|
||||
kmem_cache_free(btrfs_inode_defrag_cachep, defrag);
|
||||
} else {
|
||||
kmem_cache_free(btrfs_inode_defrag_cachep, defrag);
|
||||
}
|
||||
spin_unlock(&root->fs_info->defrag_inodes_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Requeue the defrag object. If there is a defrag object that points to
|
||||
* the same inode in the tree, we will merge them together (by
|
||||
* __btrfs_add_inode_defrag()) and free the one that we want to requeue.
|
||||
*/
|
||||
void btrfs_requeue_inode_defrag(struct inode *inode,
|
||||
struct inode_defrag *defrag)
|
||||
{
|
||||
struct btrfs_root *root = BTRFS_I(inode)->root;
|
||||
int ret;
|
||||
|
||||
if (!__need_auto_defrag(root))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Here we don't check the IN_DEFRAG flag, because we need merge
|
||||
* them together.
|
||||
*/
|
||||
spin_lock(&root->fs_info->defrag_inodes_lock);
|
||||
ret = __btrfs_add_inode_defrag(inode, defrag);
|
||||
spin_unlock(&root->fs_info->defrag_inodes_lock);
|
||||
if (ret)
|
||||
goto out;
|
||||
return;
|
||||
out:
|
||||
kmem_cache_free(btrfs_inode_defrag_cachep, defrag);
|
||||
}
|
||||
|
||||
/*
|
||||
* must be called with the defrag_inodes lock held
|
||||
*/
|
||||
@ -294,7 +334,7 @@ int btrfs_run_defrag_inodes(struct btrfs_fs_info *fs_info)
|
||||
*/
|
||||
if (num_defrag == defrag_batch) {
|
||||
defrag->last_offset = range.start;
|
||||
__btrfs_add_inode_defrag(inode, defrag);
|
||||
btrfs_requeue_inode_defrag(inode, defrag);
|
||||
/*
|
||||
* we don't want to kfree defrag, we added it back to
|
||||
* the rbtree
|
||||
@ -308,7 +348,7 @@ int btrfs_run_defrag_inodes(struct btrfs_fs_info *fs_info)
|
||||
*/
|
||||
defrag->last_offset = 0;
|
||||
defrag->cycled = 1;
|
||||
__btrfs_add_inode_defrag(inode, defrag);
|
||||
btrfs_requeue_inode_defrag(inode, defrag);
|
||||
defrag = NULL;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user