aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--btrfs-convert.c24
-rw-r--r--mkfs.c2
-rw-r--r--utils.c91
-rw-r--r--utils.h27
4 files changed, 118 insertions, 26 deletions
diff --git a/btrfs-convert.c b/btrfs-convert.c
index 89df815..36ffba0 100644
--- a/btrfs-convert.c
+++ b/btrfs-convert.c
@@ -108,28 +108,6 @@ struct btrfs_convert_operations {
void (*close_fs)(struct btrfs_convert_context *cctx);
};
-struct btrfs_convert_context {
- u32 blocksize;
- u32 first_data_block;
- u32 block_count;
- u32 inodes_count;
- u32 free_inodes_count;
- u64 total_bytes;
- char *volume_name;
- const struct btrfs_convert_operations *convert_ops;
-
- /* The accurate used space of old filesystem */
- struct cache_tree used;
-
- /* Batched ranges which must be covered by data chunks */
- struct cache_tree data_chunks;
-
- /* Free space which is not covered by data_chunks */
- struct cache_tree free;
-
- void *fs_data;
-};
-
static void init_convert_context(struct btrfs_convert_context *cctx)
{
cache_tree_init(&cctx->used);
@@ -2834,7 +2812,7 @@ static int do_convert(const char *devname, int datacsum, int packing, int noxatt
mkfs_cfg.stripesize = blocksize;
mkfs_cfg.features = features;
- ret = make_btrfs(fd, &mkfs_cfg);
+ ret = make_btrfs(fd, &mkfs_cfg, NULL);
if (ret) {
fprintf(stderr, "unable to create initial ctree: %s\n",
strerror(-ret));
diff --git a/mkfs.c b/mkfs.c
index aff9a68..4243a4a 100644
--- a/mkfs.c
+++ b/mkfs.c
@@ -1675,7 +1675,7 @@ int main(int argc, char **argv)
mkfs_cfg.stripesize = stripesize;
mkfs_cfg.features = features;
- ret = make_btrfs(fd, &mkfs_cfg);
+ ret = make_btrfs(fd, &mkfs_cfg, NULL);
if (ret) {
fprintf(stderr, "error during mkfs: %s\n", strerror(-ret));
exit(1);
diff --git a/utils.c b/utils.c
index eeff910..71b2200 100644
--- a/utils.c
+++ b/utils.c
@@ -182,9 +182,96 @@ int test_uuid_unique(char *fs_uuid)
}
/*
+ * Reserve space from free_tree.
+ * The algorithm is very simple, find the first cache_extent with enough space
+ * and allocate from its beginning.
+ */
+static int reserve_free_space(struct cache_tree *free_tree, u64 len,
+ u64 *ret_start)
+{
+ struct cache_extent *cache;
+ int found = 0;
+
+ BUG_ON(!ret_start);
+ cache = first_cache_extent(free_tree);
+ while (cache) {
+ if (cache->size > len) {
+ found = 1;
+ *ret_start = cache->start;
+
+ cache->size -= len;
+ if (cache->size == 0) {
+ remove_cache_extent(free_tree, cache);
+ free(cache);
+ } else {
+ cache->start += len;
+ }
+ break;
+ }
+ cache = next_cache_extent(cache);
+ }
+ if (!found)
+ return -ENOSPC;
+ return 0;
+}
+
+/*
+ * Improved version of make_btrfs().
+ *
+ * This one will
+ * 1) Do chunk allocation to avoid used data
+ * And after this function, extent type matches chunk type
+ * 2) Better structured code
+ * No super long hand written codes to initialized all tree blocks
+ * Split into small blocks and reuse codes.
+ * TODO: Reuse tree operation facilities by introducing new flags
+ */
+static int make_convert_btrfs(int fd, struct btrfs_mkfs_config *cfg,
+ struct btrfs_convert_context *cctx)
+{
+ struct cache_tree *free = &cctx->free;
+ struct cache_tree *used = &cctx->used;
+ u64 sys_chunk_start;
+ u64 meta_chunk_start;
+ int ret;
+
+ /* Shouldn't happen */
+ BUG_ON(cache_tree_empty(used));
+
+ /*
+ * reserve space for temporary superblock first
+ * Here we allocate a little larger space, to keep later
+ * free space will be STRIPE_LEN aligned
+ */
+ ret = reserve_free_space(free, BTRFS_STRIPE_LEN,
+ &cfg->super_bytenr);
+ if (ret < 0)
+ goto out;
+
+ /*
+ * Then reserve system chunk space
+ * TODO: Change system group size depending on cctx->total_bytes.
+ * If using current 4M, it can only handle less than one TB for
+ * worst case and then run out of sys space.
+ */
+ ret = reserve_free_space(free, BTRFS_MKFS_SYSTEM_GROUP_SIZE,
+ &sys_chunk_start);
+ if (ret < 0)
+ goto out;
+ ret = reserve_free_space(free, BTRFS_CONVERT_META_GROUP_SIZE,
+ &meta_chunk_start);
+ if (ret < 0)
+ goto out;
+
+out:
+ return ret;
+}
+
+/*
* @fs_uuid - if NULL, generates a UUID, returns back the new filesystem UUID
*/
-int make_btrfs(int fd, struct btrfs_mkfs_config *cfg)
+int make_btrfs(int fd, struct btrfs_mkfs_config *cfg,
+ struct btrfs_convert_context *cctx)
{
struct btrfs_super_block super;
struct extent_buffer *buf;
@@ -209,6 +296,8 @@ int make_btrfs(int fd, struct btrfs_mkfs_config *cfg)
BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA);
u64 num_bytes;
+ if (cctx)
+ return make_convert_btrfs(fd, cfg, cctx);
buf = malloc(sizeof(*buf) + max(cfg->sectorsize, cfg->nodesize));
if (!buf)
return -ENOMEM;
diff --git a/utils.h b/utils.h
index 4fee469..c79f81e 100644
--- a/utils.h
+++ b/utils.h
@@ -46,6 +46,8 @@
| BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA \
| BTRFS_FEATURE_INCOMPAT_NO_HOLES)
+#define BTRFS_CONVERT_META_GROUP_SIZE (32 * 1024 * 1024)
+
#define BTRFS_FEATURE_LIST_ALL (1ULL << 63)
#define BTRFS_SCAN_MOUNTED (1ULL << 0)
@@ -123,7 +125,30 @@ struct btrfs_mkfs_config {
u64 super_bytenr;
};
-int make_btrfs(int fd, struct btrfs_mkfs_config *cfg);
+struct btrfs_convert_context {
+ u32 blocksize;
+ u32 first_data_block;
+ u32 block_count;
+ u32 inodes_count;
+ u32 free_inodes_count;
+ u64 total_bytes;
+ char *volume_name;
+ const struct btrfs_convert_operations *convert_ops;
+
+ /* The accurate used space of old filesystem */
+ struct cache_tree used;
+
+ /* Batched ranges which must be covered by data chunks */
+ struct cache_tree data_chunks;
+
+ /* Free space which is not covered by data_chunks */
+ struct cache_tree free;
+
+ void *fs_data;
+};
+
+int make_btrfs(int fd, struct btrfs_mkfs_config *cfg,
+ struct btrfs_convert_context *cctx);
int btrfs_make_root_dir(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 objectid);
int btrfs_prepare_device(int fd, const char *file, int zero_end,