btrfs: fix race in reada
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>
This commit is contained in:
Родитель
848cce0d41
Коммит
8c9c2bf7a3
|
@ -4332,7 +4332,13 @@ static int btrfs_real_readdir(struct file *filp, void *dirent,
|
||||||
}
|
}
|
||||||
no_dentry:
|
no_dentry:
|
||||||
/* is this a reference to our own snapshot? If so
|
/* 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 &&
|
if (location.type == BTRFS_ROOT_ITEM_KEY &&
|
||||||
location.objectid == root->root_key.objectid) {
|
location.objectid == root->root_key.objectid) {
|
||||||
|
|
|
@ -250,14 +250,12 @@ static struct reada_zone *reada_find_zone(struct btrfs_fs_info *fs_info,
|
||||||
struct btrfs_bio *bbio)
|
struct btrfs_bio *bbio)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
int looped = 0;
|
|
||||||
struct reada_zone *zone;
|
struct reada_zone *zone;
|
||||||
struct btrfs_block_group_cache *cache = NULL;
|
struct btrfs_block_group_cache *cache = NULL;
|
||||||
u64 start;
|
u64 start;
|
||||||
u64 end;
|
u64 end;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
again:
|
|
||||||
zone = NULL;
|
zone = NULL;
|
||||||
spin_lock(&fs_info->reada_lock);
|
spin_lock(&fs_info->reada_lock);
|
||||||
ret = radix_tree_gang_lookup(&dev->reada_zones, (void **)&zone,
|
ret = radix_tree_gang_lookup(&dev->reada_zones, (void **)&zone,
|
||||||
|
@ -274,9 +272,6 @@ again:
|
||||||
spin_unlock(&fs_info->reada_lock);
|
spin_unlock(&fs_info->reada_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (looped)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
cache = btrfs_lookup_block_group(fs_info, logical);
|
cache = btrfs_lookup_block_group(fs_info, logical);
|
||||||
if (!cache)
|
if (!cache)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -307,13 +302,15 @@ again:
|
||||||
ret = radix_tree_insert(&dev->reada_zones,
|
ret = radix_tree_insert(&dev->reada_zones,
|
||||||
(unsigned long)(zone->end >> PAGE_CACHE_SHIFT),
|
(unsigned long)(zone->end >> PAGE_CACHE_SHIFT),
|
||||||
zone);
|
zone);
|
||||||
spin_unlock(&fs_info->reada_lock);
|
|
||||||
|
|
||||||
if (ret) {
|
if (ret == -EEXIST) {
|
||||||
kfree(zone);
|
kfree(zone);
|
||||||
looped = 1;
|
ret = radix_tree_gang_lookup(&dev->reada_zones, (void **)&zone,
|
||||||
goto again;
|
logical >> PAGE_CACHE_SHIFT, 1);
|
||||||
|
if (ret == 1)
|
||||||
|
kref_get(&zone->refcnt);
|
||||||
}
|
}
|
||||||
|
spin_unlock(&fs_info->reada_lock);
|
||||||
|
|
||||||
return zone;
|
return zone;
|
||||||
}
|
}
|
||||||
|
@ -323,8 +320,8 @@ static struct reada_extent *reada_find_extent(struct btrfs_root *root,
|
||||||
struct btrfs_key *top, int level)
|
struct btrfs_key *top, int level)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
int looped = 0;
|
|
||||||
struct reada_extent *re = NULL;
|
struct reada_extent *re = NULL;
|
||||||
|
struct reada_extent *re_exist = NULL;
|
||||||
struct btrfs_fs_info *fs_info = root->fs_info;
|
struct btrfs_fs_info *fs_info = root->fs_info;
|
||||||
struct btrfs_mapping_tree *map_tree = &fs_info->mapping_tree;
|
struct btrfs_mapping_tree *map_tree = &fs_info->mapping_tree;
|
||||||
struct btrfs_bio *bbio = NULL;
|
struct btrfs_bio *bbio = NULL;
|
||||||
|
@ -335,14 +332,13 @@ static struct reada_extent *reada_find_extent(struct btrfs_root *root,
|
||||||
int i;
|
int i;
|
||||||
unsigned long index = logical >> PAGE_CACHE_SHIFT;
|
unsigned long index = logical >> PAGE_CACHE_SHIFT;
|
||||||
|
|
||||||
again:
|
|
||||||
spin_lock(&fs_info->reada_lock);
|
spin_lock(&fs_info->reada_lock);
|
||||||
re = radix_tree_lookup(&fs_info->reada_tree, index);
|
re = radix_tree_lookup(&fs_info->reada_tree, index);
|
||||||
if (re)
|
if (re)
|
||||||
kref_get(&re->refcnt);
|
kref_get(&re->refcnt);
|
||||||
spin_unlock(&fs_info->reada_lock);
|
spin_unlock(&fs_info->reada_lock);
|
||||||
|
|
||||||
if (re || looped)
|
if (re)
|
||||||
return re;
|
return re;
|
||||||
|
|
||||||
re = kzalloc(sizeof(*re), GFP_NOFS);
|
re = kzalloc(sizeof(*re), GFP_NOFS);
|
||||||
|
@ -398,12 +394,15 @@ again:
|
||||||
/* insert extent in reada_tree + all per-device trees, all or nothing */
|
/* insert extent in reada_tree + all per-device trees, all or nothing */
|
||||||
spin_lock(&fs_info->reada_lock);
|
spin_lock(&fs_info->reada_lock);
|
||||||
ret = radix_tree_insert(&fs_info->reada_tree, index, re);
|
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) {
|
if (ret) {
|
||||||
spin_unlock(&fs_info->reada_lock);
|
spin_unlock(&fs_info->reada_lock);
|
||||||
if (ret != -ENOMEM) {
|
|
||||||
/* someone inserted the extent in the meantime */
|
|
||||||
looped = 1;
|
|
||||||
}
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
for (i = 0; i < nzones; ++i) {
|
for (i = 0; i < nzones; ++i) {
|
||||||
|
@ -450,9 +449,7 @@ error:
|
||||||
}
|
}
|
||||||
kfree(bbio);
|
kfree(bbio);
|
||||||
kfree(re);
|
kfree(re);
|
||||||
if (looped)
|
return re_exist;
|
||||||
goto again;
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void reada_kref_dummy(struct kref *kr)
|
static void reada_kref_dummy(struct kref *kr)
|
||||||
|
|
Загрузка…
Ссылка в новой задаче