linux-btrfs.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Ilya Dryomov <idryomov@gmail.com>
To: linux-btrfs@vger.kernel.org
Cc: Chris Mason <chris.mason@oracle.com>, idryomov@gmail.com
Subject: [PATCH 3/3] Btrfs-progs: add restriper commands
Date: Fri,  3 Feb 2012 23:49:12 +0200	[thread overview]
Message-ID: <1328305752-32160-4-git-send-email-idryomov@gmail.com> (raw)
In-Reply-To: <1328305752-32160-1-git-send-email-idryomov@gmail.com>

Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
---
 cmds-balance.c |  588 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 588 insertions(+), 0 deletions(-)

diff --git a/cmds-balance.c b/cmds-balance.c
index d141b46..a6886a0 100644
--- a/cmds-balance.c
+++ b/cmds-balance.c
@@ -18,6 +18,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <getopt.h>
 #include <sys/ioctl.h>
 #include <errno.h>
 
@@ -35,6 +36,240 @@ static const char balance_cmd_group_info[] =
 	"'btrfs filesystem balance' command is deprecated, please use\n"
 	"'btrfs balance start' command instead.";
 
+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_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);
+	}
+}
+
 static int do_balance(const char *path, struct btrfs_ioctl_balance_args *args)
 {
 	int fd;
@@ -74,8 +309,361 @@ static int do_balance(const char *path, struct btrfs_ioctl_balance_args *args)
 	return 0;
 }
 
+static const char * const cmd_balance_start_usage[] = {
+	"btrfs [filesystem] balance start [options] <path>",
+	"Balance chunks across the devices",
+	"Balance and/or convert (change allocation profile of) chunks that",
+	"passed all filters in a comma-separated list of filters for a",
+	"particular chunk type.  If filter list is not given balance all",
+	"chunks of that type.  In case none of the -d, -m or -s options is",
+	"given balance all chunks in a filesystem.",
+	"",
+	"-d[filters]    act on data chunks",
+	"-m[filters]    act on metadata chunks",
+	"-s[filetrs]    act on system chunks (only under -f)",
+	"-v             be verbose",
+	"-f             force reducing of metadata integrity",
+	NULL
+};
+
+static int cmd_balance_start(int argc, char **argv)
+{
+	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;
+
+	memset(&args, 0, sizeof(args));
+
+	optind = 1;
+	while (1) {
+		int longindex;
+		static struct option 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 }
+		};
+
+		int opt = getopt_long(argc, argv, "d::s::m::fv", 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:
+			usage(cmd_balance_start_usage);
+		}
+	}
+
+	if (check_argc_exact(argc - optind, 1))
+		usage(cmd_balance_start_usage);
+
+	/*
+	 * 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;
+		}
+	}
+
+	if (force)
+		args.flags |= BTRFS_BALANCE_FORCE;
+	if (verbose)
+		dump_ioctl_balance_args(&args);
+
+	return do_balance(argv[optind], &args);
+}
+
+static const char * const cmd_balance_pause_usage[] = {
+	"btrfs [filesystem] balance pause <path>",
+	"Pause running balance",
+	NULL
+};
+
+static int cmd_balance_pause(int argc, char **argv)
+{
+	const char *path;
+	int fd;
+	int ret;
+	int e;
+
+	if (check_argc_exact(argc, 2))
+		usage(cmd_balance_pause_usage);
+
+	path = argv[1];
+
+	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;
+}
+
+static const char * const cmd_balance_cancel_usage[] = {
+	"btrfs [filesystem] balance cancel <path>",
+	"Cancel running or paused balance",
+	NULL
+};
+
+static int cmd_balance_cancel(int argc, char **argv)
+{
+	const char *path;
+	int fd;
+	int ret;
+	int e;
+
+	if (check_argc_exact(argc, 2))
+		usage(cmd_balance_cancel_usage);
+
+	path = argv[1];
+
+	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;
+}
+
+static const char * const cmd_balance_resume_usage[] = {
+	"btrfs [filesystem] balance resume <path>",
+	"Resume interrupted balance",
+	NULL
+};
+
+static int cmd_balance_resume(int argc, char **argv)
+{
+	struct btrfs_ioctl_balance_args args;
+	const char *path;
+	int fd;
+	int ret;
+	int e;
+
+	if (check_argc_exact(argc, 2))
+		usage(cmd_balance_resume_usage);
+
+	path = argv[1];
+
+	fd = open_file_or_dir(path);
+	if (fd < 0) {
+		fprintf(stderr, "ERROR: can't access to '%s'\n", path);
+		return 12;
+	}
+
+	memset(&args, 0, sizeof(args));
+	args.flags |= BTRFS_BALANCE_RESUME;
+
+	ret = ioctl(fd, BTRFS_IOC_BALANCE_V2, &args);
+	e = errno;
+	close(fd);
+
+	if (ret < 0) {
+		if (e == ECANCELED) {
+			if (args.state & BTRFS_BALANCE_STATE_PAUSE_REQ)
+				fprintf(stderr, "balance paused by user\n");
+			if (args.state & BTRFS_BALANCE_STATE_CANCEL_REQ)
+				fprintf(stderr, "balance canceled 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;
+		}
+	} else {
+		printf("Done, had to relocate %llu out of %llu chunks\n",
+		       (unsigned long long)args.stat.completed,
+		       (unsigned long long)args.stat.considered);
+	}
+
+	return 0;
+}
+
+static const char * const cmd_balance_status_usage[] = {
+	"btrfs [filesystem] balance status [-v] <path>",
+	"Show status of running or paused balance",
+	"",
+	"-v     be verbose",
+	NULL
+};
+
+static int cmd_balance_status(int argc, char **argv)
+{
+	struct btrfs_ioctl_balance_args args;
+	const char *path;
+	int fd;
+	int verbose = 0;
+	int ret;
+	int e;
+
+	optind = 1;
+	while (1) {
+		int longindex;
+		static struct option longopts[] = {
+			{ "verbose", no_argument, NULL, 'v' },
+			{ 0, 0, 0, 0}
+		};
+
+		int opt = getopt_long(argc, argv, "v", longopts, &longindex);
+		if (opt < 0)
+			break;
+
+		switch (opt) {
+		case 'v':
+			verbose = 1;
+			break;
+		default:
+			usage(cmd_balance_status_usage);
+		}
+	}
+
+	if (check_argc_exact(argc - optind, 1))
+		usage(cmd_balance_status_usage);
+
+	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;
+}
+
 const struct cmd_group balance_cmd_group = {
 	balance_cmd_group_usage, balance_cmd_group_info, {
+		{ "start", cmd_balance_start, cmd_balance_start_usage, NULL, 0 },
+		{ "pause", cmd_balance_pause, cmd_balance_pause_usage, NULL, 0 },
+		{ "cancel", cmd_balance_cancel, cmd_balance_cancel_usage, NULL, 0 },
+		{ "resume", cmd_balance_resume, cmd_balance_resume_usage, NULL, 0 },
+		{ "status", cmd_balance_status, cmd_balance_status_usage, NULL, 0 },
 		{ 0, 0, 0, 0, 0 }
 	}
 };
-- 
1.7.6.3


  parent reply	other threads:[~2012-02-03 21:49 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-02-03 21:49 [PATCH 0/3] Btrfs-progs: restriper interface Ilya Dryomov
2012-02-03 21:49 ` [PATCH 1/3] Btrfs-progs: add restriper headers Ilya Dryomov
2012-02-03 21:49 ` [PATCH 2/3] Btrfs-progs: add 'balance' command group infrastructure Ilya Dryomov
2012-02-03 21:49 ` Ilya Dryomov [this message]
2012-02-03 22:13 ` [PATCH 0/3] Btrfs-progs: restriper interface Hugo Mills
2012-02-05 15:40   ` Chris Mason
2012-02-04 13:06 ` Goffredo Baroncelli
2012-02-04 14:47   ` Ilya Dryomov
2012-02-06 22:20     ` Goffredo Baroncelli
2012-02-07  9:30       ` Ilya Dryomov
2012-02-07 11:05         ` Hugo Mills

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1328305752-32160-4-git-send-email-idryomov@gmail.com \
    --to=idryomov@gmail.com \
    --cc=chris.mason@oracle.com \
    --cc=linux-btrfs@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).