diff options
Diffstat (limited to 'fs/btrfs/reada.c')
-rw-r--r-- | fs/btrfs/reada.c | 48 |
1 files changed, 29 insertions, 19 deletions
diff --git a/fs/btrfs/reada.c b/fs/btrfs/reada.c index dc5d33146fdb..ac5d01085884 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 | ||
260 | again: | ||
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,26 +320,26 @@ 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; |
331 | struct btrfs_device *dev; | 328 | struct btrfs_device *dev; |
329 | struct btrfs_device *prev_dev; | ||
332 | u32 blocksize; | 330 | u32 blocksize; |
333 | u64 length; | 331 | u64 length; |
334 | int nzones = 0; | 332 | int nzones = 0; |
335 | int i; | 333 | int i; |
336 | unsigned long index = logical >> PAGE_CACHE_SHIFT; | 334 | unsigned long index = logical >> PAGE_CACHE_SHIFT; |
337 | 335 | ||
338 | again: | ||
339 | spin_lock(&fs_info->reada_lock); | 336 | spin_lock(&fs_info->reada_lock); |
340 | re = radix_tree_lookup(&fs_info->reada_tree, index); | 337 | re = radix_tree_lookup(&fs_info->reada_tree, index); |
341 | if (re) | 338 | if (re) |
342 | kref_get(&re->refcnt); | 339 | kref_get(&re->refcnt); |
343 | spin_unlock(&fs_info->reada_lock); | 340 | spin_unlock(&fs_info->reada_lock); |
344 | 341 | ||
345 | if (re || looped) | 342 | if (re) |
346 | return re; | 343 | return re; |
347 | 344 | ||
348 | re = kzalloc(sizeof(*re), GFP_NOFS); | 345 | re = kzalloc(sizeof(*re), GFP_NOFS); |
@@ -398,16 +395,31 @@ again: | |||
398 | /* insert extent in reada_tree + all per-device trees, all or nothing */ | 395 | /* insert extent in reada_tree + all per-device trees, all or nothing */ |
399 | spin_lock(&fs_info->reada_lock); | 396 | spin_lock(&fs_info->reada_lock); |
400 | ret = radix_tree_insert(&fs_info->reada_tree, index, re); | 397 | ret = radix_tree_insert(&fs_info->reada_tree, index, re); |
398 | if (ret == -EEXIST) { | ||
399 | re_exist = radix_tree_lookup(&fs_info->reada_tree, index); | ||
400 | BUG_ON(!re_exist); | ||
401 | kref_get(&re_exist->refcnt); | ||
402 | spin_unlock(&fs_info->reada_lock); | ||
403 | goto error; | ||
404 | } | ||
401 | if (ret) { | 405 | if (ret) { |
402 | spin_unlock(&fs_info->reada_lock); | 406 | 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; | 407 | goto error; |
408 | } | 408 | } |
409 | prev_dev = NULL; | ||
409 | for (i = 0; i < nzones; ++i) { | 410 | for (i = 0; i < nzones; ++i) { |
410 | dev = bbio->stripes[i].dev; | 411 | dev = bbio->stripes[i].dev; |
412 | if (dev == prev_dev) { | ||
413 | /* | ||
414 | * in case of DUP, just add the first zone. As both | ||
415 | * are on the same device, there's nothing to gain | ||
416 | * from adding both. | ||
417 | * Also, it wouldn't work, as the tree is per device | ||
418 | * and adding would fail with EEXIST | ||
419 | */ | ||
420 | continue; | ||
421 | } | ||
422 | prev_dev = dev; | ||
411 | ret = radix_tree_insert(&dev->reada_extents, index, re); | 423 | ret = radix_tree_insert(&dev->reada_extents, index, re); |
412 | if (ret) { | 424 | if (ret) { |
413 | while (--i >= 0) { | 425 | while (--i >= 0) { |
@@ -450,9 +462,7 @@ error: | |||
450 | } | 462 | } |
451 | kfree(bbio); | 463 | kfree(bbio); |
452 | kfree(re); | 464 | kfree(re); |
453 | if (looped) | 465 | return re_exist; |
454 | goto again; | ||
455 | return NULL; | ||
456 | } | 466 | } |
457 | 467 | ||
458 | static void reada_kref_dummy(struct kref *kr) | 468 | static void reada_kref_dummy(struct kref *kr) |