diff options
Diffstat (limited to 'fs/btrfs/delayed-ref.c')
| -rw-r--r-- | fs/btrfs/delayed-ref.c | 163 | 
1 files changed, 128 insertions, 35 deletions
diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c index da7419ed01b..ae941177339 100644 --- a/fs/btrfs/delayed-ref.c +++ b/fs/btrfs/delayed-ref.c @@ -38,17 +38,14 @@  static int comp_tree_refs(struct btrfs_delayed_tree_ref *ref2,  			  struct btrfs_delayed_tree_ref *ref1)  { -	if (ref1->node.type == BTRFS_TREE_BLOCK_REF_KEY) { -		if (ref1->root < ref2->root) -			return -1; -		if (ref1->root > ref2->root) -			return 1; -	} else { -		if (ref1->parent < ref2->parent) -			return -1; -		if (ref1->parent > ref2->parent) -			return 1; -	} +	if (ref1->root < ref2->root) +		return -1; +	if (ref1->root > ref2->root) +		return 1; +	if (ref1->parent < ref2->parent) +		return -1; +	if (ref1->parent > ref2->parent) +		return 1;  	return 0;  } @@ -85,7 +82,8 @@ static int comp_data_refs(struct btrfs_delayed_data_ref *ref2,   * type of the delayed backrefs and content of delayed backrefs.   */  static int comp_entry(struct btrfs_delayed_ref_node *ref2, -		      struct btrfs_delayed_ref_node *ref1) +		      struct btrfs_delayed_ref_node *ref1, +		      bool compare_seq)  {  	if (ref1->bytenr < ref2->bytenr)  		return -1; @@ -102,10 +100,12 @@ static int comp_entry(struct btrfs_delayed_ref_node *ref2,  	if (ref1->type > ref2->type)  		return 1;  	/* merging of sequenced refs is not allowed */ -	if (ref1->seq < ref2->seq) -		return -1; -	if (ref1->seq > ref2->seq) -		return 1; +	if (compare_seq) { +		if (ref1->seq < ref2->seq) +			return -1; +		if (ref1->seq > ref2->seq) +			return 1; +	}  	if (ref1->type == BTRFS_TREE_BLOCK_REF_KEY ||  	    ref1->type == BTRFS_SHARED_BLOCK_REF_KEY) {  		return comp_tree_refs(btrfs_delayed_node_to_tree_ref(ref2), @@ -139,7 +139,7 @@ static struct btrfs_delayed_ref_node *tree_insert(struct rb_root *root,  		entry = rb_entry(parent_node, struct btrfs_delayed_ref_node,  				 rb_node); -		cmp = comp_entry(entry, ins); +		cmp = comp_entry(entry, ins, 1);  		if (cmp < 0)  			p = &(*p)->rb_left;  		else if (cmp > 0) @@ -233,6 +233,114 @@ int btrfs_delayed_ref_lock(struct btrfs_trans_handle *trans,  	return 0;  } +static void inline drop_delayed_ref(struct btrfs_trans_handle *trans, +				    struct btrfs_delayed_ref_root *delayed_refs, +				    struct btrfs_delayed_ref_node *ref) +{ +	rb_erase(&ref->rb_node, &delayed_refs->root); +	ref->in_tree = 0; +	btrfs_put_delayed_ref(ref); +	delayed_refs->num_entries--; +	if (trans->delayed_ref_updates) +		trans->delayed_ref_updates--; +} + +static int merge_ref(struct btrfs_trans_handle *trans, +		     struct btrfs_delayed_ref_root *delayed_refs, +		     struct btrfs_delayed_ref_node *ref, u64 seq) +{ +	struct rb_node *node; +	int merged = 0; +	int mod = 0; +	int done = 0; + +	node = rb_prev(&ref->rb_node); +	while (node) { +		struct btrfs_delayed_ref_node *next; + +		next = rb_entry(node, struct btrfs_delayed_ref_node, rb_node); +		node = rb_prev(node); +		if (next->bytenr != ref->bytenr) +			break; +		if (seq && next->seq >= seq) +			break; +		if (comp_entry(ref, next, 0)) +			continue; + +		if (ref->action == next->action) { +			mod = next->ref_mod; +		} else { +			if (ref->ref_mod < next->ref_mod) { +				struct btrfs_delayed_ref_node *tmp; + +				tmp = ref; +				ref = next; +				next = tmp; +				done = 1; +			} +			mod = -next->ref_mod; +		} + +		merged++; +		drop_delayed_ref(trans, delayed_refs, next); +		ref->ref_mod += mod; +		if (ref->ref_mod == 0) { +			drop_delayed_ref(trans, delayed_refs, ref); +			break; +		} else { +			/* +			 * You can't have multiples of the same ref on a tree +			 * block. +			 */ +			WARN_ON(ref->type == BTRFS_TREE_BLOCK_REF_KEY || +				ref->type == BTRFS_SHARED_BLOCK_REF_KEY); +		} + +		if (done) +			break; +		node = rb_prev(&ref->rb_node); +	} + +	return merged; +} + +void btrfs_merge_delayed_refs(struct btrfs_trans_handle *trans, +			      struct btrfs_fs_info *fs_info, +			      struct btrfs_delayed_ref_root *delayed_refs, +			      struct btrfs_delayed_ref_head *head) +{ +	struct rb_node *node; +	u64 seq = 0; + +	spin_lock(&fs_info->tree_mod_seq_lock); +	if (!list_empty(&fs_info->tree_mod_seq_list)) { +		struct seq_list *elem; + +		elem = list_first_entry(&fs_info->tree_mod_seq_list, +					struct seq_list, list); +		seq = elem->seq; +	} +	spin_unlock(&fs_info->tree_mod_seq_lock); + +	node = rb_prev(&head->node.rb_node); +	while (node) { +		struct btrfs_delayed_ref_node *ref; + +		ref = rb_entry(node, struct btrfs_delayed_ref_node, +			       rb_node); +		if (ref->bytenr != head->node.bytenr) +			break; + +		/* We can't merge refs that are outside of our seq count */ +		if (seq && ref->seq >= seq) +			break; +		if (merge_ref(trans, delayed_refs, ref, seq)) +			node = rb_prev(&head->node.rb_node); +		else +			node = rb_prev(node); +	} +} +  int btrfs_check_delayed_seq(struct btrfs_fs_info *fs_info,  			    struct btrfs_delayed_ref_root *delayed_refs,  			    u64 seq) @@ -336,18 +444,11 @@ update_existing_ref(struct btrfs_trans_handle *trans,  		 * every changing the extent allocation tree.  		 */  		existing->ref_mod--; -		if (existing->ref_mod == 0) { -			rb_erase(&existing->rb_node, -				 &delayed_refs->root); -			existing->in_tree = 0; -			btrfs_put_delayed_ref(existing); -			delayed_refs->num_entries--; -			if (trans->delayed_ref_updates) -				trans->delayed_ref_updates--; -		} else { +		if (existing->ref_mod == 0) +			drop_delayed_ref(trans, delayed_refs, existing); +		else  			WARN_ON(existing->type == BTRFS_TREE_BLOCK_REF_KEY ||  				existing->type == BTRFS_SHARED_BLOCK_REF_KEY); -		}  	} else {  		WARN_ON(existing->type == BTRFS_TREE_BLOCK_REF_KEY ||  			existing->type == BTRFS_SHARED_BLOCK_REF_KEY); @@ -662,9 +763,6 @@ int btrfs_add_delayed_tree_ref(struct btrfs_fs_info *fs_info,  	add_delayed_tree_ref(fs_info, trans, &ref->node, bytenr,  				   num_bytes, parent, ref_root, level, action,  				   for_cow); -	if (!need_ref_seq(for_cow, ref_root) && -	    waitqueue_active(&fs_info->tree_mod_seq_wait)) -		wake_up(&fs_info->tree_mod_seq_wait);  	spin_unlock(&delayed_refs->lock);  	if (need_ref_seq(for_cow, ref_root))  		btrfs_qgroup_record_ref(trans, &ref->node, extent_op); @@ -713,9 +811,6 @@ int btrfs_add_delayed_data_ref(struct btrfs_fs_info *fs_info,  	add_delayed_data_ref(fs_info, trans, &ref->node, bytenr,  				   num_bytes, parent, ref_root, owner, offset,  				   action, for_cow); -	if (!need_ref_seq(for_cow, ref_root) && -	    waitqueue_active(&fs_info->tree_mod_seq_wait)) -		wake_up(&fs_info->tree_mod_seq_wait);  	spin_unlock(&delayed_refs->lock);  	if (need_ref_seq(for_cow, ref_root))  		btrfs_qgroup_record_ref(trans, &ref->node, extent_op); @@ -744,8 +839,6 @@ int btrfs_add_delayed_extent_op(struct btrfs_fs_info *fs_info,  				   num_bytes, BTRFS_UPDATE_DELAYED_HEAD,  				   extent_op->is_data); -	if (waitqueue_active(&fs_info->tree_mod_seq_wait)) -		wake_up(&fs_info->tree_mod_seq_wait);  	spin_unlock(&delayed_refs->lock);  	return 0;  }  |