Btrfs-progs: add support for mixed data+metadata block groups
authorJosef Bacik <josef@redhat.com>
Thu, 9 Dec 2010 18:31:08 +0000 (18:31 +0000)
committerChris Mason <chris.mason@oracle.com>
Tue, 25 Oct 2011 13:18:31 +0000 (09:18 -0400)
So alot of crazy people (I'm looking at you Meego) want to use btrfs on phones
and such with small devices.  Unfortunately the way we split out metadata/data
chunks it makes space usage inefficient for volumes that are smaller than
1gigabyte.  So add a -M option for mixing metadata+data, and default to this
mixed mode if the filesystem is less than or equal to 1 gigabyte.  I've tested
this with xfstests on a 100mb filesystem and everything is a-ok.

Signed-off-by: Josef Bacik <josef@redhat.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
btrfs-vol.c
btrfs_cmds.c
ctree.h
mkfs.c
utils.c
utils.h

index 4ed799d..f573023 100644 (file)
@@ -143,7 +143,9 @@ int main(int ac, char **av)
                exit(1);
        }
        if (cmd == BTRFS_IOC_ADD_DEV) {
-               ret = btrfs_prepare_device(devfd, device, 1, &dev_block_count);
+               int mixed = 0;
+
+               ret = btrfs_prepare_device(devfd, device, 1, &dev_block_count, &mixed);
                if (ret) {
                        fprintf(stderr, "Unable to init %s\n", device);
                        exit(1);
index 775bfe1..c21a007 100644 (file)
@@ -720,6 +720,7 @@ int do_add_volume(int nargs, char **args)
                int     devfd, res;
                u64 dev_block_count = 0;
                struct stat st;
+               int mixed = 0;
 
                devfd = open(args[i], O_RDWR);
                if (!devfd) {
@@ -742,7 +743,7 @@ int do_add_volume(int nargs, char **args)
                        continue;
                }
 
-               res = btrfs_prepare_device(devfd, args[i], 1, &dev_block_count);
+               res = btrfs_prepare_device(devfd, args[i], 1, &dev_block_count, &mixed);
                if (res) {
                        fprintf(stderr, "ERROR: Unable to init '%s'\n", args[i]);
                        close(devfd);
@@ -920,8 +921,14 @@ int do_df_filesystem(int nargs, char **argv)
                memset(description, 0, 80);
 
                if (flags & BTRFS_BLOCK_GROUP_DATA) {
-                       snprintf(description, 5, "%s", "Data");
-                       written += 4;
+                       if (flags & BTRFS_BLOCK_GROUP_METADATA) {
+                               snprintf(description, 15, "%s",
+                                        "Data+Metadata");
+                               written += 14;
+                       } else {
+                               snprintf(description, 5, "%s", "Data");
+                               written += 4;
+                       }
                } else if (flags & BTRFS_BLOCK_GROUP_SYSTEM) {
                        snprintf(description, 7, "%s", "System");
                        written += 6;
diff --git a/ctree.h b/ctree.h
index 962c510..ed83d02 100644 (file)
--- a/ctree.h
+++ b/ctree.h
@@ -352,13 +352,15 @@ struct btrfs_super_block {
  * ones specified below then we will fail to mount
  */
 #define BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF   (1ULL << 0)
-#define BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL  (2ULL << 0)
+#define BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL  (1ULL << 1)
+#define BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS    (1ULL << 2)
 
 #define BTRFS_FEATURE_COMPAT_SUPP              0ULL
 #define BTRFS_FEATURE_COMPAT_RO_SUPP           0ULL
-#define BTRFS_FEATURE_INCOMPAT_SUPP            \
-       (BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF | \
-        BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL)
+#define BTRFS_FEATURE_INCOMPAT_SUPP                    \
+       (BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF |         \
+        BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL |        \
+        BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS)
 
 /*
  * A leaf is full of items. offset and size tell us where to find
diff --git a/mkfs.c b/mkfs.c
index 2e99b95..04de93a 100644 (file)
--- a/mkfs.c
+++ b/mkfs.c
@@ -69,7 +69,7 @@ static u64 parse_size(char *s)
        return atol(s) * mult;
 }
 
-static int make_root_dir(struct btrfs_root *root)
+static int make_root_dir(struct btrfs_root *root, int mixed)
 {
        struct btrfs_trans_handle *trans;
        struct btrfs_key location;
@@ -88,30 +88,47 @@ static int make_root_dir(struct btrfs_root *root)
                                     0, BTRFS_MKFS_SYSTEM_GROUP_SIZE);
        BUG_ON(ret);
 
-       ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root,
-                               &chunk_start, &chunk_size,
-                               BTRFS_BLOCK_GROUP_METADATA);
-       BUG_ON(ret);
-       ret = btrfs_make_block_group(trans, root, 0,
-                                    BTRFS_BLOCK_GROUP_METADATA,
-                                    BTRFS_FIRST_CHUNK_TREE_OBJECTID,
-                                    chunk_start, chunk_size);
-       BUG_ON(ret);
+       if (mixed) {
+               ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root,
+                                       &chunk_start, &chunk_size,
+                                       BTRFS_BLOCK_GROUP_METADATA |
+                                       BTRFS_BLOCK_GROUP_DATA);
+               BUG_ON(ret);
+               ret = btrfs_make_block_group(trans, root, 0,
+                                            BTRFS_BLOCK_GROUP_METADATA |
+                                            BTRFS_BLOCK_GROUP_DATA,
+                                            BTRFS_FIRST_CHUNK_TREE_OBJECTID,
+                                            chunk_start, chunk_size);
+               BUG_ON(ret);
+               printf("Created a data/metadata chunk of size %llu\n", chunk_size);
+       } else {
+               ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root,
+                                       &chunk_start, &chunk_size,
+                                       BTRFS_BLOCK_GROUP_METADATA);
+               BUG_ON(ret);
+               ret = btrfs_make_block_group(trans, root, 0,
+                                            BTRFS_BLOCK_GROUP_METADATA,
+                                            BTRFS_FIRST_CHUNK_TREE_OBJECTID,
+                                            chunk_start, chunk_size);
+               BUG_ON(ret);
+       }
 
        root->fs_info->system_allocs = 0;
        btrfs_commit_transaction(trans, root);
        trans = btrfs_start_transaction(root, 1);
        BUG_ON(!trans);
 
-       ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root,
-                               &chunk_start, &chunk_size,
-                               BTRFS_BLOCK_GROUP_DATA);
-       BUG_ON(ret);
-       ret = btrfs_make_block_group(trans, root, 0,
-                                    BTRFS_BLOCK_GROUP_DATA,
-                                    BTRFS_FIRST_CHUNK_TREE_OBJECTID,
-                                    chunk_start, chunk_size);
-       BUG_ON(ret);
+       if (!mixed) {
+               ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root,
+                                       &chunk_start, &chunk_size,
+                                       BTRFS_BLOCK_GROUP_DATA);
+               BUG_ON(ret);
+               ret = btrfs_make_block_group(trans, root, 0,
+                                            BTRFS_BLOCK_GROUP_DATA,
+                                            BTRFS_FIRST_CHUNK_TREE_OBJECTID,
+                                            chunk_start, chunk_size);
+               BUG_ON(ret);
+       }
 
        ret = btrfs_make_root_dir(trans, root->fs_info->tree_root,
                              BTRFS_ROOT_TREE_DIR_OBJECTID);
@@ -200,7 +217,7 @@ static int create_one_raid_group(struct btrfs_trans_handle *trans,
 
 static int create_raid_groups(struct btrfs_trans_handle *trans,
                              struct btrfs_root *root, u64 data_profile,
-                             u64 metadata_profile)
+                             u64 metadata_profile, int mixed)
 {
        u64 num_devices = btrfs_super_num_devices(&root->fs_info->super_copy);
        u64 allowed;
@@ -215,20 +232,24 @@ static int create_raid_groups(struct btrfs_trans_handle *trans,
                allowed = BTRFS_BLOCK_GROUP_RAID0 | BTRFS_BLOCK_GROUP_RAID1;
 
        if (allowed & metadata_profile) {
+               u64 meta_flags = BTRFS_BLOCK_GROUP_METADATA;
+
                ret = create_one_raid_group(trans, root,
                                            BTRFS_BLOCK_GROUP_SYSTEM |
                                            (allowed & metadata_profile));
                BUG_ON(ret);
 
-               ret = create_one_raid_group(trans, root,
-                                           BTRFS_BLOCK_GROUP_METADATA |
+               if (mixed)
+                       meta_flags |= BTRFS_BLOCK_GROUP_DATA;
+
+               ret = create_one_raid_group(trans, root, meta_flags |
                                            (allowed & metadata_profile));
                BUG_ON(ret);
 
                ret = recow_roots(trans, root);
                BUG_ON(ret);
        }
-       if (num_devices > 1 && (allowed & data_profile)) {
+       if (!mixed && num_devices > 1 && (allowed & data_profile)) {
                ret = create_one_raid_group(trans, root,
                                            BTRFS_BLOCK_GROUP_DATA |
                                            (allowed & data_profile));
@@ -274,6 +295,7 @@ static void print_usage(void)
        fprintf(stderr, "\t -l --leafsize size of btree leaves\n");
        fprintf(stderr, "\t -L --label set a label\n");
        fprintf(stderr, "\t -m --metadata metadata profile, values like data profile\n");
+       fprintf(stderr, "\t -M --mixed mix metadata and data together\n");
        fprintf(stderr, "\t -n --nodesize size of btree nodes\n");
        fprintf(stderr, "\t -s --sectorsize min block allocation\n");
        fprintf(stderr, "%s\n", BTRFS_BUILD_VERSION);
@@ -328,6 +350,7 @@ static struct option long_options[] = {
        { "leafsize", 1, NULL, 'l' },
        { "label", 1, NULL, 'L'},
        { "metadata", 1, NULL, 'm' },
+       { "mixed", 0, NULL, 'M' },
        { "nodesize", 1, NULL, 'n' },
        { "sectorsize", 1, NULL, 's' },
        { "data", 1, NULL, 'd' },
@@ -358,10 +381,13 @@ int main(int ac, char **av)
        int first_fd;
        int ret;
        int i;
+       int mixed = 0;
+       int data_profile_opt = 0;
+       int metadata_profile_opt = 0;
 
        while(1) {
                int c;
-               c = getopt_long(ac, av, "A:b:l:n:s:m:d:L:V", long_options,
+               c = getopt_long(ac, av, "A:b:l:n:s:m:d:L:VM", long_options,
                                &option_index);
                if (c < 0)
                        break;
@@ -371,6 +397,7 @@ int main(int ac, char **av)
                                break;
                        case 'd':
                                data_profile = parse_profile(optarg);
+                               data_profile_opt = 1;
                                break;
                        case 'l':
                                leafsize = parse_size(optarg);
@@ -380,6 +407,10 @@ int main(int ac, char **av)
                                break;
                        case 'm':
                                metadata_profile = parse_profile(optarg);
+                               metadata_profile_opt = 1;
+                               break;
+                       case 'M':
+                               mixed = 1;
                                break;
                        case 'n':
                                nodesize = parse_size(optarg);
@@ -389,12 +420,10 @@ int main(int ac, char **av)
                                break;
                        case 'b':
                                block_count = parse_size(optarg);
-                               if (block_count < 256*1024*1024) {
-                                       fprintf(stderr, "File system size "
-                                               "%llu bytes is too small, "
-                                               "256M is required at least\n",
-                                               (unsigned long long)block_count);
-                                       exit(1);
+                               if (block_count <= 1024*1024*1024) {
+                                       printf("SMALL VOLUME: forcing mixed "
+                                              "metadata/data groups\n");
+                                       mixed = 1;
                                }
                                zero_end = 0;
                                break;
@@ -439,9 +468,22 @@ int main(int ac, char **av)
        }
        first_fd = fd;
        first_file = file;
-       ret = btrfs_prepare_device(fd, file, zero_end, &dev_block_count);
+       ret = btrfs_prepare_device(fd, file, zero_end, &dev_block_count,
+                                  &mixed);
        if (block_count == 0)
                block_count = dev_block_count;
+       if (mixed) {
+               if (!metadata_profile_opt)
+                       metadata_profile = 0;
+               if (!data_profile_opt)
+                       data_profile = 0;
+
+               if (metadata_profile != data_profile) {
+                       fprintf(stderr, "With mixed block groups data and metadata "
+                               "profiles must be the same\n");
+                       exit(1);
+               }
+       }
 
        blocks[0] = BTRFS_SUPER_INFO_OFFSET;
        for (i = 1; i < 7; i++) {
@@ -459,7 +501,7 @@ int main(int ac, char **av)
        root = open_ctree(file, 0, O_RDWR);
        root->fs_info->alloc_start = alloc_start;
 
-       ret = make_root_dir(root);
+       ret = make_root_dir(root, mixed);
        if (ret) {
                fprintf(stderr, "failed to setup the root directory\n");
                exit(1);
@@ -478,6 +520,8 @@ int main(int ac, char **av)
 
        zero_end = 1;
        while(ac-- > 0) {
+               int old_mixed = mixed;
+
                file = av[optind++];
                ret = check_mounted(file);
                if (ret < 0) {
@@ -503,8 +547,8 @@ int main(int ac, char **av)
                        continue;
                }
                ret = btrfs_prepare_device(fd, file, zero_end,
-                                          &dev_block_count);
-
+                                          &dev_block_count, &mixed);
+               mixed = old_mixed;
                BUG_ON(ret);
 
                ret = btrfs_add_to_fsid(trans, root, fd, file, dev_block_count,
@@ -515,12 +559,20 @@ int main(int ac, char **av)
 
 raid_groups:
        ret = create_raid_groups(trans, root, data_profile,
-                                metadata_profile);
+                                metadata_profile, mixed);
        BUG_ON(ret);
 
        ret = create_data_reloc_tree(trans, root);
        BUG_ON(ret);
 
+       if (mixed) {
+               struct btrfs_super_block *super = &root->fs_info->super_copy;
+               u64 flags = btrfs_super_incompat_flags(super);
+
+               flags |= BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS;
+               btrfs_set_super_incompat_flags(super, flags);
+       }
+
        printf("fs created label %s on %s\n\tnodesize %u leafsize %u "
            "sectorsize %u size %s\n",
            label, first_file, nodesize, leafsize, sectorsize,
diff --git a/utils.c b/utils.c
index 35e17b8..ad980ae 100644 (file)
--- a/utils.c
+++ b/utils.c
@@ -512,7 +512,8 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
        return 0;
 }
 
-int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret)
+int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret,
+                        int *mixed)
 {
        u64 block_count;
        u64 bytenr;
@@ -532,10 +533,9 @@ int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret)
        }
        zero_end = 1;
 
-       if (block_count < 256 * 1024 * 1024) {
-               fprintf(stderr, "device %s is too small "
-                       "(must be at least 256 MB)\n", file);
-               exit(1);
+       if (block_count < 1024 * 1024 * 1024 && !(*mixed)) {
+               printf("SMALL VOLUME: forcing mixed metadata/data groups\n");
+               *mixed = 1;
        }
        ret = zero_dev_start(fd);
        if (ret) {
diff --git a/utils.h b/utils.h
index 9dce5b0..a28d7f4 100644 (file)
--- a/utils.h
+++ b/utils.h
@@ -27,7 +27,7 @@ int make_btrfs(int fd, const char *device, const char *label,
 int btrfs_make_root_dir(struct btrfs_trans_handle *trans,
                        struct btrfs_root *root, u64 objectid);
 int btrfs_prepare_device(int fd, char *file, int zero_end,
-                        u64 *block_count_ret);
+                        u64 *block_count_ret, int *mixed);
 int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
                      struct btrfs_root *root, int fd, char *path,
                      u64 block_count, u32 io_width, u32 io_align,