linux-btrfs.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] [RFC] Btrfs-progs: restriper interface
@ 2011-08-23 20:08 Ilya Dryomov
  2011-08-23 20:08 ` [PATCH] Btrfs-progs: add restriper commands Ilya Dryomov
  0 siblings, 1 reply; 3+ messages in thread
From: Ilya Dryomov @ 2011-08-23 20:08 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Chris Mason, Hugo Mills, idryomov

Hello,

This is a RFC, man pages are absent and just one big commit.  Here are
some specs for the new interface:

./btrfs fi restripe start [-d[filters]] [-m[filters]] [-s[filters]]
    [-vf] <path>

where 'filters' is comma-separated list of filters (comma == AND):

o profiles={profiles mask} - profiles filter

    profiles mask is '|'-separated list ('|' == OR) of profiles

o usage={percentage} - usage filter

o devid={devid} - devid filter

o drange={start..end} - devid subset filter, it's tied to devid filter:
we say balance out range [start..end) on a particular devid.  These are
also acceptable:

    drange=start.. - [start..end of device)
    drange=..end - [start of device..end)

o vrange={start..end} - virtual address space subset filter.  Same forms
as above are acceptable.

Convert (profile changing) is specified as follows:

o convert={profile},[soft]

    soft parameter makes sense only for convert option.  It turns on
"soft" mode for profile changing, see the kernel patch.

Each chunk type can be either balanced or converted to some new profile.
By specifying some filters w/o convert option we balance chunks that
passed all filters (remember, comma == AND).  If only convert parameter
is specified we convert all chunks of that type.  If both convert and
filters are specified restriper filters out chunks according to the
given filters and then converts everything that passed through all the
filters.

Some examples:

o ./btrfs fi restripe start <path>

will balance everything (what ./btrfs fi balance <path> would do)

o ./btrfs fi restripe start -d

will balance only data chunks

o ./btrfs fi restripe start -d -s

will balance all data and system chunks, won't touch meta chunks

o ./btrfs fi restripe start -dprofiles=raid1\|raid0

\ - shell escape, will balance only data chunks that have raid1 or
raid0 profile

o ./btrfs fi restripe start -mprofiles=raid1\|raid0,devid=2

will balance meta chunks that have raid1 or raid0 profile and at least
one stripe located on device with devid 2

o ./btrfs fi restripe start -s -musage=80,profiles=dup

will balance all system chunks and dup'ed metadata chunks which are less
than 80% full

o ./btrfs fi restripe start -s -mprofiles=dup,convert=raid1

will *balance* all system chunks and *convert* dup meta chunks to raid1

o ./btrfs fi restripe start -dvrange=100..803337011,convert=raid0,soft

will soft-convert chunks in that virtual address space range to raid0

Note that you can't put a space between e.g. -m and a list of filters
because of the way getopt(3) works.  There are also long options, if you
prefer (--data, --metadata, --system), e.g:

    ./btrfs fi restripe start --data=profiles=raid1\|raid0

All permutations are possible, restriper doesn't care about the order in
which options are given, all settings are per-chunk-type, and what you
do with one chunk type is completely independent of what you do with the
other.

The -f (--force) flag has to be given if you want to "downgrade" the
profile.  By downgrading I mean reducing the number of copies, so
raid10->raid1 can be done w/o this flag, while raid1->raid0 cannot.

And the management commands:

./btrfs fi restripe cancel <path>
./btrfs fi restripe pause <path>
./btrfs fi restripe resume <path>

Any suggestions on the interface are welcome.  Patch is on top of tmp
branch of cmason's repo, available at:

git://github.com/idryomov/btrfs-progs.git restriper-rfc

Thanks,

		Ilya


Ilya Dryomov (1):
  Btrfs-progs: add restriper commands

 btrfs.c      |   25 +++-
 btrfs_cmds.c |  508 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 btrfs_cmds.h |    5 +
 ctree.h      |    9 +
 ioctl.h      |   44 +++++
 print-tree.c |    3 +
 volumes.h    |   42 +++++
 7 files changed, 632 insertions(+), 4 deletions(-)

-- 
1.7.5.4


^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH] Btrfs-progs: add restriper commands
  2011-08-23 20:08 [PATCH] [RFC] Btrfs-progs: restriper interface Ilya Dryomov
@ 2011-08-23 20:08 ` Ilya Dryomov
  0 siblings, 0 replies; 3+ messages in thread
From: Ilya Dryomov @ 2011-08-23 20:08 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Chris Mason, Hugo Mills, idryomov

Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
---
 btrfs.c      |   25 +++-
 btrfs_cmds.c |  508 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 btrfs_cmds.h |    5 +
 ctree.h      |    9 +
 ioctl.h      |   44 +++++
 print-tree.c |    3 +
 volumes.h    |   42 +++++
 7 files changed, 632 insertions(+), 4 deletions(-)

diff --git a/btrfs.c b/btrfs.c
index 4cd4210..ae088f5 100644
--- a/btrfs.c
+++ b/btrfs.c
@@ -95,8 +95,29 @@ static struct Command commands[] = {
 	  "filesystem balance", "<path>\n"
 		"Balance the chunks across the device."
 	},
-	{ do_scan, 999, 
-	  "device scan", "[<device>...]\n"
+	{ do_restripe, -1,
+	  "filesystem restripe start", "[-d [filters]] [-m [filters]] "
+	  "[-s [filters]] [-vf] <path>\n"
+		"Start restriper."
+	},
+	{ do_restripe_cancel, 1,
+	  "filesystem restripe cancel", "<path>\n"
+		"Cancel restriper."
+	},
+	{ do_restripe_pause, 1,
+	  "filesystem restripe pause", "<path>\n"
+		"Pause restriper."
+	},
+	{ do_restripe_resume, 1,
+	  "filesystem restripe resume", "<path>\n"
+		"Resume interrupted restripe operation."
+	},
+	{ do_restripe_progress, -1,
+	  "filesystem restripe status", "[-v] <path>\n"
+		"Show status of running or paused restripe operation."
+	},
+	{ do_scan,
+	  999, "device scan", "[<device> [<device>..]\n"
 		"Scan all device for or the passed device for a btrfs\n"
 		"filesystem."
 	},
diff --git a/btrfs_cmds.c b/btrfs_cmds.c
index 32f6b25..c386f74 100644
--- a/btrfs_cmds.c
+++ b/btrfs_cmds.c
@@ -18,6 +18,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <getopt.h>
 #include <sys/ioctl.h>
 #include <sys/types.h>
 #include <dirent.h>
@@ -819,13 +820,516 @@ int do_balance(int argc, char **argv)
 	e = errno;
 	close(fdmnt);
 	if(ret<0){
-		fprintf(stderr, "ERROR: error during balancing '%s' - %s\n", 
-			path, strerror(e));
+		if (e == ECANCELED) {
+			fprintf(stderr, "restripe interrupted by user\n");
+		} else {
+			fprintf(stderr, "ERROR: error during restriping '%s' "
+				"- %s\n", path, strerror(e));
+			return 19;
+		}
+	}
+	return 0;
+}
+
+static int parse_one_profile(char *profile, u64 *flags)
+{
+	if (!strcmp(profile, "raid0")) {
+		*flags |= BTRFS_BLOCK_GROUP_RAID0;
+	} else if (!strcmp(profile, "raid1")) {
+		*flags |= BTRFS_BLOCK_GROUP_RAID1;
+	} else if (!strcmp(profile, "raid10")) {
+		*flags |= BTRFS_BLOCK_GROUP_RAID10;
+	} else if (!strcmp(profile, "dup")) {
+		*flags |= BTRFS_BLOCK_GROUP_DUP;
+	} else if (!strcmp(profile, "single")) {
+		*flags |= BTRFS_AVAIL_ALLOC_BIT_SINGLE;
+	} else {
+		fprintf(stderr, "Unknown profile '%s'\n", profile);
+		return 1;
+	}
+
+	return 0;
+}
+
+static int parse_profiles(char *profiles, u64 *flags)
+{
+	char *this_char;
+	char *save_ptr;
+
+	for (this_char = strtok_r(profiles, "|", &save_ptr);
+	     this_char != NULL;
+	     this_char = strtok_r(NULL, "|", &save_ptr)) {
+		if (parse_one_profile(this_char, flags))
+			return 1;
+	}
+
+	return 0;
+}
+
+static int parse_range(char *range, u64 *start, u64 *end)
+{
+	char *dots;
+
+	dots = strstr(range, "..");
+	if (dots) {
+		const char *rest = dots + 2;
+		int skipped = 0;
+
+		*dots = 0;
+
+		if (!*rest) {
+			*end = (u64)-1;
+			skipped++;
+		} else {
+			*end = strtoull(rest, (char **)NULL, 10);
+		}
+		if (dots == range) {
+			*start = 0;
+			skipped++;
+		} else {
+			*start = strtoull(range, (char **)NULL, 10);
+		}
+
+		if (skipped <= 1)
+			return 0;
+	}
+
+	return 1;
+}
+
+static int parse_filters(char *filters, struct btrfs_restripe_args *rargs)
+{
+	char *this_char;
+	char *value;
+	char *save_ptr;
+
+	if (!filters)
+		return 0;
+
+	for (this_char = strtok_r(filters, ",", &save_ptr);
+	     this_char != NULL;
+	     this_char = strtok_r(NULL, ",", &save_ptr)) {
+		if ((value = strchr(this_char, '=')) != NULL)
+			*value++ = 0;
+		if (!strcmp(this_char, "profiles")) {
+			if (!value || !*value) {
+				fprintf(stderr, "the profiles filter requires "
+				       "an argument\n");
+				return 1;
+			}
+			if (parse_profiles(value, &rargs->profiles)) {
+				fprintf(stderr, "Invalid profiles argument\n");
+				return 1;
+			}
+			rargs->flags |= BTRFS_RESTRIPE_ARGS_PROFILES;
+		} else if (!strcmp(this_char, "usage")) {
+			if (!value || !*value) {
+				fprintf(stderr, "the usage filter requires "
+				       "an argument\n");
+				return 1;
+			}
+			rargs->usage = strtoull(value, (char **)NULL, 10);
+			if (rargs->usage < 1 || rargs->usage > 100) {
+				fprintf(stderr, "Invalid usage argument: %s\n",
+				       value);
+				return 1;
+			}
+			rargs->flags |= BTRFS_RESTRIPE_ARGS_USAGE;
+		} else if (!strcmp(this_char, "devid")) {
+			if (!value || !*value) {
+				fprintf(stderr, "the devid filter requires "
+				       "an argument\n");
+				return 1;
+			}
+			rargs->devid = strtoull(value, (char **)NULL, 10);
+			if (rargs->devid == 0) {
+				fprintf(stderr, "Invalid devid argument: %s\n",
+				       value);
+				return 1;
+			}
+			rargs->flags |= BTRFS_RESTRIPE_ARGS_DEVID;
+		} else if (!strcmp(this_char, "drange")) {
+			if (!value || !*value) {
+				fprintf(stderr, "the drange filter requires "
+				       "an argument\n");
+				return 1;
+			}
+			if (parse_range(value, &rargs->pstart, &rargs->pend)) {
+				fprintf(stderr, "Invalid drange argument\n");
+				return 1;
+			}
+			rargs->flags |= BTRFS_RESTRIPE_ARGS_DRANGE;
+		} else if (!strcmp(this_char, "vrange")) {
+			if (!value || !*value) {
+				fprintf(stderr, "the vrange filter requires "
+				       "an argument\n");
+				return 1;
+			}
+			if (parse_range(value, &rargs->vstart, &rargs->vend)) {
+				fprintf(stderr, "Invalid vrange argument\n");
+				return 1;
+			}
+			rargs->flags |= BTRFS_RESTRIPE_ARGS_VRANGE;
+		} else if (!strcmp(this_char, "convert")) {
+			if (!value || !*value) {
+				fprintf(stderr, "the convert option requires "
+				       "an argument\n");
+				return 1;
+			}
+			if (parse_one_profile(value, &rargs->target)) {
+				fprintf(stderr, "Invalid convert argument\n");
+				return 1;
+			}
+			rargs->flags |= BTRFS_RESTRIPE_ARGS_CONVERT;
+		} else if (!strcmp(this_char, "soft")) {
+			rargs->flags |= BTRFS_RESTRIPE_ARGS_SOFT;
+		} else {
+			fprintf(stderr, "Unrecognized restripe option '%s'\n",
+				this_char);
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+static void dump_ioctl_restripe_args(struct btrfs_ioctl_restripe_args *args);
+
+static struct option restripe_longopts[] = {
+	{ "data", 2, NULL, 'd'},
+	{ "metadata", 2, NULL, 'm' },
+	{ "system", 2, NULL, 's' },
+	{ "force", 0, NULL, 'f' },
+	{ "verbose", 0, NULL, 'v' },
+	{ 0, 0, 0, 0}
+};
+
+/*
+ * [-d [filters]] [-m [filters]] [-s [filters]] [-vf]
+ */
+int do_restripe(int ac, char **av)
+{
+	int fd;
+	char *path;
+	struct btrfs_ioctl_restripe_args args;
+	struct btrfs_restripe_args *ptrs[] = { &args.data, &args.sys,
+						&args.meta, NULL };
+	int force = 0;
+	int verbose = 0;
+	int nofilters = 1;
+	int i;
+	int longindex;
+	int ret;
+	int e;
+
+	memset(&args, 0, sizeof(args));
+
+	while (1) {
+		int opt = getopt_long(ac, av, "d::s::m::fv", restripe_longopts,
+				      &longindex);
+		if (opt < 0)
+			break;
+
+		switch (opt) {
+		case 'd':
+			nofilters = 0;
+			args.flags |= BTRFS_RESTRIPE_DATA;
+
+			if (parse_filters(optarg, &args.data))
+				return 1;
+			break;
+		case 's':
+			nofilters = 0;
+			args.flags |= BTRFS_RESTRIPE_SYSTEM;
+
+			if (parse_filters(optarg, &args.sys))
+				return 1;
+			break;
+		case 'm':
+			nofilters = 0;
+			args.flags |= BTRFS_RESTRIPE_METADATA;
+
+			if (parse_filters(optarg, &args.meta))
+				return 1;
+			break;
+		case 'f':
+			force = 1;
+			break;
+		case 'v':
+			verbose = 1;
+			break;
+		default:
+			fprintf(stderr, "Invalid arguments for restripe\n");
+			return 1;
+		}
+	}
+
+	if (ac - optind != 1) {
+		fprintf(stderr, "Invalid arguments for restripe\n");
+		return 1;
+	}
+
+	if (nofilters) {
+		/* relocate everything - no filters */
+		args.flags |= BTRFS_RESTRIPE_TYPE_MASK;
+	}
+
+	/* drange makes sense only when devid is set */
+	for (i = 0; ptrs[i]; i++) {
+		if ((ptrs[i]->flags & BTRFS_RESTRIPE_ARGS_DRANGE) &&
+		    !(ptrs[i]->flags & BTRFS_RESTRIPE_ARGS_DEVID)) {
+			fprintf(stderr, "drange filter can be used only if "
+				"devid filter is used\n");
+			return 1;
+		}
+	}
+
+	/* soft makes sense only when convert for corresponding type is set */
+	for (i = 0; ptrs[i]; i++) {
+		if ((ptrs[i]->flags & BTRFS_RESTRIPE_ARGS_SOFT) &&
+		    !(ptrs[i]->flags & BTRFS_RESTRIPE_ARGS_CONVERT)) {
+			fprintf(stderr, "'soft' option can be used only if "
+				"changing profiles\n");
+			return 1;
+		}
+	}
+
+	path = av[optind];
+	fd = open_file_or_dir(path);
+	if (fd < 0) {
+		fprintf(stderr, "ERROR: can't access to '%s'\n", path);
+		return 12;
+	}
+
+	if (force)
+		args.flags |= BTRFS_RESTRIPE_FORCE;
+	if (verbose)
+		dump_ioctl_restripe_args(&args);
+
+	ret = ioctl(fd, BTRFS_IOC_RESTRIPE, &args);
+	e = errno;
+	close(fd);
+
+	if (ret < 0) {
+		if (e == ECANCELED) {
+			fprintf(stderr, "restripe interrupted by user\n");
+		} else {
+			fprintf(stderr, "ERROR: error during restriping '%s' "
+				"- %s\n", path, strerror(e));
+			return 19;
+		}
+	}
+
+	return 0;
+}
+
+int do_restripe_cancel(int ac, char **av)
+{
+	int fd;
+	char *path = av[1];
+	int ret;
+	int e;
+
+	fd = open_file_or_dir(path);
+	if (fd < 0) {
+		fprintf(stderr, "ERROR: can't access to '%s'\n", path);
+		return 12;
+	}
+
+	ret = ioctl(fd, BTRFS_IOC_RESTRIPE_CTL, BTRFS_RESTRIPE_CTL_CANCEL);
+	e = errno;
+	close(fd);
+
+	if (ret < 0) {
+		fprintf(stderr, "ERROR: restripe cancel on '%s' failed - %s\n",
+			path, (e == ENOTCONN) ? "Not in progress" : strerror(e));
+		return 19;
+	}
+
+	return 0;
+}
+
+int do_restripe_pause(int ac, char **av)
+{
+	int fd;
+	char *path = av[1];
+	int ret;
+	int e;
+
+	fd = open_file_or_dir(path);
+	if (fd < 0) {
+		fprintf(stderr, "ERROR: can't access to '%s'\n", path);
+		return 12;
+	}
+
+	ret = ioctl(fd, BTRFS_IOC_RESTRIPE_CTL, BTRFS_RESTRIPE_CTL_PAUSE);
+	e = errno;
+	close(fd);
+
+	if (ret < 0) {
+		fprintf(stderr, "ERROR: restripe pause on '%s' failed - %s\n",
+			path, (e == ENOTCONN) ? "Not running" : strerror(e));
+		return 19;
+	}
+
+	return 0;
+}
+
+int do_restripe_resume(int ac, char **av)
+{
+	int fd;
+	char *path = av[1];
+	int ret;
+	int e;
+
+	fd = open_file_or_dir(path);
+	if (fd < 0) {
+		fprintf(stderr, "ERROR: can't access to '%s'\n", path);
+		return 12;
+	}
+
+	ret = ioctl(fd, BTRFS_IOC_RESTRIPE_CTL, BTRFS_RESTRIPE_CTL_RESUME);
+	e = errno;
+	close(fd);
 
+	if (ret < 0) {
+		if (e == ECANCELED) {
+			fprintf(stderr, "restripe interrupted by user\n");
+		} else if (e == ENOTCONN || e == EINPROGRESS) {
+			fprintf(stderr, "ERROR: restripe resume on '%s' "
+				"failed - %s\n", path,
+				(e == ENOTCONN) ? "Not in progress" :
+						  "Already running");
+			return 19;
+		} else {
+			fprintf(stderr, "ERROR: error during restriping '%s' "
+				"- %s\n", path, strerror(e));
+			return 19;
+		}
+	}
+
+	return 0;
+}
+
+static struct option restripe_progress_longopts[] = {
+	{ "verbose", 0, NULL, 'v' },
+	{ 0, 0, 0, 0}
+};
+
+int do_restripe_progress(int ac, char **av)
+{
+	int fd;
+	char *path;
+	struct btrfs_ioctl_restripe_args args;
+	int verbose = 0;
+	int longindex;
+	int ret;
+	int e;
+
+	while (1) {
+		int opt = getopt_long(ac, av, "v", restripe_progress_longopts,
+				      &longindex);
+		if (opt < 0)
+			break;
+
+		switch (opt) {
+		case 'v':
+			verbose = 1;
+			break;
+		default:
+			fprintf(stderr, "Invalid arguments for restripe "
+				"status\n");
+			return 1;
+		}
+	}
+
+	if (ac - optind != 1) {
+		fprintf(stderr, "Invalid arguments for restripe status\n");
+		return 1;
+	}
+
+	path = av[optind];
+	fd = open_file_or_dir(path);
+	if (fd < 0) {
+		fprintf(stderr, "ERROR: can't access to '%s'\n", path);
+		return 12;
+	}
+
+	ret = ioctl(fd, BTRFS_IOC_RESTRIPE_PROGRESS, &args);
+	e = errno;
+	close(fd);
+
+	if (ret < 0) {
+		fprintf(stderr, "ERROR: restripe status on '%s' failed - %s\n",
+			path, (e == ENOTCONN) ? "Not in progress" : strerror(e));
 		return 19;
 	}
+
+	if (args.state & BTRFS_RESTRIPE_ST_RUNNING) {
+		printf("Restripe on '%s' is running", path);
+		if (args.state & BTRFS_RESTRIPE_ST_CANCEL_REQ)
+			printf(", cancel requested\n");
+		else if (args.state & BTRFS_RESTRIPE_ST_PAUSE_REQ)
+			printf(", pause requested\n");
+		else
+			printf("\n");
+	} else {
+		printf("Restripe on '%s' is paused\n", path);
+	}
+
+	printf("%llu out of about %llu chunks restriped (%llu considered), "
+	       "%3.f%% left\n", args.stat.completed, args.stat.expected,
+	       args.stat.considered,
+	       100 * (1 - (float)args.stat.completed/args.stat.expected));
+
+	if (verbose)
+		dump_ioctl_restripe_args(&args);
+
 	return 0;
 }
+
+static void dump_restripe_args(struct btrfs_restripe_args *args)
+{
+	if (args->flags & BTRFS_RESTRIPE_ARGS_CONVERT) {
+		printf("converting, target=%llu, soft is %s", args->target,
+		       (args->flags & BTRFS_RESTRIPE_ARGS_SOFT) ? "on" : "off");
+	} else {
+		printf("balancing");
+	}
+
+	if (args->flags & BTRFS_RESTRIPE_ARGS_PROFILES)
+		printf(", profiles=%llu", args->profiles);
+	if (args->flags & BTRFS_RESTRIPE_ARGS_USAGE)
+		printf(", usage=%llu", args->usage);
+	if (args->flags & BTRFS_RESTRIPE_ARGS_DEVID)
+		printf(", devid=%llu", args->devid);
+	if (args->flags & BTRFS_RESTRIPE_ARGS_DRANGE)
+		printf(", drange=%llu..%llu", args->pstart, args->pend);
+	if (args->flags & BTRFS_RESTRIPE_ARGS_VRANGE)
+		printf(", vrange=%llu..%llu", args->vstart, args->vend);
+
+	printf("\n");
+}
+
+static void dump_ioctl_restripe_args(struct btrfs_ioctl_restripe_args *args)
+{
+	printf("Dumping filters: flags 0x%llx, state 0x%llx, force is %s\n",
+	       args->flags, args->state,
+	       (args->flags & BTRFS_RESTRIPE_FORCE) ? "on" : "off");
+	if (args->flags & BTRFS_RESTRIPE_DATA) {
+		printf("  DATA (flags 0x%llx): ", args->data.flags);
+		dump_restripe_args(&args->data);
+	}
+	if (args->flags & BTRFS_RESTRIPE_METADATA) {
+		printf("  METADATA (flags 0x%llx): ", args->meta.flags);
+		dump_restripe_args(&args->meta);
+	}
+	if (args->flags & BTRFS_RESTRIPE_SYSTEM) {
+		printf("  SYSTEM (flags 0x%llx): ", args->sys.flags);
+		dump_restripe_args(&args->sys);
+	}
+}
+
 int do_remove_volume(int nargs, char **args)
 {
 
diff --git a/btrfs_cmds.h b/btrfs_cmds.h
index ab722d4..de09b65 100644
--- a/btrfs_cmds.h
+++ b/btrfs_cmds.h
@@ -23,6 +23,11 @@ int do_defrag(int argc, char **argv);
 int do_show_filesystem(int nargs, char **argv);
 int do_add_volume(int nargs, char **args);
 int do_balance(int nargs, char **argv);
+int do_restripe(int ac, char **av);
+int do_restripe_cancel(int ac, char **av);
+int do_restripe_pause(int ac, char **av);
+int do_restripe_resume(int ac, char **av);
+int do_restripe_progress(int ac, char **av);
 int do_remove_volume(int nargs, char **args);
 int do_scan(int nargs, char **argv);
 int do_resize(int nargs, char **argv);
diff --git a/ctree.h b/ctree.h
index 61eb639..46bb860 100644
--- a/ctree.h
+++ b/ctree.h
@@ -60,6 +60,9 @@ struct btrfs_trans_handle;
 #define BTRFS_CSUM_TREE_OBJECTID 7ULL
 
 
+/* for storing restripe params in the root tree */
+#define BTRFS_RESTRIPE_OBJECTID -4ULL
+
 /* oprhan objectid for tracking unlinked/truncated files */
 #define BTRFS_ORPHAN_OBJECTID -5ULL
 
@@ -651,6 +654,12 @@ struct btrfs_csum_item {
 #define BTRFS_BLOCK_GROUP_DUP	   (1 << 5)
 #define BTRFS_BLOCK_GROUP_RAID10   (1 << 6)
 
+/*
+ * to avoid troubles..
+ */
+#define BTRFS_AVAIL_ALLOC_BIT_SINGLE	(1 << 7)
+#define BTRFS_BLOCK_GROUP_RESERVED	(1 << 7)
+
 struct btrfs_block_group_item {
 	__le64 used;
 	__le64 chunk_objectid;
diff --git a/ioctl.h b/ioctl.h
index bb7b9e0..6eb5d70 100644
--- a/ioctl.h
+++ b/ioctl.h
@@ -30,6 +30,45 @@ struct btrfs_ioctl_vol_args {
 	char name[BTRFS_PATH_NAME_MAX + 1];
 };
 
+#define BTRFS_RESTRIPE_CTL_CANCEL	1
+#define BTRFS_RESTRIPE_CTL_PAUSE	2
+#define BTRFS_RESTRIPE_CTL_RESUME	3
+
+struct btrfs_restripe_args {
+	__u64 profiles;
+	__u64 usage;
+	__u64 devid;
+	__u64 pstart;
+	__u64 pend;
+	__u64 vstart;
+	__u64 vend;
+
+	__u64 target;
+
+	__u64 flags;
+
+	__u64 unused[8];
+} __attribute__ ((__packed__));
+
+struct btrfs_restripe_progress {
+	__u64 expected;
+	__u64 considered;
+	__u64 completed;
+};
+
+struct btrfs_ioctl_restripe_args {
+	__u64 flags;
+	__u64 state;
+
+	struct btrfs_restripe_args data;
+	struct btrfs_restripe_args sys;
+	struct btrfs_restripe_args meta;
+
+	struct btrfs_restripe_progress stat;
+
+	__u64 unused[72]; /* pad to 1k */
+};
+
 struct btrfs_ioctl_search_key {
 	/* which root are we searching.  0 is the tree of tree roots */
 	__u64 tree_id;
@@ -176,4 +215,9 @@ struct btrfs_ioctl_space_args {
 #define BTRFS_IOC_DEFAULT_SUBVOL _IOW(BTRFS_IOCTL_MAGIC, 19, u64)
 #define BTRFS_IOC_SPACE_INFO _IOWR(BTRFS_IOCTL_MAGIC, 20, \
 				    struct btrfs_ioctl_space_args)
+#define BTRFS_IOC_RESTRIPE _IOW(BTRFS_IOCTL_MAGIC, 32, \
+				struct btrfs_ioctl_restripe_args)
+#define BTRFS_IOC_RESTRIPE_CTL _IOW(BTRFS_IOCTL_MAGIC, 33, int)
+#define BTRFS_IOC_RESTRIPE_PROGRESS _IOR(BTRFS_IOCTL_MAGIC, 34, \
+				struct btrfs_ioctl_restripe_args)
 #endif
diff --git a/print-tree.c b/print-tree.c
index ac575d5..14480d6 100644
--- a/print-tree.c
+++ b/print-tree.c
@@ -391,6 +391,9 @@ static void print_objectid(unsigned long long objectid, u8 type)
 	case BTRFS_CSUM_TREE_OBJECTID:
 		printf("CSUM_TREE");
 		break;
+	case BTRFS_RESTRIPE_OBJECTID:
+		printf("RESTRIPE");
+		break;
 	case BTRFS_ORPHAN_OBJECTID:
 		printf("ORPHAN");
 		break;
diff --git a/volumes.h b/volumes.h
index 93b0e48..5aaa61b 100644
--- a/volumes.h
+++ b/volumes.h
@@ -91,6 +91,48 @@ struct btrfs_multi_bio {
 #define btrfs_multi_bio_size(n) (sizeof(struct btrfs_multi_bio) + \
 			    (sizeof(struct btrfs_bio_stripe) * (n)))
 
+/*
+ * Restriper's general "type" filter.  Shares bits with chunk type for
+ * simplicity, RESTRIPE prefix is used to avoid confusion.
+ */
+#define BTRFS_RESTRIPE_DATA		(1ULL << 0)
+#define BTRFS_RESTRIPE_SYSTEM		(1ULL << 1)
+#define BTRFS_RESTRIPE_METADATA		(1ULL << 2)
+
+#define BTRFS_RESTRIPE_TYPE_MASK	(BTRFS_RESTRIPE_DATA |		    \
+					 BTRFS_RESTRIPE_SYSTEM |	    \
+					 BTRFS_RESTRIPE_METADATA)
+
+#define BTRFS_RESTRIPE_FORCE		(1ULL << 3)
+
+/*
+ * Restripe filters
+ */
+#define BTRFS_RESTRIPE_ARGS_PROFILES	(1ULL << 0)
+#define BTRFS_RESTRIPE_ARGS_USAGE	(1ULL << 1)
+#define BTRFS_RESTRIPE_ARGS_DEVID	(1ULL << 2)
+#define BTRFS_RESTRIPE_ARGS_DRANGE	(1ULL << 3)
+#define BTRFS_RESTRIPE_ARGS_VRANGE	(1ULL << 4)
+
+/*
+ * Profile changing flags.  When SOFT is set we won't relocate chunk if
+ * it already has the target profile (even though it may be
+ * half-filled).
+ */
+#define BTRFS_RESTRIPE_ARGS_CONVERT	(1ULL << 8)
+#define BTRFS_RESTRIPE_ARGS_SOFT	(1ULL << 9)
+
+/*
+ * Restripe state bits
+ */
+#define RESTRIPE_RUNNING	0
+#define RESTRIPE_CANCEL_REQ	1
+#define RESTRIPE_PAUSE_REQ	2
+
+#define BTRFS_RESTRIPE_ST_RUNNING	(1ULL << RESTRIPE_RUNNING)
+#define BTRFS_RESTRIPE_ST_CANCEL_REQ	(1ULL << RESTRIPE_CANCEL_REQ)
+#define BTRFS_RESTRIPE_ST_PAUSE_REQ	(1ULL << RESTRIPE_PAUSE_REQ)
+
 int btrfs_alloc_dev_extent(struct btrfs_trans_handle *trans,
 			   struct btrfs_device *device,
 			   u64 chunk_tree, u64 chunk_objectid,
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [PATCH] Btrfs-progs: add restriper commands
  2012-01-06 14:37 [PATCH] Btrfs-progs: restriper interface Ilya Dryomov
@ 2012-01-06 14:38 ` Ilya Dryomov
  0 siblings, 0 replies; 3+ messages in thread
From: Ilya Dryomov @ 2012-01-06 14:38 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Chris Mason, idryomov

Import restriper commands under btrfs fi balance:

  btrfs fi balance start
  btrfs fi balance cancel
  btrfs fi balance pause
  btrfs fi balance resume
  btrfs fi balance status

NOTE: Backwards compatibility is broken for now, to get the old "balance
everything" behaviour one has to call 'btrfs fi balance start' with no
options instead of 'btrfs fi balance'.  This is because btrfs utility
sub-command parser is not flexible enough.

Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
---
 btrfs.c      |   27 +++-
 btrfs_cmds.c |  577 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 btrfs_cmds.h |    8 +-
 ctree.h      |   23 ++-
 ioctl.h      |   53 ++++++
 print-tree.c |    6 +
 volumes.h    |   30 +++
 7 files changed, 687 insertions(+), 37 deletions(-)

diff --git a/btrfs.c b/btrfs.c
index 1def354..b639e83 100644
--- a/btrfs.c
+++ b/btrfs.c
@@ -119,9 +119,30 @@ static struct Command commands[] = {
 		"Show space usage information for a mount point.",
 	  NULL
 	},
-	{ do_balance, 1,
-	  "filesystem balance", "<path>\n"
-		"Balance the chunks across the device.",
+	{ do_balance, -1,
+	  "filesystem balance start", "[-d [filters]] [-m [filters]] "
+	  "[-s [filters]] [-vf] <path>\n"
+		"Balance chunks across the devices.",
+	  NULL
+	},
+	{ do_balance_pause, 1,
+	  "filesystem balance pause", "<path>\n"
+		"Pause running balance.",
+	  NULL
+	},
+	{ do_balance_cancel, 1,
+	  "filesystem balance cancel", "<path>\n"
+		"Cancel running or paused balance.",
+	  NULL
+	},
+	{ do_balance_resume, 1,
+	  "filesystem balance resume", "<path>\n"
+		"Resume interrupted balance.",
+	  NULL
+	},
+	{ do_balance_progress, -1,
+	  "filesystem balance status", "[-v] <path>\n"
+		"Show status of running or paused balance.",
 	  NULL
 	},
 	{ do_change_label, -1,
diff --git a/btrfs_cmds.c b/btrfs_cmds.c
index b59e9cb..f3ea9ab 100644
--- a/btrfs_cmds.c
+++ b/btrfs_cmds.c
@@ -18,6 +18,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <getopt.h>
 #include <sys/ioctl.h>
 #include <sys/types.h>
 #include <dirent.h>
@@ -888,31 +889,6 @@ int do_add_volume(int nargs, char **args)
 
 }
 
-int do_balance(int argc, char **argv)
-{
-
-	int	fdmnt, ret=0, e;
-	struct btrfs_ioctl_vol_args args;
-	char	*path = argv[1];
-
-	fdmnt = open_file_or_dir(path);
-	if (fdmnt < 0) {
-		fprintf(stderr, "ERROR: can't access to '%s'\n", path);
-		return 12;
-	}
-
-	memset(&args, 0, sizeof(args));
-	ret = ioctl(fdmnt, BTRFS_IOC_BALANCE, &args);
-	e = errno;
-	close(fdmnt);
-	if(ret<0){
-		fprintf(stderr, "ERROR: error during balancing '%s' - %s\n", 
-			path, strerror(e));
-
-		return 19;
-	}
-	return 0;
-}
 int do_remove_volume(int nargs, char **args)
 {
 
@@ -946,6 +922,557 @@ int do_remove_volume(int nargs, char **args)
 		return 0;
 }
 
+static int parse_one_profile(const char *profile, u64 *flags)
+{
+	if (!strcmp(profile, "raid0")) {
+		*flags |= BTRFS_BLOCK_GROUP_RAID0;
+	} else if (!strcmp(profile, "raid1")) {
+		*flags |= BTRFS_BLOCK_GROUP_RAID1;
+	} else if (!strcmp(profile, "raid10")) {
+		*flags |= BTRFS_BLOCK_GROUP_RAID10;
+	} else if (!strcmp(profile, "dup")) {
+		*flags |= BTRFS_BLOCK_GROUP_DUP;
+	} else if (!strcmp(profile, "single")) {
+		*flags |= BTRFS_AVAIL_ALLOC_BIT_SINGLE;
+	} else {
+		fprintf(stderr, "Unknown profile '%s'\n", profile);
+		return 1;
+	}
+
+	return 0;
+}
+
+static int parse_profiles(char *profiles, u64 *flags)
+{
+	char *this_char;
+	char *save_ptr;
+
+	for (this_char = strtok_r(profiles, "|", &save_ptr);
+	     this_char != NULL;
+	     this_char = strtok_r(NULL, "|", &save_ptr)) {
+		if (parse_one_profile(this_char, flags))
+			return 1;
+	}
+
+	return 0;
+}
+
+static int parse_u64(const char *str, u64 *result)
+{
+	char *endptr;
+	u64 val;
+
+	val = strtoull(str, &endptr, 10);
+	if (*endptr)
+		return 1;
+
+	*result = val;
+	return 0;
+}
+
+static int parse_range(const char *range, u64 *start, u64 *end)
+{
+	char *dots;
+
+	dots = strstr(range, "..");
+	if (dots) {
+		const char *rest = dots + 2;
+		int skipped = 0;
+
+		*dots = 0;
+
+		if (!*rest) {
+			*end = (u64)-1;
+			skipped++;
+		} else {
+			if (parse_u64(rest, end))
+				return 1;
+		}
+		if (dots == range) {
+			*start = 0;
+			skipped++;
+		} else {
+			if (parse_u64(range, start))
+				return 1;
+		}
+
+		if (*start >= *end) {
+			fprintf(stderr, "Range %llu..%llu doesn't make "
+				"sense\n", (unsigned long long)*start,
+				(unsigned long long)*end);
+			return 1;
+		}
+
+		if (skipped <= 1)
+			return 0;
+	}
+
+	return 1;
+}
+
+static int parse_filters(char *filters, struct btrfs_balance_args *args)
+{
+	char *this_char;
+	char *value;
+	char *save_ptr;
+
+	if (!filters)
+		return 0;
+
+	for (this_char = strtok_r(filters, ",", &save_ptr);
+	     this_char != NULL;
+	     this_char = strtok_r(NULL, ",", &save_ptr)) {
+		if ((value = strchr(this_char, '=')) != NULL)
+			*value++ = 0;
+		if (!strcmp(this_char, "profiles")) {
+			if (!value || !*value) {
+				fprintf(stderr, "the profiles filter requires "
+				       "an argument\n");
+				return 1;
+			}
+			if (parse_profiles(value, &args->profiles)) {
+				fprintf(stderr, "Invalid profiles argument\n");
+				return 1;
+			}
+			args->flags |= BTRFS_BALANCE_ARGS_PROFILES;
+		} else if (!strcmp(this_char, "usage")) {
+			if (!value || !*value) {
+				fprintf(stderr, "the usage filter requires "
+				       "an argument\n");
+				return 1;
+			}
+			if (parse_u64(value, &args->usage) ||
+			    args->usage < 1 || args->usage > 100) {
+				fprintf(stderr, "Invalid usage argument: %s\n",
+				       value);
+				return 1;
+			}
+			args->flags |= BTRFS_BALANCE_ARGS_USAGE;
+		} else if (!strcmp(this_char, "devid")) {
+			if (!value || !*value) {
+				fprintf(stderr, "the devid filter requires "
+				       "an argument\n");
+				return 1;
+			}
+			if (parse_u64(value, &args->devid) ||
+			    args->devid == 0) {
+				fprintf(stderr, "Invalid devid argument: %s\n",
+				       value);
+				return 1;
+			}
+			args->flags |= BTRFS_BALANCE_ARGS_DEVID;
+		} else if (!strcmp(this_char, "drange")) {
+			if (!value || !*value) {
+				fprintf(stderr, "the drange filter requires "
+				       "an argument\n");
+				return 1;
+			}
+			if (parse_range(value, &args->pstart, &args->pend)) {
+				fprintf(stderr, "Invalid drange argument\n");
+				return 1;
+			}
+			args->flags |= BTRFS_BALANCE_ARGS_DRANGE;
+		} else if (!strcmp(this_char, "vrange")) {
+			if (!value || !*value) {
+				fprintf(stderr, "the vrange filter requires "
+				       "an argument\n");
+				return 1;
+			}
+			if (parse_range(value, &args->vstart, &args->vend)) {
+				fprintf(stderr, "Invalid vrange argument\n");
+				return 1;
+			}
+			args->flags |= BTRFS_BALANCE_ARGS_VRANGE;
+		} else if (!strcmp(this_char, "convert")) {
+			if (!value || !*value) {
+				fprintf(stderr, "the convert option requires "
+				       "an argument\n");
+				return 1;
+			}
+			if (parse_one_profile(value, &args->target)) {
+				fprintf(stderr, "Invalid convert argument\n");
+				return 1;
+			}
+			args->flags |= BTRFS_BALANCE_ARGS_CONVERT;
+		} else if (!strcmp(this_char, "soft")) {
+			args->flags |= BTRFS_BALANCE_ARGS_SOFT;
+		} else {
+			fprintf(stderr, "Unrecognized balance option '%s'\n",
+				this_char);
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+static void dump_ioctl_balance_args(struct btrfs_ioctl_balance_args *args);
+
+static struct option balance_longopts[] = {
+	{ "data", optional_argument, NULL, 'd'},
+	{ "metadata", optional_argument, NULL, 'm' },
+	{ "system", optional_argument, NULL, 's' },
+	{ "force", no_argument, NULL, 'f' },
+	{ "verbose", no_argument, NULL, 'v' },
+	{ 0, 0, 0, 0}
+};
+
+/*
+ * [-d [filters]] [-m [filters]] [-s [filters]] [-vf]
+ */
+int do_balance(int argc, char **argv)
+{
+	int fd;
+	char *path;
+	struct btrfs_ioctl_balance_args args;
+	struct btrfs_balance_args *ptrs[] = { &args.data, &args.sys,
+						&args.meta, NULL };
+	int force = 0;
+	int verbose = 0;
+	int nofilters = 1;
+	int i;
+	int longindex;
+	int ret;
+	int e;
+
+	memset(&args, 0, sizeof(args));
+
+	while (1) {
+		int opt = getopt_long(argc, argv, "d::s::m::fv",
+				      balance_longopts, &longindex);
+		if (opt < 0)
+			break;
+
+		switch (opt) {
+		case 'd':
+			nofilters = 0;
+			args.flags |= BTRFS_BALANCE_DATA;
+
+			if (parse_filters(optarg, &args.data))
+				return 1;
+			break;
+		case 's':
+			nofilters = 0;
+			args.flags |= BTRFS_BALANCE_SYSTEM;
+
+			if (parse_filters(optarg, &args.sys))
+				return 1;
+			break;
+		case 'm':
+			nofilters = 0;
+			args.flags |= BTRFS_BALANCE_METADATA;
+
+			if (parse_filters(optarg, &args.meta))
+				return 1;
+			break;
+		case 'f':
+			force = 1;
+			break;
+		case 'v':
+			verbose = 1;
+			break;
+		default:
+			fprintf(stderr, "Invalid arguments for balance\n");
+			return 1;
+		}
+	}
+
+	if (argc - optind != 1) {
+		fprintf(stderr, "Invalid arguments for balance\n");
+		return 1;
+	}
+
+	/* 
+	 * allow -s only under --force, otherwise do with system chunks
+	 * the same thing we were ordered to do with meta chunks
+	 */
+	if (args.flags & BTRFS_BALANCE_SYSTEM) {
+		if (!force) {
+			fprintf(stderr,
+"Refusing to explicitly operate on system chunks.\n"
+"Pass --force if you really want to do that.\n");
+			return 1;
+		}
+	} else if (args.flags & BTRFS_BALANCE_METADATA) {
+		args.flags |= BTRFS_BALANCE_SYSTEM;
+		memcpy(&args.sys, &args.meta,
+			sizeof(struct btrfs_balance_args));
+	}
+
+	if (nofilters) {
+		/* relocate everything - no filters */
+		args.flags |= BTRFS_BALANCE_TYPE_MASK;
+	}
+
+	/* drange makes sense only when devid is set */
+	for (i = 0; ptrs[i]; i++) {
+		if ((ptrs[i]->flags & BTRFS_BALANCE_ARGS_DRANGE) &&
+		    !(ptrs[i]->flags & BTRFS_BALANCE_ARGS_DEVID)) {
+			fprintf(stderr, "drange filter can be used only if "
+				"devid filter is used\n");
+			return 1;
+		}
+	}
+
+	/* soft makes sense only when convert for corresponding type is set */
+	for (i = 0; ptrs[i]; i++) {
+		if ((ptrs[i]->flags & BTRFS_BALANCE_ARGS_SOFT) &&
+		    !(ptrs[i]->flags & BTRFS_BALANCE_ARGS_CONVERT)) {
+			fprintf(stderr, "'soft' option can be used only if "
+				"changing profiles\n");
+			return 1;
+		}
+	}
+
+	path = argv[optind];
+	fd = open_file_or_dir(path);
+	if (fd < 0) {
+		fprintf(stderr, "ERROR: can't access to '%s'\n", path);
+		return 12;
+	}
+
+	if (force)
+		args.flags |= BTRFS_BALANCE_FORCE;
+	if (verbose)
+		dump_ioctl_balance_args(&args);
+
+	ret = ioctl(fd, BTRFS_IOC_BALANCE_V2, &args);
+	e = errno;
+	close(fd);
+
+	if (ret < 0) {
+		if (e == ECANCELED) {
+			fprintf(stderr, "balance interrupted by user\n");
+		} else {
+			fprintf(stderr, "ERROR: error during balancing '%s' "
+				"- %s\n", path, strerror(e));
+			if (e != EINPROGRESS)
+				fprintf(stderr, "There may be more info in "
+					"syslog - try dmesg | tail\n");
+			return 19;
+		}
+	}
+
+	return 0;
+}
+
+int do_balance_pause(int argc, char **argv)
+{
+	int fd;
+	char *path = argv[1];
+	int ret;
+	int e;
+
+	fd = open_file_or_dir(path);
+	if (fd < 0) {
+		fprintf(stderr, "ERROR: can't access to '%s'\n", path);
+		return 12;
+	}
+
+	ret = ioctl(fd, BTRFS_IOC_BALANCE_CTL, BTRFS_BALANCE_CTL_PAUSE);
+	e = errno;
+	close(fd);
+
+	if (ret < 0) {
+		fprintf(stderr, "ERROR: balance pause on '%s' failed - %s\n",
+			path, (e == ENOTCONN) ? "Not running" : strerror(e));
+		return 19;
+	}
+
+	return 0;
+}
+
+int do_balance_cancel(int argc, char **argv)
+{
+	int fd;
+	char *path = argv[1];
+	int ret;
+	int e;
+
+	fd = open_file_or_dir(path);
+	if (fd < 0) {
+		fprintf(stderr, "ERROR: can't access to '%s'\n", path);
+		return 12;
+	}
+
+	ret = ioctl(fd, BTRFS_IOC_BALANCE_CTL, BTRFS_BALANCE_CTL_CANCEL);
+	e = errno;
+	close(fd);
+
+	if (ret < 0) {
+		fprintf(stderr, "ERROR: balance cancel on '%s' failed - %s\n",
+			path, (e == ENOTCONN) ? "Not in progress" : strerror(e));
+		return 19;
+	}
+
+	return 0;
+}
+
+int do_balance_resume(int argc, char **argv)
+{
+	int fd;
+	char *path = argv[1];
+	int ret;
+	int e;
+
+	fd = open_file_or_dir(path);
+	if (fd < 0) {
+		fprintf(stderr, "ERROR: can't access to '%s'\n", path);
+		return 12;
+	}
+
+	ret = ioctl(fd, BTRFS_IOC_BALANCE_CTL, BTRFS_BALANCE_CTL_RESUME);
+	e = errno;
+	close(fd);
+
+	if (ret < 0) {
+		if (e == ECANCELED) {
+			fprintf(stderr, "balance interrupted by user\n");
+		} else if (e == ENOTCONN || e == EINPROGRESS) {
+			fprintf(stderr, "ERROR: balance resume on '%s' "
+				"failed - %s\n", path,
+				(e == ENOTCONN) ? "Not in progress" :
+						  "Already running");
+			return 19;
+		} else {
+			fprintf(stderr,
+"ERROR: error during balancing '%s' - %s\n"
+"There may be more info in syslog - try dmesg | tail\n", path, strerror(e));
+			return 19;
+		}
+	}
+
+	return 0;
+}
+
+static struct option balance_progress_longopts[] = {
+	{ "verbose", no_argument, NULL, 'v' },
+	{ 0, 0, 0, 0}
+};
+
+int do_balance_progress(int argc, char **argv)
+{
+	int fd;
+	char *path;
+	struct btrfs_ioctl_balance_args args;
+	int verbose = 0;
+	int longindex;
+	int ret;
+	int e;
+
+	while (1) {
+		int opt = getopt_long(argc, argv, "v",
+				      balance_progress_longopts, &longindex);
+		if (opt < 0)
+			break;
+
+		switch (opt) {
+		case 'v':
+			verbose = 1;
+			break;
+		default:
+			fprintf(stderr, "Invalid arguments for balance "
+				"status\n");
+			return 1;
+		}
+	}
+
+	if (argc - optind != 1) {
+		fprintf(stderr, "Invalid arguments for balance status\n");
+		return 1;
+	}
+
+	path = argv[optind];
+	fd = open_file_or_dir(path);
+	if (fd < 0) {
+		fprintf(stderr, "ERROR: can't access to '%s'\n", path);
+		return 12;
+	}
+
+	ret = ioctl(fd, BTRFS_IOC_BALANCE_PROGRESS, &args);
+	e = errno;
+	close(fd);
+
+	if (ret < 0) {
+		fprintf(stderr, "ERROR: balance status on '%s' failed - %s\n",
+			path, (e == ENOTCONN) ? "Not in progress" : strerror(e));
+		return 19;
+	}
+
+	if (args.state & BTRFS_BALANCE_STATE_RUNNING) {
+		printf("Balance on '%s' is running", path);
+		if (args.state & BTRFS_BALANCE_STATE_CANCEL_REQ)
+			printf(", cancel requested\n");
+		else if (args.state & BTRFS_BALANCE_STATE_PAUSE_REQ)
+			printf(", pause requested\n");
+		else
+			printf("\n");
+	} else {
+		printf("Balance on '%s' is paused\n", path);
+	}
+
+	printf("%llu out of about %llu chunks balanced (%llu considered), "
+	       "%3.f%% left\n", (unsigned long long)args.stat.completed,
+	       (unsigned long long)args.stat.expected,
+	       (unsigned long long)args.stat.considered,
+	       100 * (1 - (float)args.stat.completed/args.stat.expected));
+
+	if (verbose)
+		dump_ioctl_balance_args(&args);
+
+	return 0;
+}
+
+static void dump_balance_args(struct btrfs_balance_args *args)
+{
+	if (args->flags & BTRFS_BALANCE_ARGS_CONVERT) {
+		printf("converting, target=%llu, soft is %s",
+		       (unsigned long long)args->target,
+		       (args->flags & BTRFS_BALANCE_ARGS_SOFT) ? "on" : "off");
+	} else {
+		printf("balancing");
+	}
+
+	if (args->flags & BTRFS_BALANCE_ARGS_PROFILES)
+		printf(", profiles=%llu", (unsigned long long)args->profiles);
+	if (args->flags & BTRFS_BALANCE_ARGS_USAGE)
+		printf(", usage=%llu", (unsigned long long)args->usage);
+	if (args->flags & BTRFS_BALANCE_ARGS_DEVID)
+		printf(", devid=%llu", (unsigned long long)args->devid);
+	if (args->flags & BTRFS_BALANCE_ARGS_DRANGE)
+		printf(", drange=%llu..%llu",
+		       (unsigned long long)args->pstart,
+		       (unsigned long long)args->pend);
+	if (args->flags & BTRFS_BALANCE_ARGS_VRANGE)
+		printf(", vrange=%llu..%llu",
+		       (unsigned long long)args->vstart,
+		       (unsigned long long)args->vend);
+
+	printf("\n");
+}
+
+static void dump_ioctl_balance_args(struct btrfs_ioctl_balance_args *args)
+{
+	printf("Dumping filters: flags 0x%llx, state 0x%llx, force is %s\n",
+	       (unsigned long long)args->flags, (unsigned long long)args->state,
+	       (args->flags & BTRFS_BALANCE_FORCE) ? "on" : "off");
+	if (args->flags & BTRFS_BALANCE_DATA) {
+		printf("  DATA (flags 0x%llx): ",
+		       (unsigned long long)args->data.flags);
+		dump_balance_args(&args->data);
+	}
+	if (args->flags & BTRFS_BALANCE_METADATA) {
+		printf("  METADATA (flags 0x%llx): ",
+		       (unsigned long long)args->meta.flags);
+		dump_balance_args(&args->meta);
+	}
+	if (args->flags & BTRFS_BALANCE_SYSTEM) {
+		printf("  SYSTEM (flags 0x%llx): ",
+		       (unsigned long long)args->sys.flags);
+		dump_balance_args(&args->sys);
+	}
+}
+
 int do_set_default_subvol(int nargs, char **argv)
 {
 	int	ret=0, fd, e;
diff --git a/btrfs_cmds.h b/btrfs_cmds.h
index 81182b1..53d51d6 100644
--- a/btrfs_cmds.h
+++ b/btrfs_cmds.h
@@ -22,12 +22,16 @@ int do_fssync(int nargs, char **argv);
 int do_defrag(int argc, char **argv);
 int do_show_filesystem(int nargs, char **argv);
 int do_add_volume(int nargs, char **args);
-int do_balance(int nargs, char **argv);
+int do_remove_volume(int nargs, char **args);
+int do_balance(int argc, char **argv);
+int do_balance_pause(int argc, char **argv);
+int do_balance_cancel(int argc, char **argv);
+int do_balance_resume(int argc, char **argv);
+int do_balance_progress(int argc, char **argv);
 int do_scrub_start(int nargs, char **argv);
 int do_scrub_status(int argc, char **argv);
 int do_scrub_resume(int argc, char **argv);
 int do_scrub_cancel(int nargs, char **argv);
-int do_remove_volume(int nargs, char **args);
 int do_scan(int nargs, char **argv);
 int do_resize(int nargs, char **argv);
 int do_subvol_list(int nargs, char **argv);
diff --git a/ctree.h b/ctree.h
index 54748c8..1c3740c 100644
--- a/ctree.h
+++ b/ctree.h
@@ -61,6 +61,9 @@ struct btrfs_trans_handle;
 #define BTRFS_CSUM_TREE_OBJECTID 7ULL
 
 
+/* for storing balance parameters in the root tree */
+#define BTRFS_BALANCE_OBJECTID -4ULL
+
 /* oprhan objectid for tracking unlinked/truncated files */
 #define BTRFS_ORPHAN_OBJECTID -5ULL
 
@@ -697,13 +700,17 @@ struct btrfs_csum_item {
 } __attribute__ ((__packed__));
 
 /* tag for the radix tree of block groups in ram */
-#define BTRFS_BLOCK_GROUP_DATA     (1 << 0)
-#define BTRFS_BLOCK_GROUP_SYSTEM   (1 << 1)
-#define BTRFS_BLOCK_GROUP_METADATA (1 << 2)
-#define BTRFS_BLOCK_GROUP_RAID0    (1 << 3)
-#define BTRFS_BLOCK_GROUP_RAID1    (1 << 4)
-#define BTRFS_BLOCK_GROUP_DUP	   (1 << 5)
-#define BTRFS_BLOCK_GROUP_RAID10   (1 << 6)
+#define BTRFS_BLOCK_GROUP_DATA		(1ULL << 0)
+#define BTRFS_BLOCK_GROUP_SYSTEM	(1ULL << 1)
+#define BTRFS_BLOCK_GROUP_METADATA	(1ULL << 2)
+#define BTRFS_BLOCK_GROUP_RAID0		(1ULL << 3)
+#define BTRFS_BLOCK_GROUP_RAID1		(1ULL << 4)
+#define BTRFS_BLOCK_GROUP_DUP		(1ULL << 5)
+#define BTRFS_BLOCK_GROUP_RAID10	(1ULL << 6)
+#define BTRFS_BLOCK_GROUP_RESERVED	BTRFS_AVAIL_ALLOC_BIT_SINGLE
+
+/* used in struct btrfs_balance_args fields */
+#define BTRFS_AVAIL_ALLOC_BIT_SINGLE	(1ULL << 48)
 
 struct btrfs_block_group_item {
 	__le64 used;
@@ -911,6 +918,8 @@ struct btrfs_root {
 #define BTRFS_DEV_ITEM_KEY	216
 #define BTRFS_CHUNK_ITEM_KEY	228
 
+#define BTRFS_BALANCE_ITEM_KEY	248
+
 /*
  * string items are for debugging.  They just store a short string of
  * data in the FS
diff --git a/ioctl.h b/ioctl.h
index 1ae7537..78aebce 100644
--- a/ioctl.h
+++ b/ioctl.h
@@ -91,6 +91,54 @@ struct btrfs_ioctl_fs_info_args {
 	__u64 reserved[124];			/* pad to 1k */
 };
 
+/* balance control ioctl modes */
+#define BTRFS_BALANCE_CTL_PAUSE		1
+#define BTRFS_BALANCE_CTL_CANCEL	2
+#define BTRFS_BALANCE_CTL_RESUME	3
+
+/*
+ * this is packed, because it should be exactly the same as its disk
+ * byte order counterpart (struct btrfs_disk_balance_args)
+ */
+struct btrfs_balance_args {
+	__u64 profiles;
+	__u64 usage;
+	__u64 devid;
+	__u64 pstart;
+	__u64 pend;
+	__u64 vstart;
+	__u64 vend;
+
+	__u64 target;
+
+	__u64 flags;
+
+	__u64 unused[8];
+} __attribute__ ((__packed__));
+
+struct btrfs_balance_progress {
+	__u64 expected;
+	__u64 considered;
+	__u64 completed;
+};
+
+#define BTRFS_BALANCE_STATE_RUNNING	(1ULL << 0)
+#define BTRFS_BALANCE_STATE_CANCEL_REQ	(1ULL << 1)
+#define BTRFS_BALANCE_STATE_PAUSE_REQ	(1ULL << 2)
+
+struct btrfs_ioctl_balance_args {
+	__u64 flags;				/* in/out */
+	__u64 state;				/* out */
+
+	struct btrfs_balance_args data;		/* in/out */
+	struct btrfs_balance_args meta;		/* in/out */
+	struct btrfs_balance_args sys;		/* in/out */
+
+	struct btrfs_balance_progress stat;	/* out */
+
+	__u64 unused[72];			/* pad to 1k */
+};
+
 struct btrfs_ioctl_search_key {
 	/* which root are we searching.  0 is the tree of tree roots */
 	__u64 tree_id;
@@ -273,6 +321,11 @@ struct btrfs_ioctl_logical_ino_args {
 					struct btrfs_ioctl_dev_info_args)
 #define BTRFS_IOC_FS_INFO _IOR(BTRFS_IOCTL_MAGIC, 31, \
                                  struct btrfs_ioctl_fs_info_args)
+#define BTRFS_IOC_BALANCE_V2 _IOW(BTRFS_IOCTL_MAGIC, 32, \
+				  struct btrfs_ioctl_balance_args)
+#define BTRFS_IOC_BALANCE_CTL _IOW(BTRFS_IOCTL_MAGIC, 33, int)
+#define BTRFS_IOC_BALANCE_PROGRESS _IOR(BTRFS_IOCTL_MAGIC, 34, \
+					struct btrfs_ioctl_balance_args)
 #define BTRFS_IOC_INO_PATHS _IOWR(BTRFS_IOCTL_MAGIC, 35, \
 					struct btrfs_ioctl_ino_path_args)
 #define BTRFS_IOC_LOGICAL_INO _IOWR(BTRFS_IOCTL_MAGIC, 36, \
diff --git a/print-tree.c b/print-tree.c
index 6039699..494ba8b 100644
--- a/print-tree.c
+++ b/print-tree.c
@@ -351,6 +351,9 @@ static void print_key_type(u8 type)
 	case BTRFS_DEV_EXTENT_KEY:
 		printf("DEV_EXTENT");
 		break;
+	case BTRFS_BALANCE_ITEM_KEY:
+		printf("BALANCE_ITEM");
+		break;
 	case BTRFS_STRING_ITEM_KEY:
 		printf("STRING_ITEM");
 		break;
@@ -391,6 +394,9 @@ static void print_objectid(unsigned long long objectid, u8 type)
 	case BTRFS_CSUM_TREE_OBJECTID:
 		printf("CSUM_TREE");
 		break;
+	case BTRFS_BALANCE_OBJECTID:
+		printf("BALANCE");
+		break;
 	case BTRFS_ORPHAN_OBJECTID:
 		printf("ORPHAN");
 		break;
diff --git a/volumes.h b/volumes.h
index 7104d36..3c2457e 100644
--- a/volumes.h
+++ b/volumes.h
@@ -91,6 +91,36 @@ struct btrfs_multi_bio {
 #define btrfs_multi_bio_size(n) (sizeof(struct btrfs_multi_bio) + \
 			    (sizeof(struct btrfs_bio_stripe) * (n)))
 
+/*
+ * Restriper's general type filter
+ */
+#define BTRFS_BALANCE_DATA		(1ULL << 0)
+#define BTRFS_BALANCE_SYSTEM		(1ULL << 1)
+#define BTRFS_BALANCE_METADATA		(1ULL << 2)
+
+#define BTRFS_BALANCE_TYPE_MASK		(BTRFS_BALANCE_DATA |	    \
+					 BTRFS_BALANCE_SYSTEM |	    \
+					 BTRFS_BALANCE_METADATA)
+
+#define BTRFS_BALANCE_FORCE		(1ULL << 3)
+
+/*
+ * Balance filters
+ */
+#define BTRFS_BALANCE_ARGS_PROFILES	(1ULL << 0)
+#define BTRFS_BALANCE_ARGS_USAGE	(1ULL << 1)
+#define BTRFS_BALANCE_ARGS_DEVID	(1ULL << 2)
+#define BTRFS_BALANCE_ARGS_DRANGE	(1ULL << 3)
+#define BTRFS_BALANCE_ARGS_VRANGE	(1ULL << 4)
+
+/*
+ * Profile changing flags.  When SOFT is set we won't relocate chunk if
+ * it already has the target profile (even though it may be
+ * half-filled).
+ */
+#define BTRFS_BALANCE_ARGS_CONVERT	(1ULL << 8)
+#define BTRFS_BALANCE_ARGS_SOFT		(1ULL << 9)
+
 int btrfs_alloc_dev_extent(struct btrfs_trans_handle *trans,
 			   struct btrfs_device *device,
 			   u64 chunk_tree, u64 chunk_objectid,
-- 
1.7.6.3


^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2012-01-06 14:38 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-08-23 20:08 [PATCH] [RFC] Btrfs-progs: restriper interface Ilya Dryomov
2011-08-23 20:08 ` [PATCH] Btrfs-progs: add restriper commands Ilya Dryomov
  -- strict thread matches above, loose matches on Subject: below --
2012-01-06 14:37 [PATCH] Btrfs-progs: restriper interface Ilya Dryomov
2012-01-06 14:38 ` [PATCH] Btrfs-progs: add restriper commands Ilya Dryomov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).