return btrfs_grab_root(btrfs_global_root(fs_info, &key));
case BTRFS_RAID_STRIPE_TREE_OBJECTID:
return btrfs_grab_root(fs_info->stripe_root);
+ case BTRFS_REMAP_TREE_OBJECTID:
+ return btrfs_grab_root(fs_info->remap_root);
default:
return NULL;
}
btrfs_put_root(fs_info->data_reloc_root);
btrfs_put_root(fs_info->block_group_root);
btrfs_put_root(fs_info->stripe_root);
+ btrfs_put_root(fs_info->remap_root);
btrfs_check_leaked_roots(fs_info);
btrfs_extent_buffer_leak_debug_check(fs_info);
kfree(fs_info->super_copy);
free_root_extent_buffers(info->data_reloc_root);
free_root_extent_buffers(info->block_group_root);
free_root_extent_buffers(info->stripe_root);
+ free_root_extent_buffers(info->remap_root);
if (free_chunk_root)
free_root_extent_buffers(info->chunk_root);
}
if (ret)
goto out;
- /*
- * This tree can share blocks with some other fs tree during relocation
- * and we need a proper setup by btrfs_get_fs_root
- */
- root = btrfs_get_fs_root(tree_root->fs_info,
- BTRFS_DATA_RELOC_TREE_OBJECTID, true);
- if (IS_ERR(root)) {
- if (!btrfs_test_opt(fs_info, IGNOREBADROOTS)) {
- location.objectid = BTRFS_DATA_RELOC_TREE_OBJECTID;
- ret = PTR_ERR(root);
- goto out;
+ if (btrfs_fs_incompat(fs_info, REMAP_TREE)) {
+ /* The remap_root has already been loaded in load_important_roots(). */
+ root = fs_info->remap_root;
+
+ set_bit(BTRFS_ROOT_TRACK_DIRTY, &root->state);
+
+ root->root_key.objectid = BTRFS_REMAP_TREE_OBJECTID;
+ root->root_key.type = BTRFS_ROOT_ITEM_KEY;
+ root->root_key.offset = 0;
+
+ /* Check that data reloc tree doesn't also exist. */
+ location.objectid = BTRFS_DATA_RELOC_TREE_OBJECTID;
+ root = btrfs_read_tree_root(fs_info->tree_root, &location);
+ if (!IS_ERR(root)) {
+ btrfs_err(fs_info, "data reloc tree exists when remap-tree enabled");
+ btrfs_put_root(root);
+ return -EIO;
+ } else if (PTR_ERR(root) != -ENOENT) {
+ btrfs_warn(fs_info, "error %ld when checking for data reloc tree",
+ PTR_ERR(root));
}
} else {
- set_bit(BTRFS_ROOT_TRACK_DIRTY, &root->state);
- fs_info->data_reloc_root = root;
+ /*
+ * This tree can share blocks with some other fs tree during
+ * relocation and we need a proper setup by btrfs_get_fs_root().
+ */
+ root = btrfs_get_fs_root(tree_root->fs_info,
+ BTRFS_DATA_RELOC_TREE_OBJECTID, true);
+ if (IS_ERR(root)) {
+ if (!btrfs_test_opt(fs_info, IGNOREBADROOTS)) {
+ location.objectid = BTRFS_DATA_RELOC_TREE_OBJECTID;
+ ret = PTR_ERR(root);
+ goto out;
+ }
+ } else {
+ set_bit(BTRFS_ROOT_TRACK_DIRTY, &root->state);
+ fs_info->data_reloc_root = root;
+ }
}
location.objectid = BTRFS_QUOTA_TREE_OBJECTID;
ret = -EINVAL;
}
+ if (btrfs_fs_incompat(fs_info, REMAP_TREE)) {
+ /*
+ * Reduce test matrix for remap tree by requiring block-group-tree
+ * and no-holes. Free-space-tree is a hard requirement.
+ */
+ if (!btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE_VALID) ||
+ !btrfs_fs_incompat(fs_info, NO_HOLES) ||
+ !btrfs_fs_compat_ro(fs_info, BLOCK_GROUP_TREE)) {
+ btrfs_err(fs_info,
+"remap-tree feature requires free-space-tree, no-holes, and block-group-tree");
+ ret = -EINVAL;
+ }
+
+ if (btrfs_fs_incompat(fs_info, MIXED_GROUPS)) {
+ btrfs_err(fs_info, "remap-tree not supported with mixed-bg");
+ ret = -EINVAL;
+ }
+
+ if (btrfs_fs_incompat(fs_info, ZONED)) {
+ btrfs_err(fs_info, "remap-tree not supported with zoned devices");
+ ret = -EINVAL;
+ }
+
+ if (sectorsize > PAGE_SIZE) {
+ btrfs_err(fs_info, "remap-tree not supported when block size > page size");
+ ret = -EINVAL;
+ }
+ }
+
/*
* Hint to catch really bogus numbers, bitflips or so, more exact checks are
* done later
btrfs_warn(fs_info, "couldn't read tree root");
return ret;
}
+
+ if (btrfs_fs_incompat(fs_info, REMAP_TREE)) {
+ bytenr = btrfs_super_remap_root(sb);
+ gen = btrfs_super_remap_root_generation(sb);
+ level = btrfs_super_remap_root_level(sb);
+ ret = load_super_root(fs_info->remap_root, bytenr, gen, level);
+ if (ret) {
+ btrfs_warn(fs_info, "couldn't read remap root");
+ return ret;
+ }
+ }
+
return 0;
}
struct btrfs_fs_info *fs_info = btrfs_sb(sb);
struct btrfs_root *tree_root;
struct btrfs_root *chunk_root;
+ struct btrfs_root *remap_root;
int ret;
int level;
if (ret < 0)
goto fail_alloc;
+ if (btrfs_super_incompat_flags(disk_super) & BTRFS_FEATURE_INCOMPAT_REMAP_TREE) {
+ remap_root = btrfs_alloc_root(fs_info, BTRFS_REMAP_TREE_OBJECTID,
+ GFP_KERNEL);
+ fs_info->remap_root = remap_root;
+ if (!remap_root) {
+ ret = -ENOMEM;
+ goto fail_alloc;
+ }
+ }
+
/*
* At this point our mount options are validated, if we set ->max_inline
* to something non-standard make sure we truncate it to sectorsize.