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 /* bcacheadm globals */
43 EXIT_ERROR = 1, /* General/OS error */
44 EXIT_SHELL = 2, /* Start maintenance shell */
45 EXIT_SHELL_REBOOT = 3, /* Start maintenance shell, reboot when done */
46 EXIT_REBOOT = 4, /* System must reboot */
50 /* make-bcache globals */
53 char *cache_devices[MAX_DEVS];
54 int tier_mapping[MAX_DEVS];
55 char *backing_devices[MAX_DEVS];
56 char *backing_dev_labels[MAX_DEVS];
57 size_t i, nr_backing_devices = 0, nr_cache_devices = 0;
58 unsigned block_size = 0;
59 unsigned bucket_sizes[MAX_DEVS];
60 int num_bucket_sizes = 0;
61 int writeback = 0, discard = 0, wipe_bcache = 0;
62 unsigned replication_set = 0, replacement_policy = 0;
63 uint64_t data_offset = BDEV_DATA_START_DEFAULT;
65 struct cache_sb *cache_set_sb = NULL;
73 const char *cache_set_uuid = 0;
74 const char *csum_type = 0;
75 char *metadata_replicas = 0;
76 char *data_replicas = 0;
80 /* query-dev globals */
81 bool force_csum = false;
82 bool uuid_only = false;
88 char *cset_dir = "/sys/fs/bcache";
89 bool list_devs = false;
92 bool status_all = false;
95 bool stats_all = false;
96 bool stats_list = false;
97 static const char *stats_uuid = NULL;
98 static const char *stats_dev_uuid = NULL;
99 static const char *stats_cache_num = NULL;
100 bool stats_five_min = false;
101 bool stats_hour = false;
102 bool stats_day = false;
103 bool stats_total = false;
105 /* make-bcache option setters */
106 static int set_block_size(NihOption *option, const char *arg)
108 block_size = hatoi_validate(arg, "block size");
112 static int set_cache(NihOption *option, const char *arg)
115 cache_devices[nr_cache_devices] = (char *)malloc(sizeof(char *) *
117 strcpy(cache_devices[nr_cache_devices], arg);
119 tier_mapping[nr_cache_devices] = 0;
121 tier_mapping[nr_cache_devices] = atoi(tier);
127 static int set_bdev(NihOption *option, const char *arg)
132 backing_dev_labels[nr_backing_devices] =
133 (char *)malloc(sizeof(char *) * strlen(label) + 1);
134 strcpy(backing_dev_labels[nr_backing_devices], label);
137 backing_devices[nr_backing_devices] = (char *)malloc(sizeof(char *) *
139 strcpy(backing_devices[nr_backing_devices], arg);
141 nr_backing_devices++;
147 static int set_bucket_sizes(NihOption *option, const char *arg)
149 bucket_sizes[num_bucket_sizes]=hatoi_validate(arg, "bucket size");
155 static int set_udev(NihOption *option, const char *arg)
157 if (strcmp("udev", arg)) {
158 printf("Invalid output format %s\n", arg);
167 static NihOption make_bcache_options[] = {
168 // {int shortoption, char* longoption, char* help, NihOptionGroup, char* argname, void *value, NihOptionSetter}
169 {'C', "cache", N_("Format a cache device"), NULL, "dev", NULL, set_cache},
170 {'B', "bdev", N_("Format a backing device"), NULL, "dev", NULL, set_bdev},
171 {'l', "label", N_("label"), NULL, "label", &label, NULL},
172 //Only one bucket_size supported until a list of bucket sizes is parsed correctly
173 {'b', "bucket", N_("bucket size"), NULL, "size", NULL, set_bucket_sizes},
174 //Does the default setter automatically convert strings to an int?
175 {'w', "block", N_("block size (hard sector size of SSD, often 2k"), NULL,"size", NULL, set_block_size},
176 {'t', "tier", N_("tier of subsequent devices"), NULL,"#", &tier, NULL},
177 {'p', "cache_replacement_policy", N_("one of (lru|fifo|random)"), NULL,"policy", &replacement_policy, NULL},
178 {'o', "data_offset", N_("data offset in sectors"), NULL,"offset", &data_offset, NULL},
180 {0, "cset-uuid", N_("UUID for the cache set"), NULL, "uuid", &cache_set_uuid, NULL},
181 {0, "csum-type", N_("One of (none|crc32c|crc64)"), NULL, "type", &csum_type, NULL },
182 {0, "replication-set",N_("replication set of subsequent devices"), NULL, NULL, &replication_set, NULL },
183 {0, "meta-replicas",N_("number of metadata replicas"), NULL, "#", &metadata_replicas, NULL},
184 {0, "data-replicas",N_("number of data replicas"), NULL, "#", &data_replicas, NULL },
186 {0, "wipe-bcache", N_("destroy existing bcache data if present"), NULL, NULL, &wipe_bcache, NULL},
187 {0, "discard", N_("enable discards"), NULL, NULL, &discard, NULL},
188 {0, "writeback", N_("enable writeback"), NULL, NULL, &writeback, NULL},
193 static NihOption probe_bcache_options[] = {
194 {'o', "udev", N_("udev"), NULL, NULL, NULL, set_udev},
198 static NihOption bcache_register_options[] = {
202 static NihOption bcache_unregister_options[] = {
206 static NihOption query_devs_options[] = {
207 {'f', "force_csum", N_("force_csum"), NULL, NULL, &force_csum, NULL},
208 {'u', "uuid-only", N_("only print out the uuid for the devices, not the whole superblock"), NULL, NULL, &uuid_only, NULL},
212 static NihOption list_cachesets_options[] = {
213 {'d', "dir", N_("directory"), NULL, NULL, &cset_dir, NULL},
214 {0, "list-devs", N_("list all devices in the cache sets as well"), NULL, NULL, &list_devs, NULL},
218 static NihOption status_options[] = {
219 {'a', "all", N_("all"), NULL, NULL, &status_all, NULL},
223 static NihOption stats_options[] = {
224 {'a', "all", N_("all"), NULL, NULL, &stats_all, NULL},
225 {'l', "list", N_("list"), NULL, NULL, &stats_list, NULL},
226 {'u', "set", N_("cache_set UUID"), NULL, "UUID", &stats_uuid, NULL},
227 {'d', "dev", N_("dev UUID"), NULL, "UUID", &stats_dev_uuid, NULL},
228 {'c', "cache", N_("cache number (starts from 0)"), NULL, "CACHE#", &stats_cache_num, NULL},
229 {0, "five-min-stats", N_("stats accumulated in last 5 minutes"), NULL, NULL, &stats_five_min, NULL},
230 {0, "hour-stats", N_("stats accumulated in last hour"), NULL, NULL, &stats_hour, NULL},
231 {0, "day-stats", N_("stats accumulated in last day"), NULL, NULL, &stats_day, NULL},
232 {0, "total-stats", N_("stats accumulated in total"), NULL, NULL, &stats_total, NULL},
236 static NihOption options[] = {
242 int make_bcache (NihCommand *command, char *const *args)
244 int cache_dev_fd[devs];
246 int backing_dev_fd[devs];
248 cache_set_sb = calloc(1, sizeof(*cache_set_sb) +
249 sizeof(struct cache_member) * devs);
251 if (cache_set_uuid) {
252 if(uuid_parse(cache_set_uuid, cache_set_sb->set_uuid.b)) {
253 fprintf(stderr, "Bad uuid\n");
257 uuid_generate(cache_set_sb->set_uuid.b);
261 SET_CACHE_PREFERRED_CSUM_TYPE(cache_set_sb,
262 read_string_list_or_die(csum_type, csum_types,
265 SET_CACHE_PREFERRED_CSUM_TYPE(cache_set_sb, BCH_CSUM_CRC32C);
268 if (metadata_replicas) {
269 SET_CACHE_SET_META_REPLICAS_WANT(cache_set_sb,
270 strtoul_or_die(metadata_replicas,
271 CACHE_SET_META_REPLICAS_WANT_MAX,
274 SET_CACHE_SET_META_REPLICAS_WANT(cache_set_sb, 1);
278 SET_CACHE_SET_DATA_REPLICAS_WANT(cache_set_sb,
279 strtoul_or_die(data_replicas,
280 CACHE_SET_DATA_REPLICAS_WANT_MAX,
283 SET_CACHE_SET_DATA_REPLICAS_WANT(cache_set_sb, 1);
287 fprintf(stderr, "Please specify -C or -B\n");
291 if(!bucket_sizes[0]) bucket_sizes[0] = 1024;
293 for(i = 0; i < nr_cache_devices; i++)
294 next_cache_device(cache_set_sb,
300 if (!cache_set_sb->nr_in_set && !nr_backing_devices) {
301 fprintf(stderr, "Please supply a device\n");
307 if (bucket_sizes[i] < block_size) {
309 "Bucket size cannot be smaller than block size\n");
313 } while (i < num_bucket_sizes);
316 for (i = 0; i < cache_set_sb->nr_in_set; i++)
317 block_size = max(block_size,
318 get_blocksize(cache_devices[i]));
320 for (i = 0; i < nr_backing_devices; i++)
321 block_size = max(block_size,
322 get_blocksize(backing_devices[i]));
325 for (i = 0; i < cache_set_sb->nr_in_set; i++)
326 cache_dev_fd[i] = dev_open(cache_devices[i], wipe_bcache);
328 for (i = 0; i < nr_backing_devices; i++)
329 backing_dev_fd[i] = dev_open(backing_devices[i], wipe_bcache);
331 write_cache_sbs(cache_dev_fd, cache_set_sb, block_size,
332 bucket_sizes, num_bucket_sizes);
334 for (i = 0; i < nr_backing_devices; i++)
335 write_backingdev_sb(backing_dev_fd[i],
336 block_size, bucket_sizes,
337 writeback, data_offset,
338 backing_dev_labels[i],
339 cache_set_sb->set_uuid);
345 int probe_bcache (NihCommand *command, char *const *args)
349 for (i = 0; args[i] != NULL; i++) {
350 probe(args[i], udev);
356 int bcache_register (NihCommand *command, char *const *args)
358 int ret = register_bcache(args);
363 int bcache_unregister (NihCommand *command, char *const *args)
365 int ret = unregister_bcache(args);
370 int bcache_list_cachesets (NihCommand *command, char *const *args)
372 return list_cachesets(cset_dir, list_devs);
375 int bcache_query_devs (NihCommand *command, char *const *args)
379 for (i = 0; args[i] != NULL; i++){
381 query_dev(args[i], force_csum, true, uuid_only, dev_uuid);
383 printf("%s\n", dev_uuid);
387 int bcache_status (NihCommand *command, char *const *args)
390 struct cache_sb *sb_tier0 = NULL, *sb_tier1 = NULL;
391 char *dev0 = NULL, *dev1 = NULL;
393 for (i = 0; args[i] != NULL; i++) {
394 struct cache_sb *sb = query_dev(args[i], false, false, false, NULL);
395 struct cache_member *m = ((struct cache_member *) sb->d) +
397 long long unsigned cache_tier = CACHE_TIER(m);
400 if (!sb_tier0 || sb->seq > sb_tier0->seq) {
404 else if (cache_tier == 1)
405 if (!sb_tier1 || sb->seq > sb_tier1->seq) {
410 if (sb_tier0) sb_state(sb_tier0, dev0);
411 if (sb_tier1) sb_state(sb_tier1, dev1);
414 static void stats_subdir(char* stats_dir)
418 strcat(tmp, "cache");
419 find_matching_uuid(stats_dir, tmp, stats_dev_uuid);
420 } else if(stats_cache_num) {
421 strcat(tmp, "cache");
422 strcat(tmp, stats_cache_num);
423 } else if (stats_five_min)
424 strcat(tmp, "stats_five_minute");
426 strcat(tmp, "stats_hour");
428 strcat(tmp, "stats_day");
429 else if (stats_total)
430 strcat(tmp, "stats_total");
434 strcat(stats_dir, tmp);
437 int bcache_stats (NihCommand *command, char *const *args)
442 struct dirent *ent = NULL;
445 strcpy(stats_dir, cset_dir);
446 strcat(stats_dir, "/");
447 strcat(stats_dir, stats_uuid);
448 stats_subdir(stats_dir);
449 dir = opendir(stats_dir);
451 fprintf(stderr, "Failed to open dir %s\n", cset_dir);
455 printf("Must provide a cacheset uuid\n");
459 if(stats_list || stats_all)
460 while ((ent = readdir(dir)) != NULL)
461 read_stat_dir(dir, stats_dir, ent->d_name, stats_all);
464 for (i = 0; args[i] != NULL; i++)
465 read_stat_dir(dir, stats_dir, args[i], true);
470 static NihCommand commands[] = {
471 {"format", N_("format <list of drives>"),
472 "Format one or a list of devices with bcache datastructures."
473 " You need to do this before you create a volume",
474 N_("format drive[s] with bcache"),
475 NULL, make_bcache_options, make_bcache},
476 {"probe", N_("probe <list of devices>"),
477 "Does a blkid_probe on a device",
478 N_("Does a blkid_probe on a device"),
479 NULL, probe_bcache_options, probe_bcache},
480 {"register", N_("register <list of devices>"),
481 "Registers a list of devices",
482 N_("Registers a list of devices"),
483 NULL, bcache_register_options, bcache_register},
484 {"unregister", N_("unregister <list of devices>"),
485 "Unregisters a list of devices",
486 N_("Unregisters a list of devices"),
487 NULL, bcache_unregister_options, bcache_unregister},
488 {"list-cachesets", N_("list-cachesets"),
489 "Lists cachesets in /sys/fs/bcache",
490 N_("Lists cachesets in /sys/fs/bcache"),
491 NULL, list_cachesets_options, bcache_list_cachesets},
492 {"query-devs", N_("query <list of devices>"),
493 "Gives info about the superblock of a list of devices",
494 N_("show superblock on each of the listed drive"),
495 NULL, query_devs_options, bcache_query_devs},
496 {"status", N_("status <list of devices>"),
497 "Finds the status of the most up to date superblock",
498 N_("Finds the status of the most up to date superblock"),
499 NULL, status_options, bcache_status},
500 {"stats", N_("stats <list of devices>"),
501 "List various bcache statistics",
502 N_("List various bcache statistics"),
503 NULL, stats_options, bcache_stats},
508 int main(int argc, char *argv[])
511 nih_main_init (argv[0]);
513 nih_option_set_synopsis (_("Manage bcache devices"));
514 nih_option_set_help (
515 _("Helps you manage bcache devices"));
517 ret = nih_command_parser (NULL, argc, argv, options, commands);