From: Greg Kroah-Hartman Date: Fri, 13 Apr 2012 17:40:23 +0000 (-0700) Subject: 3.3-stable patches X-Git-Tag: v3.2.16~56 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=4b045a4efa18eda6e3ac1b1946e0b648698103a9;p=thirdparty%2Fkernel%2Fstable-queue.git 3.3-stable patches added patches: btrfs-fix-regression-in-scrub-path-resolving.patch --- diff --git a/queue-3.3/btrfs-fix-regression-in-scrub-path-resolving.patch b/queue-3.3/btrfs-fix-regression-in-scrub-path-resolving.patch new file mode 100644 index 00000000000..669a45142a8 --- /dev/null +++ b/queue-3.3/btrfs-fix-regression-in-scrub-path-resolving.patch @@ -0,0 +1,310 @@ +From 7a3ae2f8c8c8432e65467b7fc84d5deab04061a0 Mon Sep 17 00:00:00 2001 +From: Jan Schmidt +Date: Fri, 23 Mar 2012 17:32:28 +0100 +Subject: Btrfs: fix regression in scrub path resolving + +From: Jan Schmidt + +commit 7a3ae2f8c8c8432e65467b7fc84d5deab04061a0 upstream. + +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 +Signed-off-by: Chris Mason +Signed-off-by: Greg Kroah-Hartman + +--- + fs/btrfs/backref.c | 115 ++++++++++++++++++++++++++++++----------------------- + fs/btrfs/backref.h | 5 +- + fs/btrfs/ioctl.c | 4 - + fs/btrfs/scrub.c | 4 - + 4 files changed, 73 insertions(+), 55 deletions(-) + +--- a/fs/btrfs/backref.c ++++ b/fs/btrfs/backref.c +@@ -116,6 +116,7 @@ add_parent: + * to a logical address + */ + static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info, ++ int search_commit_root, + struct __prelim_ref *ref, + struct ulist *parents) + { +@@ -131,6 +132,7 @@ static int __resolve_indirect_ref(struct + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; ++ path->search_commit_root = !!search_commit_root; + + root_key.objectid = ref->root_id; + root_key.type = BTRFS_ROOT_ITEM_KEY; +@@ -188,6 +190,7 @@ out: + * resolve all indirect backrefs from the list + */ + static int __resolve_indirect_refs(struct btrfs_fs_info *fs_info, ++ int search_commit_root, + struct list_head *head) + { + int err; +@@ -212,7 +215,8 @@ static int __resolve_indirect_refs(struc + continue; + if (ref->count == 0) + continue; +- err = __resolve_indirect_ref(fs_info, ref, parents); ++ err = __resolve_indirect_ref(fs_info, search_commit_root, ++ ref, parents); + if (err) { + if (ret == 0) + ret = err; +@@ -586,6 +590,7 @@ static int find_parent_nodes(struct btrf + struct btrfs_delayed_ref_head *head; + int info_level = 0; + int ret; ++ int search_commit_root = (trans == BTRFS_BACKREF_SEARCH_COMMIT_ROOT); + struct list_head prefs_delayed; + struct list_head prefs; + struct __prelim_ref *ref; +@@ -600,6 +605,7 @@ static int find_parent_nodes(struct btrf + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; ++ path->search_commit_root = !!search_commit_root; + + /* + * grab both a lock on the path and a lock on the delayed ref head. +@@ -614,35 +620,39 @@ again: + goto out; + BUG_ON(ret == 0); + +- /* +- * look if there are updates for this ref queued and lock the head +- */ +- delayed_refs = &trans->transaction->delayed_refs; +- spin_lock(&delayed_refs->lock); +- head = btrfs_find_delayed_ref_head(trans, bytenr); +- if (head) { +- if (!mutex_trylock(&head->mutex)) { +- atomic_inc(&head->node.refs); +- spin_unlock(&delayed_refs->lock); +- +- btrfs_release_path(path); +- +- /* +- * Mutex was contended, block until it's +- * released and try again +- */ +- mutex_lock(&head->mutex); +- mutex_unlock(&head->mutex); +- btrfs_put_delayed_ref(&head->node); +- goto again; +- } +- ret = __add_delayed_refs(head, seq, &info_key, &prefs_delayed); +- if (ret) { +- spin_unlock(&delayed_refs->lock); +- goto out; ++ if (trans != BTRFS_BACKREF_SEARCH_COMMIT_ROOT) { ++ /* ++ * look if there are updates for this ref queued and lock the ++ * head ++ */ ++ delayed_refs = &trans->transaction->delayed_refs; ++ spin_lock(&delayed_refs->lock); ++ head = btrfs_find_delayed_ref_head(trans, bytenr); ++ if (head) { ++ if (!mutex_trylock(&head->mutex)) { ++ atomic_inc(&head->node.refs); ++ spin_unlock(&delayed_refs->lock); ++ ++ btrfs_release_path(path); ++ ++ /* ++ * Mutex was contended, block until it's ++ * released and try again ++ */ ++ mutex_lock(&head->mutex); ++ mutex_unlock(&head->mutex); ++ btrfs_put_delayed_ref(&head->node); ++ goto again; ++ } ++ ret = __add_delayed_refs(head, seq, &info_key, ++ &prefs_delayed); ++ if (ret) { ++ spin_unlock(&delayed_refs->lock); ++ goto out; ++ } + } ++ spin_unlock(&delayed_refs->lock); + } +- spin_unlock(&delayed_refs->lock); + + if (path->slots[0]) { + struct extent_buffer *leaf; +@@ -679,7 +689,7 @@ again: + if (ret) + goto out; + +- ret = __resolve_indirect_refs(fs_info, &prefs); ++ ret = __resolve_indirect_refs(fs_info, search_commit_root, &prefs); + if (ret) + goto out; + +@@ -1074,8 +1084,7 @@ int tree_backref_for_extent(unsigned lon + return 0; + } + +-static int iterate_leaf_refs(struct btrfs_fs_info *fs_info, +- struct btrfs_path *path, u64 logical, ++static int iterate_leaf_refs(struct btrfs_fs_info *fs_info, u64 logical, + u64 orig_extent_item_objectid, + u64 extent_item_pos, u64 root, + iterate_extent_inodes_t *iterate, void *ctx) +@@ -1143,35 +1152,38 @@ static int iterate_leaf_refs(struct btrf + * calls iterate() for every inode that references the extent identified by + * the given parameters. + * when the iterator function returns a non-zero value, iteration stops. +- * path is guaranteed to be in released state when iterate() is called. + */ + int iterate_extent_inodes(struct btrfs_fs_info *fs_info, +- struct btrfs_path *path, + u64 extent_item_objectid, u64 extent_item_pos, ++ int search_commit_root, + iterate_extent_inodes_t *iterate, void *ctx) + { + int ret; + struct list_head data_refs = LIST_HEAD_INIT(data_refs); + struct list_head shared_refs = LIST_HEAD_INIT(shared_refs); + struct btrfs_trans_handle *trans; +- struct ulist *refs; +- struct ulist *roots; ++ struct ulist *refs = NULL; ++ struct ulist *roots = NULL; + struct ulist_node *ref_node = NULL; + struct ulist_node *root_node = NULL; + struct seq_list seq_elem; +- struct btrfs_delayed_ref_root *delayed_refs; +- +- trans = btrfs_join_transaction(fs_info->extent_root); +- if (IS_ERR(trans)) +- return PTR_ERR(trans); ++ struct btrfs_delayed_ref_root *delayed_refs = NULL; + + pr_debug("resolving all inodes for extent %llu\n", + extent_item_objectid); + +- delayed_refs = &trans->transaction->delayed_refs; +- spin_lock(&delayed_refs->lock); +- btrfs_get_delayed_seq(delayed_refs, &seq_elem); +- spin_unlock(&delayed_refs->lock); ++ if (search_commit_root) { ++ trans = BTRFS_BACKREF_SEARCH_COMMIT_ROOT; ++ } else { ++ trans = btrfs_join_transaction(fs_info->extent_root); ++ if (IS_ERR(trans)) ++ return PTR_ERR(trans); ++ ++ delayed_refs = &trans->transaction->delayed_refs; ++ spin_lock(&delayed_refs->lock); ++ btrfs_get_delayed_seq(delayed_refs, &seq_elem); ++ spin_unlock(&delayed_refs->lock); ++ } + + ret = btrfs_find_all_leafs(trans, fs_info, extent_item_objectid, + extent_item_pos, seq_elem.seq, +@@ -1188,7 +1200,7 @@ int iterate_extent_inodes(struct btrfs_f + while (!ret && (root_node = ulist_next(roots, root_node))) { + pr_debug("root %llu references leaf %llu\n", + root_node->val, ref_node->val); +- ret = iterate_leaf_refs(fs_info, path, ref_node->val, ++ ret = iterate_leaf_refs(fs_info, ref_node->val, + extent_item_objectid, + extent_item_pos, root_node->val, + iterate, ctx); +@@ -1198,8 +1210,11 @@ int iterate_extent_inodes(struct btrfs_f + ulist_free(refs); + ulist_free(roots); + out: +- btrfs_put_delayed_seq(delayed_refs, &seq_elem); +- btrfs_end_transaction(trans, fs_info->extent_root); ++ if (!search_commit_root) { ++ btrfs_put_delayed_seq(delayed_refs, &seq_elem); ++ btrfs_end_transaction(trans, fs_info->extent_root); ++ } ++ + return ret; + } + +@@ -1210,6 +1225,7 @@ int iterate_inodes_from_logical(u64 logi + int ret; + u64 extent_item_pos; + struct btrfs_key found_key; ++ int search_commit_root = path->search_commit_root; + + ret = extent_from_logical(fs_info, logical, path, + &found_key); +@@ -1220,8 +1236,9 @@ int iterate_inodes_from_logical(u64 logi + return ret; + + extent_item_pos = logical - found_key.objectid; +- ret = iterate_extent_inodes(fs_info, path, found_key.objectid, +- extent_item_pos, iterate, ctx); ++ ret = iterate_extent_inodes(fs_info, found_key.objectid, ++ extent_item_pos, search_commit_root, ++ iterate, ctx); + + return ret; + } +--- a/fs/btrfs/backref.h ++++ b/fs/btrfs/backref.h +@@ -22,6 +22,8 @@ + #include "ioctl.h" + #include "ulist.h" + ++#define BTRFS_BACKREF_SEARCH_COMMIT_ROOT ((struct btrfs_trans_handle *)0) ++ + struct inode_fs_paths { + struct btrfs_path *btrfs_path; + struct btrfs_root *fs_root; +@@ -44,9 +46,8 @@ int tree_backref_for_extent(unsigned lon + u64 *out_root, u8 *out_level); + + int iterate_extent_inodes(struct btrfs_fs_info *fs_info, +- struct btrfs_path *path, + u64 extent_item_objectid, +- u64 extent_offset, ++ u64 extent_offset, int search_commit_root, + iterate_extent_inodes_t *iterate, void *ctx); + + int iterate_inodes_from_logical(u64 logical, struct btrfs_fs_info *fs_info, +--- a/fs/btrfs/ioctl.c ++++ b/fs/btrfs/ioctl.c +@@ -3066,8 +3066,8 @@ static long btrfs_ioctl_logical_to_ino(s + goto out; + + extent_item_pos = loi->logical - key.objectid; +- ret = iterate_extent_inodes(root->fs_info, path, key.objectid, +- extent_item_pos, build_ino_list, ++ ret = iterate_extent_inodes(root->fs_info, key.objectid, ++ extent_item_pos, 0, build_ino_list, + inodes); + + if (ret < 0) +--- a/fs/btrfs/scrub.c ++++ b/fs/btrfs/scrub.c +@@ -352,8 +352,8 @@ static void scrub_print_warning(const ch + } while (ret != 1); + } else { + swarn.path = path; +- iterate_extent_inodes(fs_info, path, found_key.objectid, +- extent_item_pos, ++ iterate_extent_inodes(fs_info, found_key.objectid, ++ extent_item_pos, 1, + scrub_print_warning_inode, &swarn); + } + diff --git a/queue-3.3/series b/queue-3.3/series new file mode 100644 index 00000000000..8c7efe2e8bb --- /dev/null +++ b/queue-3.3/series @@ -0,0 +1 @@ +btrfs-fix-regression-in-scrub-path-resolving.patch