]> git.sesse.net Git - bcachefs-tools-debian/blob - bcacheadm.c
Set the label field in the cache set superblock if label is
[bcachefs-tools-debian] / bcacheadm.c
1 /*
2  * Authors: Kent Overstreet <kmo@daterainc.com>
3  *          Gabriel de Perthuis <g2p.code@gmail.com>
4  *          Jacob Malevich <jam@datera.io>
5  *
6  * GPLv2
7  */
8
9 #include <nih/option.h>
10 #include <nih/command.h>
11 #include <nih/main.h>
12 #include <nih/logging.h>
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <ctype.h>
16 #include <errno.h>
17 #include <inttypes.h>
18 #include <limits.h>
19 #include <fcntl.h>
20 #include <unistd.h>
21 #include <stdbool.h>
22 #include <stdint.h>
23 #include <blkid.h>
24 #include <string.h>
25 #include <linux/fs.h>
26 #include <sys/ioctl.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <uuid/uuid.h>
30 #include <dirent.h>
31 #include <bcache.h> //libbcache
32
33 #define PACKAGE_NAME "bcacheadm"
34 #define PACKAGE_VERSION "1.0"
35 #define PACKAGE_BUGREPORT "bugreport"
36
37 #define MAX_DEVS MAX_CACHES_PER_SET
38
39
40 /* bcacheadm globals */
41 enum exit {
42         EXIT_OK = 0,            /* Ok */
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 */
47 };
48
49
50 /* make-bcache globals */
51 int bdev = -1;
52 int devs = 0;
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;
64 char *label = NULL;
65 struct cache_sb *cache_set_sb = NULL;
66 enum long_opts {
67         CACHE_SET_UUID = 256,
68         CSUM_TYPE,
69         REPLICATION_SET,
70         META_REPLICAS,
71         DATA_REPLICAS,
72 };
73 const char *cache_set_uuid = 0;
74 const char *csum_type = 0;
75 char *metadata_replicas = 0;
76 char *data_replicas = 0;
77 char *tier = 0;
78
79
80 /* query-dev globals */
81 bool force_csum = false;
82 bool uuid_only = false;
83
84 /* probe globals */
85 bool udev = false;
86
87 /* list globals */
88 char *cset_dir = "/sys/fs/bcache";
89 bool list_devs = false;
90
91 /* status globals */
92 bool status_all = false;
93
94 /* stats globals */
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;
104
105 /* make-bcache option setters */
106 static int set_block_size(NihOption *option, const char *arg)
107 {
108         block_size = hatoi_validate(arg, "block size");
109         return 0;
110 }
111
112 static int set_cache(NihOption *option, const char *arg)
113 {
114         bdev = 0;
115         cache_devices[nr_cache_devices] = strdup(arg);
116         if(!tier)
117                 tier_mapping[nr_cache_devices] = 0;
118         else {
119                 int ntier = atoi(tier);
120                 if(ntier == 0 || ntier == 1)
121                         tier_mapping[nr_cache_devices] = ntier;
122                 else
123                         printf("Invalid tier\n");
124         }
125
126         devs++;
127         nr_cache_devices++;
128 }
129
130 static int set_bdev(NihOption *option, const char *arg)
131 {
132         bdev = 1;
133
134         if(label)
135                 backing_dev_labels[nr_backing_devices] = strdup(label);
136
137         backing_devices[nr_backing_devices] = strdup(arg);
138
139         nr_backing_devices++;
140         devs++;
141
142         return 0;
143 }
144
145 static int set_bucket_sizes(NihOption *option, const char *arg)
146 {
147         bucket_sizes[num_bucket_sizes]=hatoi_validate(arg, "bucket size");
148         num_bucket_sizes++;
149         return 0;
150 }
151
152 /* probe setters */
153 static int set_udev(NihOption *option, const char *arg)
154 {
155         if (strcmp("udev", arg)) {
156                 printf("Invalid output format %s\n", arg);
157                 exit(EXIT_FAILURE);
158         }
159         udev = true;
160         return 0;
161 }
162
163
164 /* options */
165 static NihOption make_bcache_options[] = {
166 //      {int shortoption, char* longoption, char* help, NihOptionGroup, char* argname, void *value, NihOptionSetter}
167         {'C', "cache",  N_("Format a cache device"), NULL, "dev", NULL, set_cache},
168         {'B', "bdev",   N_("Format a backing device"), NULL, "dev", NULL, set_bdev},
169         {'l', "label",  N_("label"), NULL, "label", &label, NULL},
170         //Only one bucket_size supported until a list of bucket sizes is parsed correctly
171         {'b', "bucket", N_("bucket size"), NULL, "size", NULL, set_bucket_sizes},
172         //Does the default setter automatically convert strings to an int?
173         {'w', "block",  N_("block size (hard sector size of SSD, often 2k"), NULL,"size", NULL, set_block_size},
174         {'t', "tier",   N_("tier of subsequent devices"), NULL,"#", &tier, NULL},
175         {'p', "cache_replacement_policy", N_("one of (lru|fifo|random)"), NULL,"policy", &replacement_policy, NULL},
176         {'o', "data_offset", N_("data offset in sectors"), NULL,"offset", &data_offset, NULL},
177
178         {0, "cset-uuid",        N_("UUID for the cache set"),           NULL,   "uuid", &cache_set_uuid, NULL},
179         {0, "csum-type",        N_("One of (none|crc32c|crc64)"),               NULL,   "type", &csum_type, NULL },
180         {0, "replication-set",N_("replication set of subsequent devices"),      NULL,   NULL, &replication_set, NULL },
181         {0, "meta-replicas",N_("number of metadata replicas"),          NULL,   "#", &metadata_replicas, NULL},
182         {0, "data-replicas",N_("number of data replicas"),              NULL,   "#", &data_replicas, NULL },
183
184         {0, "wipe-bcache",      N_("destroy existing bcache data if present"),          NULL, NULL, &wipe_bcache, NULL},
185         {0, "discard",          N_("enable discards"),          NULL, NULL, &discard,           NULL},
186         {0, "writeback",        N_("enable writeback"),         NULL, NULL, &writeback,         NULL},
187
188         NIH_OPTION_LAST
189 };
190
191 static NihOption probe_bcache_options[] = {
192         {'o', "udev", N_("udev"), NULL, NULL, NULL, set_udev},
193         NIH_OPTION_LAST
194 };
195
196 static NihOption bcache_register_options[] = {
197         NIH_OPTION_LAST
198 };
199
200 static NihOption bcache_unregister_options[] = {
201         NIH_OPTION_LAST
202 };
203
204 static NihOption query_devs_options[] = {
205         {'f', "force_csum", N_("force_csum"), NULL, NULL, &force_csum, NULL},
206         {'u', "uuid-only", N_("only print out the uuid for the devices, not the whole superblock"), NULL, NULL, &uuid_only, NULL},
207         NIH_OPTION_LAST
208 };
209
210 static NihOption list_cachesets_options[] = {
211         {'d', "dir", N_("directory"), NULL, NULL, &cset_dir, NULL},
212         {0, "list-devs", N_("list all devices in the cache sets as well"), NULL, NULL, &list_devs, NULL},
213         NIH_OPTION_LAST
214 };
215
216 static NihOption status_options[] = {
217         {'a', "all", N_("all"), NULL, NULL, &status_all, NULL},
218         NIH_OPTION_LAST
219 };
220
221 static NihOption stats_options[] = {
222         {'a', "all", N_("all"), NULL, NULL, &stats_all, NULL},
223         {'l', "list", N_("list"), NULL, NULL, &stats_list, NULL},
224         {'u', "set", N_("cache_set UUID"), NULL, "UUID", &stats_uuid, NULL},
225         {'d', "dev", N_("dev UUID"), NULL, "UUID", &stats_dev_uuid, NULL},
226         {'c', "cache", N_("cache number (starts from 0)"), NULL, "CACHE#", &stats_cache_num, NULL},
227         {0, "five-min-stats", N_("stats accumulated in last 5 minutes"), NULL, NULL, &stats_five_min, NULL},
228         {0, "hour-stats", N_("stats accumulated in last hour"), NULL, NULL, &stats_hour, NULL},
229         {0, "day-stats", N_("stats accumulated in last day"), NULL, NULL, &stats_day, NULL},
230         {0, "total-stats", N_("stats accumulated in total"), NULL, NULL, &stats_total, NULL},
231         NIH_OPTION_LAST
232 };
233
234 static NihOption options[] = {
235         NIH_OPTION_LAST
236 };
237
238
239 /* commands */
240 int make_bcache(NihCommand *command, char *const *args)
241 {
242         int cache_dev_fd[devs];
243
244         int backing_dev_fd[devs];
245
246         cache_set_sb = calloc(1, sizeof(*cache_set_sb) +
247                                      sizeof(struct cache_member) * devs);
248
249         if (cache_set_uuid) {
250                 if(uuid_parse(cache_set_uuid, cache_set_sb->set_uuid.b)) {
251                         fprintf(stderr, "Bad uuid\n");
252                         return -1;
253                 }
254         } else {
255                 uuid_generate(cache_set_sb->set_uuid.b);
256         }
257
258         if (label) 
259                 memcpy(cache_set_sb->label, label, sizeof(cache_set_sb->label));
260
261         if (csum_type) {
262                 SET_CACHE_PREFERRED_CSUM_TYPE(cache_set_sb,
263                                 read_string_list_or_die(csum_type, csum_types,
264                                         "csum type"));
265         } else {
266                 SET_CACHE_PREFERRED_CSUM_TYPE(cache_set_sb, BCH_CSUM_CRC32C);
267         }
268
269         if (metadata_replicas) {
270                 SET_CACHE_SET_META_REPLICAS_WANT(cache_set_sb,
271                                 strtoul_or_die(metadata_replicas,
272                                         CACHE_SET_META_REPLICAS_WANT_MAX,
273                                         "meta replicas"));
274         } else {
275                 SET_CACHE_SET_META_REPLICAS_WANT(cache_set_sb, 1);
276         }
277
278         if (data_replicas) {
279                 SET_CACHE_SET_DATA_REPLICAS_WANT(cache_set_sb,
280                         strtoul_or_die(data_replicas,
281                                 CACHE_SET_DATA_REPLICAS_WANT_MAX,
282                                 "data replicas"));
283         } else {
284                 SET_CACHE_SET_DATA_REPLICAS_WANT(cache_set_sb, 1);
285         }
286
287         if (bdev == -1) {
288                 fprintf(stderr, "Please specify -C or -B\n");
289                 exit(EXIT_FAILURE);
290         }
291
292         if(!bucket_sizes[0]) bucket_sizes[0] = 1024;
293
294         for(i = 0; i < nr_cache_devices; i++)
295                 next_cache_device(cache_set_sb,
296                                   replication_set,
297                                   tier_mapping[i],
298                                   replacement_policy,
299                                   discard);
300
301         if (!cache_set_sb->nr_in_set && !nr_backing_devices) {
302                 fprintf(stderr, "Please supply a device\n");
303                 exit(EXIT_FAILURE);
304         }
305
306         i = 0;
307         do {
308                 if (bucket_sizes[i] < block_size) {
309                         fprintf(stderr,
310                         "Bucket size cannot be smaller than block size\n");
311                         exit(EXIT_FAILURE);
312                 }
313                 i++;
314         } while (i < num_bucket_sizes);
315
316         if (!block_size) {
317                 for (i = 0; i < cache_set_sb->nr_in_set; i++)
318                         block_size = max(block_size,
319                                          get_blocksize(cache_devices[i]));
320
321                 for (i = 0; i < nr_backing_devices; i++)
322                         block_size = max(block_size,
323                                          get_blocksize(backing_devices[i]));
324         }
325
326         for (i = 0; i < cache_set_sb->nr_in_set; i++)
327                 cache_dev_fd[i] = dev_open(cache_devices[i], wipe_bcache);
328
329         for (i = 0; i < nr_backing_devices; i++)
330                 backing_dev_fd[i] = dev_open(backing_devices[i], wipe_bcache);
331
332         write_cache_sbs(cache_dev_fd, cache_set_sb, block_size,
333                                         bucket_sizes, num_bucket_sizes);
334
335         for (i = 0; i < nr_backing_devices; i++)
336                 write_backingdev_sb(backing_dev_fd[i],
337                                     block_size, bucket_sizes,
338                                     writeback, data_offset,
339                                     backing_dev_labels[i],
340                                     cache_set_sb->set_uuid);
341
342
343         return 0;
344 }
345
346 int probe_bcache(NihCommand *command, char *const *args)
347 {
348         int i;
349         char *err = NULL;
350
351         for (i = 0; args[i] != NULL; i++) {
352                 err = probe(args[i], udev);
353                 if(err) {
354                         printf("probe_bcache error: %s\n", err);
355                         return -1;
356                 }
357         }
358
359         return 0;
360 }
361
362 int bcache_register(NihCommand *command, char *const *args)
363 {
364         char *err = NULL;
365
366         err = register_bcache(args);
367         if (err) {
368                 printf("bcache_register error: %s\n", err);
369                 return -1;
370         }
371
372         return 0;
373 }
374
375 int bcache_unregister(NihCommand *command, char *const *args)
376 {
377         char *err = NULL;
378
379         err = unregister_bcache(args);
380         if (err) {
381                 printf("bcache_unregister error: %s\n", err);
382                 return -1;
383         }
384
385         return 0;
386 }
387
388 int bcache_list_cachesets(NihCommand *command, char *const *args)
389 {
390         char *err = NULL;
391         err = list_cachesets(cset_dir, list_devs);
392         if (err) {
393                 printf("bcache_list_cachesets error :%s\n", err);
394                 return -1;
395         }
396
397         return 0;
398 }
399
400 int bcache_query_devs(NihCommand *command, char *const *args)
401 {
402         int i;
403
404         for (i = 0; args[i] != NULL; i++){
405                 char dev_uuid[40];
406                 query_dev(args[i], force_csum, true, uuid_only, dev_uuid);
407                 if(uuid_only)
408                         printf("%s\n", dev_uuid);
409         }
410 }
411
412 int bcache_status(NihCommand *command, char *const *args)
413 {
414         int i;
415         struct cache_sb *sb_tier0 = NULL, *sb_tier1 = NULL;
416         char *dev0 = NULL, *dev1 = NULL;
417
418         for (i = 0; args[i] != NULL; i++) {
419                 struct cache_sb *sb = query_dev(args[i], false, false, false, NULL);
420                 struct cache_member *m = ((struct cache_member *) sb->d) +
421                         sb->nr_this_dev;
422                 long long unsigned cache_tier = CACHE_TIER(m);
423
424                 if (!cache_tier)
425                         if (!sb_tier0 || sb->seq > sb_tier0->seq) {
426                                 sb_tier0 = sb;
427                                 dev0 = args[i];
428                         }
429                 else if (cache_tier == 1)
430                         if (!sb_tier1 || sb->seq > sb_tier1->seq) {
431                                 sb_tier1 = sb;
432                                 dev1 = args[i];
433                         }
434         }
435         if (sb_tier0) sb_state(sb_tier0, dev0);
436         if (sb_tier1) sb_state(sb_tier1, dev1);
437 }
438
439 static char *stats_subdir(char* stats_dir)
440 {
441         char tmp[50] = "/";
442         char *err = NULL;
443         if(stats_dev_uuid) {
444                 strcat(tmp, "cache");
445                 err = find_matching_uuid(stats_dir, tmp, stats_dev_uuid);
446                 if(err)
447                         goto err;
448         } else if(stats_cache_num) {
449                 strcat(tmp, "cache");
450                 strcat(tmp, stats_cache_num);
451         } else if (stats_five_min)
452                 strcat(tmp, "stats_five_minute");
453         else if (stats_hour)
454                 strcat(tmp, "stats_hour");
455         else if (stats_day)
456                 strcat(tmp, "stats_day");
457         else if (stats_total)
458                 strcat(tmp, "stats_total");
459         else
460                 return err;
461
462         strcat(stats_dir, tmp);
463
464 err:
465         return err;
466 }
467
468 int bcache_stats(NihCommand *command, char *const *args)
469 {
470         int i;
471         char stats_dir[MAX_PATH];
472         DIR *dir = NULL;
473         struct dirent *ent = NULL;
474         char *err = NULL;
475
476         if (stats_uuid) {
477                 snprintf(stats_dir, MAX_PATH, "%s/%s", cset_dir, stats_uuid);
478                 err = stats_subdir(stats_dir);
479                 if(err)
480                         goto err;
481
482                 dir = opendir(stats_dir);
483                 if (!dir) {
484                         err = "Failed to open dir";
485                         goto err;
486                 }
487         } else {
488                 err = "Must provide a cacheset uuid";
489                 goto err;
490         }
491
492         if(stats_list || stats_all) {
493                 while ((ent = readdir(dir)) != NULL) {
494                         err = read_stat_dir(dir, stats_dir, ent->d_name, stats_all);
495                         if (err)
496                                 goto err;
497                 }
498         }
499
500
501         for (i = 0; args[i] != NULL; i++) {
502                 err = read_stat_dir(dir, stats_dir, args[i], true);
503                 if (err)
504                         goto err;
505         }
506
507         closedir(dir);
508         return 0;
509
510 err:
511         closedir(dir);
512         printf("bcache_stats error: %s\n", err);
513         return -1;
514 }
515
516 static NihCommand commands[] = {
517         {"format", N_("format <list of drives>"),
518                   "Format one or a list of devices with bcache datastructures."
519                   " You need to do this before you create a volume",
520                   N_("format drive[s] with bcache"),
521                   NULL, make_bcache_options, make_bcache},
522         {"probe", N_("probe <list of devices>"),
523                   "Does a blkid_probe on a device",
524                   N_("Does a blkid_probe on a device"),
525                   NULL, probe_bcache_options, probe_bcache},
526         {"register", N_("register <list of devices>"),
527                      "Registers a list of devices",
528                      N_("Registers a list of devices"),
529                      NULL, bcache_register_options, bcache_register},
530         {"unregister", N_("unregister <list of devices>"),
531                      "Unregisters a list of devices",
532                      N_("Unregisters a list of devices"),
533                      NULL, bcache_unregister_options, bcache_unregister},
534         {"list-cachesets", N_("list-cachesets"),
535                            "Lists cachesets in /sys/fs/bcache",
536                            N_("Lists cachesets in /sys/fs/bcache"),
537                            NULL, list_cachesets_options, bcache_list_cachesets},
538         {"query-devs", N_("query <list of devices>"),
539                        "Gives info about the superblock of a list of devices",
540                        N_("show superblock on each of the listed drive"),
541                        NULL, query_devs_options, bcache_query_devs},
542         {"status", N_("status <list of devices>"),
543                    "Finds the status of the most up to date superblock",
544                    N_("Finds the status of the most up to date superblock"),
545                    NULL, status_options, bcache_status},
546         {"stats", N_("stats <list of devices>"),
547                   "List various bcache statistics",
548                   N_("List various bcache statistics"),
549                   NULL, stats_options, bcache_stats},
550         NIH_COMMAND_LAST
551 };
552
553
554 int main(int argc, char *argv[])
555 {
556         int ret = 0;
557         nih_main_init (argv[0]);
558
559         nih_option_set_synopsis (_("Manage bcache devices"));
560         nih_option_set_help (
561                         _("Helps you manage bcache devices"));
562
563         ret = nih_command_parser (NULL, argc, argv, options, commands);
564         if (ret < 0)
565                 exit (1);
566
567         nih_signal_reset();
568 }