options: add a parsing function for an additional cmdprio_bssplit format
authorNiklas Cassel <niklas.cassel@wdc.com>
Thu, 3 Feb 2022 19:28:27 +0000 (19:28 +0000)
committerJens Axboe <axboe@kernel.dk>
Thu, 3 Feb 2022 22:30:06 +0000 (15:30 -0700)
The cmdprio_bssplit ioengine option for io_uring/libaio is currently parsed
using split_parse_ddir(). While this function works fine for parsing the
existing cmdprio_bssplit entry format, it forces every cmdprio_bssplit
entry to use the priority defined by cmdprio and cmdprio_class. This means
that there will only ever be at most two different priority values used in
the job.

To enable us to use more than two different priority values, add a new
parsing function, split_parse_prio_ddir(), that will support parsing the
existing cmdprio_bssplit entry format (blocksize/percentage), and a new
cmdprio_bssplit entry format (blocksize/percentage/prioclass/priolevel).

Since IO engines can be compiled as plugins, having the parse function in
options.c avoids potential problems with ioengines having different
versions of the same parsing function.

A follow up patch will change to the new parsing function.

Signed-off-by: Niklas Cassel <niklas.cassel@wdc.com>
Link: https://lore.kernel.org/r/20220203192814.18552-8-Niklas.Cassel@wdc.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
options.c
thread_options.h

index 102bcf5661a0c0fbffd54a61ad85281809250d3a..dcf5e09fc2f5cdd733c3a5093482c38400ec6b15 100644 (file)
--- a/options.c
+++ b/options.c
@@ -278,6 +278,128 @@ static int str_bssplit_cb(void *data, const char *input)
        return ret;
 }
 
+static int parse_cmdprio_bssplit_entry(struct thread_options *o,
+                                      struct split_prio *entry, char *str)
+{
+       int matches = 0;
+       char *bs_str = NULL;
+       long long bs_val;
+       unsigned int perc = 0, class, level;
+
+       /*
+        * valid entry formats:
+        * bs/ - %s/ - set perc to 0, prio to -1.
+        * bs/perc - %s/%u - set prio to -1.
+        * bs/perc/class/level - %s/%u/%u/%u
+        */
+       matches = sscanf(str, "%m[^/]/%u/%u/%u", &bs_str, &perc, &class, &level);
+       if (matches < 1) {
+               log_err("fio: invalid cmdprio_bssplit format\n");
+               return 1;
+       }
+
+       if (str_to_decimal(bs_str, &bs_val, 1, o, 0, 0)) {
+               log_err("fio: split conversion failed\n");
+               free(bs_str);
+               return 1;
+       }
+       free(bs_str);
+
+       entry->bs = bs_val;
+       entry->perc = min(perc, 100u);
+       entry->prio = -1;
+       switch (matches) {
+       case 1: /* bs/ case */
+       case 2: /* bs/perc case */
+               break;
+       case 4: /* bs/perc/class/level case */
+               class = min(class, (unsigned int) IOPRIO_MAX_PRIO_CLASS);
+               level = min(level, (unsigned int) IOPRIO_MAX_PRIO);
+               entry->prio = ioprio_value(class, level);
+               break;
+       default:
+               log_err("fio: invalid cmdprio_bssplit format\n");
+               return 1;
+       }
+
+       return 0;
+}
+
+/*
+ * Returns a negative integer if the first argument should be before the second
+ * argument in the sorted list. A positive integer if the first argument should
+ * be after the second argument in the sorted list. A zero if they are equal.
+ */
+static int fio_split_prio_cmp(const void *p1, const void *p2)
+{
+       const struct split_prio *tmp1 = p1;
+       const struct split_prio *tmp2 = p2;
+
+       if (tmp1->bs > tmp2->bs)
+               return 1;
+       if (tmp1->bs < tmp2->bs)
+               return -1;
+       return 0;
+}
+
+int split_parse_prio_ddir(struct thread_options *o, struct split_prio **entries,
+                         int *nr_entries, char *str)
+{
+       struct split_prio *tmp_entries;
+       unsigned int nr_bssplits;
+       char *str_cpy, *p, *fname;
+
+       /* strsep modifies the string, dup it so that we can use strsep twice */
+       p = str_cpy = strdup(str);
+       if (!p)
+               return 1;
+
+       nr_bssplits = 0;
+       while ((fname = strsep(&str_cpy, ":")) != NULL) {
+               if (!strlen(fname))
+                       break;
+               nr_bssplits++;
+       }
+       free(p);
+
+       if (nr_bssplits > BSSPLIT_MAX) {
+               log_err("fio: too many cmdprio_bssplit entries\n");
+               return 1;
+       }
+
+       tmp_entries = calloc(nr_bssplits, sizeof(*tmp_entries));
+       if (!tmp_entries)
+               return 1;
+
+       nr_bssplits = 0;
+       while ((fname = strsep(&str, ":")) != NULL) {
+               struct split_prio *entry;
+
+               if (!strlen(fname))
+                       break;
+
+               entry = &tmp_entries[nr_bssplits];
+
+               if (parse_cmdprio_bssplit_entry(o, entry, fname)) {
+                       log_err("fio: failed to parse cmdprio_bssplit entry\n");
+                       free(tmp_entries);
+                       return 1;
+               }
+
+               /* skip zero perc entries, they provide no useful information */
+               if (entry->perc)
+                       nr_bssplits++;
+       }
+
+       qsort(tmp_entries, nr_bssplits, sizeof(*tmp_entries),
+             fio_split_prio_cmp);
+
+       *entries = tmp_entries;
+       *nr_entries = nr_bssplits;
+
+       return 0;
+}
+
 static int str2error(char *str)
 {
        const char *err[] = { "EPERM", "ENOENT", "ESRCH", "EINTR", "EIO",
index 8f4c8a5996aefa912d13f6ec263d82d9d05db033..ace658b7144d2b69ca7d059c2b246987f5544162 100644 (file)
@@ -50,6 +50,12 @@ struct split {
        unsigned long long val2[ZONESPLIT_MAX];
 };
 
+struct split_prio {
+       uint64_t bs;
+       int32_t prio;
+       uint32_t perc;
+};
+
 struct bssplit {
        uint64_t bs;
        uint32_t perc;
@@ -702,4 +708,8 @@ extern int str_split_parse(struct thread_data *td, char *str,
 extern int split_parse_ddir(struct thread_options *o, struct split *split,
                            char *str, bool absolute, unsigned int max_splits);
 
+extern int split_parse_prio_ddir(struct thread_options *o,
+                                struct split_prio **entries, int *nr_entries,
+                                char *str);
+
 #endif