Add basic RAID[56] support
[platform/upstream/btrfs-progs.git] / cmds-balance.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public
4  * License v2 as published by the Free Software Foundation.
5  *
6  * This program is distributed in the hope that it will be useful,
7  * but WITHOUT ANY WARRANTY; without even the implied warranty of
8  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
9  * General Public License for more details.
10  *
11  * You should have received a copy of the GNU General Public
12  * License along with this program; if not, write to the
13  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
14  * Boston, MA 021110-1307, USA.
15  */
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21 #include <getopt.h>
22 #include <sys/ioctl.h>
23 #include <errno.h>
24
25 #include "kerncompat.h"
26 #include "ctree.h"
27 #include "ioctl.h"
28 #include "volumes.h"
29
30 #include "commands.h"
31
32 static const char * const balance_cmd_group_usage[] = {
33         "btrfs [filesystem] balance <command> [options] <path>",
34         "btrfs [filesystem] balance <path>",
35         NULL
36 };
37
38 static const char balance_cmd_group_info[] =
39         "'btrfs filesystem balance' command is deprecated, please use\n"
40         "'btrfs balance start' command instead.";
41
42 static int parse_one_profile(const char *profile, u64 *flags)
43 {
44         if (!strcmp(profile, "raid0")) {
45                 *flags |= BTRFS_BLOCK_GROUP_RAID0;
46         } else if (!strcmp(profile, "raid1")) {
47                 *flags |= BTRFS_BLOCK_GROUP_RAID1;
48         } else if (!strcmp(profile, "raid10")) {
49                 *flags |= BTRFS_BLOCK_GROUP_RAID10;
50         } else if (!strcmp(profile, "raid5")) {
51                 *flags |= BTRFS_BLOCK_GROUP_RAID5;
52         } else if (!strcmp(profile, "raid6")) {
53                 *flags |= BTRFS_BLOCK_GROUP_RAID6;
54         } else if (!strcmp(profile, "dup")) {
55                 *flags |= BTRFS_BLOCK_GROUP_DUP;
56         } else if (!strcmp(profile, "single")) {
57                 *flags |= BTRFS_AVAIL_ALLOC_BIT_SINGLE;
58         } else {
59                 fprintf(stderr, "Unknown profile '%s'\n", profile);
60                 return 1;
61         }
62
63         return 0;
64 }
65
66 static int parse_profiles(char *profiles, u64 *flags)
67 {
68         char *this_char;
69         char *save_ptr;
70
71         for (this_char = strtok_r(profiles, "|", &save_ptr);
72              this_char != NULL;
73              this_char = strtok_r(NULL, "|", &save_ptr)) {
74                 if (parse_one_profile(this_char, flags))
75                         return 1;
76         }
77
78         return 0;
79 }
80
81 static int parse_u64(const char *str, u64 *result)
82 {
83         char *endptr;
84         u64 val;
85
86         val = strtoull(str, &endptr, 10);
87         if (*endptr)
88                 return 1;
89
90         *result = val;
91         return 0;
92 }
93
94 static int parse_range(const char *range, u64 *start, u64 *end)
95 {
96         char *dots;
97
98         dots = strstr(range, "..");
99         if (dots) {
100                 const char *rest = dots + 2;
101                 int skipped = 0;
102
103                 *dots = 0;
104
105                 if (!*rest) {
106                         *end = (u64)-1;
107                         skipped++;
108                 } else {
109                         if (parse_u64(rest, end))
110                                 return 1;
111                 }
112                 if (dots == range) {
113                         *start = 0;
114                         skipped++;
115                 } else {
116                         if (parse_u64(range, start))
117                                 return 1;
118                 }
119
120                 if (*start >= *end) {
121                         fprintf(stderr, "Range %llu..%llu doesn't make "
122                                 "sense\n", (unsigned long long)*start,
123                                 (unsigned long long)*end);
124                         return 1;
125                 }
126
127                 if (skipped <= 1)
128                         return 0;
129         }
130
131         return 1;
132 }
133
134 static int parse_filters(char *filters, struct btrfs_balance_args *args)
135 {
136         char *this_char;
137         char *value;
138         char *save_ptr;
139
140         if (!filters)
141                 return 0;
142
143         for (this_char = strtok_r(filters, ",", &save_ptr);
144              this_char != NULL;
145              this_char = strtok_r(NULL, ",", &save_ptr)) {
146                 if ((value = strchr(this_char, '=')) != NULL)
147                         *value++ = 0;
148                 if (!strcmp(this_char, "profiles")) {
149                         if (!value || !*value) {
150                                 fprintf(stderr, "the profiles filter requires "
151                                        "an argument\n");
152                                 return 1;
153                         }
154                         if (parse_profiles(value, &args->profiles)) {
155                                 fprintf(stderr, "Invalid profiles argument\n");
156                                 return 1;
157                         }
158                         args->flags |= BTRFS_BALANCE_ARGS_PROFILES;
159                 } else if (!strcmp(this_char, "usage")) {
160                         if (!value || !*value) {
161                                 fprintf(stderr, "the usage filter requires "
162                                        "an argument\n");
163                                 return 1;
164                         }
165                         if (parse_u64(value, &args->usage) ||
166                             args->usage < 1 || args->usage > 100) {
167                                 fprintf(stderr, "Invalid usage argument: %s\n",
168                                        value);
169                                 return 1;
170                         }
171                         args->flags |= BTRFS_BALANCE_ARGS_USAGE;
172                 } else if (!strcmp(this_char, "devid")) {
173                         if (!value || !*value) {
174                                 fprintf(stderr, "the devid filter requires "
175                                        "an argument\n");
176                                 return 1;
177                         }
178                         if (parse_u64(value, &args->devid) ||
179                             args->devid == 0) {
180                                 fprintf(stderr, "Invalid devid argument: %s\n",
181                                        value);
182                                 return 1;
183                         }
184                         args->flags |= BTRFS_BALANCE_ARGS_DEVID;
185                 } else if (!strcmp(this_char, "drange")) {
186                         if (!value || !*value) {
187                                 fprintf(stderr, "the drange filter requires "
188                                        "an argument\n");
189                                 return 1;
190                         }
191                         if (parse_range(value, &args->pstart, &args->pend)) {
192                                 fprintf(stderr, "Invalid drange argument\n");
193                                 return 1;
194                         }
195                         args->flags |= BTRFS_BALANCE_ARGS_DRANGE;
196                 } else if (!strcmp(this_char, "vrange")) {
197                         if (!value || !*value) {
198                                 fprintf(stderr, "the vrange filter requires "
199                                        "an argument\n");
200                                 return 1;
201                         }
202                         if (parse_range(value, &args->vstart, &args->vend)) {
203                                 fprintf(stderr, "Invalid vrange argument\n");
204                                 return 1;
205                         }
206                         args->flags |= BTRFS_BALANCE_ARGS_VRANGE;
207                 } else if (!strcmp(this_char, "convert")) {
208                         if (!value || !*value) {
209                                 fprintf(stderr, "the convert option requires "
210                                        "an argument\n");
211                                 return 1;
212                         }
213                         if (parse_one_profile(value, &args->target)) {
214                                 fprintf(stderr, "Invalid convert argument\n");
215                                 return 1;
216                         }
217                         args->flags |= BTRFS_BALANCE_ARGS_CONVERT;
218                 } else if (!strcmp(this_char, "soft")) {
219                         args->flags |= BTRFS_BALANCE_ARGS_SOFT;
220                 } else {
221                         fprintf(stderr, "Unrecognized balance option '%s'\n",
222                                 this_char);
223                         return 1;
224                 }
225         }
226
227         return 0;
228 }
229
230 static void dump_balance_args(struct btrfs_balance_args *args)
231 {
232         if (args->flags & BTRFS_BALANCE_ARGS_CONVERT) {
233                 printf("converting, target=%llu, soft is %s",
234                        (unsigned long long)args->target,
235                        (args->flags & BTRFS_BALANCE_ARGS_SOFT) ? "on" : "off");
236         } else {
237                 printf("balancing");
238         }
239
240         if (args->flags & BTRFS_BALANCE_ARGS_PROFILES)
241                 printf(", profiles=%llu", (unsigned long long)args->profiles);
242         if (args->flags & BTRFS_BALANCE_ARGS_USAGE)
243                 printf(", usage=%llu", (unsigned long long)args->usage);
244         if (args->flags & BTRFS_BALANCE_ARGS_DEVID)
245                 printf(", devid=%llu", (unsigned long long)args->devid);
246         if (args->flags & BTRFS_BALANCE_ARGS_DRANGE)
247                 printf(", drange=%llu..%llu",
248                        (unsigned long long)args->pstart,
249                        (unsigned long long)args->pend);
250         if (args->flags & BTRFS_BALANCE_ARGS_VRANGE)
251                 printf(", vrange=%llu..%llu",
252                        (unsigned long long)args->vstart,
253                        (unsigned long long)args->vend);
254
255         printf("\n");
256 }
257
258 static void dump_ioctl_balance_args(struct btrfs_ioctl_balance_args *args)
259 {
260         printf("Dumping filters: flags 0x%llx, state 0x%llx, force is %s\n",
261                (unsigned long long)args->flags, (unsigned long long)args->state,
262                (args->flags & BTRFS_BALANCE_FORCE) ? "on" : "off");
263         if (args->flags & BTRFS_BALANCE_DATA) {
264                 printf("  DATA (flags 0x%llx): ",
265                        (unsigned long long)args->data.flags);
266                 dump_balance_args(&args->data);
267         }
268         if (args->flags & BTRFS_BALANCE_METADATA) {
269                 printf("  METADATA (flags 0x%llx): ",
270                        (unsigned long long)args->meta.flags);
271                 dump_balance_args(&args->meta);
272         }
273         if (args->flags & BTRFS_BALANCE_SYSTEM) {
274                 printf("  SYSTEM (flags 0x%llx): ",
275                        (unsigned long long)args->sys.flags);
276                 dump_balance_args(&args->sys);
277         }
278 }
279
280 static int do_balance_v1(int fd)
281 {
282         struct btrfs_ioctl_vol_args args;
283         int ret;
284
285         memset(&args, 0, sizeof(args));
286         ret = ioctl(fd, BTRFS_IOC_BALANCE, &args);
287         return ret;
288 }
289
290 static int do_balance(const char *path, struct btrfs_ioctl_balance_args *args,
291                       int nofilters)
292 {
293         int fd;
294         int ret;
295         int e;
296
297         fd = open_file_or_dir(path);
298         if (fd < 0) {
299                 fprintf(stderr, "ERROR: can't access to '%s'\n", path);
300                 return 12;
301         }
302
303         ret = ioctl(fd, BTRFS_IOC_BALANCE_V2, args);
304         e = errno;
305
306         if (ret < 0) {
307                 /*
308                  * older kernels don't have the new balance ioctl, try the
309                  * old one.  But, the old one doesn't know any filters, so
310                  * don't fall back if they tried to use the fancy new things
311                  */
312                 if (e == ENOTTY && nofilters) {
313                         ret = do_balance_v1(fd);
314                         if (ret == 0)
315                                 goto out;
316                         e = errno;
317                 }
318
319                 if (e == ECANCELED) {
320                         if (args->state & BTRFS_BALANCE_STATE_PAUSE_REQ)
321                                 fprintf(stderr, "balance paused by user\n");
322                         if (args->state & BTRFS_BALANCE_STATE_CANCEL_REQ)
323                                 fprintf(stderr, "balance canceled by user\n");
324                         ret = 0;
325                 } else {
326                         fprintf(stderr, "ERROR: error during balancing '%s' "
327                                 "- %s\n", path, strerror(e));
328                         if (e != EINPROGRESS)
329                                 fprintf(stderr, "There may be more info in "
330                                         "syslog - try dmesg | tail\n");
331                         ret = 19;
332                 }
333         } else {
334                 printf("Done, had to relocate %llu out of %llu chunks\n",
335                        (unsigned long long)args->stat.completed,
336                        (unsigned long long)args->stat.considered);
337                 ret = 0;
338         }
339
340 out:
341         close(fd);
342         return ret;
343 }
344
345 static const char * const cmd_balance_start_usage[] = {
346         "btrfs [filesystem] balance start [options] <path>",
347         "Balance chunks across the devices",
348         "Balance and/or convert (change allocation profile of) chunks that",
349         "passed all filters in a comma-separated list of filters for a",
350         "particular chunk type.  If filter list is not given balance all",
351         "chunks of that type.  In case none of the -d, -m or -s options is",
352         "given balance all chunks in a filesystem.",
353         "",
354         "-d[filters]    act on data chunks",
355         "-m[filters]    act on metadata chunks",
356         "-s[filetrs]    act on system chunks (only under -f)",
357         "-v             be verbose",
358         "-f             force reducing of metadata integrity",
359         NULL
360 };
361
362 static int cmd_balance_start(int argc, char **argv)
363 {
364         struct btrfs_ioctl_balance_args args;
365         struct btrfs_balance_args *ptrs[] = { &args.data, &args.sys,
366                                                 &args.meta, NULL };
367         int force = 0;
368         int verbose = 0;
369         int nofilters = 1;
370         int i;
371
372         memset(&args, 0, sizeof(args));
373
374         optind = 1;
375         while (1) {
376                 int longindex;
377                 static struct option longopts[] = {
378                         { "data", optional_argument, NULL, 'd'},
379                         { "metadata", optional_argument, NULL, 'm' },
380                         { "system", optional_argument, NULL, 's' },
381                         { "force", no_argument, NULL, 'f' },
382                         { "verbose", no_argument, NULL, 'v' },
383                         { 0, 0, 0, 0 }
384                 };
385
386                 int opt = getopt_long(argc, argv, "d::s::m::fv", longopts,
387                                       &longindex);
388                 if (opt < 0)
389                         break;
390
391                 switch (opt) {
392                 case 'd':
393                         nofilters = 0;
394                         args.flags |= BTRFS_BALANCE_DATA;
395
396                         if (parse_filters(optarg, &args.data))
397                                 return 1;
398                         break;
399                 case 's':
400                         nofilters = 0;
401                         args.flags |= BTRFS_BALANCE_SYSTEM;
402
403                         if (parse_filters(optarg, &args.sys))
404                                 return 1;
405                         break;
406                 case 'm':
407                         nofilters = 0;
408                         args.flags |= BTRFS_BALANCE_METADATA;
409
410                         if (parse_filters(optarg, &args.meta))
411                                 return 1;
412                         break;
413                 case 'f':
414                         force = 1;
415                         break;
416                 case 'v':
417                         verbose = 1;
418                         break;
419                 default:
420                         usage(cmd_balance_start_usage);
421                 }
422         }
423
424         if (check_argc_exact(argc - optind, 1))
425                 usage(cmd_balance_start_usage);
426
427         /*
428          * allow -s only under --force, otherwise do with system chunks
429          * the same thing we were ordered to do with meta chunks
430          */
431         if (args.flags & BTRFS_BALANCE_SYSTEM) {
432                 if (!force) {
433                         fprintf(stderr,
434 "Refusing to explicitly operate on system chunks.\n"
435 "Pass --force if you really want to do that.\n");
436                         return 1;
437                 }
438         } else if (args.flags & BTRFS_BALANCE_METADATA) {
439                 args.flags |= BTRFS_BALANCE_SYSTEM;
440                 memcpy(&args.sys, &args.meta,
441                         sizeof(struct btrfs_balance_args));
442         }
443
444         if (nofilters) {
445                 /* relocate everything - no filters */
446                 args.flags |= BTRFS_BALANCE_TYPE_MASK;
447         }
448
449         /* drange makes sense only when devid is set */
450         for (i = 0; ptrs[i]; i++) {
451                 if ((ptrs[i]->flags & BTRFS_BALANCE_ARGS_DRANGE) &&
452                     !(ptrs[i]->flags & BTRFS_BALANCE_ARGS_DEVID)) {
453                         fprintf(stderr, "drange filter can be used only if "
454                                 "devid filter is used\n");
455                         return 1;
456                 }
457         }
458
459         /* soft makes sense only when convert for corresponding type is set */
460         for (i = 0; ptrs[i]; i++) {
461                 if ((ptrs[i]->flags & BTRFS_BALANCE_ARGS_SOFT) &&
462                     !(ptrs[i]->flags & BTRFS_BALANCE_ARGS_CONVERT)) {
463                         fprintf(stderr, "'soft' option can be used only if "
464                                 "changing profiles\n");
465                         return 1;
466                 }
467         }
468
469         if (force)
470                 args.flags |= BTRFS_BALANCE_FORCE;
471         if (verbose)
472                 dump_ioctl_balance_args(&args);
473
474         return do_balance(argv[optind], &args, nofilters);
475 }
476
477 static const char * const cmd_balance_pause_usage[] = {
478         "btrfs [filesystem] balance pause <path>",
479         "Pause running balance",
480         NULL
481 };
482
483 static int cmd_balance_pause(int argc, char **argv)
484 {
485         const char *path;
486         int fd;
487         int ret;
488         int e;
489
490         if (check_argc_exact(argc, 2))
491                 usage(cmd_balance_pause_usage);
492
493         path = argv[1];
494
495         fd = open_file_or_dir(path);
496         if (fd < 0) {
497                 fprintf(stderr, "ERROR: can't access to '%s'\n", path);
498                 return 12;
499         }
500
501         ret = ioctl(fd, BTRFS_IOC_BALANCE_CTL, BTRFS_BALANCE_CTL_PAUSE);
502         e = errno;
503         close(fd);
504
505         if (ret < 0) {
506                 fprintf(stderr, "ERROR: balance pause on '%s' failed - %s\n",
507                         path, (e == ENOTCONN) ? "Not running" : strerror(e));
508                 return 19;
509         }
510
511         return 0;
512 }
513
514 static const char * const cmd_balance_cancel_usage[] = {
515         "btrfs [filesystem] balance cancel <path>",
516         "Cancel running or paused balance",
517         NULL
518 };
519
520 static int cmd_balance_cancel(int argc, char **argv)
521 {
522         const char *path;
523         int fd;
524         int ret;
525         int e;
526
527         if (check_argc_exact(argc, 2))
528                 usage(cmd_balance_cancel_usage);
529
530         path = argv[1];
531
532         fd = open_file_or_dir(path);
533         if (fd < 0) {
534                 fprintf(stderr, "ERROR: can't access to '%s'\n", path);
535                 return 12;
536         }
537
538         ret = ioctl(fd, BTRFS_IOC_BALANCE_CTL, BTRFS_BALANCE_CTL_CANCEL);
539         e = errno;
540         close(fd);
541
542         if (ret < 0) {
543                 fprintf(stderr, "ERROR: balance cancel on '%s' failed - %s\n",
544                         path, (e == ENOTCONN) ? "Not in progress" : strerror(e));
545                 return 19;
546         }
547
548         return 0;
549 }
550
551 static const char * const cmd_balance_resume_usage[] = {
552         "btrfs [filesystem] balance resume <path>",
553         "Resume interrupted balance",
554         NULL
555 };
556
557 static int cmd_balance_resume(int argc, char **argv)
558 {
559         struct btrfs_ioctl_balance_args args;
560         const char *path;
561         int fd;
562         int ret;
563         int e;
564
565         if (check_argc_exact(argc, 2))
566                 usage(cmd_balance_resume_usage);
567
568         path = argv[1];
569
570         fd = open_file_or_dir(path);
571         if (fd < 0) {
572                 fprintf(stderr, "ERROR: can't access to '%s'\n", path);
573                 return 12;
574         }
575
576         memset(&args, 0, sizeof(args));
577         args.flags |= BTRFS_BALANCE_RESUME;
578
579         ret = ioctl(fd, BTRFS_IOC_BALANCE_V2, &args);
580         e = errno;
581         close(fd);
582
583         if (ret < 0) {
584                 if (e == ECANCELED) {
585                         if (args.state & BTRFS_BALANCE_STATE_PAUSE_REQ)
586                                 fprintf(stderr, "balance paused by user\n");
587                         if (args.state & BTRFS_BALANCE_STATE_CANCEL_REQ)
588                                 fprintf(stderr, "balance canceled by user\n");
589                 } else if (e == ENOTCONN || e == EINPROGRESS) {
590                         fprintf(stderr, "ERROR: balance resume on '%s' "
591                                 "failed - %s\n", path,
592                                 (e == ENOTCONN) ? "Not in progress" :
593                                                   "Already running");
594                         return 19;
595                 } else {
596                         fprintf(stderr,
597 "ERROR: error during balancing '%s' - %s\n"
598 "There may be more info in syslog - try dmesg | tail\n", path, strerror(e));
599                         return 19;
600                 }
601         } else {
602                 printf("Done, had to relocate %llu out of %llu chunks\n",
603                        (unsigned long long)args.stat.completed,
604                        (unsigned long long)args.stat.considered);
605         }
606
607         return 0;
608 }
609
610 static const char * const cmd_balance_status_usage[] = {
611         "btrfs [filesystem] balance status [-v] <path>",
612         "Show status of running or paused balance",
613         "",
614         "-v     be verbose",
615         NULL
616 };
617
618 static int cmd_balance_status(int argc, char **argv)
619 {
620         struct btrfs_ioctl_balance_args args;
621         const char *path;
622         int fd;
623         int verbose = 0;
624         int ret;
625         int e;
626
627         optind = 1;
628         while (1) {
629                 int longindex;
630                 static struct option longopts[] = {
631                         { "verbose", no_argument, NULL, 'v' },
632                         { 0, 0, 0, 0}
633                 };
634
635                 int opt = getopt_long(argc, argv, "v", longopts, &longindex);
636                 if (opt < 0)
637                         break;
638
639                 switch (opt) {
640                 case 'v':
641                         verbose = 1;
642                         break;
643                 default:
644                         usage(cmd_balance_status_usage);
645                 }
646         }
647
648         if (check_argc_exact(argc - optind, 1))
649                 usage(cmd_balance_status_usage);
650
651         path = argv[optind];
652
653         fd = open_file_or_dir(path);
654         if (fd < 0) {
655                 fprintf(stderr, "ERROR: can't access to '%s'\n", path);
656                 return 12;
657         }
658
659         ret = ioctl(fd, BTRFS_IOC_BALANCE_PROGRESS, &args);
660         e = errno;
661         close(fd);
662
663         if (ret < 0) {
664                 fprintf(stderr, "ERROR: balance status on '%s' failed - %s\n",
665                         path, (e == ENOTCONN) ? "Not in progress" : strerror(e));
666                 return 19;
667         }
668
669         if (args.state & BTRFS_BALANCE_STATE_RUNNING) {
670                 printf("Balance on '%s' is running", path);
671                 if (args.state & BTRFS_BALANCE_STATE_CANCEL_REQ)
672                         printf(", cancel requested\n");
673                 else if (args.state & BTRFS_BALANCE_STATE_PAUSE_REQ)
674                         printf(", pause requested\n");
675                 else
676                         printf("\n");
677         } else {
678                 printf("Balance on '%s' is paused\n", path);
679         }
680
681         printf("%llu out of about %llu chunks balanced (%llu considered), "
682                "%3.f%% left\n", (unsigned long long)args.stat.completed,
683                (unsigned long long)args.stat.expected,
684                (unsigned long long)args.stat.considered,
685                100 * (1 - (float)args.stat.completed/args.stat.expected));
686
687         if (verbose)
688                 dump_ioctl_balance_args(&args);
689
690         return 0;
691 }
692
693 const struct cmd_group balance_cmd_group = {
694         balance_cmd_group_usage, balance_cmd_group_info, {
695                 { "start", cmd_balance_start, cmd_balance_start_usage, NULL, 0 },
696                 { "pause", cmd_balance_pause, cmd_balance_pause_usage, NULL, 0 },
697                 { "cancel", cmd_balance_cancel, cmd_balance_cancel_usage, NULL, 0 },
698                 { "resume", cmd_balance_resume, cmd_balance_resume_usage, NULL, 0 },
699                 { "status", cmd_balance_status, cmd_balance_status_usage, NULL, 0 },
700                 { 0, 0, 0, 0, 0 }
701         }
702 };
703
704 int cmd_balance(int argc, char **argv)
705 {
706         if (argc == 2) {
707                 /* old 'btrfs filesystem balance <path>' syntax */
708                 struct btrfs_ioctl_balance_args args;
709
710                 memset(&args, 0, sizeof(args));
711                 args.flags |= BTRFS_BALANCE_TYPE_MASK;
712
713                 return do_balance(argv[1], &args, 1);
714         }
715
716         return handle_command_group(&balance_cmd_group, argc, argv);
717 }