Commit | Line | Data |
---|---|---|
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 | ||
8 | static 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 | ||
45 | int fio_cmdprio_bssplit_parse(struct thread_data *td, const char *input, | |
46 | struct cmdprio *cmdprio) | |
47 | { | |
48 | char *str, *p; | |
d6cbeab4 | 49 | int ret = 0; |
e27b9ff0 NC |
50 | |
51 | p = str = strdup(input); | |
52 | ||
53 | strip_blank_front(&str); | |
54 | strip_blank_end(str); | |
55 | ||
d6cbeab4 NC |
56 | ret = str_split_parse(td, str, fio_cmdprio_bssplit_ddir, cmdprio, |
57 | false); | |
e27b9ff0 NC |
58 | |
59 | free(p); | |
60 | return ret; | |
61 | } | |
62 | ||
127715b6 | 63 | static int fio_cmdprio_percentage(struct cmdprio *cmdprio, struct io_u *io_u) |
e27b9ff0 NC |
64 | { |
65 | enum fio_ddir ddir = io_u->ddir; | |
d6cbeab4 | 66 | struct cmdprio_options *options = cmdprio->options; |
e27b9ff0 NC |
67 | int i; |
68 | ||
97f2d484 NC |
69 | switch (cmdprio->mode) { |
70 | case CMDPRIO_MODE_PERC: | |
d6cbeab4 | 71 | return options->percentage[ddir]; |
97f2d484 NC |
72 | case CMDPRIO_MODE_BSSPLIT: |
73 | for (i = 0; i < cmdprio->bssplit_nr[ddir]; i++) { | |
74 | if (cmdprio->bssplit[ddir][i].bs == io_u->buflen) | |
75 | return cmdprio->bssplit[ddir][i].perc; | |
76 | } | |
77 | break; | |
78 | default: | |
79 | /* | |
80 | * An I/O engine should never call this function if cmdprio | |
81 | * is not is use. | |
82 | */ | |
83 | assert(0); | |
e27b9ff0 NC |
84 | } |
85 | ||
86 | return 0; | |
87 | } | |
88 | ||
127715b6 NC |
89 | /** |
90 | * fio_cmdprio_set_ioprio - Set an io_u ioprio according to cmdprio options | |
91 | * | |
92 | * Generates a random percentage value to determine if an io_u ioprio needs | |
93 | * to be set. If the random percentage value is within the user specified | |
94 | * percentage of I/Os that should use a cmdprio priority value (rather than | |
95 | * the default priority), then this function updates the io_u with an ioprio | |
96 | * value as defined by the cmdprio/cmdprio_class or cmdprio_bssplit options. | |
97 | * | |
98 | * Return true if the io_u ioprio was changed and false otherwise. | |
99 | */ | |
100 | bool fio_cmdprio_set_ioprio(struct thread_data *td, struct cmdprio *cmdprio, | |
101 | struct io_u *io_u) | |
102 | { | |
103 | enum fio_ddir ddir = io_u->ddir; | |
d6cbeab4 | 104 | struct cmdprio_options *options = cmdprio->options; |
97f2d484 | 105 | unsigned int p; |
127715b6 | 106 | unsigned int cmdprio_value = |
d6cbeab4 | 107 | ioprio_value(options->class[ddir], options->level[ddir]); |
127715b6 | 108 | |
97f2d484 | 109 | p = fio_cmdprio_percentage(cmdprio, io_u); |
127715b6 NC |
110 | if (p && rand_between(&td->prio_state, 0, 99) < p) { |
111 | io_u->ioprio = cmdprio_value; | |
112 | if (!td->ioprio || cmdprio_value < td->ioprio) { | |
113 | /* | |
114 | * The async IO priority is higher (has a lower value) | |
115 | * than the default priority (which is either 0 or the | |
116 | * value set by "prio" and "prioclass" options). | |
117 | */ | |
118 | io_u->flags |= IO_U_F_HIGH_PRIO; | |
119 | } | |
120 | return true; | |
121 | } | |
122 | ||
123 | if (td->ioprio && td->ioprio < cmdprio_value) { | |
124 | /* | |
125 | * The IO will be executed with the default priority (which is | |
126 | * either 0 or the value set by "prio" and "prioclass options), | |
127 | * and this priority is higher (has a lower value) than the | |
128 | * async IO priority. | |
129 | */ | |
130 | io_u->flags |= IO_U_F_HIGH_PRIO; | |
131 | } | |
132 | ||
133 | return false; | |
134 | } | |
135 | ||
d6cbeab4 NC |
136 | static int fio_cmdprio_parse_and_gen_bssplit(struct thread_data *td, |
137 | struct cmdprio *cmdprio) | |
e27b9ff0 | 138 | { |
d6cbeab4 NC |
139 | struct cmdprio_options *options = cmdprio->options; |
140 | int ret; | |
141 | ||
142 | ret = fio_cmdprio_bssplit_parse(td, options->bssplit_str, cmdprio); | |
143 | if (ret) | |
144 | goto err; | |
145 | ||
146 | return 0; | |
147 | ||
148 | err: | |
149 | fio_cmdprio_cleanup(cmdprio); | |
150 | ||
151 | return ret; | |
152 | } | |
153 | ||
154 | static int fio_cmdprio_parse_and_gen(struct thread_data *td, | |
155 | struct cmdprio *cmdprio) | |
156 | { | |
157 | struct cmdprio_options *options = cmdprio->options; | |
158 | int i, ret; | |
159 | ||
160 | switch (cmdprio->mode) { | |
161 | case CMDPRIO_MODE_BSSPLIT: | |
162 | ret = fio_cmdprio_parse_and_gen_bssplit(td, cmdprio); | |
163 | break; | |
164 | case CMDPRIO_MODE_PERC: | |
165 | ret = 0; | |
166 | break; | |
167 | default: | |
168 | assert(0); | |
169 | return 1; | |
170 | } | |
e27b9ff0 NC |
171 | |
172 | /* | |
173 | * If cmdprio_percentage/cmdprio_bssplit is set and cmdprio_class | |
174 | * is not set, default to RT priority class. | |
175 | */ | |
a5af2a8b | 176 | for (i = 0; i < CMDPRIO_RWDIR_CNT; i++) { |
d6cbeab4 NC |
177 | if (options->percentage[i] || cmdprio->bssplit_nr[i]) { |
178 | if (!options->class[i]) | |
179 | options->class[i] = IOPRIO_CLASS_RT; | |
e27b9ff0 NC |
180 | } |
181 | } | |
182 | ||
d6cbeab4 NC |
183 | return ret; |
184 | } | |
185 | ||
186 | void fio_cmdprio_cleanup(struct cmdprio *cmdprio) | |
187 | { | |
188 | int ddir; | |
189 | ||
190 | for (ddir = 0; ddir < CMDPRIO_RWDIR_CNT; ddir++) { | |
191 | free(cmdprio->bssplit[ddir]); | |
192 | cmdprio->bssplit[ddir] = NULL; | |
193 | cmdprio->bssplit_nr[ddir] = 0; | |
194 | } | |
195 | ||
196 | /* | |
197 | * options points to a cmdprio_options struct that is part of td->eo. | |
198 | * td->eo itself will be freed by free_ioengine(). | |
199 | */ | |
200 | cmdprio->options = NULL; | |
201 | } | |
202 | ||
203 | int fio_cmdprio_init(struct thread_data *td, struct cmdprio *cmdprio, | |
204 | struct cmdprio_options *options) | |
205 | { | |
206 | struct thread_options *to = &td->o; | |
207 | bool has_cmdprio_percentage = false; | |
208 | bool has_cmdprio_bssplit = false; | |
209 | int i; | |
210 | ||
211 | cmdprio->options = options; | |
212 | ||
213 | if (options->bssplit_str && strlen(options->bssplit_str)) | |
214 | has_cmdprio_bssplit = true; | |
215 | ||
216 | for (i = 0; i < CMDPRIO_RWDIR_CNT; i++) { | |
217 | if (options->percentage[i]) | |
218 | has_cmdprio_percentage = true; | |
219 | } | |
220 | ||
e27b9ff0 NC |
221 | /* |
222 | * Check for option conflicts | |
223 | */ | |
224 | if (has_cmdprio_percentage && has_cmdprio_bssplit) { | |
225 | log_err("%s: cmdprio_percentage and cmdprio_bssplit options " | |
226 | "are mutually exclusive\n", | |
227 | to->name); | |
228 | return 1; | |
229 | } | |
230 | ||
97f2d484 NC |
231 | if (has_cmdprio_bssplit) |
232 | cmdprio->mode = CMDPRIO_MODE_BSSPLIT; | |
233 | else if (has_cmdprio_percentage) | |
234 | cmdprio->mode = CMDPRIO_MODE_PERC; | |
235 | else | |
236 | cmdprio->mode = CMDPRIO_MODE_NONE; | |
e27b9ff0 | 237 | |
d6cbeab4 NC |
238 | /* Nothing left to do if cmdprio is not used */ |
239 | if (cmdprio->mode == CMDPRIO_MODE_NONE) | |
240 | return 0; | |
241 | ||
242 | return fio_cmdprio_parse_and_gen(td, cmdprio); | |
e27b9ff0 | 243 | } |