cmdprio: add mode to make the logic easier to reason about
[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 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()) {
59                 for (i = 0; i < CMDPRIO_RWDIR_CNT; i++) {
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
70 static int fio_cmdprio_percentage(struct cmdprio *cmdprio, struct io_u *io_u)
71 {
72         enum fio_ddir ddir = io_u->ddir;
73         int i;
74
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);
90         }
91
92         return 0;
93 }
94
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  */
106 bool 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;
110         unsigned int p;
111         unsigned int cmdprio_value =
112                 ioprio_value(cmdprio->class[ddir], cmdprio->level[ddir]);
113
114         p = fio_cmdprio_percentage(cmdprio, io_u);
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
141 int fio_cmdprio_init(struct thread_data *td, struct cmdprio *cmdprio)
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          */
152         for (i = 0; i < CMDPRIO_RWDIR_CNT; i++) {
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
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;
181
182         return 0;
183 }