init.c revision 61697c37566a211ad3fd70f46397d9b7675b0fc2
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", }, 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 memset(&threads[td->thread_number - 1], 0, sizeof(*td)); 568 thread_number--; 569} 570 571/* 572 * Lazy way of fixing up options that depend on each other. We could also 573 * define option callback handlers, but this is easier. 574 */ 575static void fixup_options(struct thread_data *td) 576{ 577 if (!td->rwmixread && td->rwmixwrite) 578 td->rwmixread = 100 - td->rwmixwrite; 579 580 if (td->write_iolog_file && td->read_iolog_file) { 581 log_err("fio: read iolog overrides write_iolog\n"); 582 free(td->write_iolog_file); 583 td->write_iolog_file = NULL; 584 } 585 586 if (td->io_ops->flags & FIO_SYNCIO) 587 td->iodepth = 1; 588 else { 589 if (!td->iodepth) 590 td->iodepth = td->nr_files; 591 } 592 593 /* 594 * only really works for sequential io for now, and with 1 file 595 */ 596 if (td->zone_size && !td->sequential && td->nr_files == 1) 597 td->zone_size = 0; 598 599 /* 600 * Reads can do overwrites, we always need to pre-create the file 601 */ 602 if (td_read(td) || td_rw(td)) 603 td->overwrite = 1; 604 605 if (!td->min_bs[DDIR_READ]) 606 td->min_bs[DDIR_READ]= td->bs[DDIR_READ]; 607 if (!td->max_bs[DDIR_READ]) 608 td->max_bs[DDIR_READ] = td->bs[DDIR_READ]; 609 if (!td->min_bs[DDIR_WRITE]) 610 td->min_bs[DDIR_WRITE]= td->bs[DDIR_WRITE]; 611 if (!td->max_bs[DDIR_WRITE]) 612 td->max_bs[DDIR_WRITE] = td->bs[DDIR_WRITE]; 613 614 td->rw_min_bs = min(td->min_bs[DDIR_READ], td->min_bs[DDIR_WRITE]); 615 616 if (td_read(td) && !td_rw(td)) 617 td->verify = 0; 618 619 if (td->norandommap && td->verify != VERIFY_NONE) { 620 log_err("fio: norandommap given, verify disabled\n"); 621 td->verify = VERIFY_NONE; 622 } 623 if (td->bs_unaligned && (td->odirect || td->io_ops->flags & FIO_RAWIO)) 624 log_err("fio: bs_unaligned may not work with raw io\n"); 625 626 /* 627 * O_DIRECT and char doesn't mix, clear that flag if necessary. 628 */ 629 if (td->filetype == FIO_TYPE_CHAR && td->odirect) 630 td->odirect = 0; 631} 632 633/* 634 * This function leaks the buffer 635 */ 636static char *to_kmg(unsigned int val) 637{ 638 char *buf = malloc(32); 639 char post[] = { 0, 'K', 'M', 'G', 'P', 0 }; 640 char *p = post; 641 642 do { 643 if (val & 1023) 644 break; 645 646 val >>= 10; 647 p++; 648 } while (*p); 649 650 snprintf(buf, 31, "%u%c", val, *p); 651 return buf; 652} 653 654/* 655 * Adds a job to the list of things todo. Sanitizes the various options 656 * to make sure we don't have conflicts, and initializes various 657 * members of td. 658 */ 659static int add_job(struct thread_data *td, const char *jobname, int job_add_num) 660{ 661 const char *ddir_str[] = { "read", "write", "randread", "randwrite", 662 "rw", NULL, "randrw" }; 663 struct stat sb; 664 int numjobs, ddir, i; 665 struct fio_file *f; 666 667 /* 668 * the def_thread is just for options, it's not a real job 669 */ 670 if (td == &def_thread) 671 return 0; 672 673 assert(td->io_ops); 674 675 if (td->odirect) 676 td->io_ops->flags |= FIO_RAWIO; 677 678 td->filetype = FIO_TYPE_FILE; 679 if (!stat(jobname, &sb)) { 680 if (S_ISBLK(sb.st_mode)) 681 td->filetype = FIO_TYPE_BD; 682 else if (S_ISCHR(sb.st_mode)) 683 td->filetype = FIO_TYPE_CHAR; 684 } 685 686 fixup_options(td); 687 688 if (td->filename) 689 td->nr_uniq_files = 1; 690 else 691 td->nr_uniq_files = td->nr_files; 692 693 if (td->filetype == FIO_TYPE_FILE || td->filename) { 694 char tmp[PATH_MAX]; 695 int len = 0; 696 697 if (td->directory && td->directory[0] != '\0') 698 len = sprintf(tmp, "%s/", td->directory); 699 700 td->files = malloc(sizeof(struct fio_file) * td->nr_files); 701 702 for_each_file(td, f, i) { 703 memset(f, 0, sizeof(*f)); 704 f->fd = -1; 705 706 if (td->filename) 707 sprintf(tmp + len, "%s", td->filename); 708 else 709 sprintf(tmp + len, "%s.%d.%d", jobname, td->thread_number, i); 710 f->file_name = strdup(tmp); 711 } 712 } else { 713 td->nr_files = 1; 714 td->files = malloc(sizeof(struct fio_file)); 715 f = &td->files[0]; 716 717 memset(f, 0, sizeof(*f)); 718 f->fd = -1; 719 f->file_name = strdup(jobname); 720 } 721 722 for_each_file(td, f, i) { 723 f->file_size = td->total_file_size / td->nr_files; 724 f->file_offset = td->start_offset; 725 } 726 727 fio_sem_init(&td->mutex, 0); 728 729 td->clat_stat[0].min_val = td->clat_stat[1].min_val = ULONG_MAX; 730 td->slat_stat[0].min_val = td->slat_stat[1].min_val = ULONG_MAX; 731 td->bw_stat[0].min_val = td->bw_stat[1].min_val = ULONG_MAX; 732 733 if (td->stonewall && td->thread_number > 1) 734 groupid++; 735 736 td->groupid = groupid; 737 738 if (setup_rate(td)) 739 goto err; 740 741 if (td->write_lat_log) { 742 setup_log(&td->slat_log); 743 setup_log(&td->clat_log); 744 } 745 if (td->write_bw_log) 746 setup_log(&td->bw_log); 747 748 if (!td->name) 749 td->name = strdup(jobname); 750 751 ddir = td->ddir + (!td->sequential << 1) + (td->iomix << 2); 752 753 if (!terse_output) { 754 if (!job_add_num) { 755 if (td->io_ops->flags & FIO_CPUIO) 756 fprintf(f_out, "%s: ioengine=cpu, cpuload=%u, cpucycle=%u\n", td->name, td->cpuload, td->cpucycle); 757 else { 758 char *c1, *c2, *c3, *c4; 759 760 c1 = to_kmg(td->min_bs[DDIR_READ]); 761 c2 = to_kmg(td->max_bs[DDIR_READ]); 762 c3 = to_kmg(td->min_bs[DDIR_WRITE]); 763 c4 = to_kmg(td->max_bs[DDIR_WRITE]); 764 765 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); 766 767 free(c1); 768 free(c2); 769 free(c3); 770 free(c4); 771 } 772 } else if (job_add_num == 1) 773 fprintf(f_out, "...\n"); 774 } 775 776 /* 777 * recurse add identical jobs, clear numjobs and stonewall options 778 * as they don't apply to sub-jobs 779 */ 780 numjobs = td->numjobs; 781 while (--numjobs) { 782 struct thread_data *td_new = get_new_job(0, td); 783 784 if (!td_new) 785 goto err; 786 787 td_new->numjobs = 1; 788 td_new->stonewall = 0; 789 job_add_num = numjobs - 1; 790 791 if (add_job(td_new, jobname, job_add_num)) 792 goto err; 793 } 794 return 0; 795err: 796 put_job(td); 797 return -1; 798} 799 800/* 801 * Initialize the various random states we need (random io, block size ranges, 802 * read/write mix, etc). 803 */ 804int init_random_state(struct thread_data *td) 805{ 806 unsigned long seeds[4]; 807 int fd, num_maps, blocks, i; 808 struct fio_file *f; 809 810 if (td->io_ops->flags & FIO_CPUIO) 811 return 0; 812 813 fd = open("/dev/urandom", O_RDONLY); 814 if (fd == -1) { 815 td_verror(td, errno); 816 return 1; 817 } 818 819 if (read(fd, seeds, sizeof(seeds)) < (int) sizeof(seeds)) { 820 td_verror(td, EIO); 821 close(fd); 822 return 1; 823 } 824 825 close(fd); 826 827 os_random_seed(seeds[0], &td->bsrange_state); 828 os_random_seed(seeds[1], &td->verify_state); 829 os_random_seed(seeds[2], &td->rwmix_state); 830 831 if (td->sequential) 832 return 0; 833 834 if (td->rand_repeatable) 835 seeds[3] = FIO_RANDSEED; 836 837 if (!td->norandommap) { 838 for_each_file(td, f, i) { 839 blocks = (f->file_size + td->rw_min_bs - 1) / td->rw_min_bs; 840 num_maps = (blocks + BLOCKS_PER_MAP-1)/ BLOCKS_PER_MAP; 841 f->file_map = malloc(num_maps * sizeof(long)); 842 f->num_maps = num_maps; 843 memset(f->file_map, 0, num_maps * sizeof(long)); 844 } 845 } 846 847 os_random_seed(seeds[3], &td->random_state); 848 return 0; 849} 850 851static void fill_cpu_mask(os_cpu_mask_t cpumask, int cpu) 852{ 853#ifdef FIO_HAVE_CPU_AFFINITY 854 unsigned int i; 855 856 CPU_ZERO(&cpumask); 857 858 for (i = 0; i < sizeof(int) * 8; i++) { 859 if ((1 << i) & cpu) 860 CPU_SET(i, &cpumask); 861 } 862#endif 863} 864 865static int is_empty_or_comment(char *line) 866{ 867 unsigned int i; 868 869 for (i = 0; i < strlen(line); i++) { 870 if (line[i] == ';') 871 return 1; 872 if (!isspace(line[i]) && !iscntrl(line[i])) 873 return 0; 874 } 875 876 return 1; 877} 878 879static int str_rw_cb(void *data, const char *mem) 880{ 881 struct thread_data *td = data; 882 883 if (!strncmp(mem, "read", 4) || !strncmp(mem, "0", 1)) { 884 td->ddir = DDIR_READ; 885 td->sequential = 1; 886 return 0; 887 } else if (!strncmp(mem, "randread", 8)) { 888 td->ddir = DDIR_READ; 889 td->sequential = 0; 890 return 0; 891 } else if (!strncmp(mem, "write", 5) || !strncmp(mem, "1", 1)) { 892 td->ddir = DDIR_WRITE; 893 td->sequential = 1; 894 return 0; 895 } else if (!strncmp(mem, "randwrite", 9)) { 896 td->ddir = DDIR_WRITE; 897 td->sequential = 0; 898 return 0; 899 } else if (!strncmp(mem, "rw", 2)) { 900 td->ddir = DDIR_READ; 901 td->iomix = 1; 902 td->sequential = 1; 903 return 0; 904 } else if (!strncmp(mem, "randrw", 6)) { 905 td->ddir = DDIR_READ; 906 td->iomix = 1; 907 td->sequential = 0; 908 return 0; 909 } 910 911 log_err("fio: data direction: read, write, randread, randwrite, rw, randrw\n"); 912 return 1; 913} 914 915static int str_verify_cb(void *data, const char *mem) 916{ 917 struct thread_data *td = data; 918 919 if (!strncmp(mem, "0", 1)) { 920 td->verify = VERIFY_NONE; 921 return 0; 922 } else if (!strncmp(mem, "md5", 3) || !strncmp(mem, "1", 1)) { 923 td->verify = VERIFY_MD5; 924 return 0; 925 } else if (!strncmp(mem, "crc32", 5)) { 926 td->verify = VERIFY_CRC32; 927 return 0; 928 } 929 930 log_err("fio: verify types: md5, crc32\n"); 931 return 1; 932} 933 934/* 935 * Check if mmap/mmaphuge has a :/foo/bar/file at the end. If so, return that. 936 */ 937static char *get_mmap_file(const char *str) 938{ 939 char *p = strstr(str, ":"); 940 941 if (!p) 942 return NULL; 943 944 p++; 945 strip_blank_front(&p); 946 strip_blank_end(p); 947 return strdup(p); 948} 949 950static int str_mem_cb(void *data, const char *mem) 951{ 952 struct thread_data *td = data; 953 954 if (!strncmp(mem, "malloc", 6)) { 955 td->mem_type = MEM_MALLOC; 956 return 0; 957 } else if (!strncmp(mem, "mmaphuge", 8)) { 958#ifdef FIO_HAVE_HUGETLB 959 /* 960 * mmaphuge must be appended with the actual file 961 */ 962 td->mmapfile = get_mmap_file(mem); 963 if (!td->mmapfile) { 964 log_err("fio: mmaphuge:/path/to/file\n"); 965 return 1; 966 } 967 968 td->mem_type = MEM_MMAPHUGE; 969 return 0; 970#else 971 log_err("fio: mmaphuge not available\n"); 972 return 1; 973#endif 974 } else if (!strncmp(mem, "mmap", 4)) { 975 /* 976 * Check if the user wants file backed memory. It's ok 977 * if there's no file given, we'll just use anon mamp then. 978 */ 979 td->mmapfile = get_mmap_file(mem); 980 td->mem_type = MEM_MMAP; 981 return 0; 982 } else if (!strncmp(mem, "shmhuge", 7)) { 983#ifdef FIO_HAVE_HUGETLB 984 td->mem_type = MEM_SHMHUGE; 985 return 0; 986#else 987 log_err("fio: shmhuge not available\n"); 988 return 1; 989#endif 990 } else if (!strncmp(mem, "shm", 3)) { 991 td->mem_type = MEM_SHM; 992 return 0; 993 } 994 995 log_err("fio: mem type: malloc, shm, shmhuge, mmap, mmaphuge\n"); 996 return 1; 997} 998 999static int str_ioengine_cb(void *data, const char *str) 1000{ 1001 struct thread_data *td = data; 1002 1003 td->io_ops = load_ioengine(td, str); 1004 if (td->io_ops) 1005 return 0; 1006 1007 log_err("fio: ioengine= libaio, posixaio, sync, mmap, sgio, splice, cpu, null\n"); 1008 log_err("fio: or specify path to dynamic ioengine module\n"); 1009 return 1; 1010} 1011 1012static int str_lockmem_cb(void fio_unused *data, unsigned long *val) 1013{ 1014 mlock_size = *val; 1015 return 0; 1016} 1017 1018#ifdef FIO_HAVE_IOPRIO 1019static int str_prioclass_cb(void *data, unsigned int *val) 1020{ 1021 struct thread_data *td = data; 1022 1023 td->ioprio |= *val << IOPRIO_CLASS_SHIFT; 1024 return 0; 1025} 1026 1027static int str_prio_cb(void *data, unsigned int *val) 1028{ 1029 struct thread_data *td = data; 1030 1031 td->ioprio |= *val; 1032 return 0; 1033} 1034#endif 1035 1036static int str_exitall_cb(void) 1037{ 1038 exitall_on_terminate = 1; 1039 return 0; 1040} 1041 1042static int str_cpumask_cb(void *data, unsigned int *val) 1043{ 1044 struct thread_data *td = data; 1045 1046 fill_cpu_mask(td->cpumask, *val); 1047 return 0; 1048} 1049 1050/* 1051 * This is our [ini] type file parser. 1052 */ 1053static int parse_jobs_ini(char *file, int stonewall_flag) 1054{ 1055 unsigned int global; 1056 struct thread_data *td; 1057 char *string, *name; 1058 fpos_t off; 1059 FILE *f; 1060 char *p; 1061 int ret = 0, stonewall; 1062 1063 f = fopen(file, "r"); 1064 if (!f) { 1065 perror("fopen job file"); 1066 return 1; 1067 } 1068 1069 string = malloc(4096); 1070 name = malloc(256); 1071 memset(name, 0, 256); 1072 1073 stonewall = stonewall_flag; 1074 do { 1075 p = fgets(string, 4095, f); 1076 if (!p) 1077 break; 1078 if (is_empty_or_comment(p)) 1079 continue; 1080 if (sscanf(p, "[%255s]", name) != 1) 1081 continue; 1082 1083 global = !strncmp(name, "global", 6); 1084 1085 name[strlen(name) - 1] = '\0'; 1086 1087 td = get_new_job(global, &def_thread); 1088 if (!td) { 1089 ret = 1; 1090 break; 1091 } 1092 1093 /* 1094 * Seperate multiple job files by a stonewall 1095 */ 1096 if (!global && stonewall) { 1097 td->stonewall = stonewall; 1098 stonewall = 0; 1099 } 1100 1101 fgetpos(f, &off); 1102 while ((p = fgets(string, 4096, f)) != NULL) { 1103 if (is_empty_or_comment(p)) 1104 continue; 1105 1106 strip_blank_front(&p); 1107 1108 if (p[0] == '[') 1109 break; 1110 1111 strip_blank_end(p); 1112 1113 fgetpos(f, &off); 1114 1115 /* 1116 * Don't break here, continue parsing options so we 1117 * dump all the bad ones. Makes trial/error fixups 1118 * easier on the user. 1119 */ 1120 ret |= parse_option(p, options, td); 1121 } 1122 1123 if (!ret) { 1124 fsetpos(f, &off); 1125 ret = add_job(td, name, 0); 1126 } else { 1127 log_err("fio: job %s dropped\n", name); 1128 put_job(td); 1129 } 1130 } while (!ret); 1131 1132 free(string); 1133 free(name); 1134 fclose(f); 1135 return ret; 1136} 1137 1138static int fill_def_thread(void) 1139{ 1140 memset(&def_thread, 0, sizeof(def_thread)); 1141 1142 if (fio_getaffinity(getpid(), &def_thread.cpumask) == -1) { 1143 perror("sched_getaffinity"); 1144 return 1; 1145 } 1146 1147 /* 1148 * fill default options 1149 */ 1150 fill_default_options(&def_thread, options); 1151 1152 def_thread.timeout = def_timeout; 1153 def_thread.write_bw_log = write_bw_log; 1154 def_thread.write_lat_log = write_lat_log; 1155 1156#ifdef FIO_HAVE_DISK_UTIL 1157 def_thread.do_disk_util = 1; 1158#endif 1159 1160 return 0; 1161} 1162 1163static void usage(void) 1164{ 1165 printf("%s\n", fio_version_string); 1166 printf("\t--output\tWrite output to file\n"); 1167 printf("\t--timeout\tRuntime in seconds\n"); 1168 printf("\t--latency-log\tGenerate per-job latency logs\n"); 1169 printf("\t--bandwidth-log\tGenerate per-job bandwidth logs\n"); 1170 printf("\t--minimal\tMinimal (terse) output\n"); 1171 printf("\t--version\tPrint version info and exit\n"); 1172 printf("\t--help\t\tPrint this page\n"); 1173 printf("\t--cmdhelp=cmd\tPrint command help, \"all\" for all of them\n"); 1174} 1175 1176static int parse_cmd_line(int argc, char *argv[]) 1177{ 1178 struct thread_data *td = NULL; 1179 int c, ini_idx = 0, lidx, ret; 1180 1181 while ((c = getopt_long(argc, argv, "", long_options, &lidx)) != -1) { 1182 switch (c) { 1183 case 't': 1184 def_timeout = atoi(optarg); 1185 break; 1186 case 'l': 1187 write_lat_log = 1; 1188 break; 1189 case 'w': 1190 write_bw_log = 1; 1191 break; 1192 case 'o': 1193 f_out = fopen(optarg, "w+"); 1194 if (!f_out) { 1195 perror("fopen output"); 1196 exit(1); 1197 } 1198 f_err = f_out; 1199 break; 1200 case 'm': 1201 terse_output = 1; 1202 break; 1203 case 'h': 1204 usage(); 1205 exit(0); 1206 case 'c': 1207 ret = show_cmd_help(options, optarg); 1208 exit(ret); 1209 case 'v': 1210 printf("%s\n", fio_version_string); 1211 exit(0); 1212 case FIO_GETOPT_JOB: { 1213 const char *opt = long_options[lidx].name; 1214 char *val = optarg; 1215 1216 if (!strncmp(opt, "name", 4) && td) { 1217 ret = add_job(td, td->name ?: "fio", 0); 1218 if (ret) { 1219 put_job(td); 1220 return 0; 1221 } 1222 td = NULL; 1223 } 1224 if (!td) { 1225 int global = !strncmp(val, "global", 6); 1226 1227 td = get_new_job(global, &def_thread); 1228 if (!td) 1229 return 0; 1230 } 1231 1232 ret = parse_cmd_option(opt, val, options, td); 1233 if (ret) { 1234 log_err("fio: job dropped\n"); 1235 put_job(td); 1236 td = NULL; 1237 } 1238 break; 1239 } 1240 default: 1241 break; 1242 } 1243 } 1244 1245 if (td) { 1246 ret = add_job(td, td->name ?: "fio", 0); 1247 if (ret) 1248 put_job(td); 1249 } 1250 1251 while (optind < argc) { 1252 ini_idx++; 1253 ini_file = realloc(ini_file, ini_idx * sizeof(char *)); 1254 ini_file[ini_idx - 1] = strdup(argv[optind]); 1255 optind++; 1256 } 1257 1258 return ini_idx; 1259} 1260 1261static void free_shm(void) 1262{ 1263 struct shmid_ds sbuf; 1264 1265 if (threads) { 1266 shmdt((void *) threads); 1267 threads = NULL; 1268 shmctl(shm_id, IPC_RMID, &sbuf); 1269 } 1270} 1271 1272/* 1273 * The thread area is shared between the main process and the job 1274 * threads/processes. So setup a shared memory segment that will hold 1275 * all the job info. 1276 */ 1277static int setup_thread_area(void) 1278{ 1279 /* 1280 * 1024 is too much on some machines, scale max_jobs if 1281 * we get a failure that looks like too large a shm segment 1282 */ 1283 do { 1284 size_t size = max_jobs * sizeof(struct thread_data); 1285 1286 shm_id = shmget(0, size, IPC_CREAT | 0600); 1287 if (shm_id != -1) 1288 break; 1289 if (errno != EINVAL) { 1290 perror("shmget"); 1291 break; 1292 } 1293 1294 max_jobs >>= 1; 1295 } while (max_jobs); 1296 1297 if (shm_id == -1) 1298 return 1; 1299 1300 threads = shmat(shm_id, NULL, 0); 1301 if (threads == (void *) -1) { 1302 perror("shmat"); 1303 return 1; 1304 } 1305 1306 atexit(free_shm); 1307 return 0; 1308} 1309 1310/* 1311 * Copy the fio options into the long options map, so we mirror 1312 * job and cmd line options. 1313 */ 1314static void dupe_job_options(void) 1315{ 1316 struct fio_option *o; 1317 unsigned int i; 1318 1319 i = 0; 1320 while (long_options[i].name) 1321 i++; 1322 1323 o = &options[0]; 1324 while (o->name) { 1325 long_options[i].name = o->name; 1326 long_options[i].val = FIO_GETOPT_JOB; 1327 if (o->type == FIO_OPT_STR_SET) 1328 long_options[i].has_arg = no_argument; 1329 else 1330 long_options[i].has_arg = required_argument; 1331 1332 i++; 1333 o++; 1334 assert(i < FIO_JOB_OPTS + FIO_CMD_OPTS); 1335 } 1336} 1337 1338int parse_options(int argc, char *argv[]) 1339{ 1340 int job_files, i; 1341 1342 f_out = stdout; 1343 f_err = stderr; 1344 1345 options_init(options); 1346 1347 dupe_job_options(); 1348 1349 if (setup_thread_area()) 1350 return 1; 1351 if (fill_def_thread()) 1352 return 1; 1353 1354 job_files = parse_cmd_line(argc, argv); 1355 1356 for (i = 0; i < job_files; i++) { 1357 if (fill_def_thread()) 1358 return 1; 1359 if (parse_jobs_ini(ini_file[i], i)) 1360 return 1; 1361 free(ini_file[i]); 1362 } 1363 1364 free(ini_file); 1365 1366 if (!thread_number) { 1367 log_err("No jobs defined(s)\n"); 1368 return 1; 1369 } 1370 1371 return 0; 1372} 1373