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