diff options
Diffstat (limited to 'fs/btrfs')
-rw-r--r-- | fs/btrfs/delayed-ref.c | 18 | ||||
-rw-r--r-- | fs/btrfs/delayed-ref.h | 5 | ||||
-rw-r--r-- | fs/btrfs/extent-tree.c | 67 |
3 files changed, 87 insertions, 3 deletions
diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c index 874565a1f634..3e7eeaf86408 100644 --- a/fs/btrfs/delayed-ref.c +++ b/fs/btrfs/delayed-ref.c | |||
@@ -511,6 +511,24 @@ int btrfs_add_delayed_ref(struct btrfs_trans_handle *trans, | |||
511 | } | 511 | } |
512 | 512 | ||
513 | /* | 513 | /* |
514 | * this does a simple search for the head node for a given extent. | ||
515 | * It must be called with the delayed ref spinlock held, and it returns | ||
516 | * the head node if any where found, or NULL if not. | ||
517 | */ | ||
518 | struct btrfs_delayed_ref_head * | ||
519 | btrfs_find_delayed_ref_head(struct btrfs_trans_handle *trans, u64 bytenr) | ||
520 | { | ||
521 | struct btrfs_delayed_ref_node *ref; | ||
522 | struct btrfs_delayed_ref_root *delayed_refs; | ||
523 | |||
524 | delayed_refs = &trans->transaction->delayed_refs; | ||
525 | ref = tree_search(&delayed_refs->root, bytenr, (u64)-1); | ||
526 | if (ref) | ||
527 | return btrfs_delayed_node_to_head(ref); | ||
528 | return NULL; | ||
529 | } | ||
530 | |||
531 | /* | ||
514 | * add a delayed ref to the tree. This does all of the accounting required | 532 | * add a delayed ref to the tree. This does all of the accounting required |
515 | * to make sure the delayed ref is eventually processed before this | 533 | * to make sure the delayed ref is eventually processed before this |
516 | * transaction commits. | 534 | * transaction commits. |
diff --git a/fs/btrfs/delayed-ref.h b/fs/btrfs/delayed-ref.h index 37919e5c007f..c345fee9f96b 100644 --- a/fs/btrfs/delayed-ref.h +++ b/fs/btrfs/delayed-ref.h | |||
@@ -137,9 +137,8 @@ int btrfs_add_delayed_ref(struct btrfs_trans_handle *trans, | |||
137 | u64 ref_generation, u64 owner_objectid, int action, | 137 | u64 ref_generation, u64 owner_objectid, int action, |
138 | int pin); | 138 | int pin); |
139 | 139 | ||
140 | struct btrfs_delayed_ref * | 140 | struct btrfs_delayed_ref_head * |
141 | btrfs_find_delayed_ref(struct btrfs_trans_handle *trans, u64 bytenr, | 141 | btrfs_find_delayed_ref_head(struct btrfs_trans_handle *trans, u64 bytenr); |
142 | u64 parent); | ||
143 | int btrfs_delayed_ref_pending(struct btrfs_trans_handle *trans, u64 bytenr); | 142 | int btrfs_delayed_ref_pending(struct btrfs_trans_handle *trans, u64 bytenr); |
144 | int btrfs_lock_delayed_ref(struct btrfs_trans_handle *trans, | 143 | int btrfs_lock_delayed_ref(struct btrfs_trans_handle *trans, |
145 | struct btrfs_delayed_ref_node *ref, | 144 | struct btrfs_delayed_ref_node *ref, |
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 9b5da2b013e4..8471c79b0877 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c | |||
@@ -1021,6 +1021,7 @@ again: | |||
1021 | if (!locked_ref && count == 0) | 1021 | if (!locked_ref && count == 0) |
1022 | break; | 1022 | break; |
1023 | 1023 | ||
1024 | cond_resched(); | ||
1024 | spin_lock(&delayed_refs->lock); | 1025 | spin_lock(&delayed_refs->lock); |
1025 | } | 1026 | } |
1026 | if (run_all) { | 1027 | if (run_all) { |
@@ -1045,6 +1046,7 @@ again: | |||
1045 | mutex_unlock(&head->mutex); | 1046 | mutex_unlock(&head->mutex); |
1046 | 1047 | ||
1047 | btrfs_put_delayed_ref(ref); | 1048 | btrfs_put_delayed_ref(ref); |
1049 | cond_resched(); | ||
1048 | goto again; | 1050 | goto again; |
1049 | } | 1051 | } |
1050 | node = rb_next(node); | 1052 | node = rb_next(node); |
@@ -2361,6 +2363,68 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, | |||
2361 | owner_objectid, pin, pin == 0, refs_to_drop); | 2363 | owner_objectid, pin, pin == 0, refs_to_drop); |
2362 | } | 2364 | } |
2363 | 2365 | ||
2366 | /* | ||
2367 | * when we free an extent, it is possible (and likely) that we free the last | ||
2368 | * delayed ref for that extent as well. This searches the delayed ref tree for | ||
2369 | * a given extent, and if there are no other delayed refs to be processed, it | ||
2370 | * removes it from the tree. | ||
2371 | */ | ||
2372 | static noinline int check_ref_cleanup(struct btrfs_trans_handle *trans, | ||
2373 | struct btrfs_root *root, u64 bytenr) | ||
2374 | { | ||
2375 | struct btrfs_delayed_ref_head *head; | ||
2376 | struct btrfs_delayed_ref_root *delayed_refs; | ||
2377 | struct btrfs_delayed_ref_node *ref; | ||
2378 | struct rb_node *node; | ||
2379 | int ret; | ||
2380 | |||
2381 | delayed_refs = &trans->transaction->delayed_refs; | ||
2382 | spin_lock(&delayed_refs->lock); | ||
2383 | head = btrfs_find_delayed_ref_head(trans, bytenr); | ||
2384 | if (!head) | ||
2385 | goto out; | ||
2386 | |||
2387 | node = rb_prev(&head->node.rb_node); | ||
2388 | if (!node) | ||
2389 | goto out; | ||
2390 | |||
2391 | ref = rb_entry(node, struct btrfs_delayed_ref_node, rb_node); | ||
2392 | |||
2393 | /* there are still entries for this ref, we can't drop it */ | ||
2394 | if (ref->bytenr == bytenr) | ||
2395 | goto out; | ||
2396 | |||
2397 | /* | ||
2398 | * waiting for the lock here would deadlock. If someone else has it | ||
2399 | * locked they are already in the process of dropping it anyway | ||
2400 | */ | ||
2401 | if (!mutex_trylock(&head->mutex)) | ||
2402 | goto out; | ||
2403 | |||
2404 | /* | ||
2405 | * at this point we have a head with no other entries. Go | ||
2406 | * ahead and process it. | ||
2407 | */ | ||
2408 | head->node.in_tree = 0; | ||
2409 | rb_erase(&head->node.rb_node, &delayed_refs->root); | ||
2410 | delayed_refs->num_entries--; | ||
2411 | |||
2412 | /* | ||
2413 | * we don't take a ref on the node because we're removing it from the | ||
2414 | * tree, so we just steal the ref the tree was holding. | ||
2415 | */ | ||
2416 | spin_unlock(&delayed_refs->lock); | ||
2417 | |||
2418 | ret = run_one_delayed_ref(trans, root->fs_info->tree_root, | ||
2419 | &head->node, head->must_insert_reserved); | ||
2420 | BUG_ON(ret); | ||
2421 | btrfs_put_delayed_ref(&head->node); | ||
2422 | return 0; | ||
2423 | out: | ||
2424 | spin_unlock(&delayed_refs->lock); | ||
2425 | return 0; | ||
2426 | } | ||
2427 | |||
2364 | int btrfs_free_extent(struct btrfs_trans_handle *trans, | 2428 | int btrfs_free_extent(struct btrfs_trans_handle *trans, |
2365 | struct btrfs_root *root, | 2429 | struct btrfs_root *root, |
2366 | u64 bytenr, u64 num_bytes, u64 parent, | 2430 | u64 bytenr, u64 num_bytes, u64 parent, |
@@ -2388,6 +2452,9 @@ int btrfs_free_extent(struct btrfs_trans_handle *trans, | |||
2388 | root_objectid, ref_generation, | 2452 | root_objectid, ref_generation, |
2389 | owner_objectid, | 2453 | owner_objectid, |
2390 | BTRFS_DROP_DELAYED_REF, 1); | 2454 | BTRFS_DROP_DELAYED_REF, 1); |
2455 | BUG_ON(ret); | ||
2456 | ret = check_ref_cleanup(trans, root, bytenr); | ||
2457 | BUG_ON(ret); | ||
2391 | } | 2458 | } |
2392 | return ret; | 2459 | return ret; |
2393 | } | 2460 | } |