#define DEF_RWMIX_READ (50)
#define DEF_NICE (0)
+static int def_timeout = DEF_TIMEOUT;
+
static char fio_version_string[] = "fio 1.4";
-static int repeatable = DEF_RAND_REPEAT;
-static char *ini_file;
+static char **ini_file;
static int max_jobs = MAX_JOBS;
struct thread_data def_thread;
int write_lat_log = 0;
int write_bw_log = 0;
int exitall_on_terminate = 0;
+int terse_output = 0;
unsigned long long mlock_size = 0;
-
-static int setup_rate(struct thread_data *td)
-{
- int nr_reads_per_sec;
-
- if (!td->rate)
- return 0;
-
- if (td->rate < td->ratemin) {
- fprintf(stderr, "min rate larger than nominal rate\n");
- return -1;
- }
-
- nr_reads_per_sec = (td->rate * 1024) / td->min_bs;
- td->rate_usec_cycle = 1000000 / nr_reads_per_sec;
- td->rate_pending_usleep = 0;
- return 0;
-}
-
-static void setup_log(struct io_log **log)
-{
- struct io_log *l = malloc(sizeof(*l));
-
- l->nr_samples = 0;
- l->max_samples = 1024;
- l->log = malloc(l->max_samples * sizeof(struct io_sample));
- *log = l;
-}
-
-void finish_log(struct thread_data *td, struct io_log *log, const char *name)
-{
- char file_name[256];
- FILE *f;
- unsigned int i;
-
- snprintf(file_name, 200, "client%d_%s.log", td->thread_number, name);
- f = fopen(file_name, "w");
- if (!f) {
- perror("fopen log");
- return;
- }
-
- for (i = 0; i < log->nr_samples; i++)
- fprintf(f, "%lu, %lu, %u\n", log->log[i].time, log->log[i].val, log->log[i].ddir);
-
- fclose(f);
- free(log->log);
- free(log);
-}
+FILE *f_out = NULL;
+FILE *f_err = NULL;
static struct thread_data *get_new_job(int global, struct thread_data *parent)
{
return NULL;
td = &threads[thread_number++];
- if (parent)
- *td = *parent;
- else
- memset(td, 0, sizeof(*td));
+ *td = *parent;
+ td->name[0] = '\0';
td->fd = -1;
td->thread_number = thread_number;
-
- td->ddir = parent->ddir;
- td->ioprio = parent->ioprio;
- td->sequential = parent->sequential;
- td->bs = parent->bs;
- td->min_bs = parent->min_bs;
- td->max_bs = parent->max_bs;
- td->odirect = parent->odirect;
- td->thinktime = parent->thinktime;
- td->fsync_blocks = parent->fsync_blocks;
- td->start_delay = parent->start_delay;
- td->timeout = parent->timeout;
- td->io_engine = parent->io_engine;
- td->create_file = parent->create_file;
- td->overwrite = parent->overwrite;
- td->invalidate_cache = parent->invalidate_cache;
- td->file_size = parent->file_size;
- td->file_offset = parent->file_offset;
- td->zone_size = parent->zone_size;
- td->zone_skip = parent->zone_skip;
- td->rate = parent->rate;
- td->ratemin = parent->ratemin;
- td->ratecycle = parent->ratecycle;
- td->iodepth = parent->iodepth;
- td->sync_io = parent->sync_io;
- td->mem_type = parent->mem_type;
- td->bw_avg_time = parent->bw_avg_time;
- td->create_serialize = parent->create_serialize;
- td->create_fsync = parent->create_fsync;
- td->loops = parent->loops;
- td->verify = parent->verify;
- td->stonewall = parent->stonewall;
- td->numjobs = parent->numjobs;
- td->use_thread = parent->use_thread;
- td->do_disk_util = parent->do_disk_util;
- memcpy(&td->cpumask, &parent->cpumask, sizeof(td->cpumask));
- strcpy(td->io_engine_name, parent->io_engine_name);
-
return td;
}
#ifndef FIO_HAVE_LIBAIO
if (td->io_engine == FIO_LIBAIO) {
- fprintf(stderr, "Linux libaio not available\n");
+ log_err("Linux libaio not available\n");
return 1;
}
#endif
#ifndef FIO_HAVE_POSIXAIO
if (td->io_engine == FIO_POSIXAIO) {
- fprintf(stderr, "posix aio not available\n");
+ log_err("posix aio not available\n");
return 1;
}
#endif
}
if (td->filetype == FIO_TYPE_FILE) {
+ char tmp[PATH_MAX];
+
if (td->directory && td->directory[0] != '\0')
- sprintf(td->file_name, "%s/%s.%d", td->directory, jobname, td->jobnum);
+ sprintf(tmp, "%s/%s.%d", td->directory, jobname, td->thread_number);
else
- sprintf(td->file_name, "%s.%d", jobname, td->jobnum);
+ sprintf(tmp, "%s.%d", jobname, td->thread_number);
+ td->file_name = strdup(tmp);
} else
- strncpy(td->file_name, jobname, sizeof(td->file_name) - 1);
+ td->file_name = strdup(jobname);
- sem_init(&td->mutex, 0, 0);
+ fio_sem_init(&td->mutex, 0);
td->clat_stat[0].min_val = td->clat_stat[1].min_val = ULONG_MAX;
td->slat_stat[0].min_val = td->slat_stat[1].min_val = ULONG_MAX;
if (write_bw_log)
setup_log(&td->bw_log);
+ if (td->name[0] == '\0')
+ snprintf(td->name, sizeof(td->name)-1, "client%d", td->thread_number);
+
ddir = td->ddir + (!td->sequential << 1) + (td->iomix << 2);
- if (!job_add_num)
- printf("Client%d (g=%d): rw=%s, odir=%d, bs=%d-%d, rate=%d, ioengine=%s, iodepth=%d\n", td->thread_number, td->groupid, ddir_str[ddir], td->odirect, td->min_bs, td->max_bs, td->rate, td->io_engine_name, td->iodepth);
- else if (job_add_num == 1)
- printf("...\n");
+ if (!terse_output) {
+ if (!job_add_num)
+ fprintf(f_out, "%s: (g=%d): rw=%s, odir=%d, bs=%d-%d, rate=%d, ioengine=%s, iodepth=%d\n", td->name, td->groupid, ddir_str[ddir], td->odirect, td->min_bs, td->max_bs, td->rate, td->io_engine_name, td->iodepth);
+ else if (job_add_num == 1)
+ fprintf(f_out, "...\n");
+ }
/*
* recurse add identical jobs, clear numjobs and stonewall options
td_new->numjobs = 1;
td_new->stonewall = 0;
- td_new->jobnum = numjobs;
job_add_num = numjobs - 1;
if (add_job(td_new, jobname, job_add_num))
close(fd);
- srand48_r(seeds[0], &td->bsrange_state);
- srand48_r(seeds[1], &td->verify_state);
- srand48_r(seeds[2], &td->rwmix_state);
+ os_random_seed(seeds[0], &td->bsrange_state);
+ os_random_seed(seeds[1], &td->verify_state);
+ os_random_seed(seeds[2], &td->rwmix_state);
if (td->sequential)
return 0;
- if (repeatable)
+ if (td->rand_repeatable)
seeds[3] = DEF_RANDSEED;
blocks = (td->io_size + td->min_bs - 1) / td->min_bs;
td->num_maps = num_maps;
memset(td->file_map, 0, num_maps * sizeof(long));
- srand48_r(seeds[3], &td->random_state);
+ os_random_seed(seeds[3], &td->random_state);
return 0;
}
return 0;
}
- fprintf(stderr, "bad data direction: %s\n", mem);
+ log_err("fio: data direction: read, write, randread, randwrite, rw, randrw\n");
return 1;
}
return 0;
}
- fprintf(stderr, "bad verify type: %s\n", mem);
+ log_err("fio: verify types: md5, crc32\n");
return 1;
}
return 0;
}
- fprintf(stderr, "bad mem type: %s\n", mem);
+ log_err("fio: mem type: malloc, shm, mmap\n");
return 1;
}
return 0;
}
- fprintf(stderr, "bad ioengine type: %s\n", str);
+ log_err("fio: ioengine: { linuxaio, aio, libaio }, posixaio, sync, mmap, sgio, splice\n");
return 1;
}
-static int str_iolog_cb(struct thread_data *td, char *file)
-{
- td->iolog_file = strdup(file);
- return 0;
-}
-
-static int str_prerun_cb(struct thread_data *td, char *file)
-{
- td->exec_prerun = strdup(file);
- return 0;
-}
-
-static int str_postrun_cb(struct thread_data *td, char *file)
-{
- td->exec_postrun = strdup(file);
- return 0;
-}
-
-static int str_iosched_cb(struct thread_data *td, char *file)
-{
- td->ioscheduler = strdup(file);
- return 0;
-}
-
+/*
+ * This is our [ini] type file parser.
+ */
int parse_jobs_ini(char *file)
{
unsigned int prioclass, prio, cpu, global, il;
fpos_t off;
FILE *f;
char *p;
+ int ret = 0, stonewall = 1;
f = fopen(file, "r");
if (!f) {
tmpbuf = malloc(4096);
while ((p = fgets(string, 4096, f)) != NULL) {
+ if (ret)
+ break;
if (is_empty_or_comment(p))
continue;
if (sscanf(p, "[%s]", name) != 1)
name[strlen(name) - 1] = '\0';
td = get_new_job(global, &def_thread);
- if (!td)
- return 1;
+ if (!td) {
+ ret = 1;
+ break;
+ }
+
+ /*
+ * Seperate multiple job files by a stonewall
+ */
+ if (!global && stonewall) {
+ td->stonewall = stonewall;
+ stonewall = 0;
+ }
fgetpos(f, &off);
while ((p = fgets(string, 4096, f)) != NULL) {
if (!check_int(p, "prio", &prio)) {
#ifndef FIO_HAVE_IOPRIO
- fprintf(stderr, "io priorities not available\n");
- return 1;
+ log_err("io priorities not available\n");
+ ret = 1;
+ break;
#endif
td->ioprio |= prio;
fgetpos(f, &off);
}
if (!check_int(p, "prioclass", &prioclass)) {
#ifndef FIO_HAVE_IOPRIO
- fprintf(stderr, "io priorities not available\n");
- return 1;
-#endif
+ log_err("io priorities not available\n");
+ ret = 1;
+ break;
+#else
td->ioprio |= prioclass << IOPRIO_CLASS_SHIFT;
fgetpos(f, &off);
continue;
+#endif
}
- if (!check_int(p, "direct", &td->odirect)) {
+ if (!check_int(p, "direct", &il)) {
+ td->odirect = il;
+ fgetpos(f, &off);
+ continue;
+ }
+ if (!check_int(p, "rand_repeatable", &il)) {
+ td->rand_repeatable = il;
fgetpos(f, &off);
continue;
}
}
if (!check_int(p, "cpumask", &cpu)) {
#ifndef FIO_HAVE_CPU_AFFINITY
- fprintf(stderr, "cpu affinity not available\n");
- return 1;
+ log_err("cpu affinity not available\n");
+ ret = 1;
+ break;
#endif
fill_cpu_mask(td->cpumask, cpu);
fgetpos(f, &off);
fgetpos(f, &off);
continue;
}
- if (!check_int(p, "invalidate",&td->invalidate_cache)) {
+ if (!check_int(p, "invalidate", &il)) {
+ td->invalidate_cache = il;
fgetpos(f, &off);
continue;
}
fgetpos(f, &off);
continue;
}
- if (!check_int(p, "sync", &td->sync_io)) {
+ if (!check_int(p, "sync", &il)) {
+ td->sync_io = il;
fgetpos(f, &off);
continue;
}
fgetpos(f, &off);
continue;
}
- if (!check_int(p, "create_serialize", &td->create_serialize)) {
+ if (!check_int(p, "create_serialize", &il)) {
+ td->create_serialize = il;
fgetpos(f, &off);
continue;
}
- if (!check_int(p, "create_fsync", &td->create_fsync)) {
+ if (!check_int(p, "create_fsync", &il)) {
+ td->create_fsync = il;
fgetpos(f, &off);
continue;
}
- if (!check_int(p, "end_fsync", &td->end_fsync)) {
+ if (!check_int(p, "end_fsync", &il)) {
+ td->end_fsync = il;
fgetpos(f, &off);
continue;
}
fgetpos(f, &off);
continue;
}
- if (!check_int(p, "overwrite", &td->overwrite)) {
+ if (!check_int(p, "overwrite", &il)) {
+ td->overwrite = il;
fgetpos(f, &off);
continue;
}
fgetpos(f, &off);
continue;
}
+ if (!check_strstore(p, "name", tmpbuf)) {
+ snprintf(td->name, sizeof(td->name)-1, "%s%d", tmpbuf, td->thread_number);
+ fgetpos(f, &off);
+ continue;
+ }
if (!check_str(p, "mem", str_mem_cb, td)) {
fgetpos(f, &off);
continue;
fgetpos(f, &off);
continue;
}
- if (!check_str(p, "iolog", str_iolog_cb, td)) {
+ if (!check_strstore(p, "iolog", tmpbuf)) {
+ if (td->write_iolog) {
+ log_err("fio: read iolog overrides given write_iolog\n");
+ free(td->iolog_file);
+ td->write_iolog = 0;
+ }
+ td->iolog_file = strdup(tmpbuf);
td->read_iolog = 1;
- td->write_iolog = 0;
fgetpos(f, &off);
continue;
}
- if (!td->read_iolog &&
- !check_str(p, "write_iolog", str_iolog_cb, td)) {
- td->write_iolog = 1;
+ if (!check_strstore(p, "write_iolog", tmpbuf)) {
+ if (!td->read_iolog) {
+ td->iolog_file = strdup(tmpbuf);
+ td->write_iolog = 1;
+ } else
+ log_err("fio: read iolog overrides given write_iolog\n");
fgetpos(f, &off);
continue;
}
- if (!check_str(p, "exec_prerun", str_prerun_cb, td)) {
+ if (!check_strstore(p, "exec_prerun", tmpbuf)) {
+ td->exec_prerun = strdup(tmpbuf);
fgetpos(f, &off);
continue;
}
- if (!check_str(p, "exec_postrun", str_postrun_cb, td)) {
+ if (!check_strstore(p, "exec_postrun", tmpbuf)) {
+ td->exec_postrun = strdup(tmpbuf);
fgetpos(f, &off);
continue;
}
- if (!check_str(p, "ioscheduler", str_iosched_cb, td)) {
+ if (!check_strstore(p, "ioscheduler", tmpbuf)) {
+#ifndef FIO_HAVE_IOSCHED_SWITCH
+ log_err("io scheduler switching not available\n");
+ ret = 1;
+ break;
+#else
+ td->ioscheduler = strdup(tmpbuf);
fgetpos(f, &off);
continue;
+#endif
}
+ /*
+ * Don't break here, continue parsing options so we
+ * dump all the bad ones. Makes trial/error fixups
+ * easier on the user.
+ */
printf("Client%d: bad option %s\n",td->thread_number,p);
- return 1;
+ ret = 1;
}
- fsetpos(f, &off);
- if (add_job(td, name, 0))
- return 1;
+ if (!ret) {
+ fsetpos(f, &off);
+ ret = add_job(td, name, 0);
+ }
+ if (ret)
+ break;
}
free(string);
free(name);
free(tmpbuf);
fclose(f);
- return 0;
+ return ret;
}
static int fill_def_thread(void)
def_thread.odirect = DEF_ODIRECT;
def_thread.ratecycle = DEF_RATE_CYCLE;
def_thread.sequential = DEF_SEQUENTIAL;
- def_thread.timeout = DEF_TIMEOUT;
+ def_thread.timeout = def_timeout;
def_thread.create_file = DEF_CREATE;
def_thread.overwrite = DEF_OVERWRITE;
def_thread.invalidate_cache = DEF_INVALIDATE;
def_thread.rwmixcycle = DEF_RWMIX_CYCLE;
def_thread.rwmixread = DEF_RWMIX_READ;
def_thread.nice = DEF_NICE;
+ def_thread.rand_repeatable = DEF_RAND_REPEAT;
#ifdef FIO_HAVE_DISK_UTIL
def_thread.do_disk_util = 1;
#endif
static void usage(char *name)
{
printf("%s\n", fio_version_string);
- printf("\t-s IO is sequential\n");
- printf("\t-b Block size in KiB for each IO\n");
+ printf("\t-o Write output to file\n");
printf("\t-t Runtime in seconds\n");
- printf("\t-R Exit all threads on failure to meet rate goal\n");
- printf("\t-o Use O_DIRECT\n");
printf("\t-l Generate per-job latency logs\n");
printf("\t-w Generate per-job bandwidth logs\n");
+ printf("\t-m Minimal (terse) output\n");
printf("\t-f Job file (Required)\n");
printf("\t-v Print version info and exit\n");
}
-static void parse_cmd_line(int argc, char *argv[])
+static int parse_cmd_line(int argc, char *argv[])
{
- int c;
+ int c, idx = 1, ini_idx = 0;
- while ((c = getopt(argc, argv, "s:b:t:r:R:o:f:lwvh")) != EOF) {
+ while ((c = getopt(argc, argv, "t:o:f:lwvhm")) != EOF) {
switch (c) {
- case 's':
- def_thread.sequential = !!atoi(optarg);
- break;
- case 'b':
- def_thread.bs = atoi(optarg);
- def_thread.bs <<= 10;
- if (!def_thread.bs) {
- printf("bad block size\n");
- def_thread.bs = DEF_BS;
- }
- break;
case 't':
- def_thread.timeout = atoi(optarg);
- break;
- case 'r':
- repeatable = !!atoi(optarg);
- break;
- case 'R':
- rate_quit = !!atoi(optarg);
- break;
- case 'o':
- def_thread.odirect = !!atoi(optarg);
+ def_timeout = atoi(optarg);
+ idx++;
break;
case 'f':
- ini_file = strdup(optarg);
+ ini_idx++;
+ ini_file = realloc(ini_file, ini_idx * sizeof(char *));
+ ini_file[ini_idx - 1] = strdup(optarg);
+ idx++;
break;
case 'l':
write_lat_log = 1;
+ idx++;
break;
case 'w':
write_bw_log = 1;
+ idx++;
+ break;
+ case 'o':
+ f_out = fopen(optarg, "w+");
+ if (!f_out) {
+ perror("fopen output");
+ exit(1);
+ }
+ f_err = f_out;
+ idx++;
+ break;
+ case 'm':
+ terse_output = 1;
+ idx++;
break;
case 'h':
usage(argv[0]);
}
}
- if (!ini_file && argc > 1 && argv[argc - 1][0] != '-')
- ini_file = strdup(argv[argc - 1]);
+ while (idx < argc) {
+ ini_idx++;
+ ini_file = realloc(ini_file, ini_idx * sizeof(char *));
+ ini_file[ini_idx - 1] = strdup(argv[idx]);
+ idx++;
+ }
+
+ if (!f_out) {
+ f_out = stdout;
+ f_err = stderr;
+ }
+
+ return ini_idx;
}
static void free_shm(void)
struct shmid_ds sbuf;
if (threads) {
- shmdt(threads);
+ shmdt((void *) threads);
threads = NULL;
shmctl(shm_id, IPC_RMID, &sbuf);
}
int parse_options(int argc, char *argv[])
{
+ int job_files, i;
+
if (setup_thread_area())
return 1;
if (fill_def_thread())
return 1;
- parse_cmd_line(argc, argv);
-
- if (!ini_file) {
- printf("Need job file\n");
+ job_files = parse_cmd_line(argc, argv);
+ if (!job_files) {
+ log_err("Need job file(s)\n");
usage(argv[0]);
return 1;
}
- if (parse_jobs_ini(ini_file)) {
- usage(argv[0]);
- return 1;
+ for (i = 0; i < job_files; i++) {
+ if (fill_def_thread())
+ return 1;
+ if (parse_jobs_ini(ini_file[i]))
+ return 1;
+ free(ini_file[i]);
}
+ free(ini_file);
return 0;
}