aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/reada.c
diff options
context:
space:
mode:
authorArne Jansen <sensille@gmx.net>2012-02-25 03:09:30 -0500
committerDavid Sterba <dsterba@suse.cz>2012-04-18 13:12:44 -0400
commit8c9c2bf7a3c4f7e9d158c0be9c49f372fb943ad2 (patch)
tree6c17b4db9b39cb8eb950c21377f453df18f467c3 /fs/btrfs/reada.c
parent848cce0d4102b5b4b26b0987b43e1919d462afe2 (diff)
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>
Diffstat (limited to 'fs/btrfs/reada.c')
-rw-r--r--fs/btrfs/reada.c35
1 files changed, 16 insertions, 19 deletions
diff --git a/fs/btrfs/reada.c b/fs/btrfs/reada.c
index dc5d33146fdb..8dec650099c8 100644
--- a/fs/btrfs/reada.c
+++ b/fs/btrfs/reada.c
@@ -250,14 +250,12 @@ static struct reada_zone *reada_find_zone(struct btrfs_fs_info *fs_info,
250 struct btrfs_bio *bbio) 250 struct btrfs_bio *bbio)
251{ 251{
252 int ret; 252 int ret;
253 int looped = 0;
254 struct reada_zone *zone; 253 struct reada_zone *zone;
255 struct btrfs_block_group_cache *cache = NULL; 254 struct btrfs_block_group_cache *cache = NULL;
256 u64 start; 255 u64 start;
257 u64 end; 256 u64 end;
258 int i; 257 int i;
259 258
260again:
261 zone = NULL; 259 zone = NULL;
262 spin_lock(&fs_info->reada_lock); 260 spin_lock(&fs_info->reada_lock);
263 ret = radix_tree_gang_lookup(&dev->reada_zones, (void **)&zone, 261 ret = radix_tree_gang_lookup(&dev->reada_zones, (void **)&zone,
@@ -274,9 +272,6 @@ again:
274 spin_unlock(&fs_info->reada_lock); 272 spin_unlock(&fs_info->reada_lock);
275 } 273 }
276 274
277 if (looped)
278 return NULL;
279
280 cache = btrfs_lookup_block_group(fs_info, logical); 275 cache = btrfs_lookup_block_group(fs_info, logical);
281 if (!cache) 276 if (!cache)
282 return NULL; 277 return NULL;
@@ -307,13 +302,15 @@ again:
307 ret = radix_tree_insert(&dev->reada_zones, 302 ret = radix_tree_insert(&dev->reada_zones,
308 (unsigned long)(zone->end >> PAGE_CACHE_SHIFT), 303 (unsigned long)(zone->end >> PAGE_CACHE_SHIFT),
309 zone); 304 zone);
310 spin_unlock(&fs_info->reada_lock);
311 305
312 if (ret) { 306 if (ret == -EEXIST) {
313 kfree(zone); 307 kfree(zone);
314 looped = 1; 308 ret = radix_tree_gang_lookup(&dev->reada_zones, (void **)&zone,
315 goto again; 309 logical >> PAGE_CACHE_SHIFT, 1);
310 if (ret == 1)
311 kref_get(&zone->refcnt);
316 } 312 }
313 spin_unlock(&fs_info->reada_lock);
317 314
318 return zone; 315 return zone;
319} 316}
@@ -323,8 +320,8 @@ static struct reada_extent *reada_find_extent(struct btrfs_root *root,
323 struct btrfs_key *top, int level) 320 struct btrfs_key *top, int level)
324{ 321{
325 int ret; 322 int ret;
326 int looped = 0;
327 struct reada_extent *re = NULL; 323 struct reada_extent *re = NULL;
324 struct reada_extent *re_exist = NULL;
328 struct btrfs_fs_info *fs_info = root->fs_info; 325 struct btrfs_fs_info *fs_info = root->fs_info;
329 struct btrfs_mapping_tree *map_tree = &fs_info->mapping_tree; 326 struct btrfs_mapping_tree *map_tree = &fs_info->mapping_tree;
330 struct btrfs_bio *bbio = NULL; 327 struct btrfs_bio *bbio = NULL;
@@ -335,14 +332,13 @@ static struct reada_extent *reada_find_extent(struct btrfs_root *root,
335 int i; 332 int i;
336 unsigned long index = logical >> PAGE_CACHE_SHIFT; 333 unsigned long index = logical >> PAGE_CACHE_SHIFT;
337 334
338again:
339 spin_lock(&fs_info->reada_lock); 335 spin_lock(&fs_info->reada_lock);
340 re = radix_tree_lookup(&fs_info->reada_tree, index); 336 re = radix_tree_lookup(&fs_info->reada_tree, index);
341 if (re) 337 if (re)
342 kref_get(&re->refcnt); 338 kref_get(&re->refcnt);
343 spin_unlock(&fs_info->reada_lock); 339 spin_unlock(&fs_info->reada_lock);
344 340
345 if (re || looped) 341 if (re)
346 return re; 342 return re;
347 343
348 re = kzalloc(sizeof(*re), GFP_NOFS); 344 re = kzalloc(sizeof(*re), GFP_NOFS);
@@ -398,12 +394,15 @@ again:
398 /* insert extent in reada_tree + all per-device trees, all or nothing */ 394 /* insert extent in reada_tree + all per-device trees, all or nothing */
399 spin_lock(&fs_info->reada_lock); 395 spin_lock(&fs_info->reada_lock);
400 ret = radix_tree_insert(&fs_info->reada_tree, index, re); 396 ret = radix_tree_insert(&fs_info->reada_tree, index, re);
397 if (ret == -EEXIST) {
398 re_exist = radix_tree_lookup(&fs_info->reada_tree, index);
399 BUG_ON(!re_exist);
400 kref_get(&re_exist->refcnt);
401 spin_unlock(&fs_info->reada_lock);
402 goto error;
403 }
401 if (ret) { 404 if (ret) {
402 spin_unlock(&fs_info->reada_lock); 405 spin_unlock(&fs_info->reada_lock);
403 if (ret != -ENOMEM) {
404 /* someone inserted the extent in the meantime */
405 looped = 1;
406 }
407 goto error; 406 goto error;
408 } 407 }
409 for (i = 0; i < nzones; ++i) { 408 for (i = 0; i < nzones; ++i) {
@@ -450,9 +449,7 @@ error:
450 } 449 }
451 kfree(bbio); 450 kfree(bbio);
452 kfree(re); 451 kfree(re);
453 if (looped) 452 return re_exist;
454 goto again;
455 return NULL;
456} 453}
457 454
458static void reada_kref_dummy(struct kref *kr) 455static void reada_kref_dummy(struct kref *kr)