X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=bcache-format.c;h=a7aabc3b1dccd238c68ed850df78c06d7ebc010d;hb=379520ee87b92c51f1db1e5c19379d738a7c207a;hp=9b4d02e5d3abcd072d0d3b0ddfb3b555a4fd47d9;hpb=009d6db7b01462a264a8fb15477611bc2d8674eb;p=bcachefs-tools-debian diff --git a/bcache-format.c b/bcache-format.c index 9b4d02e..a7aabc3 100644 --- a/bcache-format.c +++ b/bcache-format.c @@ -5,532 +5,261 @@ * * GPLv2 */ - #include +#include +#include #include #include #include #include #include +#include #include #include +#include #include -#include -#include - -#include "ccan/ilog/ilog.h" #include "ccan/darray/darray.h" -#include "bcache.h" -#include "bcache-format.h" - -struct cache_opts { - int fd; - const char *dev; - unsigned bucket_size; - unsigned tier; - unsigned replacement_policy; - unsigned replication_set; - u64 size; /* 512 byte sectors */ - - u64 first_bucket; - u64 nbuckets; -}; - -struct backingdev_opts { - int fd; - const char *dev; - const char *label; -}; - -static darray(struct cache_opts) cache_devices; -static darray(struct backingdev_opts) backing_devices; - -static char *label = NULL; - -/* All in units of 512 byte sectors */ -static unsigned block_size, bucket_size, btree_node_size; -static u64 filesystem_size; -static unsigned tier, replacement_policy; - -static uuid_le set_uuid, user_uuid; -static unsigned meta_csum_type = BCH_CSUM_CRC32C; -static unsigned data_csum_type = BCH_CSUM_CRC32C; -static unsigned compression_type = BCH_COMPRESSION_NONE; - -static unsigned replication_set, meta_replicas = 1, data_replicas = 1; -static unsigned on_error_action; -static int discard; -static unsigned version = 1; - -static u64 data_offset = BDEV_DATA_START_DEFAULT; -static unsigned cache_mode = CACHE_MODE_WRITEBACK; - -static int set_cache(NihOption *option, const char *arg) -{ - darray_append(cache_devices, (struct cache_opts) { - .fd = dev_open(arg), - .dev = strdup(arg), - .bucket_size = bucket_size, - .tier = tier, - .replacement_policy = replacement_policy, - .replication_set = replication_set, - .size = filesystem_size, - }); - return 0; -} - -static int set_bdev(NihOption *option, const char *arg) -{ - darray_append(backing_devices, (struct backingdev_opts) { - .fd = dev_open(arg), - .dev = strdup(arg), - .label = label ? strdup(label) : NULL, - }); - return 0; -} - -static int set_cache_set_uuid(NihOption *option, const char *arg) -{ - if (uuid_parse(arg, user_uuid.b)) - die("Bad uuid"); - return 0; -} - -static int set_block_size(NihOption *option, const char *arg) -{ - block_size = hatoi_validate(arg, "block size"); - return 0; -} - -static int set_bucket_sizes(NihOption *option, const char *arg) -{ - bucket_size = hatoi_validate(arg, "bucket size"); - return 0; -} - -static int set_btree_node_size(NihOption *option, const char *arg) -{ - btree_node_size = hatoi_validate(arg, "btree node size"); - return 0; -} - -static int set_filesystem_size(NihOption *option, const char *arg) -{ - filesystem_size = hatoi(arg) >> 9; - return 0; -} - -static int set_replacement_policy(NihOption *option, const char *arg) -{ - replacement_policy = read_string_list_or_die(arg, replacement_policies, - "replacement policy"); - return 0; -} - -static int set_csum_type(NihOption *option, const char *arg) -{ - unsigned *csum_type = option->value; - - *csum_type = read_string_list_or_die(arg, csum_types, "checksum type"); - return 0; -} - -static int set_compression_type(NihOption *option, const char *arg) -{ - compression_type = read_string_list_or_die(arg, compression_types, - "compression type"); - return 0; -} - -static int set_on_error_action(NihOption *option, const char *arg) -{ - on_error_action = read_string_list_or_die(arg, error_actions, - "error action"); - return 0; -} - -static int set_tier(NihOption *option, const char *arg) -{ - tier = strtoul_or_die(arg, CACHE_TIERS, "tier"); - return 0; -} +#include "bcache-cmds.h" +#include "libbcache.h" -static int set_replication_set(NihOption *option, const char *arg) +/* Open a block device, do magic blkid stuff: */ +static int open_for_format(const char *dev, bool force) { - replication_set = strtoul_or_die(arg, CACHE_REPLICATION_SET_MAX, - "replication set"); - return 0; -} - -static int set_meta_replicas(NihOption *option, const char *arg) -{ - meta_replicas = strtoul_or_die(arg, CACHE_SET_META_REPLICAS_WANT_MAX, - "meta_replicas"); - return 0; -} - -static int set_data_replicas(NihOption *option, const char *arg) -{ - data_replicas = strtoul_or_die(arg, CACHE_SET_DATA_REPLICAS_WANT_MAX, - "data_replicas"); - return 0; -} - -static int set_cache_mode(NihOption *option, const char *arg) -{ - cache_mode = read_string_list_or_die(arg, bdev_cache_mode, - "cache mode"); - return 0; -} - -static int set_version(NihOption *option, const char *arg) -{ - version = strtoul_or_die(arg, 2, "version"); - return 0; -} - -NihOption opts_format[] = { -// { int shortoption, char *longoption, char *help, NihOptionGroup, char *argname, void *value, NihOptionSetter} - - { 'C', "cache", N_("Format a cache device"), - NULL, "dev", NULL, set_cache }, - { 'B', "bdev", N_("Format a backing device"), - NULL, "dev", NULL, set_bdev }, - - { 'l', "label", N_("label"), - NULL, "label", &label, NULL}, - { 0, "cset_uuid", N_("UUID for the cache set"), - NULL, "uuid", NULL, set_cache_set_uuid }, - - { 'w', "block", N_("block size (hard sector size of SSD, often 2k"), - NULL, "size", NULL, set_block_size }, - { 'b', "bucket", N_("bucket size"), - NULL, "size", NULL, set_bucket_sizes }, - { 'n', "btree_node", N_("Btree node size, default 256k"), - NULL, "size", NULL, set_btree_node_size }, - { 0, "fs_size", N_("Size of filesystem on device" ), - NULL, "size", NULL, set_filesystem_size }, - - { 'p', "cache_replacement_policy", NULL, - NULL, "(lru|fifo|random)", NULL, set_replacement_policy }, - - { 0, "metadata_csum_type", N_("Checksum type"), - NULL, "(none|crc32c|crc64)", &meta_csum_type, set_csum_type }, - - { 0, "data_csum_type", N_("Checksum type"), - NULL, "(none|crc32c|crc64)", &data_csum_type, set_csum_type }, - - { 0, "compression_type", N_("Compression type"), - NULL, "(none|gzip)", NULL, set_compression_type }, - - { 0, "error_action", N_("Action to take on filesystem error"), - NULL, "(continue|readonly|panic)", NULL, set_on_error_action }, - - { 0, "discard", N_("Enable discards"), - NULL, NULL, &discard, NULL }, - - { 't', "tier", N_("tier of subsequent devices"), - NULL, "#", NULL, set_tier }, - - { 0, "replication_set", N_("replication set of subsequent devices"), - NULL, "#", NULL, set_replication_set }, - - { 0, "meta_replicas", N_("number of metadata replicas"), - NULL, "#", NULL, set_meta_replicas }, - - { 0, "data_replicas", N_("number of data replicas"), - NULL, "#", NULL, set_data_replicas }, - - { 0, "cache_mode", N_("Cache mode (for backing devices)"), - NULL, "(writethrough|writeback|writearound", NULL, set_cache_mode }, - - { 'o', "data_offset", N_("data offset in sectors"), - NULL, "offset", &data_offset, NULL}, - - { 'v', "version", N_("superblock version"), - NULL, "#", NULL, set_version}, - - NIH_OPTION_LAST -}; - -void __do_write_sb(int fd, void *sb, size_t bytes) -{ - char zeroes[SB_SECTOR << 9] = {0}; - - /* Zero start of disk */ - if (pwrite(fd, zeroes, SB_SECTOR << 9, 0) != SB_SECTOR << 9) { - perror("write error trying to zero start of disk\n"); - exit(EXIT_FAILURE); - } - /* Write superblock */ - if (pwrite(fd, sb, bytes, SB_SECTOR << 9) != bytes) { - perror("write error trying to write superblock\n"); - exit(EXIT_FAILURE); + blkid_probe pr; + const char *fs_type = NULL, *fs_label = NULL; + size_t fs_type_len, fs_label_len; + int fd; + + if ((fd = open(dev, O_RDWR|O_EXCL)) == -1) + die("Can't open dev %s: %s\n", dev, strerror(errno)); + + if (force) + return fd; + + if (!(pr = blkid_new_probe())) + die("blkid error 1"); + if (blkid_probe_set_device(pr, fd, 0, 0)) + die("blkid error 2"); + if (blkid_probe_enable_partitions(pr, true)) + die("blkid error 3"); + if (blkid_do_fullprobe(pr) < 0) + die("blkid error 4"); + + blkid_probe_lookup_value(pr, "TYPE", &fs_type, &fs_type_len); + blkid_probe_lookup_value(pr, "LABEL", &fs_label, &fs_label_len); + + if (fs_type) { + if (fs_label) + printf("%s contains a %s filesystem labelled '%s'\n", + dev, fs_type, fs_label); + else + printf("%s contains a %s filesystem\n", + dev, fs_type); + if (!ask_proceed()) + exit(EXIT_FAILURE); } - fsync(fd); - close(fd); + blkid_free_probe(pr); + return fd; } -#define do_write_sb(_fd, _sb) \ - __do_write_sb(_fd, _sb, ((void *) __bset_bkey_last(_sb)) - (void *) _sb); - -void write_backingdev_sb(int fd, unsigned block_size, unsigned mode, - u64 data_offset, const char *label, - uuid_le set_uuid) +static void usage(void) { - char uuid_str[40]; - struct cache_sb sb; - - memset(&sb, 0, sizeof(struct cache_sb)); - - sb.offset = SB_SECTOR; - sb.version = BCACHE_SB_VERSION_BDEV; - sb.magic = BCACHE_MAGIC; - uuid_generate(sb.disk_uuid.b); - sb.set_uuid = set_uuid; - sb.block_size = block_size; - - uuid_unparse(sb.disk_uuid.b, uuid_str); - if (label) - memcpy(sb.label, label, SB_LABEL_SIZE); - - SET_BDEV_CACHE_MODE(&sb, mode); - - if (data_offset != BDEV_DATA_START_DEFAULT) { - sb.version = BCACHE_SB_VERSION_BDEV_WITH_OFFSET; - sb.bdev_data_offset = data_offset; - } - - sb.csum = csum_set(&sb, BCH_CSUM_CRC64); - - printf("UUID: %s\n" - "version: %u\n" - "block_size: %u\n" - "data_offset: %llu\n", - uuid_str, (unsigned) sb.version, - sb.block_size, data_offset); - - do_write_sb(fd, &sb); + puts("bcache format - create a new bcache filesystem on one or more devices\n" + "Usage: bcache format [OPTION]... \n" + "\n" + "Options:\n" + " -b, --block=size\n" + " --btree_node=size Btree node size, default 256k\n" + " --metadata_checksum_type=(none|crc32c|crc64)\n" + " --data_checksum_type=(none|crc32c|crc64)\n" + " --compression_type=(none|lz4|gzip)\n" + " --error_action=(continue|readonly|panic)\n" + " Action to take on filesystem error\n" + " --max_journal_entry_size=size\n" + " -l, --label=label\n" + " --uuid=uuid\n" + " -f, --force\n" + "\n" + "Device specific options:\n" + " --fs_size=size Size of filesystem on device\n" + " --bucket=size bucket size\n" + " --discard Enable discards\n" + " -t, --tier=# tier of subsequent devices\n" + "\n" + " -h, --help display this help and exit\n" + "\n" + "Device specific options must come before corresponding devices, e.g.\n" + " bcache format --tier 0 /dev/sdb --tier 1 /dev/sdc\n" + "\n" + "Report bugs to "); + exit(EXIT_SUCCESS); } -static void format_v0(void) -{ - struct cache_opts *i; - - set_uuid = user_uuid; - - darray_foreach(i, cache_devices) - bucket_size = min(bucket_size, i->bucket_size); - - struct cache_sb_v0 *sb = calloc(1, sizeof(*sb)); - - sb->offset = SB_SECTOR; - sb->version = BCACHE_SB_VERSION_CDEV_WITH_UUID; - sb->magic = BCACHE_MAGIC; - sb->block_size = block_size; - sb->bucket_size = bucket_size; - sb->set_uuid = set_uuid; - sb->nr_in_set = darray_size(cache_devices); - - if (label) - memcpy(sb->label, label, sizeof(sb->label)); - - darray_foreach(i, cache_devices) { - char uuid_str[40], set_uuid_str[40]; - - uuid_generate(sb->uuid.b); - sb->nbuckets = i->nbuckets; - sb->first_bucket = i->first_bucket; - sb->nr_this_dev = i - cache_devices.item; - sb->csum = csum_set(sb, BCH_CSUM_CRC64); - - uuid_unparse(sb->uuid.b, uuid_str); - uuid_unparse(sb->set_uuid.b, set_uuid_str); - printf("UUID: %s\n" - "Set UUID: %s\n" - "version: %u\n" - "nbuckets: %llu\n" - "block_size: %u\n" - "bucket_size: %u\n" - "nr_in_set: %u\n" - "nr_this_dev: %u\n" - "first_bucket: %u\n", - uuid_str, set_uuid_str, - (unsigned) sb->version, - sb->nbuckets, - sb->block_size, - sb->bucket_size, - sb->nr_in_set, - sb->nr_this_dev, - sb->first_bucket); - - do_write_sb(i->fd, sb); - } -} - -static void format_v1(void) -{ - struct cache_sb *sb; - struct cache_opts *i; - - sb = calloc(1, sizeof(*sb) + sizeof(struct cache_member) * - darray_size(cache_devices)); - - sb->offset = SB_SECTOR; - sb->version = BCACHE_SB_VERSION_CDEV_V3; - sb->magic = BCACHE_MAGIC; - sb->block_size = block_size; - sb->set_uuid = set_uuid; - sb->user_uuid = user_uuid; - sb->nr_in_set = darray_size(cache_devices); - - if (label) - memcpy(sb->label, label, sizeof(sb->label)); - - /* - * don't have a userspace crc32c implementation handy, just always use - * crc64 - */ - SET_CACHE_SB_CSUM_TYPE(sb, BCH_CSUM_CRC64); - SET_CACHE_META_PREFERRED_CSUM_TYPE(sb, meta_csum_type); - SET_CACHE_DATA_PREFERRED_CSUM_TYPE(sb, data_csum_type); - SET_CACHE_COMPRESSION_TYPE(sb, compression_type); - - SET_CACHE_BTREE_NODE_SIZE(sb, btree_node_size); - SET_CACHE_SET_META_REPLICAS_WANT(sb, meta_replicas); - SET_CACHE_SET_META_REPLICAS_HAVE(sb, meta_replicas); - SET_CACHE_SET_DATA_REPLICAS_WANT(sb, data_replicas); - SET_CACHE_SET_DATA_REPLICAS_HAVE(sb, data_replicas); - SET_CACHE_ERROR_ACTION(sb, on_error_action); - - darray_foreach(i, cache_devices) { - struct cache_member *m = sb->members + - (i - cache_devices.item); - - uuid_generate(m->uuid.b); - m->nbuckets = i->nbuckets; - m->first_bucket = i->first_bucket; - m->bucket_size = i->bucket_size; - - if (m->nbuckets < 1 << 7) - die("Not enough buckets: %llu, need %u", - m->nbuckets, 1 << 7); - - SET_CACHE_TIER(m, i->tier); - SET_CACHE_REPLICATION_SET(m, i->replication_set); - SET_CACHE_REPLACEMENT(m, i->replacement_policy); - SET_CACHE_DISCARD(m, discard); - } - - sb->u64s = bch_journal_buckets_offset(sb); - - darray_foreach(i, cache_devices) { - char uuid_str[40], set_uuid_str[40]; - struct cache_member *m = sb->members + - (i - cache_devices.item); - - sb->disk_uuid = m->uuid; - sb->nr_this_dev = i - cache_devices.item; - sb->csum = csum_set(sb, CACHE_SB_CSUM_TYPE(sb)); - - uuid_unparse(sb->disk_uuid.b, uuid_str); - uuid_unparse(sb->user_uuid.b, set_uuid_str); - printf("UUID: %s\n" - "Set UUID: %s\n" - "version: %u\n" - "nbuckets: %llu\n" - "block_size: %u\n" - "bucket_size: %u\n" - "nr_in_set: %u\n" - "nr_this_dev: %u\n" - "first_bucket: %u\n", - uuid_str, set_uuid_str, - (unsigned) sb->version, - m->nbuckets, - sb->block_size, - m->bucket_size, - sb->nr_in_set, - sb->nr_this_dev, - m->first_bucket); +#define OPTS \ + OPT('b', block_size, required_argument) \ + OPT(0, btree_node_size, required_argument) \ + OPT(0, metadata_checksum_type, required_argument) \ + OPT(0, data_checksum_type, required_argument) \ + OPT(0, compression_type, required_argument) \ + OPT('e', error_action, required_argument) \ + OPT(0, max_journal_entry_size, required_argument) \ + OPT('L', label, required_argument) \ + OPT('U', uuid, required_argument) \ + OPT('f', force, no_argument) \ + OPT(0, fs_size, required_argument) \ + OPT(0, bucket_size, required_argument) \ + OPT('t', tier, required_argument) \ + OPT(0, discard, no_argument) \ + OPT('h', help, no_argument) + +enum { + Opt_no_opt = 1, +#define OPT(shortopt, longopt, has_arg) Opt_##longopt, + OPTS +#undef OPT +}; - do_write_sb(i->fd, sb); - } -} +static const struct option format_opts[] = { +#define OPT(shortopt, longopt, has_arg) { \ + #longopt, has_arg, NULL, Opt_##longopt \ + }, + OPTS +#undef OPT + { NULL } +}; -int cmd_format(NihCommand *command, char * const *args) +int cmd_format(int argc, char *argv[]) { - struct cache_opts *i; - struct backingdev_opts *ib; - - if (!darray_size(cache_devices) && - !darray_size(backing_devices)) - die("Please supply a device"); - - if (uuid_is_null(user_uuid.b)) - uuid_generate(user_uuid.b); - - uuid_generate(set_uuid.b); - - if (!block_size) { - darray_foreach(i, cache_devices) - block_size = max(block_size, - get_blocksize(i->dev, i->fd)); - - darray_foreach(ib, backing_devices) - block_size = max(block_size, - get_blocksize(ib->dev, ib->fd)); - } - - darray_foreach(i, cache_devices) { - if (!i->size) - i->size = get_size(i->dev, i->fd); - - if (!i->bucket_size) { - u64 bytes = i->size << 9; - - if (bytes < 1 << 20) /* 1M device - 256 4k buckets*/ - i->bucket_size = rounddown_pow_of_two(bytes >> 17); - else - /* Max 1M bucket at around 256G */ - i->bucket_size = 8 << min((ilog2(bytes >> 20) / 2), 9U); + darray(struct dev_opts) devices; + struct dev_opts *dev; + unsigned block_size = 0; + unsigned btree_node_size = 0; + unsigned meta_csum_type = BCH_CSUM_CRC32C; + unsigned data_csum_type = BCH_CSUM_CRC32C; + unsigned compression_type = BCH_COMPRESSION_NONE; + unsigned on_error_action = BCH_ON_ERROR_RO; + char *label = NULL; + uuid_le uuid; + bool force = false; + + /* Device specific options: */ + u64 filesystem_size = 0; + unsigned bucket_size = 0; + unsigned tier = 0; + bool discard = false; + unsigned max_journal_entry_size = 0; + char *passphrase = NULL; + int opt; + + darray_init(devices); + uuid_clear(uuid.b); + + while ((opt = getopt_long(argc, argv, + "-b:e:L:U:ft:h", + format_opts, + NULL)) != -1) + switch (opt) { + case Opt_block_size: + case 'b': + block_size = hatoi_validate(optarg, + "block size"); + break; + case Opt_btree_node_size: + btree_node_size = hatoi_validate(optarg, + "btree node size"); + break; + case Opt_metadata_checksum_type: + meta_csum_type = read_string_list_or_die(optarg, + csum_types, "checksum type"); + break; + case Opt_data_checksum_type: + data_csum_type = read_string_list_or_die(optarg, + csum_types, "checksum type"); + break; + case Opt_compression_type: + compression_type = read_string_list_or_die(optarg, + compression_types, "compression type"); + break; + case Opt_error_action: + case 'e': + on_error_action = read_string_list_or_die(optarg, + error_actions, "error action"); + break; + case Opt_max_journal_entry_size: + max_journal_entry_size = hatoi_validate(optarg, + "journal entry size"); + break; + case Opt_label: + case 'L': + label = strdup(optarg); + break; + case Opt_uuid: + case 'U': + if (uuid_parse(optarg, uuid.b)) + die("Bad uuid"); + break; + case Opt_force: + case 'f': + force = true; + break; + case Opt_fs_size: + filesystem_size = hatoi(optarg) >> 9; + break; + case Opt_bucket_size: + bucket_size = hatoi_validate(optarg, "bucket size"); + break; + case Opt_tier: + case 't': + tier = strtoul_or_die(optarg, CACHE_TIERS, "tier"); + break; + case Opt_discard: + discard = true; + break; + case Opt_no_opt: + darray_append(devices, (struct dev_opts) { + .path = strdup(optarg), + .size = filesystem_size, + .bucket_size = bucket_size, + .tier = tier, + .discard = discard, + }); + break; + case Opt_help: + case 'h': + usage(); + break; } - if (i->bucket_size < block_size) - die("Bucket size cannot be smaller than block size"); - - i->nbuckets = i->size / i->bucket_size; - i->first_bucket = (23 / i->bucket_size) + 3; - - if (i->nbuckets < 1 << 7) - die("Not enough buckets: %llu, need %u", - i->nbuckets, 1 << 7); - } - - if (!btree_node_size) { - /* 256k default btree node size */ - btree_node_size = 512; - - darray_foreach(i, cache_devices) - btree_node_size = min(btree_node_size, i->bucket_size); - } + if (!darray_size(devices)) + die("Please supply a device"); - switch (version) { - case 0: - format_v0(); - break; - case 1: - format_v1(); - break; + if (uuid_is_null(uuid.b)) + uuid_generate(uuid.b); + + darray_foreach(dev, devices) + dev->fd = open_for_format(dev->path, force); + + bcache_format(devices.item, darray_size(devices), + block_size, + btree_node_size, + meta_csum_type, + data_csum_type, + compression_type, + 1, + 1, + on_error_action, + max_journal_entry_size, + label, + uuid); + + if (passphrase) { + memzero_explicit(passphrase, strlen(passphrase)); + free(passphrase); } - darray_foreach(ib, backing_devices) - write_backingdev_sb(ib->fd, block_size, cache_mode, - data_offset, ib->label, - set_uuid); - return 0; }