+/*
+ * 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;
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;
}