Skip to content

V4.13 rc3 ref type #5

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions fs/btrfs/backref.c
Original file line number Diff line number Diff line change
Expand Up @@ -1012,7 +1012,11 @@ static int __add_inline_refs(struct btrfs_path *path, u64 bytenr,
int type;

iref = (struct btrfs_extent_inline_ref *)ptr;
type = btrfs_extent_inline_ref_type(leaf, iref);
type = btrfs_get_extent_inline_ref_type(leaf, iref,
BTRFS_REF_TYPE_ANY);
if (type == BTRFS_REF_TYPE_INVALID)
return -EINVAL;

offset = btrfs_extent_inline_ref_offset(leaf, iref);

switch (type) {
Expand Down Expand Up @@ -1908,7 +1912,10 @@ static int __get_extent_inline_ref(unsigned long *ptr, struct extent_buffer *eb,

end = (unsigned long)ei + item_size;
*out_eiref = (struct btrfs_extent_inline_ref *)(*ptr);
*out_type = btrfs_extent_inline_ref_type(eb, *out_eiref);
*out_type = btrfs_get_extent_inline_ref_type(eb, *out_eiref,
BTRFS_REF_TYPE_ANY);
if (*out_type == BTRFS_REF_TYPE_INVALID)
return -EINVAL;

*ptr += btrfs_extent_inline_ref_size(*out_type);
WARN_ON(*ptr > end);
Expand Down
12 changes: 11 additions & 1 deletion fs/btrfs/ctree.h
Original file line number Diff line number Diff line change
Expand Up @@ -1799,7 +1799,6 @@ static inline u32 btrfs_extent_inline_ref_size(int type)
if (type == BTRFS_EXTENT_DATA_REF_KEY)
return sizeof(struct btrfs_extent_data_ref) +
offsetof(struct btrfs_extent_inline_ref, offset);
BUG();
return 0;
}

Expand Down Expand Up @@ -2561,6 +2560,17 @@ static inline gfp_t btrfs_alloc_write_mask(struct address_space *mapping)

/* extent-tree.c */

enum btrfs_inline_ref_type {
BTRFS_REF_TYPE_INVALID = 0,
BTRFS_REF_TYPE_BLOCK = 1,
BTRFS_REF_TYPE_DATA = 2,
BTRFS_REF_TYPE_ANY = 3,
};

int btrfs_get_extent_inline_ref_type(struct extent_buffer *eb,
struct btrfs_extent_inline_ref *iref,
enum btrfs_inline_ref_type is_data);

u64 btrfs_csum_bytes_to_leaves(struct btrfs_fs_info *fs_info, u64 csum_bytes);

static inline u64 btrfs_calc_trans_metadata_size(struct btrfs_fs_info *fs_info,
Expand Down
94 changes: 88 additions & 6 deletions fs/btrfs/extent-tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -1148,6 +1148,64 @@ static int convert_extent_item_v0(struct btrfs_trans_handle *trans,
}
#endif

/*
* is_data == BTRFS_REF_TYPE_BLOCK, tree block type is required,
* is_data == BTRFS_REF_TYPE_DATA, data type is requried,
* is_data == BTRFS_REF_TYPE_ANY, either type is OK.
*/
int btrfs_get_extent_inline_ref_type(struct extent_buffer *eb,
struct btrfs_extent_inline_ref *iref,
enum btrfs_inline_ref_type is_data)
{
int type = btrfs_extent_inline_ref_type(eb, iref);
u64 offset = btrfs_extent_inline_ref_offset(eb, iref);

if (type == BTRFS_TREE_BLOCK_REF_KEY ||
type == BTRFS_SHARED_BLOCK_REF_KEY ||
type == BTRFS_SHARED_DATA_REF_KEY ||
type == BTRFS_EXTENT_DATA_REF_KEY) {
if (is_data == BTRFS_REF_TYPE_BLOCK) {
if (type == BTRFS_TREE_BLOCK_REF_KEY)
return type;
if (type == BTRFS_SHARED_BLOCK_REF_KEY) {
ASSERT(eb->fs_info);
/*
* Every shared one has parent tree
* block, which must be aligned to
* nodesize.
*/
if (offset &&
IS_ALIGNED(offset, eb->fs_info->nodesize))
return type;
}
} else if (is_data == BTRFS_REF_TYPE_DATA) {
if (type == BTRFS_EXTENT_DATA_REF_KEY)
return type;
if (type == BTRFS_SHARED_DATA_REF_KEY) {
ASSERT(eb->fs_info);
/*
* Every shared one has parent tree
* block, which must be aligned to
* nodesize.
*/
if (offset &&
IS_ALIGNED(offset, eb->fs_info->nodesize))
return type;
}
} else {
ASSERT(is_data == BTRFS_REF_TYPE_ANY);
return type;
}
}

btrfs_print_leaf(eb->fs_info, eb);
btrfs_err(eb->fs_info, "eb %llu invalid extent inline ref type %d",
eb->start, type);
WARN_ON(1);

return BTRFS_REF_TYPE_INVALID;
}

static u64 hash_extent_data_ref(u64 root_objectid, u64 owner, u64 offset)
{
u32 high_crc = ~(u32)0;
Expand Down Expand Up @@ -1417,12 +1475,18 @@ static noinline u32 extent_data_ref_count(struct btrfs_path *path,
struct btrfs_extent_data_ref *ref1;
struct btrfs_shared_data_ref *ref2;
u32 num_refs = 0;
int type;

leaf = path->nodes[0];
btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
if (iref) {
if (btrfs_extent_inline_ref_type(leaf, iref) ==
BTRFS_EXTENT_DATA_REF_KEY) {
/*
* If type is invalid, we should have bailed out earlier than
* this call.
*/
type = btrfs_get_extent_inline_ref_type(leaf, iref, BTRFS_REF_TYPE_DATA);
ASSERT(type != BTRFS_REF_TYPE_INVALID);
if (type == BTRFS_EXTENT_DATA_REF_KEY) {
ref1 = (struct btrfs_extent_data_ref *)(&iref->offset);
num_refs = btrfs_extent_data_ref_count(leaf, ref1);
} else {
Expand Down Expand Up @@ -1583,6 +1647,7 @@ int lookup_inline_extent_backref(struct btrfs_trans_handle *trans,
int ret;
int err = 0;
bool skinny_metadata = btrfs_fs_incompat(fs_info, SKINNY_METADATA);
int needed;

key.objectid = bytenr;
key.type = BTRFS_EXTENT_ITEM_KEY;
Expand Down Expand Up @@ -1674,14 +1739,24 @@ int lookup_inline_extent_backref(struct btrfs_trans_handle *trans,
BUG_ON(ptr > end);
}

if (owner >= BTRFS_FIRST_FREE_OBJECTID)
needed = BTRFS_REF_TYPE_DATA;
else
needed = BTRFS_REF_TYPE_BLOCK;

err = -ENOENT;
while (1) {
if (ptr >= end) {
WARN_ON(ptr > end);
break;
}
iref = (struct btrfs_extent_inline_ref *)ptr;
type = btrfs_extent_inline_ref_type(leaf, iref);
type = btrfs_get_extent_inline_ref_type(leaf, iref, needed);
if (type == BTRFS_REF_TYPE_INVALID) {
err = -EINVAL;
goto out;
}

if (want < type)
break;
if (want > type) {
Expand Down Expand Up @@ -1873,7 +1948,12 @@ void update_inline_extent_backref(struct btrfs_fs_info *fs_info,
if (extent_op)
__run_delayed_extent_op(extent_op, leaf, ei);

type = btrfs_extent_inline_ref_type(leaf, iref);
/*
* If type is invalid, we should have bailed out after
* lookup_inline_extent_backref().
*/
type = btrfs_get_extent_inline_ref_type(leaf, iref, BTRFS_REF_TYPE_ANY);
ASSERT(type != BTRFS_REF_TYPE_INVALID);

if (type == BTRFS_EXTENT_DATA_REF_KEY) {
dref = (struct btrfs_extent_data_ref *)(&iref->offset);
Expand Down Expand Up @@ -3158,6 +3238,7 @@ static noinline int check_committed_ref(struct btrfs_root *root,
struct btrfs_extent_item *ei;
struct btrfs_key key;
u32 item_size;
int type;
int ret;

key.objectid = bytenr;
Expand Down Expand Up @@ -3199,8 +3280,9 @@ static noinline int check_committed_ref(struct btrfs_root *root,
goto out;

iref = (struct btrfs_extent_inline_ref *)(ei + 1);
if (btrfs_extent_inline_ref_type(leaf, iref) !=
BTRFS_EXTENT_DATA_REF_KEY)

type = btrfs_get_extent_inline_ref_type(leaf, iref, BTRFS_REF_TYPE_DATA);
if (type != BTRFS_EXTENT_DATA_REF_KEY)
goto out;

ref = (struct btrfs_extent_data_ref *)(&iref->offset);
Expand Down
22 changes: 14 additions & 8 deletions fs/btrfs/extent_io.c
Original file line number Diff line number Diff line change
Expand Up @@ -5416,13 +5416,19 @@ void read_extent_buffer(struct extent_buffer *eb, void *dstv,
char *dst = (char *)dstv;
size_t start_offset = eb->start & ((u64)PAGE_SIZE - 1);
unsigned long i = (start_offset + start) >> PAGE_SHIFT;
unsigned long num_pages = num_extent_pages(eb->start, eb->len);

WARN_ON(start > eb->len);
WARN_ON(start + len > eb->start + eb->len);
if (start + len > eb->len) {
WARN(1, KERN_ERR "btrfs bad mapping eb start %llu len %lu, wanted %lu %lu\n",
eb->start, eb->len, start, len);
memset(dst, 0, len);
return;
}

offset = (start_offset + start) & (PAGE_SIZE - 1);

while (len > 0) {
ASSERT(i < num_pages);
page = eb->pages[i];

cur = min(len, (PAGE_SIZE - offset));
Expand Down Expand Up @@ -5491,6 +5497,12 @@ int map_private_extent_buffer(struct extent_buffer *eb, unsigned long start,
unsigned long end_i = (start_offset + start + min_len - 1) >>
PAGE_SHIFT;

if (start + min_len > eb->len) {
WARN(1, KERN_ERR "btrfs bad mapping eb start %llu len %lu, wanted %lu %lu\n",
eb->start, eb->len, start, min_len);
return -EINVAL;
}

if (i != end_i)
return 1;

Expand All @@ -5502,12 +5514,6 @@ int map_private_extent_buffer(struct extent_buffer *eb, unsigned long start,
*map_start = ((u64)i << PAGE_SHIFT) - start_offset;
}

if (start + min_len > eb->len) {
WARN(1, KERN_ERR "btrfs bad mapping eb start %llu len %lu, wanted %lu %lu\n",
eb->start, eb->len, start, min_len);
return -EINVAL;
}

p = eb->pages[i];
kaddr = page_address(p);
*map = kaddr + offset;
Expand Down
28 changes: 23 additions & 5 deletions fs/btrfs/print-tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ static void print_dev_item(struct extent_buffer *eb,
static void print_extent_data_ref(struct extent_buffer *eb,
struct btrfs_extent_data_ref *ref)
{
pr_info("\t\textent data backref root %llu objectid %llu offset %llu count %u\n",
pr_cont("extent data backref root %llu objectid %llu offset %llu count %u\n",
btrfs_extent_data_ref_root(eb, ref),
btrfs_extent_data_ref_objectid(eb, ref),
btrfs_extent_data_ref_offset(eb, ref),
Expand All @@ -63,6 +63,7 @@ static void print_extent_item(struct extent_buffer *eb, int slot, int type)
u32 item_size = btrfs_item_size_nr(eb, slot);
u64 flags;
u64 offset;
int ref_index = 0;

if (item_size < sizeof(*ei)) {
#ifdef BTRFS_COMPAT_EXTENT_TREE_V0
Expand Down Expand Up @@ -104,24 +105,41 @@ static void print_extent_item(struct extent_buffer *eb, int slot, int type)
iref = (struct btrfs_extent_inline_ref *)ptr;
type = btrfs_extent_inline_ref_type(eb, iref);
offset = btrfs_extent_inline_ref_offset(eb, iref);
pr_info("\t\tref#%d: ", ref_index++);
switch (type) {
case BTRFS_TREE_BLOCK_REF_KEY:
pr_info("\t\ttree block backref root %llu\n", offset);
pr_cont("tree block backref root %llu\n", offset);
break;
case BTRFS_SHARED_BLOCK_REF_KEY:
pr_info("\t\tshared block backref parent %llu\n", offset);
pr_cont("shared block backref parent %llu\n", offset);
/*
* offset is supposed to be a tree block which
* must be aligned to nodesize.
*/
if (!IS_ALIGNED(offset, eb->fs_info->nodesize))
pr_info("\t\t\t(parent %llu is NOT ALIGNED to nodesize %llu)\n",
offset, (unsigned long long)eb->fs_info->nodesize);
break;
case BTRFS_EXTENT_DATA_REF_KEY:
dref = (struct btrfs_extent_data_ref *)(&iref->offset);
print_extent_data_ref(eb, dref);
break;
case BTRFS_SHARED_DATA_REF_KEY:
sref = (struct btrfs_shared_data_ref *)(iref + 1);
pr_info("\t\tshared data backref parent %llu count %u\n",
pr_cont("shared data backref parent %llu count %u\n",
offset, btrfs_shared_data_ref_count(eb, sref));
/*
* offset is supposed to be a tree block which
* must be aligned to nodesize.
*/
if (!IS_ALIGNED(offset, eb->fs_info->nodesize))
pr_info("\t\t\t(parent %llu is NOT ALIGNED to nodesize %llu)\n",
offset, (unsigned long long)eb->fs_info->nodesize);
break;
default:
BUG();
pr_cont("(extent %llu has INVALID ref type %d)\n",
eb->start, type);
return;
}
ptr += btrfs_extent_inline_ref_size(type);
}
Expand Down
30 changes: 26 additions & 4 deletions fs/btrfs/relocation.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "free-space-cache.h"
#include "inode-map.h"
#include "qgroup.h"
#include "print-tree.h"

/*
* backref_node, mapping_node and tree_block start with this
Expand Down Expand Up @@ -799,9 +800,17 @@ struct backref_node *build_backref_tree(struct reloc_control *rc,
if (ptr < end) {
/* update key for inline back ref */
struct btrfs_extent_inline_ref *iref;
int type;
iref = (struct btrfs_extent_inline_ref *)ptr;
key.type = btrfs_extent_inline_ref_type(eb, iref);
type = btrfs_get_extent_inline_ref_type(eb, iref,
BTRFS_REF_TYPE_BLOCK);
if (type == BTRFS_REF_TYPE_INVALID) {
err = -EINVAL;
goto out;
}
key.type = type;
key.offset = btrfs_extent_inline_ref_offset(eb, iref);

WARN_ON(key.type != BTRFS_TREE_BLOCK_REF_KEY &&
key.type != BTRFS_SHARED_BLOCK_REF_KEY);
}
Expand Down Expand Up @@ -3477,7 +3486,16 @@ static int __add_tree_block(struct reloc_control *rc,
goto again;
}
}
BUG_ON(ret);
if (ret) {
ASSERT(ret == 1);
btrfs_print_leaf(rc->extent_root->fs_info, path->nodes[0]);
btrfs_err(fs_info,
"tree block extent item (%llu) is not found in extent tree",
bytenr);
WARN_ON(1);
ret = -EINVAL;
goto out;
}

ret = add_tree_block(rc, &key, path, blocks);
out:
Expand Down Expand Up @@ -3755,7 +3773,8 @@ int add_data_references(struct reloc_control *rc,

while (ptr < end) {
iref = (struct btrfs_extent_inline_ref *)ptr;
key.type = btrfs_extent_inline_ref_type(eb, iref);
key.type = btrfs_get_extent_inline_ref_type(eb, iref,
BTRFS_REF_TYPE_DATA);
if (key.type == BTRFS_SHARED_DATA_REF_KEY) {
key.offset = btrfs_extent_inline_ref_offset(eb, iref);
ret = __add_tree_block(rc, key.offset, blocksize,
Expand All @@ -3765,7 +3784,10 @@ int add_data_references(struct reloc_control *rc,
ret = find_data_references(rc, extent_key,
eb, dref, blocks);
} else {
BUG();
ret = -EINVAL;
btrfs_err(rc->extent_root->fs_info,
"extent %llu slot %d has an invalid inline ref type",
eb->start, path->slots[0]);
}
if (ret) {
err = ret;
Expand Down