Skip to content

Commit

Permalink
Btrfs: fix regression in scrub path resolving
Browse files Browse the repository at this point in the history
In commit 4692cf5 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>
  • Loading branch information
Jan Schmidt committed Mar 27, 2012
1 parent 103e976 commit 7a3ae2f
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 55 deletions.
115 changes: 66 additions & 49 deletions fs/btrfs/backref.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ static int add_all_parents(struct btrfs_root *root, struct btrfs_path *path,
* 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)
{
Expand All @@ -131,6 +132,7 @@ static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info,
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;
Expand Down Expand Up @@ -188,6 +190,7 @@ static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info,
* 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;
Expand All @@ -212,7 +215,8 @@ static int __resolve_indirect_refs(struct btrfs_fs_info *fs_info,
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;
Expand Down Expand Up @@ -586,6 +590,7 @@ static int find_parent_nodes(struct btrfs_trans_handle *trans,
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;
Expand All @@ -600,6 +605,7 @@ static int find_parent_nodes(struct btrfs_trans_handle *trans,
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.
Expand All @@ -614,35 +620,39 @@ static int find_parent_nodes(struct btrfs_trans_handle *trans,
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;
Expand Down Expand Up @@ -679,7 +689,7 @@ static int find_parent_nodes(struct btrfs_trans_handle *trans,
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;

Expand Down Expand Up @@ -1074,8 +1084,7 @@ int tree_backref_for_extent(unsigned long *ptr, struct extent_buffer *eb,
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)
Expand Down Expand Up @@ -1143,35 +1152,38 @@ static int iterate_leaf_refs(struct btrfs_fs_info *fs_info,
* 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,
Expand All @@ -1188,7 +1200,7 @@ int iterate_extent_inodes(struct btrfs_fs_info *fs_info,
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);
Expand All @@ -1198,8 +1210,11 @@ int iterate_extent_inodes(struct btrfs_fs_info *fs_info,
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;
}

Expand All @@ -1210,6 +1225,7 @@ int iterate_inodes_from_logical(u64 logical, struct btrfs_fs_info *fs_info,
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);
Expand All @@ -1220,8 +1236,9 @@ int iterate_inodes_from_logical(u64 logical, struct btrfs_fs_info *fs_info,
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;
}
Expand Down
5 changes: 3 additions & 2 deletions fs/btrfs/backref.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -44,9 +46,8 @@ int tree_backref_for_extent(unsigned long *ptr, struct extent_buffer *eb,
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,
Expand Down
4 changes: 2 additions & 2 deletions fs/btrfs/ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -3067,8 +3067,8 @@ static long btrfs_ioctl_logical_to_ino(struct btrfs_root *root,
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)
Expand Down
4 changes: 2 additions & 2 deletions fs/btrfs/scrub.c
Original file line number Diff line number Diff line change
Expand Up @@ -352,8 +352,8 @@ static void scrub_print_warning(const char *errstr, struct scrub_bio *sbio,
} 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);
}

Expand Down

0 comments on commit 7a3ae2f

Please sign in to comment.