From 82ac2a0338b17fc41d8c7673cfd79dbf9ad598ff Mon Sep 17 00:00:00 2001 From: Jacob Malevich Date: Fri, 26 Sep 2014 16:10:20 -0700 Subject: [PATCH] bcacheadm tool bcacheadm is a combination of make-bcache, probe-bcache, bcache-super-show, and bcachectl(register). Change-Id: Ia4f4cdd5f882516358eb0e26a75296d2103af1d0 Signed-off-by: Jacob Malevich --- Makefile.am | 8 +- bcache-tools.spec | 2 +- bcacheadm.c | 403 ++++++++++++++++++++++++++++++++++++++++++++++ configure.ac | 1 - 4 files changed, 411 insertions(+), 3 deletions(-) create mode 100644 bcacheadm.c diff --git a/Makefile.am b/Makefile.am index 06631cbf..1cc47d3a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -9,7 +9,8 @@ AM_LDFLAGS=`pkg-config --libs uuid blkid` -L$(top_builddir) bin_PROGRAMS=make-bcache \ probe-bcache \ bcache-super-show \ - bcachectl + bcachectl \ + bcacheadm noinst_PROGRAMS=bcache-test @@ -32,6 +33,11 @@ bcache_test_SOURCES=bcache-test.c bcache_test_LDFLAGS=-lm `pkg-config --libs openssl` bcache_test_CFLAGS=$(AM_CFLAGS) `pkg-config --cflags openssl` +bcacheadm_SOURCES=bcacheadm.c +bcacheadm_LDFLAGS=$(AM_LDFLAGS) -lnih +bcacheadm_LDADD=libbcache.a + + udevrule_DATA=69-bcache.rules udevruledir=$(prefix)/lib/udev/rules.d diff --git a/bcache-tools.spec b/bcache-tools.spec index 9b04d8b9..77815c2b 100644 --- a/bcache-tools.spec +++ b/bcache-tools.spec @@ -7,7 +7,7 @@ License: GPL Group: tools BuildRoot: %{_tmppath}/%{name}-root Requires: libblkid -BuildRequires: pkgconfig libblkid-devel linux-headers +BuildRequires: pkgconfig libblkid-devel linux-headers libnih-devel %package dev diff --git a/bcacheadm.c b/bcacheadm.c new file mode 100644 index 00000000..ecfc650c --- /dev/null +++ b/bcacheadm.c @@ -0,0 +1,403 @@ +/* + * Authors: Kent Overstreet + * Gabriel de Perthuis + * Jacob Malevich + * + * GPLv2 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include //libbcache +#define __KERNEL__ +#include +#undef __KERNEL__ + +#define PACKAGE_NAME "bcacheadm" +#define PACKAGE_VERSION "1.0" +#define PACKAGE_BUGREPORT "bugreport" + +//What is the actual max? +#define MAX_DEVS 64 + + + +/* bcacheadm globals */ +enum exit { + EXIT_OK = 0, /* Ok */ + EXIT_ERROR = 1, /* General/OS error */ + EXIT_SHELL = 2, /* Start maintenance shell */ + EXIT_SHELL_REBOOT = 3, /* Start maintenance shell, reboot when done */ + EXIT_REBOOT = 4, /* System must reboot */ +}; + + +/* make-bcache globals */ +int bdev = -1; +int devs = 0; +const char *cache_devices[MAX_DEVS]; +const char *backing_devices[MAX_DEVS]; +const char *backing_dev_labels[MAX_DEVS]; +size_t i, nr_backing_devices = 0; +unsigned block_size = 0; +unsigned bucket_sizes[MAX_DEVS]; +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; +struct cache_sb *cache_set_sb; +enum long_opts { + CACHE_SET_UUID = 256, + CSUM_TYPE, + REPLICATION_SET, + META_REPLICAS, + DATA_REPLICAS, +}; + + +/* super-show globals */ +bool force_csum = false; +char *show_dev = NULL; + + +/* probe globals */ +bool udev = false; + +/* register globals */ +int bcachefd; + +/* make-bcache option setters */ +static int set_CACHE_SET_UUID(NihOption *option, const char *arg) +{ + if(uuid_parse(arg, cache_set_sb->set_uuid.b)){ + fprintf(stderr, "Bad uuid\n"); + return -1; + } + return 0; +} +static int set_CSUM_TYPE(NihOption *option, const char *arg) +{ + SET_CACHE_PREFERRED_CSUM_TYPE(cache_set_sb, + read_string_list_or_die(arg, csum_types, + "csum type")); + return 0; +} +static int set_REPLICATION_SET(NihOption *option, const char *arg) +{ + replication_set = strtoul_or_die(arg, + CACHE_REPLICATION_SET_MAX, + "replication set"); + return 0; +} +static int set_META_REPLICAS(NihOption *option, const char *arg) +{ + SET_CACHE_SET_META_REPLICAS_WANT(cache_set_sb, + strtoul_or_die(arg, + CACHE_SET_META_REPLICAS_WANT_MAX, + "meta replicas")); + return 0; +} +static int set_DATA_REPLICAS(NihOption *option, const char *arg) +{ + SET_CACHE_SET_DATA_REPLICAS_WANT(cache_set_sb, + strtoul_or_die(arg, + CACHE_SET_DATA_REPLICAS_WANT_MAX, + "data replicas")); + return 0; +} +static int set_cache(NihOption *option, const char *arg) +{ + bdev=0; + cache_devices[cache_set_sb->nr_in_set] = arg; + next_cache_device(cache_set_sb, + replication_set, + tier, + replacement_policy, + discard); + devs++; + return 0; +} +static int set_bdev(NihOption *option, const char *arg) +{ + bdev=1; + backing_dev_labels[nr_backing_devices]=label; + backing_devices[nr_backing_devices++]=arg; + devs++; + return 0; +} +static int set_bucket_sizes(NihOption *option, const char *arg) +{ + bucket_sizes[num_bucket_sizes]=hatoi_validate(arg, "bucket size"); + num_bucket_sizes++; + return 0; +} + + +/* probe setters */ +static int set_udev(NihOption *option, const char *arg) +{ + if (strcmp("udev", arg)) { + printf("Invalid output format %s\n", arg); + exit(EXIT_FAILURE); + } + udev = true; + return 0; +} + + +/* options */ +static NihOption make_bcache_options[] = { +// {int shortoption, char* longoption, char* help, NihOptionGroup, char* argname, void *value, NihOptionSetter} + {'C', "cache", N_("Format a cache device"), NULL, NULL, NULL, set_cache}, + {'B', "bdev", N_("Format a backing device"), NULL, NULL, NULL, set_bdev}, + {'l', "label", N_("label"), NULL, NULL, &label, NULL}, + //Only one bucket_size supported until a list of bucket sizes is parsed correctly + {'b', "bucket", N_("bucket size"), NULL, NULL, NULL, set_bucket_sizes}, + //Does the default setter automatically convert strings to an int? + {'w', "block", N_("block size (hard sector size of SSD, often 2k"), NULL,NULL, &block_size, NULL}, + {'t', "tier", N_("tier of subsequent devices"), NULL,NULL, &tier, NULL}, + {'p', "cache_replacement_policy", N_("one of (lru|fifo|random)"), NULL,NULL, &replacement_policy, NULL}, + {'o', "data_offset", N_("data offset in sectors"), NULL,NULL, &data_offset, NULL}, + {'h', "help", N_("display this help and exit"), NULL,NULL, NULL, NULL}, + + {0, "cset-uuid", N_("UUID for the cache set"), NULL, NULL, NULL, set_CACHE_SET_UUID }, + {0, "csum-type", N_("One of (none|crc32c|crc64)"), NULL, NULL, NULL, set_CSUM_TYPE }, + {0, "replication-set",N_("replication set of subsequent devices"), NULL, NULL, NULL, set_REPLICATION_SET }, + {0, "meta-replicas",N_("number of metadata replicas"), NULL, NULL, NULL, set_META_REPLICAS}, + {0, "data-replicas",N_("number of data replicas"), NULL, NULL, NULL, set_DATA_REPLICAS }, + + {0, "wipe-bcache", N_("destroy existing bcache data if present"), NULL, NULL, &wipe_bcache, NULL}, + {0, "discard", N_("enable discards"), NULL, NULL, &discard, NULL}, + {0, "writeback", N_("enable writeback"), NULL, NULL, &writeback, NULL}, + + NIH_OPTION_LAST +}; + +static NihOption super_show_options[] = { + {'f', "force_csum", N_("force_csum"), NULL, NULL, &force_csum, NULL}, + {0, "dev", N_("dev"), NULL, NULL, &show_dev, NULL}, + NIH_OPTION_LAST + +}; + +static NihOption probe_bcache_options[] = { + {'o', "udev", N_("udev"), NULL, NULL, NULL, set_udev}, + NIH_OPTION_LAST +}; + +static NihOption bcache_register_options[] = { + NIH_OPTION_LAST +}; + +static NihOption options[] = { + NIH_OPTION_LAST +}; + + +/* commands */ +int make_bcache (NihCommand *command, char *const *args) +{ + int cache_dev_fd[devs]; + + int backing_dev_fd[devs]; + + cache_set_sb = calloc(1, sizeof(*cache_set_sb) + + sizeof(struct cache_member) * devs); + + 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); + + if(!bucket_sizes[0]) bucket_sizes[0] = 1024; + + if (bdev == -1) { + fprintf(stderr, "Please specify -C or -B\n"); + exit(EXIT_FAILURE); + } + + if (!cache_set_sb->nr_in_set && !nr_backing_devices) { + fprintf(stderr, "Please supply a device\n"); + exit(EXIT_FAILURE); + } + + 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, data_offset, + backing_dev_labels[i], + cache_set_sb->set_uuid); + + + return 0; +} + +int super_show (NihCommand *command, char *const *args) +{ + struct cache_sb sb_stack, *sb = &sb_stack; + size_t bytes = sizeof(*sb); + + int fd = open(show_dev, O_RDONLY); + if (fd < 0) { + printf("Can't open dev %s: %s\n", show_dev, strerror(errno)); + exit(2); + } + + if (pread(fd, sb, bytes, SB_START) != bytes) { + fprintf(stderr, "Couldn't read\n"); + exit(2); + } + + if (sb->keys) { + bytes = sizeof(*sb) + sb->keys * sizeof(uint64_t); + sb = malloc(bytes); + + if (pread(fd, sb, bytes, SB_START) != bytes) { + fprintf(stderr, "Couldn't read\n"); + exit(2); + } + } + + if (!SB_IS_BDEV(sb)) + show_super_cache(sb, force_csum); + else + show_super_backingdev(sb, force_csum); + + return 0; +} + +int probe_bcache (NihCommand *command, char *const *args) +{ + int i; + struct cache_sb sb; + char uuid[40]; + blkid_probe pr; + + for (i = 0; args[i]!=NULL; i++) { + int fd = open(args[i], O_RDONLY); + if (fd == -1) + continue; + + if (!(pr = blkid_new_probe())) + continue; + if (blkid_probe_set_device(pr, fd, 0, 0)) + continue; + /* probe partitions too */ + if (blkid_probe_enable_partitions(pr, true)) + continue; + /* bail if anything was found + * probe-bcache isn't needed once blkid recognizes bcache */ + if (!blkid_do_probe(pr)) { + continue; + } + + if (pread(fd, &sb, sizeof(sb), SB_START) != sizeof(sb)) + continue; + + if (memcmp(&sb.magic, &BCACHE_MAGIC, sizeof(sb.magic))) + continue; + + uuid_unparse(sb.uuid.b, uuid); + + if (udev) + printf("ID_FS_UUID=%s\n" + "ID_FS_UUID_ENC=%s\n" + "ID_FS_TYPE=bcache\n", + uuid, uuid); + else + printf("%s: UUID=\"\" TYPE=\"bcache\"\n", uuid); + } + + return 0; +} + +int bcache_register (NihCommand *command, char *const *args) +{ + int ret; + + bcachefd = open("/dev/bcache", O_RDWR); + if (bcachefd < 0) { + perror("Can't open bcache device"); + exit(EXIT_FAILURE); + } + + ret = ioctl(bcachefd, BCH_IOCTL_REGISTER, args); + if (ret < 0) { + fprintf(stderr, "ioctl error %d", ret); + exit(EXIT_FAILURE); + } + return 0; +} + +static NihCommand commands[] = { + {"format", N_("format "), "Format one or a list of devices with bcache datastructures. You need to do this before you create a volume", N_("format drive[s] with bcache"), NULL, make_bcache_options, make_bcache}, + {"show-sb", N_("show-sb "), "View a superblock on one or a list of bcache formated devices", N_("show superblock on each of the listed drive"), NULL, super_show_options, super_show}, + {"probe", N_("probe "), NULL, NULL, NULL, probe_bcache_options, probe_bcache}, + {"register", N_("register "), NULL, NULL, NULL, bcache_register_options, bcache_register}, + NIH_COMMAND_LAST +}; + + +int main(int argc, char *argv[]) +{ + int ret = 0; + nih_main_init (argv[0]); + + nih_option_set_synopsis (_("Manage bcache devices")); + nih_option_set_help ( + _("Helps you manage bcache devices")); + + ret = nih_command_parser (NULL, argc, argv, options, commands); + if (ret < 0) + exit (1); + + nih_signal_reset(); +} diff --git a/configure.ac b/configure.ac index a8039c00..f32ce3df 100644 --- a/configure.ac +++ b/configure.ac @@ -11,7 +11,6 @@ AC_CONFIG_MACRO_DIR([m4]) # Checks for programs. AC_PROG_CC - # Checks for libraries. # Checks for header files.