gfio: improve option edit
authorJens Axboe <axboe@kernel.dk>
Mon, 19 Mar 2012 13:37:08 +0000 (14:37 +0100)
committerJens Axboe <axboe@kernel.dk>
Mon, 19 Mar 2012 13:37:08 +0000 (14:37 +0100)
Allow grouping of options into both categories, and groups. The
groups are within a category. This shrinks down the number of
job edit tabs a lot.

Work in progress...

Signed-off-by: Jens Axboe <axboe@kernel.dk>
engines/libaio.c
engines/net.c
goptions.c
options.c
options.h
parse.c
parse.h
profiles/tiobench.c

index 6bbd363..2b8c6da 100644 (file)
@@ -32,10 +32,11 @@ struct libaio_options {
 static struct fio_option options[] = {
        {
                .name   = "userspace_reap",
+               .lname  = "Libaio userspace reaping",
                .type   = FIO_OPT_STR_SET,
                .off1   = offsetof(struct libaio_options, userspace_reap),
                .help   = "Use alternative user-space reap implementation",
-               .category = FIO_OPT_G_IO_ENG,
+               .category = FIO_OPT_C_IO,
        },
        {
                .name   = NULL,
index 55c8ab2..636939e 100644 (file)
@@ -53,28 +53,31 @@ static int str_hostname_cb(void *data, const char *input);
 static struct fio_option options[] = {
        {
                .name   = "hostname",
+               .lname  = "net engine hostname",
                .type   = FIO_OPT_STR_STORE,
                .cb     = str_hostname_cb,
                .help   = "Hostname for net IO engine",
-               .category = FIO_OPT_G_IO_ENG,
+               .category = FIO_OPT_C_IO,
        },
        {
                .name   = "port",
+               .lname  = "net engine port",
                .type   = FIO_OPT_INT,
                .off1   = offsetof(struct netio_options, port),
                .minval = 1,
                .maxval = 65535,
                .help   = "Port to use for TCP or UDP net connections",
-               .category = FIO_OPT_G_IO_ENG,
+               .category = FIO_OPT_C_IO,
        },
        {
                .name   = "protocol",
+               .lname  = "net engine protocol",
                .alias  = "proto",
                .type   = FIO_OPT_STR,
                .off1   = offsetof(struct netio_options, proto),
                .help   = "Network protocol to use",
                .def    = "tcp",
-               .category = FIO_OPT_G_IO_ENG,
+               .category = FIO_OPT_C_IO,
                .posval = {
                          { .ival = "tcp",
                            .oval = FIO_TYPE_TCP,
@@ -92,10 +95,11 @@ static struct fio_option options[] = {
        },
        {
                .name   = "listen",
+               .lname  = "net engine listen",
                .type   = FIO_OPT_STR_SET,
                .off1   = offsetof(struct netio_options, listen),
                .help   = "Listen for incoming TCP connections",
-               .category = FIO_OPT_G_IO_ENG,
+               .category = FIO_OPT_C_IO,
        },
        {
                .name   = NULL,
index d7178eb..127f5d3 100644 (file)
@@ -44,22 +44,45 @@ struct gopt_range {
        GtkWidget *spins[GOPT_RANGE_SPIN];
 };
 
-struct gopt_widget {
-       struct flist_head list;
-       GtkWidget *widget;
-};
+static GtkWidget *gopt_widgets[FIO_MAX_OPTS];
 
-static struct flist_head gopt_list[FIO_MAX_OPTS];
+struct gopt_frame_widget {
+       GtkWidget *vbox[2];
+       unsigned int nr;
+};
+static struct gopt_frame_widget gopt_g_widgets[__FIO_OPT_G_NR];
 
-static void __gopt_set_children_visible(unsigned int idx, gboolean visible)
+static GtkWidget *gopt_get_group_frame(GtkWidget *box, unsigned int groupmask)
 {
-       struct flist_head *entry;
-       struct gopt_widget *gw;
-
-       flist_for_each(entry, &gopt_list[idx]) {
-               gw = flist_entry(entry, struct gopt_widget, list);
-               gtk_widget_set_sensitive(gw->widget, visible);
+       unsigned int mask, group;
+       struct opt_group *og;
+       GtkWidget *frame, *hbox;
+       struct gopt_frame_widget *gfw;
+
+       if (!groupmask)
+               return 0;
+
+       mask = groupmask;
+       og = opt_group_cat_from_mask(&mask);
+       if (!og)
+               return NULL;
+
+       group = ffz(~groupmask);
+       gfw = &gopt_g_widgets[group];
+       if (!gfw->vbox[0]) {
+               frame = gtk_frame_new(og->name);
+               gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 3);
+               hbox = gtk_hbox_new(FALSE, 0);
+               gtk_container_add(GTK_CONTAINER(frame), hbox);
+               gfw->vbox[0] = gtk_vbox_new(TRUE, 5);
+               gfw->vbox[1] = gtk_vbox_new(TRUE, 5);
+               gtk_box_pack_start(GTK_BOX(hbox), gfw->vbox[0], TRUE, TRUE, 5);
+               gtk_box_pack_start(GTK_BOX(hbox), gfw->vbox[1], TRUE, TRUE, 5);
        }
+
+       hbox = gtk_hbox_new(FALSE, 3);
+       gtk_box_pack_start(GTK_BOX(gfw->vbox[gfw->nr++ & 1]), hbox, FALSE, FALSE, 5);
+       return hbox;
 }
 
 /*
@@ -83,7 +106,8 @@ static void gopt_set_children_visible(struct fio_option *parent, gboolean visibl
                if (strcmp(parent->name, o->parent))
                        continue;
 
-               __gopt_set_children_visible(i, visible);
+               if (gopt_widgets[i])
+                       gtk_widget_set_sensitive(gopt_widgets[i], visible);
        }
 }
 
@@ -101,13 +125,9 @@ static void gopt_str_changed(GtkEntry *entry, gpointer data)
 
 static void gopt_mark_index(struct gopt *gopt, unsigned int idx)
 {
-       struct gopt_widget *gw;
-
+       assert(!gopt_widgets[idx]);
        gopt->opt_index = idx;
-
-       gw = malloc(sizeof(*gw));
-       gw->widget = gopt->box;
-       flist_add_tail(&gw->list, &gopt_list[idx]);
+       gopt_widgets[idx] = gopt->box;
 }
 
 static struct gopt *gopt_new_str_store(struct fio_option *o, const char *text, unsigned int idx)
@@ -118,8 +138,10 @@ static struct gopt *gopt_new_str_store(struct fio_option *o, const char *text, u
        s = malloc(sizeof(*s));
 
        s->gopt.box = gtk_hbox_new(FALSE, 3);
-       label = gtk_label_new(o->name);
-       gtk_box_pack_start(GTK_BOX(s->gopt.box), label, FALSE, FALSE, 0);
+       if (!o->lname)
+               label = gtk_label_new(o->name);
+       else
+               label = gtk_label_new(o->lname);
 
        s->entry = gtk_entry_new();
        gopt_mark_index(&s->gopt, idx);
@@ -132,6 +154,7 @@ static struct gopt *gopt_new_str_store(struct fio_option *o, const char *text, u
                gtk_entry_set_text(GTK_ENTRY(s->entry), o->def);
 
        gtk_box_pack_start(GTK_BOX(s->gopt.box), s->entry, FALSE, FALSE, 0);
+       gtk_box_pack_start(GTK_BOX(s->gopt.box), label, FALSE, FALSE, 0);
        return &s->gopt;
 }
 
@@ -151,15 +174,18 @@ static struct gopt_combo *__gopt_new_combo(struct fio_option *o, unsigned int id
        c = malloc(sizeof(*c));
 
        c->gopt.box = gtk_hbox_new(FALSE, 3);
-       label = gtk_label_new(o->name);
-       gtk_box_pack_start(GTK_BOX(c->gopt.box), label, FALSE, FALSE, 0);
+       if (!o->lname)
+               label = gtk_label_new(o->name);
+       else
+               label = gtk_label_new(o->lname);
 
        c->combo = gtk_combo_box_new_text();
        gopt_mark_index(&c->gopt, idx);
-       gtk_box_pack_start(GTK_BOX(c->gopt.box), c->combo, FALSE, FALSE, 0);
-
        g_signal_connect(GTK_OBJECT(c->combo), "changed", G_CALLBACK(gopt_combo_changed), c);
 
+       gtk_box_pack_start(GTK_BOX(c->gopt.box), c->combo, FALSE, FALSE, 0);
+       gtk_box_pack_start(GTK_BOX(c->gopt.box), label, FALSE, FALSE, 0);
+
        return c;
 }
 
@@ -227,8 +253,10 @@ static struct gopt_int *__gopt_new_int(struct fio_option *o, unsigned long long
 
        i = malloc(sizeof(*i));
        i->gopt.box = gtk_hbox_new(FALSE, 3);
-       label = gtk_label_new(o->name);
-       gtk_box_pack_start(GTK_BOX(i->gopt.box), label, FALSE, FALSE, 0);
+       if (!o->lname)
+               label = gtk_label_new(o->name);
+       else
+               label = gtk_label_new(o->lname);
 
        maxval = o->maxval;
        if (!maxval)
@@ -252,10 +280,10 @@ static struct gopt_int *__gopt_new_int(struct fio_option *o, unsigned long long
        gopt_mark_index(&i->gopt, idx);
        gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(i->spin), GTK_UPDATE_IF_VALID);
        gtk_spin_button_set_value(GTK_SPIN_BUTTON(i->spin), defval);
+       g_signal_connect(G_OBJECT(i->spin), "value-changed", G_CALLBACK(gopt_int_changed), i);
 
        gtk_box_pack_start(GTK_BOX(i->gopt.box), i->spin, FALSE, FALSE, 0);
-
-       g_signal_connect(G_OBJECT(i->spin), "value-changed", G_CALLBACK(gopt_int_changed), i);
+       gtk_box_pack_start(GTK_BOX(i->gopt.box), label, FALSE, FALSE, 0);
 
        return i;
 }
@@ -301,8 +329,10 @@ static struct gopt *gopt_new_bool(struct fio_option *o, unsigned int *val, unsig
 
        b = malloc(sizeof(*b));
        b->gopt.box = gtk_hbox_new(FALSE, 3);
-       label = gtk_label_new(o->name);
-       gtk_box_pack_start(GTK_BOX(b->gopt.box), label, FALSE, FALSE, 0);
+       if (!o->lname)
+               label = gtk_label_new(o->name);
+       else
+               label = gtk_label_new(o->lname);
 
        b->check = gtk_check_button_new();
        gopt_mark_index(&b->gopt, idx);
@@ -315,6 +345,7 @@ static struct gopt *gopt_new_bool(struct fio_option *o, unsigned int *val, unsig
        g_signal_connect(G_OBJECT(b->check), "toggled", G_CALLBACK(gopt_bool_toggled), b);
 
        gtk_box_pack_start(GTK_BOX(b->gopt.box), b->check, FALSE, FALSE, 0);
+       gtk_box_pack_start(GTK_BOX(b->gopt.box), label, FALSE, FALSE, 0);
        return &b->gopt;
 }
 
@@ -370,8 +401,10 @@ static struct gopt *gopt_new_int_range(struct fio_option *o, unsigned int **ip,
        r = malloc(sizeof(*r));
        r->gopt.box = gtk_hbox_new(FALSE, 3);
        gopt_mark_index(&r->gopt, idx);
-       label = gtk_label_new(o->name);
-       gtk_box_pack_start(GTK_BOX(r->gopt.box), label, FALSE, FALSE, 0);
+       if (!o->lname)
+               label = gtk_label_new(o->name);
+       else
+               label = gtk_label_new(o->lname);
 
        maxval = o->maxval;
        if (!maxval)
@@ -401,6 +434,7 @@ static struct gopt *gopt_new_int_range(struct fio_option *o, unsigned int **ip,
                g_signal_connect(G_OBJECT(r->spins[i]), "value-changed", G_CALLBACK(range_value_changed), r);
        }
 
+       gtk_box_pack_start(GTK_BOX(r->gopt.box), label, FALSE, FALSE, 0);
        return &r->gopt;
 }
 
@@ -487,11 +521,18 @@ static void gopt_add_option(GtkWidget *hbox, struct fio_option *o,
        }
 
        if (go) {
+               GtkWidget *dest;
+
                if (o->help)
                        gtk_widget_set_tooltip_text(go->box, o->help);
-       
-               gtk_box_pack_start(GTK_BOX(hbox), go->box, FALSE, FALSE, 5);
+
                go->opt_type = o->type;
+
+               dest = gopt_get_group_frame(hbox, o->group);
+               if (!dest)
+                       gtk_box_pack_start(GTK_BOX(hbox), go->box, FALSE, FALSE, 5);
+               else
+                       gtk_box_pack_start(GTK_BOX(dest), go->box, FALSE, FALSE, 5);
        }
 }
 
@@ -537,9 +578,6 @@ static void gopt_add_group_tabs(GtkWidget *notebook, GtkWidget **vbox)
        struct opt_group *og;
        unsigned int i;
 
-       for (i = 0; i < FIO_MAX_OPTS; i++)
-               INIT_FLIST_HEAD(&gopt_list[i]);
-
        i = 0;
        do {
                unsigned int mask = (1U << i);
@@ -555,7 +593,7 @@ static void gopt_add_group_tabs(GtkWidget *notebook, GtkWidget **vbox)
 void gopt_get_options_window(GtkWidget *window, struct thread_options *o)
 {
        GtkWidget *dialog, *notebook;
-       GtkWidget *vboxes[__FIO_OPT_G_NR];
+       GtkWidget *vboxes[__FIO_OPT_C_NR];
 
        dialog = gtk_dialog_new_with_buttons("Fio options",
                        GTK_WINDOW(window), GTK_DIALOG_DESTROY_WITH_PARENT,
index 6f87fa2..5749161 100644 (file)
--- a/options.c
+++ b/options.c
@@ -847,100 +847,97 @@ static int kb_base_verify(struct fio_option *o, void *data)
  */
 static struct opt_group fio_opt_groups[] = {
        {
-               .name   = "Description",
-               .mask   = FIO_OPT_G_DESC,
+               .name   = "General",
+               .mask   = FIO_OPT_C_GENERAL,
        },
        {
-               .name   = "File",
-               .mask   = FIO_OPT_G_FILE,
-       },
-       {
-               .name   = "Misc",
-               .mask   = FIO_OPT_G_MISC,
+               .name   = "I/O",
+               .mask   = FIO_OPT_C_IO,
        },
        {
-               .name   = "IO (main)",
-               .mask   = FIO_OPT_G_IO,
+               .name   = "File",
+               .mask   = FIO_OPT_C_FILE,
        },
        {
-               .name   = "IO direction",
-               .mask   = FIO_OPT_G_IO_DDIR,
+               .name   = "Statistics",
+               .mask   = FIO_OPT_C_STAT,
        },
        {
-               .name   = "IO buffer",
-               .mask   = FIO_OPT_G_IO_BUF,
+               .name   = "Logging",
+               .mask   = FIO_OPT_C_LOG,
        },
        {
-               .name   = "IO engine",
-               .mask   = FIO_OPT_G_IO_ENG,
+               .name   = NULL,
        },
+};
+
+static struct opt_group *__opt_group_from_mask(struct opt_group *ogs, unsigned int *mask,
+                                              unsigned int inv_mask)
+{
+       struct opt_group *og;
+       int i;
+
+       if (*mask == inv_mask || !*mask)
+               return NULL;
+
+       for (i = 0; ogs[i].name; i++) {
+               og = &ogs[i];
+
+               if (*mask & og->mask) {
+                       *mask &= ~(og->mask);
+                       return og;
+               }
+       }
+
+       return NULL;
+}
+
+struct opt_group *opt_group_from_mask(unsigned int *mask)
+{
+       return __opt_group_from_mask(fio_opt_groups, mask, FIO_OPT_C_INVALID);
+}
+
+static struct opt_group fio_opt_cat_groups[] = {
        {
-               .name   = "Random",
-               .mask   = FIO_OPT_G_RAND,
+               .name   = "Rate",
+               .mask   = FIO_OPT_G_RATE,
        },
        {
-               .name   = "OS",
-               .mask   = FIO_OPT_G_OS,
+               .name   = "Zone",
+               .mask   = FIO_OPT_G_ZONE,
        },
        {
-               .name   = "Memory",
-               .mask   = FIO_OPT_G_MEM,
+               .name   = "Read/write mix",
+               .mask   = FIO_OPT_G_RWMIX,
        },
        {
                .name   = "Verify",
                .mask   = FIO_OPT_G_VERIFY,
        },
        {
-               .name   = "CPU",
-               .mask   = FIO_OPT_G_CPU,
-       },
-       {
-               .name   = "Log",
-               .mask   = FIO_OPT_G_LOG,
-       },
-       {
-               .name   = "Zone",
-               .mask   = FIO_OPT_G_ZONE,
-       },
-       {
-               .name   = "Cache",
-               .mask   = FIO_OPT_G_CACHE,
+               .name   = "Trim",
+               .mask   = FIO_OPT_G_TRIM,
        },
        {
-               .name   = "Stat",
-               .mask   = FIO_OPT_G_STAT,
+               .name   = "I/O Logging",
+               .mask   = FIO_OPT_G_IOLOG,
        },
        {
-               .name   = "Error",
-               .mask   = FIO_OPT_G_ERR,
+               .name   = "I/O Depth",
+               .mask   = FIO_OPT_G_IO_DEPTH,
        },
        {
-               .name   = "Job",
-               .mask   = FIO_OPT_G_JOB,
+               .name   = "I/O Flow",
+               .mask   = FIO_OPT_G_IO_FLOW,
        },
        {
                .name   = NULL,
-       },
+       }
 };
 
-struct opt_group *opt_group_from_mask(unsigned int *mask)
+struct opt_group *opt_group_cat_from_mask(unsigned int *mask)
 {
-       struct opt_group *og;
-       int i;
-
-       if (*mask == FIO_OPT_G_INVALID)
-               return NULL;
-
-       for (i = 0; fio_opt_groups[i].name; i++) {
-               og = &fio_opt_groups[i];
-
-               if (*mask & og->mask) {
-                       *mask &= ~(og->mask);
-                       return og;
-               }
-       }
-
-       return NULL;
+       return __opt_group_from_mask(fio_opt_cat_groups, mask, FIO_OPT_G_INVALID);
 }
 
 /*
@@ -949,47 +946,58 @@ struct opt_group *opt_group_from_mask(unsigned int *mask)
 struct fio_option fio_options[FIO_MAX_OPTS] = {
        {
                .name   = "description",
+               .lname  = "Description of job",
                .type   = FIO_OPT_STR_STORE,
                .off1   = td_var_offset(description),
                .help   = "Text job description",
-               .category = FIO_OPT_G_DESC,
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "name",
+               .lname  = "Job name",
                .type   = FIO_OPT_STR_STORE,
                .off1   = td_var_offset(name),
                .help   = "Name of this job",
-               .category = FIO_OPT_G_DESC,
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
-               .name   = "directory",
-               .type   = FIO_OPT_STR_STORE,
-               .off1   = td_var_offset(directory),
-               .cb     = str_directory_cb,
-               .help   = "Directory to store files in",
-               .category = FIO_OPT_G_FILE,
+               .name   = "kb_base",
+               .lname  = "KB Base",
+               .type   = FIO_OPT_INT,
+               .off1   = td_var_offset(kb_base),
+               .verify = kb_base_verify,
+               .prio   = 1,
+               .def    = "1024",
+               .help   = "How many bytes per KB for reporting (1000 or 1024)",
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "filename",
+               .lname  = "Filename(s)",
                .type   = FIO_OPT_STR_STORE,
                .off1   = td_var_offset(filename),
                .cb     = str_filename_cb,
                .prio   = -1, /* must come after "directory" */
                .help   = "File(s) to use for the workload",
-               .category = FIO_OPT_G_FILE,
+               .category = FIO_OPT_C_FILE,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
-               .name   = "kb_base",
-               .type   = FIO_OPT_INT,
-               .off1   = td_var_offset(kb_base),
-               .verify = kb_base_verify,
-               .prio   = 1,
-               .def    = "1024",
-               .help   = "How many bytes per KB for reporting (1000 or 1024)",
-               .category = FIO_OPT_G_MISC,
+               .name   = "directory",
+               .lname  = "Directory",
+               .type   = FIO_OPT_STR_STORE,
+               .off1   = td_var_offset(directory),
+               .cb     = str_directory_cb,
+               .help   = "Directory to store files in",
+               .category = FIO_OPT_C_FILE,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "lockfile",
+               .lname  = "Lockfile",
                .type   = FIO_OPT_STR,
                .cb     = str_lockfile_cb,
                .off1   = td_var_offset(file_lock_mode),
@@ -997,7 +1005,8 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .parent = "filename",
                .hide   = 0,
                .def    = "none",
-               .category = FIO_OPT_G_FILE,
+               .category = FIO_OPT_C_FILE,
+               .group  = FIO_OPT_G_INVALID,
                .posval = {
                          { .ival = "none",
                            .oval = FILE_LOCK_NONE,
@@ -1016,14 +1025,17 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
        },
        {
                .name   = "opendir",
+               .lname  = "Open directory",
                .type   = FIO_OPT_STR_STORE,
                .off1   = td_var_offset(opendir),
                .cb     = str_opendir_cb,
                .help   = "Recursively add files from this directory and down",
-               .category = FIO_OPT_G_FILE,
+               .category = FIO_OPT_C_FILE,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "rw",
+               .lname  = "Read/write",
                .alias  = "readwrite",
                .type   = FIO_OPT_STR,
                .cb     = str_rw_cb,
@@ -1031,7 +1043,8 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .help   = "IO direction",
                .def    = "read",
                .verify = rw_verify,
-               .category = FIO_OPT_G_IO_DDIR,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_INVALID,
                .posval = {
                          { .ival = "read",
                            .oval = TD_DDIR_READ,
@@ -1061,11 +1074,13 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
        },
        {
                .name   = "rw_sequencer",
+               .lname  = "RW Sequencer",
                .type   = FIO_OPT_STR,
                .off1   = td_var_offset(rw_seq),
                .help   = "IO offset generator modifier",
                .def    = "sequential",
-               .category = FIO_OPT_G_IO_DDIR,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_INVALID,
                .posval = {
                          { .ival = "sequential",
                            .oval = RW_SEQ_SEQ,
@@ -1080,11 +1095,13 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
 
        {
                .name   = "ioengine",
+               .lname  = "IO Engine",
                .type   = FIO_OPT_STR_STORE,
                .off1   = td_var_offset(ioengine),
                .help   = "IO engine to use",
                .def    = FIO_PREFERRED_ENGINE,
-               .category = FIO_OPT_G_IO,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_INVALID,
                .posval = {
                          { .ival = "sync",
                            .help = "Use read/write",
@@ -1167,16 +1184,19 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
        },
        {
                .name   = "iodepth",
+               .lname  = "IO Depth",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(iodepth),
                .help   = "Number of IO buffers to keep in flight",
                .minval = 1,
                .interval = 1,
                .def    = "1",
-               .category = FIO_OPT_G_IO,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_IO_DEPTH,
        },
        {
                .name   = "iodepth_batch",
+               .lname  = "IO Depth batch",
                .alias  = "iodepth_batch_submit",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(iodepth_batch),
@@ -1186,10 +1206,12 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .minval = 1,
                .interval = 1,
                .def    = "1",
-               .category = FIO_OPT_G_IO,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_IO_DEPTH,
        },
        {
                .name   = "iodepth_batch_complete",
+               .lname  = "IO Depth batch complete",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(iodepth_batch_complete),
                .help   = "Number of IO buffers to retrieve in one go",
@@ -1198,57 +1220,69 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .minval = 0,
                .interval = 1,
                .def    = "1",
-               .category = FIO_OPT_G_IO,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_IO_DEPTH,
        },
        {
                .name   = "iodepth_low",
+               .lname  = "IO Depth batch low",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(iodepth_low),
                .help   = "Low water mark for queuing depth",
                .parent = "iodepth",
                .hide   = 1,
                .interval = 1,
-               .category = FIO_OPT_G_IO,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_IO_DEPTH,
        },
        {
                .name   = "size",
+               .lname  = "Size",
                .type   = FIO_OPT_STR_VAL,
                .cb     = str_size_cb,
                .help   = "Total size of device or files",
                .interval = 1024 * 1024,
-               .category = FIO_OPT_G_IO,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "fill_device",
+               .lname  = "Fill device",
                .alias  = "fill_fs",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(fill_device),
                .help   = "Write until an ENOSPC error occurs",
                .def    = "0",
-               .category = FIO_OPT_G_IO,
+               .category = FIO_OPT_C_FILE,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "filesize",
+               .lname  = "File size",
                .type   = FIO_OPT_STR_VAL,
                .off1   = td_var_offset(file_size_low),
                .off2   = td_var_offset(file_size_high),
                .minval = 1,
                .help   = "Size of individual files",
                .interval = 1024 * 1024,
-               .category = FIO_OPT_G_IO | FIO_OPT_G_FILE,
+               .category = FIO_OPT_C_FILE,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "offset",
+               .lname  = "IO offset",
                .alias  = "fileoffset",
                .type   = FIO_OPT_STR_VAL,
                .off1   = td_var_offset(start_offset),
                .help   = "Start IO from this offset",
                .def    = "0",
                .interval = 1024 * 1024,
-               .category = FIO_OPT_G_IO,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "offset_increment",
+               .lname  = "IO offset increment",
                .type   = FIO_OPT_STR_VAL,
                .off1   = td_var_offset(offset_increment),
                .help   = "What is the increment from one offset to the next",
@@ -1256,10 +1290,12 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .hide   = 1,
                .def    = "0",
                .interval = 1024 * 1024,
-               .category = FIO_OPT_G_IO,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "bs",
+               .lname  = "Block size",
                .alias  = "blocksize",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(bs[DDIR_READ]),
@@ -1270,10 +1306,12 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .parent = "rw",
                .hide   = 1,
                .interval = 512,
-               .category = FIO_OPT_G_IO,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "ba",
+               .lname  = "Block size align",
                .alias  = "blockalign",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(ba[DDIR_READ]),
@@ -1283,10 +1321,12 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .parent = "rw",
                .hide   = 1,
                .interval = 512,
-               .category = FIO_OPT_G_IO | FIO_OPT_G_IO_BUF,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "bsrange",
+               .lname  = "Block size range",
                .alias  = "blocksize_range",
                .type   = FIO_OPT_RANGE,
                .off1   = td_var_offset(min_bs[DDIR_READ]),
@@ -1298,91 +1338,110 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .parent = "rw",
                .hide   = 1,
                .interval = 4096,
-               .category = FIO_OPT_G_IO,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "bssplit",
+               .lname  = "Block size split",
                .type   = FIO_OPT_STR,
                .cb     = str_bssplit_cb,
                .help   = "Set a specific mix of block sizes",
                .parent = "rw",
                .hide   = 1,
-               .category = FIO_OPT_G_IO,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "bs_unaligned",
+               .lname  = "Block size unaligned",
                .alias  = "blocksize_unaligned",
                .type   = FIO_OPT_STR_SET,
                .off1   = td_var_offset(bs_unaligned),
                .help   = "Don't sector align IO buffer sizes",
                .parent = "rw",
                .hide   = 1,
-               .category = FIO_OPT_G_IO,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "randrepeat",
+               .lname  = "Random repeatable",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(rand_repeatable),
                .help   = "Use repeatable random IO pattern",
                .def    = "1",
                .parent = "rw",
                .hide   = 1,
-               .category = FIO_OPT_G_IO | FIO_OPT_G_RAND,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "use_os_rand",
+               .lname  = "Use OS random",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(use_os_rand),
                .help   = "Set to use OS random generator",
                .def    = "0",
                .parent = "rw",
                .hide   = 1,
-               .category = FIO_OPT_G_RAND,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "norandommap",
+               .lname  = "No randommap",
                .type   = FIO_OPT_STR_SET,
                .off1   = td_var_offset(norandommap),
                .help   = "Accept potential duplicate random blocks",
                .parent = "rw",
                .hide   = 1,
-               .category = FIO_OPT_G_RAND,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "softrandommap",
+               .lname  = "Soft randommap",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(softrandommap),
                .help   = "Set norandommap if randommap allocation fails",
                .parent = "norandommap",
                .hide   = 1,
                .def    = "0",
-               .category = FIO_OPT_G_RAND,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "nrfiles",
+               .lname  = "Number of files",
                .alias  = "nr_files",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(nr_files),
                .help   = "Split job workload between this number of files",
                .def    = "1",
                .interval = 1,
-               .category = FIO_OPT_G_FILE,
+               .category = FIO_OPT_C_FILE,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "openfiles",
+               .lname  = "Number of open files",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(open_files),
                .help   = "Number of files to keep open at the same time",
-               .category = FIO_OPT_G_FILE,
+               .category = FIO_OPT_C_FILE,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "file_service_type",
+               .lname  = "File service type",
                .type   = FIO_OPT_STR,
                .cb     = str_fst_cb,
                .off1   = td_var_offset(file_service_type),
                .help   = "How to select which file to service next",
                .def    = "roundrobin",
-               .category = FIO_OPT_G_FILE,
+               .category = FIO_OPT_C_FILE,
+               .group  = FIO_OPT_G_INVALID,
                .posval = {
                          { .ival = "random",
                            .oval = FIO_FSERVICE_RANDOM,
@@ -1403,11 +1462,13 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
 #ifdef FIO_HAVE_FALLOCATE
        {
                .name   = "fallocate",
+               .lname  = "Fallocate",
                .type   = FIO_OPT_STR,
                .off1   = td_var_offset(fallocate_mode),
                .help   = "Whether pre-allocation is performed when laying out files",
                .def    = "posix",
-               .category = FIO_OPT_G_FILE,
+               .category = FIO_OPT_C_FILE,
+               .group  = FIO_OPT_G_INVALID,
                .posval = {
                          { .ival = "none",
                            .oval = FIO_FALLOCATE_NONE,
@@ -1437,42 +1498,51 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
 #endif /* FIO_HAVE_FALLOCATE */
        {
                .name   = "fadvise_hint",
+               .lname  = "Fadvise hint",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(fadvise_hint),
                .help   = "Use fadvise() to advise the kernel on IO pattern",
                .def    = "1",
-               .category = FIO_OPT_G_FILE,
+               .category = FIO_OPT_C_FILE,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "fsync",
+               .lname  = "Fsync",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(fsync_blocks),
                .help   = "Issue fsync for writes every given number of blocks",
                .def    = "0",
                .interval = 1,
-               .category = FIO_OPT_G_FILE,
+               .category = FIO_OPT_C_FILE,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "fdatasync",
+               .lname  = "Fdatasync",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(fdatasync_blocks),
                .help   = "Issue fdatasync for writes every given number of blocks",
                .def    = "0",
                .interval = 1,
-               .category = FIO_OPT_G_FILE,
+               .category = FIO_OPT_C_FILE,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "write_barrier",
+               .lname  = "Write barrier",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(barrier_blocks),
                .help   = "Make every Nth write a barrier write",
                .def    = "0",
                .interval = 1,
-               .category = FIO_OPT_G_IO,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_INVALID,
        },
 #ifdef FIO_HAVE_SYNC_FILE_RANGE
        {
                .name   = "sync_file_range",
+               .lname  = "Sync file range",
                .posval = {
                          { .ival = "wait_before",
                            .oval = SYNC_FILE_RANGE_WAIT_BEFORE,
@@ -1495,90 +1565,111 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .cb     = str_sfr_cb,
                .off1   = td_var_offset(sync_file_range),
                .help   = "Use sync_file_range()",
-               .category = FIO_OPT_G_FILE,
+               .category = FIO_OPT_C_FILE,
+               .group  = FIO_OPT_G_INVALID,
        },
 #endif
        {
                .name   = "direct",
+               .lname  = "Direct I/O",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(odirect),
                .help   = "Use O_DIRECT IO (negates buffered)",
                .def    = "0",
-               .category = FIO_OPT_G_IO,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "buffered",
+               .lname  = "Buffered I/O",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(odirect),
                .neg    = 1,
                .help   = "Use buffered IO (negates direct)",
                .def    = "1",
-               .category = FIO_OPT_G_IO,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "overwrite",
+               .lname  = "Overwrite",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(overwrite),
                .help   = "When writing, set whether to overwrite current data",
                .def    = "0",
-               .category = FIO_OPT_G_IO | FIO_OPT_G_FILE,
+               .category = FIO_OPT_C_FILE,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "loops",
+               .lname  = "Loops",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(loops),
                .help   = "Number of times to run the job",
                .def    = "1",
                .interval = 1,
-               .category = FIO_OPT_G_MISC,
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "numjobs",
+               .lname  = "Number of jobs",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(numjobs),
                .help   = "Duplicate this job this many times",
                .def    = "1",
                .interval = 1,
-               .category = FIO_OPT_G_MISC,
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "startdelay",
+               .lname  = "Start delay",
                .type   = FIO_OPT_STR_VAL_TIME,
                .off1   = td_var_offset(start_delay),
                .help   = "Only start job when this period has passed",
                .def    = "0",
-               .category = FIO_OPT_G_MISC,
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "runtime",
+               .lname  = "Runtime",
                .alias  = "timeout",
                .type   = FIO_OPT_STR_VAL_TIME,
                .off1   = td_var_offset(timeout),
                .help   = "Stop workload when this amount of time has passed",
                .def    = "0",
-               .category = FIO_OPT_G_MISC,
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "time_based",
+               .lname  = "Time based",
                .type   = FIO_OPT_STR_SET,
                .off1   = td_var_offset(time_based),
                .help   = "Keep running until runtime/timeout is met",
-               .category = FIO_OPT_G_MISC,
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "ramp_time",
+               .lname  = "Ramp time",
                .type   = FIO_OPT_STR_VAL_TIME,
                .off1   = td_var_offset(ramp_time),
                .help   = "Ramp up time before measuring performance",
-               .category = FIO_OPT_G_MISC,
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "clocksource",
+               .lname  = "Clock source",
                .type   = FIO_OPT_STR,
                .cb     = fio_clock_source_cb,
                .off1   = td_var_offset(clocksource),
                .help   = "What type of timing source to use",
-               .category = FIO_OPT_G_OS,
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_INVALID,
                .posval = {
                          { .ival = "gettimeofday",
                            .oval = CS_GTOD,
@@ -1599,12 +1690,14 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
        {
                .name   = "mem",
                .alias  = "iomem",
+               .lname  = "I/O Memory",
                .type   = FIO_OPT_STR,
                .cb     = str_mem_cb,
                .off1   = td_var_offset(mem_type),
                .help   = "Backing type for IO buffers",
                .def    = "malloc",
-               .category = FIO_OPT_G_IO_BUF | FIO_OPT_G_MEM,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_INVALID,
                .posval = {
                          { .ival = "malloc",
                            .oval = MEM_MALLOC,
@@ -1635,6 +1728,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
        {
                .name   = "iomem_align",
                .alias  = "mem_align",
+               .lname  = "I/O memory alignment",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(mem_align),
                .minval = 0,
@@ -1642,16 +1736,19 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .def    = "0",
                .parent = "iomem",
                .hide   = 1,
-               .category = FIO_OPT_G_IO_BUF | FIO_OPT_G_MEM,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "verify",
+               .lname  = "Verify",
                .type   = FIO_OPT_STR,
                .off1   = td_var_offset(verify),
                .help   = "Verify data written",
                .cb     = str_verify_cb,
                .def    = "0",
-               .category = FIO_OPT_G_IO | FIO_OPT_G_VERIFY,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_INVALID,
                .posval = {
                          { .ival = "0",
                            .oval = VERIFY_NONE,
@@ -1710,26 +1807,31 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
        },
        {
                .name   = "do_verify",
+               .lname  = "Perform verify step",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(do_verify),
                .help   = "Run verification stage after write",
                .def    = "1",
                .parent = "verify",
                .hide   = 1,
-               .category = FIO_OPT_G_IO | FIO_OPT_G_VERIFY,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_VERIFY,
        },
        {
                .name   = "verifysort",
+               .lname  = "Verify sort",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(verifysort),
                .help   = "Sort written verify blocks for read back",
                .def    = "1",
                .parent = "verify",
                .hide   = 1,
-               .category = FIO_OPT_G_IO | FIO_OPT_G_VERIFY,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_VERIFY,
        },
        {
                .name   = "verify_interval",
+               .lname  = "Verify interval",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(verify_interval),
                .minval = 2 * sizeof(struct verify_header),
@@ -1737,89 +1839,107 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .parent = "verify",
                .hide   = 1,
                .interval = 2 * sizeof(struct verify_header),
-               .category = FIO_OPT_G_IO | FIO_OPT_G_VERIFY,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_VERIFY,
        },
        {
                .name   = "verify_offset",
+               .lname  = "Verify offset",
                .type   = FIO_OPT_INT,
                .help   = "Offset verify header location by N bytes",
                .def    = "0",
                .cb     = str_verify_offset_cb,
                .parent = "verify",
                .hide   = 1,
-               .category = FIO_OPT_G_IO | FIO_OPT_G_VERIFY,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_VERIFY,
        },
        {
                .name   = "verify_pattern",
+               .lname  = "Verify pattern",
                .type   = FIO_OPT_STR,
                .cb     = str_verify_pattern_cb,
                .help   = "Fill pattern for IO buffers",
                .parent = "verify",
                .hide   = 1,
-               .category = FIO_OPT_G_IO | FIO_OPT_G_VERIFY,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_VERIFY,
        },
        {
                .name   = "verify_fatal",
+               .lname  = "Verify fatal",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(verify_fatal),
                .def    = "0",
                .help   = "Exit on a single verify failure, don't continue",
                .parent = "verify",
                .hide   = 1,
-               .category = FIO_OPT_G_IO | FIO_OPT_G_VERIFY | FIO_OPT_G_ERR,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_VERIFY,
        },
        {
                .name   = "verify_dump",
+               .lname  = "Verify dump",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(verify_dump),
                .def    = "0",
                .help   = "Dump contents of good and bad blocks on failure",
                .parent = "verify",
                .hide   = 1,
-               .category = FIO_OPT_G_IO | FIO_OPT_G_VERIFY | FIO_OPT_G_ERR,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_VERIFY,
        },
        {
                .name   = "verify_async",
+               .lname  = "Verify asynchronously",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(verify_async),
                .def    = "0",
                .help   = "Number of async verifier threads to use",
                .parent = "verify",
                .hide   = 1,
-               .category = FIO_OPT_G_IO | FIO_OPT_G_VERIFY,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_VERIFY,
        },
        {
                .name   = "verify_backlog",
+               .lname  = "Verify backlog",
                .type   = FIO_OPT_STR_VAL,
                .off1   = td_var_offset(verify_backlog),
                .help   = "Verify after this number of blocks are written",
                .parent = "verify",
                .hide   = 1,
-               .category = FIO_OPT_G_IO | FIO_OPT_G_VERIFY,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_VERIFY,
        },
        {
                .name   = "verify_backlog_batch",
+               .lname  = "Verify backlog batch",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(verify_batch),
                .help   = "Verify this number of IO blocks",
                .parent = "verify",
                .hide   = 1,
-               .category = FIO_OPT_G_IO | FIO_OPT_G_VERIFY,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_VERIFY,
        },
 #ifdef FIO_HAVE_CPU_AFFINITY
        {
                .name   = "verify_async_cpus",
+               .lname  = "Async verify CPUs",
                .type   = FIO_OPT_STR,
                .cb     = str_verify_cpus_allowed_cb,
                .help   = "Set CPUs allowed for async verify threads",
                .parent = "verify_async",
                .hide   = 1,
-               .category = FIO_OPT_G_OS | FIO_OPT_G_CPU | FIO_OPT_G_VERIFY,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_VERIFY,
        },
 #endif
 #ifdef FIO_HAVE_TRIM
        {
                .name   = "trim_percentage",
+               .lname  = "Trim percentage",
                .type   = FIO_OPT_INT,
                .cb     = str_verify_trim_cb,
                .minval = 0,
@@ -1829,158 +1949,194 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .def    = "0",
                .interval = 1,
                .hide   = 1,
-               .category = FIO_OPT_G_IO,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_TRIM,
        },
        {
                .name   = "trim_verify_zero",
+               .lname  = "Verify trim zero",
                .type   = FIO_OPT_BOOL,
                .help   = "Verify that trim/discarded blocks are returned as zeroes",
                .off1   = td_var_offset(trim_zero),
                .parent = "trim_percentage",
                .hide   = 1,
                .def    = "1",
-               .category = FIO_OPT_G_IO,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_TRIM,
        },
        {
                .name   = "trim_backlog",
+               .lname  = "Trim backlog",
                .type   = FIO_OPT_STR_VAL,
                .off1   = td_var_offset(trim_backlog),
                .help   = "Trim after this number of blocks are written",
                .parent = "trim_percentage",
                .hide   = 1,
                .interval = 1,
-               .category = FIO_OPT_G_IO,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_TRIM,
        },
        {
                .name   = "trim_backlog_batch",
+               .lname  = "Trim backlog batch",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(trim_batch),
                .help   = "Trim this number of IO blocks",
                .parent = "trim_percentage",
                .hide   = 1,
                .interval = 1,
-               .category = FIO_OPT_G_IO,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_TRIM,
        },
 #endif
        {
                .name   = "write_iolog",
+               .lname  = "Write I/O log",
                .type   = FIO_OPT_STR_STORE,
                .off1   = td_var_offset(write_iolog_file),
                .help   = "Store IO pattern to file",
-               .category = FIO_OPT_G_IO | FIO_OPT_G_LOG,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_IOLOG,
        },
        {
                .name   = "read_iolog",
+               .lname  = "Read I/O log",
                .type   = FIO_OPT_STR_STORE,
                .off1   = td_var_offset(read_iolog_file),
                .help   = "Playback IO pattern from file",
-               .category = FIO_OPT_G_IO | FIO_OPT_G_LOG,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_IOLOG,
        },
        {
                .name   = "replay_no_stall",
+               .lname  = "Don't stall on replay",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(no_stall),
                .def    = "0",
                .parent = "read_iolog",
                .hide   = 1,
                .help   = "Playback IO pattern file as fast as possible without stalls",
-               .category = FIO_OPT_G_IO | FIO_OPT_G_LOG,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_IOLOG,
        },
        {
                .name   = "replay_redirect",
+               .lname  = "Redirect device for replay",
                .type   = FIO_OPT_STR_STORE,
                .off1   = td_var_offset(replay_redirect),
                .parent = "read_iolog",
                .hide   = 1,
                .help   = "Replay all I/O onto this device, regardless of trace device",
-               .category = FIO_OPT_G_IO | FIO_OPT_G_LOG,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_IOLOG,
        },
        {
                .name   = "exec_prerun",
+               .lname  = "Pre-execute runnable",
                .type   = FIO_OPT_STR_STORE,
                .off1   = td_var_offset(exec_prerun),
                .help   = "Execute this file prior to running job",
-               .category = FIO_OPT_G_MISC | FIO_OPT_G_OS,
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "exec_postrun",
+               .lname  = "Post-execute runnable",
                .type   = FIO_OPT_STR_STORE,
                .off1   = td_var_offset(exec_postrun),
                .help   = "Execute this file after running job",
-               .category = FIO_OPT_G_MISC | FIO_OPT_G_OS,
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_INVALID,
        },
 #ifdef FIO_HAVE_IOSCHED_SWITCH
        {
                .name   = "ioscheduler",
+               .lname  = "I/O scheduler",
                .type   = FIO_OPT_STR_STORE,
                .off1   = td_var_offset(ioscheduler),
                .help   = "Use this IO scheduler on the backing device",
-               .category = FIO_OPT_G_OS | FIO_OPT_G_IO,
+               .category = FIO_OPT_C_FILE,
+               .group  = FIO_OPT_G_INVALID,
        },
 #endif
        {
                .name   = "zonesize",
+               .lname  = "Zone size",
                .type   = FIO_OPT_STR_VAL,
                .off1   = td_var_offset(zone_size),
                .help   = "Amount of data to read per zone",
                .def    = "0",
                .interval = 1024 * 1024,
-               .category = FIO_OPT_G_IO | FIO_OPT_G_ZONE,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_ZONE,
        },
        {
                .name   = "zonerange",
+               .lname  = "Zone range",
                .type   = FIO_OPT_STR_VAL,
                .off1   = td_var_offset(zone_range),
                .help   = "Give size of an IO zone",
                .def    = "0",
                .interval = 1024 * 1024,
-               .category = FIO_OPT_G_IO | FIO_OPT_G_ZONE,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_ZONE,
        },
        {
                .name   = "zoneskip",
+               .lname  = "Zone skip",
                .type   = FIO_OPT_STR_VAL,
                .off1   = td_var_offset(zone_skip),
                .help   = "Space between IO zones",
                .def    = "0",
                .interval = 1024 * 1024,
-               .category = FIO_OPT_G_IO | FIO_OPT_G_ZONE,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_ZONE,
        },
        {
                .name   = "lockmem",
+               .lname  = "Lock memory",
                .type   = FIO_OPT_STR_VAL,
                .cb     = str_lockmem_cb,
                .help   = "Lock down this amount of memory",
                .def    = "0",
                .interval = 1024 * 1024,
-               .category = FIO_OPT_G_OS | FIO_OPT_G_MEM,
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "rwmixread",
+               .lname  = "Read/write mix read",
                .type   = FIO_OPT_INT,
                .cb     = str_rwmix_read_cb,
                .maxval = 100,
                .help   = "Percentage of mixed workload that is reads",
                .def    = "50",
                .interval = 5,
-               .category = FIO_OPT_G_IO,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_RWMIX,
        },
        {
                .name   = "rwmixwrite",
+               .lname  = "Read/write mix write",
                .type   = FIO_OPT_INT,
                .cb     = str_rwmix_write_cb,
                .maxval = 100,
                .help   = "Percentage of mixed workload that is writes",
                .def    = "50",
                .interval = 5,
-               .category = FIO_OPT_G_IO,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_RWMIX,
        },
        {
                .name   = "rwmixcycle",
+               .lname  = "Read/write mix cycle",
                .type   = FIO_OPT_DEPRECATED,
-               .category = FIO_OPT_G_IO,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_RWMIX,
        },
        {
                .name   = "nice",
+               .lname  = "Nice",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(nice),
                .help   = "Set job CPU nice value",
@@ -1988,125 +2144,151 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .maxval = 20,
                .def    = "0",
                .interval = 1,
-               .category = FIO_OPT_G_OS | FIO_OPT_G_CPU,
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_INVALID,
        },
 #ifdef FIO_HAVE_IOPRIO
        {
                .name   = "prio",
+               .lname  = "I/O nice priority",
                .type   = FIO_OPT_INT,
                .cb     = str_prio_cb,
                .help   = "Set job IO priority value",
                .minval = 0,
                .maxval = 7,
                .interval = 1,
-               .category = FIO_OPT_G_OS | FIO_OPT_G_CPU,
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "prioclass",
+               .lname  = "I/O nice priority class",
                .type   = FIO_OPT_INT,
                .cb     = str_prioclass_cb,
                .help   = "Set job IO priority class",
                .minval = 0,
                .maxval = 3,
                .interval = 1,
-               .category = FIO_OPT_G_OS | FIO_OPT_G_CPU,
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_INVALID,
        },
 #endif
        {
                .name   = "thinktime",
+               .lname  = "Thinktime",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(thinktime),
                .help   = "Idle time between IO buffers (usec)",
                .def    = "0",
-               .category = FIO_OPT_G_MISC,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "thinktime_spin",
+               .lname  = "Thinktime spin",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(thinktime_spin),
                .help   = "Start think time by spinning this amount (usec)",
                .def    = "0",
                .parent = "thinktime",
                .hide   = 1,
-               .category = FIO_OPT_G_MISC,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "thinktime_blocks",
+               .lname  = "Thinktime blocks",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(thinktime_blocks),
                .help   = "IO buffer period between 'thinktime'",
                .def    = "1",
                .parent = "thinktime",
                .hide   = 1,
-               .category = FIO_OPT_G_MISC,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "rate",
+               .lname  = "I/O rate",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(rate[0]),
                .off2   = td_var_offset(rate[1]),
                .help   = "Set bandwidth rate",
-               .category = FIO_OPT_G_IO,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_RATE,
        },
        {
                .name   = "ratemin",
+               .lname  = "I/O min rate",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(ratemin[0]),
                .off2   = td_var_offset(ratemin[1]),
                .help   = "Job must meet this rate or it will be shutdown",
                .parent = "rate",
                .hide   = 1,
-               .category = FIO_OPT_G_IO,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_RATE,
        },
        {
                .name   = "rate_iops",
+               .lname  = "I/O rate IOPS",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(rate_iops[0]),
                .off2   = td_var_offset(rate_iops[1]),
                .help   = "Limit IO used to this number of IO operations/sec",
                .hide   = 1,
-               .category = FIO_OPT_G_IO,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_RATE,
        },
        {
                .name   = "rate_iops_min",
+               .lname  = "I/O min rate IOPS",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(rate_iops_min[0]),
                .off2   = td_var_offset(rate_iops_min[1]),
                .help   = "Job must meet this rate or it will be shut down",
                .parent = "rate_iops",
                .hide   = 1,
-               .category = FIO_OPT_G_IO,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_RATE,
        },
        {
                .name   = "ratecycle",
+               .lname  = "I/O rate cycle",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(ratecycle),
                .help   = "Window average for rate limits (msec)",
                .def    = "1000",
                .parent = "rate",
                .hide   = 1,
-               .category = FIO_OPT_G_IO,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_RATE,
        },
        {
                .name   = "invalidate",
+               .lname  = "Cache invalidate",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(invalidate_cache),
                .help   = "Invalidate buffer/page cache prior to running job",
                .def    = "1",
-               .category = FIO_OPT_G_IO | FIO_OPT_G_CACHE,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "sync",
+               .lname  = "Synchronous I/O",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(sync_io),
                .help   = "Use O_SYNC for buffered writes",
                .def    = "0",
                .parent = "buffered",
                .hide   = 1,
-               .category = FIO_OPT_G_IO | FIO_OPT_G_FILE,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "bwavgtime",
+               .lname  = "Bandwidth average time",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(bw_avg_time),
                .help   = "Time window over which to calculate bandwidth"
@@ -2115,10 +2297,12 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .parent = "write_bw_log",
                .hide   = 1,
                .interval = 100,
-               .category = FIO_OPT_G_LOG | FIO_OPT_G_STAT,
+               .category = FIO_OPT_C_LOG,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "iopsavgtime",
+               .lname  = "IOPS average time",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(iops_avg_time),
                .help   = "Time window over which to calculate IOPS (msec)",
@@ -2126,226 +2310,283 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .parent = "write_iops_log",
                .hide   = 1,
                .interval = 100,
-               .category = FIO_OPT_G_LOG | FIO_OPT_G_STAT,
+               .category = FIO_OPT_C_LOG,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "create_serialize",
+               .lname  = "Create serialize",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(create_serialize),
                .help   = "Serialize creating of job files",
                .def    = "1",
-               .category = FIO_OPT_G_FILE,
+               .category = FIO_OPT_C_FILE,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "create_fsync",
+               .lname  = "Create fsync",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(create_fsync),
                .help   = "fsync file after creation",
                .def    = "1",
-               .category = FIO_OPT_G_FILE,
+               .category = FIO_OPT_C_FILE,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "create_on_open",
+               .lname  = "Create on open",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(create_on_open),
                .help   = "Create files when they are opened for IO",
                .def    = "0",
-               .category = FIO_OPT_G_FILE,
+               .category = FIO_OPT_C_FILE,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "pre_read",
+               .lname  = "Pre-read files",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(pre_read),
                .help   = "Pre-read files before starting official testing",
                .def    = "0",
-               .category = FIO_OPT_G_FILE | FIO_OPT_G_CACHE,
+               .category = FIO_OPT_C_FILE,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "cpuload",
+               .lname  = "CPU load",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(cpuload),
                .help   = "Use this percentage of CPU",
-               .category = FIO_OPT_G_CPU,
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "cpuchunks",
+               .lname  = "CPU chunk",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(cpucycle),
                .help   = "Length of the CPU burn cycles (usecs)",
                .def    = "50000",
                .parent = "cpuload",
                .hide   = 1,
-               .category = FIO_OPT_G_CPU,
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_INVALID,
        },
 #ifdef FIO_HAVE_CPU_AFFINITY
        {
                .name   = "cpumask",
+               .lname  = "CPU mask",
                .type   = FIO_OPT_INT,
                .cb     = str_cpumask_cb,
                .help   = "CPU affinity mask",
-               .category = FIO_OPT_G_CPU | FIO_OPT_G_OS,
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "cpus_allowed",
+               .lname  = "CPUs allowed",
                .type   = FIO_OPT_STR,
                .cb     = str_cpus_allowed_cb,
                .help   = "Set CPUs allowed",
-               .category = FIO_OPT_G_CPU | FIO_OPT_G_OS,
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_INVALID,
        },
 #endif
        {
                .name   = "end_fsync",
+               .lname  = "End fsync",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(end_fsync),
                .help   = "Include fsync at the end of job",
                .def    = "0",
-               .category = FIO_OPT_G_FILE,
+               .category = FIO_OPT_C_FILE,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "fsync_on_close",
+               .lname  = "Fsync on close",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(fsync_on_close),
                .help   = "fsync files on close",
                .def    = "0",
-               .category = FIO_OPT_G_FILE,
+               .category = FIO_OPT_C_FILE,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "unlink",
+               .lname  = "Unlink file",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(unlink),
                .help   = "Unlink created files after job has completed",
                .def    = "0",
-               .category = FIO_OPT_G_FILE,
+               .category = FIO_OPT_C_FILE,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "exitall",
+               .lname  = "Exit-all on terminate",
                .type   = FIO_OPT_STR_SET,
                .cb     = str_exitall_cb,
                .help   = "Terminate all jobs when one exits",
-               .category = FIO_OPT_G_MISC | FIO_OPT_G_JOB,
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "stonewall",
+               .lname  = "Wait for previous",
                .alias  = "wait_for_previous",
                .type   = FIO_OPT_STR_SET,
                .off1   = td_var_offset(stonewall),
                .help   = "Insert a hard barrier between this job and previous",
-               .category = FIO_OPT_G_MISC | FIO_OPT_G_JOB,
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "new_group",
+               .lname  = "New group",
                .type   = FIO_OPT_STR_SET,
                .off1   = td_var_offset(new_group),
                .help   = "Mark the start of a new group (for reporting)",
-               .category = FIO_OPT_G_MISC | FIO_OPT_G_JOB,
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "thread",
+               .lname  = "Thread",
                .type   = FIO_OPT_STR_SET,
                .off1   = td_var_offset(use_thread),
                .help   = "Use threads instead of processes",
-               .category = FIO_OPT_G_MISC | FIO_OPT_G_OS | FIO_OPT_G_JOB,
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "write_bw_log",
+               .lname  = "Write bandwidth log",
                .type   = FIO_OPT_STR,
                .off1   = td_var_offset(write_bw_log),
                .cb     = str_write_bw_log_cb,
                .help   = "Write log of bandwidth during run",
-               .category = FIO_OPT_G_LOG,
+               .category = FIO_OPT_C_LOG,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "write_lat_log",
+               .lname  = "Write latency log",
                .type   = FIO_OPT_STR,
                .off1   = td_var_offset(write_lat_log),
                .cb     = str_write_lat_log_cb,
                .help   = "Write log of latency during run",
-               .category = FIO_OPT_G_LOG,
+               .category = FIO_OPT_C_LOG,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "write_iops_log",
+               .lname  = "Write IOPS log",
                .type   = FIO_OPT_STR,
                .off1   = td_var_offset(write_iops_log),
                .cb     = str_write_iops_log_cb,
                .help   = "Write log of IOPS during run",
-               .category = FIO_OPT_G_LOG,
+               .category = FIO_OPT_C_LOG,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "log_avg_msec",
+               .lname  = "Log averaging (msec)",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(log_avg_msec),
                .help   = "Average bw/iops/lat logs over this period of time",
                .def    = "0",
-               .category = FIO_OPT_G_LOG,
+               .category = FIO_OPT_C_LOG,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "hugepage-size",
+               .lname  = "Hugepage size",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(hugepage_size),
                .help   = "When using hugepages, specify size of each page",
                .def    = __fio_stringify(FIO_HUGE_PAGE),
                .interval = 1024 * 1024,
-               .category = FIO_OPT_G_OS | FIO_OPT_G_MEM,
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "group_reporting",
-               .type   = FIO_OPT_STR_SET,
+               .lname  = "Group reporting",
+               .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(group_reporting),
                .help   = "Do reporting on a per-group basis",
-               .category = FIO_OPT_G_MISC,
+               .def    = "1",
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "zero_buffers",
+               .lname  = "Zero I/O buffers",
                .type   = FIO_OPT_STR_SET,
                .off1   = td_var_offset(zero_buffers),
                .help   = "Init IO buffers to all zeroes",
-               .category = FIO_OPT_G_IO_BUF,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "refill_buffers",
+               .lname  = "Refill I/O buffers",
                .type   = FIO_OPT_STR_SET,
                .off1   = td_var_offset(refill_buffers),
                .help   = "Refill IO buffers on every IO submit",
-               .category = FIO_OPT_G_IO_BUF,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "scramble_buffers",
+               .lname  = "Scramble I/O buffers",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(scramble_buffers),
                .help   = "Slightly scramble buffers on every IO submit",
                .def    = "1",
-               .category = FIO_OPT_G_IO_BUF,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "buffer_compress_percentage",
+               .lname  = "Buffer compression percentage",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(compress_percentage),
                .maxval = 100,
                .minval = 1,
                .help   = "How compressible the buffer is (approximately)",
                .interval = 5,
-               .category = FIO_OPT_G_IO_BUF,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "buffer_compress_chunk",
+               .lname  = "Buffer compression chunk size",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(compress_chunk),
                .parent = "buffer_compress_percentage",
                .hide   = 1,
                .help   = "Size of compressible region in buffer",
                .interval = 256,
-               .category = FIO_OPT_G_IO_BUF,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "clat_percentiles",
+               .lname  = "Completion latency percentiles",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(clat_percentiles),
                .help   = "Enable the reporting of completion latency percentiles",
                .def    = "1",
-               .category = FIO_OPT_G_STAT,
+               .category = FIO_OPT_C_STAT,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "percentile_list",
+               .lname  = "Completion latency percentile list",
                .type   = FIO_OPT_FLOAT_LIST,
                .off1   = td_var_offset(percentile_list),
                .off2   = td_var_offset(overwrite_plist),
@@ -2353,82 +2594,99 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .maxlen = FIO_IO_U_LIST_MAX_LEN,
                .minfp  = 0.0,
                .maxfp  = 100.0,
-               .category = FIO_OPT_G_STAT,
+               .category = FIO_OPT_C_STAT,
+               .group  = FIO_OPT_G_INVALID,
        },
 
 #ifdef FIO_HAVE_DISK_UTIL
        {
                .name   = "disk_util",
+               .lname  = "Disk utilization",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(do_disk_util),
                .help   = "Log disk utilization statistics",
                .def    = "1",
-               .category = FIO_OPT_G_OS | FIO_OPT_G_STAT,
+               .category = FIO_OPT_C_STAT,
+               .group  = FIO_OPT_G_INVALID,
        },
 #endif
        {
                .name   = "gtod_reduce",
+               .lname  = "Reduce gettimeofday() calls",
                .type   = FIO_OPT_BOOL,
                .help   = "Greatly reduce number of gettimeofday() calls",
                .cb     = str_gtod_reduce_cb,
                .def    = "0",
-               .category = FIO_OPT_G_OS | FIO_OPT_G_MISC | FIO_OPT_G_STAT,
+               .category = FIO_OPT_C_STAT,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "disable_lat",
+               .lname  = "Disable all latency stats",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(disable_lat),
                .help   = "Disable latency numbers",
                .parent = "gtod_reduce",
                .hide   = 1,
                .def    = "0",
-               .category = FIO_OPT_G_OS | FIO_OPT_G_MISC | FIO_OPT_G_STAT,
+               .category = FIO_OPT_C_STAT,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "disable_clat",
+               .lname  = "Disable completion latency stats",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(disable_clat),
                .help   = "Disable completion latency numbers",
                .parent = "gtod_reduce",
                .hide   = 1,
                .def    = "0",
-               .category = FIO_OPT_G_OS | FIO_OPT_G_MISC | FIO_OPT_G_STAT,
+               .category = FIO_OPT_C_STAT,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "disable_slat",
+               .lname  = "Disable submission latency stats",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(disable_slat),
                .help   = "Disable submission latency numbers",
                .parent = "gtod_reduce",
                .hide   = 1,
                .def    = "0",
-               .category = FIO_OPT_G_OS | FIO_OPT_G_MISC | FIO_OPT_G_STAT,
+               .category = FIO_OPT_C_STAT,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "disable_bw_measurement",
+               .lname  = "Disable bandwidth stats",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(disable_bw),
                .help   = "Disable bandwidth logging",
                .parent = "gtod_reduce",
                .hide   = 1,
                .def    = "0",
-               .category = FIO_OPT_G_OS | FIO_OPT_G_MISC | FIO_OPT_G_STAT,
+               .category = FIO_OPT_C_STAT,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "gtod_cpu",
+               .lname  = "Dedicated gettimeofday() CPU",
                .type   = FIO_OPT_INT,
                .cb     = str_gtod_cpu_cb,
                .help   = "Set up dedicated gettimeofday() thread on this CPU",
                .verify = gtod_cpu_verify,
-               .category = FIO_OPT_G_OS | FIO_OPT_G_MISC | FIO_OPT_G_STAT,
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "continue_on_error",
+               .lname  = "Continue on error",
                .type   = FIO_OPT_STR,
                .off1   = td_var_offset(continue_on_error),
                .help   = "Continue on non-fatal errors during IO",
                .def    = "none",
-               .category = FIO_OPT_G_MISC | FIO_OPT_G_ERR,
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_INVALID,
                .posval = {
                          { .ival = "none",
                            .oval = ERROR_TYPE_NONE,
@@ -2466,69 +2724,86 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
        },
        {
                .name   = "profile",
+               .lname  = "Profile",
                .type   = FIO_OPT_STR_STORE,
                .off1   = td_var_offset(profile),
                .help   = "Select a specific builtin performance test",
-               .category = FIO_OPT_G_MISC | FIO_OPT_G_JOB,
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "cgroup",
+               .lname  = "Cgroup",
                .type   = FIO_OPT_STR_STORE,
                .off1   = td_var_offset(cgroup),
                .help   = "Add job to cgroup of this name",
-               .category = FIO_OPT_G_MISC | FIO_OPT_G_OS,
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "cgroup_weight",
+               .lname  = "Cgroup weight",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(cgroup_weight),
                .help   = "Use given weight for cgroup",
                .minval = 100,
                .maxval = 1000,
-               .category = FIO_OPT_G_MISC | FIO_OPT_G_OS,
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "cgroup_nodelete",
+               .lname  = "Cgroup no-delete",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(cgroup_nodelete),
                .help   = "Do not delete cgroups after job completion",
                .def    = "0",
-               .category = FIO_OPT_G_MISC | FIO_OPT_G_OS,
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "uid",
+               .lname  = "User ID",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(uid),
                .help   = "Run job with this user ID",
-               .category = FIO_OPT_G_OS | FIO_OPT_G_JOB,
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "gid",
+               .lname  = "Group ID",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(gid),
                .help   = "Run job with this group ID",
-               .category = FIO_OPT_G_OS | FIO_OPT_G_JOB,
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "flow_id",
+               .lname  = "I/O flow ID",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(flow_id),
                .help   = "The flow index ID to use",
                .def    = "0",
-               .category = FIO_OPT_G_IO,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_IO_FLOW,
        },
        {
                .name   = "flow",
+               .lname  = "I/O flow weight",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(flow),
                .help   = "Weight for flow control of this job",
                .parent = "flow_id",
                .hide   = 1,
                .def    = "0",
-               .category = FIO_OPT_G_IO,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_IO_FLOW,
        },
        {
                .name   = "flow_watermark",
+               .lname  = "I/O flow watermark",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(flow_watermark),
                .help   = "High watermark for flow control. This option"
@@ -2537,10 +2812,12 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .parent = "flow_id",
                .hide   = 1,
                .def    = "1024",
-               .category = FIO_OPT_G_IO,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_IO_FLOW,
        },
        {
                .name   = "flow_sleep",
+               .lname  = "I/O flow sleep",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(flow_sleep),
                .help   = "How many microseconds to sleep after being held"
@@ -2548,7 +2825,8 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .parent = "flow_id",
                .hide   = 1,
                .def    = "0",
-               .category = FIO_OPT_G_IO,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_IO_FLOW,
        },
        {
                .name = NULL,
index d130fc7..256bf60 100644 (file)
--- a/options.h
+++ b/options.h
@@ -48,47 +48,44 @@ struct opt_group {
 };
 
 enum opt_category {
-       __FIO_OPT_G_DESC        = 0,
-       __FIO_OPT_G_FILE,
-       __FIO_OPT_G_IO,
-       __FIO_OPT_G_IO_DDIR,
-       __FIO_OPT_G_IO_BUF,
-       __FIO_OPT_G_IO_ENG,
-       __FIO_OPT_G_CACHE,
-       __FIO_OPT_G_VERIFY,
+       __FIO_OPT_C_GENERAL     = 0,
+       __FIO_OPT_C_IO,
+       __FIO_OPT_C_FILE,
+       __FIO_OPT_C_STAT,
+       __FIO_OPT_C_LOG,
+       __FIO_OPT_C_NR,
+
+       FIO_OPT_C_GENERAL       = (1U << __FIO_OPT_C_GENERAL),
+       FIO_OPT_C_IO            = (1U << __FIO_OPT_C_IO),
+       FIO_OPT_C_FILE          = (1U << __FIO_OPT_C_FILE),
+       FIO_OPT_C_STAT          = (1U << __FIO_OPT_C_STAT),
+       FIO_OPT_C_LOG           = (1U << __FIO_OPT_C_LOG),
+       FIO_OPT_C_INVALID       = (1U << __FIO_OPT_C_NR),
+};
+
+enum opt_category_group {
+       __FIO_OPT_G_RATE        = 0,
        __FIO_OPT_G_ZONE,
-       __FIO_OPT_G_MEM,
-       __FIO_OPT_G_LOG,
-       __FIO_OPT_G_ERR,
-       __FIO_OPT_G_STAT,
-       __FIO_OPT_G_CPU,
-       __FIO_OPT_G_OS,
-       __FIO_OPT_G_MISC,
-       __FIO_OPT_G_RAND,
-       __FIO_OPT_G_JOB,
+       __FIO_OPT_G_RWMIX,
+       __FIO_OPT_G_VERIFY,
+       __FIO_OPT_G_TRIM,
+       __FIO_OPT_G_IOLOG,
+       __FIO_OPT_G_IO_DEPTH,
+       __FIO_OPT_G_IO_FLOW,
        __FIO_OPT_G_NR,
 
-       FIO_OPT_G_DESC          = (1U << __FIO_OPT_G_DESC),
-       FIO_OPT_G_FILE          = (1U << __FIO_OPT_G_FILE),
-       FIO_OPT_G_MISC          = (1U << __FIO_OPT_G_MISC),
-       FIO_OPT_G_IO            = (1U << __FIO_OPT_G_IO),
-       FIO_OPT_G_IO_DDIR       = (1U << __FIO_OPT_G_IO_DDIR),
-       FIO_OPT_G_IO_BUF        = (1U << __FIO_OPT_G_IO_BUF),
-       FIO_OPT_G_IO_ENG        = (1U << __FIO_OPT_G_IO_ENG),
-       FIO_OPT_G_RAND          = (1U << __FIO_OPT_G_RAND),
-       FIO_OPT_G_OS            = (1U << __FIO_OPT_G_OS),
-       FIO_OPT_G_MEM           = (1U << __FIO_OPT_G_MEM),
-       FIO_OPT_G_VERIFY        = (1U << __FIO_OPT_G_VERIFY),
-       FIO_OPT_G_CPU           = (1U << __FIO_OPT_G_CPU),
-       FIO_OPT_G_LOG           = (1U << __FIO_OPT_G_LOG),
+       FIO_OPT_G_RATE          = (1U << __FIO_OPT_G_RATE),
        FIO_OPT_G_ZONE          = (1U << __FIO_OPT_G_ZONE),
-       FIO_OPT_G_CACHE         = (1U << __FIO_OPT_G_CACHE),
-       FIO_OPT_G_STAT          = (1U << __FIO_OPT_G_STAT),
-       FIO_OPT_G_ERR           = (1U << __FIO_OPT_G_ERR),
-       FIO_OPT_G_JOB           = (1U << __FIO_OPT_G_JOB),
+       FIO_OPT_G_RWMIX         = (1U << __FIO_OPT_G_RWMIX),
+       FIO_OPT_G_VERIFY        = (1U << __FIO_OPT_G_VERIFY),
+       FIO_OPT_G_TRIM          = (1U << __FIO_OPT_G_TRIM),
+       FIO_OPT_G_IOLOG         = (1U << __FIO_OPT_G_IOLOG),
+       FIO_OPT_G_IO_DEPTH      = (1U << __FIO_OPT_G_IO_DEPTH),
+       FIO_OPT_G_IO_FLOW       = (1U << __FIO_OPT_G_IO_FLOW),
        FIO_OPT_G_INVALID       = (1U << __FIO_OPT_G_NR),
 };
 
 extern struct opt_group *opt_group_from_mask(unsigned int *mask);
+extern struct opt_group *opt_group_cat_from_mask(unsigned int *mask);
 
 #endif
diff --git a/parse.c b/parse.c
index dd4e4dd..c1fed56 100644 (file)
--- a/parse.c
+++ b/parse.c
@@ -1049,7 +1049,7 @@ void option_init(struct fio_option *o)
        }
        if (!o->category) {
                log_info("Options %s: no category defined. Setting to misc\n", o->name);
-               o->category = FIO_OPT_G_MISC;
+               o->category = FIO_OPT_C_GENERAL;
        }
 }
 
diff --git a/parse.h b/parse.h
index 36154dd..030a180 100644 (file)
--- a/parse.h
+++ b/parse.h
@@ -40,6 +40,7 @@ struct value_pair {
  */
 struct fio_option {
        const char *name;               /* option name */
+       const char *lname;              /* long option name */
        const char *alias;              /* possible old allowed name */
        enum fio_opt_type type;         /* option type */
        unsigned int off1;              /* potential parameters */
@@ -63,7 +64,8 @@ struct fio_option {
        int hide;                       /* hide if parent isn't set */
        int (*verify)(struct fio_option *, void *);
        const char *prof_name;          /* only valid for specific profile */
-       unsigned int category;          /* for type grouping */
+       unsigned int category;          /* what type of option */
+       unsigned int group;             /* who to group with */
 };
 
 typedef int (str_cb_fn)(void *, char *);
index 517d937..5ab602a 100644 (file)
@@ -23,39 +23,44 @@ static const char *tb_opts[] = {
 static struct fio_option options[] = {
        {
                .name   = "size",
+               .lname  = "Tiobench size",
                .type   = FIO_OPT_STR_VAL,
                .roff1  = &size,
                .help   = "Size in MB",
-               .category = FIO_OPT_G_IO,
+               .category = FIO_OPT_C_IO,
        },
        {
                .name   = "block",
+               .lname  = "Tiobench block",
                .type   = FIO_OPT_INT,
                .roff1  = &bs,
                .help   = "Block size in bytes",
                .def    = "4k",
-               .category = FIO_OPT_G_IO,
+               .category = FIO_OPT_C_IO,
        },
        {
                .name   = "numruns",
+               .lname  = "Tiobench numruns",
                .type   = FIO_OPT_INT,
                .roff1  = &loops,
                .help   = "Number of runs",
-               .category = FIO_OPT_G_JOB,
+               .category = FIO_OPT_C_GENERAL,
        },
        {
                .name   = "dir",
+               .lname  = "Tiobench directory",
                .type   = FIO_OPT_STR_STORE,
                .roff1  = &dir,
                .help   = "Test directory",
-               .category = FIO_OPT_G_FILE,
+               .category = FIO_OPT_C_FILE,
        },
        {
                .name   = "threads",
+               .lname  = "Tiobench threads",
                .type   = FIO_OPT_INT,
                .roff1  = &nthreads,
                .help   = "Number of Threads",
-               .category = FIO_OPT_G_JOB,
+               .category = FIO_OPT_C_GENERAL,
        },
        {
                .name   = NULL,