init.c revision 509df2bc49284a93efd6b890bdf5e3c4ea2aba4a
1/* 2 * This file contains job initialization and setup functions. 3 */ 4#include <stdio.h> 5#include <stdlib.h> 6#include <unistd.h> 7#include <fcntl.h> 8#include <ctype.h> 9#include <string.h> 10#include <errno.h> 11#include <getopt.h> 12#include <sys/ipc.h> 13#include <sys/shm.h> 14#include <sys/types.h> 15#include <sys/stat.h> 16 17#include "fio.h" 18#include "parse.h" 19 20static char fio_version_string[] = "fio 1.19"; 21 22#define FIO_RANDSEED (0xb1899bedUL) 23 24static char **ini_file; 25static int max_jobs = MAX_JOBS; 26static int dump_cmdline; 27 28struct thread_data def_thread; 29struct thread_data *threads = NULL; 30 31int exitall_on_terminate = 0; 32int terse_output = 0; 33int eta_print; 34unsigned long long mlock_size = 0; 35FILE *f_out = NULL; 36FILE *f_err = NULL; 37char *job_section = NULL; 38 39int write_bw_log = 0; 40int read_only = 0; 41 42static int def_timeout = 0; 43static int write_lat_log = 0; 44 45static int prev_group_jobs; 46 47unsigned long fio_debug = 0; 48 49/* 50 * Command line options. These will contain the above, plus a few 51 * extra that only pertain to fio itself and not jobs. 52 */ 53static struct option long_options[FIO_NR_OPTIONS] = { 54 { 55 .name = "output", 56 .has_arg = required_argument, 57 .val = 'o', 58 }, 59 { 60 .name = "timeout", 61 .has_arg = required_argument, 62 .val = 't', 63 }, 64 { 65 .name = "latency-log", 66 .has_arg = required_argument, 67 .val = 'l', 68 }, 69 { 70 .name = "bandwidth-log", 71 .has_arg = required_argument, 72 .val = 'b', 73 }, 74 { 75 .name = "minimal", 76 .has_arg = optional_argument, 77 .val = 'm', 78 }, 79 { 80 .name = "version", 81 .has_arg = no_argument, 82 .val = 'v', 83 }, 84 { 85 .name = "help", 86 .has_arg = no_argument, 87 .val = 'h', 88 }, 89 { 90 .name = "cmdhelp", 91 .has_arg = optional_argument, 92 .val = 'c', 93 }, 94 { 95 .name = "showcmd", 96 .has_arg = no_argument, 97 .val = 's', 98 }, 99 { 100 .name = "readonly", 101 .has_arg = no_argument, 102 .val = 'r', 103 }, 104 { 105 .name = "eta", 106 .has_arg = required_argument, 107 .val = 'e', 108 }, 109 { 110 .name = "debug", 111 .has_arg = required_argument, 112 .val = 'd', 113 }, 114 { 115 .name = "section", 116 .has_arg = required_argument, 117 .val = 'x', 118 }, 119 { 120 .name = NULL, 121 }, 122}; 123 124FILE *get_f_out() 125{ 126 return f_out; 127} 128 129FILE *get_f_err() 130{ 131 return f_err; 132} 133 134/* 135 * Return a free job structure. 136 */ 137static struct thread_data *get_new_job(int global, struct thread_data *parent) 138{ 139 struct thread_data *td; 140 141 if (global) 142 return &def_thread; 143 if (thread_number >= max_jobs) 144 return NULL; 145 146 td = &threads[thread_number++]; 147 *td = *parent; 148 149 dup_files(td, parent); 150 options_mem_dupe(td); 151 152 td->thread_number = thread_number; 153 return td; 154} 155 156static void put_job(struct thread_data *td) 157{ 158 if (td == &def_thread) 159 return; 160 161 if (td->error) 162 log_info("fio: %s\n", td->verror); 163 164 memset(&threads[td->thread_number - 1], 0, sizeof(*td)); 165 thread_number--; 166} 167 168static int setup_rate(struct thread_data *td) 169{ 170 unsigned long nr_reads_per_msec; 171 unsigned long long rate; 172 unsigned int bs; 173 174 if (!td->o.rate && !td->o.rate_iops) 175 return 0; 176 177 if (td_rw(td)) 178 bs = td->o.rw_min_bs; 179 else if (td_read(td)) 180 bs = td->o.min_bs[DDIR_READ]; 181 else 182 bs = td->o.min_bs[DDIR_WRITE]; 183 184 if (td->o.rate) { 185 rate = td->o.rate; 186 nr_reads_per_msec = (rate * 1024 * 1000LL) / bs; 187 } else 188 nr_reads_per_msec = td->o.rate_iops * 1000UL; 189 190 if (!nr_reads_per_msec) { 191 log_err("rate lower than supported\n"); 192 return -1; 193 } 194 195 td->rate_usec_cycle = 1000000000ULL / nr_reads_per_msec; 196 td->rate_pending_usleep = 0; 197 return 0; 198} 199 200/* 201 * Lazy way of fixing up options that depend on each other. We could also 202 * define option callback handlers, but this is easier. 203 */ 204static int fixup_options(struct thread_data *td) 205{ 206 struct thread_options *o = &td->o; 207 208 if (read_only && td_write(td)) { 209 log_err("fio: job <%s> has write bit set, but fio is in read-only mode\n", td->o.name); 210 return 1; 211 } 212 213 if (o->rwmix[DDIR_READ] + o->rwmix[DDIR_WRITE] > 100) 214 o->rwmix[DDIR_WRITE] = 100 - o->rwmix[DDIR_READ]; 215 216 if (o->write_iolog_file && o->read_iolog_file) { 217 log_err("fio: read iolog overrides write_iolog\n"); 218 free(o->write_iolog_file); 219 o->write_iolog_file = NULL; 220 } 221 222 /* 223 * only really works for sequential io for now, and with 1 file 224 */ 225 if (o->zone_size && td_random(td) && o->open_files == 1) 226 o->zone_size = 0; 227 228 /* 229 * Reads can do overwrites, we always need to pre-create the file 230 */ 231 if (td_read(td) || td_rw(td)) 232 o->overwrite = 1; 233 234 if (!o->min_bs[DDIR_READ]) 235 o->min_bs[DDIR_READ]= o->bs[DDIR_READ]; 236 if (!o->max_bs[DDIR_READ]) 237 o->max_bs[DDIR_READ] = o->bs[DDIR_READ]; 238 if (!o->min_bs[DDIR_WRITE]) 239 o->min_bs[DDIR_WRITE]= o->bs[DDIR_WRITE]; 240 if (!o->max_bs[DDIR_WRITE]) 241 o->max_bs[DDIR_WRITE] = o->bs[DDIR_WRITE]; 242 243 o->rw_min_bs = min(o->min_bs[DDIR_READ], o->min_bs[DDIR_WRITE]); 244 245 if (!o->file_size_high) 246 o->file_size_high = o->file_size_low; 247 248 if (o->norandommap && o->verify != VERIFY_NONE) { 249 log_err("fio: norandommap given, verify disabled\n"); 250 o->verify = VERIFY_NONE; 251 } 252 if (o->bs_unaligned && (o->odirect || td->io_ops->flags & FIO_RAWIO)) 253 log_err("fio: bs_unaligned may not work with raw io\n"); 254 255 /* 256 * thinktime_spin must be less than thinktime 257 */ 258 if (o->thinktime_spin > o->thinktime) 259 o->thinktime_spin = o->thinktime; 260 261 /* 262 * The low water mark cannot be bigger than the iodepth 263 */ 264 if (o->iodepth_low > o->iodepth || !o->iodepth_low) { 265 /* 266 * syslet work around - if the workload is sequential, 267 * we want to let the queue drain all the way down to 268 * avoid seeking between async threads 269 */ 270 if (!strcmp(td->io_ops->name, "syslet-rw") && !td_random(td)) 271 o->iodepth_low = 1; 272 else 273 o->iodepth_low = o->iodepth; 274 } 275 276 /* 277 * If batch number isn't set, default to the same as iodepth 278 */ 279 if (o->iodepth_batch > o->iodepth || !o->iodepth_batch) 280 o->iodepth_batch = o->iodepth; 281 282 if (o->nr_files > td->files_index) 283 o->nr_files = td->files_index; 284 285 if (o->open_files > o->nr_files || !o->open_files) 286 o->open_files = o->nr_files; 287 288 if ((o->rate && o->rate_iops) || (o->ratemin && o->rate_iops_min)) { 289 log_err("fio: rate and rate_iops are mutually exclusive\n"); 290 return 1; 291 } 292 if ((o->rate < o->ratemin) || (o->rate_iops < o->rate_iops_min)) { 293 log_err("fio: minimum rate exceeds rate\n"); 294 return 1; 295 } 296 297 if (!o->timeout && o->time_based) { 298 log_err("fio: time_based requires a runtime/timeout setting\n"); 299 o->time_based = 0; 300 } 301 302 if (o->fill_device && !o->size) 303 o->size = ULONG_LONG_MAX; 304 305 if (td_rw(td) && td->o.verify != VERIFY_NONE) 306 log_info("fio: mixed read/write workload with verify. May not " 307 "work as expected, unless you pre-populated the file\n"); 308 309 return 0; 310} 311 312/* 313 * This function leaks the buffer 314 */ 315static char *to_kmg(unsigned int val) 316{ 317 char *buf = malloc(32); 318 char post[] = { 0, 'K', 'M', 'G', 'P', 'E', 0 }; 319 char *p = post; 320 321 do { 322 if (val & 1023) 323 break; 324 325 val >>= 10; 326 p++; 327 } while (*p); 328 329 snprintf(buf, 31, "%u%c", val, *p); 330 return buf; 331} 332 333/* External engines are specified by "external:name.o") */ 334static const char *get_engine_name(const char *str) 335{ 336 char *p = strstr(str, ":"); 337 338 if (!p) 339 return str; 340 341 p++; 342 strip_blank_front(&p); 343 strip_blank_end(p); 344 return p; 345} 346 347static int exists_and_not_file(const char *filename) 348{ 349 struct stat sb; 350 351 if (lstat(filename, &sb) == -1) 352 return 0; 353 354 if (S_ISREG(sb.st_mode)) 355 return 0; 356 357 return 1; 358} 359 360/* 361 * Initialize the various random states we need (random io, block size ranges, 362 * read/write mix, etc). 363 */ 364static int init_random_state(struct thread_data *td) 365{ 366 unsigned long seeds[6]; 367 int fd; 368 369 fd = open("/dev/urandom", O_RDONLY); 370 if (fd == -1) { 371 td_verror(td, errno, "open"); 372 return 1; 373 } 374 375 if (read(fd, seeds, sizeof(seeds)) < (int) sizeof(seeds)) { 376 td_verror(td, EIO, "read"); 377 close(fd); 378 return 1; 379 } 380 381 close(fd); 382 383 os_random_seed(seeds[0], &td->bsrange_state); 384 os_random_seed(seeds[1], &td->verify_state); 385 os_random_seed(seeds[2], &td->rwmix_state); 386 387 if (td->o.file_service_type == FIO_FSERVICE_RANDOM) 388 os_random_seed(seeds[3], &td->next_file_state); 389 390 os_random_seed(seeds[5], &td->file_size_state); 391 392 if (!td_random(td)) 393 return 0; 394 395 if (td->o.rand_repeatable) 396 seeds[4] = FIO_RANDSEED * td->thread_number; 397 398 os_random_seed(seeds[4], &td->random_state); 399 return 0; 400} 401 402/* 403 * Adds a job to the list of things todo. Sanitizes the various options 404 * to make sure we don't have conflicts, and initializes various 405 * members of td. 406 */ 407static int add_job(struct thread_data *td, const char *jobname, int job_add_num) 408{ 409 const char *ddir_str[] = { NULL, "read", "write", "rw", NULL, 410 "randread", "randwrite", "randrw" }; 411 unsigned int i; 412 const char *engine; 413 char fname[PATH_MAX]; 414 int numjobs, file_alloced; 415 416 /* 417 * the def_thread is just for options, it's not a real job 418 */ 419 if (td == &def_thread) 420 return 0; 421 422 /* 423 * if we are just dumping the output command line, don't add the job 424 */ 425 if (dump_cmdline) { 426 put_job(td); 427 return 0; 428 } 429 430 engine = get_engine_name(td->o.ioengine); 431 td->io_ops = load_ioengine(td, engine); 432 if (!td->io_ops) { 433 log_err("fio: failed to load engine %s\n", engine); 434 goto err; 435 } 436 437 if (td->o.use_thread) 438 nr_thread++; 439 else 440 nr_process++; 441 442 if (td->o.odirect) 443 td->io_ops->flags |= FIO_RAWIO; 444 445 file_alloced = 0; 446 if (!td->o.filename && !td->files_index) { 447 file_alloced = 1; 448 449 if (td->o.nr_files == 1 && exists_and_not_file(jobname)) 450 add_file(td, jobname); 451 else { 452 for (i = 0; i < td->o.nr_files; i++) { 453 sprintf(fname, "%s.%d.%d", jobname, td->thread_number, i); 454 add_file(td, fname); 455 } 456 } 457 } 458 459 if (fixup_options(td)) 460 goto err; 461 462 if (td->io_ops->flags & FIO_DISKLESSIO) { 463 struct fio_file *f; 464 465 for_each_file(td, f, i) 466 f->real_file_size = -1ULL; 467 } 468 469 td->mutex = fio_sem_init(0); 470 471 td->ts.clat_stat[0].min_val = td->ts.clat_stat[1].min_val = ULONG_MAX; 472 td->ts.slat_stat[0].min_val = td->ts.slat_stat[1].min_val = ULONG_MAX; 473 td->ts.bw_stat[0].min_val = td->ts.bw_stat[1].min_val = ULONG_MAX; 474 td->ddir_nr = td->o.ddir_nr; 475 476 if ((td->o.stonewall || td->o.numjobs > 1 || td->o.new_group) 477 && prev_group_jobs) { 478 prev_group_jobs = 0; 479 groupid++; 480 } 481 482 td->groupid = groupid; 483 prev_group_jobs++; 484 485 if (init_random_state(td)) 486 goto err; 487 488 if (setup_rate(td)) 489 goto err; 490 491 if (td->o.write_lat_log) { 492 setup_log(&td->ts.slat_log); 493 setup_log(&td->ts.clat_log); 494 } 495 if (td->o.write_bw_log) 496 setup_log(&td->ts.bw_log); 497 498 if (!td->o.name) 499 td->o.name = strdup(jobname); 500 501 if (!terse_output) { 502 if (!job_add_num) { 503 if (!strcmp(td->io_ops->name, "cpuio")) 504 log_info("%s: ioengine=cpu, cpuload=%u, cpucycle=%u\n", td->o.name, td->o.cpuload, td->o.cpucycle); 505 else { 506 char *c1, *c2, *c3, *c4; 507 508 c1 = to_kmg(td->o.min_bs[DDIR_READ]); 509 c2 = to_kmg(td->o.max_bs[DDIR_READ]); 510 c3 = to_kmg(td->o.min_bs[DDIR_WRITE]); 511 c4 = to_kmg(td->o.max_bs[DDIR_WRITE]); 512 513 log_info("%s: (g=%d): rw=%s, bs=%s-%s/%s-%s, ioengine=%s, iodepth=%u\n", td->o.name, td->groupid, ddir_str[td->o.td_ddir], c1, c2, c3, c4, td->io_ops->name, td->o.iodepth); 514 515 free(c1); 516 free(c2); 517 free(c3); 518 free(c4); 519 } 520 } else if (job_add_num == 1) 521 log_info("...\n"); 522 } 523 524 /* 525 * recurse add identical jobs, clear numjobs and stonewall options 526 * as they don't apply to sub-jobs 527 */ 528 numjobs = td->o.numjobs; 529 while (--numjobs) { 530 struct thread_data *td_new = get_new_job(0, td); 531 532 if (!td_new) 533 goto err; 534 535 td_new->o.numjobs = 1; 536 td_new->o.stonewall = 0; 537 td_new->o.new_group = 0; 538 539 if (file_alloced) { 540 td_new->o.filename = NULL; 541 td_new->files_index = 0; 542 td_new->files = NULL; 543 } 544 545 job_add_num = numjobs - 1; 546 547 if (add_job(td_new, jobname, job_add_num)) 548 goto err; 549 } 550 551 return 0; 552err: 553 put_job(td); 554 return -1; 555} 556 557static int skip_this_section(const char *name) 558{ 559 if (!job_section) 560 return 0; 561 if (!strncmp(name, "global", 6)) 562 return 0; 563 564 return strncmp(job_section, name, strlen(job_section)); 565} 566 567static int is_empty_or_comment(char *line) 568{ 569 unsigned int i; 570 571 for (i = 0; i < strlen(line); i++) { 572 if (line[i] == ';') 573 return 1; 574 if (line[i] == '#') 575 return 1; 576 if (!isspace(line[i]) && !iscntrl(line[i])) 577 return 0; 578 } 579 580 return 1; 581} 582 583/* 584 * This is our [ini] type file parser. 585 */ 586static int parse_jobs_ini(char *file, int stonewall_flag) 587{ 588 unsigned int global; 589 struct thread_data *td; 590 char *string, *name; 591 FILE *f; 592 char *p; 593 int ret = 0, stonewall; 594 int first_sect = 1; 595 int skip_fgets = 0; 596 int inside_skip = 0; 597 598 if (!strcmp(file, "-")) 599 f = stdin; 600 else 601 f = fopen(file, "r"); 602 603 if (!f) { 604 perror("fopen job file"); 605 return 1; 606 } 607 608 string = malloc(4096); 609 610 /* 611 * it's really 256 + small bit, 280 should suffice 612 */ 613 name = malloc(280); 614 memset(name, 0, 280); 615 616 stonewall = stonewall_flag; 617 do { 618 /* 619 * if skip_fgets is set, we already have loaded a line we 620 * haven't handled. 621 */ 622 if (!skip_fgets) { 623 p = fgets(string, 4095, f); 624 if (!p) 625 break; 626 } 627 628 skip_fgets = 0; 629 strip_blank_front(&p); 630 strip_blank_end(p); 631 632 if (is_empty_or_comment(p)) 633 continue; 634 if (sscanf(p, "[%255s]", name) != 1) { 635 if (inside_skip) 636 continue; 637 log_err("fio: option <%s> outside of [] job section\n", p); 638 break; 639 } 640 641 if (skip_this_section(name)) { 642 inside_skip = 1; 643 continue; 644 } else 645 inside_skip = 0; 646 647 global = !strncmp(name, "global", 6); 648 649 name[strlen(name) - 1] = '\0'; 650 651 if (dump_cmdline) { 652 if (first_sect) 653 log_info("fio "); 654 if (!global) 655 log_info("--name=%s ", name); 656 first_sect = 0; 657 } 658 659 td = get_new_job(global, &def_thread); 660 if (!td) { 661 ret = 1; 662 break; 663 } 664 665 /* 666 * Seperate multiple job files by a stonewall 667 */ 668 if (!global && stonewall) { 669 td->o.stonewall = stonewall; 670 stonewall = 0; 671 } 672 673 while ((p = fgets(string, 4096, f)) != NULL) { 674 if (is_empty_or_comment(p)) 675 continue; 676 677 strip_blank_front(&p); 678 679 /* 680 * new section, break out and make sure we don't 681 * fgets() a new line at the top. 682 */ 683 if (p[0] == '[') { 684 skip_fgets = 1; 685 break; 686 } 687 688 strip_blank_end(p); 689 690 /* 691 * Don't break here, continue parsing options so we 692 * dump all the bad ones. Makes trial/error fixups 693 * easier on the user. 694 */ 695 ret |= fio_option_parse(td, p); 696 if (!ret && dump_cmdline) 697 log_info("--%s ", p); 698 } 699 700 if (!ret) 701 ret = add_job(td, name, 0); 702 else { 703 log_err("fio: job %s dropped\n", name); 704 put_job(td); 705 } 706 } while (!ret); 707 708 if (dump_cmdline) 709 log_info("\n"); 710 711 free(string); 712 free(name); 713 if (f != stdin) 714 fclose(f); 715 return ret; 716} 717 718static int fill_def_thread(void) 719{ 720 memset(&def_thread, 0, sizeof(def_thread)); 721 722 fio_getaffinity(getpid(), &def_thread.o.cpumask); 723 724 /* 725 * fill default options 726 */ 727 fio_fill_default_options(&def_thread); 728 729 def_thread.o.timeout = def_timeout; 730 def_thread.o.write_bw_log = write_bw_log; 731 def_thread.o.write_lat_log = write_lat_log; 732 733 return 0; 734} 735 736static void free_shm(void) 737{ 738 struct shmid_ds sbuf; 739 740 if (threads) { 741 shmdt((void *) threads); 742 threads = NULL; 743 shmctl(shm_id, IPC_RMID, &sbuf); 744 } 745} 746 747/* 748 * The thread area is shared between the main process and the job 749 * threads/processes. So setup a shared memory segment that will hold 750 * all the job info. 751 */ 752static int setup_thread_area(void) 753{ 754 /* 755 * 1024 is too much on some machines, scale max_jobs if 756 * we get a failure that looks like too large a shm segment 757 */ 758 do { 759 size_t size = max_jobs * sizeof(struct thread_data); 760 761 shm_id = shmget(0, size, IPC_CREAT | 0600); 762 if (shm_id != -1) 763 break; 764 if (errno != EINVAL) { 765 perror("shmget"); 766 break; 767 } 768 769 max_jobs >>= 1; 770 } while (max_jobs); 771 772 if (shm_id == -1) 773 return 1; 774 775 threads = shmat(shm_id, NULL, 0); 776 if (threads == (void *) -1) { 777 perror("shmat"); 778 return 1; 779 } 780 781 memset(threads, 0, max_jobs * sizeof(struct thread_data)); 782 atexit(free_shm); 783 return 0; 784} 785 786static void usage(const char *name) 787{ 788 printf("%s\n", fio_version_string); 789 printf("%s [options] [job options] <job file(s)>\n", name); 790 printf("\t--debug=options\tEnable debug logging\n"); 791 printf("\t--output\tWrite output to file\n"); 792 printf("\t--timeout\tRuntime in seconds\n"); 793 printf("\t--latency-log\tGenerate per-job latency logs\n"); 794 printf("\t--bandwidth-log\tGenerate per-job bandwidth logs\n"); 795 printf("\t--minimal\tMinimal (terse) output\n"); 796 printf("\t--version\tPrint version info and exit\n"); 797 printf("\t--help\t\tPrint this page\n"); 798 printf("\t--cmdhelp=cmd\tPrint command help, \"all\" for all of them\n"); 799 printf("\t--showcmd\tTurn a job file into command line options\n"); 800 printf("\t--eta=when\tWhen ETA estimate should be printed\n"); 801 printf("\t \tMay be \"always\", \"never\" or \"auto\"\n"); 802 printf("\t--readonly\tTurn on safety read-only checks, preventing writes\n"); 803 printf("\t--section=name\tOnly run specified section in job file\n"); 804} 805 806#ifdef FIO_INC_DEBUG 807struct debug_level debug_levels[] = { 808 { .name = "process", .shift = FD_PROCESS, }, 809 { .name = "file", .shift = FD_FILE, }, 810 { .name = "io", .shift = FD_IO, }, 811 { .name = "mem", .shift = FD_MEM, }, 812 { .name = "blktrace", .shift = FD_BLKTRACE }, 813 { .name = "verify", .shift = FD_VERIFY }, 814 { .name = "random", .shift = FD_RANDOM }, 815 { }, 816}; 817 818static int set_debug(const char *string) 819{ 820 struct debug_level *dl; 821 char *p = (char *) string; 822 char *opt; 823 int i; 824 825 if (!strcmp(string, "?") || !strcmp(string, "help")) { 826 int i; 827 828 log_info("fio: dumping debug options:"); 829 for (i = 0; debug_levels[i].name; i++) { 830 dl = &debug_levels[i]; 831 log_info("%s,", dl->name); 832 } 833 log_info("all\n"); 834 return 1; 835 } else if (!strcmp(string, "all")) { 836 fio_debug = ~0UL; 837 return 0; 838 } 839 840 while ((opt = strsep(&p, ",")) != NULL) { 841 int found = 0; 842 843 for (i = 0; debug_levels[i].name; i++) { 844 dl = &debug_levels[i]; 845 if (!strncmp(opt, dl->name, strlen(opt))) { 846 log_info("fio: set debug option %s\n", opt); 847 found = 1; 848 fio_debug |= (1UL << dl->shift); 849 break; 850 } 851 } 852 853 if (!found) 854 log_err("fio: debug mask %s not found\n", opt); 855 } 856 return 0; 857} 858#else 859static void set_debug(const char *string) 860{ 861 log_err("fio: debug tracing not included in build\n"); 862 return 1; 863} 864#endif 865 866static int parse_cmd_line(int argc, char *argv[]) 867{ 868 struct thread_data *td = NULL; 869 int c, ini_idx = 0, lidx, ret = 0, do_exit = 0, exit_val = 0; 870 871 while ((c = getopt_long_only(argc, argv, "", long_options, &lidx)) != -1) { 872 switch (c) { 873 case 't': 874 def_timeout = atoi(optarg); 875 break; 876 case 'l': 877 write_lat_log = 1; 878 break; 879 case 'w': 880 write_bw_log = 1; 881 break; 882 case 'o': 883 f_out = fopen(optarg, "w+"); 884 if (!f_out) { 885 perror("fopen output"); 886 exit(1); 887 } 888 f_err = f_out; 889 break; 890 case 'm': 891 terse_output = 1; 892 break; 893 case 'h': 894 usage(argv[0]); 895 exit(0); 896 case 'c': 897 exit(fio_show_option_help(optarg)); 898 case 's': 899 dump_cmdline = 1; 900 break; 901 case 'r': 902 read_only = 1; 903 break; 904 case 'v': 905 printf("%s\n", fio_version_string); 906 exit(0); 907 case 'e': 908 if (!strcmp("always", optarg)) 909 eta_print = FIO_ETA_ALWAYS; 910 else if (!strcmp("never", optarg)) 911 eta_print = FIO_ETA_NEVER; 912 break; 913 case 'd': 914 if (set_debug(optarg)) 915 do_exit++; 916 break; 917 case 'x': 918 if (!strcmp(optarg, "global")) { 919 log_err("fio: can't use global as only section\n"); 920 do_exit++; 921 exit_val = 1; 922 break; 923 } 924 if (job_section) 925 free(job_section); 926 job_section = strdup(optarg); 927 break; 928 case FIO_GETOPT_JOB: { 929 const char *opt = long_options[lidx].name; 930 char *val = optarg; 931 932 if (!strncmp(opt, "name", 4) && td) { 933 ret = add_job(td, td->o.name ?: "fio", 0); 934 if (ret) { 935 put_job(td); 936 return 0; 937 } 938 td = NULL; 939 } 940 if (!td) { 941 int is_section = !strncmp(opt, "name", 4); 942 int global = 0; 943 944 if (!is_section || !strncmp(val, "global", 6)) 945 global = 1; 946 947 if (is_section && skip_this_section(val)) 948 continue; 949 950 td = get_new_job(global, &def_thread); 951 if (!td) 952 return 0; 953 } 954 955 ret = fio_cmd_option_parse(td, opt, val); 956 break; 957 } 958 default: 959 do_exit++; 960 exit_val = 1; 961 break; 962 } 963 } 964 965 if (do_exit) 966 exit(exit_val); 967 968 if (td) { 969 if (!ret) 970 ret = add_job(td, td->o.name ?: "fio", 0); 971 if (ret) 972 put_job(td); 973 } 974 975 while (optind < argc) { 976 ini_idx++; 977 ini_file = realloc(ini_file, ini_idx * sizeof(char *)); 978 ini_file[ini_idx - 1] = strdup(argv[optind]); 979 optind++; 980 } 981 982 return ini_idx; 983} 984 985int parse_options(int argc, char *argv[]) 986{ 987 int job_files, i; 988 989 f_out = stdout; 990 f_err = stderr; 991 992 fio_options_dup_and_init(long_options); 993 994 if (setup_thread_area()) 995 return 1; 996 if (fill_def_thread()) 997 return 1; 998 999 job_files = parse_cmd_line(argc, argv); 1000 1001 for (i = 0; i < job_files; i++) { 1002 if (fill_def_thread()) 1003 return 1; 1004 if (parse_jobs_ini(ini_file[i], i)) 1005 return 1; 1006 free(ini_file[i]); 1007 } 1008 1009 free(ini_file); 1010 options_mem_free(&def_thread); 1011 1012 if (!thread_number) { 1013 if (dump_cmdline) 1014 return 0; 1015 1016 log_err("No jobs defined(s)\n"); 1017 usage(argv[0]); 1018 return 1; 1019 } 1020 1021 return 0; 1022} 1023