diff options
author | Qu Wenruo <quwenruo@cn.fujitsu.com> | 2015-07-23 17:18:08 +0800 |
---|---|---|
committer | David Sterba <dsterba@suse.com> | 2015-08-31 19:25:10 +0200 |
commit | 595c57d2f4dd3199aacb23b4c68d6aff49f97d29 (patch) | |
tree | 5a417bbf5a482dc7fbd30254e4286382b63c655e /cmds-check.c | |
parent | 045dc8fb6cb41bc804af3069f76ac3c94681ea42 (diff) | |
download | btrfs-progs-595c57d2f4dd3199aacb23b4c68d6aff49f97d29.tar.gz |
btrfs-progs: fsck: Check if a metadata tree block crossing stripe boundary
Kernel btrfs_map_block() function has a limitation that it can only
map BTRFS_STRIPE_LEN size.
That will cause scrub fails to scrub tree block which crosses strip
boundary, causing BUG_ON().
Normally, it's OK as metadata is always in metadata chunk and
BTRFS_STRIPE_LEN can always be divided by node/leaf size.
So without mixed block group, tree block won't cross stripe boundary.
But for mixed block group, especially for btrfs converted from ext4,
it's almost sure one or more tree blocks are not aligned with node size
and cross stripe boundary.
Causing bug with kernel scrub.
This patch will report the problem, although we don't have a good idea
how to fix it in user space until we add the ability to relocate tree
block in user space.
Also, kernel code should also be checked for such tree block alloc
problems.
Reported-by: Chris Murphy <lists@colorremedies.com>
Reported-by: Zhao Lei <zhaolei@cn.fujitsu.com>
Signed-off-by: Zhao Lei <zhaolei@cn.fujitsu.com>
Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com>
Signed-off-by: David Sterba <dsterba@suse.com>
Diffstat (limited to 'cmds-check.c')
-rw-r--r-- | cmds-check.c | 28 |
1 files changed, 27 insertions, 1 deletions
diff --git a/cmds-check.c b/cmds-check.c index dd2fce3..50bb6f3 100644 --- a/cmds-check.c +++ b/cmds-check.c @@ -126,6 +126,7 @@ struct extent_record { unsigned int is_root:1; unsigned int metadata:1; unsigned int bad_full_backref:1; + unsigned int crossing_stripes:1; }; struct inode_backref { @@ -3734,7 +3735,7 @@ static int maybe_free_extent_rec(struct cache_tree *extent_cache, if (rec->content_checked && rec->owner_ref_checked && rec->extent_item_refs == rec->refs && rec->refs > 0 && rec->num_duplicates == 0 && !all_backpointers_checked(rec, 0) && - !rec->bad_full_backref) { + !rec->bad_full_backref && !rec->crossing_stripes) { remove_cache_extent(extent_cache, &rec->cache); free_all_extent_backrefs(rec); list_del_init(&rec->list); @@ -4381,6 +4382,15 @@ static int add_extent_rec(struct cache_tree *extent_cache, if (rec->max_size < max_size) rec->max_size = max_size; + /* + * A metadata extent can't cross stripe_len boundary, otherwise + * kernel scrub won't be able to handle it. + * As now stripe_len is fixed to BTRFS_STRIPE_LEN, just check + * it. + */ + if (metadata && check_crossing_stripes(rec->start, + rec->max_size)) + rec->crossing_stripes = 1; maybe_free_extent_rec(extent_cache, rec); return ret; } @@ -4433,6 +4443,10 @@ static int add_extent_rec(struct cache_tree *extent_cache, rec->content_checked = 1; rec->owner_ref_checked = 1; } + + if (metadata) + if (check_crossing_stripes(rec->start, rec->max_size)) + rec->crossing_stripes = 1; return ret; } @@ -7478,6 +7492,18 @@ static int check_extent_refs(struct btrfs_root *root, err = 1; cur_err = 1; } + /* + * Although it's not a extent ref's problem, we reuse this + * routine for error reporting. + * No repair function yet. + */ + if (rec->crossing_stripes) { + fprintf(stderr, + "bad metadata [%llu, %llu) crossing stripe boundary\n", + rec->start, rec->start + rec->max_size); + err = 1; + cur_err = 1; + } remove_cache_extent(extent_cache, cache); free_all_extent_backrefs(rec); |