diff options
author | Qu Wenruo <quwenruo@cn.fujitsu.com> | 2015-10-26 02:11:18 -0400 |
---|---|---|
committer | Chris Mason <clm@fb.com> | 2015-10-26 22:44:39 -0400 |
commit | 5846a3c26873e86b034c702a8bc202aa76082369 (patch) | |
tree | 8495bf1ae8236d363579751a63414ba31c498a0d | |
parent | 696249132158014d594896df3a81390616069c5c (diff) |
btrfs: qgroup: Fix a race in delayed_ref which leads to abort trans
Between btrfs_allocerved_file_extent() and
btrfs_add_delayed_qgroup_reserve(), there is a window that delayed_refs
are run and delayed ref head maybe freed before
btrfs_add_delayed_qgroup_reserve().
This will cause btrfs_dad_delayed_qgroup_reserve() to return -ENOENT,
and cause transaction to be aborted.
This patch will record qgroup reserve space info into delayed_ref_head
at btrfs_add_delayed_ref(), to eliminate the race window.
Reported-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com>
Signed-off-by: Chris Mason <clm@fb.com>
-rw-r--r-- | fs/btrfs/ctree.h | 3 | ||||
-rw-r--r-- | fs/btrfs/delayed-ref.c | 22 | ||||
-rw-r--r-- | fs/btrfs/delayed-ref.h | 2 | ||||
-rw-r--r-- | fs/btrfs/extent-tree.c | 14 | ||||
-rw-r--r-- | fs/btrfs/inode.c | 12 |
5 files changed, 32 insertions, 21 deletions
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 4001585ec434..a2e73f6053a8 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h | |||
@@ -3430,7 +3430,8 @@ void btrfs_free_tree_block(struct btrfs_trans_handle *trans, | |||
3430 | int btrfs_alloc_reserved_file_extent(struct btrfs_trans_handle *trans, | 3430 | int btrfs_alloc_reserved_file_extent(struct btrfs_trans_handle *trans, |
3431 | struct btrfs_root *root, | 3431 | struct btrfs_root *root, |
3432 | u64 root_objectid, u64 owner, | 3432 | u64 root_objectid, u64 owner, |
3433 | u64 offset, struct btrfs_key *ins); | 3433 | u64 offset, u64 ram_bytes, |
3434 | struct btrfs_key *ins); | ||
3434 | int btrfs_alloc_logged_file_extent(struct btrfs_trans_handle *trans, | 3435 | int btrfs_alloc_logged_file_extent(struct btrfs_trans_handle *trans, |
3435 | struct btrfs_root *root, | 3436 | struct btrfs_root *root, |
3436 | u64 root_objectid, u64 owner, u64 offset, | 3437 | u64 root_objectid, u64 owner, u64 offset, |
diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c index 1c3588a70ce6..e06dd75ad13f 100644 --- a/fs/btrfs/delayed-ref.c +++ b/fs/btrfs/delayed-ref.c | |||
@@ -535,7 +535,8 @@ add_delayed_ref_head(struct btrfs_fs_info *fs_info, | |||
535 | struct btrfs_trans_handle *trans, | 535 | struct btrfs_trans_handle *trans, |
536 | struct btrfs_delayed_ref_node *ref, | 536 | struct btrfs_delayed_ref_node *ref, |
537 | struct btrfs_qgroup_extent_record *qrecord, | 537 | struct btrfs_qgroup_extent_record *qrecord, |
538 | u64 bytenr, u64 num_bytes, int action, int is_data) | 538 | u64 bytenr, u64 num_bytes, u64 ref_root, u64 reserved, |
539 | int action, int is_data) | ||
539 | { | 540 | { |
540 | struct btrfs_delayed_ref_head *existing; | 541 | struct btrfs_delayed_ref_head *existing; |
541 | struct btrfs_delayed_ref_head *head_ref = NULL; | 542 | struct btrfs_delayed_ref_head *head_ref = NULL; |
@@ -544,6 +545,9 @@ add_delayed_ref_head(struct btrfs_fs_info *fs_info, | |||
544 | int count_mod = 1; | 545 | int count_mod = 1; |
545 | int must_insert_reserved = 0; | 546 | int must_insert_reserved = 0; |
546 | 547 | ||
548 | /* If reserved is provided, it must be a data extent. */ | ||
549 | BUG_ON(!is_data && reserved); | ||
550 | |||
547 | /* | 551 | /* |
548 | * the head node stores the sum of all the mods, so dropping a ref | 552 | * the head node stores the sum of all the mods, so dropping a ref |
549 | * should drop the sum in the head node by one. | 553 | * should drop the sum in the head node by one. |
@@ -593,6 +597,11 @@ add_delayed_ref_head(struct btrfs_fs_info *fs_info, | |||
593 | 597 | ||
594 | /* Record qgroup extent info if provided */ | 598 | /* Record qgroup extent info if provided */ |
595 | if (qrecord) { | 599 | if (qrecord) { |
600 | if (ref_root && reserved) { | ||
601 | head_ref->qgroup_ref_root = ref_root; | ||
602 | head_ref->qgroup_reserved = reserved; | ||
603 | } | ||
604 | |||
596 | qrecord->bytenr = bytenr; | 605 | qrecord->bytenr = bytenr; |
597 | qrecord->num_bytes = num_bytes; | 606 | qrecord->num_bytes = num_bytes; |
598 | qrecord->old_roots = NULL; | 607 | qrecord->old_roots = NULL; |
@@ -611,6 +620,8 @@ add_delayed_ref_head(struct btrfs_fs_info *fs_info, | |||
611 | existing = htree_insert(&delayed_refs->href_root, | 620 | existing = htree_insert(&delayed_refs->href_root, |
612 | &head_ref->href_node); | 621 | &head_ref->href_node); |
613 | if (existing) { | 622 | if (existing) { |
623 | WARN_ON(ref_root && reserved && existing->qgroup_ref_root | ||
624 | && existing->qgroup_reserved); | ||
614 | update_existing_head_ref(delayed_refs, &existing->node, ref); | 625 | update_existing_head_ref(delayed_refs, &existing->node, ref); |
615 | /* | 626 | /* |
616 | * we've updated the existing ref, free the newly | 627 | * we've updated the existing ref, free the newly |
@@ -777,7 +788,7 @@ int btrfs_add_delayed_tree_ref(struct btrfs_fs_info *fs_info, | |||
777 | * the spin lock | 788 | * the spin lock |
778 | */ | 789 | */ |
779 | head_ref = add_delayed_ref_head(fs_info, trans, &head_ref->node, record, | 790 | head_ref = add_delayed_ref_head(fs_info, trans, &head_ref->node, record, |
780 | bytenr, num_bytes, action, 0); | 791 | bytenr, num_bytes, 0, 0, action, 0); |
781 | 792 | ||
782 | add_delayed_tree_ref(fs_info, trans, head_ref, &ref->node, bytenr, | 793 | add_delayed_tree_ref(fs_info, trans, head_ref, &ref->node, bytenr, |
783 | num_bytes, parent, ref_root, level, action); | 794 | num_bytes, parent, ref_root, level, action); |
@@ -800,7 +811,7 @@ int btrfs_add_delayed_data_ref(struct btrfs_fs_info *fs_info, | |||
800 | struct btrfs_trans_handle *trans, | 811 | struct btrfs_trans_handle *trans, |
801 | u64 bytenr, u64 num_bytes, | 812 | u64 bytenr, u64 num_bytes, |
802 | u64 parent, u64 ref_root, | 813 | u64 parent, u64 ref_root, |
803 | u64 owner, u64 offset, int action, | 814 | u64 owner, u64 offset, u64 reserved, int action, |
804 | struct btrfs_delayed_extent_op *extent_op) | 815 | struct btrfs_delayed_extent_op *extent_op) |
805 | { | 816 | { |
806 | struct btrfs_delayed_data_ref *ref; | 817 | struct btrfs_delayed_data_ref *ref; |
@@ -839,7 +850,8 @@ int btrfs_add_delayed_data_ref(struct btrfs_fs_info *fs_info, | |||
839 | * the spin lock | 850 | * the spin lock |
840 | */ | 851 | */ |
841 | head_ref = add_delayed_ref_head(fs_info, trans, &head_ref->node, record, | 852 | head_ref = add_delayed_ref_head(fs_info, trans, &head_ref->node, record, |
842 | bytenr, num_bytes, action, 1); | 853 | bytenr, num_bytes, ref_root, reserved, |
854 | action, 1); | ||
843 | 855 | ||
844 | add_delayed_data_ref(fs_info, trans, head_ref, &ref->node, bytenr, | 856 | add_delayed_data_ref(fs_info, trans, head_ref, &ref->node, bytenr, |
845 | num_bytes, parent, ref_root, owner, offset, | 857 | num_bytes, parent, ref_root, owner, offset, |
@@ -894,7 +906,7 @@ int btrfs_add_delayed_extent_op(struct btrfs_fs_info *fs_info, | |||
894 | spin_lock(&delayed_refs->lock); | 906 | spin_lock(&delayed_refs->lock); |
895 | 907 | ||
896 | add_delayed_ref_head(fs_info, trans, &head_ref->node, NULL, bytenr, | 908 | add_delayed_ref_head(fs_info, trans, &head_ref->node, NULL, bytenr, |
897 | num_bytes, BTRFS_UPDATE_DELAYED_HEAD, | 909 | num_bytes, 0, 0, BTRFS_UPDATE_DELAYED_HEAD, |
898 | extent_op->is_data); | 910 | extent_op->is_data); |
899 | 911 | ||
900 | spin_unlock(&delayed_refs->lock); | 912 | spin_unlock(&delayed_refs->lock); |
diff --git a/fs/btrfs/delayed-ref.h b/fs/btrfs/delayed-ref.h index f9cf2345b864..00ed02cbf3e9 100644 --- a/fs/btrfs/delayed-ref.h +++ b/fs/btrfs/delayed-ref.h | |||
@@ -248,7 +248,7 @@ int btrfs_add_delayed_data_ref(struct btrfs_fs_info *fs_info, | |||
248 | struct btrfs_trans_handle *trans, | 248 | struct btrfs_trans_handle *trans, |
249 | u64 bytenr, u64 num_bytes, | 249 | u64 bytenr, u64 num_bytes, |
250 | u64 parent, u64 ref_root, | 250 | u64 parent, u64 ref_root, |
251 | u64 owner, u64 offset, int action, | 251 | u64 owner, u64 offset, u64 reserved, int action, |
252 | struct btrfs_delayed_extent_op *extent_op); | 252 | struct btrfs_delayed_extent_op *extent_op); |
253 | int btrfs_add_delayed_qgroup_reserve(struct btrfs_fs_info *fs_info, | 253 | int btrfs_add_delayed_qgroup_reserve(struct btrfs_fs_info *fs_info, |
254 | struct btrfs_trans_handle *trans, | 254 | struct btrfs_trans_handle *trans, |
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index c1f8c7e27a6d..f50c7c2e2b59 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c | |||
@@ -2087,8 +2087,8 @@ int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans, | |||
2087 | BTRFS_ADD_DELAYED_REF, NULL); | 2087 | BTRFS_ADD_DELAYED_REF, NULL); |
2088 | } else { | 2088 | } else { |
2089 | ret = btrfs_add_delayed_data_ref(fs_info, trans, bytenr, | 2089 | ret = btrfs_add_delayed_data_ref(fs_info, trans, bytenr, |
2090 | num_bytes, | 2090 | num_bytes, parent, root_objectid, |
2091 | parent, root_objectid, owner, offset, | 2091 | owner, offset, 0, |
2092 | BTRFS_ADD_DELAYED_REF, NULL); | 2092 | BTRFS_ADD_DELAYED_REF, NULL); |
2093 | } | 2093 | } |
2094 | return ret; | 2094 | return ret; |
@@ -6832,8 +6832,8 @@ int btrfs_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root *root, | |||
6832 | ret = btrfs_add_delayed_data_ref(fs_info, trans, bytenr, | 6832 | ret = btrfs_add_delayed_data_ref(fs_info, trans, bytenr, |
6833 | num_bytes, | 6833 | num_bytes, |
6834 | parent, root_objectid, owner, | 6834 | parent, root_objectid, owner, |
6835 | offset, BTRFS_DROP_DELAYED_REF, | 6835 | offset, 0, |
6836 | NULL); | 6836 | BTRFS_DROP_DELAYED_REF, NULL); |
6837 | } | 6837 | } |
6838 | return ret; | 6838 | return ret; |
6839 | } | 6839 | } |
@@ -7759,7 +7759,8 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans, | |||
7759 | int btrfs_alloc_reserved_file_extent(struct btrfs_trans_handle *trans, | 7759 | int btrfs_alloc_reserved_file_extent(struct btrfs_trans_handle *trans, |
7760 | struct btrfs_root *root, | 7760 | struct btrfs_root *root, |
7761 | u64 root_objectid, u64 owner, | 7761 | u64 root_objectid, u64 owner, |
7762 | u64 offset, struct btrfs_key *ins) | 7762 | u64 offset, u64 ram_bytes, |
7763 | struct btrfs_key *ins) | ||
7763 | { | 7764 | { |
7764 | int ret; | 7765 | int ret; |
7765 | 7766 | ||
@@ -7768,7 +7769,8 @@ int btrfs_alloc_reserved_file_extent(struct btrfs_trans_handle *trans, | |||
7768 | ret = btrfs_add_delayed_data_ref(root->fs_info, trans, ins->objectid, | 7769 | ret = btrfs_add_delayed_data_ref(root->fs_info, trans, ins->objectid, |
7769 | ins->offset, 0, | 7770 | ins->offset, 0, |
7770 | root_objectid, owner, offset, | 7771 | root_objectid, owner, offset, |
7771 | BTRFS_ADD_DELAYED_EXTENT, NULL); | 7772 | ram_bytes, BTRFS_ADD_DELAYED_EXTENT, |
7773 | NULL); | ||
7772 | return ret; | 7774 | return ret; |
7773 | } | 7775 | } |
7774 | 7776 | ||
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 6f030c299de8..4439fbb4ff45 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c | |||
@@ -2127,17 +2127,13 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans, | |||
2127 | ins.type = BTRFS_EXTENT_ITEM_KEY; | 2127 | ins.type = BTRFS_EXTENT_ITEM_KEY; |
2128 | ret = btrfs_alloc_reserved_file_extent(trans, root, | 2128 | ret = btrfs_alloc_reserved_file_extent(trans, root, |
2129 | root->root_key.objectid, | 2129 | root->root_key.objectid, |
2130 | btrfs_ino(inode), file_pos, &ins); | 2130 | btrfs_ino(inode), file_pos, |
2131 | if (ret < 0) | 2131 | ram_bytes, &ins); |
2132 | goto out; | ||
2133 | /* | 2132 | /* |
2134 | * Release the reserved range from inode dirty range map, and | 2133 | * Release the reserved range from inode dirty range map, as it is |
2135 | * move it to delayed ref codes, as now accounting only happens at | 2134 | * already moved into delayed_ref_head |
2136 | * commit_transaction() time. | ||
2137 | */ | 2135 | */ |
2138 | btrfs_qgroup_release_data(inode, file_pos, ram_bytes); | 2136 | btrfs_qgroup_release_data(inode, file_pos, ram_bytes); |
2139 | ret = btrfs_add_delayed_qgroup_reserve(root->fs_info, trans, | ||
2140 | root->objectid, disk_bytenr, ram_bytes); | ||
2141 | out: | 2137 | out: |
2142 | btrfs_free_path(path); | 2138 | btrfs_free_path(path); |
2143 | 2139 | ||