init.c revision 890df538b3645d9310a3527513195773130d1d7f
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 <assert.h> 13#include <sys/ipc.h> 14#include <sys/shm.h> 15#include <sys/types.h> 16#include <sys/stat.h> 17 18#include "fio.h" 19#include "parse.h" 20 21#define FIO_RANDSEED (0xb1899bedUL) 22 23#define td_var_offset(var) ((size_t) &((struct thread_data *)0)->var) 24 25static int str_rw_cb(void *, const char *); 26static int str_ioengine_cb(void *, const char *); 27static int str_mem_cb(void *, const char *); 28static int str_verify_cb(void *, const char *); 29static int str_lockmem_cb(void *, unsigned long *); 30#ifdef FIO_HAVE_IOPRIO 31static int str_prio_cb(void *, unsigned int *); 32static int str_prioclass_cb(void *, unsigned int *); 33#endif 34static int str_exitall_cb(void); 35static int str_cpumask_cb(void *, unsigned int *); 36 37#define __stringify_1(x) #x 38#define __stringify(x) __stringify_1(x) 39 40/* 41 * Map of job/command line options 42 */ 43static struct fio_option options[] = { 44 { 45 .name = "description", 46 .type = FIO_OPT_STR_STORE, 47 .off1 = td_var_offset(description), 48 .help = "Text job description", 49 }, 50 { 51 .name = "name", 52 .type = FIO_OPT_STR_STORE, 53 .off1 = td_var_offset(name), 54 .help = "Name of this job", 55 }, 56 { 57 .name = "directory", 58 .type = FIO_OPT_STR_STORE, 59 .off1 = td_var_offset(directory), 60 .help = "Directory to store files in", 61 }, 62 { 63 .name = "filename", 64 .type = FIO_OPT_STR_STORE, 65 .off1 = td_var_offset(filename), 66 .help = "Force the use of a specific file", 67 }, 68 { 69 .name = "rw", 70 .type = FIO_OPT_STR, 71 .cb = str_rw_cb, 72 .help = "IO direction", 73 .def = "read", 74 .posval = { "read", "write", "randwrite", "randread", "rw", 75 "randrw", }, 76 }, 77 { 78 .name = "ioengine", 79 .type = FIO_OPT_STR, 80 .cb = str_ioengine_cb, 81 .help = "IO engine to use", 82 .def = "sync", 83 .posval = { "sync", "libaio", "posixaio", "mmap", "splice", 84 "sg", "null", "net", "syslet-rw" }, 85 }, 86 { 87 .name = "iodepth", 88 .type = FIO_OPT_INT, 89 .off1 = td_var_offset(iodepth), 90 .help = "Amount of IO buffers to keep in flight", 91 .def = "1", 92 }, 93 { 94 .name = "size", 95 .type = FIO_OPT_STR_VAL, 96 .off1 = td_var_offset(total_file_size), 97 .help = "Size of device or file", 98 }, 99 { 100 .name = "bs", 101 .type = FIO_OPT_STR_VAL_INT, 102 .off1 = td_var_offset(bs[DDIR_READ]), 103 .off2 = td_var_offset(bs[DDIR_WRITE]), 104 .help = "Block size unit", 105 .def = "4k", 106 }, 107 { 108 .name = "bsrange", 109 .type = FIO_OPT_RANGE, 110 .off1 = td_var_offset(min_bs[DDIR_READ]), 111 .off2 = td_var_offset(max_bs[DDIR_READ]), 112 .off3 = td_var_offset(min_bs[DDIR_WRITE]), 113 .off4 = td_var_offset(max_bs[DDIR_WRITE]), 114 .help = "Set block size range (in more detail than bs)", 115 }, 116 { 117 .name = "bs_unaligned", 118 .type = FIO_OPT_STR_SET, 119 .off1 = td_var_offset(bs_unaligned), 120 .help = "Don't sector align IO buffer sizes", 121 }, 122 { 123 .name = "offset", 124 .type = FIO_OPT_STR_VAL, 125 .off1 = td_var_offset(start_offset), 126 .help = "Start IO from this offset", 127 .def = "0", 128 }, 129 { 130 .name = "randrepeat", 131 .type = FIO_OPT_BOOL, 132 .off1 = td_var_offset(rand_repeatable), 133 .help = "Use repeatable random IO pattern", 134 .def = "1", 135 }, 136 { 137 .name = "norandommap", 138 .type = FIO_OPT_STR_SET, 139 .off1 = td_var_offset(norandommap), 140 .help = "Accept potential duplicate random blocks", 141 }, 142 { 143 .name = "nrfiles", 144 .type = FIO_OPT_INT, 145 .off1 = td_var_offset(nr_files), 146 .help = "Split job workload between this number of files", 147 .def = "1", 148 }, 149 { 150 .name = "fsync", 151 .type = FIO_OPT_INT, 152 .off1 = td_var_offset(fsync_blocks), 153 .help = "Issue fsync for writes every given number of blocks", 154 .def = "0", 155 }, 156 { 157 .name = "direct", 158 .type = FIO_OPT_BOOL, 159 .off1 = td_var_offset(odirect), 160 .help = "Use O_DIRECT IO (negates buffered)", 161 .def = "0", 162 }, 163 { 164 .name = "buffered", 165 .type = FIO_OPT_BOOL, 166 .off1 = td_var_offset(odirect), 167 .neg = 1, 168 .help = "Use buffered IO (negates direct)", 169 .def = "1", 170 }, 171 { 172 .name = "overwrite", 173 .type = FIO_OPT_BOOL, 174 .off1 = td_var_offset(overwrite), 175 .help = "When writing, set whether to overwrite current data", 176 .def = "0", 177 }, 178 { 179 .name = "loops", 180 .type = FIO_OPT_INT, 181 .off1 = td_var_offset(loops), 182 .help = "Number of times to run the job", 183 .def = "1", 184 }, 185 { 186 .name = "numjobs", 187 .type = FIO_OPT_INT, 188 .off1 = td_var_offset(numjobs), 189 .help = "Duplicate this job this many times", 190 .def = "1", 191 }, 192 { 193 .name = "startdelay", 194 .type = FIO_OPT_INT, 195 .off1 = td_var_offset(start_delay), 196 .help = "Only start job when this period has passed", 197 .def = "0", 198 }, 199 { 200 .name = "runtime", 201 .alias = "timeout", 202 .type = FIO_OPT_STR_VAL_TIME, 203 .off1 = td_var_offset(timeout), 204 .help = "Stop workload when this amount of time has passed", 205 .def = "0", 206 }, 207 { 208 .name = "mem", 209 .type = FIO_OPT_STR, 210 .cb = str_mem_cb, 211 .help = "Backing type for IO buffers", 212 .def = "malloc", 213 .posval = { "malloc", "shm", "shmhuge", "mmap", "mmaphuge", }, 214 }, 215 { 216 .name = "verify", 217 .type = FIO_OPT_STR, 218 .cb = str_verify_cb, 219 .help = "Verify sum function", 220 .def = "0", 221 .posval = { "crc32", "md5", }, 222 }, 223 { 224 .name = "write_iolog", 225 .type = FIO_OPT_STR_STORE, 226 .off1 = td_var_offset(write_iolog_file), 227 .help = "Store IO pattern to file", 228 }, 229 { 230 .name = "read_iolog", 231 .type = FIO_OPT_STR_STORE, 232 .off1 = td_var_offset(read_iolog_file), 233 .help = "Playback IO pattern from file", 234 }, 235 { 236 .name = "exec_prerun", 237 .type = FIO_OPT_STR_STORE, 238 .off1 = td_var_offset(exec_prerun), 239 .help = "Execute this file prior to running job", 240 }, 241 { 242 .name = "exec_postrun", 243 .type = FIO_OPT_STR_STORE, 244 .off1 = td_var_offset(exec_postrun), 245 .help = "Execute this file after running job", 246 }, 247#ifdef FIO_HAVE_IOSCHED_SWITCH 248 { 249 .name = "ioscheduler", 250 .type = FIO_OPT_STR_STORE, 251 .off1 = td_var_offset(ioscheduler), 252 .help = "Use this IO scheduler on the backing device", 253 }, 254#endif 255 { 256 .name = "zonesize", 257 .type = FIO_OPT_STR_VAL, 258 .off1 = td_var_offset(zone_size), 259 .help = "Give size of an IO zone", 260 .def = "0", 261 }, 262 { 263 .name = "zoneskip", 264 .type = FIO_OPT_STR_VAL, 265 .off1 = td_var_offset(zone_skip), 266 .help = "Space between IO zones", 267 .def = "0", 268 }, 269 { 270 .name = "lockmem", 271 .type = FIO_OPT_STR_VAL, 272 .cb = str_lockmem_cb, 273 .help = "Lock down this amount of memory", 274 .def = "0", 275 }, 276 { 277 .name = "rwmixcycle", 278 .type = FIO_OPT_INT, 279 .off1 = td_var_offset(rwmixcycle), 280 .help = "Cycle period for mixed read/write workloads (msec)", 281 .def = "500", 282 }, 283 { 284 .name = "rwmixread", 285 .type = FIO_OPT_INT, 286 .off1 = td_var_offset(rwmixread), 287 .maxval = 100, 288 .help = "Percentage of mixed workload that is reads", 289 .def = "50", 290 }, 291 { 292 .name = "rwmixwrite", 293 .type = FIO_OPT_INT, 294 .off1 = td_var_offset(rwmixwrite), 295 .maxval = 100, 296 .help = "Percentage of mixed workload that is writes", 297 .def = "50", 298 }, 299 { 300 .name = "nice", 301 .type = FIO_OPT_INT, 302 .off1 = td_var_offset(nice), 303 .help = "Set job CPU nice value", 304 .minval = -19, 305 .maxval = 20, 306 .def = "0", 307 }, 308#ifdef FIO_HAVE_IOPRIO 309 { 310 .name = "prio", 311 .type = FIO_OPT_INT, 312 .cb = str_prio_cb, 313 .help = "Set job IO priority value", 314 .minval = 0, 315 .maxval = 7, 316 }, 317 { 318 .name = "prioclass", 319 .type = FIO_OPT_INT, 320 .cb = str_prioclass_cb, 321 .help = "Set job IO priority class", 322 .minval = 0, 323 .maxval = 3, 324 }, 325#endif 326 { 327 .name = "thinktime", 328 .type = FIO_OPT_INT, 329 .off1 = td_var_offset(thinktime), 330 .help = "Idle time between IO buffers (usec)", 331 .def = "0", 332 }, 333 { 334 .name = "thinktime_blocks", 335 .type = FIO_OPT_INT, 336 .off1 = td_var_offset(thinktime_blocks), 337 .help = "IO buffer period between 'thinktime'", 338 .def = "1", 339 }, 340 { 341 .name = "rate", 342 .type = FIO_OPT_INT, 343 .off1 = td_var_offset(rate), 344 .help = "Set bandwidth rate", 345 }, 346 { 347 .name = "ratemin", 348 .type = FIO_OPT_INT, 349 .off1 = td_var_offset(ratemin), 350 .help = "The bottom limit accepted", 351 }, 352 { 353 .name = "ratecycle", 354 .type = FIO_OPT_INT, 355 .off1 = td_var_offset(ratecycle), 356 .help = "Window average for rate limits (msec)", 357 .def = "1000", 358 }, 359 { 360 .name = "invalidate", 361 .type = FIO_OPT_BOOL, 362 .off1 = td_var_offset(invalidate_cache), 363 .help = "Invalidate buffer/page cache prior to running job", 364 .def = "1", 365 }, 366 { 367 .name = "sync", 368 .type = FIO_OPT_BOOL, 369 .off1 = td_var_offset(sync_io), 370 .help = "Use O_SYNC for buffered writes", 371 .def = "0", 372 }, 373 { 374 .name = "bwavgtime", 375 .type = FIO_OPT_INT, 376 .off1 = td_var_offset(bw_avg_time), 377 .help = "Time window over which to calculate bandwidth (msec)", 378 .def = "500", 379 }, 380 { 381 .name = "create_serialize", 382 .type = FIO_OPT_BOOL, 383 .off1 = td_var_offset(create_serialize), 384 .help = "Serialize creating of job files", 385 .def = "1", 386 }, 387 { 388 .name = "create_fsync", 389 .type = FIO_OPT_BOOL, 390 .off1 = td_var_offset(create_fsync), 391 .help = "Fsync file after creation", 392 .def = "1", 393 }, 394 { 395 .name = "cpuload", 396 .type = FIO_OPT_INT, 397 .off1 = td_var_offset(cpuload), 398 .help = "Use this percentage of CPU", 399 }, 400 { 401 .name = "cpuchunks", 402 .type = FIO_OPT_INT, 403 .off1 = td_var_offset(cpucycle), 404 .help = "Length of the CPU burn cycles", 405 }, 406#ifdef FIO_HAVE_CPU_AFFINITY 407 { 408 .name = "cpumask", 409 .type = FIO_OPT_INT, 410 .cb = str_cpumask_cb, 411 .help = "CPU affinity mask", 412 }, 413#endif 414 { 415 .name = "end_fsync", 416 .type = FIO_OPT_BOOL, 417 .off1 = td_var_offset(end_fsync), 418 .help = "Include fsync at the end of job", 419 .def = "0", 420 }, 421 { 422 .name = "unlink", 423 .type = FIO_OPT_BOOL, 424 .off1 = td_var_offset(unlink), 425 .help = "Unlink created files after job has completed", 426 .def = "0", 427 }, 428 { 429 .name = "exitall", 430 .type = FIO_OPT_STR_SET, 431 .cb = str_exitall_cb, 432 .help = "Terminate all jobs when one exits", 433 }, 434 { 435 .name = "stonewall", 436 .type = FIO_OPT_STR_SET, 437 .off1 = td_var_offset(stonewall), 438 .help = "Insert a hard barrier between this job and previous", 439 }, 440 { 441 .name = "thread", 442 .type = FIO_OPT_STR_SET, 443 .off1 = td_var_offset(use_thread), 444 .help = "Use threads instead of forks", 445 }, 446 { 447 .name = "write_bw_log", 448 .type = FIO_OPT_STR_SET, 449 .off1 = td_var_offset(write_bw_log), 450 .help = "Write log of bandwidth during run", 451 }, 452 { 453 .name = "write_lat_log", 454 .type = FIO_OPT_STR_SET, 455 .off1 = td_var_offset(write_lat_log), 456 .help = "Write log of latency during run", 457 }, 458 { 459 .name = "hugepage-size", 460 .type = FIO_OPT_STR_VAL, 461 .off1 = td_var_offset(hugepage_size), 462 .help = "When using hugepages, specify size of each page", 463 .def = __stringify(FIO_HUGE_PAGE), 464 }, 465 { 466 .name = NULL, 467 }, 468}; 469 470#define FIO_JOB_OPTS (sizeof(options) / sizeof(struct fio_option)) 471#define FIO_CMD_OPTS (16) 472#define FIO_GETOPT_JOB (0x89988998) 473 474/* 475 * Command line options. These will contain the above, plus a few 476 * extra that only pertain to fio itself and not jobs. 477 */ 478static struct option long_options[FIO_JOB_OPTS + FIO_CMD_OPTS] = { 479 { 480 .name = "output", 481 .has_arg = required_argument, 482 .val = 'o', 483 }, 484 { 485 .name = "timeout", 486 .has_arg = required_argument, 487 .val = 't', 488 }, 489 { 490 .name = "latency-log", 491 .has_arg = required_argument, 492 .val = 'l', 493 }, 494 { 495 .name = "bandwidth-log", 496 .has_arg = required_argument, 497 .val = 'b', 498 }, 499 { 500 .name = "minimal", 501 .has_arg = optional_argument, 502 .val = 'm', 503 }, 504 { 505 .name = "version", 506 .has_arg = no_argument, 507 .val = 'v', 508 }, 509 { 510 .name = "help", 511 .has_arg = no_argument, 512 .val = 'h', 513 }, 514 { 515 .name = "cmdhelp", 516 .has_arg = required_argument, 517 .val = 'c', 518 }, 519 { 520 .name = NULL, 521 }, 522}; 523 524static int def_timeout = 0; 525 526static char fio_version_string[] = "fio 1.11"; 527 528static char **ini_file; 529static int max_jobs = MAX_JOBS; 530 531struct thread_data def_thread; 532struct thread_data *threads = NULL; 533 534int exitall_on_terminate = 0; 535int terse_output = 0; 536unsigned long long mlock_size = 0; 537FILE *f_out = NULL; 538FILE *f_err = NULL; 539 540static int write_lat_log = 0; 541int write_bw_log = 0; 542 543/* 544 * Return a free job structure. 545 */ 546static struct thread_data *get_new_job(int global, struct thread_data *parent) 547{ 548 struct thread_data *td; 549 550 if (global) 551 return &def_thread; 552 if (thread_number >= max_jobs) 553 return NULL; 554 555 td = &threads[thread_number++]; 556 *td = *parent; 557 558 td->thread_number = thread_number; 559 return td; 560} 561 562static void put_job(struct thread_data *td) 563{ 564 if (td == &def_thread) 565 return; 566 567 if (td->error) 568 fprintf(f_out, "fio: %s\n", td->verror); 569 570 memset(&threads[td->thread_number - 1], 0, sizeof(*td)); 571 thread_number--; 572} 573 574/* 575 * Lazy way of fixing up options that depend on each other. We could also 576 * define option callback handlers, but this is easier. 577 */ 578static void fixup_options(struct thread_data *td) 579{ 580 if (!td->rwmixread && td->rwmixwrite) 581 td->rwmixread = 100 - td->rwmixwrite; 582 583 if (td->write_iolog_file && td->read_iolog_file) { 584 log_err("fio: read iolog overrides write_iolog\n"); 585 free(td->write_iolog_file); 586 td->write_iolog_file = NULL; 587 } 588 589 if (td->io_ops->flags & FIO_SYNCIO) 590 td->iodepth = 1; 591 else { 592 if (!td->iodepth) 593 td->iodepth = td->nr_files; 594 } 595 596 /* 597 * only really works for sequential io for now, and with 1 file 598 */ 599 if (td->zone_size && !td->sequential && td->nr_files == 1) 600 td->zone_size = 0; 601 602 /* 603 * Reads can do overwrites, we always need to pre-create the file 604 */ 605 if (td_read(td) || td_rw(td)) 606 td->overwrite = 1; 607 608 if (!td->min_bs[DDIR_READ]) 609 td->min_bs[DDIR_READ]= td->bs[DDIR_READ]; 610 if (!td->max_bs[DDIR_READ]) 611 td->max_bs[DDIR_READ] = td->bs[DDIR_READ]; 612 if (!td->min_bs[DDIR_WRITE]) 613 td->min_bs[DDIR_WRITE]= td->bs[DDIR_WRITE]; 614 if (!td->max_bs[DDIR_WRITE]) 615 td->max_bs[DDIR_WRITE] = td->bs[DDIR_WRITE]; 616 617 td->rw_min_bs = min(td->min_bs[DDIR_READ], td->min_bs[DDIR_WRITE]); 618 619 if (td_read(td) && !td_rw(td)) 620 td->verify = 0; 621 622 if (td->norandommap && td->verify != VERIFY_NONE) { 623 log_err("fio: norandommap given, verify disabled\n"); 624 td->verify = VERIFY_NONE; 625 } 626 if (td->bs_unaligned && (td->odirect || td->io_ops->flags & FIO_RAWIO)) 627 log_err("fio: bs_unaligned may not work with raw io\n"); 628 629 /* 630 * O_DIRECT and char doesn't mix, clear that flag if necessary. 631 */ 632 if (td->filetype == FIO_TYPE_CHAR && td->odirect) 633 td->odirect = 0; 634} 635 636/* 637 * This function leaks the buffer 638 */ 639static char *to_kmg(unsigned int val) 640{ 641 char *buf = malloc(32); 642 char post[] = { 0, 'K', 'M', 'G', 'P', 'E', 0 }; 643 char *p = post; 644 645 do { 646 if (val & 1023) 647 break; 648 649 val >>= 10; 650 p++; 651 } while (*p); 652 653 snprintf(buf, 31, "%u%c", val, *p); 654 return buf; 655} 656 657/* 658 * Adds a job to the list of things todo. Sanitizes the various options 659 * to make sure we don't have conflicts, and initializes various 660 * members of td. 661 */ 662static int add_job(struct thread_data *td, const char *jobname, int job_add_num) 663{ 664 const char *ddir_str[] = { "read", "write", "randread", "randwrite", 665 "rw", NULL, "randrw" }; 666 struct stat sb; 667 int numjobs, ddir, i; 668 struct fio_file *f; 669 670 /* 671 * the def_thread is just for options, it's not a real job 672 */ 673 if (td == &def_thread) 674 return 0; 675 676 assert(td->io_ops); 677 678 if (td->odirect) 679 td->io_ops->flags |= FIO_RAWIO; 680 681 td->filetype = FIO_TYPE_FILE; 682 if (td->filename && !lstat(td->filename, &sb)) { 683 if (S_ISBLK(sb.st_mode)) 684 td->filetype = FIO_TYPE_BD; 685 else if (S_ISCHR(sb.st_mode)) 686 td->filetype = FIO_TYPE_CHAR; 687 } 688 689 fixup_options(td); 690 691 if (td->filename) 692 td->nr_uniq_files = 1; 693 else 694 td->nr_uniq_files = td->nr_files; 695 696 if (td->filetype == FIO_TYPE_FILE || td->filename) { 697 char tmp[PATH_MAX]; 698 int len = 0; 699 700 if (td->directory && td->directory[0] != '\0') { 701 if (lstat(td->directory, &sb) < 0) { 702 log_err("fio: %s is not a directory\n", td->directory); 703 td_verror(td, errno); 704 return 1; 705 } 706 if (!S_ISDIR(sb.st_mode)) { 707 log_err("fio: %s is not a directory\n", td->directory); 708 return 1; 709 } 710 len = sprintf(tmp, "%s/", td->directory); 711 } 712 713 td->files = malloc(sizeof(struct fio_file) * td->nr_files); 714 715 for_each_file(td, f, i) { 716 memset(f, 0, sizeof(*f)); 717 f->fd = -1; 718 719 if (td->filename) 720 sprintf(tmp + len, "%s", td->filename); 721 else 722 sprintf(tmp + len, "%s.%d.%d", jobname, td->thread_number, i); 723 f->file_name = strdup(tmp); 724 } 725 } else { 726 td->nr_files = 1; 727 td->files = malloc(sizeof(struct fio_file)); 728 f = &td->files[0]; 729 730 memset(f, 0, sizeof(*f)); 731 f->fd = -1; 732 f->file_name = strdup(jobname); 733 } 734 735 for_each_file(td, f, i) { 736 f->file_size = td->total_file_size / td->nr_files; 737 f->file_offset = td->start_offset; 738 } 739 740 fio_sem_init(&td->mutex, 0); 741 742 td->clat_stat[0].min_val = td->clat_stat[1].min_val = ULONG_MAX; 743 td->slat_stat[0].min_val = td->slat_stat[1].min_val = ULONG_MAX; 744 td->bw_stat[0].min_val = td->bw_stat[1].min_val = ULONG_MAX; 745 746 if (td->stonewall && td->thread_number > 1) 747 groupid++; 748 749 td->groupid = groupid; 750 751 if (setup_rate(td)) 752 goto err; 753 754 if (td->write_lat_log) { 755 setup_log(&td->slat_log); 756 setup_log(&td->clat_log); 757 } 758 if (td->write_bw_log) 759 setup_log(&td->bw_log); 760 761 if (!td->name) 762 td->name = strdup(jobname); 763 764 ddir = td->ddir + (!td->sequential << 1) + (td->iomix << 2); 765 766 if (!terse_output) { 767 if (!job_add_num) { 768 if (td->io_ops->flags & FIO_CPUIO) 769 fprintf(f_out, "%s: ioengine=cpu, cpuload=%u, cpucycle=%u\n", td->name, td->cpuload, td->cpucycle); 770 else { 771 char *c1, *c2, *c3, *c4; 772 773 c1 = to_kmg(td->min_bs[DDIR_READ]); 774 c2 = to_kmg(td->max_bs[DDIR_READ]); 775 c3 = to_kmg(td->min_bs[DDIR_WRITE]); 776 c4 = to_kmg(td->max_bs[DDIR_WRITE]); 777 778 fprintf(f_out, "%s: (g=%d): rw=%s, bs=%s-%s/%s-%s, ioengine=%s, iodepth=%u\n", td->name, td->groupid, ddir_str[ddir], c1, c2, c3, c4, td->io_ops->name, td->iodepth); 779 780 free(c1); 781 free(c2); 782 free(c3); 783 free(c4); 784 } 785 } else if (job_add_num == 1) 786 fprintf(f_out, "...\n"); 787 } 788 789 /* 790 * recurse add identical jobs, clear numjobs and stonewall options 791 * as they don't apply to sub-jobs 792 */ 793 numjobs = td->numjobs; 794 while (--numjobs) { 795 struct thread_data *td_new = get_new_job(0, td); 796 797 if (!td_new) 798 goto err; 799 800 td_new->numjobs = 1; 801 td_new->stonewall = 0; 802 job_add_num = numjobs - 1; 803 804 if (add_job(td_new, jobname, job_add_num)) 805 goto err; 806 } 807 return 0; 808err: 809 put_job(td); 810 return -1; 811} 812 813/* 814 * Initialize the various random states we need (random io, block size ranges, 815 * read/write mix, etc). 816 */ 817int init_random_state(struct thread_data *td) 818{ 819 unsigned long seeds[4]; 820 int fd, num_maps, blocks, i; 821 struct fio_file *f; 822 823 if (td->io_ops->flags & FIO_CPUIO) 824 return 0; 825 826 fd = open("/dev/urandom", O_RDONLY); 827 if (fd == -1) { 828 td_verror(td, errno); 829 return 1; 830 } 831 832 if (read(fd, seeds, sizeof(seeds)) < (int) sizeof(seeds)) { 833 td_verror(td, EIO); 834 close(fd); 835 return 1; 836 } 837 838 close(fd); 839 840 os_random_seed(seeds[0], &td->bsrange_state); 841 os_random_seed(seeds[1], &td->verify_state); 842 os_random_seed(seeds[2], &td->rwmix_state); 843 844 if (td->sequential) 845 return 0; 846 847 if (td->rand_repeatable) 848 seeds[3] = FIO_RANDSEED; 849 850 if (!td->norandommap) { 851 for_each_file(td, f, i) { 852 blocks = (f->real_file_size + td->rw_min_bs - 1) / td->rw_min_bs; 853 num_maps = (blocks + BLOCKS_PER_MAP-1)/ BLOCKS_PER_MAP; 854 f->file_map = malloc(num_maps * sizeof(long)); 855 f->num_maps = num_maps; 856 memset(f->file_map, 0, num_maps * sizeof(long)); 857 } 858 } 859 860 os_random_seed(seeds[3], &td->random_state); 861 return 0; 862} 863 864static void fill_cpu_mask(os_cpu_mask_t cpumask, int cpu) 865{ 866#ifdef FIO_HAVE_CPU_AFFINITY 867 unsigned int i; 868 869 CPU_ZERO(&cpumask); 870 871 for (i = 0; i < sizeof(int) * 8; i++) { 872 if ((1 << i) & cpu) 873 CPU_SET(i, &cpumask); 874 } 875#endif 876} 877 878static int is_empty_or_comment(char *line) 879{ 880 unsigned int i; 881 882 for (i = 0; i < strlen(line); i++) { 883 if (line[i] == ';') 884 return 1; 885 if (!isspace(line[i]) && !iscntrl(line[i])) 886 return 0; 887 } 888 889 return 1; 890} 891 892static int str_rw_cb(void *data, const char *mem) 893{ 894 struct thread_data *td = data; 895 896 if (!strncmp(mem, "read", 4) || !strncmp(mem, "0", 1)) { 897 td->ddir = DDIR_READ; 898 td->sequential = 1; 899 return 0; 900 } else if (!strncmp(mem, "randread", 8)) { 901 td->ddir = DDIR_READ; 902 td->sequential = 0; 903 return 0; 904 } else if (!strncmp(mem, "write", 5) || !strncmp(mem, "1", 1)) { 905 td->ddir = DDIR_WRITE; 906 td->sequential = 1; 907 return 0; 908 } else if (!strncmp(mem, "randwrite", 9)) { 909 td->ddir = DDIR_WRITE; 910 td->sequential = 0; 911 return 0; 912 } else if (!strncmp(mem, "rw", 2)) { 913 td->ddir = DDIR_READ; 914 td->iomix = 1; 915 td->sequential = 1; 916 return 0; 917 } else if (!strncmp(mem, "randrw", 6)) { 918 td->ddir = DDIR_READ; 919 td->iomix = 1; 920 td->sequential = 0; 921 return 0; 922 } 923 924 log_err("fio: data direction: read, write, randread, randwrite, rw, randrw\n"); 925 return 1; 926} 927 928static int str_verify_cb(void *data, const char *mem) 929{ 930 struct thread_data *td = data; 931 932 if (!strncmp(mem, "0", 1)) { 933 td->verify = VERIFY_NONE; 934 return 0; 935 } else if (!strncmp(mem, "md5", 3) || !strncmp(mem, "1", 1)) { 936 td->verify = VERIFY_MD5; 937 return 0; 938 } else if (!strncmp(mem, "crc32", 5)) { 939 td->verify = VERIFY_CRC32; 940 return 0; 941 } 942 943 log_err("fio: verify types: md5, crc32\n"); 944 return 1; 945} 946 947/* 948 * Check if mmap/mmaphuge has a :/foo/bar/file at the end. If so, return that. 949 */ 950static char *get_mmap_file(const char *str) 951{ 952 char *p = strstr(str, ":"); 953 954 if (!p) 955 return NULL; 956 957 p++; 958 strip_blank_front(&p); 959 strip_blank_end(p); 960 return strdup(p); 961} 962 963static int str_mem_cb(void *data, const char *mem) 964{ 965 struct thread_data *td = data; 966 967 if (!strncmp(mem, "malloc", 6)) { 968 td->mem_type = MEM_MALLOC; 969 return 0; 970 } else if (!strncmp(mem, "mmaphuge", 8)) { 971#ifdef FIO_HAVE_HUGETLB 972 /* 973 * mmaphuge must be appended with the actual file 974 */ 975 td->mmapfile = get_mmap_file(mem); 976 if (!td->mmapfile) { 977 log_err("fio: mmaphuge:/path/to/file\n"); 978 return 1; 979 } 980 981 td->mem_type = MEM_MMAPHUGE; 982 return 0; 983#else 984 log_err("fio: mmaphuge not available\n"); 985 return 1; 986#endif 987 } else if (!strncmp(mem, "mmap", 4)) { 988 /* 989 * Check if the user wants file backed memory. It's ok 990 * if there's no file given, we'll just use anon mamp then. 991 */ 992 td->mmapfile = get_mmap_file(mem); 993 td->mem_type = MEM_MMAP; 994 return 0; 995 } else if (!strncmp(mem, "shmhuge", 7)) { 996#ifdef FIO_HAVE_HUGETLB 997 td->mem_type = MEM_SHMHUGE; 998 return 0; 999#else 1000 log_err("fio: shmhuge not available\n"); 1001 return 1; 1002#endif 1003 } else if (!strncmp(mem, "shm", 3)) { 1004 td->mem_type = MEM_SHM; 1005 return 0; 1006 } 1007 1008 log_err("fio: mem type: malloc, shm, shmhuge, mmap, mmaphuge\n"); 1009 return 1; 1010} 1011 1012static int str_ioengine_cb(void *data, const char *str) 1013{ 1014 struct thread_data *td = data; 1015 1016 td->io_ops = load_ioengine(td, str); 1017 if (td->io_ops) 1018 return 0; 1019 1020 log_err("fio: ioengine= libaio, posixaio, sync, mmap, sgio, splice, cpu, null\n"); 1021 log_err("fio: or specify path to dynamic ioengine module\n"); 1022 return 1; 1023} 1024 1025static int str_lockmem_cb(void fio_unused *data, unsigned long *val) 1026{ 1027 mlock_size = *val; 1028 return 0; 1029} 1030 1031#ifdef FIO_HAVE_IOPRIO 1032static int str_prioclass_cb(void *data, unsigned int *val) 1033{ 1034 struct thread_data *td = data; 1035 1036 td->ioprio |= *val << IOPRIO_CLASS_SHIFT; 1037 return 0; 1038} 1039 1040static int str_prio_cb(void *data, unsigned int *val) 1041{ 1042 struct thread_data *td = data; 1043 1044 td->ioprio |= *val; 1045 return 0; 1046} 1047#endif 1048 1049static int str_exitall_cb(void) 1050{ 1051 exitall_on_terminate = 1; 1052 return 0; 1053} 1054 1055static int str_cpumask_cb(void *data, unsigned int *val) 1056{ 1057 struct thread_data *td = data; 1058 1059 fill_cpu_mask(td->cpumask, *val); 1060 return 0; 1061} 1062 1063/* 1064 * This is our [ini] type file parser. 1065 */ 1066static int parse_jobs_ini(char *file, int stonewall_flag) 1067{ 1068 unsigned int global; 1069 struct thread_data *td; 1070 char *string, *name; 1071 fpos_t off; 1072 FILE *f; 1073 char *p; 1074 int ret = 0, stonewall; 1075 1076 f = fopen(file, "r"); 1077 if (!f) { 1078 perror("fopen job file"); 1079 return 1; 1080 } 1081 1082 string = malloc(4096); 1083 name = malloc(256); 1084 memset(name, 0, 256); 1085 1086 stonewall = stonewall_flag; 1087 do { 1088 p = fgets(string, 4095, f); 1089 if (!p) 1090 break; 1091 if (is_empty_or_comment(p)) 1092 continue; 1093 if (sscanf(p, "[%255s]", name) != 1) 1094 continue; 1095 1096 global = !strncmp(name, "global", 6); 1097 1098 name[strlen(name) - 1] = '\0'; 1099 1100 td = get_new_job(global, &def_thread); 1101 if (!td) { 1102 ret = 1; 1103 break; 1104 } 1105 1106 /* 1107 * Seperate multiple job files by a stonewall 1108 */ 1109 if (!global && stonewall) { 1110 td->stonewall = stonewall; 1111 stonewall = 0; 1112 } 1113 1114 fgetpos(f, &off); 1115 while ((p = fgets(string, 4096, f)) != NULL) { 1116 if (is_empty_or_comment(p)) 1117 continue; 1118 1119 strip_blank_front(&p); 1120 1121 if (p[0] == '[') 1122 break; 1123 1124 strip_blank_end(p); 1125 1126 fgetpos(f, &off); 1127 1128 /* 1129 * Don't break here, continue parsing options so we 1130 * dump all the bad ones. Makes trial/error fixups 1131 * easier on the user. 1132 */ 1133 ret |= parse_option(p, options, td); 1134 } 1135 1136 if (!ret) { 1137 fsetpos(f, &off); 1138 ret = add_job(td, name, 0); 1139 } else { 1140 log_err("fio: job %s dropped\n", name); 1141 put_job(td); 1142 } 1143 } while (!ret); 1144 1145 free(string); 1146 free(name); 1147 fclose(f); 1148 return ret; 1149} 1150 1151static int fill_def_thread(void) 1152{ 1153 memset(&def_thread, 0, sizeof(def_thread)); 1154 1155 if (fio_getaffinity(getpid(), &def_thread.cpumask) == -1) { 1156 perror("sched_getaffinity"); 1157 return 1; 1158 } 1159 1160 /* 1161 * fill default options 1162 */ 1163 fill_default_options(&def_thread, options); 1164 1165 def_thread.timeout = def_timeout; 1166 def_thread.write_bw_log = write_bw_log; 1167 def_thread.write_lat_log = write_lat_log; 1168 1169#ifdef FIO_HAVE_DISK_UTIL 1170 def_thread.do_disk_util = 1; 1171#endif 1172 1173 return 0; 1174} 1175 1176static void usage(void) 1177{ 1178 printf("%s\n", fio_version_string); 1179 printf("\t--output\tWrite output to file\n"); 1180 printf("\t--timeout\tRuntime in seconds\n"); 1181 printf("\t--latency-log\tGenerate per-job latency logs\n"); 1182 printf("\t--bandwidth-log\tGenerate per-job bandwidth logs\n"); 1183 printf("\t--minimal\tMinimal (terse) output\n"); 1184 printf("\t--version\tPrint version info and exit\n"); 1185 printf("\t--help\t\tPrint this page\n"); 1186 printf("\t--cmdhelp=cmd\tPrint command help, \"all\" for all of them\n"); 1187} 1188 1189static int parse_cmd_line(int argc, char *argv[]) 1190{ 1191 struct thread_data *td = NULL; 1192 int c, ini_idx = 0, lidx, ret; 1193 1194 while ((c = getopt_long(argc, argv, "", long_options, &lidx)) != -1) { 1195 switch (c) { 1196 case 't': 1197 def_timeout = atoi(optarg); 1198 break; 1199 case 'l': 1200 write_lat_log = 1; 1201 break; 1202 case 'w': 1203 write_bw_log = 1; 1204 break; 1205 case 'o': 1206 f_out = fopen(optarg, "w+"); 1207 if (!f_out) { 1208 perror("fopen output"); 1209 exit(1); 1210 } 1211 f_err = f_out; 1212 break; 1213 case 'm': 1214 terse_output = 1; 1215 break; 1216 case 'h': 1217 usage(); 1218 exit(0); 1219 case 'c': 1220 ret = show_cmd_help(options, optarg); 1221 exit(ret); 1222 case 'v': 1223 printf("%s\n", fio_version_string); 1224 exit(0); 1225 case FIO_GETOPT_JOB: { 1226 const char *opt = long_options[lidx].name; 1227 char *val = optarg; 1228 1229 if (!strncmp(opt, "name", 4) && td) { 1230 ret = add_job(td, td->name ?: "fio", 0); 1231 if (ret) { 1232 put_job(td); 1233 return 0; 1234 } 1235 td = NULL; 1236 } 1237 if (!td) { 1238 int global = !strncmp(val, "global", 6); 1239 1240 td = get_new_job(global, &def_thread); 1241 if (!td) 1242 return 0; 1243 } 1244 1245 ret = parse_cmd_option(opt, val, options, td); 1246 if (ret) { 1247 log_err("fio: job dropped\n"); 1248 put_job(td); 1249 td = NULL; 1250 } 1251 break; 1252 } 1253 default: 1254 break; 1255 } 1256 } 1257 1258 if (td) { 1259 ret = add_job(td, td->name ?: "fio", 0); 1260 if (ret) 1261 put_job(td); 1262 } 1263 1264 while (optind < argc) { 1265 ini_idx++; 1266 ini_file = realloc(ini_file, ini_idx * sizeof(char *)); 1267 ini_file[ini_idx - 1] = strdup(argv[optind]); 1268 optind++; 1269 } 1270 1271 return ini_idx; 1272} 1273 1274static void free_shm(void) 1275{ 1276 struct shmid_ds sbuf; 1277 1278 if (threads) { 1279 shmdt((void *) threads); 1280 threads = NULL; 1281 shmctl(shm_id, IPC_RMID, &sbuf); 1282 } 1283} 1284 1285/* 1286 * The thread area is shared between the main process and the job 1287 * threads/processes. So setup a shared memory segment that will hold 1288 * all the job info. 1289 */ 1290static int setup_thread_area(void) 1291{ 1292 /* 1293 * 1024 is too much on some machines, scale max_jobs if 1294 * we get a failure that looks like too large a shm segment 1295 */ 1296 do { 1297 size_t size = max_jobs * sizeof(struct thread_data); 1298 1299 shm_id = shmget(0, size, IPC_CREAT | 0600); 1300 if (shm_id != -1) 1301 break; 1302 if (errno != EINVAL) { 1303 perror("shmget"); 1304 break; 1305 } 1306 1307 max_jobs >>= 1; 1308 } while (max_jobs); 1309 1310 if (shm_id == -1) 1311 return 1; 1312 1313 threads = shmat(shm_id, NULL, 0); 1314 if (threads == (void *) -1) { 1315 perror("shmat"); 1316 return 1; 1317 } 1318 1319 atexit(free_shm); 1320 return 0; 1321} 1322 1323/* 1324 * Copy the fio options into the long options map, so we mirror 1325 * job and cmd line options. 1326 */ 1327static void dupe_job_options(void) 1328{ 1329 struct fio_option *o; 1330 unsigned int i; 1331 1332 i = 0; 1333 while (long_options[i].name) 1334 i++; 1335 1336 o = &options[0]; 1337 while (o->name) { 1338 long_options[i].name = o->name; 1339 long_options[i].val = FIO_GETOPT_JOB; 1340 if (o->type == FIO_OPT_STR_SET) 1341 long_options[i].has_arg = no_argument; 1342 else 1343 long_options[i].has_arg = required_argument; 1344 1345 i++; 1346 o++; 1347 assert(i < FIO_JOB_OPTS + FIO_CMD_OPTS); 1348 } 1349} 1350 1351int parse_options(int argc, char *argv[]) 1352{ 1353 int job_files, i; 1354 1355 f_out = stdout; 1356 f_err = stderr; 1357 1358 options_init(options); 1359 1360 dupe_job_options(); 1361 1362 if (setup_thread_area()) 1363 return 1; 1364 if (fill_def_thread()) 1365 return 1; 1366 1367 job_files = parse_cmd_line(argc, argv); 1368 1369 for (i = 0; i < job_files; i++) { 1370 if (fill_def_thread()) 1371 return 1; 1372 if (parse_jobs_ini(ini_file[i], i)) 1373 return 1; 1374 free(ini_file[i]); 1375 } 1376 1377 free(ini_file); 1378 1379 if (!thread_number) { 1380 log_err("No jobs defined(s)\n"); 1381 return 1; 1382 } 1383 1384 return 0; 1385} 1386