From 7dd1389e9d3e0f414b0f66210ba7d209d4d34785 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 18 Oct 2005 11:37:36 +0200 Subject: [PATCH] [PATCH] fio: add support for ini file job descriptions The cmd line options are a little screwy to use, so add support for defining the wanted jobs in a config file. --- README.fio | 41 +++++++++++- fio.c | 183 ++++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 198 insertions(+), 26 deletions(-) diff --git a/README.fio b/README.fio index 0b95e2c..1bd93af 100644 --- a/README.fio +++ b/README.fio @@ -16,6 +16,7 @@ $ fio -w Write statistics -r For random io, sequence must be repeatable -o Use direct IO is 1, buffered if 0 + -f Read for job descriptions @@ -33,8 +34,8 @@ The format is as follows: rate=x Throttle rate to x KiB/sec -Examples --------- +Examples using cmd line jobs +---------------------------- Spawn 2 threads, one read and one writer. Both threads want direct and sequential io, set these as global options. The reader wants a 4kb block @@ -48,3 +49,39 @@ size. $ fio -o0 -s -b4096 "{rw=0,file=rf1,prio=6}" "{rw=0,file=rf2,prio=3}" "{rw=0,file=rf3,prio=0,direct=1}" +Examples using a job file +------------------------- + +A sample job file doing the same as above would look like this: + +[read_file] +rw=0 +bs=4096 + +[write_file] +rw=1 +bs=16384 + +And fio would be invoked as: + +$ fio -o1 -s -f file_with_above + +The second example would look like this: + +[rf1] +rw=0 +prio=6 + +[rf2] +rw=0 +prio=3 + +[rf3] +rw=0 +prio=0 +direct=1 + +And fio would be invoked as: + +$ fio -o0 -s -b4096 -f file_with_above + diff --git a/fio.c b/fio.c index a7a9ec8..685c64a 100644 --- a/fio.c +++ b/fio.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -91,6 +92,7 @@ static int thread_number; static int timeout = TIMEOUT; static int odirect = 1; static int global_bs = BS; +static char *ini_file; static int shm_id; @@ -112,9 +114,9 @@ struct thread_data { int odirect; int delay_sleep; - int rate; - int rate_usec_cycle; - int rate_pending_usleep; + unsigned int rate; + unsigned int rate_usec_cycle; + unsigned int rate_pending_usleep; unsigned long max_latency; /* msec */ unsigned long min_latency; /* msec */ @@ -164,7 +166,7 @@ int init_random_state(struct thread_data *td) return 1; } - if (read(fd, &seed, sizeof(seed)) < sizeof(seed)) { + if (read(fd, &seed, sizeof(seed)) < (int) sizeof(seed)) { td->error = EIO; close(fd); return 1; @@ -272,7 +274,7 @@ unsigned long get_next_offset(struct thread_data *td) return b * td->bs; } -void add_stat_sample(struct thread_data *td, unsigned long block, unsigned long msec) +void add_stat_sample(struct thread_data *td, unsigned long msec) { char sample[256]; @@ -381,7 +383,7 @@ void do_thread_io(struct thread_data *td) if (td->rate) rate_throttle(td, usec); - add_stat_sample(td, offset / td->bs, msec); + add_stat_sample(td, msec); td->blocks_read++; @@ -419,15 +421,15 @@ void *thread_main(int shm_id, int offset, char *argv[]) sprintf(argv[0], "%s%d\n", argv[0], offset); - if (td->ddir == DDIR_READ) - flags = O_RDONLY; - else - flags = O_WRONLY | O_CREAT | O_TRUNC; - + flags = 0; if (td->odirect) flags |= O_DIRECT; - td->fd = open(td->file_name, flags); + if (td->ddir == DDIR_READ) + td->fd = open(td->file_name, flags | O_RDONLY); + else + td->fd = open(td->file_name, flags | O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (td->fd == -1) { td->error = errno; goto err; @@ -530,6 +532,8 @@ void add_job(const char *filename, int rw, int bs, int direct, int prio, int ran if (rate) setup_rate(td, rate); + + printf("Client%d: file=%s, rw=%d, prio=%d, seq=%d, odir=%d, bs=%d, rate=%d\n", thread_number, filename, rw, prio, !random, direct, bs, rate); } void fill_option(const char *input, char *output) @@ -553,7 +557,7 @@ void fill_option(const char *input, char *output) * rw= * direct= */ -int parse_jobs(int argc, char *argv[], int index) +int parse_jobs_cmd(int argc, char *argv[], int index) { int rw, bs, direct, prio, random, prioclass, delay, rate; char *string, *filename, *p, *c; @@ -647,9 +651,126 @@ int parse_jobs(int argc, char *argv[], int index) add_job(filename, rw, bs, direct, (prioclass << IOPRIO_CLASS_SHIFT) | prio, random, delay, rate); } + free(string); + free(filename); return thread_number; } +int check_int(char *p, char *name, int *val) +{ + char str[128]; + + sprintf(str, "%s=%%d", name); + if (sscanf(p, str, val) == 1) + return 0; + + sprintf(str, "%s = %%d", name); + if (sscanf(p, str, val) == 1) + return 0; + + return 1; +} + +int is_empty(char *line) +{ + unsigned int i; + + for (i = 0; i < strlen(line); i++) + if (!isspace(line[i]) && !iscntrl(line[i])) + return 0; + + return 1; +} + +int parse_jobs_ini(char *file) +{ + int rw, bs, direct, prio, random, prioclass, delay, rate, jobs; + char *string, *name; + fpos_t off; + FILE *f; + char *p; + + f = fopen(file, "r"); + if (!f) { + perror("fopen"); + return 0; + } + + string = malloc(4096); + name = malloc(256); + + jobs = 0; + + while ((p = fgets(string, 4096, f)) != NULL) { + if (sscanf(p, "[%s]", name) != 1) + continue; + + name[strlen(name) - 1] = '\0'; + + rw = DDIR_READ; + bs = global_bs; + direct = 1; + prio = 4; + random = !sequential; + prioclass = 2; + delay = 0; + rate = 0; + + fgetpos(f, &off); + while ((p = fgets(string, 4096, f)) != NULL) { + if (is_empty(p)) + break; + if (!check_int(p, "bs", &bs)) { + bs <<= 10; + fgetpos(f, &off); + continue; + } + if (!check_int(p, "rw", &rw)) { + fgetpos(f, &off); + continue; + } + if (!check_int(p, "prio", &prio)) { + fgetpos(f, &off); + continue; + } + if (!check_int(p, "prioclass", &prioclass)) { + fgetpos(f, &off); + continue; + } + if (!check_int(p, "direct", &direct)) { + fgetpos(f, &off); + continue; + } + if (!check_int(p, "rate", &rate)) { + fgetpos(f, &off); + continue; + } + if (!check_int(p, "delay", &delay)) { + fgetpos(f, &off); + continue; + } + if (!strcmp(p, "sequential")) { + sequential = 1; + fgetpos(f, &off); + continue; + } + if (!strcmp(p, "random")) { + sequential = 0; + fgetpos(f, &off); + continue; + } + } + fsetpos(f, &off); + + add_job(name, rw, bs, direct, (prioclass << IOPRIO_CLASS_SHIFT) | prio, random, delay, rate); + jobs++; + } + + free(string); + free(name); + return jobs; +} + int parse_options(int argc, char *argv[]) { int i; @@ -687,18 +808,19 @@ int parse_options(int argc, char *argv[]) parm++; odirect = !!atoi(parm); break; + case 'f': + if (i + 1 >= argc) { + printf("-f needs file as arg\n"); + break; + } + ini_file = strdup(argv[i+1]); + break; default: - printf("bad option %s\n", argv[1]); + printf("bad option %s\n", argv[i]); break; } } - if (global_bs <= 0) - global_bs = BS; - if (timeout <= 0) - timeout = TIMEOUT; - - printf("%s: %s, bs=%uKiB, timeo=%u, write_stat=%u, odirect=%d\n", argv[0], sequential ? "sequential" : "random", global_bs >> 10, timeout, write_stat, odirect); return i; } @@ -724,12 +846,24 @@ int main(int argc, char *argv[]) atexit(free_shm); i = parse_options(argc, argv); - jobs = parse_jobs(argc, argv, i); - + + if (ini_file) + jobs = parse_jobs_ini(ini_file); + else + jobs = parse_jobs_cmd(argc, argv, i); + + if (global_bs <= 0) + global_bs = BS; + if (timeout <= 0) + timeout = TIMEOUT; + + printf("%s: %s, bs=%uKiB, timeo=%u, write_stat=%u, odirect=%d\n", argv[0], sequential ? "sequential" : "random", global_bs >> 10, timeout, write_stat, odirect); + if (!jobs) { printf("Nothing to do\n"); return 1; - } + } else + printf("%d Clients configured\n", jobs); for (i = 0; i < jobs; i++) { sem_init(&startup_sem, 1, 1); @@ -771,7 +905,7 @@ int main(int argc, char *argv[]) unsigned long bw = 0; if (td->error) - continue; + goto show_stat; if (td->runtime < min_run[td->ddir]) min_run[td->ddir] = td->runtime; @@ -802,6 +936,7 @@ int main(int argc, char *argv[]) write_agg += (td->blocks_read * td->bs) / td->runtime; } +show_stat: show_thread_status(td); } -- 2.25.1