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