+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;
+}
+