]> git.sesse.net Git - bcachefs-tools-debian/blob - bcacheadm.c
bcacheadm: format bug fixes
[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 unsigned 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 unsigned tier = 0;
78
79
80 /* super-show globals */
81 bool force_csum = false;
82
83 /* probe globals */
84 bool udev = false;
85
86 /* list globals */
87 char *cset_dir = "/sys/fs/bcache";
88
89 /* status globals */
90 bool status_all = false;
91
92 /* stats globals */
93 bool stats_all = false;
94 bool stats_list = false;
95 static const char *stats_uuid = NULL;
96 static const char *stats_cache_num = NULL;
97 bool stats_five_min = false;
98 bool stats_hour = false;
99 bool stats_day = false;
100 bool stats_total = false;
101
102 /* make-bcache option setters */
103 static int set_block_size(NihOption *option, const char *arg)
104 {
105         block_size = hatoi_validate(arg, "block size");
106         return 0;
107 }
108
109 static int set_cache(NihOption *option, const char *arg)
110 {
111         bdev = 0;
112         cache_devices[nr_cache_devices] = (char *)malloc(sizeof(char *) *
113                         strlen(arg) + 1);
114         strcpy(cache_devices[nr_cache_devices], arg);
115         tier_mapping[nr_cache_devices] = tier;
116
117         devs++;
118         nr_cache_devices++;
119 }
120
121 static int set_bdev(NihOption *option, const char *arg)
122 {
123         bdev = 1;
124
125         backing_dev_labels[nr_backing_devices] = (char *)malloc(sizeof(char *) *
126                         strlen(label) + 1);
127         strcpy(backing_dev_labels[nr_backing_devices], label);
128
129         backing_devices[nr_backing_devices] = (char *)malloc(sizeof(char *) *
130                         strlen(arg) + 1);
131         strcpy(backing_devices[nr_backing_devices], arg);
132
133         nr_backing_devices++;
134         devs++;
135
136         return 0;
137 }
138
139 static int set_bucket_sizes(NihOption *option, const char *arg)
140 {
141         bucket_sizes[num_bucket_sizes]=hatoi_validate(arg, "bucket size");
142         num_bucket_sizes++;
143         return 0;
144 }
145
146 /* probe setters */
147 static int set_udev(NihOption *option, const char *arg)
148 {
149         if (strcmp("udev", arg)) {
150                 printf("Invalid output format %s\n", arg);
151                 exit(EXIT_FAILURE);
152         }
153         udev = true;
154         return 0;
155 }
156
157
158 /* options */
159 static NihOption make_bcache_options[] = {
160 //      {int shortoption, char* longoption, char* help, NihOptionGroup, char* argname, void *value, NihOptionSetter}
161         {'C', "cache",  N_("Format a cache device"), NULL, "dev", NULL, set_cache},
162         {'B', "bdev",   N_("Format a backing device"), NULL, "dev", NULL, set_bdev},
163         {'l', "label",  N_("label"), NULL, "label", &label, NULL},
164         //Only one bucket_size supported until a list of bucket sizes is parsed correctly
165         {'b', "bucket", N_("bucket size"), NULL, "size", NULL, set_bucket_sizes},
166         //Does the default setter automatically convert strings to an int?
167         {'w', "block",  N_("block size (hard sector size of SSD, often 2k"), NULL,"size", NULL, set_block_size},
168         {'t', "tier",   N_("tier of subsequent devices"), NULL,"#", &tier, NULL},
169         {'p', "cache_replacement_policy", N_("one of (lru|fifo|random)"), NULL,"policy", &replacement_policy, NULL},
170         {'o', "data_offset", N_("data offset in sectors"), NULL,"offset", &data_offset, NULL},
171
172         {0, "cset-uuid",        N_("UUID for the cache set"),           NULL,   "uuid", &cache_set_uuid, NULL},
173         {0, "csum-type",        N_("One of (none|crc32c|crc64)"),               NULL,   "type", &csum_type, NULL },
174         {0, "replication-set",N_("replication set of subsequent devices"),      NULL,   NULL, &replication_set, NULL },
175         {0, "meta-replicas",N_("number of metadata replicas"),          NULL,   "#", &metadata_replicas, NULL},
176         {0, "data-replicas",N_("number of data replicas"),              NULL,   "#", &data_replicas, NULL },
177
178         {0, "wipe-bcache",      N_("destroy existing bcache data if present"),          NULL, NULL, &wipe_bcache, NULL},
179         {0, "discard",          N_("enable discards"),          NULL, NULL, &discard,           NULL},
180         {0, "writeback",        N_("enable writeback"),         NULL, NULL, &writeback,         NULL},
181
182         NIH_OPTION_LAST
183 };
184
185 static NihOption probe_bcache_options[] = {
186         {'o', "udev", N_("udev"), NULL, NULL, NULL, set_udev},
187         NIH_OPTION_LAST
188 };
189
190 static NihOption bcache_register_options[] = {
191         NIH_OPTION_LAST
192 };
193
194 static NihOption query_devs_options[] = {
195         {'f', "force_csum", N_("force_csum"), NULL, NULL, &force_csum, NULL},
196         NIH_OPTION_LAST
197 };
198
199 static NihOption list_cachesets_options[] = {
200         {'d', "dir", N_("directory"), NULL, NULL, &cset_dir, NULL},
201         NIH_OPTION_LAST
202 };
203
204 static NihOption status_options[] = {
205         {'a', "all", N_("all"), NULL, NULL, &status_all, NULL},
206         NIH_OPTION_LAST
207 };
208
209 static NihOption stats_options[] = {
210         {'a', "all", N_("all"), NULL, NULL, &stats_all, NULL},
211         {'l', "list", N_("list"), NULL, NULL, &stats_list, NULL},
212         {'u', "uuid", N_("cache_set UUID"), NULL, "UUID", &stats_uuid, NULL},
213         {'c', "cache", N_("cache number (starts from 0)"), NULL, "CACHE#", &stats_cache_num, NULL},
214         {0, "five-min-stats", N_("stats accumulated in last 5 minutes"), NULL, NULL, &stats_five_min, NULL},
215         {0, "hour-stats", N_("stats accumulated in last hour"), NULL, NULL, &stats_hour, NULL},
216         {0, "day-stats", N_("stats accumulated in last day"), NULL, NULL, &stats_day, NULL},
217         {0, "total-stats", N_("stats accumulated in total"), NULL, NULL, &stats_total, NULL},
218         NIH_OPTION_LAST
219 };
220
221 static NihOption options[] = {
222         NIH_OPTION_LAST
223 };
224
225
226 /* commands */
227 int make_bcache (NihCommand *command, char *const *args)
228 {
229         int cache_dev_fd[devs];
230
231         int backing_dev_fd[devs];
232         printf("meta: %s, data: %s", metadata_replicas, data_replicas);
233
234         cache_set_sb = calloc(1, sizeof(*cache_set_sb) +
235                                      sizeof(struct cache_member) * devs);
236
237         if (cache_set_uuid) {
238                 if(uuid_parse(cache_set_uuid, cache_set_sb->set_uuid.b)) {
239                         fprintf(stderr, "Bad uuid\n");
240                         return -1;
241                 }
242         } else {
243                 uuid_generate(cache_set_sb->set_uuid.b);
244         }
245
246         if (csum_type) {
247                 SET_CACHE_PREFERRED_CSUM_TYPE(cache_set_sb,
248                                 read_string_list_or_die(csum_type, csum_types,
249                                         "csum type"));
250         } else {
251                 SET_CACHE_PREFERRED_CSUM_TYPE(cache_set_sb, BCH_CSUM_CRC32C);
252         }
253
254         if (metadata_replicas) {
255                 SET_CACHE_SET_META_REPLICAS_WANT(cache_set_sb,
256                                 strtoul_or_die(metadata_replicas,
257                                         CACHE_SET_META_REPLICAS_WANT_MAX,
258                                         "meta replicas"));
259         } else {
260                 SET_CACHE_SET_META_REPLICAS_WANT(cache_set_sb, 1);
261         }
262
263         if (data_replicas) {
264                 SET_CACHE_SET_DATA_REPLICAS_WANT(cache_set_sb,
265                         strtoul_or_die(data_replicas,
266                                 CACHE_SET_DATA_REPLICAS_WANT_MAX,
267                                 "data replicas"));
268         } else {
269                 SET_CACHE_SET_DATA_REPLICAS_WANT(cache_set_sb, 1);
270         }
271
272         if (bdev == -1) {
273                 fprintf(stderr, "Please specify -C or -B\n");
274                 exit(EXIT_FAILURE);
275         }
276
277         if(!bucket_sizes[0]) bucket_sizes[0] = 1024;
278
279
280         for(i = 0; i < nr_cache_devices; i++)
281                 next_cache_device(cache_set_sb,
282                                   replication_set,
283                                   tier_mapping[i],
284                                   replacement_policy,
285                                   discard);
286
287         if (!cache_set_sb->nr_in_set && !nr_backing_devices) {
288                 fprintf(stderr, "Please supply a device\n");
289                 exit(EXIT_FAILURE);
290         }
291
292         i = 0;
293         do {
294                 if (bucket_sizes[i] < block_size) {
295                         fprintf(stderr,
296                         "Bucket size cannot be smaller than block size\n");
297                         exit(EXIT_FAILURE);
298                 }
299                 i++;
300         } while (i < num_bucket_sizes);
301
302         if (!block_size) {
303                 for (i = 0; i < cache_set_sb->nr_in_set; i++)
304                         block_size = max(block_size,
305                                          get_blocksize(cache_devices[i]));
306
307                 for (i = 0; i < nr_backing_devices; i++)
308                         block_size = max(block_size,
309                                          get_blocksize(backing_devices[i]));
310         }
311
312         for (i = 0; i < cache_set_sb->nr_in_set; i++)
313                 cache_dev_fd[i] = dev_open(cache_devices[i], wipe_bcache);
314
315         for (i = 0; i < nr_backing_devices; i++)
316                 backing_dev_fd[i] = dev_open(backing_devices[i], wipe_bcache);
317
318         write_cache_sbs(cache_dev_fd, cache_set_sb, block_size,
319                                         bucket_sizes, num_bucket_sizes);
320
321         for (i = 0; i < nr_backing_devices; i++)
322                 write_backingdev_sb(backing_dev_fd[i],
323                                     block_size, bucket_sizes,
324                                     writeback, data_offset,
325                                     backing_dev_labels[i],
326                                     cache_set_sb->set_uuid);
327
328
329         return 0;
330 }
331
332 int probe_bcache (NihCommand *command, char *const *args)
333 {
334         int i;
335
336         for (i = 0; args[i] != NULL; i++) {
337                 probe(args[i], udev);
338         }
339
340         return 0;
341 }
342
343 int bcache_register (NihCommand *command, char *const *args)
344 {
345         int ret;
346         char *arg_list = parse_array_to_list(args);
347
348         if(arg_list) {
349                 ret = register_bcache(arg_list);
350                 free(arg_list);
351         }
352
353         return ret;
354 }
355
356 int bcache_list_cachesets (NihCommand *command, char *const *args)
357 {
358         return list_cachesets(cset_dir);
359 }
360
361 int bcache_query_devs (NihCommand *command, char *const *args)
362 {
363         int i;
364
365
366         for (i = 0; args[i] != NULL; i++) {
367                 printf("query-devs on :%s\n", args[i]);
368                 struct cache_sb *sb = query_dev(args[i], false);
369                 print_dev_info(sb, force_csum);
370         }
371 }
372
373 int bcache_status (NihCommand *command, char *const *args)
374 {
375         int i;
376         struct cache_sb *sb_tier0 = NULL, *sb_tier1 = NULL;
377         char *dev0 = NULL, *dev1 = NULL;
378
379         for (i = 0; args[i] != NULL; i++) {
380                 struct cache_sb *sb = query_dev(args[i], false);
381                 struct cache_member *m = ((struct cache_member *) sb->d) +
382                         sb->nr_this_dev;
383                 long long unsigned cache_tier = CACHE_TIER(m);
384
385                 if (!cache_tier)
386                         if (!sb_tier0 || sb->seq > sb_tier0->seq) {
387                                 sb_tier0 = sb;
388                                 dev0 = args[i];
389                         }
390                 else if (cache_tier == 1)
391                         if (!sb_tier1 || sb->seq > sb_tier1->seq) {
392                                 sb_tier1 = sb;
393                                 dev1 = args[i];
394                         }
395         }
396         if (sb_tier0) sb_state(sb_tier0, dev0);
397         if (sb_tier1) sb_state(sb_tier1, dev1);
398 }
399
400 static void stats_subdir(char* stats_dir)
401 {
402         char tmp[50] = "/";
403         if(stats_cache_num) {
404                 strcat(tmp, "cache");
405                 strcat(tmp, stats_cache_num);
406         } else if (stats_five_min)
407                 strcat(tmp, "stats_five_minute");
408         else if (stats_hour)
409                 strcat(tmp, "stats_hour");
410         else if (stats_day)
411                 strcat(tmp, "stats_day");
412         else if (stats_total)
413                 strcat(tmp, "stats_total");
414         else
415                 return;
416
417         strcat(stats_dir, tmp);
418 }
419
420 int bcache_stats (NihCommand *command, char *const *args)
421 {
422         int i;
423         char stats_dir[200];
424         DIR *dir = NULL;
425         struct dirent *ent = NULL;
426
427         if (stats_uuid) {
428                 strcpy(stats_dir, cset_dir);
429                 strcat(stats_dir, "/");
430                 strcat(stats_dir, stats_uuid);
431                 stats_subdir(stats_dir);
432                 dir = opendir(stats_dir);
433                 if (!dir) {
434                         fprintf(stderr, "Failed to open dir %s\n", cset_dir);
435                         return 1;
436                 }
437         } else {
438                 printf("Must provide a cacheset uuid\n");
439                 exit(EXIT_FAILURE);
440         }
441
442         if(stats_list || stats_all)
443                 while ((ent = readdir(dir)) != NULL)
444                         read_stat_dir(dir, stats_dir, ent->d_name, stats_all);
445
446
447         for (i = 0; args[i] != NULL; i++)
448                 read_stat_dir(dir, stats_dir, args[i], true);
449
450         closedir(dir);
451 }
452
453 static NihCommand commands[] = {
454         {"format", N_("format <list of drives>"),
455                   "Format one or a list of devices with bcache datastructures."
456                   " You need to do this before you create a volume",
457                   N_("format drive[s] with bcache"),
458                   NULL, make_bcache_options, make_bcache},
459         {"probe", N_("probe <list of devices>"),
460                   "Does a blkid_probe on a device",
461                   N_("Does a blkid_probe on a device"),
462                   NULL, probe_bcache_options, probe_bcache},
463         {"register", N_("register <list of devices>"),
464                      "Registers a list of devices",
465                      N_("Registers a list of devices"),
466                      NULL, bcache_register_options, bcache_register},
467         {"list-cachesets", N_("list-cachesets"),
468                            "Lists cachesets in /sys/fs/bcache",
469                            N_("Lists cachesets in /sys/fs/bcache"),
470                            NULL, list_cachesets_options, bcache_list_cachesets},
471         {"query-devs", N_("query <list of devices>"),
472                        "Gives info about the superblock of a list of devices",
473                        N_("show superblock on each of the listed drive"),
474                        NULL, query_devs_options, bcache_query_devs},
475         {"status", N_("status <list of devices>"),
476                    "Finds the status of the most up to date superblock",
477                    N_("Finds the status of the most up to date superblock"),
478                    NULL, status_options, bcache_status},
479         {"stats", N_("stats <list of devices>"),
480                   "List various bcache statistics",
481                   N_("List various bcache statistics"),
482                   NULL, stats_options, bcache_stats},
483         NIH_COMMAND_LAST
484 };
485
486
487 int main(int argc, char *argv[])
488 {
489         int ret = 0;
490         nih_main_init (argv[0]);
491
492         nih_option_set_synopsis (_("Manage bcache devices"));
493         nih_option_set_help (
494                         _("Helps you manage bcache devices"));
495
496         ret = nih_command_parser (NULL, argc, argv, options, commands);
497         if (ret < 0)
498                 exit (1);
499
500         nih_signal_reset();
501 }