* [PATCH] Btrfs-progs: add restriper commands
2011-08-23 20:08 [PATCH] [RFC] " Ilya Dryomov
@ 2011-08-23 20:08 ` Ilya Dryomov
0 siblings, 0 replies; 9+ 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] 9+ messages in thread
* [PATCH] Btrfs-progs: restriper interface
@ 2012-01-06 14:37 Ilya Dryomov
2012-01-06 14:38 ` [PATCH] Btrfs-progs: add restriper commands Ilya Dryomov
2012-01-07 12:48 ` [PATCH] Btrfs-progs: restriper interface Goffredo Baroncelli
0 siblings, 2 replies; 9+ messages in thread
From: Ilya Dryomov @ 2012-01-06 14:37 UTC (permalink / raw)
To: linux-btrfs; +Cc: Chris Mason, idryomov
Hello,
This is an update of userspace restriper interface. The main change is
that restriper commands have been moved under balance prefix. So now we
have:
btrfs fi balance start
btrfs fi balance pause
btrfs fi balance cancel
btrfs fi balance resume
btrfs fi balance status
This breaks btrfs-progs backwards compatibility: to get the old
balancing behaviour you have to call 'btrfs fi balance start' instead of
'btrfs fi balance'. This is caused by stupidity of the core sub-command
matcher. There are also some other problems with that parser and I'll
fix them all in one commit shortly. After that the "start" will be
optional:
btrfs fi balance [options]
btrfs fi balance start [options]
Apart from some minor error handling fixes went in.
Here are some specs:
./btrfs fi balance 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.
By default system chunks are relocated along with meta chunks with the
same exact options. To operate explicitly on system chunks -f (--force)
flag has to be specified.
Examples (somewhat contrived, but they demonstrate the flexibility of
the interface):
o ./btrfs fi balance start <path>
will balance everything (what ./btrfs fi balance <path> did)
o ./btrfs fi balance start -d
will balance only data chunks
o ./btrfs fi balance start -d -m
will balance everything (because -m by default applies to system chunks
too, see above)
o ./btrfs fi balance start -d -s -f
will balance all data and system chunks, won't touch meta chunks (note
that the force is used to operate explicitly on system chunks)
o ./btrfs fi balance start -dprofiles=raid1\|raid0
will balance only data chunks that have raid1 or raid0 profile
(\ - shell escape)
o ./btrfs fi balance start -mprofiles=raid1\|raid0,devid=2
will balance meta and sys chunks that have raid1 or raid0 profile and at
least one stripe located on device with devid 2
o ./btrfs fi balance start -s -musage=80,profiles=dup -f
will balance all system chunks and dup'ed metadata chunks which are less
than 80% full
o ./btrfs fi balance start -s -mprofiles=dup,convert=raid1 -f
will *balance* all system chunks and *convert* dup meta chunks to raid1
o ./btrfs fi balance start -dvrange=100..803337011,convert=raid0,soft
will soft-convert data 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 balance 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 force flag also 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 balance cancel <path>
./btrfs fi balance pause <path>
./btrfs fi balance resume <path>
./btrfs fi balance status [-v] <path>
Patch is on top of master branch of btrfs-progs repo, available at:
git://github.com/idryomov/btrfs-progs.git restriper
Thanks,
Ilya
Ilya Dryomov (1):
Btrfs-progs: add restriper commands
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(-)
--
1.7.6.3
^ permalink raw reply [flat|nested] 9+ 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
2012-01-07 12:48 ` [PATCH] Btrfs-progs: restriper interface Goffredo Baroncelli
1 sibling, 0 replies; 9+ 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] 9+ messages in thread
* Re: [PATCH] Btrfs-progs: restriper interface
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
@ 2012-01-07 12:48 ` Goffredo Baroncelli
2012-01-07 14:25 ` Ilya Dryomov
1 sibling, 1 reply; 9+ messages in thread
From: Goffredo Baroncelli @ 2012-01-07 12:48 UTC (permalink / raw)
To: Ilya Dryomov, Chris Mason; +Cc: linux-btrfs
On Friday, 06 January, 2012 15:37:59 Ilya Dryomov wrote:
> Hello,
>
> This is an update of userspace restriper interface. The main change is
> that restriper commands have been moved under balance prefix. So now we
> have:
>
> btrfs fi balance start
> btrfs fi balance pause
> btrfs fi balance cancel
> btrfs fi balance resume
> btrfs fi balance status
>
> This breaks btrfs-progs backwards compatibility: to get the old
> balancing behaviour you have to call 'btrfs fi balance start' instead of
> 'btrfs fi balance'. This is caused by stupidity of the core sub-command
> matcher.
Hi Ilya,
what you called "stupidity of the core sub-command matcher" was a design
decision.
If you make "btrfs balance" a both command and a prefix you (my opinion)
confuses a possible user and complex the interface:
for example what means:
# btrfs filesystem balance pause
when 'pause' (and all the valid abbreviation like "pa" or "p" ) is a valid
mount point ?
My opinion, in order to not break the backward compatibility is to add another
subcommand family, like 'restriper' or whatsoever, then deprecating the
'balance' sub command.
Finally, please could you update the man page too ?
BR
G.Baroncelli
Disclaimer, I was the author of the sub-command matcher.
--
gpg key@ keyserver.linux.it: Goffredo Baroncelli (ghigo) <kreijack@inwind.it>
Key fingerprint = 4769 7E51 5293 D36C 814E C054 BF04 F161 3DC5 0512
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH] Btrfs-progs: restriper interface
2012-01-07 12:48 ` [PATCH] Btrfs-progs: restriper interface Goffredo Baroncelli
@ 2012-01-07 14:25 ` Ilya Dryomov
2012-01-07 14:32 ` Roman Mamedov
0 siblings, 1 reply; 9+ messages in thread
From: Ilya Dryomov @ 2012-01-07 14:25 UTC (permalink / raw)
To: Goffredo Baroncelli; +Cc: Chris Mason, linux-btrfs
On Sat, Jan 07, 2012 at 01:48:44PM +0100, Goffredo Baroncelli wrote:
> On Friday, 06 January, 2012 15:37:59 Ilya Dryomov wrote:
> > Hello,
> >
> > This is an update of userspace restriper interface. The main change is
> > that restriper commands have been moved under balance prefix. So now we
> > have:
> >
> > btrfs fi balance start
> > btrfs fi balance pause
> > btrfs fi balance cancel
> > btrfs fi balance resume
> > btrfs fi balance status
> >
> > This breaks btrfs-progs backwards compatibility: to get the old
> > balancing behaviour you have to call 'btrfs fi balance start' instead of
> > 'btrfs fi balance'. This is caused by stupidity of the core sub-command
> > matcher.
>
> Hi Ilya,
>
> what you called "stupidity of the core sub-command matcher" was a design
> decision.
>
> If you make "btrfs balance" a both command and a prefix you (my opinion)
> confuses a possible user and complex the interface:
>
> for example what means:
>
> # btrfs filesystem balance pause
>
> when 'pause' (and all the valid abbreviation like "pa" or "p" ) is a valid
> mount point ?
pause needs an argument so technically we have a way out here, but at
the expense of an awful interface (because then we have to be able to
deal with sub-commands with no arguments, etc)
You are right, I was too quick to call it stupid. I guess my problem is
that I never bought this "the shortest abbreviation possible" thing.
IMHO it would be much better if we had our long names and a handful of
predefined abbreviations (like ip command for example).
>
> My opinion, in order to not break the backward compatibility is to add another
> subcommand family, like 'restriper' or whatsoever, then deprecating the
> 'balance' sub command.
It was that way initially, but half of what restriper does is selective
balancing. The other half is profile changing, but that also boils down
to a balance. So I decided not to introduce another obscure term.
As for compatibility, I guess we will have to eat it and have everybody
switch from "btrfs fi balance" to "btrfs fi balance start" if we want
that shortest match behaviour. However if people feel strong about it I
surely can put it under "restripe" and keep "btrfs fi balance" intact.
Chris ?
>
> Finally, please could you update the man page too ?
I will, this version was not intended for pulling anyway because of this
problem.
Thanks,
Ilya
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH] Btrfs-progs: restriper interface
2012-01-07 14:25 ` Ilya Dryomov
@ 2012-01-07 14:32 ` Roman Mamedov
2012-01-07 15:14 ` Ilya Dryomov
0 siblings, 1 reply; 9+ messages in thread
From: Roman Mamedov @ 2012-01-07 14:32 UTC (permalink / raw)
To: Ilya Dryomov; +Cc: Goffredo Baroncelli, Chris Mason, linux-btrfs
[-- Attachment #1: Type: text/plain, Size: 586 bytes --]
On Sat, 7 Jan 2012 16:25:29 +0200
Ilya Dryomov <idryomov@gmail.com> wrote:
> IMHO it would be much better if we had our long names and a handful of
> predefined abbreviations (like ip command for example).
Does the ip command use just a handful of predefined abbrebiations?
ip addr show
ip add sho
ip ad sh
ip a s
all work.
which suggests to me they use exactly the same scheme as btrfs currently.
--
With respect,
Roman
~~~~~~~~~~~~~~~~~~~~~~~~~~~
"Stallman had a printer,
with code he could not see.
So he began to tinker,
and set the software free."
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 198 bytes --]
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH] Btrfs-progs: restriper interface
2012-01-07 14:32 ` Roman Mamedov
@ 2012-01-07 15:14 ` Ilya Dryomov
2012-01-07 16:49 ` Goffredo Baroncelli
0 siblings, 1 reply; 9+ messages in thread
From: Ilya Dryomov @ 2012-01-07 15:14 UTC (permalink / raw)
To: Roman Mamedov; +Cc: Goffredo Baroncelli, Chris Mason, linux-btrfs
On Sat, Jan 07, 2012 at 08:32:51PM +0600, Roman Mamedov wrote:
> On Sat, 7 Jan 2012 16:25:29 +0200
> Ilya Dryomov <idryomov@gmail.com> wrote:
>
> > IMHO it would be much better if we had our long names and a handful of
> > predefined abbreviations (like ip command for example).
>
> Does the ip command use just a handful of predefined abbrebiations?
>
> ip addr show
> ip add sho
> ip ad sh
> ip a s
>
> all work.
> which suggests to me they use exactly the same scheme as btrfs currently.
Look at ntbl for ntable, tunl for tunnel, tap for tuntap, lst for list,
etc. On top af that they are using a "first match" policy, which leads
to a very simple and clean code and is not at all what btrfs uses:
ip route
ip rout
ip rou
ip ro
ip r
all work and expand to "ip route"
and if you want the "rule" sub-command, you have to use
ip ru
at the very least.
btrfs instead tries to be clever, and if ip was doing the same thing
btrfs does you would get "ambiguous command 'r'" error in response to
"ip r".
Thanks,
Ilya
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH] Btrfs-progs: restriper interface
2012-01-07 15:14 ` Ilya Dryomov
@ 2012-01-07 16:49 ` Goffredo Baroncelli
2012-01-07 17:21 ` Goffredo Baroncelli
0 siblings, 1 reply; 9+ messages in thread
From: Goffredo Baroncelli @ 2012-01-07 16:49 UTC (permalink / raw)
To: Ilya Dryomov; +Cc: Chris Mason, linux-btrfs
On Saturday, 07 January, 2012 16:14:43 you wrote:
> On Sat, Jan 07, 2012 at 08:32:51PM +0600, Roman Mamedov wrote:
> > On Sat, 7 Jan 2012 16:25:29 +0200
> >
> > Ilya Dryomov <idryomov@gmail.com> wrote:
> > > IMHO it would be much better if we had our long names and a handful of
> > > predefined abbreviations (like ip command for example).
> >
> > Does the ip command use just a handful of predefined abbrebiations?
> >
> > ip addr show
> > ip add sho
> > ip ad sh
> > ip a s
> >
> > all work.
> > which suggests to me they use exactly the same scheme as btrfs currently.
>
> Look at ntbl for ntable, tunl for tunnel, tap for tuntap, lst for list,
> etc. On top af that they are using a "first match" policy, which leads
> to a very simple and clean code and is not at all what btrfs uses:
>
> ip route
> ip rout
> ip rou
> ip ro
> ip r
>
> all work and expand to "ip route"
>
> and if you want the "rule" sub-command, you have to use
>
> ip ru
>
> at the very least.
>
> btrfs instead tries to be clever, and if ip was doing the same thing
> btrfs does you would get "ambiguous command 'r'" error in response to
> "ip r".
You are right, but this is a bug.
1) btrfs s sn -> means btrfs subvolume snapshot
2) btrfs s se -> means btrfs subvolume set-default
3) btrfs s s -> is ambiguous command because it could means both 1) and 2)
However if we try 1) we get an error. But this is not the intended behavior. I
have to investigate why.
BR
G.Baroncelli
>
> Thanks,
>
> Ilya
--
gpg key@ keyserver.linux.it: Goffredo Baroncelli (ghigo) <kreijack@inwind.it>
Key fingerprint = 4769 7E51 5293 D36C 814E C054 BF04 F161 3DC5 0512
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH] Btrfs-progs: restriper interface
2012-01-07 16:49 ` Goffredo Baroncelli
@ 2012-01-07 17:21 ` Goffredo Baroncelli
0 siblings, 0 replies; 9+ messages in thread
From: Goffredo Baroncelli @ 2012-01-07 17:21 UTC (permalink / raw)
To: Ilya Dryomov; +Cc: Chris Mason, linux-btrfs
On Saturday, 07 January, 2012 17:49:01 Goffredo Baroncelli wrote:
> On Saturday, 07 January, 2012 16:14:43 you wrote:
[..]
> > btrfs instead tries to be clever, and if ip was doing the same thing
> > btrfs does you would get "ambiguous command 'r'" error in response to
> > "ip r".
>
> You are right, but this is a bug.
>
> 1) btrfs s sn -> means btrfs subvolume snapshot
> 2) btrfs s se -> means btrfs subvolume set-default
> 3) btrfs s s -> is ambiguous command because it could means both 1) and 2)
>
> However if we try 1) we get an error. But this is not the intended
> behavior. I have to investigate why.
The change should be quite simple
diff --git a/btrfs.c b/btrfs.c
index 1def354..981afa4 100644
--- a/btrfs.c
+++ b/btrfs.c
@@ -268,8 +268,8 @@ static int check_ambiguity(struct Command *cmd, char
**argv)
if( cp->ncmds < i )
continue;
- for( skip = 0, j = 0 ; j < i ; j++ )
- if( strcmp(cmd->cmds[j], cp->cmds[j])){
+ for( skip = 0, j = 0 ; j <= i ; j++ )
+ if( !strcmp(cmd->cmds[j], cp->cmds[j])){
skip=1;
break;
}
But before issue a new patch I want to be sure about its correctness. I have
to make a more formal test.
>
> BR
> G.Baroncelli
>
> > Thanks,
> >
> > Ilya
--
gpg key@ keyserver.linux.it: Goffredo Baroncelli (ghigo) <kreijack@inwind.it>
Key fingerprint = 4769 7E51 5293 D36C 814E C054 BF04 F161 3DC5 0512
^ permalink raw reply related [flat|nested] 9+ messages in thread
end of thread, other threads:[~2012-01-07 17:21 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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
2012-01-07 12:48 ` [PATCH] Btrfs-progs: restriper interface Goffredo Baroncelli
2012-01-07 14:25 ` Ilya Dryomov
2012-01-07 14:32 ` Roman Mamedov
2012-01-07 15:14 ` Ilya Dryomov
2012-01-07 16:49 ` Goffredo Baroncelli
2012-01-07 17:21 ` Goffredo Baroncelli
-- strict thread matches above, loose matches on Subject: below --
2011-08-23 20:08 [PATCH] [RFC] " Ilya Dryomov
2011-08-23 20:08 ` [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).