It is useful to be able to balance a subset of the full filesystem. This patch implements the infrastructure for filtering block groups on different criteria when balancing the filesystem.
Signed-off-by: Hugo Mills <h...@carfax.org.uk> --- btrfs.c | 4 +- btrfs_cmds.c | 169 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- ioctl.h | 24 ++++++++ man/btrfs.8.in | 40 +++++++++++-- 4 files changed, 225 insertions(+), 12 deletions(-) diff --git a/btrfs.c b/btrfs.c index 7b42658..19b0e56 100644 --- a/btrfs.c +++ b/btrfs.c @@ -92,8 +92,8 @@ static struct Command commands[] = { "Show space usage information for a mount point\n." }, { do_balance, -1, - "filesystem balance", "[-w|--wait] <path>\n" - "Balance the chunks across the device." + "filesystem balance", "[-w|--wait] [-f|--filter=<filter>:...] <path>\n" + "Balance chunks across the devices. --filter=help for help on filters.\n" }, { do_balance, -1, "balance start", "[-w|--wait] <path>\n" diff --git a/btrfs_cmds.c b/btrfs_cmds.c index fadcb4f..f0588d2 100644 --- a/btrfs_cmds.c +++ b/btrfs_cmds.c @@ -756,26 +756,175 @@ int do_add_volume(int nargs, char **args) const struct option balance_options[] = { { "wait", 0, NULL, 'w' }, + { "filter", 1, NULL, 'f' }, + { "count", 0, NULL, 'c' }, + { "verbose", 0, NULL, 'v' }, { NULL, 0, NULL, 0 } }; +struct filter_class_desc { + char *keyword; + char *description; + int flag; +}; + +const struct filter_class_desc filter_class[] = { + { "type", + "type=[~]<flagname>[,...]\n" + "\tWhere <flagname> is one of:\n" + "\t\tmeta, sys, data, raid0, raid1, raid10, dup\n" + "\tPrefix a <flagname> with ~ to negate the match.\n", + BTRFS_BALANCE_FILTER_CHUNK_TYPE }, + { NULL, NULL, 0 } +}; + +struct type_filter_desc { + char *keyword; + __u64 mask; + __u64 set; + __u64 unset; +}; + +#define BTRFS_BLOCK_GROUP_SINGLE \ + BTRFS_BLOCK_GROUP_RAID0 | \ + BTRFS_BLOCK_GROUP_RAID1 | \ + BTRFS_BLOCK_GROUP_RAID10 | \ + BTRFS_BLOCK_GROUP_DUP + +const struct type_filter_desc type_filters[] = { + { "data", BTRFS_BLOCK_GROUP_DATA, BTRFS_BLOCK_GROUP_DATA, 0 }, + { "sys", BTRFS_BLOCK_GROUP_SYSTEM, BTRFS_BLOCK_GROUP_SYSTEM, 0 }, + { "meta", BTRFS_BLOCK_GROUP_METADATA, BTRFS_BLOCK_GROUP_METADATA, 0 }, + { "raid0", BTRFS_BLOCK_GROUP_RAID0, BTRFS_BLOCK_GROUP_RAID0, 0 }, + { "raid1", BTRFS_BLOCK_GROUP_RAID1, BTRFS_BLOCK_GROUP_RAID1, 0 }, + { "raid10", BTRFS_BLOCK_GROUP_RAID10, BTRFS_BLOCK_GROUP_RAID10, 0 }, + { "dup", BTRFS_BLOCK_GROUP_DUP, BTRFS_BLOCK_GROUP_DUP, 0 }, + { "single", BTRFS_BLOCK_GROUP_SINGLE, 0, BTRFS_BLOCK_GROUP_SINGLE }, + { NULL, 0, 0, 0 } +}; + +int parse_filter(struct btrfs_ioctl_balance_start *args, char *filters_string) +{ + char *this_filter_string; + char *saveptr; + + printf("(entry) %s Args: required %llx, mask %llx\n", filters_string, args->chunk_type, args->chunk_type_mask); + + /* Parse the filters string, if there is one */ + this_filter_string = strtok_r(filters_string, ":", &saveptr); + while(this_filter_string) { + char *subsave; + char *part; + char *type = strtok_r(this_filter_string, "=,", &subsave); + int class_id = -1; + + /* Work out what filter type we're looking at */ + if(strcmp(type, "help") == 0) { + while(filter_class[++class_id].keyword) { + printf("%s", filter_class[class_id].description); + } + return 1; + } + + while(filter_class[++class_id].keyword) { + if(strcmp(type, filter_class[class_id].keyword) == 0) + break; + } + if(filter_class[class_id].keyword == NULL) { + fprintf(stderr, "ERROR: Unknown filter type '%s'\n", type); + free(args); + return 14; + } + + /* Mark this filter class as being in use */ + args->flags |= filter_class[class_id].flag; + + /* Parse the arguments for this filter */ + part = strtok_r(NULL, "=,", &subsave); + + switch(filter_class[class_id].flag) { + case BTRFS_BALANCE_FILTER_CHUNK_TYPE: + while(part) { + int negated = 0; + int i = 0; + if(part[0] == '~') { + negated = 1; + part += 1; + } + while(type_filters[i].keyword) { + if(strcmp(part, type_filters[i].keyword) == 0) + break; + i += 1; + } + if(type_filters[i].keyword == NULL) { + fprintf(stderr, "ERROR: Unknown chunk type '%s'\n", part); + free(args); + return 15; + } + + args->chunk_type_mask |= type_filters[i].mask; + args->chunk_type &= ~type_filters[i].mask; + if (negated) + args->chunk_type |= type_filters[i].unset; + else + args->chunk_type |= type_filters[i].set; + + part = strtok_r(NULL, "=,", &subsave); + } + break; + } + + this_filter_string = strtok_r(NULL, ":", &saveptr); + } + + printf("(exit) %s Args: required %llx, mask %llx\n", filters_string, args->chunk_type, args->chunk_type_mask); + + return 0; +} + int do_balance(int argc, char **argv) { int fdmnt, ret=0; int background = 1; - struct btrfs_ioctl_vol_args args; + struct btrfs_ioctl_balance_start *args; char *path; int ttyfd; + int verbose = 0; + int count_only = 0; + + args = malloc(4096); + if (!args) { + fprintf(stderr, "ERROR: Not enough memory\n"); + return 13; + } + + args->flags = 0; + args->chunk_type = 0; + args->chunk_type_mask = 0; optind = 1; while(1) { - int c = getopt_long(argc, argv, "w", balance_options, NULL); + int c = getopt_long(argc, argv, "wf:", balance_options, NULL); if (c < 0) break; switch(c) { case 'w': background = 0; break; + case 'f': + ret = parse_filter(args, optarg); + if (ret != 0) { + free(args); + return ret; + } + break; + case 'c': + count_only = 1; + background = 0; + /* Counting is only sensible if we also print some output. */ + case 'v': + verbose = 1; + break; default: fprintf(stderr, "Invalid arguments for balance\n"); free(argv); @@ -783,6 +932,9 @@ int do_balance(int argc, char **argv) } } + if (background) + verbose = 0; + if(optind >= argc) { fprintf(stderr, "No filesystem path given for balance\n"); return 1; @@ -796,6 +948,9 @@ int do_balance(int argc, char **argv) return 12; } + if (count_only) + args->flags |= BTRFS_BALANCE_FILTER_COUNT_ONLY; + if (background) { int pid = fork(); if (pid == 0) { @@ -815,14 +970,20 @@ int do_balance(int argc, char **argv) } } - memset(&args, 0, sizeof(args)); - ret = ioctl(fdmnt, BTRFS_IOC_BALANCE, &args); + ret = ioctl(fdmnt, BTRFS_IOC_BALANCE_FILTERED, args); + free(args); close(fdmnt); if(ret<0){ fprintf(stderr, "ERROR: balancing '%s'\n", path); return 19; } + + if (verbose) { + printf("%llu chunks considered, %llu chunks balanced\n", + args->examined, args->balanced); + } + return 0; } diff --git a/ioctl.h b/ioctl.h index 40c0b57..6488e82 100644 --- a/ioctl.h +++ b/ioctl.h @@ -137,6 +137,28 @@ struct btrfs_ioctl_balance_progress { __u32 completed; }; +/* Types of balance filter */ +#define BTRFS_BALANCE_FILTER_COUNT_ONLY 0x1 + +#define BTRFS_BALANCE_FILTER_CHUNK_TYPE 0x2 +#define BTRFS_BALANCE_FILTER_MASK 0x3 + +/* All the possible options for a filter */ +struct btrfs_ioctl_balance_start { + __u64 flags; /* Bit field indicating which fields of this struct are filled */ + + /* Output values: chunk counts */ + __u64 examined; + __u64 balanced; + + /* For FILTER_CHUNK_TYPE */ + __u64 chunk_type; /* Flag bits required */ + __u64 chunk_type_mask; /* Mask of bits to examine */ + + __u64 spare[506]; /* Make up the size of the structure to 4088 + * bytes for future expansion */ +}; + #define BTRFS_IOC_SNAP_CREATE _IOW(BTRFS_IOCTL_MAGIC, 1, \ struct btrfs_ioctl_vol_args) #define BTRFS_IOC_DEFRAG _IOW(BTRFS_IOCTL_MAGIC, 2, \ @@ -177,4 +199,6 @@ struct btrfs_ioctl_balance_progress { #define BTRFS_IOC_BALANCE_PROGRESS _IOR(BTRFS_IOCTL_MAGIC, 27, \ struct btrfs_ioctl_balance_progress) #define BTRFS_IOC_BALANCE_CANCEL _IO(BTRFS_IOCTL_MAGIC, 28) +#define BTRFS_IOC_BALANCE_FILTERED _IOWR(BTRFS_IOCTL_MAGIC, 29, \ + struct btrfs_ioctl_balance_start) #endif diff --git a/man/btrfs.8.in b/man/btrfs.8.in index 95e39c3..3023eb5 100644 --- a/man/btrfs.8.in +++ b/man/btrfs.8.in @@ -31,7 +31,7 @@ btrfs \- control a btrfs filesystem .PP \fBbtrfs\fP \fBdevice show\fP\fI <dev>|<label> [<dev>|<label>...]\fP .PP -\fBbtrfs\fP \fBfilesystem balance\fP [\fB-w\fP|\fB--wait\fP] \fI<path>\fP +\fBbtrfs\fP \fBfilesystem balance\fP [\fB-wcv\fP] [\fB--wait\fP] [\fB--count\fP] [\fB--verbose\fP] [\fB-f\fP|\fBfilter=\fP\fI<filter>\fP] \fI<path>\fP .PP \fBbtrfs\fP \fBdevice add\fP\fI <dev> [<dev>..] <path> \fP .PP @@ -149,13 +149,18 @@ Show the btrfs filesystem with some additional info. If no UUID or label is passed, \fBbtrfs\fR show info of all the btrfs filesystem. .TP -\fBdevice balance\fR [\fB-w\fP|\fB--wait\fP] \fI<path>\fR +\fBdevice balance\fP [\fB-wcv\fP] [\fB--wait\fP] [\fB--count\fP] [\fB--verbose\fP] [\fB-f\fP|\fBfilter=\fP\fI<filter>\fP] \fI<path>\fP -\fBbalance start\fR [\fB-w\fP|\fB--wait\fP] \fI<path>\fR +\fBbalance start\fR [\fB-wcv\fP] [\fB--wait\fP] [\fB--count\fP] [\fB--verbose\fP] [\fB-f\fP|\fBfilter=\fP\fI<filter>\fP] \fI<path>\fP Balance the chunks of the filesystem identified by \fI<path>\fR across -the devices. The process runs in the background. Use \fB--wait\fP to -wait in the foreground for completion of the balance. +the devices. The command returns immediately, and the balance +operation runs in the background. Use \fB--wait\fP to run +synchronously instead. Use \fB--count\fP to scan the filesystem and +report the number of chunks that would be processed. Use +\fB--verbose\fP in synchronous mode to report the number of chunks +examined and balanced. See \fBBALANCE FILTERS\fR, below, for details +of the different filter types and syntax. .TP \fBdevice add\fR\fI <dev> [<dev>..] <path>\fR @@ -171,10 +176,33 @@ Report progress on the currently-running balance operation on the filesystem mounted at \fI<path>\fP. Use --monitor to report progress continually, including an estimate of completion time. -\fbalance cancel\fP \fI<path>\fP +\fBbalance cancel\fP \fI<path>\fP Cancel the balance currently running on the filesystem mounted at \fI<path>\fP. +.SH BALANCE FILTERS +With balance filters, it is possible to perform a balance operation on +only a subset of the available chunks. Filters are specified with the +\fB--filter\fR option of \fBbtrfs device balance\fR or \fBbtrfs +balance start\fR. Multiple filters may be given, either with multiple +\fB--filter\fR options, or in a colon-separated list. When multiple +filters are given, only the chunks meeting all of the selection +critera are balanced. Help on the avaialble filters can be obtained +with \fB--filter=help\fR. + +.TP +\fBtype\fR=[\fB~\fR]\fI<flagname>\fR[\fB,\fR...] + +Select only the chunks with the given type flag(s). Requiring a flag +to be off can be specified with a \fB~\fR preceding the flag +name. Flag names are: + +\fBmeta\fR, \fBdata\fR, \fBsys\fR for metadata, file data and system +chunk types. + +\fBraid0\fR, \fBraid1\fR, \fBraid10\fR, \fBdup\fR for chunks of the +given replication levels. + .SH EXIT STATUS \fBbtrfs\fR returns a zero exist status if it succeeds. Non zero is returned in case of failure. -- 1.7.2.5 -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html