Skip to content

Commit

Permalink
Btrfs: fix possible panic on unmount
Browse files Browse the repository at this point in the history
We can race with the unmount of an fs and the stopping of a kthread where we
will free the block group before we're done using it.  The reason for this is
because we do not hold a reference on the block group while its caching, since
the allocator drops its reference once it exits or moves on to the next block
group.  This patch fixes the problem by taking a reference to the block group
before we start caching and dropping it when we're done to make sure all
accesses to the block group are safe.  Thanks,

Signed-off-by: Josef Bacik <josef@redhat.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
  • Loading branch information
Josef Bacik authored and Chris Mason committed Jan 18, 2010
1 parent a9cc71a commit 11dfe35
Showing 1 changed file with 19 additions and 13 deletions.
32 changes: 19 additions & 13 deletions fs/btrfs/extent-tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,17 @@ static int block_group_bits(struct btrfs_block_group_cache *cache, u64 bits)
return (cache->flags & bits) == bits;
}

void btrfs_get_block_group(struct btrfs_block_group_cache *cache)
{
atomic_inc(&cache->count);
}

void btrfs_put_block_group(struct btrfs_block_group_cache *cache)
{
if (atomic_dec_and_test(&cache->count))
kfree(cache);
}

/*
* this adds the block group to the fs_info rb tree for the block group
* cache
Expand Down Expand Up @@ -156,7 +167,7 @@ block_group_cache_tree_search(struct btrfs_fs_info *info, u64 bytenr,
}
}
if (ret)
atomic_inc(&ret->count);
btrfs_get_block_group(ret);
spin_unlock(&info->block_group_cache_lock);

return ret;
Expand Down Expand Up @@ -407,6 +418,8 @@ static int caching_kthread(void *data)

put_caching_control(caching_ctl);
atomic_dec(&block_group->space_info->caching_threads);
btrfs_put_block_group(block_group);

return 0;
}

Expand Down Expand Up @@ -447,6 +460,7 @@ static int cache_block_group(struct btrfs_block_group_cache *cache)
up_write(&fs_info->extent_commit_sem);

atomic_inc(&cache->space_info->caching_threads);
btrfs_get_block_group(cache);

tsk = kthread_run(caching_kthread, cache, "btrfs-cache-%llu\n",
cache->key.objectid);
Expand Down Expand Up @@ -486,12 +500,6 @@ struct btrfs_block_group_cache *btrfs_lookup_block_group(
return cache;
}

void btrfs_put_block_group(struct btrfs_block_group_cache *cache)
{
if (atomic_dec_and_test(&cache->count))
kfree(cache);
}

static struct btrfs_space_info *__find_space_info(struct btrfs_fs_info *info,
u64 flags)
{
Expand Down Expand Up @@ -2582,7 +2590,7 @@ next_block_group(struct btrfs_root *root,
if (node) {
cache = rb_entry(node, struct btrfs_block_group_cache,
cache_node);
atomic_inc(&cache->count);
btrfs_get_block_group(cache);
} else
cache = NULL;
spin_unlock(&root->fs_info->block_group_cache_lock);
Expand Down Expand Up @@ -4227,7 +4235,7 @@ static noinline int find_free_extent(struct btrfs_trans_handle *trans,
u64 offset;
int cached;

atomic_inc(&block_group->count);
btrfs_get_block_group(block_group);
search_start = block_group->key.objectid;

have_block_group:
Expand Down Expand Up @@ -4315,7 +4323,7 @@ static noinline int find_free_extent(struct btrfs_trans_handle *trans,

btrfs_put_block_group(block_group);
block_group = last_ptr->block_group;
atomic_inc(&block_group->count);
btrfs_get_block_group(block_group);
spin_unlock(&last_ptr->lock);
spin_unlock(&last_ptr->refill_lock);

Expand Down Expand Up @@ -7395,9 +7403,7 @@ int btrfs_free_block_groups(struct btrfs_fs_info *info)
wait_block_group_cache_done(block_group);

btrfs_remove_free_space_cache(block_group);

WARN_ON(atomic_read(&block_group->count) != 1);
kfree(block_group);
btrfs_put_block_group(block_group);

spin_lock(&info->block_group_cache_lock);
}
Expand Down

0 comments on commit 11dfe35

Please sign in to comment.