stats: Add hint information to per priority level stats
[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 /*
9  * Temporary array used during parsing. Will be freed after the corresponding
10  * struct bsprio_desc has been generated and saved in cmdprio->bsprio_desc.
11  */
12 struct cmdprio_parse_result {
13         struct split_prio *entries;
14         int nr_entries;
15 };
16
17 /*
18  * Temporary array used during init. Will be freed after the corresponding
19  * struct clat_prio_stat array has been saved in td->ts.clat_prio and the
20  * matching clat_prio_indexes have been saved in each struct cmdprio_prio.
21  */
22 struct cmdprio_values {
23         unsigned int *prios;
24         int nr_prios;
25 };
26
27 static int find_clat_prio_index(unsigned int *all_prios, int nr_prios,
28                                 int32_t prio)
29 {
30         int i;
31
32         for (i = 0; i < nr_prios; i++) {
33                 if (all_prios[i] == prio)
34                         return i;
35         }
36
37         return -1;
38 }
39
40 /**
41  * assign_clat_prio_index - In order to avoid stat.c the need to loop through
42  * all possible priorities each time add_clat_sample() / add_lat_sample() is
43  * called, save which index to use in each cmdprio_prio. This will later be
44  * propagated to the io_u, if the specific io_u was determined to use a cmdprio
45  * priority value.
46  */
47 static void assign_clat_prio_index(struct cmdprio_prio *prio,
48                                    struct cmdprio_values *values)
49 {
50         int clat_prio_index = find_clat_prio_index(values->prios,
51                                                    values->nr_prios,
52                                                    prio->prio);
53         if (clat_prio_index == -1) {
54                 clat_prio_index = values->nr_prios;
55                 values->prios[clat_prio_index] = prio->prio;
56                 values->nr_prios++;
57         }
58         prio->clat_prio_index = clat_prio_index;
59 }
60
61 /**
62  * init_cmdprio_values - Allocate a temporary array that can hold all unique
63  * priorities (per ddir), so that we can assign_clat_prio_index() for each
64  * cmdprio_prio during setup. This temporary array is freed after setup.
65  */
66 static int init_cmdprio_values(struct cmdprio_values *values,
67                                int max_unique_prios, struct thread_stat *ts)
68 {
69         values->prios = calloc(max_unique_prios + 1,
70                                sizeof(*values->prios));
71         if (!values->prios)
72                 return 1;
73
74         /* td->ioprio/ts->ioprio is always stored at index 0. */
75         values->prios[0] = ts->ioprio;
76         values->nr_prios++;
77
78         return 0;
79 }
80
81 /**
82  * init_ts_clat_prio - Allocates and fills a clat_prio_stat array which holds
83  * all unique priorities (per ddir).
84  */
85 static int init_ts_clat_prio(struct thread_stat *ts, enum fio_ddir ddir,
86                              struct cmdprio_values *values)
87 {
88         int i;
89
90         if (alloc_clat_prio_stat_ddir(ts, ddir, values->nr_prios))
91                 return 1;
92
93         for (i = 0; i < values->nr_prios; i++)
94                 ts->clat_prio[ddir][i].ioprio = values->prios[i];
95
96         return 0;
97 }
98
99 static int fio_cmdprio_fill_bsprio(struct cmdprio_bsprio *bsprio,
100                                    struct split_prio *entries,
101                                    struct cmdprio_values *values,
102                                    int implicit_cmdprio, int start, int end)
103 {
104         struct cmdprio_prio *prio;
105         int i = end - start + 1;
106
107         bsprio->prios = calloc(i, sizeof(*bsprio->prios));
108         if (!bsprio->prios)
109                 return 1;
110
111         bsprio->bs = entries[start].bs;
112         bsprio->nr_prios = 0;
113         for (i = start; i <= end; i++) {
114                 prio = &bsprio->prios[bsprio->nr_prios];
115                 prio->perc = entries[i].perc;
116                 if (entries[i].prio == -1)
117                         prio->prio = implicit_cmdprio;
118                 else
119                         prio->prio = entries[i].prio;
120                 assign_clat_prio_index(prio, values);
121                 bsprio->tot_perc += entries[i].perc;
122                 if (bsprio->tot_perc > 100) {
123                         log_err("fio: cmdprio_bssplit total percentage "
124                                 "for bs: %"PRIu64" exceeds 100\n",
125                                 bsprio->bs);
126                         free(bsprio->prios);
127                         return 1;
128                 }
129                 bsprio->nr_prios++;
130         }
131
132         return 0;
133 }
134
135 static int
136 fio_cmdprio_generate_bsprio_desc(struct cmdprio_bsprio_desc *bsprio_desc,
137                                  struct cmdprio_parse_result *parse_res,
138                                  struct cmdprio_values *values,
139                                  int implicit_cmdprio)
140 {
141         struct split_prio *entries = parse_res->entries;
142         int nr_entries = parse_res->nr_entries;
143         struct cmdprio_bsprio *bsprio;
144         int i, start, count = 0;
145
146         /*
147          * The parsed result is sorted by blocksize, so count only the number
148          * of different blocksizes, to know how many cmdprio_bsprio we need.
149          */
150         for (i = 0; i < nr_entries; i++) {
151                 while (i + 1 < nr_entries && entries[i].bs == entries[i + 1].bs)
152                         i++;
153                 count++;
154         }
155
156         /*
157          * This allocation is not freed on error. Instead, the calling function
158          * is responsible for calling fio_cmdprio_cleanup() on error.
159          */
160         bsprio_desc->bsprios = calloc(count, sizeof(*bsprio_desc->bsprios));
161         if (!bsprio_desc->bsprios)
162                 return 1;
163
164         start = 0;
165         bsprio_desc->nr_bsprios = 0;
166         for (i = 0; i < nr_entries; i++) {
167                 while (i + 1 < nr_entries && entries[i].bs == entries[i + 1].bs)
168                         i++;
169                 bsprio = &bsprio_desc->bsprios[bsprio_desc->nr_bsprios];
170                 /*
171                  * All parsed entries with the same blocksize get saved in the
172                  * same cmdprio_bsprio, to expedite the search in the hot path.
173                  */
174                 if (fio_cmdprio_fill_bsprio(bsprio, entries, values,
175                                             implicit_cmdprio, start, i))
176                         return 1;
177
178                 start = i + 1;
179                 bsprio_desc->nr_bsprios++;
180         }
181
182         return 0;
183 }
184
185 static int fio_cmdprio_bssplit_ddir(struct thread_options *to, void *cb_arg,
186                                     enum fio_ddir ddir, char *str, bool data)
187 {
188         struct cmdprio_parse_result *parse_res_arr = cb_arg;
189         struct cmdprio_parse_result *parse_res = &parse_res_arr[ddir];
190
191         if (ddir == DDIR_TRIM)
192                 return 0;
193
194         if (split_parse_prio_ddir(to, &parse_res->entries,
195                                   &parse_res->nr_entries, str))
196                 return 1;
197
198         return 0;
199 }
200
201 static int fio_cmdprio_bssplit_parse(struct thread_data *td, const char *input,
202                                      struct cmdprio_parse_result *parse_res)
203 {
204         char *str, *p;
205         int ret = 0;
206
207         p = str = strdup(input);
208
209         strip_blank_front(&str);
210         strip_blank_end(str);
211
212         ret = str_split_parse(td, str, fio_cmdprio_bssplit_ddir, parse_res,
213                               false);
214
215         free(p);
216         return ret;
217 }
218
219 /**
220  * fio_cmdprio_percentage - Returns the percentage of I/Os that should
221  * use a cmdprio priority value (rather than the default context priority).
222  *
223  * For CMDPRIO_MODE_BSSPLIT, if the percentage is non-zero, we will also
224  * return the matching bsprio, to avoid the same linear search elsewhere.
225  * For CMDPRIO_MODE_PERC, we will never return a bsprio.
226  */
227 static int fio_cmdprio_percentage(struct cmdprio *cmdprio, struct io_u *io_u,
228                                   struct cmdprio_bsprio **bsprio)
229 {
230         struct cmdprio_bsprio *bsprio_entry;
231         enum fio_ddir ddir = io_u->ddir;
232         int i;
233
234         switch (cmdprio->mode) {
235         case CMDPRIO_MODE_PERC:
236                 *bsprio = NULL;
237                 return cmdprio->perc_entry[ddir].perc;
238         case CMDPRIO_MODE_BSSPLIT:
239                 for (i = 0; i < cmdprio->bsprio_desc[ddir].nr_bsprios; i++) {
240                         bsprio_entry = &cmdprio->bsprio_desc[ddir].bsprios[i];
241                         if (bsprio_entry->bs == io_u->buflen) {
242                                 *bsprio = bsprio_entry;
243                                 return bsprio_entry->tot_perc;
244                         }
245                 }
246                 break;
247         default:
248                 /*
249                  * An I/O engine should never call this function if cmdprio
250                  * is not is use.
251                  */
252                 assert(0);
253         }
254
255         /*
256          * This is totally fine, the given blocksize simply does not
257          * have any (non-zero) cmdprio_bssplit entries defined.
258          */
259         *bsprio = NULL;
260         return 0;
261 }
262
263 /**
264  * fio_cmdprio_set_ioprio - Set an io_u ioprio according to cmdprio options
265  *
266  * Generates a random percentage value to determine if an io_u ioprio needs
267  * to be set. If the random percentage value is within the user specified
268  * percentage of I/Os that should use a cmdprio priority value (rather than
269  * the default priority), then this function updates the io_u with an ioprio
270  * value as defined by the cmdprio/cmdprio_hint/cmdprio_class or
271  * cmdprio_bssplit options.
272  *
273  * Return true if the io_u ioprio was changed and false otherwise.
274  */
275 bool fio_cmdprio_set_ioprio(struct thread_data *td, struct cmdprio *cmdprio,
276                             struct io_u *io_u)
277 {
278         struct cmdprio_bsprio *bsprio;
279         unsigned int p, rand;
280         uint32_t perc = 0;
281         int i;
282
283         p = fio_cmdprio_percentage(cmdprio, io_u, &bsprio);
284         if (!p)
285                 return false;
286
287         rand = rand_between(&td->prio_state, 0, 99);
288         if (rand >= p)
289                 return false;
290
291         switch (cmdprio->mode) {
292         case CMDPRIO_MODE_PERC:
293                 io_u->ioprio = cmdprio->perc_entry[io_u->ddir].prio;
294                 io_u->clat_prio_index =
295                         cmdprio->perc_entry[io_u->ddir].clat_prio_index;
296                 return true;
297         case CMDPRIO_MODE_BSSPLIT:
298                 assert(bsprio);
299                 for (i = 0; i < bsprio->nr_prios; i++) {
300                         struct cmdprio_prio *prio = &bsprio->prios[i];
301
302                         perc += prio->perc;
303                         if (rand < perc) {
304                                 io_u->ioprio = prio->prio;
305                                 io_u->clat_prio_index = prio->clat_prio_index;
306                                 return true;
307                         }
308                 }
309                 break;
310         default:
311                 assert(0);
312         }
313
314         /* When rand < p (total perc), we should always find a cmdprio_prio. */
315         assert(0);
316         return false;
317 }
318
319 static int fio_cmdprio_gen_perc(struct thread_data *td, struct cmdprio *cmdprio)
320 {
321         struct cmdprio_options *options = cmdprio->options;
322         struct cmdprio_prio *prio;
323         struct cmdprio_values values[CMDPRIO_RWDIR_CNT] = {};
324         struct thread_stat *ts = &td->ts;
325         enum fio_ddir ddir;
326         int ret;
327
328         for (ddir = 0; ddir < CMDPRIO_RWDIR_CNT; ddir++) {
329                 /*
330                  * Do not allocate a clat_prio array nor set the cmdprio struct
331                  * if zero percent of the I/Os (for the ddir) should use a
332                  * cmdprio priority value, or when the ddir is not enabled.
333                  */
334                 if (!options->percentage[ddir] ||
335                     (ddir == DDIR_READ && !td_read(td)) ||
336                     (ddir == DDIR_WRITE && !td_write(td)))
337                         continue;
338
339                 ret = init_cmdprio_values(&values[ddir], 1, ts);
340                 if (ret)
341                         goto err;
342
343                 prio = &cmdprio->perc_entry[ddir];
344                 prio->perc = options->percentage[ddir];
345                 prio->prio = ioprio_value(options->class[ddir],
346                                           options->level[ddir],
347                                           options->hint[ddir]);
348                 assign_clat_prio_index(prio, &values[ddir]);
349
350                 ret = init_ts_clat_prio(ts, ddir, &values[ddir]);
351                 if (ret)
352                         goto err;
353
354                 free(values[ddir].prios);
355                 values[ddir].prios = NULL;
356                 values[ddir].nr_prios = 0;
357         }
358
359         return 0;
360
361 err:
362         for (ddir = 0; ddir < CMDPRIO_RWDIR_CNT; ddir++)
363                 free(values[ddir].prios);
364         free_clat_prio_stats(ts);
365
366         return ret;
367 }
368
369 static int fio_cmdprio_parse_and_gen_bssplit(struct thread_data *td,
370                                              struct cmdprio *cmdprio)
371 {
372         struct cmdprio_options *options = cmdprio->options;
373         struct cmdprio_parse_result parse_res[CMDPRIO_RWDIR_CNT] = {};
374         struct cmdprio_values values[CMDPRIO_RWDIR_CNT] = {};
375         struct thread_stat *ts = &td->ts;
376         int ret, implicit_cmdprio;
377         enum fio_ddir ddir;
378
379         ret = fio_cmdprio_bssplit_parse(td, options->bssplit_str,
380                                         &parse_res[0]);
381         if (ret)
382                 goto err;
383
384         for (ddir = 0; ddir < CMDPRIO_RWDIR_CNT; ddir++) {
385                 /*
386                  * Do not allocate a clat_prio array nor set the cmdprio structs
387                  * if there are no non-zero entries (for the ddir), or when the
388                  * ddir is not enabled.
389                  */
390                 if (!parse_res[ddir].nr_entries ||
391                     (ddir == DDIR_READ && !td_read(td)) ||
392                     (ddir == DDIR_WRITE && !td_write(td))) {
393                         free(parse_res[ddir].entries);
394                         parse_res[ddir].entries = NULL;
395                         parse_res[ddir].nr_entries = 0;
396                         continue;
397                 }
398
399                 ret = init_cmdprio_values(&values[ddir],
400                                           parse_res[ddir].nr_entries, ts);
401                 if (ret)
402                         goto err;
403
404                 implicit_cmdprio = ioprio_value(options->class[ddir],
405                                                 options->level[ddir],
406                                                 options->hint[ddir]);
407
408                 ret = fio_cmdprio_generate_bsprio_desc(&cmdprio->bsprio_desc[ddir],
409                                                        &parse_res[ddir],
410                                                        &values[ddir],
411                                                        implicit_cmdprio);
412                 if (ret)
413                         goto err;
414
415                 free(parse_res[ddir].entries);
416                 parse_res[ddir].entries = NULL;
417                 parse_res[ddir].nr_entries = 0;
418
419                 ret = init_ts_clat_prio(ts, ddir, &values[ddir]);
420                 if (ret)
421                         goto err;
422
423                 free(values[ddir].prios);
424                 values[ddir].prios = NULL;
425                 values[ddir].nr_prios = 0;
426         }
427
428         return 0;
429
430 err:
431         for (ddir = 0; ddir < CMDPRIO_RWDIR_CNT; ddir++) {
432                 free(parse_res[ddir].entries);
433                 free(values[ddir].prios);
434         }
435         free_clat_prio_stats(ts);
436         fio_cmdprio_cleanup(cmdprio);
437
438         return ret;
439 }
440
441 static int fio_cmdprio_parse_and_gen(struct thread_data *td,
442                                      struct cmdprio *cmdprio)
443 {
444         struct cmdprio_options *options = cmdprio->options;
445         int i, ret;
446
447         /*
448          * If cmdprio_percentage/cmdprio_bssplit is set and cmdprio_class
449          * is not set, default to RT priority class.
450          */
451         for (i = 0; i < CMDPRIO_RWDIR_CNT; i++) {
452                 /*
453                  * A cmdprio value is only used when fio_cmdprio_percentage()
454                  * returns non-zero, so it is safe to set a class even for a
455                  * DDIR that will never use it.
456                  */
457                 if (!options->class[i])
458                         options->class[i] = IOPRIO_CLASS_RT;
459         }
460
461         switch (cmdprio->mode) {
462         case CMDPRIO_MODE_BSSPLIT:
463                 ret = fio_cmdprio_parse_and_gen_bssplit(td, cmdprio);
464                 break;
465         case CMDPRIO_MODE_PERC:
466                 ret = fio_cmdprio_gen_perc(td, cmdprio);
467                 break;
468         default:
469                 assert(0);
470                 return 1;
471         }
472
473         return ret;
474 }
475
476 void fio_cmdprio_cleanup(struct cmdprio *cmdprio)
477 {
478         enum fio_ddir ddir;
479         int i;
480
481         for (ddir = 0; ddir < CMDPRIO_RWDIR_CNT; ddir++) {
482                 for (i = 0; i < cmdprio->bsprio_desc[ddir].nr_bsprios; i++)
483                         free(cmdprio->bsprio_desc[ddir].bsprios[i].prios);
484                 free(cmdprio->bsprio_desc[ddir].bsprios);
485                 cmdprio->bsprio_desc[ddir].bsprios = NULL;
486                 cmdprio->bsprio_desc[ddir].nr_bsprios = 0;
487         }
488
489         /*
490          * options points to a cmdprio_options struct that is part of td->eo.
491          * td->eo itself will be freed by free_ioengine().
492          */
493         cmdprio->options = NULL;
494 }
495
496 int fio_cmdprio_init(struct thread_data *td, struct cmdprio *cmdprio,
497                      struct cmdprio_options *options)
498 {
499         struct thread_options *to = &td->o;
500         bool has_cmdprio_percentage = false;
501         bool has_cmdprio_bssplit = false;
502         int i;
503
504         cmdprio->options = options;
505
506         if (options->bssplit_str && strlen(options->bssplit_str))
507                 has_cmdprio_bssplit = true;
508
509         for (i = 0; i < CMDPRIO_RWDIR_CNT; i++) {
510                 if (options->percentage[i])
511                         has_cmdprio_percentage = true;
512         }
513
514         /*
515          * Check for option conflicts
516          */
517         if (has_cmdprio_percentage && has_cmdprio_bssplit) {
518                 log_err("%s: cmdprio_percentage and cmdprio_bssplit options "
519                         "are mutually exclusive\n",
520                         to->name);
521                 return 1;
522         }
523
524         if (has_cmdprio_bssplit)
525                 cmdprio->mode = CMDPRIO_MODE_BSSPLIT;
526         else if (has_cmdprio_percentage)
527                 cmdprio->mode = CMDPRIO_MODE_PERC;
528         else
529                 cmdprio->mode = CMDPRIO_MODE_NONE;
530
531         /* Nothing left to do if cmdprio is not used */
532         if (cmdprio->mode == CMDPRIO_MODE_NONE)
533                 return 0;
534
535         return fio_cmdprio_parse_and_gen(td, cmdprio);
536 }