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"
39 /* make-bcache globals */
42 char *cache_devices[MAX_DEVS];
43 int tier_mapping[MAX_DEVS];
44 unsigned replacement_policy_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 btree_node_size = 0;
50 unsigned bucket_sizes[MAX_DEVS];
51 int num_bucket_sizes = 0;
52 int writeback = 0, writearound = 0, discard = 0, wipe_bcache = 0;
53 unsigned replication_set = 0;
54 char *replacement_policy = 0;
55 uint64_t data_offset = BDEV_DATA_START_DEFAULT;
57 struct cache_sb *cache_set_sb = NULL;
58 const char *cache_set_uuid = 0;
59 const char *csum_type = 0;
60 char *metadata_replicas = 0;
61 char *data_replicas = 0;
65 bool force_remove = false;
68 bool modify_list_attrs = false;
69 static const char *modify_set_uuid = NULL;
70 static const char *modify_dev_uuid = NULL;
72 /* query-dev globals */
73 bool force_csum = false;
74 bool uuid_only = false;
75 bool query_brief = false;
81 char *cset_dir = "/sys/fs/bcache";
82 bool list_devs = false;
83 static const char *internal_uuid = NULL;
86 bool status_all = false;
88 /* capacity globals */
89 static const char *capacity_uuid = NULL;
90 bool capacity_devs = false;
93 bool stats_all = false;
94 bool stats_list = false;
95 static const char *stats_uuid = NULL;
96 static const char *stats_dev_uuid = NULL;
97 static const char *stats_cache_num = NULL;
98 bool stats_five_min = false;
99 bool stats_hour = false;
100 bool stats_day = false;
101 bool stats_total = false;
103 /* set_failed globals */
104 static const char *dev_failed_uuid = NULL;
106 /* make-bcache option setters */
107 static int set_block_size(NihOption *option, const char *arg)
109 block_size = hatoi_validate(arg, "block size");
113 static int set_btree_node_size(NihOption *option, const char *arg)
115 btree_node_size = hatoi_validate(arg, "btree node size");
119 static int set_cache(NihOption *option, const char *arg)
122 cache_devices[nr_cache_devices] = strdup(arg);
124 tier_mapping[nr_cache_devices] = 0;
126 int ntier = atoi(tier);
127 if(ntier == 0 || ntier == 1)
128 tier_mapping[nr_cache_devices] = ntier;
130 printf("Invalid tier %s\n", tier);
133 if (!replacement_policy)
134 replacement_policy_mapping[nr_cache_devices] = 0;
138 while (replacement_policies[i] != NULL) {
139 if (!strcmp(replacement_policy,
140 replacement_policies[i])) {
141 replacement_policy_mapping[nr_cache_devices] = i;
147 if (replacement_policies[i] == NULL)
148 printf("Invalid replacement policy: %s\n",
158 static int set_bdev(NihOption *option, const char *arg)
163 backing_dev_labels[nr_backing_devices] = strdup(label);
165 backing_devices[nr_backing_devices] = strdup(arg);
167 nr_backing_devices++;
173 static int set_bucket_sizes(NihOption *option, const char *arg)
175 bucket_sizes[num_bucket_sizes]=hatoi_validate(arg, "bucket size");
181 static int set_udev(NihOption *option, const char *arg)
183 if (strcmp("udev", arg)) {
184 printf("Invalid output format %s\n", arg);
193 static NihOption make_bcache_options[] = {
194 // {int shortoption, char* longoption, char* help, NihOptionGroup, char* argname, void *value, NihOptionSetter}
195 {'C', "cache", N_("Format a cache device"), NULL, "dev", NULL, set_cache},
196 {'B', "bdev", N_("Format a backing device"), NULL, "dev", NULL, set_bdev},
197 {'l', "label", N_("label"), NULL, "label", &label, NULL},
198 //Only one bucket_size supported until a list of bucket sizes is parsed correctly
199 {'b', "bucket", N_("bucket size"), NULL, "size", NULL, set_bucket_sizes},
200 //Does the default setter automatically convert strings to an int?
201 {'w', "block", N_("block size (hard sector size of SSD, often 2k"), NULL, "size", NULL, set_block_size},
203 {'n', "btree-node", N_("Btree node size, default 256k"), NULL, "size", NULL, set_btree_node_size},
205 {'t', "tier", N_("tier of subsequent devices"), NULL,"#", &tier, NULL},
206 {'p', "cache_replacement_policy", N_("one of (lru|fifo|random)"), NULL,"policy", &replacement_policy, NULL},
207 {'o', "data_offset", N_("data offset in sectors"), NULL,"offset", &data_offset, NULL},
209 {0, "cset-uuid", N_("UUID for the cache set"), NULL, "uuid", &cache_set_uuid, NULL},
210 {0, "csum-type", N_("One of (none|crc32c|crc64)"), NULL, "type", &csum_type, NULL },
211 {0, "replication-set",N_("replication set of subsequent devices"), NULL, NULL, &replication_set, NULL },
212 {0, "meta-replicas",N_("number of metadata replicas"), NULL, "#", &metadata_replicas, NULL},
213 {0, "data-replicas",N_("number of data replicas"), NULL, "#", &data_replicas, NULL },
215 {0, "wipe-bcache", N_("destroy existing bcache data if present"), NULL, NULL, &wipe_bcache, NULL},
216 {0, "discard", N_("enable discards"), NULL, NULL, &discard, NULL},
217 {0, "writeback", N_("enable writeback"), NULL, NULL, &writeback, NULL},
218 {0, "writearound", N_("enable writearound"), NULL, NULL, &writearound, NULL},
223 static NihOption probe_bcache_options[] = {
224 {'o', "udev", N_("udev"), NULL, NULL, NULL, set_udev},
228 static NihOption bcache_register_options[] = {
232 static NihOption bcache_unregister_options[] = {
236 static NihOption bcache_add_device_options[] = {
240 static NihOption bcache_rm_device_options[] = {
241 {'f', "force", N_("force cache removal"), NULL, NULL, &force_remove, NULL},
245 static NihOption bcache_modify_options[] = {
246 {'l', "list", N_("list attributes"), NULL, NULL, &modify_list_attrs, NULL},
247 {'u', "set", N_("cacheset uuid"), NULL, "UUID", &modify_set_uuid, NULL},
248 {'d', "dev", N_("device uuid"), NULL, "UUID", &modify_dev_uuid, NULL},
252 static NihOption query_devs_options[] = {
253 {'f', "force_csum", N_("force_csum"), NULL, NULL, &force_csum, NULL},
254 {'u', "uuid-only", N_("only print out the uuid for the devices, not the whole superblock"), NULL, NULL, &uuid_only, NULL},
255 {'b', "brief", N_("only print out the cluster,server,and disk uuids"), NULL, NULL, &query_brief, NULL},
259 static NihOption list_cachesets_options[] = {
260 {'d', "dir", N_("directory"), NULL, NULL, &cset_dir, NULL},
261 {0, "list-devs", N_("list all devices in the cache sets as well"), NULL, NULL, &list_devs, NULL},
262 {0, "internal_uuid", N_("Show the internal UUID for the given cacheset UUID"), NULL, "UUID", &internal_uuid, NULL},
266 static NihOption status_options[] = {
267 {'a', "all", N_("all"), NULL, NULL, &status_all, NULL},
271 static NihOption capacity_options[] = {
272 {'u', "set", N_("cache_set UUID"), NULL, "UUID", &capacity_uuid, NULL},
273 {'d', "devs", N_("Individual device capacities"), NULL, NULL, &capacity_devs, NULL},
277 static NihOption stats_options[] = {
278 {'a', "all", N_("all"), NULL, NULL, &stats_all, NULL},
279 {'l', "list", N_("list"), NULL, NULL, &stats_list, NULL},
280 {'u', "set", N_("cache_set UUID"), NULL, "UUID", &stats_uuid, NULL},
281 {'d', "dev", N_("dev UUID"), NULL, "UUID", &stats_dev_uuid, NULL},
282 {'c', "cache", N_("cache number (starts from 0)"), NULL, "CACHE#", &stats_cache_num, NULL},
283 {0, "five-min-stats", N_("stats accumulated in last 5 minutes"), NULL, NULL, &stats_five_min, NULL},
284 {0, "hour-stats", N_("stats accumulated in last hour"), NULL, NULL, &stats_hour, NULL},
285 {0, "day-stats", N_("stats accumulated in last day"), NULL, NULL, &stats_day, NULL},
286 {0, "total-stats", N_("stats accumulated in total"), NULL, NULL, &stats_total, NULL},
290 static NihOption set_failed_options[] = {
291 {'d', "dev", N_("dev UUID"), NULL, "UUID", &dev_failed_uuid, NULL},
295 static NihOption options[] = {
301 int make_bcache(NihCommand *command, char *const *args)
303 int cache_dev_fd[devs];
304 int data_replicas_num, metadata_replicas_num;
306 int backing_dev_fd[devs];
310 cache_set_sb = calloc(1, sizeof(*cache_set_sb) +
311 sizeof(struct cache_member) * devs);
313 uuid_generate(cache_set_sb->set_uuid.b);
315 if (cache_set_uuid) {
316 if(uuid_parse(cache_set_uuid, cache_set_sb->user_uuid.b)) {
317 fprintf(stderr, "Bad uuid\n");
321 uuid_generate(cache_set_sb->user_uuid.b);
325 memcpy(cache_set_sb->label, label, sizeof(cache_set_sb->label));
328 SET_CACHE_PREFERRED_CSUM_TYPE(cache_set_sb,
329 read_string_list_or_die(csum_type, csum_types,
332 SET_CACHE_PREFERRED_CSUM_TYPE(cache_set_sb, BCH_CSUM_CRC32C);
335 if (metadata_replicas) {
336 metadata_replicas_num =
337 strtoul_or_die(metadata_replicas,
338 CACHE_SET_META_REPLICAS_WANT_MAX,
341 metadata_replicas_num = 1;
344 SET_CACHE_SET_META_REPLICAS_WANT(cache_set_sb,
345 metadata_replicas_num);
346 SET_CACHE_SET_META_REPLICAS_HAVE(cache_set_sb,
347 metadata_replicas_num);
351 strtoul_or_die(data_replicas,
352 CACHE_SET_DATA_REPLICAS_WANT_MAX,
355 data_replicas_num = 1;
358 SET_CACHE_SET_DATA_REPLICAS_WANT(cache_set_sb,
360 SET_CACHE_SET_DATA_REPLICAS_HAVE(cache_set_sb,
364 fprintf(stderr, "Please specify -C or -B\n");
368 if (!bucket_sizes[0])
369 bucket_sizes[0] = 1024;
371 for (i = 0; i < nr_cache_devices; i++)
372 next_cache_device(cache_set_sb,
375 replacement_policy_mapping[i],
378 if (!cache_set_sb->nr_in_set && !nr_backing_devices) {
379 fprintf(stderr, "Please supply a device\n");
385 if (bucket_sizes[i] < block_size) {
387 "Bucket size cannot be smaller than block size\n");
391 } while (i < num_bucket_sizes);
394 for (i = 0; i < cache_set_sb->nr_in_set; i++)
395 block_size = max(block_size,
396 get_blocksize(cache_devices[i]));
398 for (i = 0; i < nr_backing_devices; i++)
399 block_size = max(block_size,
400 get_blocksize(backing_devices[i]));
403 for (i = 0; i < cache_set_sb->nr_in_set; i++)
404 cache_dev_fd[i] = dev_open(cache_devices[i], wipe_bcache);
406 for (i = 0; i < nr_backing_devices; i++)
407 backing_dev_fd[i] = dev_open(backing_devices[i], wipe_bcache);
409 write_cache_sbs(cache_dev_fd, cache_set_sb, block_size,
410 bucket_sizes, num_bucket_sizes, btree_node_size);
413 cache_mode = CACHE_MODE_WRITEBACK;
414 else if (writearound)
415 cache_mode = CACHE_MODE_WRITEAROUND;
417 cache_mode = CACHE_MODE_WRITETHROUGH;
419 for (i = 0; i < nr_backing_devices; i++)
420 write_backingdev_sb(backing_dev_fd[i],
421 block_size, bucket_sizes,
422 cache_mode, data_offset,
423 backing_dev_labels[i],
424 cache_set_sb->user_uuid,
425 cache_set_sb->set_uuid);
431 int probe_bcache(NihCommand *command, char *const *args)
436 for (i = 0; args[i] != NULL; i++) {
437 err = probe(args[i], udev);
439 printf("probe_bcache error: %s\n", err);
447 int bcache_register(NihCommand *command, char *const *args)
451 err = register_bcache(args);
453 printf("bcache_register error: %s\n", err);
460 int bcache_unregister(NihCommand *command, char *const *args)
464 err = unregister_bcache(args);
466 printf("bcache_unregister error: %s\n", err);
473 int bcache_add_devices(NihCommand *command, char *const *args)
477 err = add_devices(args);
479 printf("bcache_add_devices error: %s\n", err);
486 int bcache_rm_device(NihCommand *command, char *const *args)
491 printf("Must provide a device name\n");
495 err = remove_device(args[0], force_remove);
497 printf("bcache_rm_devices error: %s\n", err);
504 int bcache_modify(NihCommand *command, char *const *args)
508 char *attr = args[0];
512 if (modify_list_attrs) {
517 if (!modify_set_uuid) {
518 printf("Must provide a cacheset uuid\n");
522 snprintf(path, MAX_PATH, "%s/%s", cset_dir, modify_set_uuid);
525 printf("Must provide the name of an attribute to modify\n");
529 enum sysfs_attr type = sysfs_attr_type(attr);
533 else if(type == INTERNAL_ATTR)
534 strcat(path, "/internal");
535 else if(type == CACHE_ATTR) {
536 if(modify_dev_uuid) {
537 /* searches all cache# for a matching uuid,
538 * path gets modified to the correct cache path */
539 char subdir[10] = "/cache";
540 err = find_matching_uuid(path, subdir,
543 printf("Failed to find "
544 "matching dev %s\n", err);
547 strcat(path, subdir);
550 printf("Must provide a device uuid\n");
553 /* SET_ATTRs are just in the current dir */
560 printf("Must provide a value to change the attribute to\n");
564 fd = open(path, O_WRONLY);
566 printf("Unable to open modify attr with path %s\n", path);
570 write(fd, val, strlen(val));
578 int bcache_list_cachesets(NihCommand *command, char *const *args)
583 char uuid_path[MAX_PATH];
587 snprintf(uuid_path, MAX_PATH, "%s/%s", cset_dir, internal_uuid);
589 err = "uuid does not exist";
590 if((uuid_dir = opendir(uuid_path)) == NULL)
593 err = read_stat_dir(uuid_dir, uuid_path, "/internal/internal_uuid", buf);
600 err = list_cachesets(cset_dir, list_devs);
607 printf("bcache_list_cachesets error :%s\n", err);
611 int bcache_query_devs(NihCommand *command, char *const *args)
616 printf("%-10s%-40s%-40s%-40s\n", "dev name", "disk uuid",
617 "server uuid", "cluster uuid");
619 for (i = 0; args[i] != NULL; i++) {
621 struct cache_sb *sb = query_dev(args[i], force_csum,
622 !query_brief, uuid_only, dev_uuid);
625 printf("error opening the superblock for %s\n",
631 printf("%s\n", dev_uuid);
632 } else if (query_brief) {
633 char set_uuid_str[40], dev_uuid_str[40];
634 char *clus_uuid = (char *)sb->label;
636 uuid_unparse(sb->user_uuid.b, set_uuid_str);
637 uuid_unparse(sb->disk_uuid.b, dev_uuid_str);
638 if (!strcmp(clus_uuid, ""))
641 printf("%-10s%-40s%-40s%-40s\n", args[i],
652 int bcache_status(NihCommand *command, char *const *args)
654 int i, dev_count = 0, seq, cache_count = 0;
655 struct cache_sb *seq_sb = NULL;
656 char cache_path[MAX_PATH];
657 char *dev_names[MAX_DEVS];
658 char *dev_uuids[MAX_DEVS];
662 for (i = 0; args[i] != NULL; i++) {
663 struct cache_sb *sb = query_dev(args[i], false, false,
667 printf("Unable to open superblock, bad path\n");
671 if (!seq_sb || sb->seq > seq) {
679 printf("Unable to find a superblock\n");
682 uuid_unparse(seq_sb->user_uuid.b, set_uuid);
683 printf("%-50s%-15s%-4s\n", "uuid", "state", "tier");
686 snprintf(intbuf, 4, "%d", i);
687 snprintf(cache_path, MAX_PATH, "%s/%s/%s", cset_dir, set_uuid,
691 * Get a list of all the devices from sysfs first, then
692 * compare it to the list we get back from the most up
693 * to date superblock. If there are any devices in the superblock
694 * that are not in sysfs, print out 'missing'
701 if(((cache_dir = opendir(cache_path)) == NULL) &&
702 cache_count > MAX_DEVS)
708 if((len = readlink(cache_path, buf, sizeof(buf) - 1)) != -1) {
714 dev_names[dev_count] = dev_name(buf);
715 snprintf(dev_path, MAX_PATH, "%s/%s", "/dev",
716 dev_names[dev_count]);
717 sb = query_dev(dev_path, false, false,
720 printf("error reading %s\n", dev_path);
725 dev_uuids[dev_count] = strdup(dev_uuid);
729 cache_path[strlen(cache_path) - strlen(intbuf)] = 0;
732 snprintf(intbuf, 4, "%d", cache_count);
733 strcat(cache_path, intbuf);
736 for (i = 0; i < seq_sb->nr_in_set; i++) {
738 struct cache_member *m = seq_sb->members + i;
742 uuid_unparse(m->uuid.b, uuid_str);
743 snprintf(dev_state, MAX_PATH, "%s",
744 cache_state[CACHE_STATE(m)]);
746 for (j = 0; j < dev_count; j++) {
747 if (!strcmp(uuid_str, dev_uuids[j])) {
749 } else if (j == dev_count - 1) {
750 if (!strcmp(cache_state[CACHE_STATE(m)], "active"))
751 snprintf(dev_state, MAX_PATH, "%s", "missing");
756 printf("%-50s%-15s%-4llu\n", uuid_str, dev_state,
762 for (i = 0; i < dev_count; i++) {
770 int bcache_capacity(NihCommand *command, char *const *args)
772 char *err = "Must provide a cacheset uuid";
776 err = bcache_get_capacity(cset_dir, capacity_uuid, capacity_devs);
783 printf("bcache_capacity failed with error: %s\n", err);
788 static char *stats_subdir(char* stats_dir)
793 strcat(tmp, "cache");
794 err = find_matching_uuid(stats_dir, tmp, stats_dev_uuid);
797 } else if(stats_cache_num) {
798 strcat(tmp, "cache");
799 strcat(tmp, stats_cache_num);
800 } else if (stats_five_min)
801 strcat(tmp, "stats_five_minute");
803 strcat(tmp, "stats_hour");
805 strcat(tmp, "stats_day");
806 else if (stats_total)
807 strcat(tmp, "stats_total");
811 strcat(stats_dir, tmp);
817 int bcache_stats(NihCommand *command, char *const *args)
820 char stats_dir[MAX_PATH];
822 struct dirent *ent = NULL;
827 snprintf(stats_dir, MAX_PATH, "%s/%s", cset_dir, stats_uuid);
828 err = stats_subdir(stats_dir);
832 dir = opendir(stats_dir);
834 err = "Failed to open dir";
838 err = "Must provide a cacheset uuid";
842 if(stats_list || stats_all) {
843 while ((ent = readdir(dir)) != NULL) {
844 err = read_stat_dir(dir, stats_dir, ent->d_name, buf);
848 printf("%s\n", ent->d_name);
850 printf("\t%s\n", buf);
854 for (i = 0; args[i] != NULL; i++) {
855 err = read_stat_dir(dir, stats_dir, args[i], buf);
866 printf("bcache_stats error: %s\n", err);
870 int bcache_set_failed(NihCommand *command, char *const *args)
874 if (!dev_failed_uuid) {
875 printf("Pass in a dev uuid\n");
879 err = device_set_failed(dev_failed_uuid);
881 printf("bcache_set_failed_ioctl error: %s\n", err);
888 static NihCommand commands[] = {
889 {"format", N_("format <list of drives>"),
890 "Format one or a list of devices with bcache datastructures."
891 " You need to do this before you create a volume",
892 N_("format drive[s] with bcache"),
893 NULL, make_bcache_options, make_bcache},
894 {"probe", N_("probe <list of devices>"),
895 "Does a blkid_probe on a device",
896 N_("Does a blkid_probe on a device"),
897 NULL, probe_bcache_options, probe_bcache},
898 {"register", N_("register <list of devices>"),
899 "Registers a list of devices",
900 N_("Registers a list of devices"),
901 NULL, bcache_register_options, bcache_register},
902 {"unregister", N_("unregister <list of devices>"),
903 "Unregisters a list of devices",
904 N_("Unregisters a list of devices"),
905 NULL, bcache_unregister_options, bcache_unregister},
906 {"add-devs", N_("add-devs --tier=# <list of devices>"),
907 "Adds a list of devices to a cacheset",
908 N_("Adds a list of devices to a cacheset"),
909 NULL, bcache_add_device_options, bcache_add_devices},
910 {"rm-dev", N_("rm-dev <dev>"),
911 "Removes a device from its cacheset",
912 N_("Removes a device from its cacheset"),
913 NULL, bcache_rm_device_options, bcache_rm_device},
914 {"modify", N_("modify --set=UUID (dev=UUID) name value"),
915 "Modifies attributes related to the cacheset",
916 N_("Modifies attributes related to the cacheset"),
917 NULL, bcache_modify_options, bcache_modify},
918 {"list-cachesets", N_("list-cachesets"),
919 "Lists cachesets in /sys/fs/bcache",
920 N_("Lists cachesets in /sys/fs/bcache"),
921 NULL, list_cachesets_options, bcache_list_cachesets},
922 {"query-devs", N_("query <list of devices>"),
923 "Gives info about the superblock of a list of devices",
924 N_("show superblock on each of the listed drive"),
925 NULL, query_devs_options, bcache_query_devs},
926 {"status", N_("status <list of devices>"),
927 "Finds the status of the most up to date superblock",
928 N_("Finds the status of the most up to date superblock"),
929 NULL, status_options, bcache_status},
930 {"capacity", N_("capacity --set=UUID"),
931 "Shows the capacity of the cacheset",
932 N_("Shows the capacity of the cacheset"),
933 NULL, capacity_options, bcache_capacity},
934 {"stats", N_("stats <list of devices>"),
935 "List various bcache statistics",
936 N_("List various bcache statistics"),
937 NULL, stats_options, bcache_stats},
938 {"set-failed", N_("set-failed --dev=UUID"),
939 "Sets a device to the FAILED state",
940 N_("Sets a device to the FAILED state"),
941 NULL, set_failed_options, bcache_set_failed},
946 int main(int argc, char *argv[])
949 nih_main_init (argv[0]);
951 nih_option_set_synopsis (_("Manage bcache devices"));
952 nih_option_set_help (
953 _("Helps you manage bcache devices"));
955 ret = nih_command_parser (NULL, argc, argv, options, commands);