diff options
author | Jan Schmidt <list.btrfs@jan-o-sch.net> | 2012-03-23 12:32:28 -0400 |
---|---|---|
committer | Jan Schmidt <list.btrfs@jan-o-sch.net> | 2012-03-27 08:51:21 -0400 |
commit | 7a3ae2f8c8c8432e65467b7fc84d5deab04061a0 (patch) | |
tree | e93c46eb8def633533fe80032872f71b36fc03b8 /fs/btrfs | |
parent | 103e976616fe9c2a3e40764c979fa1a592274da2 (diff) |
Btrfs: fix regression in scrub path resolving
In commit 4692cf58 we introduced new backref walking code for btrfs. This
assumes we're searching live roots, which requires a transaction context.
While scrubbing, however, we must not join a transaction because this could
deadlock with the commit path. Additionally, what scrub really wants to do
is resolving a logical address in the commit root it's currently checking.
This patch adds support for logical to path resolving on commit roots and
makes scrub use that.
Signed-off-by: Jan Schmidt <list.btrfs@jan-o-sch.net>
Diffstat (limited to 'fs/btrfs')
-rw-r--r-- | fs/btrfs/backref.c | 115 | ||||
-rw-r--r-- | fs/btrfs/backref.h | 5 | ||||
-rw-r--r-- | fs/btrfs/ioctl.c | 4 | ||||
-rw-r--r-- | fs/btrfs/scrub.c | 4 |
4 files changed, 73 insertions, 55 deletions
diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c index 0436c12da8c2..56136d9046f0 100644 --- a/fs/btrfs/backref.c +++ b/fs/btrfs/backref.c | |||
@@ -116,6 +116,7 @@ add_parent: | |||
116 | * to a logical address | 116 | * to a logical address |
117 | */ | 117 | */ |
118 | static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info, | 118 | static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info, |
119 | int search_commit_root, | ||
119 | struct __prelim_ref *ref, | 120 | struct __prelim_ref *ref, |
120 | struct ulist *parents) | 121 | struct ulist *parents) |
121 | { | 122 | { |
@@ -131,6 +132,7 @@ static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info, | |||
131 | path = btrfs_alloc_path(); | 132 | path = btrfs_alloc_path(); |
132 | if (!path) | 133 | if (!path) |
133 | return -ENOMEM; | 134 | return -ENOMEM; |
135 | path->search_commit_root = !!search_commit_root; | ||
134 | 136 | ||
135 | root_key.objectid = ref->root_id; | 137 | root_key.objectid = ref->root_id; |
136 | root_key.type = BTRFS_ROOT_ITEM_KEY; | 138 | root_key.type = BTRFS_ROOT_ITEM_KEY; |
@@ -188,6 +190,7 @@ out: | |||
188 | * resolve all indirect backrefs from the list | 190 | * resolve all indirect backrefs from the list |
189 | */ | 191 | */ |
190 | static int __resolve_indirect_refs(struct btrfs_fs_info *fs_info, | 192 | static int __resolve_indirect_refs(struct btrfs_fs_info *fs_info, |
193 | int search_commit_root, | ||
191 | struct list_head *head) | 194 | struct list_head *head) |
192 | { | 195 | { |
193 | int err; | 196 | int err; |
@@ -212,7 +215,8 @@ static int __resolve_indirect_refs(struct btrfs_fs_info *fs_info, | |||
212 | continue; | 215 | continue; |
213 | if (ref->count == 0) | 216 | if (ref->count == 0) |
214 | continue; | 217 | continue; |
215 | err = __resolve_indirect_ref(fs_info, ref, parents); | 218 | err = __resolve_indirect_ref(fs_info, search_commit_root, |
219 | ref, parents); | ||
216 | if (err) { | 220 | if (err) { |
217 | if (ret == 0) | 221 | if (ret == 0) |
218 | ret = err; | 222 | ret = err; |
@@ -586,6 +590,7 @@ static int find_parent_nodes(struct btrfs_trans_handle *trans, | |||
586 | struct btrfs_delayed_ref_head *head; | 590 | struct btrfs_delayed_ref_head *head; |
587 | int info_level = 0; | 591 | int info_level = 0; |
588 | int ret; | 592 | int ret; |
593 | int search_commit_root = (trans == BTRFS_BACKREF_SEARCH_COMMIT_ROOT); | ||
589 | struct list_head prefs_delayed; | 594 | struct list_head prefs_delayed; |
590 | struct list_head prefs; | 595 | struct list_head prefs; |
591 | struct __prelim_ref *ref; | 596 | struct __prelim_ref *ref; |
@@ -600,6 +605,7 @@ static int find_parent_nodes(struct btrfs_trans_handle *trans, | |||
600 | path = btrfs_alloc_path(); | 605 | path = btrfs_alloc_path(); |
601 | if (!path) | 606 | if (!path) |
602 | return -ENOMEM; | 607 | return -ENOMEM; |
608 | path->search_commit_root = !!search_commit_root; | ||
603 | 609 | ||
604 | /* | 610 | /* |
605 | * grab both a lock on the path and a lock on the delayed ref head. | 611 | * grab both a lock on the path and a lock on the delayed ref head. |
@@ -614,35 +620,39 @@ again: | |||
614 | goto out; | 620 | goto out; |
615 | BUG_ON(ret == 0); | 621 | BUG_ON(ret == 0); |
616 | 622 | ||
617 | /* | 623 | if (trans != BTRFS_BACKREF_SEARCH_COMMIT_ROOT) { |
618 | * look if there are updates for this ref queued and lock the head | 624 | /* |
619 | */ | 625 | * look if there are updates for this ref queued and lock the |
620 | delayed_refs = &trans->transaction->delayed_refs; | 626 | * head |
621 | spin_lock(&delayed_refs->lock); | 627 | */ |
622 | head = btrfs_find_delayed_ref_head(trans, bytenr); | 628 | delayed_refs = &trans->transaction->delayed_refs; |
623 | if (head) { | 629 | spin_lock(&delayed_refs->lock); |
624 | if (!mutex_trylock(&head->mutex)) { | 630 | head = btrfs_find_delayed_ref_head(trans, bytenr); |
625 | atomic_inc(&head->node.refs); | 631 | if (head) { |
626 | spin_unlock(&delayed_refs->lock); | 632 | if (!mutex_trylock(&head->mutex)) { |
627 | 633 | atomic_inc(&head->node.refs); | |
628 | btrfs_release_path(path); | 634 | spin_unlock(&delayed_refs->lock); |
629 | 635 | ||
630 | /* | 636 | btrfs_release_path(path); |
631 | * Mutex was contended, block until it's | 637 | |
632 | * released and try again | 638 | /* |
633 | */ | 639 | * Mutex was contended, block until it's |
634 | mutex_lock(&head->mutex); | 640 | * released and try again |
635 | mutex_unlock(&head->mutex); | 641 | */ |
636 | btrfs_put_delayed_ref(&head->node); | 642 | mutex_lock(&head->mutex); |
637 | goto again; | 643 | mutex_unlock(&head->mutex); |
638 | } | 644 | btrfs_put_delayed_ref(&head->node); |
639 | ret = __add_delayed_refs(head, seq, &info_key, &prefs_delayed); | 645 | goto again; |
640 | if (ret) { | 646 | } |
641 | spin_unlock(&delayed_refs->lock); | 647 | ret = __add_delayed_refs(head, seq, &info_key, |
642 | goto out; | 648 | &prefs_delayed); |
649 | if (ret) { | ||
650 | spin_unlock(&delayed_refs->lock); | ||
651 | goto out; | ||
652 | } | ||
643 | } | 653 | } |
654 | spin_unlock(&delayed_refs->lock); | ||
644 | } | 655 | } |
645 | spin_unlock(&delayed_refs->lock); | ||
646 | 656 | ||
647 | if (path->slots[0]) { | 657 | if (path->slots[0]) { |
648 | struct extent_buffer *leaf; | 658 | struct extent_buffer *leaf; |
@@ -679,7 +689,7 @@ again: | |||
679 | if (ret) | 689 | if (ret) |
680 | goto out; | 690 | goto out; |
681 | 691 | ||
682 | ret = __resolve_indirect_refs(fs_info, &prefs); | 692 | ret = __resolve_indirect_refs(fs_info, search_commit_root, &prefs); |
683 | if (ret) | 693 | if (ret) |
684 | goto out; | 694 | goto out; |
685 | 695 | ||
@@ -1074,8 +1084,7 @@ int tree_backref_for_extent(unsigned long *ptr, struct extent_buffer *eb, | |||
1074 | return 0; | 1084 | return 0; |
1075 | } | 1085 | } |
1076 | 1086 | ||
1077 | static int iterate_leaf_refs(struct btrfs_fs_info *fs_info, | 1087 | static int iterate_leaf_refs(struct btrfs_fs_info *fs_info, u64 logical, |
1078 | struct btrfs_path *path, u64 logical, | ||
1079 | u64 orig_extent_item_objectid, | 1088 | u64 orig_extent_item_objectid, |
1080 | u64 extent_item_pos, u64 root, | 1089 | u64 extent_item_pos, u64 root, |
1081 | iterate_extent_inodes_t *iterate, void *ctx) | 1090 | iterate_extent_inodes_t *iterate, void *ctx) |
@@ -1143,35 +1152,38 @@ static int iterate_leaf_refs(struct btrfs_fs_info *fs_info, | |||
1143 | * calls iterate() for every inode that references the extent identified by | 1152 | * calls iterate() for every inode that references the extent identified by |
1144 | * the given parameters. | 1153 | * the given parameters. |
1145 | * when the iterator function returns a non-zero value, iteration stops. | 1154 | * when the iterator function returns a non-zero value, iteration stops. |
1146 | * path is guaranteed to be in released state when iterate() is called. | ||
1147 | */ | 1155 | */ |
1148 | int iterate_extent_inodes(struct btrfs_fs_info *fs_info, | 1156 | int iterate_extent_inodes(struct btrfs_fs_info *fs_info, |
1149 | struct btrfs_path *path, | ||
1150 | u64 extent_item_objectid, u64 extent_item_pos, | 1157 | u64 extent_item_objectid, u64 extent_item_pos, |
1158 | int search_commit_root, | ||
1151 | iterate_extent_inodes_t *iterate, void *ctx) | 1159 | iterate_extent_inodes_t *iterate, void *ctx) |
1152 | { | 1160 | { |
1153 | int ret; | 1161 | int ret; |
1154 | struct list_head data_refs = LIST_HEAD_INIT(data_refs); | 1162 | struct list_head data_refs = LIST_HEAD_INIT(data_refs); |
1155 | struct list_head shared_refs = LIST_HEAD_INIT(shared_refs); | 1163 | struct list_head shared_refs = LIST_HEAD_INIT(shared_refs); |
1156 | struct btrfs_trans_handle *trans; | 1164 | struct btrfs_trans_handle *trans; |
1157 | struct ulist *refs; | 1165 | struct ulist *refs = NULL; |
1158 | struct ulist *roots; | 1166 | struct ulist *roots = NULL; |
1159 | struct ulist_node *ref_node = NULL; | 1167 | struct ulist_node *ref_node = NULL; |
1160 | struct ulist_node *root_node = NULL; | 1168 | struct ulist_node *root_node = NULL; |
1161 | struct seq_list seq_elem; | 1169 | struct seq_list seq_elem; |
1162 | struct btrfs_delayed_ref_root *delayed_refs; | 1170 | struct btrfs_delayed_ref_root *delayed_refs = NULL; |
1163 | |||
1164 | trans = btrfs_join_transaction(fs_info->extent_root); | ||
1165 | if (IS_ERR(trans)) | ||
1166 | return PTR_ERR(trans); | ||
1167 | 1171 | ||
1168 | pr_debug("resolving all inodes for extent %llu\n", | 1172 | pr_debug("resolving all inodes for extent %llu\n", |
1169 | extent_item_objectid); | 1173 | extent_item_objectid); |
1170 | 1174 | ||
1171 | delayed_refs = &trans->transaction->delayed_refs; | 1175 | if (search_commit_root) { |
1172 | spin_lock(&delayed_refs->lock); | 1176 | trans = BTRFS_BACKREF_SEARCH_COMMIT_ROOT; |
1173 | btrfs_get_delayed_seq(delayed_refs, &seq_elem); | 1177 | } else { |
1174 | spin_unlock(&delayed_refs->lock); | 1178 | trans = btrfs_join_transaction(fs_info->extent_root); |
1179 | if (IS_ERR(trans)) | ||
1180 | return PTR_ERR(trans); | ||
1181 | |||
1182 | delayed_refs = &trans->transaction->delayed_refs; | ||
1183 | spin_lock(&delayed_refs->lock); | ||
1184 | btrfs_get_delayed_seq(delayed_refs, &seq_elem); | ||
1185 | spin_unlock(&delayed_refs->lock); | ||
1186 | } | ||
1175 | 1187 | ||
1176 | ret = btrfs_find_all_leafs(trans, fs_info, extent_item_objectid, | 1188 | ret = btrfs_find_all_leafs(trans, fs_info, extent_item_objectid, |
1177 | extent_item_pos, seq_elem.seq, | 1189 | extent_item_pos, seq_elem.seq, |
@@ -1188,7 +1200,7 @@ int iterate_extent_inodes(struct btrfs_fs_info *fs_info, | |||
1188 | while (!ret && (root_node = ulist_next(roots, root_node))) { | 1200 | while (!ret && (root_node = ulist_next(roots, root_node))) { |
1189 | pr_debug("root %llu references leaf %llu\n", | 1201 | pr_debug("root %llu references leaf %llu\n", |
1190 | root_node->val, ref_node->val); | 1202 | root_node->val, ref_node->val); |
1191 | ret = iterate_leaf_refs(fs_info, path, ref_node->val, | 1203 | ret = iterate_leaf_refs(fs_info, ref_node->val, |
1192 | extent_item_objectid, | 1204 | extent_item_objectid, |
1193 | extent_item_pos, root_node->val, | 1205 | extent_item_pos, root_node->val, |
1194 | iterate, ctx); | 1206 | iterate, ctx); |
@@ -1198,8 +1210,11 @@ int iterate_extent_inodes(struct btrfs_fs_info *fs_info, | |||
1198 | ulist_free(refs); | 1210 | ulist_free(refs); |
1199 | ulist_free(roots); | 1211 | ulist_free(roots); |
1200 | out: | 1212 | out: |
1201 | btrfs_put_delayed_seq(delayed_refs, &seq_elem); | 1213 | if (!search_commit_root) { |
1202 | btrfs_end_transaction(trans, fs_info->extent_root); | 1214 | btrfs_put_delayed_seq(delayed_refs, &seq_elem); |
1215 | btrfs_end_transaction(trans, fs_info->extent_root); | ||
1216 | } | ||
1217 | |||
1203 | return ret; | 1218 | return ret; |
1204 | } | 1219 | } |
1205 | 1220 | ||
@@ -1210,6 +1225,7 @@ int iterate_inodes_from_logical(u64 logical, struct btrfs_fs_info *fs_info, | |||
1210 | int ret; | 1225 | int ret; |
1211 | u64 extent_item_pos; | 1226 | u64 extent_item_pos; |
1212 | struct btrfs_key found_key; | 1227 | struct btrfs_key found_key; |
1228 | int search_commit_root = path->search_commit_root; | ||
1213 | 1229 | ||
1214 | ret = extent_from_logical(fs_info, logical, path, | 1230 | ret = extent_from_logical(fs_info, logical, path, |
1215 | &found_key); | 1231 | &found_key); |
@@ -1220,8 +1236,9 @@ int iterate_inodes_from_logical(u64 logical, struct btrfs_fs_info *fs_info, | |||
1220 | return ret; | 1236 | return ret; |
1221 | 1237 | ||
1222 | extent_item_pos = logical - found_key.objectid; | 1238 | extent_item_pos = logical - found_key.objectid; |
1223 | ret = iterate_extent_inodes(fs_info, path, found_key.objectid, | 1239 | ret = iterate_extent_inodes(fs_info, found_key.objectid, |
1224 | extent_item_pos, iterate, ctx); | 1240 | extent_item_pos, search_commit_root, |
1241 | iterate, ctx); | ||
1225 | 1242 | ||
1226 | return ret; | 1243 | return ret; |
1227 | } | 1244 | } |
diff --git a/fs/btrfs/backref.h b/fs/btrfs/backref.h index d00dfa9ca934..57ea2e959e4d 100644 --- a/fs/btrfs/backref.h +++ b/fs/btrfs/backref.h | |||
@@ -22,6 +22,8 @@ | |||
22 | #include "ioctl.h" | 22 | #include "ioctl.h" |
23 | #include "ulist.h" | 23 | #include "ulist.h" |
24 | 24 | ||
25 | #define BTRFS_BACKREF_SEARCH_COMMIT_ROOT ((struct btrfs_trans_handle *)0) | ||
26 | |||
25 | struct inode_fs_paths { | 27 | struct inode_fs_paths { |
26 | struct btrfs_path *btrfs_path; | 28 | struct btrfs_path *btrfs_path; |
27 | struct btrfs_root *fs_root; | 29 | struct btrfs_root *fs_root; |
@@ -44,9 +46,8 @@ int tree_backref_for_extent(unsigned long *ptr, struct extent_buffer *eb, | |||
44 | u64 *out_root, u8 *out_level); | 46 | u64 *out_root, u8 *out_level); |
45 | 47 | ||
46 | int iterate_extent_inodes(struct btrfs_fs_info *fs_info, | 48 | int iterate_extent_inodes(struct btrfs_fs_info *fs_info, |
47 | struct btrfs_path *path, | ||
48 | u64 extent_item_objectid, | 49 | u64 extent_item_objectid, |
49 | u64 extent_offset, | 50 | u64 extent_offset, int search_commit_root, |
50 | iterate_extent_inodes_t *iterate, void *ctx); | 51 | iterate_extent_inodes_t *iterate, void *ctx); |
51 | 52 | ||
52 | int iterate_inodes_from_logical(u64 logical, struct btrfs_fs_info *fs_info, | 53 | int iterate_inodes_from_logical(u64 logical, struct btrfs_fs_info *fs_info, |
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 05446f77f99b..013c6371e3e8 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c | |||
@@ -3067,8 +3067,8 @@ static long btrfs_ioctl_logical_to_ino(struct btrfs_root *root, | |||
3067 | goto out; | 3067 | goto out; |
3068 | 3068 | ||
3069 | extent_item_pos = loi->logical - key.objectid; | 3069 | extent_item_pos = loi->logical - key.objectid; |
3070 | ret = iterate_extent_inodes(root->fs_info, path, key.objectid, | 3070 | ret = iterate_extent_inodes(root->fs_info, key.objectid, |
3071 | extent_item_pos, build_ino_list, | 3071 | extent_item_pos, 0, build_ino_list, |
3072 | inodes); | 3072 | inodes); |
3073 | 3073 | ||
3074 | if (ret < 0) | 3074 | if (ret < 0) |
diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c index abc0fbffa510..b9b84cdfc359 100644 --- a/fs/btrfs/scrub.c +++ b/fs/btrfs/scrub.c | |||
@@ -352,8 +352,8 @@ static void scrub_print_warning(const char *errstr, struct scrub_bio *sbio, | |||
352 | } while (ret != 1); | 352 | } while (ret != 1); |
353 | } else { | 353 | } else { |
354 | swarn.path = path; | 354 | swarn.path = path; |
355 | iterate_extent_inodes(fs_info, path, found_key.objectid, | 355 | iterate_extent_inodes(fs_info, found_key.objectid, |
356 | extent_item_pos, | 356 | extent_item_pos, 1, |
357 | scrub_print_warning_inode, &swarn); | 357 | scrub_print_warning_inode, &swarn); |
358 | } | 358 | } |
359 | 359 | ||