Skip to content

Commit cf90d88

Browse files
adam900710kdave
authored andcommitted
btrfs: Introduce mount time chunk <-> dev extent mapping check
This patch will introduce chunk <-> dev extent mapping check, to protect us against invalid dev extents or chunks. Since chunk mapping is the fundamental infrastructure of btrfs, extra check at mount time could prevent a lot of unexpected behavior (BUG_ON). Reported-by: Xu Wen <wen.xu@gatech.edu> Link: https://bugzilla.kernel.org/show_bug.cgi?id=200403 Link: https://bugzilla.kernel.org/show_bug.cgi?id=200407 Signed-off-by: Qu Wenruo <wqu@suse.com> Reviewed-by: Su Yue <suy.fnst@cn.fujitsu.com> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
1 parent 7ef4951 commit cf90d88

File tree

3 files changed

+193
-0
lines changed

3 files changed

+193
-0
lines changed

fs/btrfs/disk-io.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3030,6 +3030,13 @@ int open_ctree(struct super_block *sb,
30303030
fs_info->generation = generation;
30313031
fs_info->last_trans_committed = generation;
30323032

3033+
ret = btrfs_verify_dev_extents(fs_info);
3034+
if (ret) {
3035+
btrfs_err(fs_info,
3036+
"failed to verify dev extents against chunks: %d",
3037+
ret);
3038+
goto fail_block_groups;
3039+
}
30333040
ret = btrfs_recover_balance(fs_info);
30343041
if (ret) {
30353042
btrfs_err(fs_info, "failed to recover balance: %d", ret);

fs/btrfs/volumes.c

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6452,6 +6452,7 @@ static int read_one_chunk(struct btrfs_fs_info *fs_info, struct btrfs_key *key,
64526452
map->stripe_len = btrfs_chunk_stripe_len(leaf, chunk);
64536453
map->type = btrfs_chunk_type(leaf, chunk);
64546454
map->sub_stripes = btrfs_chunk_sub_stripes(leaf, chunk);
6455+
map->verified_stripes = 0;
64556456
for (i = 0; i < num_stripes; i++) {
64566457
map->stripes[i].physical =
64576458
btrfs_stripe_offset_nr(leaf, chunk, i);
@@ -7318,3 +7319,186 @@ int btrfs_bg_type_to_factor(u64 flags)
73187319
return 2;
73197320
return 1;
73207321
}
7322+
7323+
7324+
static u64 calc_stripe_length(u64 type, u64 chunk_len, int num_stripes)
7325+
{
7326+
int index = btrfs_bg_flags_to_raid_index(type);
7327+
int ncopies = btrfs_raid_array[index].ncopies;
7328+
int data_stripes;
7329+
7330+
switch (type & BTRFS_BLOCK_GROUP_PROFILE_MASK) {
7331+
case BTRFS_BLOCK_GROUP_RAID5:
7332+
data_stripes = num_stripes - 1;
7333+
break;
7334+
case BTRFS_BLOCK_GROUP_RAID6:
7335+
data_stripes = num_stripes - 2;
7336+
break;
7337+
default:
7338+
data_stripes = num_stripes / ncopies;
7339+
break;
7340+
}
7341+
return div_u64(chunk_len, data_stripes);
7342+
}
7343+
7344+
static int verify_one_dev_extent(struct btrfs_fs_info *fs_info,
7345+
u64 chunk_offset, u64 devid,
7346+
u64 physical_offset, u64 physical_len)
7347+
{
7348+
struct extent_map_tree *em_tree = &fs_info->mapping_tree.map_tree;
7349+
struct extent_map *em;
7350+
struct map_lookup *map;
7351+
u64 stripe_len;
7352+
bool found = false;
7353+
int ret = 0;
7354+
int i;
7355+
7356+
read_lock(&em_tree->lock);
7357+
em = lookup_extent_mapping(em_tree, chunk_offset, 1);
7358+
read_unlock(&em_tree->lock);
7359+
7360+
if (!em) {
7361+
btrfs_err(fs_info,
7362+
"dev extent physical offset %llu on devid %llu doesn't have corresponding chunk",
7363+
physical_offset, devid);
7364+
ret = -EUCLEAN;
7365+
goto out;
7366+
}
7367+
7368+
map = em->map_lookup;
7369+
stripe_len = calc_stripe_length(map->type, em->len, map->num_stripes);
7370+
if (physical_len != stripe_len) {
7371+
btrfs_err(fs_info,
7372+
"dev extent physical offset %llu on devid %llu length doesn't match chunk %llu, have %llu expect %llu",
7373+
physical_offset, devid, em->start, physical_len,
7374+
stripe_len);
7375+
ret = -EUCLEAN;
7376+
goto out;
7377+
}
7378+
7379+
for (i = 0; i < map->num_stripes; i++) {
7380+
if (map->stripes[i].dev->devid == devid &&
7381+
map->stripes[i].physical == physical_offset) {
7382+
found = true;
7383+
if (map->verified_stripes >= map->num_stripes) {
7384+
btrfs_err(fs_info,
7385+
"too many dev extents for chunk %llu found",
7386+
em->start);
7387+
ret = -EUCLEAN;
7388+
goto out;
7389+
}
7390+
map->verified_stripes++;
7391+
break;
7392+
}
7393+
}
7394+
if (!found) {
7395+
btrfs_err(fs_info,
7396+
"dev extent physical offset %llu devid %llu has no corresponding chunk",
7397+
physical_offset, devid);
7398+
ret = -EUCLEAN;
7399+
}
7400+
out:
7401+
free_extent_map(em);
7402+
return ret;
7403+
}
7404+
7405+
static int verify_chunk_dev_extent_mapping(struct btrfs_fs_info *fs_info)
7406+
{
7407+
struct extent_map_tree *em_tree = &fs_info->mapping_tree.map_tree;
7408+
struct extent_map *em;
7409+
struct rb_node *node;
7410+
int ret = 0;
7411+
7412+
read_lock(&em_tree->lock);
7413+
for (node = rb_first(&em_tree->map); node; node = rb_next(node)) {
7414+
em = rb_entry(node, struct extent_map, rb_node);
7415+
if (em->map_lookup->num_stripes !=
7416+
em->map_lookup->verified_stripes) {
7417+
btrfs_err(fs_info,
7418+
"chunk %llu has missing dev extent, have %d expect %d",
7419+
em->start, em->map_lookup->verified_stripes,
7420+
em->map_lookup->num_stripes);
7421+
ret = -EUCLEAN;
7422+
goto out;
7423+
}
7424+
}
7425+
out:
7426+
read_unlock(&em_tree->lock);
7427+
return ret;
7428+
}
7429+
7430+
/*
7431+
* Ensure that all dev extents are mapped to correct chunk, otherwise
7432+
* later chunk allocation/free would cause unexpected behavior.
7433+
*
7434+
* NOTE: This will iterate through the whole device tree, which should be of
7435+
* the same size level as the chunk tree. This slightly increases mount time.
7436+
*/
7437+
int btrfs_verify_dev_extents(struct btrfs_fs_info *fs_info)
7438+
{
7439+
struct btrfs_path *path;
7440+
struct btrfs_root *root = fs_info->dev_root;
7441+
struct btrfs_key key;
7442+
int ret = 0;
7443+
7444+
key.objectid = 1;
7445+
key.type = BTRFS_DEV_EXTENT_KEY;
7446+
key.offset = 0;
7447+
7448+
path = btrfs_alloc_path();
7449+
if (!path)
7450+
return -ENOMEM;
7451+
7452+
path->reada = READA_FORWARD;
7453+
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
7454+
if (ret < 0)
7455+
goto out;
7456+
7457+
if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) {
7458+
ret = btrfs_next_item(root, path);
7459+
if (ret < 0)
7460+
goto out;
7461+
/* No dev extents at all? Not good */
7462+
if (ret > 0) {
7463+
ret = -EUCLEAN;
7464+
goto out;
7465+
}
7466+
}
7467+
while (1) {
7468+
struct extent_buffer *leaf = path->nodes[0];
7469+
struct btrfs_dev_extent *dext;
7470+
int slot = path->slots[0];
7471+
u64 chunk_offset;
7472+
u64 physical_offset;
7473+
u64 physical_len;
7474+
u64 devid;
7475+
7476+
btrfs_item_key_to_cpu(leaf, &key, slot);
7477+
if (key.type != BTRFS_DEV_EXTENT_KEY)
7478+
break;
7479+
devid = key.objectid;
7480+
physical_offset = key.offset;
7481+
7482+
dext = btrfs_item_ptr(leaf, slot, struct btrfs_dev_extent);
7483+
chunk_offset = btrfs_dev_extent_chunk_offset(leaf, dext);
7484+
physical_len = btrfs_dev_extent_length(leaf, dext);
7485+
7486+
ret = verify_one_dev_extent(fs_info, chunk_offset, devid,
7487+
physical_offset, physical_len);
7488+
if (ret < 0)
7489+
goto out;
7490+
ret = btrfs_next_item(root, path);
7491+
if (ret < 0)
7492+
goto out;
7493+
if (ret > 0) {
7494+
ret = 0;
7495+
break;
7496+
}
7497+
}
7498+
7499+
/* Ensure all chunks have corresponding dev extents */
7500+
ret = verify_chunk_dev_extent_mapping(fs_info);
7501+
out:
7502+
btrfs_free_path(path);
7503+
return ret;
7504+
}

fs/btrfs/volumes.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,7 @@ struct map_lookup {
345345
u64 stripe_len;
346346
int num_stripes;
347347
int sub_stripes;
348+
int verified_stripes; /* For mount time dev extent verification */
348349
struct btrfs_bio_stripe stripes[];
349350
};
350351

@@ -556,5 +557,6 @@ bool btrfs_check_rw_degradable(struct btrfs_fs_info *fs_info,
556557
struct btrfs_device *failing_dev);
557558

558559
int btrfs_bg_type_to_factor(u64 flags);
560+
int btrfs_verify_dev_extents(struct btrfs_fs_info *fs_info);
559561

560562
#endif

0 commit comments

Comments
 (0)