#define MAX_DEVS MAX_CACHES_PER_SET
-/* 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;
+char *cache_devices[MAX_DEVS];
+int tier_mapping[MAX_DEVS];
+char *backing_devices[MAX_DEVS];
+char *backing_dev_labels[MAX_DEVS];
+size_t i, nr_backing_devices = 0, nr_cache_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;
+unsigned replication_set = 0, replacement_policy = 0;
uint64_t data_offset = BDEV_DATA_START_DEFAULT;
char *label = NULL;
struct cache_sb *cache_set_sb = NULL;
-enum long_opts {
- CACHE_SET_UUID = 256,
- CSUM_TYPE,
- REPLICATION_SET,
- META_REPLICAS,
- DATA_REPLICAS,
-};
+const char *cache_set_uuid = 0;
+const char *csum_type = 0;
+char *metadata_replicas = 0;
+char *data_replicas = 0;
+char *tier = 0;
+
+/* add-dev globals */
+char *add_dev_uuid = NULL;
+/* rm-dev globals */
+bool force_remove = false;
-/* super-show globals */
+/* Modify globals */
+bool modify_list_attrs = false;
+static const char *modify_set_uuid = NULL;
+static const char *modify_dev_uuid = NULL;
+
+/* query-dev globals */
bool force_csum = false;
+bool uuid_only = false;
/* probe globals */
bool udev = false;
/* list globals */
char *cset_dir = "/sys/fs/bcache";
+bool list_devs = false;
/* status globals */
bool status_all = false;
bool stats_all = false;
bool stats_list = false;
static const char *stats_uuid = NULL;
+static const char *stats_dev_uuid = NULL;
static const char *stats_cache_num = NULL;
bool stats_five_min = false;
bool stats_hour = false;
bool stats_total = false;
/* make-bcache option setters */
-static int set_CACHE_SET_UUID(NihOption *option, const char *arg)
+static int set_block_size(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"));
+ block_size = hatoi_validate(arg, "block size");
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);
+ bdev = 0;
+ cache_devices[nr_cache_devices] = strdup(arg);
+ if(!tier)
+ tier_mapping[nr_cache_devices] = 0;
+ else {
+ int ntier = atoi(tier);
+ if(ntier == 0 || ntier == 1)
+ tier_mapping[nr_cache_devices] = ntier;
+ else
+ printf("Invalid tier\n");
+ }
+
devs++;
+ nr_cache_devices++;
+
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;
+ bdev = 1;
+
+ if(label)
+ backing_dev_labels[nr_backing_devices] = strdup(label);
+
+ backing_devices[nr_backing_devices] = strdup(arg);
+
+ nr_backing_devices++;
devs++;
+
return 0;
}
+
static int set_bucket_sizes(NihOption *option, const char *arg)
{
bucket_sizes[num_bucket_sizes]=hatoi_validate(arg, "bucket size");
/* 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},
+ {'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},
//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},
+ {'b', "bucket", N_("bucket size"), NULL, "size", 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},
+ {'w', "block", N_("block size (hard sector size of SSD, often 2k"), NULL,"size", NULL, set_block_size},
+ {'t', "tier", N_("tier of subsequent devices"), NULL,"#", &tier, NULL},
+ {'p', "cache_replacement_policy", N_("one of (lru|fifo|random)"), NULL,"policy", &replacement_policy, NULL},
+ {'o', "data_offset", N_("data offset in sectors"), NULL,"offset", &data_offset, 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, "cset-uuid", N_("UUID for the cache set"), NULL, "uuid", &cache_set_uuid, NULL},
+ {0, "csum-type", N_("One of (none|crc32c|crc64)"), NULL, "type", &csum_type, NULL },
+ {0, "replication-set",N_("replication set of subsequent devices"), NULL, NULL, &replication_set, NULL },
+ {0, "meta-replicas",N_("number of metadata replicas"), NULL, "#", &metadata_replicas, NULL},
+ {0, "data-replicas",N_("number of data replicas"), NULL, "#", &data_replicas, NULL },
{0, "wipe-bcache", N_("destroy existing bcache data if present"), NULL, NULL, &wipe_bcache, NULL},
{0, "discard", N_("enable discards"), NULL, NULL, &discard, NULL},
NIH_OPTION_LAST
};
+static NihOption bcache_unregister_options[] = {
+ NIH_OPTION_LAST
+};
+
+static NihOption bcache_add_device_options[] = {
+ {'u', "set", N_("cacheset uuid"), NULL, "UUID", &add_dev_uuid, NULL},
+ NIH_OPTION_LAST
+};
+
+static NihOption bcache_rm_device_options[] = {
+ {'f', "force", N_("force cache removal"), NULL, NULL, &force_remove, NULL},
+ NIH_OPTION_LAST
+};
+
+static NihOption bcache_modify_options[] = {
+ {'l', "list", N_("list attributes"), NULL, NULL, &modify_list_attrs, NULL},
+ {'u', "set", N_("cacheset uuid"), NULL, "UUID", &modify_set_uuid, NULL},
+ {'d', "dev", N_("device uuid"), NULL, "UUID", &modify_dev_uuid, NULL},
+ NIH_OPTION_LAST
+};
+
static NihOption query_devs_options[] = {
{'f', "force_csum", N_("force_csum"), NULL, NULL, &force_csum, NULL},
+ {'u', "uuid-only", N_("only print out the uuid for the devices, not the whole superblock"), NULL, NULL, &uuid_only, NULL},
NIH_OPTION_LAST
};
static NihOption list_cachesets_options[] = {
{'d', "dir", N_("directory"), NULL, NULL, &cset_dir, NULL},
+ {0, "list-devs", N_("list all devices in the cache sets as well"), NULL, NULL, &list_devs, NULL},
NIH_OPTION_LAST
};
static NihOption stats_options[] = {
{'a', "all", N_("all"), NULL, NULL, &stats_all, NULL},
{'l', "list", N_("list"), NULL, NULL, &stats_list, NULL},
- {'u', "uuid", N_("cache_set UUID"), NULL, "UUID", &stats_uuid, NULL},
+ {'u', "set", N_("cache_set UUID"), NULL, "UUID", &stats_uuid, NULL},
+ {'d', "dev", N_("dev UUID"), NULL, "UUID", &stats_dev_uuid, NULL},
{'c', "cache", N_("cache number (starts from 0)"), NULL, "CACHE#", &stats_cache_num, NULL},
{0, "five-min-stats", N_("stats accumulated in last 5 minutes"), NULL, NULL, &stats_five_min, NULL},
{0, "hour-stats", N_("stats accumulated in last hour"), NULL, NULL, &stats_hour, NULL},
/* commands */
-int make_bcache (NihCommand *command, char *const *args)
+int make_bcache(NihCommand *command, char *const *args)
{
int cache_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 (cache_set_uuid) {
+ if(uuid_parse(cache_set_uuid, cache_set_sb->set_uuid.b)) {
+ fprintf(stderr, "Bad uuid\n");
+ return -1;
+ }
+ } else {
+ uuid_generate(cache_set_sb->set_uuid.b);
+ }
- if(!bucket_sizes[0]) bucket_sizes[0] = 1024;
+ if (label)
+ memcpy(cache_set_sb->label, label, sizeof(cache_set_sb->label));
+
+ if (csum_type) {
+ SET_CACHE_PREFERRED_CSUM_TYPE(cache_set_sb,
+ read_string_list_or_die(csum_type, csum_types,
+ "csum type"));
+ } else {
+ SET_CACHE_PREFERRED_CSUM_TYPE(cache_set_sb, BCH_CSUM_CRC32C);
+ }
+
+ if (metadata_replicas) {
+ SET_CACHE_SET_META_REPLICAS_WANT(cache_set_sb,
+ strtoul_or_die(metadata_replicas,
+ CACHE_SET_META_REPLICAS_WANT_MAX,
+ "meta replicas"));
+ } else {
+ SET_CACHE_SET_META_REPLICAS_WANT(cache_set_sb, 1);
+ }
+
+ if (data_replicas) {
+ SET_CACHE_SET_DATA_REPLICAS_WANT(cache_set_sb,
+ strtoul_or_die(data_replicas,
+ CACHE_SET_DATA_REPLICAS_WANT_MAX,
+ "data replicas"));
+ } else {
+ SET_CACHE_SET_DATA_REPLICAS_WANT(cache_set_sb, 1);
+ }
if (bdev == -1) {
fprintf(stderr, "Please specify -C or -B\n");
exit(EXIT_FAILURE);
}
+ if(!bucket_sizes[0]) bucket_sizes[0] = 1024;
+
+ for(i = 0; i < nr_cache_devices; i++)
+ next_cache_device(cache_set_sb,
+ replication_set,
+ tier_mapping[i],
+ replacement_policy,
+ discard);
+
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");
+ fprintf(stderr,
+ "Bucket size cannot be smaller than block size\n");
exit(EXIT_FAILURE);
}
i++;
return 0;
}
-int probe_bcache (NihCommand *command, char *const *args)
+int probe_bcache(NihCommand *command, char *const *args)
{
int i;
+ char *err = NULL;
for (i = 0; args[i] != NULL; i++) {
- probe(args[i], udev);
+ err = probe(args[i], udev);
+ if(err) {
+ printf("probe_bcache error: %s\n", err);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int bcache_register(NihCommand *command, char *const *args)
+{
+ char *err = NULL;
+
+ err = register_bcache(args);
+ if (err) {
+ printf("bcache_register error: %s\n", err);
+ return -1;
+ }
+
+ return 0;
+}
+
+int bcache_unregister(NihCommand *command, char *const *args)
+{
+ char *err = NULL;
+
+ err = unregister_bcache(args);
+ if (err) {
+ printf("bcache_unregister error: %s\n", err);
+ return -1;
}
return 0;
}
-int bcache_register (NihCommand *command, char *const *args)
+int bcache_add_devices(NihCommand *command, char *const *args)
{
- int ret;
- char *arg_list = parse_array_to_list(args);
+ char *err;
+
+ if (!add_dev_uuid) {
+ printf("Must specify a cacheset uuid to add the disk to\n");
+ return -1;
+ }
- if(arg_list) {
- ret = register_bcache(arg_list);
- free(arg_list);
+ err = add_devices(args, add_dev_uuid);
+ if (err) {
+ printf("bcache_add_devices error: %s\n", err);
+ return -1;
}
- return ret;
+ return 0;
}
-int bcache_list_cachesets (NihCommand *command, char *const *args)
+int bcache_rm_device(NihCommand *command, char *const *args)
{
- return list_cachesets(cset_dir);
+ char *err;
+
+ err = remove_device(args[0], force_remove);
+ if (err) {
+ printf("bcache_rm_devices error: %s\n", err);
+ return -1;
+ }
+
+ return 0;
}
-int bcache_query_devs (NihCommand *command, char *const *args)
+int bcache_modify(NihCommand *command, char *const *args)
+{
+ char *err;
+ char path[MAX_PATH];
+ DIR *path_dir;
+ struct stat cache_stat;
+ char *attr = args[0];
+ char *val = NULL;
+ int fd = -1;
+
+ if (modify_list_attrs) {
+ sysfs_attr_list();
+ return 0;
+ }
+
+ if (!modify_set_uuid) {
+ printf("Must provide a cacheset uuid\n");
+ return -1;
+ }
+
+ snprintf(path, MAX_PATH, "%s/%s", cset_dir, modify_set_uuid);
+
+ if(!attr) {
+ printf("Must provide the name of an attribute to modify\n");
+ goto err;
+ }
+
+ enum sysfs_attr type = sysfs_attr_type(attr);
+
+ if (type == -1)
+ goto err;
+ else if(type == INTERNAL_ATTR)
+ strcat(path, "/internal");
+ else if(type == CACHE_ATTR) {
+ if(modify_dev_uuid) {
+ /* searches all cache# for a matching uuid,
+ * path gets modified to the correct cache path */
+ char subdir[10] = "/cache";
+ err = find_matching_uuid(path, subdir,
+ modify_dev_uuid);
+ if (err) {
+ printf("Failed to find "
+ "matching dev %s\n", err);
+ goto err;
+ } else {
+ strcat(path, subdir);
+ }
+ } else {
+ printf("Must provide a device uuid\n");
+ }
+ }
+ /* SET_ATTRs are just in the current dir */
+
+ strcat(path, "/");
+ strcat(path, attr);
+
+ val = args[1];
+ if (!val) {
+ printf("Must provide a value to change the attribute to\n");
+ goto err;
+ }
+
+ fd = open(path, O_WRONLY);
+ if (fd < 0) {
+ printf("Unable to open modify attr with path %s\n", path);
+ goto err;
+ }
+
+ write(fd, val, strlen(val));
+
+err:
+ if(fd)
+ close(fd);
+ return 0;
+}
+
+int bcache_list_cachesets(NihCommand *command, char *const *args)
+{
+ char *err = NULL;
+ err = list_cachesets(cset_dir, list_devs);
+ if (err) {
+ printf("bcache_list_cachesets error :%s\n", err);
+ return -1;
+ }
+
+ return 0;
+}
+
+int bcache_query_devs(NihCommand *command, char *const *args)
{
int i;
for (i = 0; args[i] != NULL; i++) {
- struct cache_sb *sb = query_dev(args[i], false);
- print_dev_info(sb, force_csum);
+ char dev_uuid[40];
+ query_dev(args[i], force_csum, true, uuid_only, dev_uuid);
+ if(uuid_only)
+ printf("%s\n", dev_uuid);
}
+
+ return 0;
}
-int bcache_status (NihCommand *command, char *const *args)
+int bcache_status(NihCommand *command, char *const *args)
{
- int i;
- struct cache_sb *sb_tier0 = NULL, *sb_tier1 = NULL;
- char *dev0 = NULL, *dev1 = NULL;
+ int i, seq, nr_in_set = 0;
+ struct cache_sb *seq_sb = NULL;
for (i = 0; args[i] != NULL; i++) {
- struct cache_sb *sb = query_dev(args[i], false);
- struct cache_member *m = ((struct cache_member *) sb->d) +
- sb->nr_this_dev;
- long long unsigned cache_tier = CACHE_TIER(m);
-
- if (!cache_tier)
- if (!sb_tier0 || sb->seq > sb_tier0->seq) {
- sb_tier0 = sb;
- dev0 = args[i];
- }
- else if (cache_tier == 1)
- if (!sb_tier1 || sb->seq > sb_tier1->seq) {
- sb_tier1 = sb;
- dev1 = args[i];
- }
+ struct cache_sb *sb = query_dev(args[i], false, false,
+ false, NULL);
+
+ if(!sb) {
+ printf("Unable to open superblock, bad path\n");
+ return -1;
+ }
+
+ if(!seq_sb || sb->seq > seq) {
+ seq = sb->seq;
+ seq_sb = sb;
+ nr_in_set = sb->nr_in_set;
+ }
+ }
+
+ if(!seq_sb)
+ printf("Unable to find a superblock\n");
+ else
+ printf("%-50s%-15s%-4s\n", "uuid", "state", "tier");
+
+ for (i = 0; i < seq_sb->nr_in_set; i++) {
+ char uuid_str[40];
+ struct cache_member *m = ((struct cache_member *) seq_sb->d) + i;
+
+ uuid_unparse(m->uuid.b, uuid_str);
+
+ printf("%-50s%-15s%-4llu\n", uuid_str,
+ cache_state[CACHE_STATE(m)],
+ CACHE_TIER(m));
}
- if (sb_tier0) sb_state(sb_tier0, dev0);
- if (sb_tier1) sb_state(sb_tier1, dev1);
+
+ return 0;
}
-static void stats_subdir(char* stats_dir)
+static char *stats_subdir(char* stats_dir)
{
char tmp[50] = "/";
- if(stats_cache_num) {
+ char *err = NULL;
+ if(stats_dev_uuid) {
+ strcat(tmp, "cache");
+ err = find_matching_uuid(stats_dir, tmp, stats_dev_uuid);
+ if(err)
+ goto err;
+ } else if(stats_cache_num) {
strcat(tmp, "cache");
strcat(tmp, stats_cache_num);
} else if (stats_five_min)
else if (stats_total)
strcat(tmp, "stats_total");
else
- return;
+ return err;
strcat(stats_dir, tmp);
+
+err:
+ return err;
}
-int bcache_stats (NihCommand *command, char *const *args)
+int bcache_stats(NihCommand *command, char *const *args)
{
int i;
- char stats_dir[200];
+ char stats_dir[MAX_PATH];
DIR *dir = NULL;
struct dirent *ent = NULL;
+ char *err = NULL;
if (stats_uuid) {
- strcpy(stats_dir, cset_dir);
- strcat(stats_dir, "/");
- strcat(stats_dir, stats_uuid);
- stats_subdir(stats_dir);
+ snprintf(stats_dir, MAX_PATH, "%s/%s", cset_dir, stats_uuid);
+ err = stats_subdir(stats_dir);
+ if(err)
+ goto err;
+
dir = opendir(stats_dir);
if (!dir) {
- fprintf(stderr, "Failed to open dir %s\n", cset_dir);
- return 1;
+ err = "Failed to open dir";
+ goto err;
}
} else {
- printf("Must provide a cacheset uuid\n");
- exit(EXIT_FAILURE);
+ err = "Must provide a cacheset uuid";
+ goto err;
}
- if(stats_list || stats_all)
- while ((ent = readdir(dir)) != NULL)
- read_stat_dir(dir, stats_dir, ent->d_name, stats_all);
+ if(stats_list || stats_all) {
+ while ((ent = readdir(dir)) != NULL) {
+ err = read_stat_dir(dir, stats_dir, ent->d_name, stats_all);
+ if (err)
+ goto err;
+ }
+ }
- for (i = 0; args[i] != NULL; i++)
- read_stat_dir(dir, stats_dir, args[i], true);
+ for (i = 0; args[i] != NULL; i++) {
+ err = read_stat_dir(dir, stats_dir, args[i], true);
+ if (err)
+ goto err;
+ }
closedir(dir);
+ return 0;
+
+err:
+ closedir(dir);
+ printf("bcache_stats error: %s\n", err);
+ return -1;
}
static NihCommand commands[] = {
"Registers a list of devices",
N_("Registers a list of devices"),
NULL, bcache_register_options, bcache_register},
+ {"unregister", N_("unregister <list of devices>"),
+ "Unregisters a list of devices",
+ N_("Unregisters a list of devices"),
+ NULL, bcache_unregister_options, bcache_unregister},
+ {"add-devs", N_("add-devs --set=UUID --tier=# <list of devices>"),
+ "Adds a list of devices to a cacheset",
+ N_("Adds a list of devices to a cacheset"),
+ NULL, bcache_add_device_options, bcache_add_devices},
+ {"rm-dev", N_("rm-dev <dev>"),
+ "Removes a device from its cacheset",
+ N_("Removes a device from its cacheset"),
+ NULL, bcache_rm_device_options, bcache_rm_device},
+ {"modify", N_("modify --set=UUID (dev=UUID) name value"),
+ "Modifies attributes related to the cacheset",
+ N_("Modifies attributes related to the cacheset"),
+ NULL, bcache_modify_options, bcache_modify},
{"list-cachesets", N_("list-cachesets"),
"Lists cachesets in /sys/fs/bcache",
N_("Lists cachesets in /sys/fs/bcache"),
exit (1);
nih_signal_reset();
+
+ return 0;
}