Btrfs: fix scrub_print_warning to handle skinny metadata extents
The skinny extents are intepreted incorrectly in scrub_print_warning(), and end up hitting the BUG() in btrfs_extent_inline_ref_size. Reported-by: Konstantinos Skarlatos <k.skarlatos@gmail.com> Signed-off-by: Liu Bo <bo.li.liu@oracle.com> Signed-off-by: Chris Mason <clm@fb.com>
This commit is contained in:
parent
7ffbb598a0
commit
6eda71d0c0
3 changed files with 24 additions and 15 deletions
|
@ -1409,6 +1409,7 @@ int extent_from_logical(struct btrfs_fs_info *fs_info, u64 logical,
|
||||||
* returns <0 on error
|
* returns <0 on error
|
||||||
*/
|
*/
|
||||||
static int __get_extent_inline_ref(unsigned long *ptr, struct extent_buffer *eb,
|
static int __get_extent_inline_ref(unsigned long *ptr, struct extent_buffer *eb,
|
||||||
|
struct btrfs_key *key,
|
||||||
struct btrfs_extent_item *ei, u32 item_size,
|
struct btrfs_extent_item *ei, u32 item_size,
|
||||||
struct btrfs_extent_inline_ref **out_eiref,
|
struct btrfs_extent_inline_ref **out_eiref,
|
||||||
int *out_type)
|
int *out_type)
|
||||||
|
@ -1421,9 +1422,16 @@ static int __get_extent_inline_ref(unsigned long *ptr, struct extent_buffer *eb,
|
||||||
/* first call */
|
/* first call */
|
||||||
flags = btrfs_extent_flags(eb, ei);
|
flags = btrfs_extent_flags(eb, ei);
|
||||||
if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) {
|
if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) {
|
||||||
|
if (key->type == BTRFS_METADATA_ITEM_KEY) {
|
||||||
|
/* a skinny metadata extent */
|
||||||
|
*out_eiref =
|
||||||
|
(struct btrfs_extent_inline_ref *)(ei + 1);
|
||||||
|
} else {
|
||||||
|
WARN_ON(key->type != BTRFS_EXTENT_ITEM_KEY);
|
||||||
info = (struct btrfs_tree_block_info *)(ei + 1);
|
info = (struct btrfs_tree_block_info *)(ei + 1);
|
||||||
*out_eiref =
|
*out_eiref =
|
||||||
(struct btrfs_extent_inline_ref *)(info + 1);
|
(struct btrfs_extent_inline_ref *)(info + 1);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
*out_eiref = (struct btrfs_extent_inline_ref *)(ei + 1);
|
*out_eiref = (struct btrfs_extent_inline_ref *)(ei + 1);
|
||||||
}
|
}
|
||||||
|
@ -1433,7 +1441,7 @@ static int __get_extent_inline_ref(unsigned long *ptr, struct extent_buffer *eb,
|
||||||
}
|
}
|
||||||
|
|
||||||
end = (unsigned long)ei + item_size;
|
end = (unsigned long)ei + item_size;
|
||||||
*out_eiref = (struct btrfs_extent_inline_ref *)*ptr;
|
*out_eiref = (struct btrfs_extent_inline_ref *)(*ptr);
|
||||||
*out_type = btrfs_extent_inline_ref_type(eb, *out_eiref);
|
*out_type = btrfs_extent_inline_ref_type(eb, *out_eiref);
|
||||||
|
|
||||||
*ptr += btrfs_extent_inline_ref_size(*out_type);
|
*ptr += btrfs_extent_inline_ref_size(*out_type);
|
||||||
|
@ -1452,8 +1460,8 @@ static int __get_extent_inline_ref(unsigned long *ptr, struct extent_buffer *eb,
|
||||||
* <0 on error.
|
* <0 on error.
|
||||||
*/
|
*/
|
||||||
int tree_backref_for_extent(unsigned long *ptr, struct extent_buffer *eb,
|
int tree_backref_for_extent(unsigned long *ptr, struct extent_buffer *eb,
|
||||||
struct btrfs_extent_item *ei, u32 item_size,
|
struct btrfs_key *key, struct btrfs_extent_item *ei,
|
||||||
u64 *out_root, u8 *out_level)
|
u32 item_size, u64 *out_root, u8 *out_level)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
int type;
|
int type;
|
||||||
|
@ -1464,7 +1472,7 @@ int tree_backref_for_extent(unsigned long *ptr, struct extent_buffer *eb,
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
ret = __get_extent_inline_ref(ptr, eb, ei, item_size,
|
ret = __get_extent_inline_ref(ptr, eb, key, ei, item_size,
|
||||||
&eiref, &type);
|
&eiref, &type);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -40,8 +40,8 @@ int extent_from_logical(struct btrfs_fs_info *fs_info, u64 logical,
|
||||||
u64 *flags);
|
u64 *flags);
|
||||||
|
|
||||||
int tree_backref_for_extent(unsigned long *ptr, struct extent_buffer *eb,
|
int tree_backref_for_extent(unsigned long *ptr, struct extent_buffer *eb,
|
||||||
struct btrfs_extent_item *ei, u32 item_size,
|
struct btrfs_key *key, struct btrfs_extent_item *ei,
|
||||||
u64 *out_root, u8 *out_level);
|
u32 item_size, u64 *out_root, u8 *out_level);
|
||||||
|
|
||||||
int iterate_extent_inodes(struct btrfs_fs_info *fs_info,
|
int iterate_extent_inodes(struct btrfs_fs_info *fs_info,
|
||||||
u64 extent_item_objectid,
|
u64 extent_item_objectid,
|
||||||
|
|
|
@ -588,8 +588,9 @@ static void scrub_print_warning(const char *errstr, struct scrub_block *sblock)
|
||||||
|
|
||||||
if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) {
|
if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) {
|
||||||
do {
|
do {
|
||||||
ret = tree_backref_for_extent(&ptr, eb, ei, item_size,
|
ret = tree_backref_for_extent(&ptr, eb, &found_key, ei,
|
||||||
&ref_root, &ref_level);
|
item_size, &ref_root,
|
||||||
|
&ref_level);
|
||||||
printk_in_rcu(KERN_WARNING
|
printk_in_rcu(KERN_WARNING
|
||||||
"BTRFS: %s at logical %llu on dev %s, "
|
"BTRFS: %s at logical %llu on dev %s, "
|
||||||
"sector %llu: metadata %s (level %d) in tree "
|
"sector %llu: metadata %s (level %d) in tree "
|
||||||
|
|
Loading…
Add table
Reference in a new issue