Cleanup and enhance delayed refs
Subsystems (2)
cover Cleanup and enhance delayed refs
I'm leaving for Linux Plumbers tomorrow so I wanted to get this out for people to look at and see if there were any general comments. These patches have only been compiled, not tested in any way, so I wouldn't recommend trying them. They do a few things
1) Cleanup empty delayed ref head handling. We clean this up in a variety of different ways in different places, so I've unified it all in one helper and reduced the complexity of __btrfs_run_delayed_refs.
2) Removed the btrfs_delayed_ref_node from the btrfs_delayed_ref_head. This used to be this way because we had heads and nodes all on the same list. Since this is no longer the case it's just wasted space and weird complexity, so kill it.
3) Track delayed ref updates in an rb_tree on the head ref instead of in a list. Our merging stuff really sucks if we end up with lots of different changes to the same block. You can end up blowing loads of cpu time during the transaction commit, which isn't helpful. With the tree we can not loop as much, and make our insert time merging much better.
Here is the diffstat
fs/btrfs/backref.c | 9 +- fs/btrfs/delayed-ref.c | 294 ++++++++++++++++----------------- fs/btrfs/delayed-ref.h | 54 +++---- fs/btrfs/disk-io.c | 22 +-- fs/btrfs/extent-tree.c | 376 ++++++++++++++++++++----------------------- include/trace/events/btrfs.h | 15 +- 6 files changed, 363 insertions(+), 407 deletions(-)
Let me know if you have any questions or feedback. Thanks,
Josef
PATCH 1/10 btrfs: add a helper to return a head ref
From: Josef Bacik <[email protected]>
Simplify the error handling in __btrfs_run_delayed_refs by breaking out the code used to return a head back to the delayed_refs tree for processing into a helper function.
--- fs/btrfs/extent-tree.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-)
fs/btrfs/extent-tree.c +15 -15
@@ -2508,6 +2508,17 @@ select_delayed_ref(struct btrfs_delayed_ref_head *head)return ref; } +static void +unselect_delayed_ref_head (struct btrfs_delayed_ref_root *delayed_refs, +struct btrfs_delayed_ref_head *head) +{ +spin_lock (&delayed_refs->lock); + head->processing =0 ; + delayed_refs->num_heads_ready++; +spin_unlock (&delayed_refs->lock); +btrfs_delayed_ref_unlock (head); +} +/* * Returns 0 on success or if called with an already aborted transaction. * Returns -ENOMEM or -EIO on failure and will abort the transaction.
@@ -2581,11 +2592,7 @@ static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans,if (ref && ref->seq &&btrfs_check_delayed_seq (fs_info, delayed_refs, ref->seq)) {spin_unlock (&locked_ref->lock); -spin_lock (&delayed_refs->lock); - locked_ref->processing =0 ; - delayed_refs->num_heads_ready++; -spin_unlock (&delayed_refs->lock); -btrfs_delayed_ref_unlock (locked_ref); +unselect_delayed_ref_head (delayed_refs, locked_ref); locked_ref =NULL ;cond_resched (); count++;
@@ -2631,14 +2638,11 @@ static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans,*/ if (must_insert_reserved) locked_ref->must_insert_reserved =1 ; -spin_lock (&delayed_refs->lock); - locked_ref->processing =0 ; - delayed_refs->num_heads_ready++; -spin_unlock (&delayed_refs->lock); +unselect_delayed_ref_head (delayed_refs, + locked_ref);btrfs_debug (fs_info,"run_delayed_extent_op returned %d" , ret); -btrfs_delayed_ref_unlock (locked_ref);return ret; }continue ;
@@ -2696,11 +2700,7 @@ static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans,btrfs_free_delayed_extent_op (extent_op);if (ret) { -spin_lock (&delayed_refs->lock); - locked_ref->processing =0 ; - delayed_refs->num_heads_ready++; -spin_unlock (&delayed_refs->lock); -btrfs_delayed_ref_unlock (locked_ref); +unselect_delayed_ref_head (delayed_refs, locked_ref);btrfs_put_delayed_ref (ref);btrfs_debug (fs_info,"run_one_delayed_ref returned %d" , ret);
Signature · 1 line
2.7.4
PATCH 2/10 btrfs: move extent_op cleanup to a helper
In reply to [email protected]From: Josef Bacik <[email protected]>
Move the extent_op cleanup for an empty head ref to a helper function to help simplify __btrfs_run_delayed_refs.
--- fs/btrfs/extent-tree.c | 77 ++++++++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 37 deletions(-)
fs/btrfs/extent-tree.c +40 -37
@@ -2519,6 +2519,26 @@ unselect_delayed_ref_head(struct btrfs_delayed_ref_root *delayed_refs,btrfs_delayed_ref_unlock (head); } +static int cleanup_extent_op (struct btrfs_trans_handle *trans, +struct btrfs_fs_info *fs_info, +struct btrfs_delayed_ref_head *head) +{ +struct btrfs_delayed_extent_op *extent_op = head->extent_op; +int ret; + +if (!extent_op) +return 0 ; + head->extent_op =NULL ; +if (head->must_insert_reserved) { +btrfs_free_delayed_extent_op (extent_op); +return 0 ; + } +spin_unlock (&head->lock); + ret =run_delayed_extent_op (trans, fs_info, &head->node, extent_op); +btrfs_free_delayed_extent_op (extent_op); +return ret ? ret :1 ; +} +/* * Returns 0 on success or if called with an already aborted transaction. * Returns -ENOMEM or -EIO on failure and will abort the transaction.
@@ -2599,16 +2619,6 @@ static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans,continue ; } -/* -* record the must insert reserved flag before we -* drop the spin lock. -*/ - must_insert_reserved = locked_ref->must_insert_reserved; - locked_ref->must_insert_reserved =0 ; - - extent_op = locked_ref->extent_op; - locked_ref->extent_op =NULL ; -if (!ref) {
@@ -2618,33 +2628,17 @@ static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans,*/ ref = &locked_ref->node; -if (extent_op && must_insert_reserved) { -btrfs_free_delayed_extent_op (extent_op); - extent_op =NULL ; - } - -if (extent_op) { -spin_unlock (&locked_ref->lock); - ret =run_delayed_extent_op (trans, fs_info, - ref, extent_op); -btrfs_free_delayed_extent_op (extent_op); - -if (ret) { -/* -* Need to reset must_insert_reserved if -* there was an error so the abort stuff -* can cleanup the reserved space -* properly. -*/ -if (must_insert_reserved) - locked_ref->must_insert_reserved =1 ; -unselect_delayed_ref_head (delayed_refs, - locked_ref); -btrfs_debug (fs_info, -"run_delayed_extent_op returned %d" , - ret); -return ret; - } + ret =cleanup_extent_op (trans, fs_info, locked_ref); +if (ret <0 ) { +unselect_delayed_ref_head (delayed_refs, + locked_ref); +btrfs_debug (fs_info, +"run_delayed_extent_op returned %d" , + ret); +return ret; + }else if (ret >0 ) { +/* We dropped our lock, we need to loop. */ + ret =0 ;continue ; }
@@ -2693,6 +2687,15 @@ static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans,WARN_ON (1 ); } } +/* +* record the must insert reserved flag before we +* drop the spin lock. +*/ + must_insert_reserved = locked_ref->must_insert_reserved; + locked_ref->must_insert_reserved =0 ; + + extent_op = locked_ref->extent_op; + locked_ref->extent_op =NULL ;spin_unlock (&locked_ref->lock); ret = run_one_delayed_ref(trans, fs_info, ref, extent_op,
Signature · 1 line
2.7.4
PATCH 3/10 btrfs: breakout empty head cleanup to a helper
In reply to [email protected]From: Josef Bacik <[email protected]>
Move this code out to a helper function to further simplivy __btrfs_run_delayed_refs.
--- fs/btrfs/extent-tree.c | 80 ++++++++++++++++++++++++++++---------------------- 1 file changed, 45 insertions(+), 35 deletions(-)
fs/btrfs/extent-tree.c +45 -35
@@ -2539,6 +2539,43 @@ static int cleanup_extent_op(struct btrfs_trans_handle *trans,return ret ? ret :1 ; } +static int cleanup_ref_head (struct btrfs_trans_handle *trans, +struct btrfs_fs_info *fs_info, +struct btrfs_delayed_ref_head *head) +{ +struct btrfs_delayed_ref_root *delayed_refs; +int ret; + + delayed_refs = &trans->transaction->delayed_refs; + + ret =cleanup_extent_op (trans, fs_info, head); +if (ret <0 ) { +unselect_delayed_ref_head (delayed_refs, head); +btrfs_debug (fs_info,"run_delayed_extent_op returned %d" , ret); +return ret; + }else if (ret) { +return ret; + } + +/* +* Need to drop our head ref lock and re-acquire the delayed ref lock +* and then re-check to make sure nobody got added. +*/ +spin_unlock (&head->lock); +spin_lock (&delayed_refs->lock); +spin_lock (&head->lock); +if (!list_empty (&head->ref_list) || head->extent_op) { +spin_unlock (&head->lock); +spin_unlock (&delayed_refs->lock); +return 1 ; + } + head->node.in_tree =0 ; + delayed_refs->num_heads--; +rb_erase (&head->href_node, &delayed_refs->href_root); +spin_unlock (&delayed_refs->lock); +return 0 ; +} +/* * Returns 0 on success or if called with an already aborted transaction. * Returns -ENOMEM or -EIO on failure and will abort the transaction.
@@ -2620,47 +2657,20 @@ static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans, }if (!ref) { - - -/* All delayed refs have been processed, Go ahead -* and send the head node to run_one_delayed_ref, -* so that any accounting fixes can happen -*/ - ref = &locked_ref->node; - - ret =cleanup_extent_op (trans, fs_info, locked_ref); -if (ret <0 ) { -unselect_delayed_ref_head (delayed_refs, - locked_ref); -btrfs_debug (fs_info, -"run_delayed_extent_op returned %d" , - ret); -return ret; - }else if (ret >0 ) { + ret =cleanup_ref_head (trans, fs_info, locked_ref); +if (ret >0 ) {/* We dropped our lock, we need to loop. */ ret =0 ;continue ; + }else if (ret) { +return ret; } -/* -* Need to drop our head ref lock and re-acquire the -* delayed ref lock and then re-check to make sure -* nobody got added. +/* All delayed refs have been processed, Go ahead +* and send the head node to run_one_delayed_ref, +* so that any accounting fixes can happen */ -spin_unlock (&locked_ref->lock); -spin_lock (&delayed_refs->lock); -spin_lock (&locked_ref->lock); -if (!list_empty (&locked_ref->ref_list) || - locked_ref->extent_op) { -spin_unlock (&locked_ref->lock); -spin_unlock (&delayed_refs->lock); -continue ; - } - ref->in_tree =0 ; - delayed_refs->num_heads--; -rb_erase (&locked_ref->href_node, - &delayed_refs->href_root); -spin_unlock (&delayed_refs->lock); + ref = &locked_ref->node; }else { actual_count++; ref->in_tree =0 ;
Signature · 1 line
2.7.4
PATCH 4/10 btrfs: move ref_mod modification into the if (ref) logic
In reply to [email protected]From: Josef Bacik <[email protected]>
We only use this logic if our ref isn't a ref_head, so move it up into the if (ref) case since we know that this is a normal ref and not a delayed ref head.
--- fs/btrfs/extent-tree.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-)
fs/btrfs/extent-tree.c +2 -4
@@ -2677,10 +2677,6 @@ static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans,list_del (&ref->list);if (!list_empty (&ref->add_list))list_del (&ref->add_list); - } -atomic_dec (&delayed_refs->num_entries); - -if (!btrfs_delayed_ref_is_head (ref)) {/* * when we play the delayed ref, also correct the * ref_mod on head
@@ -2697,6 +2693,8 @@ static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans,WARN_ON (1 ); } } +atomic_dec (&delayed_refs->num_entries); +/* * record the must insert reserved flag before we * drop the spin lock.
Signature · 1 line
2.7.4
PATCH 5/10 btrfs: move all ref head cleanup to the helper function
In reply to [email protected]From: Josef Bacik <[email protected]>
We do a couple different cleanup operations on the ref head. We adjust counters, we'll free any reserved space if we didn't end up using the ref, and we clear the pending csum bytes. Move all these disparate things into cleanup_ref_head and clean up the logic in __btrfs_run_delayed_refs so that it handles the !ref case a lot cleaner, as well as making run_one_delayed_ref() only deal with real refs and not the ref head.
--- fs/btrfs/extent-tree.c | 144 ++++++++++++++++++++++--------------------------- 1 file changed, 64 insertions(+), 80 deletions(-)
fs/btrfs/extent-tree.c +64 -80
@@ -2433,44 +2433,6 @@ static int run_one_delayed_ref(struct btrfs_trans_handle *trans,return 0 ; } -if (btrfs_delayed_ref_is_head (node)) { -struct btrfs_delayed_ref_head *head; -/* -* we've hit the end of the chain and we were supposed -* to insert this extent into the tree. But, it got -* deleted before we ever needed to insert it, so all -* we have to do is clean up the accounting -*/ -BUG_ON (extent_op); - head =btrfs_delayed_node_to_head (node); -trace_run_delayed_ref_head (fs_info, node, head, node->action); - -if (head->total_ref_mod <0 ) { -struct btrfs_block_group_cache *cache; - - cache =btrfs_lookup_block_group (fs_info, node->bytenr); -ASSERT (cache); -percpu_counter_add (&cache->space_info->total_bytes_pinned, - -node->num_bytes); -btrfs_put_block_group (cache); - } - -if (insert_reserved) { -btrfs_pin_extent (fs_info, node->bytenr, - node->num_bytes,1 ); -if (head->is_data) { - ret =btrfs_del_csums (trans, fs_info, - node->bytenr, - node->num_bytes); - } - } - -/* Also free its reserved qgroup space */ -btrfs_qgroup_free_delayed_ref (fs_info, head->qgroup_ref_root, - head->qgroup_reserved); -return ret; - } -if (node->type ==BTRFS_TREE_BLOCK_REF_KEY || node->type ==BTRFS_SHARED_BLOCK_REF_KEY ) ret = run_delayed_tree_ref(trans, fs_info, node, extent_op,
@@ -2573,6 +2535,43 @@ static int cleanup_ref_head(struct btrfs_trans_handle *trans, delayed_refs->num_heads--;rb_erase (&head->href_node, &delayed_refs->href_root);spin_unlock (&delayed_refs->lock); +spin_unlock (&head->lock); +atomic_dec (&delayed_refs->num_entries); + +trace_run_delayed_ref_head (fs_info, &head->node, head, + head->node.action); + +if (head->total_ref_mod <0 ) { +struct btrfs_block_group_cache *cache; + + cache =btrfs_lookup_block_group (fs_info, head->node.bytenr); +ASSERT (cache); +percpu_counter_add (&cache->space_info->total_bytes_pinned, + -head->node.num_bytes); +btrfs_put_block_group (cache); + +if (head->is_data) { +spin_lock (&delayed_refs->lock); + delayed_refs->pending_csums -= head->node.num_bytes; +spin_unlock (&delayed_refs->lock); + } + } + +if (head->must_insert_reserved) { +btrfs_pin_extent (fs_info, head->node.bytenr, + head->node.num_bytes,1 ); +if (head->is_data) { + ret =btrfs_del_csums (trans, fs_info, + head->node.bytenr, + head->node.num_bytes); + } + } + +/* Also free its reserved qgroup space */ +btrfs_qgroup_free_delayed_ref (fs_info, head->qgroup_ref_root, + head->qgroup_reserved); +btrfs_delayed_ref_unlock (head); +btrfs_put_delayed_ref (&head->node);return 0 ; }
@@ -2656,6 +2655,10 @@ static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans,continue ; } +/* +* We're done processing refs in this ref_head, clean everything +* up and move on to the next ref_head. +*/ if (!ref) { ret =cleanup_ref_head (trans, fs_info, locked_ref);if (ret >0 ) {
@@ -2665,33 +2668,30 @@ static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans, }else if (ret) {return ret; } + locked_ref =NULL ; + count++; +continue ; + } -/* All delayed refs have been processed, Go ahead -* and send the head node to run_one_delayed_ref, -* so that any accounting fixes can happen -*/ - ref = &locked_ref->node; - }else { - actual_count++; - ref->in_tree =0 ; -list_del (&ref->list); -if (!list_empty (&ref->add_list)) -list_del (&ref->add_list); -/* -* when we play the delayed ref, also correct the -* ref_mod on head -*/ -switch (ref->action) { -case BTRFS_ADD_DELAYED_REF : -case BTRFS_ADD_DELAYED_EXTENT : - locked_ref->node.ref_mod -= ref->ref_mod; -break ; -case BTRFS_DROP_DELAYED_REF : - locked_ref->node.ref_mod += ref->ref_mod; -break ; -default : -WARN_ON (1 ); - } + actual_count++; + ref->in_tree =0 ; +list_del (&ref->list); +if (!list_empty (&ref->add_list)) +list_del (&ref->add_list); +/* +* when we play the delayed ref, also correct the +* ref_mod on head +*/ +switch (ref->action) { +case BTRFS_ADD_DELAYED_REF : +case BTRFS_ADD_DELAYED_EXTENT : + locked_ref->node.ref_mod -= ref->ref_mod; +break ; +case BTRFS_DROP_DELAYED_REF : + locked_ref->node.ref_mod += ref->ref_mod; +break ; +default : +WARN_ON (1 ); }atomic_dec (&delayed_refs->num_entries);
@@ -2718,22 +2718,6 @@ static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans,return ret; } -/* -* If this node is a head, that means all the refs in this head -* have been dealt with, and we will pick the next head to deal -* with, so we must unlock the head and drop it from the cluster -* list before we release it. -*/ -if (btrfs_delayed_ref_is_head (ref)) { -if (locked_ref->is_data && - locked_ref->total_ref_mod <0 ) { -spin_lock (&delayed_refs->lock); - delayed_refs->pending_csums -= ref->num_bytes; -spin_unlock (&delayed_refs->lock); - } -btrfs_delayed_ref_unlock (locked_ref); - locked_ref =NULL ; - }btrfs_put_delayed_ref (ref); count++;cond_resched ();
Signature · 1 line
2.7.4
PATCH 6/10 btrfs: remove delayed_ref_node from ref_head
In reply to [email protected]From: Josef Bacik <[email protected]>
This is just excessive information in the ref_head, and makes the code complicated. It is a relic from when we had the heads and the refs in the same tree, which is no longer the case. With this removal I've cleaned up a bunch of the cruft around this old assumption as well.
--- fs/btrfs/backref.c | 4 +- fs/btrfs/delayed-ref.c | 125 ++++++++++++++++++------------------------- fs/btrfs/delayed-ref.h | 49 ++++++----------- fs/btrfs/disk-io.c | 12 ++--- fs/btrfs/extent-tree.c | 90 ++++++++++++------------------- include/trace/events/btrfs.h | 15 +++--- 6 files changed, 119 insertions(+), 176 deletions(-)
fs/btrfs/backref.c +2 -2
@@ -1174,7 +1174,7 @@ static int find_parent_nodes(struct btrfs_trans_handle *trans, head =btrfs_find_delayed_ref_head (delayed_refs, bytenr);if (head) {if (!mutex_trylock (&head->mutex)) { -refcount_inc (&head->node.refs); +refcount_inc (&head->refs);spin_unlock (&delayed_refs->lock);btrfs_release_path (path);
@@ -1185,7 +1185,7 @@ static int find_parent_nodes(struct btrfs_trans_handle *trans,*/ mutex_lock (&head->mutex);mutex_unlock (&head->mutex); -btrfs_put_delayed_ref (&head->node); +btrfs_put_delayed_ref_head (head); goto again; }spin_unlock (&delayed_refs->lock);
fs/btrfs/delayed-ref.c +53 -72
273 lines not on this page · raw message
fs/btrfs/delayed-ref.h +17 -32
96 lines not on this page · raw message
fs/btrfs/disk-io.c +6 -6
@@ -4281,12 +4281,12 @@ static int btrfs_destroy_delayed_refs(struct btrfs_transaction *trans, head = rb_entry(node, struct btrfs_delayed_ref_head, href_node);if (!mutex_trylock (&head->mutex)) { -refcount_inc (&head->node.refs); +refcount_inc (&head->refs);spin_unlock (&delayed_refs->lock);mutex_lock (&head->mutex);mutex_unlock (&head->mutex); -btrfs_put_delayed_ref (&head->node); +btrfs_put_delayed_ref_head (head);spin_lock (&delayed_refs->lock);continue ; }
@@ -4307,16 +4307,16 @@ static int btrfs_destroy_delayed_refs(struct btrfs_transaction *trans,if (head->processing ==0 ) delayed_refs->num_heads_ready--;atomic_dec (&delayed_refs->num_entries); - head->node.in_tree =0 ;rb_erase (&head->href_node, &delayed_refs->href_root); +RB_CLEAR_NODE (&head->href_node);spin_unlock (&head->lock);spin_unlock (&delayed_refs->lock);mutex_unlock (&head->mutex);if (pin_bytes) -btrfs_pin_extent (fs_info, head->node.bytenr, - head->node.num_bytes,1 ); -btrfs_put_delayed_ref (&head->node); +btrfs_pin_extent (fs_info, head->bytenr, + head->num_bytes,1 ); +btrfs_put_delayed_ref_head (head);cond_resched ();spin_lock (&delayed_refs->lock); }
fs/btrfs/extent-tree.c +35 -55
220 lines not on this page · raw message
include/trace/events/btrfs.h +6 -9
@@ -791,22 +791,21 @@ DEFINE_EVENT(btrfs_delayed_data_ref, run_delayed_data_ref, DECLARE_EVENT_CLASS(btrfs_delayed_ref_head, TP_PROTO(const struct btrfs_fs_info *fs_info, - const struct btrfs_delayed_ref_node *ref, const struct btrfs_delayed_ref_head *head_ref, int action), - TP_ARGS(fs_info, ref, head_ref, action), + TP_ARGS(fs_info, head_ref, action), TP_STRUCT__entry_btrfs( __field( u64, bytenr ) __field( u64, num_bytes ) - __field( int, action ) + __field( int, action ) __field( int, is_data ) ), TP_fast_assign_btrfs(fs_info, - __entry->bytenr = ref->bytenr; - __entry->num_bytes = ref->num_bytes; + __entry->bytenr = head_ref->bytenr; + __entry->num_bytes = head_ref->num_bytes; __entry->action = action; __entry->is_data = head_ref->is_data; ),
@@ -821,21 +820,19 @@ DECLARE_EVENT_CLASS(btrfs_delayed_ref_head,DEFINE_EVENT (btrfs_delayed_ref_head, add_delayed_ref_head, TP_PROTO(const struct btrfs_fs_info *fs_info, - const struct btrfs_delayed_ref_node *ref, const struct btrfs_delayed_ref_head *head_ref, int action), -TP_ARGS (fs_info, ref, head_ref, action) +TP_ARGS (fs_info, head_ref, action) );DEFINE_EVENT (btrfs_delayed_ref_head, run_delayed_ref_head, TP_PROTO(const struct btrfs_fs_info *fs_info, - const struct btrfs_delayed_ref_node *ref, const struct btrfs_delayed_ref_head *head_ref, int action), -TP_ARGS (fs_info, ref, head_ref, action) +TP_ARGS (fs_info, head_ref, action) );#define show_chunk_type (type) \
Signature · 1 line
2.7.4
PATCH 7/10 btrfs: remove type argument from comp_tree_refs
In reply to [email protected]From: Josef Bacik <[email protected]>
We can get this from the ref we've passed in.
--- fs/btrfs/delayed-ref.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-)
fs/btrfs/delayed-ref.c +4 -6
@@ -41,9 +41,9 @@ struct kmem_cache *btrfs_delayed_extent_op_cachep;* compare two delayed tree backrefs with same bytenr and type */ static int comp_tree_refs (struct btrfs_delayed_tree_ref *ref2, -struct btrfs_delayed_tree_ref *ref1,int type) +struct btrfs_delayed_tree_ref *ref1) { -if (type ==BTRFS_TREE_BLOCK_REF_KEY ) { +if (ref1->node.type ==BTRFS_TREE_BLOCK_REF_KEY ) {if (ref1->root < ref2->root)return -1 ;if (ref1->root > ref2->root)
@@ -223,8 +223,7 @@ static bool merge_ref(struct btrfs_trans_handle *trans,if ((ref->type ==BTRFS_TREE_BLOCK_REF_KEY || ref->type ==BTRFS_SHARED_BLOCK_REF_KEY ) &&comp_tree_refs (btrfs_delayed_node_to_tree_ref (ref), -btrfs_delayed_node_to_tree_ref (next), - ref->type)) +btrfs_delayed_node_to_tree_ref (next))) goto next; if ((ref->type == BTRFS_EXTENT_DATA_REF_KEY || ref->type == BTRFS_SHARED_DATA_REF_KEY) &&
@@ -409,8 +408,7 @@ add_delayed_ref_tail_merge(struct btrfs_trans_handle *trans,if ((exist->type ==BTRFS_TREE_BLOCK_REF_KEY || exist->type ==BTRFS_SHARED_BLOCK_REF_KEY ) &&comp_tree_refs (btrfs_delayed_node_to_tree_ref (exist), -btrfs_delayed_node_to_tree_ref (ref), - ref->type)) +btrfs_delayed_node_to_tree_ref (ref))) goto add_tail; if ((exist->type == BTRFS_EXTENT_DATA_REF_KEY || exist->type == BTRFS_SHARED_DATA_REF_KEY) &&
Signature · 1 line
2.7.4
Re: [PATCH 07/10] btrfs: remove type argument from comp_tree_refs
2017-09-12 0:12 GMT+03:00 <[email protected]>:
Quoted 48 lines
From: Josef Bacik <[email protected]>
We can get this from the ref we've passed in.
Signed-off-by Josef Bacik <[email protected]>--- fs/btrfs/delayed-ref.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-)fs/btrfs/delayed-ref.c +4
-7@@ -41,9 +41,9 @@ struct kmem_cache *btrfs_delayed_extent_op_cachep;* compare two delayed tree backrefs with same bytenr and type */ static int comp_tree_refs (struct btrfs_delayed_tree_ref *ref2, -struct btrfs_delayed_tree_ref *ref1,int type) +struct btrfs_delayed_tree_ref *ref1) { -if (type ==BTRFS_TREE_BLOCK_REF_KEY ) { +if (ref1->node.type ==BTRFS_TREE_BLOCK_REF_KEY ) {if (ref1->root < ref2->root)return -1 ;if (ref1->root > ref2->root)@@ -223,8 +223,7 @@ static bool merge_ref(struct btrfs_trans_handle *trans,if ((ref->type ==BTRFS_TREE_BLOCK_REF_KEY || ref->type ==BTRFS_SHARED_BLOCK_REF_KEY ) &&comp_tree_refs (btrfs_delayed_node_to_tree_ref (ref), -btrfs_delayed_node_to_tree_ref (next), - ref->type)) +btrfs_delayed_node_to_tree_ref (next))) goto next; if ((ref->type == BTRFS_EXTENT_DATA_REF_KEY || ref->type == BTRFS_SHARED_DATA_REF_KEY) &&@@ -409,8 +408,7 @@ add_delayed_ref_tail_merge(struct btrfs_trans_handle *trans,if ((exist->type ==BTRFS_TREE_BLOCK_REF_KEY || exist->type ==BTRFS_SHARED_BLOCK_REF_KEY ) &&comp_tree_refs (btrfs_delayed_node_to_tree_ref (exist), -btrfs_delayed_node_to_tree_ref (ref), - ref->type)) +btrfs_delayed_node_to_tree_ref (ref))) goto add_tail; if ((exist->type == BTRFS_EXTENT_DATA_REF_KEY || exist->type == BTRFS_SHARED_DATA_REF_KEY) && --2.7.4
Quoted 4 lines
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Signature · 2 lines
Have a nice day, Timofey.
PATCH 8/10 btrfs: switch args for comp_*_refs
In reply to [email protected]From: Josef Bacik <[email protected]>
Because seriously? ref2 and then ref1?
--- fs/btrfs/delayed-ref.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
fs/btrfs/delayed-ref.c +4 -4
@@ -40,8 +40,8 @@ struct kmem_cache *btrfs_delayed_extent_op_cachep;/* * compare two delayed tree backrefs with same bytenr and type */ -static int comp_tree_refs (struct btrfs_delayed_tree_ref *ref2, -struct btrfs_delayed_tree_ref *ref1) +static int comp_tree_refs (struct btrfs_delayed_tree_ref *ref1, +struct btrfs_delayed_tree_ref *ref2) {if (ref1->node.type ==BTRFS_TREE_BLOCK_REF_KEY ) {if (ref1->root < ref2->root)
@@ -60,8 +60,8 @@ static int comp_tree_refs(struct btrfs_delayed_tree_ref *ref2,/* * compare two delayed data backrefs with same bytenr and type */ -static int comp_data_refs (struct btrfs_delayed_data_ref *ref2, -struct btrfs_delayed_data_ref *ref1) +static int comp_data_refs (struct btrfs_delayed_data_ref *ref1, +struct btrfs_delayed_data_ref *ref2) {if (ref1->node.type ==BTRFS_EXTENT_DATA_REF_KEY ) {if (ref1->root < ref2->root)
Signature · 1 line
2.7.4
Re: [PATCH 08/10] btrfs: switch args for comp_*_refs
2017-09-12 0:12 GMT+03:00 <[email protected]>:
Quoted 38 lines
From: Josef Bacik <[email protected]>
Because seriously? ref2 and then ref1?
Signed-off-by Josef Bacik <[email protected]>--- fs/btrfs/delayed-ref.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)fs/btrfs/delayed-ref.c +4
-5@@ -40,8 +40,8 @@ struct kmem_cache *btrfs_delayed_extent_op_cachep;/* * compare two delayed tree backrefs with same bytenr and type */ -static int comp_tree_refs (struct btrfs_delayed_tree_ref *ref2, -struct btrfs_delayed_tree_ref *ref1) +static int comp_tree_refs (struct btrfs_delayed_tree_ref *ref1, +struct btrfs_delayed_tree_ref *ref2) {if (ref1->node.type ==BTRFS_TREE_BLOCK_REF_KEY ) {if (ref1->root < ref2->root)@@ -60,8 +60,8 @@ static int comp_tree_refs(struct btrfs_delayed_tree_ref *ref2,/* * compare two delayed data backrefs with same bytenr and type */ -static int comp_data_refs (struct btrfs_delayed_data_ref *ref2, -struct btrfs_delayed_data_ref *ref1) +static int comp_data_refs (struct btrfs_delayed_data_ref *ref1, +struct btrfs_delayed_data_ref *ref2) {if (ref1->node.type ==BTRFS_EXTENT_DATA_REF_KEY ) {if (ref1->root < ref2->root) --2.7.4
Quoted 4 lines
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Result of that function does not used anywhere (i.e. only as bool), may be let's rename that like:
compr_tree_refs_eq()
and simplify to
if (ref1->root != ref2->root)
return 0;
(same for comp_data_refs())
This can made that more obvious and remove useless if. as that not used in any "sorting" code
Thanks.
Signature · 2 lines
Have a nice day, Timofey.
Re: [PATCH 08/10] btrfs: switch args for comp_*_refs
2017-09-12 19:21 GMT+03:00 Timofey Titovets <[email protected]>:
Quoted 56 lines
2017-09-12 0:12 GMT+03:00 <[email protected]>:
Quoted 38 lines
From: Josef Bacik <[email protected]>
Because seriously? ref2 and then ref1?
Signed-off-by Josef Bacik <[email protected]>--- fs/btrfs/delayed-ref.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)fs/btrfs/delayed-ref.c +4
-5@@ -40,8 +40,8 @@ struct kmem_cache *btrfs_delayed_extent_op_cachep;/* * compare two delayed tree backrefs with same bytenr and type */ -static int comp_tree_refs (struct btrfs_delayed_tree_ref *ref2, -struct btrfs_delayed_tree_ref *ref1) +static int comp_tree_refs (struct btrfs_delayed_tree_ref *ref1, +struct btrfs_delayed_tree_ref *ref2) {if (ref1->node.type ==BTRFS_TREE_BLOCK_REF_KEY ) {if (ref1->root < ref2->root)@@ -60,8 +60,8 @@ static int comp_tree_refs(struct btrfs_delayed_tree_ref *ref2,/* * compare two delayed data backrefs with same bytenr and type */ -static int comp_data_refs (struct btrfs_delayed_data_ref *ref2, -struct btrfs_delayed_data_ref *ref1) +static int comp_data_refs (struct btrfs_delayed_data_ref *ref1, +struct btrfs_delayed_data_ref *ref2) {if (ref1->node.type ==BTRFS_EXTENT_DATA_REF_KEY ) {if (ref1->root < ref2->root) --2.7.4
Quoted 4 lines
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.htmlResult of that function does not used anywhere (i.e. only as bool), may be let's rename that like:
compr_tree_refs_eq()
and simplify to
if (ref1->root!=ref2->root)return 0;(same for
comp_data_refs())This can made that more obvious and remove useless if. as that not used in any "sorting" code
Quoted 7 lines
Thanks.
Reviewed-by Timofey Titovets <[email protected]>--
Have a nice day,
Timofey.
Oh, sorry, you want convert list to RB tree, so result will be used. Just ignore my comment.
Signature · 2 lines
Have a nice day, Timofey.
PATCH 9/10 btrfs: add a comp_refs() helper
In reply to [email protected]From: Josef Bacik <[email protected]>
Instead of open-coding the delayed ref comparisons, add a helper to do the comparisons generically and use that everywhere. We compare sequence numbers last for following patches.
--- fs/btrfs/delayed-ref.c | 54 ++++++++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 24 deletions(-)
fs/btrfs/delayed-ref.c +30 -24
@@ -85,6 +85,34 @@ static int comp_data_refs(struct btrfs_delayed_data_ref *ref1,return 0 ; } +static int comp_refs (struct btrfs_delayed_ref_node *ref1, +struct btrfs_delayed_ref_node *ref2, +bool check_seq) +{ +int ret =0 ; +if (ref1->type < ref2->type) +return -1 ; +if (ref1->type > ref2->type) +return 1 ; +if (ref1->type ==BTRFS_TREE_BLOCK_REF_KEY || + ref1->type ==BTRFS_SHARED_BLOCK_REF_KEY ) + ret =comp_tree_refs (btrfs_delayed_node_to_tree_ref (ref1), +btrfs_delayed_node_to_tree_ref (ref2)); +else + ret =comp_data_refs (btrfs_delayed_node_to_data_ref (ref1), +btrfs_delayed_node_to_data_ref (ref2)); +if (ret) +return ret; +if (check_seq) { +if (ref1->seq < ref2->seq) +return -1 ; +if (ref1->seq > ref2->seq) +return 1 ; + } +return 0 ; +} + +/* insert a new ref to head ref rbtree */ static struct btrfs_delayed_ref_head *htree_insert (struct rb_root *root,struct rb_node *node)
@@ -217,18 +245,7 @@ static bool merge_ref(struct btrfs_trans_handle *trans,if (seq && next->seq >= seq) goto next; -if (next->type != ref->type) - goto next; - -if ((ref->type ==BTRFS_TREE_BLOCK_REF_KEY || - ref->type ==BTRFS_SHARED_BLOCK_REF_KEY ) && -comp_tree_refs (btrfs_delayed_node_to_tree_ref (ref), -btrfs_delayed_node_to_tree_ref (next))) - goto next; -if ((ref->type ==BTRFS_EXTENT_DATA_REF_KEY || - ref->type ==BTRFS_SHARED_DATA_REF_KEY ) && -comp_data_refs (btrfs_delayed_node_to_data_ref (ref), -btrfs_delayed_node_to_data_ref (next))) +if (comp_refs (ref, next, false)) goto next;if (ref->action == next->action) {
@@ -402,18 +419,7 @@ add_delayed_ref_tail_merge(struct btrfs_trans_handle *trans, exist = list_entry(href->ref_list.prev, struct btrfs_delayed_ref_node, list);/* No need to compare bytenr nor is_head */ -if (exist->type != ref->type || exist->seq != ref->seq) - goto add_tail; - -if ((exist->type ==BTRFS_TREE_BLOCK_REF_KEY || - exist->type ==BTRFS_SHARED_BLOCK_REF_KEY ) && -comp_tree_refs (btrfs_delayed_node_to_tree_ref (exist), -btrfs_delayed_node_to_tree_ref (ref))) - goto add_tail; -if ((exist->type ==BTRFS_EXTENT_DATA_REF_KEY || - exist->type ==BTRFS_SHARED_DATA_REF_KEY ) && -comp_data_refs (btrfs_delayed_node_to_data_ref (exist), -btrfs_delayed_node_to_data_ref (ref))) +if (comp_refs (exist, ref, true)) goto add_tail;/* Now we are sure we can merge */
Signature · 1 line
2.7.4
PATCH 10/10 btrfs: track refs in a rb_tree instead of a list
In reply to [email protected]From: Josef Bacik <[email protected]>
If we get a significant amount of delayed refs for a single block (think modifying multiple snapshots) we can end up spending an ungodly amount of time looping through all of the entries trying to see if they can be merged. This is because we only add them to a list, so we have O(2n) for every ref head. This doesn't make any sense as we likely have refs for different roots, and so they cannot be merged. Tracking in a tree will allow us to break as soon as we hit an entry that doesn't match, making our worst case O(n).
With this we can also merge entries more easily. Before we had to hope that matching refs were on the ends of our list, but with the tree we can search down to exact matches and merge them at insert time.
--- fs/btrfs/backref.c | 5 ++- fs/btrfs/delayed-ref.c | 107 +++++++++++++++++++++++++------------------------ fs/btrfs/delayed-ref.h | 5 +-- fs/btrfs/disk-io.c | 10 +++-- fs/btrfs/extent-tree.c | 21 ++++++---- 5 files changed, 81 insertions(+), 67 deletions(-)
fs/btrfs/backref.c +4 -1
@@ -769,6 +769,7 @@ static int add_delayed_refs(const struct btrfs_fs_info *fs_info,struct btrfs_key key;struct btrfs_key tmp_op_key;struct btrfs_key *op_key =NULL ; +struct rb_node *n;int count;int ret =0 ;
@@ -778,7 +779,9 @@ static int add_delayed_refs(const struct btrfs_fs_info *fs_info, }spin_lock (&head->lock); -list_for_each_entry (node, &head->ref_list, list) { +for (n =rb_first (&head->ref_tree); n; n =rb_next (n)) { + node = rb_entry(n, struct btrfs_delayed_ref_node, + ref_node);if (node->seq > seq)continue ;
fs/btrfs/delayed-ref.c +55 -52
203 lines not on this page · raw message
fs/btrfs/delayed-ref.h +2 -3
@@ -27,8 +27,7 @@#define BTRFS_UPDATE_DELAYED_HEAD 4/* not changing ref count on head ref */ struct btrfs_delayed_ref_node { -/*data/tree ref use list, stored in ref_head->ref_list. */ -struct list_head list; +struct rb_node ref_node;/* * If action is BTRFS_ADD_DELAYED_REF, also link this node to * ref_head->ref_add_list, then we do not need to iterate the
@@ -91,7 +90,7 @@ struct btrfs_delayed_ref_head {struct mutex mutex;spinlock_t lock; -struct list_head ref_list; +struct rb_root ref_tree;/* accumulate add BTRFS_ADD_DELAYED_REF nodes to this ref_add_list. */ struct list_head ref_add_list;
fs/btrfs/disk-io.c +6 -4
@@ -4275,7 +4275,7 @@ static int btrfs_destroy_delayed_refs(struct btrfs_transaction *trans,while ((node =rb_first (&delayed_refs->href_root)) !=NULL ) {struct btrfs_delayed_ref_head *head; -struct btrfs_delayed_ref_node *tmp; +struct rb_node *n;bool pin_bytes = false; head = rb_entry(node, struct btrfs_delayed_ref_head,
@@ -4291,10 +4291,12 @@ static int btrfs_destroy_delayed_refs(struct btrfs_transaction *trans,continue ; }spin_lock (&head->lock); -list_for_each_entry_safe_reverse (ref, tmp, &head->ref_list, - list) { +while ((n =rb_first (&head->ref_tree)) !=NULL ) { + ref = rb_entry(n, struct btrfs_delayed_ref_node, + ref_node); ref->in_tree =0 ; -list_del (&ref->list); +rb_erase (&ref->ref_node, &head->ref_tree); +RB_CLEAR_NODE (&ref->ref_node);if (!list_empty (&ref->add_list))list_del (&ref->add_list);atomic_dec (&delayed_refs->num_entries);
fs/btrfs/extent-tree.c +14 -7
@@ -2451,7 +2451,7 @@ select_delayed_ref(struct btrfs_delayed_ref_head *head) {struct btrfs_delayed_ref_node *ref; -if (list_empty (&head->ref_list)) +if (RB_EMPTY_ROOT (&head->ref_tree))return NULL ;/*
@@ -2464,8 +2464,8 @@ select_delayed_ref(struct btrfs_delayed_ref_head *head)return list_first_entry (&head->ref_add_list, struct btrfs_delayed_ref_node, add_list); - ref = list_first_entry(&head->ref_list, struct btrfs_delayed_ref_node, - list); + ref =rb_entry (rb_first (&head->ref_tree), + struct btrfs_delayed_ref_node, ref_node);ASSERT (list_empty (&ref->add_list));return ref; }
@@ -2526,7 +2526,7 @@ static int cleanup_ref_head(struct btrfs_trans_handle *trans,spin_unlock (&head->lock);spin_lock (&delayed_refs->lock);spin_lock (&head->lock); -if (!list_empty (&head->ref_list) || head->extent_op) { +if (!RB_EMPTY_ROOT (&head->ref_tree) || head->extent_op) {spin_unlock (&head->lock);spin_unlock (&delayed_refs->lock);return 1 ;
@@ -2673,7 +2673,8 @@ static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans, actual_count++; ref->in_tree =0 ; -list_del (&ref->list); +rb_erase (&ref->ref_node, &locked_ref->ref_tree); +RB_CLEAR_NODE (&ref->ref_node);if (!list_empty (&ref->add_list))list_del (&ref->add_list);/*
@@ -3071,6 +3072,7 @@ static noinline int check_delayed_ref(struct btrfs_root *root,struct btrfs_delayed_data_ref *data_ref;struct btrfs_delayed_ref_root *delayed_refs;struct btrfs_transaction *cur_trans; +struct rb_node *node;int ret =0 ; cur_trans = root->fs_info->running_transaction;
@@ -3103,7 +3105,12 @@ static noinline int check_delayed_ref(struct btrfs_root *root,spin_unlock (&delayed_refs->lock);spin_lock (&head->lock); -list_for_each_entry (ref, &head->ref_list, list) { +/* +* XXX: We should replace this with a proper search function in the +* future. +*/ +for (node =rb_first (&head->ref_tree); node; node =rb_next (node)) { + ref = rb_entry(node, struct btrfs_delayed_ref_node, ref_node);/* If it's a shared ref we know a cross reference exists */ if (ref->type !=BTRFS_EXTENT_DATA_REF_KEY ) { ret =1 ;
@@ -7071,7 +7078,7 @@ static noinline int check_ref_cleanup(struct btrfs_trans_handle *trans, goto out_delayed_unlock;spin_lock (&head->lock); -if (!list_empty (&head->ref_list)) +if (!RB_EMPTY_ROOT (&head->ref_tree)) goto out;if (head->extent_op) {
Signature · 1 line
2.7.4