Merge branch 'arm-detect-pmull' of https://github.com/sitsofe/fio
[fio.git] / engines / cmdprio.c
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;
49         int 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,
57                               false);
58
59         free(p);
60         return ret;
61 }
62
63 static int fio_cmdprio_percentage(struct cmdprio *cmdprio, struct io_u *io_u)
64 {
65         enum fio_ddir ddir = io_u->ddir;
66         struct cmdprio_options *options = cmdprio->options;
67         int i;
68
69         switch (cmdprio->mode) {
70         case CMDPRIO_MODE_PERC:
71                 return options->percentage[ddir];
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);
84         }
85
86         return 0;
87 }
88
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;
104         struct cmdprio_options *options = cmdprio->options;
105         unsigned int p;
106         unsigned int cmdprio_value =
107                 ioprio_value(options->class[ddir], options->level[ddir]);
108
109         p = fio_cmdprio_percentage(cmdprio, io_u);
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
136 static int fio_cmdprio_parse_and_gen_bssplit(struct thread_data *td,
137                                              struct cmdprio *cmdprio)
138 {
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         }
171
172         /*
173          * If cmdprio_percentage/cmdprio_bssplit is set and cmdprio_class
174          * is not set, default to RT priority class.
175          */
176         for (i = 0; i < CMDPRIO_RWDIR_CNT; i++) {
177                 if (options->percentage[i] || cmdprio->bssplit_nr[i]) {
178                         if (!options->class[i])
179                                 options->class[i] = IOPRIO_CLASS_RT;
180                 }
181         }
182
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
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
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;
237
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);
243 }