2 * Authors: Kent Overstreet <kmo@daterainc.com>
3 * Gabriel de Perthuis <g2p.code@gmail.com>
4 * Jacob Malevich <jam@datera.io>
9 #include <nih/option.h>
10 #include <nih/command.h>
12 #include <nih/logging.h>
26 #include <sys/ioctl.h>
27 #include <sys/types.h>
29 #include <uuid/uuid.h>
31 #include <bcache.h> //libbcache
33 #define PACKAGE_NAME "bcacheadm"
34 #define PACKAGE_VERSION "1.0"
35 #define PACKAGE_BUGREPORT "bugreport"
37 #define MAX_DEVS MAX_CACHES_PER_SET
40 /* make-bcache globals */
43 char *cache_devices[MAX_DEVS];
44 int tier_mapping[MAX_DEVS];
45 char *backing_devices[MAX_DEVS];
46 char *backing_dev_labels[MAX_DEVS];
47 size_t i, nr_backing_devices = 0, nr_cache_devices = 0;
48 unsigned block_size = 0;
49 unsigned bucket_sizes[MAX_DEVS];
50 int num_bucket_sizes = 0;
51 int writeback = 0, discard = 0, wipe_bcache = 0;
52 unsigned replication_set = 0, replacement_policy = 0;
53 uint64_t data_offset = BDEV_DATA_START_DEFAULT;
55 struct cache_sb *cache_set_sb = NULL;
56 const char *cache_set_uuid = 0;
57 const char *csum_type = 0;
58 char *metadata_replicas = 0;
59 char *data_replicas = 0;
63 char *add_dev_uuid = NULL;
66 bool force_remove = false;
69 bool modify_list_attrs = false;
70 static const char *modify_set_uuid = NULL;
71 static const char *modify_dev_uuid = NULL;
73 /* query-dev globals */
74 bool force_csum = false;
75 bool uuid_only = false;
81 char *cset_dir = "/sys/fs/bcache";
82 bool list_devs = false;
85 bool status_all = false;
88 bool stats_all = false;
89 bool stats_list = false;
90 static const char *stats_uuid = NULL;
91 static const char *stats_dev_uuid = NULL;
92 static const char *stats_cache_num = NULL;
93 bool stats_five_min = false;
94 bool stats_hour = false;
95 bool stats_day = false;
96 bool stats_total = false;
98 /* make-bcache option setters */
99 static int set_block_size(NihOption *option, const char *arg)
101 block_size = hatoi_validate(arg, "block size");
105 static int set_cache(NihOption *option, const char *arg)
108 cache_devices[nr_cache_devices] = strdup(arg);
110 tier_mapping[nr_cache_devices] = 0;
112 int ntier = atoi(tier);
113 if(ntier == 0 || ntier == 1)
114 tier_mapping[nr_cache_devices] = ntier;
116 printf("Invalid tier\n");
125 static int set_bdev(NihOption *option, const char *arg)
130 backing_dev_labels[nr_backing_devices] = strdup(label);
132 backing_devices[nr_backing_devices] = strdup(arg);
134 nr_backing_devices++;
140 static int set_bucket_sizes(NihOption *option, const char *arg)
142 bucket_sizes[num_bucket_sizes]=hatoi_validate(arg, "bucket size");
148 static int set_udev(NihOption *option, const char *arg)
150 if (strcmp("udev", arg)) {
151 printf("Invalid output format %s\n", arg);
160 static NihOption make_bcache_options[] = {
161 // {int shortoption, char* longoption, char* help, NihOptionGroup, char* argname, void *value, NihOptionSetter}
162 {'C', "cache", N_("Format a cache device"), NULL, "dev", NULL, set_cache},
163 {'B', "bdev", N_("Format a backing device"), NULL, "dev", NULL, set_bdev},
164 {'l', "label", N_("label"), NULL, "label", &label, NULL},
165 //Only one bucket_size supported until a list of bucket sizes is parsed correctly
166 {'b', "bucket", N_("bucket size"), NULL, "size", NULL, set_bucket_sizes},
167 //Does the default setter automatically convert strings to an int?
168 {'w', "block", N_("block size (hard sector size of SSD, often 2k"), NULL,"size", NULL, set_block_size},
169 {'t', "tier", N_("tier of subsequent devices"), NULL,"#", &tier, NULL},
170 {'p', "cache_replacement_policy", N_("one of (lru|fifo|random)"), NULL,"policy", &replacement_policy, NULL},
171 {'o', "data_offset", N_("data offset in sectors"), NULL,"offset", &data_offset, NULL},
173 {0, "cset-uuid", N_("UUID for the cache set"), NULL, "uuid", &cache_set_uuid, NULL},
174 {0, "csum-type", N_("One of (none|crc32c|crc64)"), NULL, "type", &csum_type, NULL },
175 {0, "replication-set",N_("replication set of subsequent devices"), NULL, NULL, &replication_set, NULL },
176 {0, "meta-replicas",N_("number of metadata replicas"), NULL, "#", &metadata_replicas, NULL},
177 {0, "data-replicas",N_("number of data replicas"), NULL, "#", &data_replicas, NULL },
179 {0, "wipe-bcache", N_("destroy existing bcache data if present"), NULL, NULL, &wipe_bcache, NULL},
180 {0, "discard", N_("enable discards"), NULL, NULL, &discard, NULL},
181 {0, "writeback", N_("enable writeback"), NULL, NULL, &writeback, NULL},
186 static NihOption probe_bcache_options[] = {
187 {'o', "udev", N_("udev"), NULL, NULL, NULL, set_udev},
191 static NihOption bcache_register_options[] = {
195 static NihOption bcache_unregister_options[] = {
199 static NihOption bcache_add_device_options[] = {
200 {'u', "set", N_("cacheset uuid"), NULL, "UUID", &add_dev_uuid, NULL},
204 static NihOption bcache_rm_device_options[] = {
205 {'f', "force", N_("force cache removal"), NULL, NULL, &force_remove, NULL},
209 static NihOption bcache_modify_options[] = {
210 {'l', "list", N_("list attributes"), NULL, NULL, &modify_list_attrs, NULL},
211 {'u', "set", N_("cacheset uuid"), NULL, "UUID", &modify_set_uuid, NULL},
212 {'d', "dev", N_("device uuid"), NULL, "UUID", &modify_dev_uuid, NULL},
216 static NihOption query_devs_options[] = {
217 {'f', "force_csum", N_("force_csum"), NULL, NULL, &force_csum, NULL},
218 {'u', "uuid-only", N_("only print out the uuid for the devices, not the whole superblock"), NULL, NULL, &uuid_only, NULL},
222 static NihOption list_cachesets_options[] = {
223 {'d', "dir", N_("directory"), NULL, NULL, &cset_dir, NULL},
224 {0, "list-devs", N_("list all devices in the cache sets as well"), NULL, NULL, &list_devs, NULL},
228 static NihOption status_options[] = {
229 {'a', "all", N_("all"), NULL, NULL, &status_all, NULL},
233 static NihOption stats_options[] = {
234 {'a', "all", N_("all"), NULL, NULL, &stats_all, NULL},
235 {'l', "list", N_("list"), NULL, NULL, &stats_list, NULL},
236 {'u', "set", N_("cache_set UUID"), NULL, "UUID", &stats_uuid, NULL},
237 {'d', "dev", N_("dev UUID"), NULL, "UUID", &stats_dev_uuid, NULL},
238 {'c', "cache", N_("cache number (starts from 0)"), NULL, "CACHE#", &stats_cache_num, NULL},
239 {0, "five-min-stats", N_("stats accumulated in last 5 minutes"), NULL, NULL, &stats_five_min, NULL},
240 {0, "hour-stats", N_("stats accumulated in last hour"), NULL, NULL, &stats_hour, NULL},
241 {0, "day-stats", N_("stats accumulated in last day"), NULL, NULL, &stats_day, NULL},
242 {0, "total-stats", N_("stats accumulated in total"), NULL, NULL, &stats_total, NULL},
246 static NihOption options[] = {
252 int make_bcache(NihCommand *command, char *const *args)
254 int cache_dev_fd[devs];
256 int backing_dev_fd[devs];
258 cache_set_sb = calloc(1, sizeof(*cache_set_sb) +
259 sizeof(struct cache_member) * devs);
261 if (cache_set_uuid) {
262 if(uuid_parse(cache_set_uuid, cache_set_sb->set_uuid.b)) {
263 fprintf(stderr, "Bad uuid\n");
267 uuid_generate(cache_set_sb->set_uuid.b);
271 memcpy(cache_set_sb->label, label, sizeof(cache_set_sb->label));
274 SET_CACHE_PREFERRED_CSUM_TYPE(cache_set_sb,
275 read_string_list_or_die(csum_type, csum_types,
278 SET_CACHE_PREFERRED_CSUM_TYPE(cache_set_sb, BCH_CSUM_CRC32C);
281 if (metadata_replicas) {
282 SET_CACHE_SET_META_REPLICAS_WANT(cache_set_sb,
283 strtoul_or_die(metadata_replicas,
284 CACHE_SET_META_REPLICAS_WANT_MAX,
287 SET_CACHE_SET_META_REPLICAS_WANT(cache_set_sb, 1);
291 SET_CACHE_SET_DATA_REPLICAS_WANT(cache_set_sb,
292 strtoul_or_die(data_replicas,
293 CACHE_SET_DATA_REPLICAS_WANT_MAX,
296 SET_CACHE_SET_DATA_REPLICAS_WANT(cache_set_sb, 1);
300 fprintf(stderr, "Please specify -C or -B\n");
304 if(!bucket_sizes[0]) bucket_sizes[0] = 1024;
306 for(i = 0; i < nr_cache_devices; i++)
307 next_cache_device(cache_set_sb,
313 if (!cache_set_sb->nr_in_set && !nr_backing_devices) {
314 fprintf(stderr, "Please supply a device\n");
320 if (bucket_sizes[i] < block_size) {
322 "Bucket size cannot be smaller than block size\n");
326 } while (i < num_bucket_sizes);
329 for (i = 0; i < cache_set_sb->nr_in_set; i++)
330 block_size = max(block_size,
331 get_blocksize(cache_devices[i]));
333 for (i = 0; i < nr_backing_devices; i++)
334 block_size = max(block_size,
335 get_blocksize(backing_devices[i]));
338 for (i = 0; i < cache_set_sb->nr_in_set; i++)
339 cache_dev_fd[i] = dev_open(cache_devices[i], wipe_bcache);
341 for (i = 0; i < nr_backing_devices; i++)
342 backing_dev_fd[i] = dev_open(backing_devices[i], wipe_bcache);
344 write_cache_sbs(cache_dev_fd, cache_set_sb, block_size,
345 bucket_sizes, num_bucket_sizes);
347 for (i = 0; i < nr_backing_devices; i++)
348 write_backingdev_sb(backing_dev_fd[i],
349 block_size, bucket_sizes,
350 writeback, data_offset,
351 backing_dev_labels[i],
352 cache_set_sb->set_uuid);
358 int probe_bcache(NihCommand *command, char *const *args)
363 for (i = 0; args[i] != NULL; i++) {
364 err = probe(args[i], udev);
366 printf("probe_bcache error: %s\n", err);
374 int bcache_register(NihCommand *command, char *const *args)
378 err = register_bcache(args);
380 printf("bcache_register error: %s\n", err);
387 int bcache_unregister(NihCommand *command, char *const *args)
391 err = unregister_bcache(args);
393 printf("bcache_unregister error: %s\n", err);
400 int bcache_add_devices(NihCommand *command, char *const *args)
405 printf("Must specify a cacheset uuid to add the disk to\n");
409 err = add_devices(args, add_dev_uuid);
411 printf("bcache_add_devices error: %s\n", err);
418 int bcache_rm_device(NihCommand *command, char *const *args)
422 err = remove_device(args[0], force_remove);
424 printf("bcache_rm_devices error: %s\n", err);
431 int bcache_modify(NihCommand *command, char *const *args)
436 struct stat cache_stat;
437 char *attr = args[0];
441 if (modify_list_attrs) {
446 if (!modify_set_uuid) {
447 printf("Must provide a cacheset uuid\n");
451 snprintf(path, MAX_PATH, "%s/%s", cset_dir, modify_set_uuid);
454 printf("Must provide the name of an attribute to modify\n");
458 enum sysfs_attr type = sysfs_attr_type(attr);
462 else if(type == INTERNAL_ATTR)
463 strcat(path, "/internal");
464 else if(type == CACHE_ATTR) {
465 if(modify_dev_uuid) {
466 /* searches all cache# for a matching uuid,
467 * path gets modified to the correct cache path */
468 char subdir[10] = "/cache";
469 err = find_matching_uuid(path, subdir,
472 printf("Failed to find "
473 "matching dev %s\n", err);
476 strcat(path, subdir);
479 printf("Must provide a device uuid\n");
482 /* SET_ATTRs are just in the current dir */
489 printf("Must provide a value to change the attribute to\n");
493 fd = open(path, O_WRONLY);
495 printf("Unable to open modify attr with path %s\n", path);
499 write(fd, val, strlen(val));
507 int bcache_list_cachesets(NihCommand *command, char *const *args)
510 err = list_cachesets(cset_dir, list_devs);
512 printf("bcache_list_cachesets error :%s\n", err);
519 int bcache_query_devs(NihCommand *command, char *const *args)
523 for (i = 0; args[i] != NULL; i++) {
525 query_dev(args[i], force_csum, true, uuid_only, dev_uuid);
527 printf("%s\n", dev_uuid);
533 int bcache_status(NihCommand *command, char *const *args)
535 int i, seq, nr_in_set = 0;
536 struct cache_sb *seq_sb = NULL;
538 for (i = 0; args[i] != NULL; i++) {
539 struct cache_sb *sb = query_dev(args[i], false, false,
543 printf("Unable to open superblock, bad path\n");
547 if(!seq_sb || sb->seq > seq) {
550 nr_in_set = sb->nr_in_set;
555 printf("Unable to find a superblock\n");
557 printf("%-50s%-15s%-4s\n", "uuid", "state", "tier");
559 for (i = 0; i < seq_sb->nr_in_set; i++) {
561 struct cache_member *m = ((struct cache_member *) seq_sb->d) + i;
563 uuid_unparse(m->uuid.b, uuid_str);
565 printf("%-50s%-15s%-4llu\n", uuid_str,
566 cache_state[CACHE_STATE(m)],
573 static char *stats_subdir(char* stats_dir)
578 strcat(tmp, "cache");
579 err = find_matching_uuid(stats_dir, tmp, stats_dev_uuid);
582 } else if(stats_cache_num) {
583 strcat(tmp, "cache");
584 strcat(tmp, stats_cache_num);
585 } else if (stats_five_min)
586 strcat(tmp, "stats_five_minute");
588 strcat(tmp, "stats_hour");
590 strcat(tmp, "stats_day");
591 else if (stats_total)
592 strcat(tmp, "stats_total");
596 strcat(stats_dir, tmp);
602 int bcache_stats(NihCommand *command, char *const *args)
605 char stats_dir[MAX_PATH];
607 struct dirent *ent = NULL;
611 snprintf(stats_dir, MAX_PATH, "%s/%s", cset_dir, stats_uuid);
612 err = stats_subdir(stats_dir);
616 dir = opendir(stats_dir);
618 err = "Failed to open dir";
622 err = "Must provide a cacheset uuid";
626 if(stats_list || stats_all) {
627 while ((ent = readdir(dir)) != NULL) {
628 err = read_stat_dir(dir, stats_dir, ent->d_name, stats_all);
635 for (i = 0; args[i] != NULL; i++) {
636 err = read_stat_dir(dir, stats_dir, args[i], true);
646 printf("bcache_stats error: %s\n", err);
650 static NihCommand commands[] = {
651 {"format", N_("format <list of drives>"),
652 "Format one or a list of devices with bcache datastructures."
653 " You need to do this before you create a volume",
654 N_("format drive[s] with bcache"),
655 NULL, make_bcache_options, make_bcache},
656 {"probe", N_("probe <list of devices>"),
657 "Does a blkid_probe on a device",
658 N_("Does a blkid_probe on a device"),
659 NULL, probe_bcache_options, probe_bcache},
660 {"register", N_("register <list of devices>"),
661 "Registers a list of devices",
662 N_("Registers a list of devices"),
663 NULL, bcache_register_options, bcache_register},
664 {"unregister", N_("unregister <list of devices>"),
665 "Unregisters a list of devices",
666 N_("Unregisters a list of devices"),
667 NULL, bcache_unregister_options, bcache_unregister},
668 {"add-devs", N_("add-devs --set=UUID --tier=# <list of devices>"),
669 "Adds a list of devices to a cacheset",
670 N_("Adds a list of devices to a cacheset"),
671 NULL, bcache_add_device_options, bcache_add_devices},
672 {"rm-dev", N_("rm-dev <dev>"),
673 "Removes a device from its cacheset",
674 N_("Removes a device from its cacheset"),
675 NULL, bcache_rm_device_options, bcache_rm_device},
676 {"modify", N_("modify --set=UUID (dev=UUID) name value"),
677 "Modifies attributes related to the cacheset",
678 N_("Modifies attributes related to the cacheset"),
679 NULL, bcache_modify_options, bcache_modify},
680 {"list-cachesets", N_("list-cachesets"),
681 "Lists cachesets in /sys/fs/bcache",
682 N_("Lists cachesets in /sys/fs/bcache"),
683 NULL, list_cachesets_options, bcache_list_cachesets},
684 {"query-devs", N_("query <list of devices>"),
685 "Gives info about the superblock of a list of devices",
686 N_("show superblock on each of the listed drive"),
687 NULL, query_devs_options, bcache_query_devs},
688 {"status", N_("status <list of devices>"),
689 "Finds the status of the most up to date superblock",
690 N_("Finds the status of the most up to date superblock"),
691 NULL, status_options, bcache_status},
692 {"stats", N_("stats <list of devices>"),
693 "List various bcache statistics",
694 N_("List various bcache statistics"),
695 NULL, stats_options, bcache_stats},
700 int main(int argc, char *argv[])
703 nih_main_init (argv[0]);
705 nih_option_set_synopsis (_("Manage bcache devices"));
706 nih_option_set_help (
707 _("Helps you manage bcache devices"));
709 ret = nih_command_parser (NULL, argc, argv, options, commands);