+
+err:
+ for (ddir = 0; ddir < CMDPRIO_RWDIR_CNT; ddir++)
+ free(values[ddir].prios);
+ free_clat_prio_stats(ts);
+
+ return ret;
+}
+
+static int fio_cmdprio_parse_and_gen_bssplit(struct thread_data *td,
+ struct cmdprio *cmdprio)
+{
+ struct cmdprio_options *options = cmdprio->options;
+ struct cmdprio_parse_result parse_res[CMDPRIO_RWDIR_CNT] = {};
+ struct cmdprio_values values[CMDPRIO_RWDIR_CNT] = {};
+ struct thread_stat *ts = &td->ts;
+ int ret, implicit_cmdprio;
+ enum fio_ddir ddir;
+
+ ret = fio_cmdprio_bssplit_parse(td, options->bssplit_str,
+ &parse_res[0]);
+ if (ret)
+ goto err;
+
+ for (ddir = 0; ddir < CMDPRIO_RWDIR_CNT; ddir++) {
+ /*
+ * Do not allocate a clat_prio array nor set the cmdprio structs
+ * if there are no non-zero entries (for the ddir), or when the
+ * ddir is not enabled.
+ */
+ if (!parse_res[ddir].nr_entries ||
+ (ddir == DDIR_READ && !td_read(td)) ||
+ (ddir == DDIR_WRITE && !td_write(td))) {
+ free(parse_res[ddir].entries);
+ parse_res[ddir].entries = NULL;
+ parse_res[ddir].nr_entries = 0;
+ continue;
+ }
+
+ ret = init_cmdprio_values(&values[ddir],
+ parse_res[ddir].nr_entries, ts);
+ if (ret)
+ goto err;
+
+ implicit_cmdprio = ioprio_value(options->class[ddir],
+ options->level[ddir],
+ options->hint[ddir]);
+
+ ret = fio_cmdprio_generate_bsprio_desc(&cmdprio->bsprio_desc[ddir],
+ &parse_res[ddir],
+ &values[ddir],
+ implicit_cmdprio);
+ if (ret)
+ goto err;
+
+ free(parse_res[ddir].entries);
+ parse_res[ddir].entries = NULL;
+ parse_res[ddir].nr_entries = 0;
+
+ ret = init_ts_clat_prio(ts, ddir, &values[ddir]);
+ if (ret)
+ goto err;
+
+ free(values[ddir].prios);
+ values[ddir].prios = NULL;
+ values[ddir].nr_prios = 0;
+ }
+
+ return 0;
+
+err:
+ for (ddir = 0; ddir < CMDPRIO_RWDIR_CNT; ddir++) {
+ free(parse_res[ddir].entries);
+ free(values[ddir].prios);
+ }
+ free_clat_prio_stats(ts);
+ fio_cmdprio_cleanup(cmdprio);
+
+ return ret;
+}
+
+static int fio_cmdprio_parse_and_gen(struct thread_data *td,
+ struct cmdprio *cmdprio)
+{
+ struct cmdprio_options *options = cmdprio->options;
+ int i, ret;
+
+ /*
+ * If cmdprio_percentage/cmdprio_bssplit is set and cmdprio_class
+ * is not set, default to RT priority class.
+ */
+ for (i = 0; i < CMDPRIO_RWDIR_CNT; i++) {
+ /*
+ * A cmdprio value is only used when fio_cmdprio_percentage()
+ * returns non-zero, so it is safe to set a class even for a
+ * DDIR that will never use it.
+ */
+ if (!options->class[i])
+ options->class[i] = IOPRIO_CLASS_RT;
+ }
+
+ switch (cmdprio->mode) {
+ case CMDPRIO_MODE_BSSPLIT:
+ ret = fio_cmdprio_parse_and_gen_bssplit(td, cmdprio);
+ break;
+ case CMDPRIO_MODE_PERC:
+ ret = fio_cmdprio_gen_perc(td, cmdprio);
+ break;
+ default:
+ assert(0);
+ return 1;
+ }
+
+ return ret;
+}
+
+void fio_cmdprio_cleanup(struct cmdprio *cmdprio)
+{
+ enum fio_ddir ddir;
+ int i;
+
+ for (ddir = 0; ddir < CMDPRIO_RWDIR_CNT; ddir++) {
+ for (i = 0; i < cmdprio->bsprio_desc[ddir].nr_bsprios; i++)
+ free(cmdprio->bsprio_desc[ddir].bsprios[i].prios);
+ free(cmdprio->bsprio_desc[ddir].bsprios);
+ cmdprio->bsprio_desc[ddir].bsprios = NULL;
+ cmdprio->bsprio_desc[ddir].nr_bsprios = 0;
+ }
+
+ /*
+ * options points to a cmdprio_options struct that is part of td->eo.
+ * td->eo itself will be freed by free_ioengine().
+ */
+ cmdprio->options = NULL;