From 20dc95c47087d486b5d4608fc15b1c2021761923 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Fri, 9 Dec 2005 10:29:35 +0100 Subject: [PATCH] [PATCH] Add support for zoned io sections --- README | 6 +++ fio-ini.c | 60 ++++++++++++++-------- fio.c | 148 ++++++++++++++++++++++++++++++------------------------ fio.h | 5 +- 4 files changed, 130 insertions(+), 89 deletions(-) diff --git a/README b/README index 5ab9d58a..9121d4cf 100644 --- a/README +++ b/README @@ -78,6 +78,12 @@ The format is as follows: stonewall Wait for preceeding jobs to end before running. numjobs=x Create 'x' similar entries for this job thread Use pthreads instead of forked jobs + zonesize=x + zoneskip=y Zone options must be paired. If given, the job + will skip y bytes for every x read/written. This + can be used to gauge hard drive speed over the entire + platter, without reading everything. Both x/y can + include k/m/g suffix. Examples using a job file diff --git a/fio-ini.c b/fio-ini.c index 2941a670..23405898 100644 --- a/fio-ini.c +++ b/fio-ini.c @@ -12,28 +12,30 @@ #include "fio.h" -#define DEF_BS (4096) -#define DEF_TIMEOUT (0) -#define DEF_RATE_CYCLE (1000) -#define DEF_ODIRECT (1) -#define DEF_IO_ENGINE (FIO_SYNCIO) +#define DEF_BS (4096) +#define DEF_TIMEOUT (0) +#define DEF_RATE_CYCLE (1000) +#define DEF_ODIRECT (1) +#define DEF_IO_ENGINE (FIO_SYNCIO) #define DEF_IO_ENGINE_NAME "sync" -#define DEF_SEQUENTIAL (1) -#define DEF_RAND_REPEAT (1) -#define DEF_OVERWRITE (1) -#define DEF_CREATE (1) -#define DEF_INVALIDATE (1) -#define DEF_SYNCIO (0) -#define DEF_RANDSEED (0xb1899bedUL) -#define DEF_BWAVGTIME (500) -#define DEF_CREATE_SER (1) +#define DEF_SEQUENTIAL (1) +#define DEF_RAND_REPEAT (1) +#define DEF_OVERWRITE (1) +#define DEF_CREATE (1) +#define DEF_INVALIDATE (1) +#define DEF_SYNCIO (0) +#define DEF_RANDSEED (0xb1899bedUL) +#define DEF_BWAVGTIME (500) +#define DEF_CREATE_SER (1) #define DEF_CREATE_FSYNC (1) -#define DEF_LOOPS (1) -#define DEF_VERIFY (0) -#define DEF_STONEWALL (0) -#define DEF_NUMJOBS (1) -#define DEF_USE_THREAD (0) -#define DEF_FILE_SIZE (1024 * 1024 * 1024UL) +#define DEF_LOOPS (1) +#define DEF_VERIFY (0) +#define DEF_STONEWALL (0) +#define DEF_NUMJOBS (1) +#define DEF_USE_THREAD (0) +#define DEF_FILE_SIZE (1024 * 1024 * 1024UL) +#define DEF_ZONE_SIZE (0) +#define DEF_ZONE_SKIP (0) static char fio_version_string[] = "fio 1.1"; @@ -130,6 +132,8 @@ static struct thread_data *get_new_job(int global, struct thread_data *parent) 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; @@ -193,6 +197,12 @@ static int add_job(struct thread_data *td, const char *jobname, int prioclass, td->iodepth = 1; } + /* + * only really works for sequential io for now + */ + if (td->zone_size && !td->sequential) + td->zone_size = 0; + td->filetype = FIO_TYPE_FILE; if (!stat(jobname, &sb) && S_ISBLK(sb.st_mode)) td->filetype = FIO_TYPE_BD; @@ -353,7 +363,7 @@ static int str_cnv(char *p, unsigned long long *val) static int check_strcnv(char *p, char *name, unsigned long long *val) { - if (!strstr(p, name)) + if (strncmp(p, name, strlen(name) - 1)) return 1; return str_cnv(p, val); @@ -755,6 +765,14 @@ int parse_jobs_ini(char *file) fgetpos(f, &off); continue; } + if (!check_strcnv(p, "zonesize", &td->zone_size)) { + fgetpos(f, &off); + continue; + } + if (!check_strcnv(p, "zoneskip", &td->zone_skip)) { + fgetpos(f, &off); + continue; + } if (!check_strstore(p, "directory", td->directory)) { fgetpos(f, &off); continue; diff --git a/fio.c b/fio.c index 44c8b380..5dbe9c3d 100644 --- a/fio.c +++ b/fio.c @@ -216,55 +216,6 @@ static void mark_random_map(struct thread_data *td, struct io_u *io_u) io_u->buflen = blocks * td->min_bs; } -static int get_next_offset(struct thread_data *td, unsigned long long *offset) -{ - unsigned long long b, rb; - long r; - - if (!td->sequential) { - unsigned long max_blocks = td->io_size / td->min_bs; - int loops = 50; - - do { - lrand48_r(&td->random_state, &r); - b = ((max_blocks - 1) * r / (RAND_MAX+1.0)); - rb = b + (td->file_offset / td->min_bs); - loops--; - } while (!random_map_free(td, rb) && loops); - - if (!loops) { - if (get_next_free_block(td, &b)) - return 1; - } - } else - b = td->last_bytes / td->min_bs; - - *offset = (b * td->min_bs) + td->file_offset; - if (*offset > td->file_size) - return 1; - - return 0; -} - -static unsigned int get_next_buflen(struct thread_data *td) -{ - unsigned int buflen; - long r; - - if (td->min_bs == td->max_bs) - buflen = td->min_bs; - else { - lrand48_r(&td->bsrange_state, &r); - buflen = (1 + (double) (td->max_bs - 1) * r / (RAND_MAX + 1.0)); - buflen = (buflen + td->min_bs - 1) & ~(td->min_bs - 1); - } - - if (buflen > td->io_size - td->this_io_bytes[td->ddir]) - buflen = td->io_size - td->this_io_bytes[td->ddir]; - - return buflen; -} - static inline void add_stat_sample(struct io_stat *is, unsigned long val) { if (val > is->max_val) @@ -327,6 +278,55 @@ static void add_bw_sample(struct thread_data *td, int ddir) td->stat_io_bytes[ddir] = td->this_io_bytes[ddir]; } +static int get_next_offset(struct thread_data *td, unsigned long long *offset) +{ + unsigned long long b, rb; + long r; + + if (!td->sequential) { + unsigned long max_blocks = td->io_size / td->min_bs; + int loops = 50; + + do { + lrand48_r(&td->random_state, &r); + b = ((max_blocks - 1) * r / (RAND_MAX+1.0)); + rb = b + (td->file_offset / td->min_bs); + loops--; + } while (!random_map_free(td, rb) && loops); + + if (!loops) { + if (get_next_free_block(td, &b)) + return 1; + } + } else + b = td->last_pos / td->min_bs; + + *offset = (b * td->min_bs) + td->file_offset; + if (*offset > td->file_size) + return 1; + + return 0; +} + +static unsigned int get_next_buflen(struct thread_data *td) +{ + unsigned int buflen; + long r; + + if (td->min_bs == td->max_bs) + buflen = td->min_bs; + else { + lrand48_r(&td->bsrange_state, &r); + buflen = (1 + (double) (td->max_bs - 1) * r / (RAND_MAX + 1.0)); + buflen = (buflen + td->min_bs - 1) & ~(td->min_bs - 1); + } + + if (buflen > td->io_size - td->this_io_bytes[td->ddir]) + buflen = td->io_size - td->this_io_bytes[td->ddir]; + + return buflen; +} + /* * busy looping version for the last few usec */ @@ -566,6 +566,19 @@ static void populate_io_u(struct thread_data *td, struct io_u *io_u) memcpy(io_u->buf, &hdr, sizeof(hdr)); } +static int td_io_prep(struct thread_data *td, struct io_u *io_u, int read) +{ + if (read) + io_u->ddir = DDIR_READ; + else + io_u->ddir = DDIR_WRITE; + + if (td->io_prep && td->io_prep(td, io_u)) + return 1; + + return 0; +} + static void put_io_u(struct thread_data *td, struct io_u *io_u) { list_del(&io_u->list); @@ -591,19 +604,6 @@ static struct io_u *__get_io_u(struct thread_data *td) return io_u; } -static int td_io_prep(struct thread_data *td, struct io_u *io_u, int read) -{ - if (read) - io_u->ddir = DDIR_READ; - else - io_u->ddir = DDIR_WRITE; - - if (td->io_prep && td->io_prep(td, io_u)) - return 1; - - return 0; -} - static struct io_u *get_io_u(struct thread_data *td) { struct io_u *io_u; @@ -612,6 +612,11 @@ static struct io_u *get_io_u(struct thread_data *td) if (!io_u) return NULL; + if (td->zone_bytes >= td->zone_size) { + td->zone_bytes = 0; + td->last_pos += td->zone_skip; + } + if (get_next_offset(td, &io_u->offset)) { put_io_u(td, io_u); return NULL; @@ -634,7 +639,7 @@ static struct io_u *get_io_u(struct thread_data *td) if (!td->sequential) mark_random_map(td, io_u); - td->last_bytes += io_u->buflen; + td->last_pos += io_u->buflen; if (td->verify != VERIFY_NONE) populate_io_u(td, io_u); @@ -751,11 +756,13 @@ static void io_completed(struct thread_data *td, struct io_u *io_u, gettimeofday(&e, NULL); if (!io_u->error) { + unsigned int bytes = io_u->buflen - io_u->resid; int idx = io_u->ddir; td->io_blocks[idx]++; - td->io_bytes[idx] += (io_u->buflen - io_u->resid); - td->this_io_bytes[idx] += (io_u->buflen - io_u->resid); + td->io_bytes[idx] += bytes; + td->zone_bytes += bytes; + td->this_io_bytes[idx] += bytes; msec = mtime_since(&io_u->issue_time, &e); @@ -765,7 +772,7 @@ static void io_completed(struct thread_data *td, struct io_u *io_u, if (td_write(td) && io_u->ddir == DDIR_WRITE) log_io_piece(td, io_u); - icd->bytes_done[idx] += (io_u->buflen - io_u->resid); + icd->bytes_done[idx] += bytes; } else icd->error = io_u->error; } @@ -1246,6 +1253,9 @@ static int get_file_size(struct thread_data *td) return 1; } + if (!td->zone_size) + td->zone_size = td->io_size; + td->total_io_size = td->io_size * td->loops; return 0; } @@ -1613,9 +1623,10 @@ static void clear_io_state(struct thread_data *td) if (td->io_engine == FIO_SYNCIO) lseek(td->fd, SEEK_SET, 0); - td->last_bytes = 0; + td->last_pos = 0; td->stat_io_bytes[0] = td->stat_io_bytes[1] = 0; td->this_io_bytes[0] = td->this_io_bytes[1] = 0; + td->zone_bytes = 0; if (td->file_map) memset(td->file_map, 0, td->num_maps * sizeof(long)); @@ -1922,6 +1933,9 @@ static void print_thread_status(void) if (td->verify) bytes_total += td->total_io_size; + if (td->zone_size) + bytes_total /= (td->zone_skip / td->zone_size); + bytes_done += td->io_bytes[DDIR_READ] +td->io_bytes[DDIR_WRITE]; check_str_update(td); diff --git a/fio.h b/fio.h index f1e49a50..da0e1fd2 100644 --- a/fio.h +++ b/fio.h @@ -134,6 +134,8 @@ struct thread_data { unsigned int loops; unsigned long long file_size; unsigned long long file_offset; + unsigned long long zone_size; + unsigned long long zone_skip; unsigned int sync_io; unsigned int mem_type; unsigned int verify; @@ -176,8 +178,9 @@ struct thread_data { unsigned long io_blocks[2]; unsigned long io_bytes[2]; + unsigned long zone_bytes; unsigned long this_io_bytes[2]; - unsigned long last_bytes; + unsigned long long last_pos; sem_t mutex; struct drand48_data random_state; -- 2.25.1