options.c revision 79d16311c8d0c7188d73df77838fb1b4b6ff58db
1#include <stdio.h> 2#include <stdlib.h> 3#include <unistd.h> 4#include <ctype.h> 5#include <string.h> 6#include <getopt.h> 7#include <assert.h> 8#include <libgen.h> 9#include <fcntl.h> 10#include <sys/types.h> 11#include <sys/stat.h> 12 13#include "fio.h" 14#include "verify.h" 15#include "parse.h" 16#include "lib/fls.h" 17#include "options.h" 18 19static FLIST_HEAD(ext_opt_list); 20 21/* 22 * Check if mmap/mmaphuge has a :/foo/bar/file at the end. If so, return that. 23 */ 24static char *get_opt_postfix(const char *str) 25{ 26 char *p = strstr(str, ":"); 27 28 if (!p) 29 return NULL; 30 31 p++; 32 strip_blank_front(&p); 33 strip_blank_end(p); 34 return strdup(p); 35} 36 37static int converthexchartoint(char a) 38{ 39 int base; 40 41 switch(a) { 42 case '0'...'9': 43 base = '0'; 44 break; 45 case 'A'...'F': 46 base = 'A' - 10; 47 break; 48 case 'a'...'f': 49 base = 'a' - 10; 50 break; 51 default: 52 base = 0; 53 } 54 return (a - base); 55} 56 57static int bs_cmp(const void *p1, const void *p2) 58{ 59 const struct bssplit *bsp1 = p1; 60 const struct bssplit *bsp2 = p2; 61 62 return bsp1->perc < bsp2->perc; 63} 64 65static int bssplit_ddir(struct thread_data *td, int ddir, char *str) 66{ 67 struct bssplit *bssplit; 68 unsigned int i, perc, perc_missing; 69 unsigned int max_bs, min_bs; 70 long long val; 71 char *fname; 72 73 td->o.bssplit_nr[ddir] = 4; 74 bssplit = malloc(4 * sizeof(struct bssplit)); 75 76 i = 0; 77 max_bs = 0; 78 min_bs = -1; 79 while ((fname = strsep(&str, ":")) != NULL) { 80 char *perc_str; 81 82 if (!strlen(fname)) 83 break; 84 85 /* 86 * grow struct buffer, if needed 87 */ 88 if (i == td->o.bssplit_nr[ddir]) { 89 td->o.bssplit_nr[ddir] <<= 1; 90 bssplit = realloc(bssplit, td->o.bssplit_nr[ddir] 91 * sizeof(struct bssplit)); 92 } 93 94 perc_str = strstr(fname, "/"); 95 if (perc_str) { 96 *perc_str = '\0'; 97 perc_str++; 98 perc = atoi(perc_str); 99 if (perc > 100) 100 perc = 100; 101 else if (!perc) 102 perc = -1; 103 } else 104 perc = -1; 105 106 if (str_to_decimal(fname, &val, 1, td)) { 107 log_err("fio: bssplit conversion failed\n"); 108 free(td->o.bssplit); 109 return 1; 110 } 111 112 if (val > max_bs) 113 max_bs = val; 114 if (val < min_bs) 115 min_bs = val; 116 117 bssplit[i].bs = val; 118 bssplit[i].perc = perc; 119 i++; 120 } 121 122 td->o.bssplit_nr[ddir] = i; 123 124 /* 125 * Now check if the percentages add up, and how much is missing 126 */ 127 perc = perc_missing = 0; 128 for (i = 0; i < td->o.bssplit_nr[ddir]; i++) { 129 struct bssplit *bsp = &bssplit[i]; 130 131 if (bsp->perc == (unsigned char) -1) 132 perc_missing++; 133 else 134 perc += bsp->perc; 135 } 136 137 if (perc > 100) { 138 log_err("fio: bssplit percentages add to more than 100%%\n"); 139 free(bssplit); 140 return 1; 141 } 142 /* 143 * If values didn't have a percentage set, divide the remains between 144 * them. 145 */ 146 if (perc_missing) { 147 for (i = 0; i < td->o.bssplit_nr[ddir]; i++) { 148 struct bssplit *bsp = &bssplit[i]; 149 150 if (bsp->perc == (unsigned char) -1) 151 bsp->perc = (100 - perc) / perc_missing; 152 } 153 } 154 155 td->o.min_bs[ddir] = min_bs; 156 td->o.max_bs[ddir] = max_bs; 157 158 /* 159 * now sort based on percentages, for ease of lookup 160 */ 161 qsort(bssplit, td->o.bssplit_nr[ddir], sizeof(struct bssplit), bs_cmp); 162 td->o.bssplit[ddir] = bssplit; 163 return 0; 164 165} 166 167static int str_bssplit_cb(void *data, const char *input) 168{ 169 struct thread_data *td = data; 170 char *str, *p, *odir; 171 int ret = 0; 172 173 p = str = strdup(input); 174 175 strip_blank_front(&str); 176 strip_blank_end(str); 177 178 odir = strchr(str, ','); 179 if (odir) { 180 ret = bssplit_ddir(td, DDIR_WRITE, odir + 1); 181 if (!ret) { 182 *odir = '\0'; 183 ret = bssplit_ddir(td, DDIR_READ, str); 184 } 185 } else { 186 char *op; 187 188 op = strdup(str); 189 190 ret = bssplit_ddir(td, DDIR_READ, str); 191 if (!ret) 192 ret = bssplit_ddir(td, DDIR_WRITE, op); 193 194 free(op); 195 } 196 197 free(p); 198 return ret; 199} 200 201static int str_rw_cb(void *data, const char *str) 202{ 203 struct thread_data *td = data; 204 char *nr = get_opt_postfix(str); 205 206 td->o.ddir_nr = 1; 207 if (nr) { 208 td->o.ddir_nr = atoi(nr); 209 free(nr); 210 } 211 212 return 0; 213} 214 215static int str_mem_cb(void *data, const char *mem) 216{ 217 struct thread_data *td = data; 218 219 if (td->o.mem_type == MEM_MMAPHUGE || td->o.mem_type == MEM_MMAP) { 220 td->mmapfile = get_opt_postfix(mem); 221 if (td->o.mem_type == MEM_MMAPHUGE && !td->mmapfile) { 222 log_err("fio: mmaphuge:/path/to/file\n"); 223 return 1; 224 } 225 } 226 227 return 0; 228} 229 230static int str_lockmem_cb(void fio_unused *data, unsigned long *val) 231{ 232 mlock_size = *val; 233 return 0; 234} 235 236static int str_rwmix_read_cb(void *data, unsigned int *val) 237{ 238 struct thread_data *td = data; 239 240 td->o.rwmix[DDIR_READ] = *val; 241 td->o.rwmix[DDIR_WRITE] = 100 - *val; 242 return 0; 243} 244 245static int str_rwmix_write_cb(void *data, unsigned int *val) 246{ 247 struct thread_data *td = data; 248 249 td->o.rwmix[DDIR_WRITE] = *val; 250 td->o.rwmix[DDIR_READ] = 100 - *val; 251 return 0; 252} 253 254#ifdef FIO_HAVE_IOPRIO 255static int str_prioclass_cb(void *data, unsigned int *val) 256{ 257 struct thread_data *td = data; 258 unsigned short mask; 259 260 /* 261 * mask off old class bits, str_prio_cb() may have set a default class 262 */ 263 mask = (1 << IOPRIO_CLASS_SHIFT) - 1; 264 td->ioprio &= mask; 265 266 td->ioprio |= *val << IOPRIO_CLASS_SHIFT; 267 td->ioprio_set = 1; 268 return 0; 269} 270 271static int str_prio_cb(void *data, unsigned int *val) 272{ 273 struct thread_data *td = data; 274 275 td->ioprio |= *val; 276 277 /* 278 * If no class is set, assume BE 279 */ 280 if ((td->ioprio >> IOPRIO_CLASS_SHIFT) == 0) 281 td->ioprio |= IOPRIO_CLASS_BE << IOPRIO_CLASS_SHIFT; 282 283 td->ioprio_set = 1; 284 return 0; 285} 286#endif 287 288static int str_exitall_cb(void) 289{ 290 exitall_on_terminate = 1; 291 return 0; 292} 293 294#ifdef FIO_HAVE_CPU_AFFINITY 295static int str_cpumask_cb(void *data, unsigned int *val) 296{ 297 struct thread_data *td = data; 298 unsigned int i; 299 long max_cpu; 300 int ret; 301 302 ret = fio_cpuset_init(&td->o.cpumask); 303 if (ret < 0) { 304 log_err("fio: cpuset_init failed\n"); 305 td_verror(td, ret, "fio_cpuset_init"); 306 return 1; 307 } 308 309 max_cpu = sysconf(_SC_NPROCESSORS_ONLN); 310 311 for (i = 0; i < sizeof(int) * 8; i++) { 312 if ((1 << i) & *val) { 313 if (i > max_cpu) { 314 log_err("fio: CPU %d too large (max=%ld)\n", i, 315 max_cpu); 316 return 1; 317 } 318 dprint(FD_PARSE, "set cpu allowed %d\n", i); 319 fio_cpu_set(&td->o.cpumask, i); 320 } 321 } 322 323 td->o.cpumask_set = 1; 324 return 0; 325} 326 327static int set_cpus_allowed(struct thread_data *td, os_cpu_mask_t *mask, 328 const char *input) 329{ 330 char *cpu, *str, *p; 331 long max_cpu; 332 int ret = 0; 333 334 ret = fio_cpuset_init(mask); 335 if (ret < 0) { 336 log_err("fio: cpuset_init failed\n"); 337 td_verror(td, ret, "fio_cpuset_init"); 338 return 1; 339 } 340 341 p = str = strdup(input); 342 343 strip_blank_front(&str); 344 strip_blank_end(str); 345 346 max_cpu = sysconf(_SC_NPROCESSORS_ONLN); 347 348 while ((cpu = strsep(&str, ",")) != NULL) { 349 char *str2, *cpu2; 350 int icpu, icpu2; 351 352 if (!strlen(cpu)) 353 break; 354 355 str2 = cpu; 356 icpu2 = -1; 357 while ((cpu2 = strsep(&str2, "-")) != NULL) { 358 if (!strlen(cpu2)) 359 break; 360 361 icpu2 = atoi(cpu2); 362 } 363 364 icpu = atoi(cpu); 365 if (icpu2 == -1) 366 icpu2 = icpu; 367 while (icpu <= icpu2) { 368 if (icpu >= FIO_MAX_CPUS) { 369 log_err("fio: your OS only supports up to" 370 " %d CPUs\n", (int) FIO_MAX_CPUS); 371 ret = 1; 372 break; 373 } 374 if (icpu > max_cpu) { 375 log_err("fio: CPU %d too large (max=%ld)\n", 376 icpu, max_cpu); 377 ret = 1; 378 break; 379 } 380 381 dprint(FD_PARSE, "set cpu allowed %d\n", icpu); 382 fio_cpu_set(mask, icpu); 383 icpu++; 384 } 385 if (ret) 386 break; 387 } 388 389 free(p); 390 if (!ret) 391 td->o.cpumask_set = 1; 392 return ret; 393} 394 395static int str_cpus_allowed_cb(void *data, const char *input) 396{ 397 struct thread_data *td = data; 398 int ret; 399 400 ret = set_cpus_allowed(td, &td->o.cpumask, input); 401 if (!ret) 402 td->o.cpumask_set = 1; 403 404 return ret; 405} 406 407static int str_verify_cpus_allowed_cb(void *data, const char *input) 408{ 409 struct thread_data *td = data; 410 int ret; 411 412 ret = set_cpus_allowed(td, &td->o.verify_cpumask, input); 413 if (!ret) 414 td->o.verify_cpumask_set = 1; 415 416 return ret; 417} 418#endif 419 420static int str_fst_cb(void *data, const char *str) 421{ 422 struct thread_data *td = data; 423 char *nr = get_opt_postfix(str); 424 425 td->file_service_nr = 1; 426 if (nr) { 427 td->file_service_nr = atoi(nr); 428 free(nr); 429 } 430 431 return 0; 432} 433 434static int check_dir(struct thread_data *td, char *fname) 435{ 436 char file[PATH_MAX], *dir; 437 int elen = 0; 438 439 if (td->o.directory) { 440 strcpy(file, td->o.directory); 441 strcat(file, "/"); 442 elen = strlen(file); 443 } 444 445 sprintf(file + elen, "%s", fname); 446 dir = dirname(file); 447 448#if 0 449 { 450 struct stat sb; 451 /* 452 * We can't do this on FIO_DISKLESSIO engines. The engine isn't loaded 453 * yet, so we can't do this check right here... 454 */ 455 if (lstat(dir, &sb) < 0) { 456 int ret = errno; 457 458 log_err("fio: %s is not a directory\n", dir); 459 td_verror(td, ret, "lstat"); 460 return 1; 461 } 462 463 if (!S_ISDIR(sb.st_mode)) { 464 log_err("fio: %s is not a directory\n", dir); 465 return 1; 466 } 467 } 468#endif 469 470 return 0; 471} 472 473/* 474 * Return next file in the string. Files are separated with ':'. If the ':' 475 * is escaped with a '\', then that ':' is part of the filename and does not 476 * indicate a new file. 477 */ 478static char *get_next_file_name(char **ptr) 479{ 480 char *str = *ptr; 481 char *p, *start; 482 483 if (!str || !strlen(str)) 484 return NULL; 485 486 start = str; 487 do { 488 /* 489 * No colon, we are done 490 */ 491 p = strchr(str, ':'); 492 if (!p) { 493 *ptr = NULL; 494 break; 495 } 496 497 /* 498 * We got a colon, but it's the first character. Skip and 499 * continue 500 */ 501 if (p == start) { 502 str = ++start; 503 continue; 504 } 505 506 if (*(p - 1) != '\\') { 507 *p = '\0'; 508 *ptr = p + 1; 509 break; 510 } 511 512 memmove(p - 1, p, strlen(p) + 1); 513 str = p; 514 } while (1); 515 516 return start; 517} 518 519static int str_filename_cb(void *data, const char *input) 520{ 521 struct thread_data *td = data; 522 char *fname, *str, *p; 523 524 p = str = strdup(input); 525 526 strip_blank_front(&str); 527 strip_blank_end(str); 528 529 if (!td->files_index) 530 td->o.nr_files = 0; 531 532 while ((fname = get_next_file_name(&str)) != NULL) { 533 if (!strlen(fname)) 534 break; 535 if (check_dir(td, fname)) { 536 free(p); 537 return 1; 538 } 539 add_file(td, fname); 540 td->o.nr_files++; 541 } 542 543 free(p); 544 return 0; 545} 546 547static int str_directory_cb(void *data, const char fio_unused *str) 548{ 549 struct thread_data *td = data; 550 struct stat sb; 551 552 if (lstat(td->o.directory, &sb) < 0) { 553 int ret = errno; 554 555 log_err("fio: %s is not a directory\n", td->o.directory); 556 td_verror(td, ret, "lstat"); 557 return 1; 558 } 559 if (!S_ISDIR(sb.st_mode)) { 560 log_err("fio: %s is not a directory\n", td->o.directory); 561 return 1; 562 } 563 564 return 0; 565} 566 567static int str_opendir_cb(void *data, const char fio_unused *str) 568{ 569 struct thread_data *td = data; 570 571 if (!td->files_index) 572 td->o.nr_files = 0; 573 574 return add_dir_files(td, td->o.opendir); 575} 576 577static int str_verify_offset_cb(void *data, unsigned int *off) 578{ 579 struct thread_data *td = data; 580 581 if (*off && *off < sizeof(struct verify_header)) { 582 log_err("fio: verify_offset too small\n"); 583 return 1; 584 } 585 586 td->o.verify_offset = *off; 587 return 0; 588} 589 590static int str_verify_pattern_cb(void *data, const char *input) 591{ 592 struct thread_data *td = data; 593 long off; 594 int i = 0, j = 0, len, k, base = 10; 595 char* loc1, * loc2; 596 597 loc1 = strstr(input, "0x"); 598 loc2 = strstr(input, "0X"); 599 if (loc1 || loc2) 600 base = 16; 601 off = strtol(input, NULL, base); 602 if (off != LONG_MAX || errno != ERANGE) { 603 while (off) { 604 td->o.verify_pattern[i] = off & 0xff; 605 off >>= 8; 606 i++; 607 } 608 } else { 609 len = strlen(input); 610 k = len - 1; 611 if (base == 16) { 612 if (loc1) 613 j = loc1 - input + 2; 614 else 615 j = loc2 - input + 2; 616 } else 617 return 1; 618 if (len - j < MAX_PATTERN_SIZE * 2) { 619 while (k >= j) { 620 off = converthexchartoint(input[k--]); 621 if (k >= j) 622 off += (converthexchartoint(input[k--]) 623 * 16); 624 td->o.verify_pattern[i++] = (char) off; 625 } 626 } 627 } 628 td->o.verify_pattern_bytes = i; 629 return 0; 630} 631 632static int str_lockfile_cb(void *data, const char *str) 633{ 634 struct thread_data *td = data; 635 char *nr = get_opt_postfix(str); 636 637 td->o.lockfile_batch = 1; 638 if (nr) { 639 td->o.lockfile_batch = atoi(nr); 640 free(nr); 641 } 642 643 return 0; 644} 645 646static int str_write_bw_log_cb(void *data, const char *str) 647{ 648 struct thread_data *td = data; 649 650 if (str) 651 td->o.bw_log_file = strdup(str); 652 653 td->o.write_bw_log = 1; 654 return 0; 655} 656 657static int str_write_lat_log_cb(void *data, const char *str) 658{ 659 struct thread_data *td = data; 660 661 if (str) 662 td->o.lat_log_file = strdup(str); 663 664 td->o.write_lat_log = 1; 665 return 0; 666} 667 668static int str_gtod_reduce_cb(void *data, int *il) 669{ 670 struct thread_data *td = data; 671 int val = *il; 672 673 td->o.disable_clat = !!val; 674 td->o.disable_slat = !!val; 675 td->o.disable_bw = !!val; 676 if (val) 677 td->tv_cache_mask = 63; 678 679 return 0; 680} 681 682static int str_gtod_cpu_cb(void *data, int *il) 683{ 684 struct thread_data *td = data; 685 int val = *il; 686 687 td->o.gtod_cpu = val; 688 td->o.gtod_offload = 1; 689 return 0; 690} 691 692static int rw_verify(struct fio_option *o, void *data) 693{ 694 struct thread_data *td = data; 695 696 if (read_only && td_write(td)) { 697 log_err("fio: job <%s> has write bit set, but fio is in" 698 " read-only mode\n", td->o.name); 699 return 1; 700 } 701 702 return 0; 703} 704 705static int gtod_cpu_verify(struct fio_option *o, void *data) 706{ 707#ifndef FIO_HAVE_CPU_AFFINITY 708 struct thread_data *td = data; 709 710 if (td->o.gtod_cpu) { 711 log_err("fio: platform must support CPU affinity for" 712 "gettimeofday() offloading\n"); 713 return 1; 714 } 715#endif 716 717 return 0; 718} 719 720static int kb_base_verify(struct fio_option *o, void *data) 721{ 722 struct thread_data *td = data; 723 724 if (td->o.kb_base != 1024 && td->o.kb_base != 1000) { 725 log_err("fio: kb_base set to nonsensical value: %u\n", 726 td->o.kb_base); 727 return 1; 728 } 729 730 return 0; 731} 732 733#define __stringify_1(x) #x 734#define __stringify(x) __stringify_1(x) 735 736/* 737 * Map of job/command line options 738 */ 739static struct fio_option options[] = { 740 { 741 .name = "description", 742 .type = FIO_OPT_STR_STORE, 743 .off1 = td_var_offset(description), 744 .help = "Text job description", 745 }, 746 { 747 .name = "name", 748 .type = FIO_OPT_STR_STORE, 749 .off1 = td_var_offset(name), 750 .help = "Name of this job", 751 }, 752 { 753 .name = "directory", 754 .type = FIO_OPT_STR_STORE, 755 .off1 = td_var_offset(directory), 756 .cb = str_directory_cb, 757 .help = "Directory to store files in", 758 }, 759 { 760 .name = "filename", 761 .type = FIO_OPT_STR_STORE, 762 .off1 = td_var_offset(filename), 763 .cb = str_filename_cb, 764 .prio = -1, /* must come after "directory" */ 765 .help = "File(s) to use for the workload", 766 }, 767 { 768 .name = "kb_base", 769 .type = FIO_OPT_INT, 770 .off1 = td_var_offset(kb_base), 771 .verify = kb_base_verify, 772 .prio = 1, 773 .def = "1024", 774 .help = "How many bytes per KB for reporting (1000 or 1024)", 775 }, 776 { 777 .name = "lockfile", 778 .type = FIO_OPT_STR, 779 .cb = str_lockfile_cb, 780 .off1 = td_var_offset(file_lock_mode), 781 .help = "Lock file when doing IO to it", 782 .parent = "filename", 783 .def = "none", 784 .posval = { 785 { .ival = "none", 786 .oval = FILE_LOCK_NONE, 787 .help = "No file locking", 788 }, 789 { .ival = "exclusive", 790 .oval = FILE_LOCK_EXCLUSIVE, 791 .help = "Exclusive file lock", 792 }, 793 { 794 .ival = "readwrite", 795 .oval = FILE_LOCK_READWRITE, 796 .help = "Read vs write lock", 797 }, 798 }, 799 }, 800 { 801 .name = "opendir", 802 .type = FIO_OPT_STR_STORE, 803 .off1 = td_var_offset(opendir), 804 .cb = str_opendir_cb, 805 .help = "Recursively add files from this directory and down", 806 }, 807 { 808 .name = "rw", 809 .alias = "readwrite", 810 .type = FIO_OPT_STR, 811 .cb = str_rw_cb, 812 .off1 = td_var_offset(td_ddir), 813 .help = "IO direction", 814 .def = "read", 815 .verify = rw_verify, 816 .posval = { 817 { .ival = "read", 818 .oval = TD_DDIR_READ, 819 .help = "Sequential read", 820 }, 821 { .ival = "write", 822 .oval = TD_DDIR_WRITE, 823 .help = "Sequential write", 824 }, 825 { .ival = "randread", 826 .oval = TD_DDIR_RANDREAD, 827 .help = "Random read", 828 }, 829 { .ival = "randwrite", 830 .oval = TD_DDIR_RANDWRITE, 831 .help = "Random write", 832 }, 833 { .ival = "rw", 834 .oval = TD_DDIR_RW, 835 .help = "Sequential read and write mix", 836 }, 837 { .ival = "randrw", 838 .oval = TD_DDIR_RANDRW, 839 .help = "Random read and write mix" 840 }, 841 }, 842 }, 843 { 844 .name = "ioengine", 845 .type = FIO_OPT_STR_STORE, 846 .off1 = td_var_offset(ioengine), 847 .help = "IO engine to use", 848 .def = "sync", 849 .posval = { 850 { .ival = "sync", 851 .help = "Use read/write", 852 }, 853 { .ival = "psync", 854 .help = "Use pread/pwrite", 855 }, 856 { .ival = "vsync", 857 .help = "Use readv/writev", 858 }, 859#ifdef FIO_HAVE_LIBAIO 860 { .ival = "libaio", 861 .help = "Linux native asynchronous IO", 862 }, 863#endif 864#ifdef FIO_HAVE_POSIXAIO 865 { .ival = "posixaio", 866 .help = "POSIX asynchronous IO", 867 }, 868#endif 869#ifdef FIO_HAVE_SOLARISAIO 870 { .ival = "solarisaio", 871 .help = "Solaris native asynchronous IO", 872 }, 873#endif 874 { .ival = "mmap", 875 .help = "Memory mapped IO", 876 }, 877#ifdef FIO_HAVE_SPLICE 878 { .ival = "splice", 879 .help = "splice/vmsplice based IO", 880 }, 881 { .ival = "netsplice", 882 .help = "splice/vmsplice to/from the network", 883 }, 884#endif 885#ifdef FIO_HAVE_SGIO 886 { .ival = "sg", 887 .help = "SCSI generic v3 IO", 888 }, 889#endif 890 { .ival = "null", 891 .help = "Testing engine (no data transfer)", 892 }, 893 { .ival = "net", 894 .help = "Network IO", 895 }, 896#ifdef FIO_HAVE_SYSLET 897 { .ival = "syslet-rw", 898 .help = "syslet enabled async pread/pwrite IO", 899 }, 900#endif 901 { .ival = "cpuio", 902 .help = "CPU cycler burner engine", 903 }, 904#ifdef FIO_HAVE_GUASI 905 { .ival = "guasi", 906 .help = "GUASI IO engine", 907 }, 908#endif 909 { .ival = "external", 910 .help = "Load external engine (append name)", 911 }, 912 }, 913 }, 914 { 915 .name = "iodepth", 916 .type = FIO_OPT_INT, 917 .off1 = td_var_offset(iodepth), 918 .help = "Amount of IO buffers to keep in flight", 919 .minval = 1, 920 .def = "1", 921 }, 922 { 923 .name = "iodepth_batch", 924 .alias = "iodepth_batch_submit", 925 .type = FIO_OPT_INT, 926 .off1 = td_var_offset(iodepth_batch), 927 .help = "Number of IO buffers to submit in one go", 928 .parent = "iodepth", 929 .minval = 1, 930 .def = "1", 931 }, 932 { 933 .name = "iodepth_batch_complete", 934 .type = FIO_OPT_INT, 935 .off1 = td_var_offset(iodepth_batch_complete), 936 .help = "Number of IO buffers to retrieve in one go", 937 .parent = "iodepth", 938 .minval = 0, 939 .def = "1", 940 }, 941 { 942 .name = "iodepth_low", 943 .type = FIO_OPT_INT, 944 .off1 = td_var_offset(iodepth_low), 945 .help = "Low water mark for queuing depth", 946 .parent = "iodepth", 947 }, 948 { 949 .name = "size", 950 .type = FIO_OPT_STR_VAL, 951 .off1 = td_var_offset(size), 952 .minval = 1, 953 .help = "Total size of device or files", 954 }, 955 { 956 .name = "fill_device", 957 .type = FIO_OPT_BOOL, 958 .off1 = td_var_offset(fill_device), 959 .help = "Write until an ENOSPC error occurs", 960 .def = "0", 961 }, 962 { 963 .name = "filesize", 964 .type = FIO_OPT_STR_VAL, 965 .off1 = td_var_offset(file_size_low), 966 .off2 = td_var_offset(file_size_high), 967 .minval = 1, 968 .help = "Size of individual files", 969 }, 970 { 971 .name = "offset", 972 .alias = "fileoffset", 973 .type = FIO_OPT_STR_VAL, 974 .off1 = td_var_offset(start_offset), 975 .help = "Start IO from this offset", 976 .def = "0", 977 }, 978 { 979 .name = "bs", 980 .alias = "blocksize", 981 .type = FIO_OPT_INT, 982 .off1 = td_var_offset(bs[DDIR_READ]), 983 .off2 = td_var_offset(bs[DDIR_WRITE]), 984 .minval = 1, 985 .help = "Block size unit", 986 .def = "4k", 987 .parent = "rw", 988 }, 989 { 990 .name = "ba", 991 .alias = "blockalign", 992 .type = FIO_OPT_INT, 993 .off1 = td_var_offset(ba[DDIR_READ]), 994 .off2 = td_var_offset(ba[DDIR_WRITE]), 995 .minval = 1, 996 .help = "IO block offset alignment", 997 .parent = "rw", 998 }, 999 { 1000 .name = "bsrange", 1001 .alias = "blocksize_range", 1002 .type = FIO_OPT_RANGE, 1003 .off1 = td_var_offset(min_bs[DDIR_READ]), 1004 .off2 = td_var_offset(max_bs[DDIR_READ]), 1005 .off3 = td_var_offset(min_bs[DDIR_WRITE]), 1006 .off4 = td_var_offset(max_bs[DDIR_WRITE]), 1007 .minval = 1, 1008 .help = "Set block size range (in more detail than bs)", 1009 .parent = "rw", 1010 }, 1011 { 1012 .name = "bssplit", 1013 .type = FIO_OPT_STR, 1014 .cb = str_bssplit_cb, 1015 .help = "Set a specific mix of block sizes", 1016 .parent = "rw", 1017 }, 1018 { 1019 .name = "bs_unaligned", 1020 .alias = "blocksize_unaligned", 1021 .type = FIO_OPT_STR_SET, 1022 .off1 = td_var_offset(bs_unaligned), 1023 .help = "Don't sector align IO buffer sizes", 1024 .parent = "rw", 1025 }, 1026 { 1027 .name = "randrepeat", 1028 .type = FIO_OPT_BOOL, 1029 .off1 = td_var_offset(rand_repeatable), 1030 .help = "Use repeatable random IO pattern", 1031 .def = "1", 1032 .parent = "rw", 1033 }, 1034 { 1035 .name = "norandommap", 1036 .type = FIO_OPT_STR_SET, 1037 .off1 = td_var_offset(norandommap), 1038 .help = "Accept potential duplicate random blocks", 1039 .parent = "rw", 1040 }, 1041 { 1042 .name = "softrandommap", 1043 .type = FIO_OPT_BOOL, 1044 .off1 = td_var_offset(softrandommap), 1045 .help = "Set norandommap if randommap allocation fails", 1046 .parent = "norandommap", 1047 .def = "0", 1048 }, 1049 { 1050 .name = "nrfiles", 1051 .type = FIO_OPT_INT, 1052 .off1 = td_var_offset(nr_files), 1053 .help = "Split job workload between this number of files", 1054 .def = "1", 1055 }, 1056 { 1057 .name = "openfiles", 1058 .type = FIO_OPT_INT, 1059 .off1 = td_var_offset(open_files), 1060 .help = "Number of files to keep open at the same time", 1061 }, 1062 { 1063 .name = "file_service_type", 1064 .type = FIO_OPT_STR, 1065 .cb = str_fst_cb, 1066 .off1 = td_var_offset(file_service_type), 1067 .help = "How to select which file to service next", 1068 .def = "roundrobin", 1069 .posval = { 1070 { .ival = "random", 1071 .oval = FIO_FSERVICE_RANDOM, 1072 .help = "Choose a file at random", 1073 }, 1074 { .ival = "roundrobin", 1075 .oval = FIO_FSERVICE_RR, 1076 .help = "Round robin select files", 1077 }, 1078 { .ival = "sequential", 1079 .oval = FIO_FSERVICE_SEQ, 1080 .help = "Finish one file before moving to the next", 1081 }, 1082 }, 1083 .parent = "nrfiles", 1084 }, 1085#ifdef FIO_HAVE_FALLOCATE 1086 { 1087 .name = "fallocate", 1088 .type = FIO_OPT_BOOL, 1089 .off1 = td_var_offset(fallocate), 1090 .help = "Use fallocate() when laying out files", 1091 .def = "1", 1092 }, 1093#endif 1094 { 1095 .name = "fadvise_hint", 1096 .type = FIO_OPT_BOOL, 1097 .off1 = td_var_offset(fadvise_hint), 1098 .help = "Use fadvise() to advise the kernel on IO pattern", 1099 .def = "1", 1100 }, 1101 { 1102 .name = "fsync", 1103 .type = FIO_OPT_INT, 1104 .off1 = td_var_offset(fsync_blocks), 1105 .help = "Issue fsync for writes every given number of blocks", 1106 .def = "0", 1107 }, 1108 { 1109 .name = "fdatasync", 1110 .type = FIO_OPT_INT, 1111 .off1 = td_var_offset(fdatasync_blocks), 1112 .help = "Issue fdatasync for writes every given number of blocks", 1113 .def = "0", 1114 }, 1115 { 1116 .name = "direct", 1117 .type = FIO_OPT_BOOL, 1118 .off1 = td_var_offset(odirect), 1119 .help = "Use O_DIRECT IO (negates buffered)", 1120 .def = "0", 1121 }, 1122 { 1123 .name = "buffered", 1124 .type = FIO_OPT_BOOL, 1125 .off1 = td_var_offset(odirect), 1126 .neg = 1, 1127 .help = "Use buffered IO (negates direct)", 1128 .def = "1", 1129 }, 1130 { 1131 .name = "overwrite", 1132 .type = FIO_OPT_BOOL, 1133 .off1 = td_var_offset(overwrite), 1134 .help = "When writing, set whether to overwrite current data", 1135 .def = "0", 1136 }, 1137 { 1138 .name = "loops", 1139 .type = FIO_OPT_INT, 1140 .off1 = td_var_offset(loops), 1141 .help = "Number of times to run the job", 1142 .def = "1", 1143 }, 1144 { 1145 .name = "numjobs", 1146 .type = FIO_OPT_INT, 1147 .off1 = td_var_offset(numjobs), 1148 .help = "Duplicate this job this many times", 1149 .def = "1", 1150 }, 1151 { 1152 .name = "startdelay", 1153 .type = FIO_OPT_INT, 1154 .off1 = td_var_offset(start_delay), 1155 .help = "Only start job when this period has passed", 1156 .def = "0", 1157 }, 1158 { 1159 .name = "runtime", 1160 .alias = "timeout", 1161 .type = FIO_OPT_STR_VAL_TIME, 1162 .off1 = td_var_offset(timeout), 1163 .help = "Stop workload when this amount of time has passed", 1164 .def = "0", 1165 }, 1166 { 1167 .name = "time_based", 1168 .type = FIO_OPT_STR_SET, 1169 .off1 = td_var_offset(time_based), 1170 .help = "Keep running until runtime/timeout is met", 1171 }, 1172 { 1173 .name = "ramp_time", 1174 .type = FIO_OPT_STR_VAL_TIME, 1175 .off1 = td_var_offset(ramp_time), 1176 .help = "Ramp up time before measuring performance", 1177 }, 1178 { 1179 .name = "mem", 1180 .alias = "iomem", 1181 .type = FIO_OPT_STR, 1182 .cb = str_mem_cb, 1183 .off1 = td_var_offset(mem_type), 1184 .help = "Backing type for IO buffers", 1185 .def = "malloc", 1186 .posval = { 1187 { .ival = "malloc", 1188 .oval = MEM_MALLOC, 1189 .help = "Use malloc(3) for IO buffers", 1190 }, 1191 { .ival = "shm", 1192 .oval = MEM_SHM, 1193 .help = "Use shared memory segments for IO buffers", 1194 }, 1195#ifdef FIO_HAVE_HUGETLB 1196 { .ival = "shmhuge", 1197 .oval = MEM_SHMHUGE, 1198 .help = "Like shm, but use huge pages", 1199 }, 1200#endif 1201 { .ival = "mmap", 1202 .oval = MEM_MMAP, 1203 .help = "Use mmap(2) (file or anon) for IO buffers", 1204 }, 1205#ifdef FIO_HAVE_HUGETLB 1206 { .ival = "mmaphuge", 1207 .oval = MEM_MMAPHUGE, 1208 .help = "Like mmap, but use huge pages", 1209 }, 1210#endif 1211 }, 1212 }, 1213 { 1214 .name = "iomem_align", 1215 .alias = "mem_align", 1216 .type = FIO_OPT_INT, 1217 .off1 = td_var_offset(mem_align), 1218 .minval = 0, 1219 .help = "IO memory buffer offset alignment", 1220 .def = "0", 1221 .parent = "iomem", 1222 }, 1223 { 1224 .name = "verify", 1225 .type = FIO_OPT_STR, 1226 .off1 = td_var_offset(verify), 1227 .help = "Verify data written", 1228 .def = "0", 1229 .posval = { 1230 { .ival = "0", 1231 .oval = VERIFY_NONE, 1232 .help = "Don't do IO verification", 1233 }, 1234 { .ival = "md5", 1235 .oval = VERIFY_MD5, 1236 .help = "Use md5 checksums for verification", 1237 }, 1238 { .ival = "crc64", 1239 .oval = VERIFY_CRC64, 1240 .help = "Use crc64 checksums for verification", 1241 }, 1242 { .ival = "crc32", 1243 .oval = VERIFY_CRC32, 1244 .help = "Use crc32 checksums for verification", 1245 }, 1246 { .ival = "crc32c-intel", 1247 .oval = VERIFY_CRC32C_INTEL, 1248 .help = "Use hw crc32c checksums for verification", 1249 }, 1250 { .ival = "crc32c", 1251 .oval = VERIFY_CRC32C, 1252 .help = "Use crc32c checksums for verification", 1253 }, 1254 { .ival = "crc16", 1255 .oval = VERIFY_CRC16, 1256 .help = "Use crc16 checksums for verification", 1257 }, 1258 { .ival = "crc7", 1259 .oval = VERIFY_CRC7, 1260 .help = "Use crc7 checksums for verification", 1261 }, 1262 { .ival = "sha1", 1263 .oval = VERIFY_SHA1, 1264 .help = "Use sha1 checksums for verification", 1265 }, 1266 { .ival = "sha256", 1267 .oval = VERIFY_SHA256, 1268 .help = "Use sha256 checksums for verification", 1269 }, 1270 { .ival = "sha512", 1271 .oval = VERIFY_SHA512, 1272 .help = "Use sha512 checksums for verification", 1273 }, 1274 { .ival = "meta", 1275 .oval = VERIFY_META, 1276 .help = "Use io information", 1277 }, 1278 { 1279 .ival = "null", 1280 .oval = VERIFY_NULL, 1281 .help = "Pretend to verify", 1282 }, 1283 }, 1284 }, 1285 { 1286 .name = "do_verify", 1287 .type = FIO_OPT_BOOL, 1288 .off1 = td_var_offset(do_verify), 1289 .help = "Run verification stage after write", 1290 .def = "1", 1291 .parent = "verify", 1292 }, 1293 { 1294 .name = "verifysort", 1295 .type = FIO_OPT_BOOL, 1296 .off1 = td_var_offset(verifysort), 1297 .help = "Sort written verify blocks for read back", 1298 .def = "1", 1299 .parent = "verify", 1300 }, 1301 { 1302 .name = "verify_interval", 1303 .type = FIO_OPT_INT, 1304 .off1 = td_var_offset(verify_interval), 1305 .minval = 2 * sizeof(struct verify_header), 1306 .help = "Store verify buffer header every N bytes", 1307 .parent = "verify", 1308 }, 1309 { 1310 .name = "verify_offset", 1311 .type = FIO_OPT_INT, 1312 .help = "Offset verify header location by N bytes", 1313 .def = "0", 1314 .cb = str_verify_offset_cb, 1315 .parent = "verify", 1316 }, 1317 { 1318 .name = "verify_pattern", 1319 .type = FIO_OPT_STR, 1320 .cb = str_verify_pattern_cb, 1321 .help = "Fill pattern for IO buffers", 1322 .parent = "verify", 1323 }, 1324 { 1325 .name = "verify_fatal", 1326 .type = FIO_OPT_BOOL, 1327 .off1 = td_var_offset(verify_fatal), 1328 .def = "0", 1329 .help = "Exit on a single verify failure, don't continue", 1330 .parent = "verify", 1331 }, 1332 { 1333 .name = "verify_async", 1334 .type = FIO_OPT_INT, 1335 .off1 = td_var_offset(verify_async), 1336 .def = "0", 1337 .help = "Number of async verifier threads to use", 1338 .parent = "verify", 1339 }, 1340#ifdef FIO_HAVE_CPU_AFFINITY 1341 { 1342 .name = "verify_async_cpus", 1343 .type = FIO_OPT_STR, 1344 .cb = str_verify_cpus_allowed_cb, 1345 .help = "Set CPUs allowed for async verify threads", 1346 .parent = "verify_async", 1347 }, 1348#endif 1349 { 1350 .name = "write_iolog", 1351 .type = FIO_OPT_STR_STORE, 1352 .off1 = td_var_offset(write_iolog_file), 1353 .help = "Store IO pattern to file", 1354 }, 1355 { 1356 .name = "read_iolog", 1357 .type = FIO_OPT_STR_STORE, 1358 .off1 = td_var_offset(read_iolog_file), 1359 .help = "Playback IO pattern from file", 1360 }, 1361 { 1362 .name = "exec_prerun", 1363 .type = FIO_OPT_STR_STORE, 1364 .off1 = td_var_offset(exec_prerun), 1365 .help = "Execute this file prior to running job", 1366 }, 1367 { 1368 .name = "exec_postrun", 1369 .type = FIO_OPT_STR_STORE, 1370 .off1 = td_var_offset(exec_postrun), 1371 .help = "Execute this file after running job", 1372 }, 1373#ifdef FIO_HAVE_IOSCHED_SWITCH 1374 { 1375 .name = "ioscheduler", 1376 .type = FIO_OPT_STR_STORE, 1377 .off1 = td_var_offset(ioscheduler), 1378 .help = "Use this IO scheduler on the backing device", 1379 }, 1380#endif 1381 { 1382 .name = "zonesize", 1383 .type = FIO_OPT_STR_VAL, 1384 .off1 = td_var_offset(zone_size), 1385 .help = "Give size of an IO zone", 1386 .def = "0", 1387 }, 1388 { 1389 .name = "zoneskip", 1390 .type = FIO_OPT_STR_VAL, 1391 .off1 = td_var_offset(zone_skip), 1392 .help = "Space between IO zones", 1393 .def = "0", 1394 }, 1395 { 1396 .name = "lockmem", 1397 .type = FIO_OPT_STR_VAL, 1398 .cb = str_lockmem_cb, 1399 .help = "Lock down this amount of memory", 1400 .def = "0", 1401 }, 1402 { 1403 .name = "rwmixread", 1404 .type = FIO_OPT_INT, 1405 .cb = str_rwmix_read_cb, 1406 .maxval = 100, 1407 .help = "Percentage of mixed workload that is reads", 1408 .def = "50", 1409 }, 1410 { 1411 .name = "rwmixwrite", 1412 .type = FIO_OPT_INT, 1413 .cb = str_rwmix_write_cb, 1414 .maxval = 100, 1415 .help = "Percentage of mixed workload that is writes", 1416 .def = "50", 1417 }, 1418 { 1419 .name = "rwmixcycle", 1420 .type = FIO_OPT_DEPRECATED, 1421 }, 1422 { 1423 .name = "nice", 1424 .type = FIO_OPT_INT, 1425 .off1 = td_var_offset(nice), 1426 .help = "Set job CPU nice value", 1427 .minval = -19, 1428 .maxval = 20, 1429 .def = "0", 1430 }, 1431#ifdef FIO_HAVE_IOPRIO 1432 { 1433 .name = "prio", 1434 .type = FIO_OPT_INT, 1435 .cb = str_prio_cb, 1436 .help = "Set job IO priority value", 1437 .minval = 0, 1438 .maxval = 7, 1439 }, 1440 { 1441 .name = "prioclass", 1442 .type = FIO_OPT_INT, 1443 .cb = str_prioclass_cb, 1444 .help = "Set job IO priority class", 1445 .minval = 0, 1446 .maxval = 3, 1447 }, 1448#endif 1449 { 1450 .name = "thinktime", 1451 .type = FIO_OPT_INT, 1452 .off1 = td_var_offset(thinktime), 1453 .help = "Idle time between IO buffers (usec)", 1454 .def = "0", 1455 }, 1456 { 1457 .name = "thinktime_spin", 1458 .type = FIO_OPT_INT, 1459 .off1 = td_var_offset(thinktime_spin), 1460 .help = "Start think time by spinning this amount (usec)", 1461 .def = "0", 1462 .parent = "thinktime", 1463 }, 1464 { 1465 .name = "thinktime_blocks", 1466 .type = FIO_OPT_INT, 1467 .off1 = td_var_offset(thinktime_blocks), 1468 .help = "IO buffer period between 'thinktime'", 1469 .def = "1", 1470 .parent = "thinktime", 1471 }, 1472 { 1473 .name = "rate", 1474 .type = FIO_OPT_INT, 1475 .off1 = td_var_offset(rate[0]), 1476 .off2 = td_var_offset(rate[1]), 1477 .help = "Set bandwidth rate", 1478 }, 1479 { 1480 .name = "ratemin", 1481 .type = FIO_OPT_INT, 1482 .off1 = td_var_offset(ratemin[0]), 1483 .off2 = td_var_offset(ratemin[1]), 1484 .help = "Job must meet this rate or it will be shutdown", 1485 .parent = "rate", 1486 }, 1487 { 1488 .name = "rate_iops", 1489 .type = FIO_OPT_INT, 1490 .off1 = td_var_offset(rate_iops[0]), 1491 .off2 = td_var_offset(rate_iops[1]), 1492 .help = "Limit IO used to this number of IO operations/sec", 1493 }, 1494 { 1495 .name = "rate_iops_min", 1496 .type = FIO_OPT_INT, 1497 .off1 = td_var_offset(rate_iops_min[0]), 1498 .off2 = td_var_offset(rate_iops_min[1]), 1499 .help = "Job must meet this rate or it will be shutdown", 1500 .parent = "rate_iops", 1501 }, 1502 { 1503 .name = "ratecycle", 1504 .type = FIO_OPT_INT, 1505 .off1 = td_var_offset(ratecycle), 1506 .help = "Window average for rate limits (msec)", 1507 .def = "1000", 1508 .parent = "rate", 1509 }, 1510 { 1511 .name = "invalidate", 1512 .type = FIO_OPT_BOOL, 1513 .off1 = td_var_offset(invalidate_cache), 1514 .help = "Invalidate buffer/page cache prior to running job", 1515 .def = "1", 1516 }, 1517 { 1518 .name = "sync", 1519 .type = FIO_OPT_BOOL, 1520 .off1 = td_var_offset(sync_io), 1521 .help = "Use O_SYNC for buffered writes", 1522 .def = "0", 1523 .parent = "buffered", 1524 }, 1525 { 1526 .name = "bwavgtime", 1527 .type = FIO_OPT_INT, 1528 .off1 = td_var_offset(bw_avg_time), 1529 .help = "Time window over which to calculate bandwidth" 1530 " (msec)", 1531 .def = "500", 1532 }, 1533 { 1534 .name = "create_serialize", 1535 .type = FIO_OPT_BOOL, 1536 .off1 = td_var_offset(create_serialize), 1537 .help = "Serialize creating of job files", 1538 .def = "1", 1539 }, 1540 { 1541 .name = "create_fsync", 1542 .type = FIO_OPT_BOOL, 1543 .off1 = td_var_offset(create_fsync), 1544 .help = "Fsync file after creation", 1545 .def = "1", 1546 }, 1547 { 1548 .name = "create_on_open", 1549 .type = FIO_OPT_BOOL, 1550 .off1 = td_var_offset(create_on_open), 1551 .help = "Create files when they are opened for IO", 1552 .def = "0", 1553 }, 1554 { 1555 .name = "pre_read", 1556 .type = FIO_OPT_BOOL, 1557 .off1 = td_var_offset(pre_read), 1558 .help = "Preread files before starting official testing", 1559 .def = "0", 1560 }, 1561 { 1562 .name = "cpuload", 1563 .type = FIO_OPT_INT, 1564 .off1 = td_var_offset(cpuload), 1565 .help = "Use this percentage of CPU", 1566 }, 1567 { 1568 .name = "cpuchunks", 1569 .type = FIO_OPT_INT, 1570 .off1 = td_var_offset(cpucycle), 1571 .help = "Length of the CPU burn cycles (usecs)", 1572 .def = "50000", 1573 .parent = "cpuload", 1574 }, 1575#ifdef FIO_HAVE_CPU_AFFINITY 1576 { 1577 .name = "cpumask", 1578 .type = FIO_OPT_INT, 1579 .cb = str_cpumask_cb, 1580 .help = "CPU affinity mask", 1581 }, 1582 { 1583 .name = "cpus_allowed", 1584 .type = FIO_OPT_STR, 1585 .cb = str_cpus_allowed_cb, 1586 .help = "Set CPUs allowed", 1587 }, 1588#endif 1589 { 1590 .name = "end_fsync", 1591 .type = FIO_OPT_BOOL, 1592 .off1 = td_var_offset(end_fsync), 1593 .help = "Include fsync at the end of job", 1594 .def = "0", 1595 }, 1596 { 1597 .name = "fsync_on_close", 1598 .type = FIO_OPT_BOOL, 1599 .off1 = td_var_offset(fsync_on_close), 1600 .help = "fsync files on close", 1601 .def = "0", 1602 }, 1603 { 1604 .name = "unlink", 1605 .type = FIO_OPT_BOOL, 1606 .off1 = td_var_offset(unlink), 1607 .help = "Unlink created files after job has completed", 1608 .def = "0", 1609 }, 1610 { 1611 .name = "exitall", 1612 .type = FIO_OPT_STR_SET, 1613 .cb = str_exitall_cb, 1614 .help = "Terminate all jobs when one exits", 1615 }, 1616 { 1617 .name = "stonewall", 1618 .type = FIO_OPT_STR_SET, 1619 .off1 = td_var_offset(stonewall), 1620 .help = "Insert a hard barrier between this job and previous", 1621 }, 1622 { 1623 .name = "new_group", 1624 .type = FIO_OPT_STR_SET, 1625 .off1 = td_var_offset(new_group), 1626 .help = "Mark the start of a new group (for reporting)", 1627 }, 1628 { 1629 .name = "thread", 1630 .type = FIO_OPT_STR_SET, 1631 .off1 = td_var_offset(use_thread), 1632 .help = "Use threads instead of forks", 1633 }, 1634 { 1635 .name = "write_bw_log", 1636 .type = FIO_OPT_STR, 1637 .off1 = td_var_offset(write_bw_log), 1638 .cb = str_write_bw_log_cb, 1639 .help = "Write log of bandwidth during run", 1640 }, 1641 { 1642 .name = "write_lat_log", 1643 .type = FIO_OPT_STR, 1644 .off1 = td_var_offset(write_lat_log), 1645 .cb = str_write_lat_log_cb, 1646 .help = "Write log of latency during run", 1647 }, 1648 { 1649 .name = "hugepage-size", 1650 .type = FIO_OPT_INT, 1651 .off1 = td_var_offset(hugepage_size), 1652 .help = "When using hugepages, specify size of each page", 1653 .def = __stringify(FIO_HUGE_PAGE), 1654 }, 1655 { 1656 .name = "group_reporting", 1657 .type = FIO_OPT_STR_SET, 1658 .off1 = td_var_offset(group_reporting), 1659 .help = "Do reporting on a per-group basis", 1660 }, 1661 { 1662 .name = "zero_buffers", 1663 .type = FIO_OPT_STR_SET, 1664 .off1 = td_var_offset(zero_buffers), 1665 .help = "Init IO buffers to all zeroes", 1666 }, 1667 { 1668 .name = "refill_buffers", 1669 .type = FIO_OPT_STR_SET, 1670 .off1 = td_var_offset(refill_buffers), 1671 .help = "Refill IO buffers on every IO submit", 1672 }, 1673#ifdef FIO_HAVE_DISK_UTIL 1674 { 1675 .name = "disk_util", 1676 .type = FIO_OPT_BOOL, 1677 .off1 = td_var_offset(do_disk_util), 1678 .help = "Log disk utilization statistics", 1679 .def = "1", 1680 }, 1681#endif 1682 { 1683 .name = "gtod_reduce", 1684 .type = FIO_OPT_BOOL, 1685 .help = "Greatly reduce number of gettimeofday() calls", 1686 .cb = str_gtod_reduce_cb, 1687 .def = "0", 1688 }, 1689 { 1690 .name = "disable_clat", 1691 .type = FIO_OPT_BOOL, 1692 .off1 = td_var_offset(disable_clat), 1693 .help = "Disable completion latency numbers", 1694 .parent = "gtod_reduce", 1695 .def = "0", 1696 }, 1697 { 1698 .name = "disable_slat", 1699 .type = FIO_OPT_BOOL, 1700 .off1 = td_var_offset(disable_slat), 1701 .help = "Disable submissionn latency numbers", 1702 .parent = "gtod_reduce", 1703 .def = "0", 1704 }, 1705 { 1706 .name = "disable_bw_measurement", 1707 .type = FIO_OPT_BOOL, 1708 .off1 = td_var_offset(disable_bw), 1709 .help = "Disable bandwidth logging", 1710 .parent = "gtod_reduce", 1711 .def = "0", 1712 }, 1713 { 1714 .name = "gtod_cpu", 1715 .type = FIO_OPT_INT, 1716 .cb = str_gtod_cpu_cb, 1717 .help = "Setup dedicated gettimeofday() thread on this CPU", 1718 .verify = gtod_cpu_verify, 1719 }, 1720 { 1721 .name = "continue_on_error", 1722 .type = FIO_OPT_BOOL, 1723 .off1 = td_var_offset(continue_on_error), 1724 .help = "Continue on non-fatal errors during I/O", 1725 .def = "0", 1726 }, 1727 { 1728 .name = "profile", 1729 .type = FIO_OPT_STR_STORE, 1730 .off1 = td_var_offset(profile), 1731 .help = "Select a specific builtin performance test", 1732 }, 1733 { 1734 .name = "cgroup", 1735 .type = FIO_OPT_STR_STORE, 1736 .off1 = td_var_offset(cgroup), 1737 .help = "Add job to cgroup of this name", 1738 }, 1739 { 1740 .name = "cgroup_weight", 1741 .type = FIO_OPT_INT, 1742 .off1 = td_var_offset(cgroup_weight), 1743 .help = "Use given weight for cgroup", 1744 .minval = 100, 1745 .maxval = 1000, 1746 }, 1747 { 1748 .name = "uid", 1749 .type = FIO_OPT_INT, 1750 .off1 = td_var_offset(uid), 1751 .help = "Run job with this user ID", 1752 }, 1753 { 1754 .name = "gid", 1755 .type = FIO_OPT_INT, 1756 .off1 = td_var_offset(gid), 1757 .help = "Run job with this group ID", 1758 }, 1759 { 1760 .name = NULL, 1761 }, 1762}; 1763 1764void fio_options_dup_and_init(struct option *long_options) 1765{ 1766 struct fio_option *o; 1767 unsigned int i; 1768 1769 options_init(options); 1770 1771 i = 0; 1772 while (long_options[i].name) 1773 i++; 1774 1775 o = &options[0]; 1776 while (o->name) { 1777 long_options[i].name = (char *) o->name; 1778 long_options[i].val = FIO_GETOPT_JOB; 1779 if (o->type == FIO_OPT_STR_SET) 1780 long_options[i].has_arg = no_argument; 1781 else 1782 long_options[i].has_arg = required_argument; 1783 1784 i++; 1785 o++; 1786 assert(i < FIO_NR_OPTIONS); 1787 } 1788} 1789 1790struct fio_keyword { 1791 const char *word; 1792 const char *desc; 1793 char *replace; 1794}; 1795 1796static struct fio_keyword fio_keywords[] = { 1797 { 1798 .word = "$pagesize", 1799 .desc = "Page size in the system", 1800 }, 1801 { 1802 .word = "$mb_memory", 1803 .desc = "Megabytes of memory online", 1804 }, 1805 { 1806 .word = "$ncpus", 1807 .desc = "Number of CPUs online in the system", 1808 }, 1809 { 1810 .word = NULL, 1811 }, 1812}; 1813 1814void fio_keywords_init(void) 1815{ 1816 unsigned long long mb_memory; 1817 char buf[128]; 1818 long l; 1819 1820 sprintf(buf, "%lu", page_size); 1821 fio_keywords[0].replace = strdup(buf); 1822 1823 mb_memory = os_phys_mem() / page_size; 1824 sprintf(buf, "%llu", mb_memory); 1825 fio_keywords[1].replace = strdup(buf); 1826 1827 l = sysconf(_SC_NPROCESSORS_ONLN); 1828 sprintf(buf, "%lu", l); 1829 fio_keywords[2].replace = strdup(buf); 1830} 1831 1832#define BC_APP "bc" 1833 1834static char *bc_calc(char *str) 1835{ 1836 char *buf, *tmp, opt[80]; 1837 FILE *f; 1838 int ret; 1839 1840 /* 1841 * No math, just return string 1842 */ 1843 if (!strchr(str, '+') && !strchr(str, '-') && !strchr(str, '*') && 1844 !strchr(str, '/')) 1845 return str; 1846 1847 /* 1848 * Split option from value, we only need to calculate the value 1849 */ 1850 tmp = strchr(str, '='); 1851 if (!tmp) 1852 return str; 1853 1854 tmp++; 1855 memset(opt, 0, sizeof(opt)); 1856 strncpy(opt, str, tmp - str); 1857 1858 buf = malloc(128); 1859 1860 sprintf(buf, "which %s > /dev/null", BC_APP); 1861 if (system(buf)) { 1862 log_err("fio: bc is needed for performing math\n"); 1863 free(buf); 1864 return NULL; 1865 } 1866 1867 sprintf(buf, "echo %s | %s", tmp, BC_APP); 1868 f = popen(buf, "r"); 1869 if (!f) { 1870 free(buf); 1871 return NULL; 1872 } 1873 1874 ret = fread(buf, 1, 128, f); 1875 if (ret <= 0) { 1876 free(buf); 1877 return NULL; 1878 } 1879 1880 buf[ret - 1] = '\0'; 1881 strcat(opt, buf); 1882 strcpy(buf, opt); 1883 pclose(f); 1884 free(str); 1885 return buf; 1886} 1887 1888/* 1889 * Look for reserved variable names and replace them with real values 1890 */ 1891static char *fio_keyword_replace(char *opt) 1892{ 1893 char *s; 1894 int i; 1895 1896 for (i = 0; fio_keywords[i].word != NULL; i++) { 1897 struct fio_keyword *kw = &fio_keywords[i]; 1898 1899 while ((s = strstr(opt, kw->word)) != NULL) { 1900 char *new = malloc(strlen(opt) + 1); 1901 char *o_org = opt; 1902 int olen = s - opt; 1903 int len; 1904 1905 /* 1906 * Copy part of the string before the keyword and 1907 * sprintf() the replacement after it. 1908 */ 1909 memcpy(new, opt, olen); 1910 len = sprintf(new + olen, "%s", kw->replace); 1911 1912 /* 1913 * If there's more in the original string, copy that 1914 * in too 1915 */ 1916 opt += strlen(kw->word) + olen; 1917 if (strlen(opt)) 1918 memcpy(new + olen + len, opt, opt - o_org - 1); 1919 1920 /* 1921 * replace opt and free the old opt 1922 */ 1923 opt = new; 1924 //free(o_org); 1925 1926 /* 1927 * Check for potential math and invoke bc, if possible 1928 */ 1929 opt = bc_calc(opt); 1930 } 1931 } 1932 1933 return opt; 1934} 1935 1936int fio_options_parse(struct thread_data *td, char **opts, int num_opts) 1937{ 1938 int i, ret; 1939 1940 sort_options(opts, options, num_opts); 1941 1942 for (ret = 0, i = 0; i < num_opts; i++) { 1943 opts[i] = fio_keyword_replace(opts[i]); 1944 ret |= parse_option(opts[i], options, &ext_opt_list, td); 1945 } 1946 1947 return ret; 1948} 1949 1950int fio_cmd_option_parse(struct thread_data *td, const char *opt, char *val) 1951{ 1952 return parse_cmd_option(opt, val, options, &ext_opt_list, td); 1953} 1954 1955void fio_fill_default_options(struct thread_data *td) 1956{ 1957 fill_default_options(td, options); 1958} 1959 1960int fio_show_option_help(const char *opt) 1961{ 1962 return show_cmd_help(options, opt); 1963} 1964 1965static void __options_mem(struct thread_data *td, int alloc) 1966{ 1967 struct thread_options *o = &td->o; 1968 struct fio_option *opt; 1969 char **ptr; 1970 int i; 1971 1972 for (i = 0, opt = &options[0]; opt->name; i++, opt = &options[i]) { 1973 if (opt->type != FIO_OPT_STR_STORE) 1974 continue; 1975 1976 ptr = (void *) o + opt->off1; 1977 if (*ptr) { 1978 if (alloc) 1979 *ptr = strdup(*ptr); 1980 else { 1981 free(*ptr); 1982 *ptr = NULL; 1983 } 1984 } 1985 } 1986} 1987 1988/* 1989 * dupe FIO_OPT_STR_STORE options 1990 */ 1991void options_mem_dupe(struct thread_data *td) 1992{ 1993 __options_mem(td, 1); 1994} 1995 1996void options_mem_free(struct thread_data fio_unused *td) 1997{ 1998#if 0 1999 __options_mem(td, 0); 2000#endif 2001} 2002 2003unsigned int fio_get_kb_base(void *data) 2004{ 2005 struct thread_data *td = data; 2006 unsigned int kb_base = 0; 2007 2008 if (td) 2009 kb_base = td->o.kb_base; 2010 if (!kb_base) 2011 kb_base = 1024; 2012 2013 return kb_base; 2014} 2015 2016void register_ext_option(struct ext_option *eopt) 2017{ 2018 dprint(FD_PARSE, "register option '%s'\n", eopt->o.name); 2019 option_init(&eopt->o); 2020 flist_add_tail(&eopt->list, &ext_opt_list); 2021} 2022