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