cmdprio: add mode to make the logic easier to reason about
[fio.git] / engines / cmdprio.c
CommitLineData
e27b9ff0
NC
1/*
2 * IO priority handling helper functions common to the libaio and io_uring
3 * engines.
4 */
5
6#include "cmdprio.h"
7
8static int fio_cmdprio_bssplit_ddir(struct thread_options *to, void *cb_arg,
9 enum fio_ddir ddir, char *str, bool data)
10{
11 struct cmdprio *cmdprio = cb_arg;
12 struct split split;
13 unsigned int i;
14
15 if (ddir == DDIR_TRIM)
16 return 0;
17
18 memset(&split, 0, sizeof(split));
19
20 if (split_parse_ddir(to, &split, str, data, BSSPLIT_MAX))
21 return 1;
22 if (!split.nr)
23 return 0;
24
25 cmdprio->bssplit_nr[ddir] = split.nr;
26 cmdprio->bssplit[ddir] = malloc(split.nr * sizeof(struct bssplit));
27 if (!cmdprio->bssplit[ddir])
28 return 1;
29
30 for (i = 0; i < split.nr; i++) {
31 cmdprio->bssplit[ddir][i].bs = split.val1[i];
32 if (split.val2[i] == -1U) {
33 cmdprio->bssplit[ddir][i].perc = 0;
34 } else {
35 if (split.val2[i] > 100)
36 cmdprio->bssplit[ddir][i].perc = 100;
37 else
38 cmdprio->bssplit[ddir][i].perc = split.val2[i];
39 }
40 }
41
42 return 0;
43}
44
45int fio_cmdprio_bssplit_parse(struct thread_data *td, const char *input,
46 struct cmdprio *cmdprio)
47{
48 char *str, *p;
49 int i, ret = 0;
50
51 p = str = strdup(input);
52
53 strip_blank_front(&str);
54 strip_blank_end(str);
55
56 ret = str_split_parse(td, str, fio_cmdprio_bssplit_ddir, cmdprio, false);
57
58 if (parse_dryrun()) {
a5af2a8b 59 for (i = 0; i < CMDPRIO_RWDIR_CNT; i++) {
e27b9ff0
NC
60 free(cmdprio->bssplit[i]);
61 cmdprio->bssplit[i] = NULL;
62 cmdprio->bssplit_nr[i] = 0;
63 }
64 }
65
66 free(p);
67 return ret;
68}
69
127715b6 70static int fio_cmdprio_percentage(struct cmdprio *cmdprio, struct io_u *io_u)
e27b9ff0
NC
71{
72 enum fio_ddir ddir = io_u->ddir;
e27b9ff0
NC
73 int i;
74
97f2d484
NC
75 switch (cmdprio->mode) {
76 case CMDPRIO_MODE_PERC:
77 return cmdprio->percentage[ddir];
78 case CMDPRIO_MODE_BSSPLIT:
79 for (i = 0; i < cmdprio->bssplit_nr[ddir]; i++) {
80 if (cmdprio->bssplit[ddir][i].bs == io_u->buflen)
81 return cmdprio->bssplit[ddir][i].perc;
82 }
83 break;
84 default:
85 /*
86 * An I/O engine should never call this function if cmdprio
87 * is not is use.
88 */
89 assert(0);
e27b9ff0
NC
90 }
91
92 return 0;
93}
94
127715b6
NC
95/**
96 * fio_cmdprio_set_ioprio - Set an io_u ioprio according to cmdprio options
97 *
98 * Generates a random percentage value to determine if an io_u ioprio needs
99 * to be set. If the random percentage value is within the user specified
100 * percentage of I/Os that should use a cmdprio priority value (rather than
101 * the default priority), then this function updates the io_u with an ioprio
102 * value as defined by the cmdprio/cmdprio_class or cmdprio_bssplit options.
103 *
104 * Return true if the io_u ioprio was changed and false otherwise.
105 */
106bool fio_cmdprio_set_ioprio(struct thread_data *td, struct cmdprio *cmdprio,
107 struct io_u *io_u)
108{
109 enum fio_ddir ddir = io_u->ddir;
97f2d484 110 unsigned int p;
127715b6
NC
111 unsigned int cmdprio_value =
112 ioprio_value(cmdprio->class[ddir], cmdprio->level[ddir]);
113
97f2d484 114 p = fio_cmdprio_percentage(cmdprio, io_u);
127715b6
NC
115 if (p && rand_between(&td->prio_state, 0, 99) < p) {
116 io_u->ioprio = cmdprio_value;
117 if (!td->ioprio || cmdprio_value < td->ioprio) {
118 /*
119 * The async IO priority is higher (has a lower value)
120 * than the default priority (which is either 0 or the
121 * value set by "prio" and "prioclass" options).
122 */
123 io_u->flags |= IO_U_F_HIGH_PRIO;
124 }
125 return true;
126 }
127
128 if (td->ioprio && td->ioprio < cmdprio_value) {
129 /*
130 * The IO will be executed with the default priority (which is
131 * either 0 or the value set by "prio" and "prioclass options),
132 * and this priority is higher (has a lower value) than the
133 * async IO priority.
134 */
135 io_u->flags |= IO_U_F_HIGH_PRIO;
136 }
137
138 return false;
139}
140
97f2d484 141int fio_cmdprio_init(struct thread_data *td, struct cmdprio *cmdprio)
e27b9ff0
NC
142{
143 struct thread_options *to = &td->o;
144 bool has_cmdprio_percentage = false;
145 bool has_cmdprio_bssplit = false;
146 int i;
147
148 /*
149 * If cmdprio_percentage/cmdprio_bssplit is set and cmdprio_class
150 * is not set, default to RT priority class.
151 */
a5af2a8b 152 for (i = 0; i < CMDPRIO_RWDIR_CNT; i++) {
e27b9ff0
NC
153 if (cmdprio->percentage[i]) {
154 if (!cmdprio->class[i])
155 cmdprio->class[i] = IOPRIO_CLASS_RT;
156 has_cmdprio_percentage = true;
157 }
158 if (cmdprio->bssplit_nr[i]) {
159 if (!cmdprio->class[i])
160 cmdprio->class[i] = IOPRIO_CLASS_RT;
161 has_cmdprio_bssplit = true;
162 }
163 }
164
165 /*
166 * Check for option conflicts
167 */
168 if (has_cmdprio_percentage && has_cmdprio_bssplit) {
169 log_err("%s: cmdprio_percentage and cmdprio_bssplit options "
170 "are mutually exclusive\n",
171 to->name);
172 return 1;
173 }
174
97f2d484
NC
175 if (has_cmdprio_bssplit)
176 cmdprio->mode = CMDPRIO_MODE_BSSPLIT;
177 else if (has_cmdprio_percentage)
178 cmdprio->mode = CMDPRIO_MODE_PERC;
179 else
180 cmdprio->mode = CMDPRIO_MODE_NONE;
e27b9ff0
NC
181
182 return 0;
183}