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