Skip to content

Commit

Permalink
btrfs: fix race in reada
Browse files Browse the repository at this point in the history
When inserting into the radix tree returns EEXIST, get the existing
entry without giving up the spinlock in between.
There was a race for both the zones trees and the extent tree.

Signed-off-by: Arne Jansen <sensille@gmx.net>
  • Loading branch information
Arne Jansen authored and David Sterba committed Apr 18, 2012
1 parent 848cce0 commit 8c9c2bf
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 20 deletions.
8 changes: 7 additions & 1 deletion fs/btrfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -4332,7 +4332,13 @@ static int btrfs_real_readdir(struct file *filp, void *dirent,
}
no_dentry:
/* is this a reference to our own snapshot? If so
* skip it
* skip it.
*
* In contrast to old kernels, we insert the snapshot's
* dir item and dir index after it has been created, so
* we won't find a reference to our own snapshot. We
* still keep the following code for backward
* compatibility.
*/
if (location.type == BTRFS_ROOT_ITEM_KEY &&
location.objectid == root->root_key.objectid) {
Expand Down
35 changes: 16 additions & 19 deletions fs/btrfs/reada.c
Original file line number Diff line number Diff line change
Expand Up @@ -250,14 +250,12 @@ static struct reada_zone *reada_find_zone(struct btrfs_fs_info *fs_info,
struct btrfs_bio *bbio)
{
int ret;
int looped = 0;
struct reada_zone *zone;
struct btrfs_block_group_cache *cache = NULL;
u64 start;
u64 end;
int i;

again:
zone = NULL;
spin_lock(&fs_info->reada_lock);
ret = radix_tree_gang_lookup(&dev->reada_zones, (void **)&zone,
Expand All @@ -274,9 +272,6 @@ static struct reada_zone *reada_find_zone(struct btrfs_fs_info *fs_info,
spin_unlock(&fs_info->reada_lock);
}

if (looped)
return NULL;

cache = btrfs_lookup_block_group(fs_info, logical);
if (!cache)
return NULL;
Expand Down Expand Up @@ -307,13 +302,15 @@ static struct reada_zone *reada_find_zone(struct btrfs_fs_info *fs_info,
ret = radix_tree_insert(&dev->reada_zones,
(unsigned long)(zone->end >> PAGE_CACHE_SHIFT),
zone);
spin_unlock(&fs_info->reada_lock);

if (ret) {
if (ret == -EEXIST) {
kfree(zone);
looped = 1;
goto again;
ret = radix_tree_gang_lookup(&dev->reada_zones, (void **)&zone,
logical >> PAGE_CACHE_SHIFT, 1);
if (ret == 1)
kref_get(&zone->refcnt);
}
spin_unlock(&fs_info->reada_lock);

return zone;
}
Expand All @@ -323,8 +320,8 @@ static struct reada_extent *reada_find_extent(struct btrfs_root *root,
struct btrfs_key *top, int level)
{
int ret;
int looped = 0;
struct reada_extent *re = NULL;
struct reada_extent *re_exist = NULL;
struct btrfs_fs_info *fs_info = root->fs_info;
struct btrfs_mapping_tree *map_tree = &fs_info->mapping_tree;
struct btrfs_bio *bbio = NULL;
Expand All @@ -335,14 +332,13 @@ static struct reada_extent *reada_find_extent(struct btrfs_root *root,
int i;
unsigned long index = logical >> PAGE_CACHE_SHIFT;

again:
spin_lock(&fs_info->reada_lock);
re = radix_tree_lookup(&fs_info->reada_tree, index);
if (re)
kref_get(&re->refcnt);
spin_unlock(&fs_info->reada_lock);

if (re || looped)
if (re)
return re;

re = kzalloc(sizeof(*re), GFP_NOFS);
Expand Down Expand Up @@ -398,12 +394,15 @@ static struct reada_extent *reada_find_extent(struct btrfs_root *root,
/* insert extent in reada_tree + all per-device trees, all or nothing */
spin_lock(&fs_info->reada_lock);
ret = radix_tree_insert(&fs_info->reada_tree, index, re);
if (ret == -EEXIST) {
re_exist = radix_tree_lookup(&fs_info->reada_tree, index);
BUG_ON(!re_exist);
kref_get(&re_exist->refcnt);
spin_unlock(&fs_info->reada_lock);
goto error;
}
if (ret) {
spin_unlock(&fs_info->reada_lock);
if (ret != -ENOMEM) {
/* someone inserted the extent in the meantime */
looped = 1;
}
goto error;
}
for (i = 0; i < nzones; ++i) {
Expand Down Expand Up @@ -450,9 +449,7 @@ static struct reada_extent *reada_find_extent(struct btrfs_root *root,
}
kfree(bbio);
kfree(re);
if (looped)
goto again;
return NULL;
return re_exist;
}

static void reada_kref_dummy(struct kref *kr)
Expand Down

0 comments on commit 8c9c2bf

Please sign in to comment.