Commit 8ddc4734 authored by Miao Xie's avatar Miao Xie Committed by Chris Mason

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: default avatarMiao Xie <miaox@cn.fujitsu.com>
Signed-off-by: default avatarChris Mason <chris.mason@fusionio.com>
parent 9247f317
...@@ -91,7 +91,7 @@ static int __compare_inode_defrag(struct inode_defrag *defrag1, ...@@ -91,7 +91,7 @@ static int __compare_inode_defrag(struct inode_defrag *defrag1,
* If an existing record is found the defrag item you * If an existing record is found the defrag item you
* pass in is freed * 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 inode_defrag *defrag)
{ {
struct btrfs_root *root = BTRFS_I(inode)->root; struct btrfs_root *root = BTRFS_I(inode)->root;
...@@ -119,18 +119,24 @@ static void __btrfs_add_inode_defrag(struct inode *inode, ...@@ -119,18 +119,24 @@ static void __btrfs_add_inode_defrag(struct inode *inode,
entry->transid = defrag->transid; entry->transid = defrag->transid;
if (defrag->last_offset > entry->last_offset) if (defrag->last_offset > entry->last_offset)
entry->last_offset = defrag->last_offset; entry->last_offset = defrag->last_offset;
goto exists; return -EEXIST;
} }
} }
set_bit(BTRFS_INODE_IN_DEFRAG, &BTRFS_I(inode)->runtime_flags); set_bit(BTRFS_INODE_IN_DEFRAG, &BTRFS_I(inode)->runtime_flags);
rb_link_node(&defrag->rb_node, parent, p); rb_link_node(&defrag->rb_node, parent, p);
rb_insert_color(&defrag->rb_node, &root->fs_info->defrag_inodes); rb_insert_color(&defrag->rb_node, &root->fs_info->defrag_inodes);
return; return 0;
}
exists: static inline int __need_auto_defrag(struct btrfs_root *root)
kmem_cache_free(btrfs_inode_defrag_cachep, defrag); {
return; 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, ...@@ -143,11 +149,9 @@ int btrfs_add_inode_defrag(struct btrfs_trans_handle *trans,
struct btrfs_root *root = BTRFS_I(inode)->root; struct btrfs_root *root = BTRFS_I(inode)->root;
struct inode_defrag *defrag; struct inode_defrag *defrag;
u64 transid; u64 transid;
int ret;
if (!btrfs_test_opt(root, AUTO_DEFRAG)) if (!__need_auto_defrag(root))
return 0;
if (btrfs_fs_closing(root->fs_info))
return 0; return 0;
if (test_bit(BTRFS_INODE_IN_DEFRAG, &BTRFS_I(inode)->runtime_flags)) 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, ...@@ -167,14 +171,50 @@ int btrfs_add_inode_defrag(struct btrfs_trans_handle *trans,
defrag->root = root->root_key.objectid; defrag->root = root->root_key.objectid;
spin_lock(&root->fs_info->defrag_inodes_lock); spin_lock(&root->fs_info->defrag_inodes_lock);
if (!test_bit(BTRFS_INODE_IN_DEFRAG, &BTRFS_I(inode)->runtime_flags)) if (!test_bit(BTRFS_INODE_IN_DEFRAG, &BTRFS_I(inode)->runtime_flags)) {
__btrfs_add_inode_defrag(inode, defrag); /*
else * 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); kmem_cache_free(btrfs_inode_defrag_cachep, defrag);
}
spin_unlock(&root->fs_info->defrag_inodes_lock); spin_unlock(&root->fs_info->defrag_inodes_lock);
return 0; 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 * must be called with the defrag_inodes lock held
*/ */
...@@ -294,7 +334,7 @@ int btrfs_run_defrag_inodes(struct btrfs_fs_info *fs_info) ...@@ -294,7 +334,7 @@ int btrfs_run_defrag_inodes(struct btrfs_fs_info *fs_info)
*/ */
if (num_defrag == defrag_batch) { if (num_defrag == defrag_batch) {
defrag->last_offset = range.start; 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 * we don't want to kfree defrag, we added it back to
* the rbtree * the rbtree
...@@ -308,7 +348,7 @@ int btrfs_run_defrag_inodes(struct btrfs_fs_info *fs_info) ...@@ -308,7 +348,7 @@ int btrfs_run_defrag_inodes(struct btrfs_fs_info *fs_info)
*/ */
defrag->last_offset = 0; defrag->last_offset = 0;
defrag->cycled = 1; defrag->cycled = 1;
__btrfs_add_inode_defrag(inode, defrag); btrfs_requeue_inode_defrag(inode, defrag);
defrag = NULL; defrag = NULL;
} }
......
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