]> git.sesse.net Git - bcachefs-tools-debian/blobdiff - make-bcache.c
update stop ioctl
[bcachefs-tools-debian] / make-bcache.c
index 5f4ebccadc3fef0b79f0a9a92eb239e6fb8451d2..8a944613e29cc6f242026da873de57bae8f17026 100644 (file)
@@ -1,5 +1,11 @@
+/*
+ * Author: Kent Overstreet <kmo@daterainc.com>
+ *
+ * GPLv2
+ */
+
 #define _FILE_OFFSET_BITS      64
-#define __USE_FILE_OFFSET64
+#define __USE_FILE_OFFSET 64
 #define _XOPEN_SOURCE 600
 
 #include <ctype.h>
 
 #include "bcache.h"
 
-uint64_t getblocks(int fd)
-{
-       uint64_t ret;
-       struct stat statbuf;
-       if (fstat(fd, &statbuf)) {
-               perror("stat error\n");
-               exit(EXIT_FAILURE);
-       }
-       ret = statbuf.st_size / 512;
-       if (S_ISBLK(statbuf.st_mode))
-               if (ioctl(fd, BLKGETSIZE, &ret)) {
-                       perror("ioctl error");
-                       exit(EXIT_FAILURE);
-               }
-       return ret;
-}
-
-uint64_t hatoi(const char *s)
-{
-       char *e;
-       long long i = strtoll(s, &e, 10);
-       switch (*e) {
-               case 't':
-               case 'T':
-                       i *= 1024;
-               case 'g':
-               case 'G':
-                       i *= 1024;
-               case 'm':
-               case 'M':
-                       i *= 1024;
-               case 'k':
-               case 'K':
-                       i *= 1024;
-       }
-       return i;
-}
-
-unsigned hatoi_validate(const char *s, const char *msg)
-{
-       uint64_t v = hatoi(s);
-
-       if (v & (v - 1)) {
-               printf("%s must be a power of two\n", msg);
-               exit(EXIT_FAILURE);
-       }
-
-       v /= 512;
-
-       if (v > USHRT_MAX) {
-               printf("%s too large\n", msg);
-               exit(EXIT_FAILURE);
-       }
-
-       if (!v) {
-               printf("%s too small\n", msg);
-               exit(EXIT_FAILURE);
-       }
-
-       return v;
-}
-
-char *skip_spaces(const char *str)
-{
-       while (isspace(*str))
-               ++str;
-       return (char *)str;
-}
-
-char *strim(char *s)
-{
-       size_t size;
-       char *end;
-
-       s = skip_spaces(s);
-       size = strlen(s);
-       if (!size)
-               return s;
-
-       end = s + size - 1;
-       while (end >= s && isspace(*end))
-               end--;
-       *(end + 1) = '\0';
-
-       return s;
-}
-
-ssize_t read_string_list(const char *buf, const char * const list[])
-{
-       size_t i;
-       char *s, *d = strdup(buf);
-       if (!d)
-               return -ENOMEM;
-
-       s = strim(d);
-
-       for (i = 0; list[i]; i++)
-               if (!strcmp(list[i], s))
-                       break;
-
-       free(d);
-
-       if (!list[i])
-               return -EINVAL;
-
-       return i;
-}
-
 void usage()
 {
-       printf("Usage: make-bcache [options] device\n"
-              "        -C, --cache             Format a cache device\n"
-              "        -B, --bdev              Format a backing device\n"
-              "        -b, --bucket            bucket size\n"
-              "        -w, --block             block size (hard sector size of SSD, often 2k)\n"
-              "        -U                      UUID\n"
-              "            --writeback         enable writeback\n"
-              "            --discard           enable discards\n"
-              "            --cache_replacement_policy=(lru|fifo)\n"
-              "        -h, --help              display this help and exit\n");
+       fprintf(stderr,
+                  "Usage: make-bcache [options] device\n"
+              "        -C, --cache                     Format a cache device\n"
+              "        -B, --bdev                      Format a backing device\n"
+              "            --wipe-bcache               destroy existing bcache data if present\n"
+              "        -l, --label                     label\n"
+              "            --cset-uuid                 UUID for the cache set\n"
+              "            --csum-type                 One of (none|crc32c|crc64)\n"
+
+              "        -b, --bucket                    bucket size\n"
+              "        -w, --block                     block size (hard sector size of SSD, often 2k)\n"
+
+              "            --replication-set           replication set of subsequent devices\n"
+              "            --meta-replicas             number of metadata replicas\n"
+              "            --data-replicas             number of data replicas\n"
+              "            --tier                      tier of subsequent devices\n"
+              "            --cache_replacement_policy  one of (lru|fifo|random)\n"
+              "            --discard                   enable discards\n"
+
+              "            --writeback                 enable writeback\n"
+              "        -o, --data-offset               data offset in sectors\n"
+              "        -h, --help                      display this help and exit\n");
        exit(EXIT_FAILURE);
 }
 
-const char * const cache_replacement_policies[] = {
-       "lru",
-       "fifo",
-       "random",
-       NULL
-};
-
-int writeback;
-int discard;
-unsigned cache_replacement_policy;
-uint64_t data_offset = BDEV_DATA_START;
-
-struct option opts[] = {
-       { "cache",              0, NULL,        'C' },
-       { "bdev",               0, NULL,        'B' },
-       { "bucket",             1, NULL,        'b' },
-       { "block",              1, NULL,        'w' },
-       { "writeback",          0, &writeback,  1 },
-       { "discard",            0, &discard,    1 },
-       { "cache_replacement_policy", 1, NULL, 'p' },
-//     { "data_offset",        1, NULL,        'o' },
-       { "help",               0, NULL,        'h' },
-       { NULL,                 0, NULL,        0 },
-};
-
-void write_sb(char *dev, struct cache_sb *sb)
-{
-       int fd;
-       char uuid[40], set_uuid[40];
-
-       if (sb->version > BCACHE_SB_MAX_VERSION) {
-               printf("Must specify one of -C or -B\n");
-               usage();
-       }
-
-       if (sb->bucket_size < sb->block_size) {
-               printf("Bucket size cannot be smaller than block size\n");
-               exit(EXIT_FAILURE);
-       }
-
-       if ((fd = open(dev, O_RDWR|O_EXCL)) == -1) {
-               printf("Can't open dev %s: %s\n", dev, strerror(errno));
-               exit(EXIT_FAILURE);
-       }
-
-       sb->flags = 0;
-
-       if (SB_BDEV(sb)) {
-               SET_BDEV_WRITEBACK(sb, writeback);
-
-               if (data_offset != BDEV_DATA_START) {
-                       sb->version = BCACHE_SB_BDEV_VERSION;
-                       sb->keys = 1;
-                       sb->d[0] = data_offset;
-               }
-       } else {
-               SET_CACHE_DISCARD(sb, discard);
-               SET_CACHE_REPLACEMENT(sb, cache_replacement_policy);
-       }
-
-       sb->offset              = SB_SECTOR;
-       memcpy(sb->magic, bcache_magic, 16);
-       sb->nbuckets            = getblocks(fd) / sb->bucket_size;
-       sb->nr_in_set           = 1;
-       sb->first_bucket        = (23 / sb->bucket_size) + 1;
-       uuid_unparse(sb->uuid, uuid);
-       uuid_unparse(sb->set_uuid, set_uuid);
-       sb->csum = csum_set(sb);
-
-       if (sb->nbuckets < 1 << 7) {
-               printf("Not enough buckets: %ju, need %u\n",
-                      sb->nbuckets, 1 << 7);
-               exit(EXIT_FAILURE);
-       }
-
-       printf("UUID:                   %s\n"
-              "Set UUID:               %s\n"
-              "nbuckets:               %ju\n"
-              "block_size:             %u\n"
-              "bucket_size:            %u\n"
-              "nr_in_set:              %u\n"
-              "nr_this_dev:            %u\n"
-              "first_bucket:           %u\n",
-              uuid, set_uuid,
-              sb->nbuckets,
-              sb->block_size,
-              sb->bucket_size,
-              sb->nr_in_set,
-              sb->nr_this_dev,
-              sb->first_bucket);
-
-       if (pwrite(fd, sb, sizeof(*sb), SB_SECTOR << 9) != sizeof(*sb)) {
-               perror("write error\n");
-               exit(EXIT_FAILURE);
-       }
-
-       fsync(fd);
-       close(fd);
-
-       uuid_generate(sb->uuid);
-}
-
 int main(int argc, char **argv)
 {
-       bool written = false;
-       int c;
-       struct cache_sb sb;
-
-       memset(&sb, 0, sizeof(struct cache_sb));
-       sb.version = -1;
-       sb.block_size = 1;
-       sb.bucket_size = 1024;
-
-       uuid_generate(sb.uuid);
-       uuid_generate(sb.set_uuid);
+       int c, bdev = -1;
+       size_t i, nr_backing_devices = 0;
+
+       unsigned block_size = 0;
+       unsigned bucket_sizes[argc];
+       int num_bucket_sizes = 0;
+       int writeback = 0, discard = 0, wipe_bcache = 0;
+       unsigned replication_set = 0, tier = 0, replacement_policy = 0;
+       uint64_t data_offset = BDEV_DATA_START_DEFAULT;
+       char *label = NULL;
+
+       const char *cache_devices[argc];
+       int cache_dev_fd[argc];
+
+       const char *backing_devices[argc];
+       int backing_dev_fd[argc];
+       const char *backing_dev_labels[argc];
+
+       enum long_opts {
+               CACHE_SET_UUID = 256,
+               CSUM_TYPE,
+               REPLICATION_SET,
+               META_REPLICAS,
+               DATA_REPLICAS,
+       };
+
+       const struct option opts[] = {
+               { "cache",                      0, NULL,        'C' },
+               { "bdev",                       0, NULL,        'B' },
+               { "wipe-bcache",                0, &wipe_bcache, 1  },
+               { "label",                      1, NULL,        'l' },
+               { "cset-uuid",                  1, NULL,        CACHE_SET_UUID },
+               { "csum-type",                  1, NULL,        CSUM_TYPE },
+
+               { "bucket",                     1, NULL,        'b' },
+               { "block",                      1, NULL,        'w' },
+
+               { "replication-set",            1, NULL,        REPLICATION_SET },
+               { "meta-replicas",              1, NULL,        META_REPLICAS},
+               { "data-replicas",              1, NULL,        DATA_REPLICAS },
+               { "tier",                       1, NULL,        't' },
+               { "cache_replacement_policy",   1, NULL,        'p' },
+               { "discard",                    0, &discard,    1   },
+
+               { "writeback",                  0, &writeback,  1   },
+               { "data_offset",                1, NULL,        'o' },
+
+               { "help",                       0, NULL,        'h' },
+               { NULL,                         0, NULL,        0 },
+       };
+
+       struct cache_sb *cache_set_sb = calloc(1, sizeof(*cache_set_sb) +
+                                    sizeof(struct cache_member) * argc);
+
+       uuid_generate(cache_set_sb->set_uuid.b);
+       SET_CACHE_PREFERRED_CSUM_TYPE(cache_set_sb, BCH_CSUM_CRC32C);
+       SET_CACHE_SET_META_REPLICAS_WANT(cache_set_sb, 1);
+       SET_CACHE_SET_DATA_REPLICAS_WANT(cache_set_sb, 1);
+       bucket_sizes[0] = 1024;
 
        while ((c = getopt_long(argc, argv,
-                               "-hCBU:w:b:",
-                               opts, NULL)) != -1)
+                               "-hCBU:w:b:l:",
+                               opts, NULL)) != -1) {
+
                switch (c) {
                case 'C':
-                       sb.version = 0;
+                       bdev = 0;
                        break;
                case 'B':
-                       sb.version = CACHE_BACKING_DEV;
+                       bdev = 1;
+                       break;
+               case 'l':
+                       label = optarg;
+                       memcpy(cache_set_sb->label, label,
+                              sizeof(cache_set_sb->label));
+                       break;
+               case CACHE_SET_UUID:
+                       if (uuid_parse(optarg, cache_set_sb->set_uuid.b)) {
+                               fprintf(stderr, "Bad uuid\n");
+                               exit(EXIT_FAILURE);
+                       }
                        break;
+               case CSUM_TYPE:
+                       SET_CACHE_PREFERRED_CSUM_TYPE(cache_set_sb,
+                               read_string_list_or_die(optarg, csum_types,
+                                                       "csum type"));
+                       break;
+
                case 'b':
-                       sb.bucket_size = hatoi_validate(optarg, "bucket size");
+                       bucket_sizes[num_bucket_sizes] = hatoi_validate(optarg, "bucket size");
+                       num_bucket_sizes++;
                        break;
                case 'w':
-                       sb.block_size = hatoi_validate(optarg, "block size");
+                       block_size = hatoi_validate(optarg, "block size");
                        break;
-               case 'U':
-                       if (uuid_parse(optarg, sb.uuid)) {
-                               printf("Bad uuid\n");
-                               exit(EXIT_FAILURE);
-                       }
+
+               case REPLICATION_SET:
+                       replication_set = strtoul_or_die(optarg,
+                                                        CACHE_REPLICATION_SET_MAX,
+                                                        "replication set");
+                       break;
+               case META_REPLICAS:
+                       SET_CACHE_SET_META_REPLICAS_WANT(cache_set_sb,
+                                       strtoul_or_die(optarg, 
+                                                      CACHE_SET_META_REPLICAS_WANT_MAX,
+                                                      "meta replicas"));
+                       break;
+               case DATA_REPLICAS:
+                       SET_CACHE_SET_DATA_REPLICAS_WANT(cache_set_sb,
+                                       strtoul_or_die(optarg, 
+                                                      CACHE_SET_DATA_REPLICAS_WANT_MAX,
+                                                      "data replicas"));
+                       break;
+               case 't':
+                       tier = strtoul_or_die(optarg, CACHE_TIERS, "tier");
                        break;
                case 'p':
-                       cache_replacement_policy = read_string_list(optarg,
-                                                   cache_replacement_policies);
+                       replacement_policy = read_string_list_or_die(optarg,
+                                                       replacement_policies,
+                                                       "cache replacement policy");
                        break;
+
                case 'o':
                        data_offset = atoll(optarg);
-                       if (sb.d[0] < BDEV_DATA_START) {
-                               printf("Bad data offset; minimum %d sectors\n", BDEV_DATA_START);
+                       if (data_offset < BDEV_DATA_START_DEFAULT) {
+                               fprintf(stderr, "Bad data offset; minimum %d sectors\n",
+                                      BDEV_DATA_START_DEFAULT);
                                exit(EXIT_FAILURE);
                        }
                        break;
@@ -297,15 +190,70 @@ int main(int argc, char **argv)
                        usage();
                        break;
                case 1:
-                       write_sb(optarg, &sb);
-                       written = true;
+                       if (bdev == -1) {
+                               fprintf(stderr, "Please specify -C or -B\n");
+                               exit(EXIT_FAILURE);
+                       }
+
+                       if (bdev) {
+                               backing_dev_labels[nr_backing_devices] = label;
+                               backing_devices[nr_backing_devices++] = optarg;
+                       } else {
+                               cache_devices[cache_set_sb->nr_in_set] = optarg;
+                               next_cache_device(cache_set_sb,
+                                                 replication_set,
+                                                 tier,
+                                                 replacement_policy,
+                                                 discard);
+                       }
+
                        break;
                }
+       }
 
-       if (!written) {
-               printf("Please supply a device\n");
+       if (!cache_set_sb->nr_in_set && !nr_backing_devices) {
+               fprintf(stderr, "Please supply a device\n");
                usage();
        }
 
+       i = 0;
+       do {
+               if (bucket_sizes[i] < block_size) {
+                       fprintf(stderr, "Bucket size cannot be smaller than block size\n");
+                       exit(EXIT_FAILURE);
+               }
+               i++;
+       } while (i < num_bucket_sizes);
+
+       if (!block_size) {
+               for (i = 0; i < cache_set_sb->nr_in_set; i++)
+                       block_size = max(block_size,
+                                        get_blocksize(cache_devices[i]));
+
+               for (i = 0; i < nr_backing_devices; i++)
+                       block_size = max(block_size,
+                                        get_blocksize(backing_devices[i]));
+       }
+
+       for (i = 0; i < cache_set_sb->nr_in_set; i++)
+               cache_dev_fd[i] = dev_open(cache_devices[i], wipe_bcache);
+
+       for (i = 0; i < nr_backing_devices; i++)
+               backing_dev_fd[i] = dev_open(backing_devices[i], wipe_bcache);
+
+       write_cache_sbs(cache_dev_fd, cache_set_sb, block_size,
+                                       bucket_sizes, num_bucket_sizes);
+
+       for (i = 0; i < nr_backing_devices; i++)
+               write_backingdev_sb(backing_dev_fd[i],
+                                   block_size, bucket_sizes,
+                                   writeback
+                                       ? CACHE_MODE_WRITEBACK
+                                       : CACHE_MODE_WRITETHROUGH,
+                                   data_offset,
+                                   backing_dev_labels[i],
+                                   cache_set_sb->user_uuid,
+                                   cache_set_sb->set_uuid);
+
        return 0;
 }