aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/btrfs/disk-io.c7
-rw-r--r--fs/btrfs/volumes.c184
-rw-r--r--fs/btrfs/volumes.h2
3 files changed, 193 insertions, 0 deletions
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 6a5a3cd12886..5124c15705ce 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -3030,6 +3030,13 @@ retry_root_backup:
3030 fs_info->generation = generation; 3030 fs_info->generation = generation;
3031 fs_info->last_trans_committed = generation; 3031 fs_info->last_trans_committed = generation;
3032 3032
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 }
3033 ret = btrfs_recover_balance(fs_info); 3040 ret = btrfs_recover_balance(fs_info);
3034 if (ret) { 3041 if (ret) {
3035 btrfs_err(fs_info, "failed to recover balance: %d", ret); 3042 btrfs_err(fs_info, "failed to recover balance: %d", ret);
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index 74977203fc85..96be1e50b027 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -6452,6 +6452,7 @@ static int read_one_chunk(struct btrfs_fs_info *fs_info, struct btrfs_key *key,
6452 map->stripe_len = btrfs_chunk_stripe_len(leaf, chunk); 6452 map->stripe_len = btrfs_chunk_stripe_len(leaf, chunk);
6453 map->type = btrfs_chunk_type(leaf, chunk); 6453 map->type = btrfs_chunk_type(leaf, chunk);
6454 map->sub_stripes = btrfs_chunk_sub_stripes(leaf, chunk); 6454 map->sub_stripes = btrfs_chunk_sub_stripes(leaf, chunk);
6455 map->verified_stripes = 0;
6455 for (i = 0; i < num_stripes; i++) { 6456 for (i = 0; i < num_stripes; i++) {
6456 map->stripes[i].physical = 6457 map->stripes[i].physical =
6457 btrfs_stripe_offset_nr(leaf, chunk, i); 6458 btrfs_stripe_offset_nr(leaf, chunk, i);
@@ -7318,3 +7319,186 @@ int btrfs_bg_type_to_factor(u64 flags)
7318 return 2; 7319 return 2;
7319 return 1; 7320 return 1;
7320} 7321}
7322
7323
7324static 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
7344static 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 }
7400out:
7401 free_extent_map(em);
7402 return ret;
7403}
7404
7405static 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 }
7425out:
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 */
7437int 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);
7501out:
7502 btrfs_free_path(path);
7503 return ret;
7504}
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h
index 049619176831..23e9285d88de 100644
--- a/fs/btrfs/volumes.h
+++ b/fs/btrfs/volumes.h
@@ -345,6 +345,7 @@ struct map_lookup {
345 u64 stripe_len; 345 u64 stripe_len;
346 int num_stripes; 346 int num_stripes;
347 int sub_stripes; 347 int sub_stripes;
348 int verified_stripes; /* For mount time dev extent verification */
348 struct btrfs_bio_stripe stripes[]; 349 struct btrfs_bio_stripe stripes[];
349}; 350};
350 351
@@ -556,5 +557,6 @@ bool btrfs_check_rw_degradable(struct btrfs_fs_info *fs_info,
556 struct btrfs_device *failing_dev); 557 struct btrfs_device *failing_dev);
557 558
558int btrfs_bg_type_to_factor(u64 flags); 559int btrfs_bg_type_to_factor(u64 flags);
560int btrfs_verify_dev_extents(struct btrfs_fs_info *fs_info);
559 561
560#endif 562#endif