Commit | Line | Data |
---|---|---|
d4afedfd JA |
1 | #include "../fio.h" |
2 | #include "../profile.h" | |
3 | #include "../parse.h" | |
d220c761 | 4 | #include "../optgroup.h" |
d4afedfd | 5 | |
00f81c37 JA |
6 | /* |
7 | * 1x loads | |
8 | */ | |
d4afedfd JA |
9 | #define R_LOAD 2000 |
10 | #define W_LOAD 1000 | |
11 | ||
12 | #define SAMPLE_SEC 3600 /* 1h checks */ | |
13 | ||
14 | struct act_pass_criteria { | |
15 | unsigned int max_usec; | |
16 | unsigned int max_perm; | |
17 | }; | |
18 | #define ACT_MAX_CRIT 3 | |
19 | ||
20 | static struct act_pass_criteria act_pass[ACT_MAX_CRIT] = { | |
21 | { | |
22 | .max_usec = 1000, | |
23 | .max_perm = 50, | |
24 | }, | |
25 | { | |
78a6aad7 | 26 | .max_usec = 8000, |
d4afedfd JA |
27 | .max_perm = 10, |
28 | }, | |
29 | { | |
30 | .max_usec = 64000, | |
31 | .max_perm = 1, | |
32 | }, | |
33 | }; | |
34 | ||
90777558 JA |
35 | struct act_slice { |
36 | uint64_t lat_buckets[ACT_MAX_CRIT]; | |
37 | uint64_t total_ios; | |
38 | }; | |
39 | ||
4a88752a JA |
40 | struct act_run_data { |
41 | struct fio_mutex *mutex; | |
42 | unsigned int pending; | |
43 | ||
90777558 JA |
44 | struct act_slice *slices; |
45 | unsigned int nr_slices; | |
4a88752a JA |
46 | }; |
47 | static struct act_run_data *act_run_data; | |
48 | ||
d4afedfd | 49 | struct act_prof_data { |
8b6a404c | 50 | struct timespec sample_tv; |
90777558 JA |
51 | struct act_slice *slices; |
52 | unsigned int cur_slice; | |
53 | unsigned int nr_slices; | |
d4afedfd JA |
54 | }; |
55 | ||
00f81c37 JA |
56 | #define ACT_MAX_OPTS 128 |
57 | static const char *act_opts[ACT_MAX_OPTS] = { | |
d4afedfd | 58 | "direct=1", |
00f81c37 | 59 | "ioengine=sync", |
d4afedfd | 60 | "random_generator=lfsr", |
00f81c37 | 61 | "group_reporting=1", |
4a88752a | 62 | "thread", |
d4afedfd JA |
63 | NULL, |
64 | }; | |
4a88752a | 65 | static unsigned int opt_idx = 5; |
d4afedfd JA |
66 | static unsigned int org_idx; |
67 | ||
00f81c37 | 68 | static int act_add_opt(const char *format, ...) __attribute__ ((__format__ (__printf__, 1, 2))); |
d4afedfd | 69 | |
7b504edd JA |
70 | struct act_options { |
71 | unsigned int pad; | |
72 | char *device_names; | |
73 | unsigned int load; | |
74 | unsigned int prep; | |
75 | unsigned int threads_per_queue; | |
76 | unsigned int num_read_blocks; | |
77 | unsigned int write_size; | |
78 | unsigned long long test_duration; | |
79 | }; | |
80 | ||
81 | static struct act_options act_options; | |
82 | ||
d4afedfd JA |
83 | static struct fio_option options[] = { |
84 | { | |
85 | .name = "device-names", | |
86 | .lname = "device-names", | |
87 | .type = FIO_OPT_STR_STORE, | |
7b504edd | 88 | .off1 = offsetof(struct act_options, device_names), |
d4afedfd JA |
89 | .help = "Devices to use", |
90 | .category = FIO_OPT_C_PROFILE, | |
91 | .group = FIO_OPT_G_ACT, | |
92 | }, | |
93 | { | |
94 | .name = "load", | |
95 | .lname = "Load multiplier", | |
96 | .type = FIO_OPT_INT, | |
7b504edd | 97 | .off1 = offsetof(struct act_options, load), |
d4afedfd | 98 | .help = "ACT load multipler (default 1x)", |
00f81c37 JA |
99 | .def = "1", |
100 | .category = FIO_OPT_C_PROFILE, | |
101 | .group = FIO_OPT_G_ACT, | |
102 | }, | |
90777558 JA |
103 | { |
104 | .name = "test-duration", | |
105 | .lname = "Test duration", | |
106 | .type = FIO_OPT_STR_VAL_TIME, | |
7b504edd | 107 | .off1 = offsetof(struct act_options, test_duration), |
90777558 JA |
108 | .help = "How long the entire test takes to run", |
109 | .def = "24h", | |
110 | .category = FIO_OPT_C_PROFILE, | |
111 | .group = FIO_OPT_G_ACT, | |
112 | }, | |
00f81c37 JA |
113 | { |
114 | .name = "threads-per-queue", | |
115 | .lname = "Number of read IO threads per device", | |
116 | .type = FIO_OPT_INT, | |
7b504edd | 117 | .off1 = offsetof(struct act_options, threads_per_queue), |
00f81c37 JA |
118 | .help = "Number of read IO threads per device", |
119 | .def = "8", | |
120 | .category = FIO_OPT_C_PROFILE, | |
121 | .group = FIO_OPT_G_ACT, | |
122 | }, | |
123 | { | |
124 | .name = "read-req-num-512-blocks", | |
4870138d | 125 | .lname = "Number of 512B blocks to read", |
00f81c37 | 126 | .type = FIO_OPT_INT, |
7b504edd | 127 | .off1 = offsetof(struct act_options, num_read_blocks), |
4870138d | 128 | .help = "Number of 512B blocks to read at the time", |
00f81c37 JA |
129 | .def = "3", |
130 | .category = FIO_OPT_C_PROFILE, | |
131 | .group = FIO_OPT_G_ACT, | |
132 | }, | |
133 | { | |
134 | .name = "large-block-op-kbytes", | |
4870138d | 135 | .lname = "Size of large block ops in KiB (writes)", |
00f81c37 | 136 | .type = FIO_OPT_INT, |
7b504edd | 137 | .off1 = offsetof(struct act_options, write_size), |
4870138d | 138 | .help = "Size of large block ops in KiB (writes)", |
2b9136a3 | 139 | .def = "131072", |
00f81c37 JA |
140 | .category = FIO_OPT_C_PROFILE, |
141 | .group = FIO_OPT_G_ACT, | |
142 | }, | |
143 | { | |
144 | .name = "prep", | |
145 | .lname = "Run ACT prep phase", | |
146 | .type = FIO_OPT_STR_SET, | |
7b504edd | 147 | .off1 = offsetof(struct act_options, prep), |
00f81c37 | 148 | .help = "Set to run ACT prep phase", |
d4afedfd JA |
149 | .category = FIO_OPT_C_PROFILE, |
150 | .group = FIO_OPT_G_ACT, | |
151 | }, | |
152 | { | |
153 | .name = NULL, | |
154 | }, | |
155 | }; | |
156 | ||
00f81c37 | 157 | static int act_add_opt(const char *str, ...) |
d4afedfd JA |
158 | { |
159 | char buffer[512]; | |
160 | va_list args; | |
161 | size_t len; | |
162 | ||
00f81c37 JA |
163 | if (opt_idx == ACT_MAX_OPTS) { |
164 | log_err("act: ACT_MAX_OPTS is too small\n"); | |
165 | return 1; | |
166 | } | |
167 | ||
d4afedfd JA |
168 | va_start(args, str); |
169 | len = vsnprintf(buffer, sizeof(buffer), str, args); | |
170 | va_end(args); | |
171 | ||
172 | if (len) | |
173 | act_opts[opt_idx++] = strdup(buffer); | |
00f81c37 JA |
174 | |
175 | return 0; | |
176 | } | |
177 | ||
178 | static int act_add_rw(const char *dev, int reads) | |
179 | { | |
dae887f3 JA |
180 | struct act_options *ao = &act_options; |
181 | ||
00f81c37 JA |
182 | if (act_add_opt("name=act-%s-%s", reads ? "read" : "write", dev)) |
183 | return 1; | |
184 | if (act_add_opt("filename=%s", dev)) | |
185 | return 1; | |
186 | if (act_add_opt("rw=%s", reads ? "randread" : "randwrite")) | |
187 | return 1; | |
188 | if (reads) { | |
dae887f3 | 189 | int rload = ao->load * R_LOAD / ao->threads_per_queue; |
00f81c37 | 190 | |
dae887f3 | 191 | if (act_add_opt("numjobs=%u", ao->threads_per_queue)) |
00f81c37 JA |
192 | return 1; |
193 | if (act_add_opt("rate_iops=%u", rload)) | |
194 | return 1; | |
dae887f3 | 195 | if (act_add_opt("bs=%u", ao->num_read_blocks * 512)) |
00f81c37 JA |
196 | return 1; |
197 | } else { | |
dae887f3 JA |
198 | const int rsize = ao->write_size / (ao->num_read_blocks * 512); |
199 | int wload = (ao->load * W_LOAD + rsize - 1) / rsize; | |
00f81c37 JA |
200 | |
201 | if (act_add_opt("rate_iops=%u", wload)) | |
202 | return 1; | |
dae887f3 | 203 | if (act_add_opt("bs=%u", ao->write_size)) |
00f81c37 JA |
204 | return 1; |
205 | } | |
206 | ||
207 | return 0; | |
208 | } | |
209 | ||
210 | static int act_add_dev_prep(const char *dev) | |
211 | { | |
212 | /* Add sequential zero phase */ | |
213 | if (act_add_opt("name=act-prep-zeroes-%s", dev)) | |
214 | return 1; | |
215 | if (act_add_opt("filename=%s", dev)) | |
216 | return 1; | |
2b9136a3 | 217 | if (act_add_opt("bs=1048576")) |
00f81c37 JA |
218 | return 1; |
219 | if (act_add_opt("zero_buffers")) | |
220 | return 1; | |
221 | if (act_add_opt("rw=write")) | |
222 | return 1; | |
223 | ||
224 | /* Randomly overwrite device */ | |
225 | if (act_add_opt("name=act-prep-salt-%s", dev)) | |
226 | return 1; | |
227 | if (act_add_opt("stonewall")) | |
228 | return 1; | |
229 | if (act_add_opt("filename=%s", dev)) | |
230 | return 1; | |
2b9136a3 | 231 | if (act_add_opt("bs=4096")) |
00f81c37 JA |
232 | return 1; |
233 | if (act_add_opt("ioengine=libaio")) | |
234 | return 1; | |
235 | if (act_add_opt("iodepth=64")) | |
236 | return 1; | |
237 | if (act_add_opt("rw=randwrite")) | |
238 | return 1; | |
239 | ||
240 | return 0; | |
d4afedfd JA |
241 | } |
242 | ||
00f81c37 | 243 | static int act_add_dev(const char *dev) |
d4afedfd | 244 | { |
dae887f3 | 245 | if (act_options.prep) |
00f81c37 JA |
246 | return act_add_dev_prep(dev); |
247 | ||
dae887f3 | 248 | if (act_add_opt("runtime=%llus", act_options.test_duration)) |
00f81c37 JA |
249 | return 1; |
250 | if (act_add_opt("time_based=1")) | |
251 | return 1; | |
252 | ||
253 | if (act_add_rw(dev, 1)) | |
254 | return 1; | |
255 | if (act_add_rw(dev, 0)) | |
256 | return 1; | |
257 | ||
258 | return 0; | |
d4afedfd JA |
259 | } |
260 | ||
261 | /* | |
262 | * Fill our private options into the command line | |
263 | */ | |
264 | static int act_prep_cmdline(void) | |
265 | { | |
dae887f3 | 266 | if (!act_options.device_names) { |
90777558 JA |
267 | log_err("act: you need to set IO target(s) with the " |
268 | "device-names option.\n"); | |
d4afedfd JA |
269 | return 1; |
270 | } | |
271 | ||
272 | org_idx = opt_idx; | |
d4afedfd JA |
273 | |
274 | do { | |
275 | char *dev; | |
276 | ||
dae887f3 | 277 | dev = strsep(&act_options.device_names, ","); |
d4afedfd JA |
278 | if (!dev) |
279 | break; | |
280 | ||
00f81c37 JA |
281 | if (act_add_dev(dev)) { |
282 | log_err("act: failed adding device to the mix\n"); | |
283 | break; | |
284 | } | |
d4afedfd JA |
285 | } while (1); |
286 | ||
287 | return 0; | |
288 | } | |
289 | ||
290 | static int act_io_u_lat(struct thread_data *td, uint64_t usec) | |
291 | { | |
292 | struct act_prof_data *apd = td->prof_data; | |
90777558 | 293 | struct act_slice *slice; |
d4afedfd JA |
294 | int i, ret = 0; |
295 | double perm; | |
296 | ||
dae887f3 | 297 | if (act_options.prep) |
00f81c37 JA |
298 | return 0; |
299 | ||
90777558 JA |
300 | /* |
301 | * Really should not happen, but lets not let jitter at the end | |
302 | * ruin our day. | |
303 | */ | |
304 | if (apd->cur_slice >= apd->nr_slices) | |
305 | return 0; | |
306 | ||
307 | slice = &apd->slices[apd->cur_slice]; | |
308 | slice->total_ios++; | |
d4afedfd | 309 | |
78a6aad7 JA |
310 | for (i = ACT_MAX_CRIT - 1; i >= 0; i--) { |
311 | if (usec > act_pass[i].max_usec) { | |
90777558 | 312 | slice->lat_buckets[i]++; |
d4afedfd JA |
313 | break; |
314 | } | |
315 | } | |
316 | ||
d4afedfd JA |
317 | if (time_since_now(&apd->sample_tv) < SAMPLE_SEC) |
318 | return 0; | |
319 | ||
320 | /* SAMPLE_SEC has passed, check criteria for pass */ | |
321 | for (i = 0; i < ACT_MAX_CRIT; i++) { | |
90777558 | 322 | perm = (1000.0 * slice->lat_buckets[i]) / slice->total_ios; |
78a6aad7 | 323 | if (perm < act_pass[i].max_perm) |
d4afedfd JA |
324 | continue; |
325 | ||
326 | log_err("act: %f%% exceeds pass criteria of %f%%\n", perm / 10.0, (double) act_pass[i].max_perm / 10.0); | |
327 | ret = 1; | |
328 | break; | |
329 | } | |
330 | ||
331 | fio_gettime(&apd->sample_tv, NULL); | |
90777558 | 332 | apd->cur_slice++; |
d4afedfd JA |
333 | return ret; |
334 | } | |
335 | ||
4a88752a JA |
336 | static void get_act_ref(void) |
337 | { | |
338 | fio_mutex_down(act_run_data->mutex); | |
339 | act_run_data->pending++; | |
340 | fio_mutex_up(act_run_data->mutex); | |
341 | } | |
342 | ||
90777558 | 343 | static int show_slice(struct act_slice *slice, unsigned int slice_num) |
4a88752a | 344 | { |
90777558 | 345 | unsigned int i, failed = 0; |
4a88752a | 346 | |
90777558 | 347 | log_info(" %2u", slice_num); |
4a88752a JA |
348 | |
349 | for (i = 0; i < ACT_MAX_CRIT; i++) { | |
90777558 | 350 | double perc = 0.0; |
4a88752a | 351 | |
90777558 JA |
352 | if (slice->total_ios) |
353 | perc = 100.0 * (double) slice->lat_buckets[i] / (double) slice->total_ios; | |
354 | if ((perc * 10.0) >= act_pass[i].max_perm) | |
355 | failed++; | |
4a88752a JA |
356 | log_info("\t%2.2f", perc); |
357 | } | |
358 | for (i = 0; i < ACT_MAX_CRIT; i++) { | |
90777558 | 359 | double perc = 0.0; |
4a88752a | 360 | |
90777558 JA |
361 | if (slice->total_ios) |
362 | perc = 100.0 * (double) slice->lat_buckets[i] / (double) slice->total_ios; | |
4a88752a JA |
363 | log_info("\t%2.2f", perc); |
364 | } | |
90777558 JA |
365 | log_info("\n"); |
366 | ||
367 | return failed; | |
368 | } | |
369 | ||
370 | static void act_show_all_stats(void) | |
371 | { | |
372 | unsigned int i, fails = 0; | |
373 | ||
374 | log_info(" trans device\n"); | |
375 | log_info(" %%>(ms) %%>(ms)\n"); | |
376 | log_info(" slice"); | |
377 | ||
378 | for (i = 0; i < ACT_MAX_CRIT; i++) | |
379 | log_info("\t %2u", act_pass[i].max_usec / 1000); | |
380 | for (i = 0; i < ACT_MAX_CRIT; i++) | |
381 | log_info("\t %2u", act_pass[i].max_usec / 1000); | |
382 | ||
383 | log_info("\n"); | |
384 | log_info(" ----- ----- ----- ------ ----- ----- ------\n"); | |
385 | ||
386 | for (i = 0; i < act_run_data->nr_slices; i++) | |
387 | fails += show_slice(&act_run_data->slices[i], i + 1); | |
4a88752a | 388 | |
90777558 | 389 | log_info("\nact: test complete, device(s): %s\n", fails ? "FAILED" : "PASSED"); |
4a88752a JA |
390 | } |
391 | ||
392 | static void put_act_ref(struct thread_data *td) | |
393 | { | |
394 | struct act_prof_data *apd = td->prof_data; | |
90777558 | 395 | unsigned int i, slice; |
4a88752a JA |
396 | |
397 | fio_mutex_down(act_run_data->mutex); | |
398 | ||
90777558 | 399 | if (!act_run_data->slices) { |
572cfb3f | 400 | act_run_data->slices = calloc(apd->nr_slices, sizeof(struct act_slice)); |
90777558 | 401 | act_run_data->nr_slices = apd->nr_slices; |
4a88752a JA |
402 | } |
403 | ||
90777558 JA |
404 | for (slice = 0; slice < apd->nr_slices; slice++) { |
405 | struct act_slice *dst = &act_run_data->slices[slice]; | |
406 | struct act_slice *src = &apd->slices[slice]; | |
407 | ||
408 | dst->total_ios += src->total_ios; | |
409 | ||
410 | for (i = 0; i < ACT_MAX_CRIT; i++) | |
411 | dst->lat_buckets[i] += src->lat_buckets[i]; | |
412 | } | |
4a88752a JA |
413 | |
414 | if (!--act_run_data->pending) | |
415 | act_show_all_stats(); | |
416 | ||
417 | fio_mutex_up(act_run_data->mutex); | |
418 | } | |
419 | ||
d4afedfd JA |
420 | static int act_td_init(struct thread_data *td) |
421 | { | |
422 | struct act_prof_data *apd; | |
90777558 | 423 | unsigned int nr_slices; |
d4afedfd | 424 | |
4a88752a JA |
425 | get_act_ref(); |
426 | ||
572cfb3f | 427 | apd = calloc(1, sizeof(*apd)); |
dae887f3 | 428 | nr_slices = (act_options.test_duration + SAMPLE_SEC - 1) / SAMPLE_SEC; |
572cfb3f | 429 | apd->slices = calloc(nr_slices, sizeof(struct act_slice)); |
90777558 | 430 | apd->nr_slices = nr_slices; |
d4afedfd JA |
431 | fio_gettime(&apd->sample_tv, NULL); |
432 | td->prof_data = apd; | |
433 | return 0; | |
434 | } | |
435 | ||
436 | static void act_td_exit(struct thread_data *td) | |
437 | { | |
90777558 JA |
438 | struct act_prof_data *apd = td->prof_data; |
439 | ||
4a88752a | 440 | put_act_ref(td); |
90777558 JA |
441 | free(apd->slices); |
442 | free(apd); | |
d4afedfd JA |
443 | td->prof_data = NULL; |
444 | } | |
445 | ||
446 | static struct prof_io_ops act_io_ops = { | |
447 | .td_init = act_td_init, | |
448 | .td_exit = act_td_exit, | |
449 | .io_u_lat = act_io_u_lat, | |
450 | }; | |
451 | ||
452 | static struct profile_ops act_profile = { | |
453 | .name = "act", | |
454 | .desc = "ACT Aerospike like benchmark", | |
455 | .options = options, | |
7b504edd | 456 | .opt_data = &act_options, |
d4afedfd JA |
457 | .prep_cmd = act_prep_cmdline, |
458 | .cmdline = act_opts, | |
459 | .io_ops = &act_io_ops, | |
460 | }; | |
461 | ||
462 | static void fio_init act_register(void) | |
463 | { | |
572cfb3f | 464 | act_run_data = calloc(1, sizeof(*act_run_data)); |
4a88752a JA |
465 | act_run_data->mutex = fio_mutex_init(FIO_MUTEX_UNLOCKED); |
466 | ||
d4afedfd JA |
467 | if (register_profile(&act_profile)) |
468 | log_err("fio: failed to register profile 'act'\n"); | |
469 | } | |
470 | ||
471 | static void fio_exit act_unregister(void) | |
472 | { | |
473 | while (org_idx && org_idx < opt_idx) | |
474 | free((void *) act_opts[++org_idx]); | |
475 | ||
476 | unregister_profile(&act_profile); | |
4a88752a | 477 | fio_mutex_remove(act_run_data->mutex); |
90777558 | 478 | free(act_run_data->slices); |
4a88752a JA |
479 | free(act_run_data); |
480 | act_run_data = NULL; | |
d4afedfd | 481 | } |