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