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