mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-21 16:29:59 +00:00
btrfs: fix inode rbtree corruption
Node may not be inserted over existing node. This causes inode tree corruption and I was seeing crashes in inode_tree_del which I can not reproduce after this patch. The other way to fix this would be to tie inode lifetime in the rbtree with inode while not in freeing state. I had a look at this but it is not so trivial at this point. At least this patch gets things working again. Signed-off-by: Nick Piggin <npiggin@suse.de> Cc: Chris Mason <chris.mason@oracle.com> Acked-by: Yan Zheng <zheng.yan@oracle.com> Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
This commit is contained in:
parent
df4ecf1524
commit
03e860bd9f
1 changed files with 14 additions and 7 deletions
|
@ -3099,8 +3099,12 @@ static void inode_tree_add(struct inode *inode)
|
||||||
{
|
{
|
||||||
struct btrfs_root *root = BTRFS_I(inode)->root;
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
||||||
struct btrfs_inode *entry;
|
struct btrfs_inode *entry;
|
||||||
struct rb_node **p = &root->inode_tree.rb_node;
|
struct rb_node **p;
|
||||||
struct rb_node *parent = NULL;
|
struct rb_node *parent;
|
||||||
|
|
||||||
|
again:
|
||||||
|
p = &root->inode_tree.rb_node;
|
||||||
|
parent = NULL;
|
||||||
|
|
||||||
spin_lock(&root->inode_lock);
|
spin_lock(&root->inode_lock);
|
||||||
while (*p) {
|
while (*p) {
|
||||||
|
@ -3108,13 +3112,16 @@ static void inode_tree_add(struct inode *inode)
|
||||||
entry = rb_entry(parent, struct btrfs_inode, rb_node);
|
entry = rb_entry(parent, struct btrfs_inode, rb_node);
|
||||||
|
|
||||||
if (inode->i_ino < entry->vfs_inode.i_ino)
|
if (inode->i_ino < entry->vfs_inode.i_ino)
|
||||||
p = &(*p)->rb_left;
|
p = &parent->rb_left;
|
||||||
else if (inode->i_ino > entry->vfs_inode.i_ino)
|
else if (inode->i_ino > entry->vfs_inode.i_ino)
|
||||||
p = &(*p)->rb_right;
|
p = &parent->rb_right;
|
||||||
else {
|
else {
|
||||||
WARN_ON(!(entry->vfs_inode.i_state &
|
WARN_ON(!(entry->vfs_inode.i_state &
|
||||||
(I_WILL_FREE | I_FREEING | I_CLEAR)));
|
(I_WILL_FREE | I_FREEING | I_CLEAR)));
|
||||||
break;
|
rb_erase(parent, &root->inode_tree);
|
||||||
|
RB_CLEAR_NODE(parent);
|
||||||
|
spin_unlock(&root->inode_lock);
|
||||||
|
goto again;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rb_link_node(&BTRFS_I(inode)->rb_node, parent, p);
|
rb_link_node(&BTRFS_I(inode)->rb_node, parent, p);
|
||||||
|
@ -3126,12 +3133,12 @@ static void inode_tree_del(struct inode *inode)
|
||||||
{
|
{
|
||||||
struct btrfs_root *root = BTRFS_I(inode)->root;
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
||||||
|
|
||||||
|
spin_lock(&root->inode_lock);
|
||||||
if (!RB_EMPTY_NODE(&BTRFS_I(inode)->rb_node)) {
|
if (!RB_EMPTY_NODE(&BTRFS_I(inode)->rb_node)) {
|
||||||
spin_lock(&root->inode_lock);
|
|
||||||
rb_erase(&BTRFS_I(inode)->rb_node, &root->inode_tree);
|
rb_erase(&BTRFS_I(inode)->rb_node, &root->inode_tree);
|
||||||
spin_unlock(&root->inode_lock);
|
|
||||||
RB_CLEAR_NODE(&BTRFS_I(inode)->rb_node);
|
RB_CLEAR_NODE(&BTRFS_I(inode)->rb_node);
|
||||||
}
|
}
|
||||||
|
spin_unlock(&root->inode_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static noinline void init_btrfs_i(struct inode *inode)
|
static noinline void init_btrfs_i(struct inode *inode)
|
||||||
|
|
Loading…
Reference in a new issue