Btrfs-progs: enhance btrfs qgroup show to sort qgroups
authorWang Shilong <wangsl-fnst@cn.fujitsu.com>
Mon, 7 Oct 2013 07:21:44 +0000 (15:21 +0800)
committerChris Mason <chris.mason@fusionio.com>
Wed, 16 Oct 2013 12:23:12 +0000 (08:23 -0400)
You might want to list qgroups in order of some items, such as 'qgroupid', 'rfer'
and so on, you can use '--sort'. Now you can sort the qgroups by 'qgroupid',
'rfer','excl','max_rfer' and 'max_excl'.

For example:
If you want to list qgroups in order of 'qgroupid'.
You can use the option like that:

btrfs qgroup show --sort=+/-qgroupid <path>
Here, '+' means the result is sorted by ascending order. '-' is by descending
order. If you don't specify either '+' nor '-', the result is sorted by
default - ascending order.

If you want to combine sort items, you do it like that:
btrfs qgroup show  --sort=-qgroupid,+rfer,max_rfer,excl <path>

Signed-off-by: Wang Shilong <wangsl-fnst@cn.fujitsu.com>
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
Signed-off-by: David Sterba <dsterba@suse.cz>
Signed-off-by: Chris Mason <chris.mason@fusionio.com>
cmds-qgroup.c
qgroup.c
qgroup.h

index 5f1550a723263082caf4b7cad45657f36bb7ece8..4fe776c031601abe55684604af935b7442e8ec8e 100644 (file)
@@ -202,7 +202,8 @@ static int cmd_qgroup_destroy(int argc, char **argv)
 }
 
 static const char * const cmd_qgroup_show_usage[] = {
-       "btrfs qgroup show -pcreFf <path>",
+       "btrfs qgroup show -pcreFf "
+       "[--sort=qgroupid,rfer,excl,max_rfer,max_excl] <path>",
        "Show subvolume quota groups.",
        "-p             print parent qgroup id",
        "-c             print child qgroup id",
@@ -212,6 +213,11 @@ static const char * const cmd_qgroup_show_usage[] = {
        "(include ancestral qgroups)",
        "-f             list all qgroups which impact the given path"
        "(exclude ancestral qgroups)",
+       "--sort=qgroupid,rfer,excl,max_rfer,max_excl",
+       "               list qgroups in order of qgroupid,"
+       "rfer,max_rfer or max_excl",
+       "               you can use '+' or '-' in front of each item.",
+       "               (+:ascending, -:descending, ascending default)",
        NULL
 };
 
@@ -226,12 +232,19 @@ static int cmd_qgroup_show(int argc, char **argv)
        u64 qgroupid;
        int filter_flag = 0;
 
+       struct btrfs_qgroup_comparer_set *comparer_set;
        struct btrfs_qgroup_filter_set *filter_set;
        filter_set = btrfs_qgroup_alloc_filter_set();
+       comparer_set = btrfs_qgroup_alloc_comparer_set();
+       struct option long_options[] = {
+               {"sort", 1, NULL, 'S'},
+               {0, 0, 0, 0}
+       };
 
        optind = 1;
        while (1) {
-               c = getopt(argc, argv, "pcreFf");
+               c = getopt_long(argc, argv, "pcreFf",
+                               long_options, NULL);
                if (c < 0)
                        break;
                switch (c) {
@@ -257,6 +270,12 @@ static int cmd_qgroup_show(int argc, char **argv)
                case 'f':
                        filter_flag |= 0x2;
                        break;
+               case 'S':
+                       ret = btrfs_qgroup_parse_sort_string(optarg,
+                                                            &comparer_set);
+                       if (ret)
+                               usage(cmd_qgroup_show_usage);
+                       break;
                default:
                        usage(cmd_qgroup_show_usage);
                }
@@ -282,7 +301,7 @@ static int cmd_qgroup_show(int argc, char **argv)
                                        BTRFS_QGROUP_FILTER_PARENT,
                                        qgroupid);
        }
-       ret = btrfs_show_qgroups(fd, filter_set);
+       ret = btrfs_show_qgroups(fd, filter_set, comparer_set);
        e = errno;
        close_file_or_dir(fd, dirstream);
        if (ret < 0)
index 28772d6d6b1c69f7becb01e1deb482d86ed1a9a0..84f5fc1d85688db93b88e3ac559fc24f4ee31dbb 100644 (file)
--- a/qgroup.c
+++ b/qgroup.c
@@ -22,6 +22,7 @@
 #include "ioctl.h"
 
 #define BTRFS_QGROUP_NFILTERS_INCREASE (2 * BTRFS_QGROUP_FILTER_MAX)
+#define BTRFS_QGROUP_NCOMPS_INCREASE (2 * BTRFS_QGROUP_COMP_MAX)
 
 struct qgroup_lookup {
        struct rb_root root;
@@ -122,6 +123,7 @@ struct {
 };
 
 static btrfs_qgroup_filter_func all_filter_funcs[];
+static btrfs_qgroup_comp_func all_comp_funcs[];
 
 void btrfs_qgroup_setup_print_column(enum btrfs_qgroup_column_enum column)
 {
@@ -236,6 +238,188 @@ static int comp_entry_with_qgroupid(struct btrfs_qgroup *entry1,
        return is_descending ? -ret : ret;
 }
 
+static int comp_entry_with_rfer(struct btrfs_qgroup *entry1,
+                               struct btrfs_qgroup *entry2,
+                               int is_descending)
+{
+       int ret;
+
+       if (entry1->rfer > entry2->rfer)
+               ret = 1;
+       else if (entry1->rfer < entry2->rfer)
+               ret = -1;
+       else
+               ret = 0;
+
+       return is_descending ? -ret : ret;
+}
+
+static int comp_entry_with_excl(struct btrfs_qgroup *entry1,
+                               struct btrfs_qgroup *entry2,
+                               int is_descending)
+{
+       int ret;
+
+       if (entry1->excl > entry2->excl)
+               ret = 1;
+       else if (entry1->excl < entry2->excl)
+               ret = -1;
+       else
+               ret = 0;
+
+       return is_descending ? -ret : ret;
+}
+
+static int comp_entry_with_max_rfer(struct btrfs_qgroup *entry1,
+                                   struct btrfs_qgroup *entry2,
+                                   int is_descending)
+{
+       int ret;
+
+       if (entry1->max_rfer > entry2->max_rfer)
+               ret = 1;
+       else if (entry1->max_rfer < entry2->max_rfer)
+               ret = -1;
+       else
+               ret = 0;
+
+       return is_descending ? -ret : ret;
+}
+
+static int comp_entry_with_max_excl(struct btrfs_qgroup *entry1,
+                                   struct btrfs_qgroup *entry2,
+                                   int is_descending)
+{
+       int ret;
+
+       if (entry1->max_excl > entry2->max_excl)
+               ret = 1;
+       else if (entry1->max_excl < entry2->max_excl)
+               ret = -1;
+       else
+               ret = 0;
+
+       return is_descending ? -ret : ret;
+}
+
+static btrfs_qgroup_comp_func all_comp_funcs[] = {
+       [BTRFS_QGROUP_COMP_QGROUPID]    = comp_entry_with_qgroupid,
+       [BTRFS_QGROUP_COMP_RFER]        = comp_entry_with_rfer,
+       [BTRFS_QGROUP_COMP_EXCL]        = comp_entry_with_excl,
+       [BTRFS_QGROUP_COMP_MAX_RFER]    = comp_entry_with_max_rfer,
+       [BTRFS_QGROUP_COMP_MAX_EXCL]    = comp_entry_with_max_excl
+};
+
+static char *all_sort_items[] = {
+       [BTRFS_QGROUP_COMP_QGROUPID]    = "qgroupid",
+       [BTRFS_QGROUP_COMP_RFER]        = "rfer",
+       [BTRFS_QGROUP_COMP_EXCL]        = "excl",
+       [BTRFS_QGROUP_COMP_MAX_RFER]    = "max_rfer",
+       [BTRFS_QGROUP_COMP_MAX_EXCL]    = "max_excl",
+       [BTRFS_QGROUP_COMP_MAX]         = NULL,
+};
+
+static int  btrfs_qgroup_get_sort_item(char *sort_name)
+{
+       int i;
+
+       for (i = 0; i < BTRFS_QGROUP_COMP_MAX; i++) {
+               if (strcmp(sort_name, all_sort_items[i]) == 0)
+                       return i;
+       }
+       return -1;
+}
+
+struct btrfs_qgroup_comparer_set *btrfs_qgroup_alloc_comparer_set(void)
+{
+       struct btrfs_qgroup_comparer_set *set;
+       int size;
+       size = sizeof(struct btrfs_qgroup_comparer_set) +
+              BTRFS_QGROUP_NCOMPS_INCREASE *
+              sizeof(struct btrfs_qgroup_comparer);
+       set = malloc(size);
+       if (!set) {
+               fprintf(stderr, "memory allocation failed\n");
+               exit(1);
+       }
+
+       memset(set, 0, size);
+       set->total = BTRFS_QGROUP_NCOMPS_INCREASE;
+
+       return set;
+}
+
+void btrfs_qgroup_free_comparer_set(struct btrfs_qgroup_comparer_set *comp_set)
+{
+       free(comp_set);
+}
+
+int btrfs_qgroup_setup_comparer(struct btrfs_qgroup_comparer_set  **comp_set,
+                               enum btrfs_qgroup_comp_enum comparer,
+                               int is_descending)
+{
+       struct btrfs_qgroup_comparer_set *set = *comp_set;
+       int size;
+
+       BUG_ON(!set);
+       BUG_ON(comparer >= BTRFS_QGROUP_COMP_MAX);
+       BUG_ON(set->ncomps > set->total);
+
+       if (set->ncomps == set->total) {
+               size = set->total + BTRFS_QGROUP_NCOMPS_INCREASE;
+               size = sizeof(*set) +
+                      size * sizeof(struct btrfs_qgroup_comparer);
+               set = realloc(set, size);
+               if (!set) {
+                       fprintf(stderr, "memory allocation failed\n");
+                       exit(1);
+               }
+
+               memset(&set->comps[set->total], 0,
+                      BTRFS_QGROUP_NCOMPS_INCREASE *
+                      sizeof(struct btrfs_qgroup_comparer));
+               set->total += BTRFS_QGROUP_NCOMPS_INCREASE;
+               *comp_set = set;
+       }
+
+       BUG_ON(set->comps[set->ncomps].comp_func);
+
+       set->comps[set->ncomps].comp_func = all_comp_funcs[comparer];
+       set->comps[set->ncomps].is_descending = is_descending;
+       set->ncomps++;
+       return 0;
+}
+
+static int sort_comp(struct btrfs_qgroup *entry1, struct btrfs_qgroup *entry2,
+                    struct btrfs_qgroup_comparer_set *set)
+{
+       int qgroupid_compared = 0;
+       int i, ret = 0;
+
+       if (!set || !set->ncomps)
+               goto comp_qgroupid;
+
+       for (i = 0; i < set->ncomps; i++) {
+               if (!set->comps[i].comp_func)
+                       break;
+
+               ret = set->comps[i].comp_func(entry1, entry2,
+                                             set->comps[i].is_descending);
+               if (ret)
+                       return ret;
+
+               if (set->comps[i].comp_func == comp_entry_with_qgroupid)
+                       qgroupid_compared = 1;
+       }
+
+       if (!qgroupid_compared) {
+comp_qgroupid:
+               ret = comp_entry_with_qgroupid(entry1, entry2, 0);
+       }
+
+       return ret;
+}
+
 /*
  * insert a new root into the tree.  returns the existing root entry
  * if one is already there.  qgroupid is used
@@ -610,7 +794,8 @@ static void pre_process_filter_set(struct qgroup_lookup *lookup,
 }
 
 static int sort_tree_insert(struct qgroup_lookup *sort_tree,
-                           struct btrfs_qgroup *bq)
+                           struct btrfs_qgroup *bq,
+                           struct btrfs_qgroup_comparer_set *comp_set)
 {
        struct rb_node **p = &sort_tree->root.rb_node;
        struct rb_node *parent = NULL;
@@ -621,7 +806,7 @@ static int sort_tree_insert(struct qgroup_lookup *sort_tree,
                parent = *p;
                curr = rb_entry(parent, struct btrfs_qgroup, sort_node);
 
-               ret = comp_entry_with_qgroupid(bq, curr, 0);
+               ret = sort_comp(bq, curr, comp_set);
                if (ret < 0)
                        p = &(*p)->rb_left;
                else if (ret > 0)
@@ -634,9 +819,10 @@ static int sort_tree_insert(struct qgroup_lookup *sort_tree,
        return 0;
 }
 
-static void __filter_all_qgroups(struct qgroup_lookup *all_qgroups,
+static void __filter_and_sort_qgroups(struct qgroup_lookup *all_qgroups,
                                 struct qgroup_lookup *sort_tree,
-                                struct btrfs_qgroup_filter_set *filter_set)
+                                struct btrfs_qgroup_filter_set *filter_set,
+                                struct btrfs_qgroup_comparer_set *comp_set)
 {
        struct rb_node *n;
        struct btrfs_qgroup *entry;
@@ -651,7 +837,7 @@ static void __filter_all_qgroups(struct qgroup_lookup *all_qgroups,
 
                ret = filter_qgroup(entry, filter_set);
                if (ret)
-                       sort_tree_insert(sort_tree, entry);
+                       sort_tree_insert(sort_tree, entry, comp_set);
 
                n = rb_prev(n);
        }
@@ -795,7 +981,8 @@ static void print_all_qgroups(struct qgroup_lookup *qgroup_lookup)
 }
 
 int btrfs_show_qgroups(int fd,
-                      struct btrfs_qgroup_filter_set *filter_set)
+                      struct btrfs_qgroup_filter_set *filter_set,
+                      struct btrfs_qgroup_comparer_set *comp_set)
 {
 
        struct qgroup_lookup qgroup_lookup;
@@ -805,8 +992,8 @@ int btrfs_show_qgroups(int fd,
        ret = __qgroups_search(fd, &qgroup_lookup);
        if (ret)
                return ret;
-       __filter_all_qgroups(&qgroup_lookup, &sort_tree,
-                            filter_set);
+       __filter_and_sort_qgroups(&qgroup_lookup, &sort_tree,
+                                 filter_set, comp_set);
        print_all_qgroups(&sort_tree);
 
        __free_all_qgroups(&qgroup_lookup);
@@ -832,6 +1019,59 @@ u64 btrfs_get_path_rootid(int fd)
        return args.treeid;
 }
 
+int btrfs_qgroup_parse_sort_string(char *opt_arg,
+                                  struct btrfs_qgroup_comparer_set **comps)
+{
+       int order;
+       int flag;
+       char *p;
+       char **ptr_argv;
+       int what_to_sort;
+
+       while ((p = strtok(opt_arg, ",")) != NULL) {
+               flag = 0;
+               ptr_argv = all_sort_items;
+
+               while (*ptr_argv) {
+                       if (strcmp(*ptr_argv, p) == 0) {
+                               flag = 1;
+                               break;
+                       } else {
+                               p++;
+                               if (strcmp(*ptr_argv, p) == 0) {
+                                       flag = 1;
+                                       p--;
+                                       break;
+                               }
+                               p--;
+                       }
+                       ptr_argv++;
+               }
+
+               if (flag == 0)
+                       return -1;
+
+               else {
+                       if (*p == '+') {
+                               order = 0;
+                               p++;
+                       } else if (*p == '-') {
+                               order = 1;
+                               p++;
+                       } else
+                               order = 0;
+
+                       what_to_sort = btrfs_qgroup_get_sort_item(p);
+                       if (what_to_sort < 0)
+                               return -1;
+                       btrfs_qgroup_setup_comparer(comps, what_to_sort, order);
+               }
+               opt_arg = NULL;
+       }
+
+       return 0;
+}
+
 u64 parse_qgroupid(char *p)
 {
        char *s = strchr(p, '/');
index 5fcdd8abe22426d4a517a3fd79eba6b31e062c12..653cf1c7176b7678666528ee8c3a111231cdc674 100644 (file)
--- a/qgroup.h
+++ b/qgroup.h
 struct btrfs_qgroup;
 
 typedef int (*btrfs_qgroup_filter_func)(struct btrfs_qgroup *, u64);
+typedef int (*btrfs_qgroup_comp_func)(struct btrfs_qgroup *,
+                                     struct btrfs_qgroup *, int);
+
 
 struct btrfs_qgroup_filter {
        btrfs_qgroup_filter_func filter_func;
        u64 data;
 };
 
+struct btrfs_qgroup_comparer {
+       btrfs_qgroup_comp_func comp_func;
+       int is_descending;
+};
+
 struct btrfs_qgroup_filter_set {
        int total;
        int nfilters;
        struct btrfs_qgroup_filter filters[0];
 };
 
+struct btrfs_qgroup_comparer_set {
+       int total;
+       int ncomps;
+       struct btrfs_qgroup_comparer comps[0];
+};
+
 enum btrfs_qgroup_column_enum {
        BTRFS_QGROUP_QGROUPID,
        BTRFS_QGROUP_RFER,
@@ -48,19 +62,36 @@ enum btrfs_qgroup_column_enum {
        BTRFS_QGROUP_ALL,
 };
 
+enum btrfs_qgroup_comp_enum {
+       BTRFS_QGROUP_COMP_QGROUPID,
+       BTRFS_QGROUP_COMP_RFER,
+       BTRFS_QGROUP_COMP_EXCL,
+       BTRFS_QGROUP_COMP_MAX_RFER,
+       BTRFS_QGROUP_COMP_MAX_EXCL,
+       BTRFS_QGROUP_COMP_MAX
+};
+
 enum btrfs_qgroup_filter_enum {
        BTRFS_QGROUP_FILTER_PARENT,
        BTRFS_QGROUP_FILTER_ALL_PARENT,
        BTRFS_QGROUP_FILTER_MAX,
 };
 
+int btrfs_qgroup_parse_sort_string(char *opt_arg,
+                               struct btrfs_qgroup_comparer_set **comps);
 u64 btrfs_get_path_rootid(int fd);
-int btrfs_show_qgroups(int fd, struct btrfs_qgroup_filter_set *);
+int btrfs_show_qgroups(int fd, struct btrfs_qgroup_filter_set *,
+                      struct btrfs_qgroup_comparer_set *);
 void btrfs_qgroup_setup_print_column(enum btrfs_qgroup_column_enum column);
 struct btrfs_qgroup_filter_set *btrfs_qgroup_alloc_filter_set(void);
 void btrfs_qgroup_free_filter_set(struct btrfs_qgroup_filter_set *filter_set);
 int btrfs_qgroup_setup_filter(struct btrfs_qgroup_filter_set **filter_set,
                              enum btrfs_qgroup_filter_enum, u64 data);
+struct btrfs_qgroup_comparer_set *btrfs_qgroup_alloc_comparer_set(void);
+void btrfs_qgroup_free_comparer_set(struct btrfs_qgroup_comparer_set *comp_set);
+int btrfs_qgroup_setup_comparer(struct btrfs_qgroup_comparer_set **comp_set,
+                               enum btrfs_qgroup_comp_enum comparer,
+                               int is_descending);
 u64 parse_qgroupid(char *p);
 int qgroup_inherit_size(struct btrfs_qgroup_inherit *p);
 int qgroup_inherit_add_group(struct btrfs_qgroup_inherit **inherit, char *arg);