From b990b5c06801d6d25e3fcc5415efbbe7bb23341e Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 14 Sep 2006 09:48:22 +0200 Subject: [PATCH] [PATCH] Basic support for a cpu cycle eater job This will allow you to put some cpu load on the box, while other threads are doing IO. --- README | 3 +++ fio.c | 48 +++++++++++++++++++++++++++++++++++++++++++++--- fio.h | 16 ++++++++++++++-- init.c | 23 +++++++++++++++++++---- ioengines.c | 14 ++++++++++++++ os.h | 1 + time.c | 2 +- 7 files changed, 97 insertions(+), 10 deletions(-) diff --git a/README b/README index f323384f..359fc2d2 100644 --- a/README +++ b/README @@ -142,6 +142,9 @@ The job file parameters are: exec_prerun=x Run 'x' before job io is begun. exec_postrun=x Run 'x' after job io has finished. ioscheduler=x Use ioscheduler 'x' for this job. + cpuload=x For a CPU io thread, percentage of CPU time to attempt + to burn. + cpuchunks=x Split burn cycles into pieces of x. Examples using a job file diff --git a/fio.c b/fio.c index c83e8921..445babd5 100644 --- a/fio.c +++ b/fio.c @@ -777,6 +777,31 @@ static void do_verify(struct thread_data *td) td_set_runstate(td, TD_RUNNING); } +/* + * Not really an io thread, all it does is burn CPU cycles in the specified + * manner. + */ +static void do_cpuio(struct thread_data *td) +{ + struct timeval e; + int split = 100 / td->cpuload; + int i = 0; + + while (!td->terminate) { + gettimeofday(&e, NULL); + + if (runtime_exceeded(td, &e)) + break; + + if (!(i % split)) + __usec_sleep(10000); + else + usec_sleep(td, 10000); + + i++; + } +} + /* * Main IO worker function. It retrieves io_u's to process and queues * and reaps them, checking for rate and errors along the way. @@ -890,6 +915,8 @@ static int init_io(struct thread_data *td) return fio_sgio_init(td); else if (td->io_engine == FIO_SPLICEIO) return fio_spliceio_init(td); + else if (td->io_engine == FIO_CPUIO) + return fio_cpuio_init(td); else { log_err("bad io_engine %d\n", td->io_engine); return 1; @@ -929,6 +956,9 @@ static int init_io_u(struct thread_data *td) int i, max_units; char *p; + if (td->io_engine == FIO_CPUIO) + return 0; + if (td->io_engine & FIO_SYNCIO) max_units = 1; else @@ -1199,6 +1229,9 @@ static int setup_file(struct thread_data *td) struct stat st; int flags = 0; + if (td->io_engine == FIO_CPUIO) + return 0; + if (stat(td->file_name, &st) == -1) { if (errno != ENOENT) { td_verror(td, errno); @@ -1390,7 +1423,10 @@ static void *thread_main(void *data) clear_io_state(td); prune_io_piece_log(td); - do_io(td); + if (td->io_engine == FIO_CPUIO) + do_cpuio(td); + else + do_io(td); td->runtime[td->ddir] += mtime_since_now(&td->start); if (td_rw(td) && td->io_bytes[td->ddir ^ 1]) @@ -1694,14 +1730,17 @@ static void print_thread_status(void) */ static void reap_threads(int *nr_running, int *t_rate, int *m_rate) { - int i; + int i, cputhreads; /* * reap exited threads (TD_EXITED -> TD_REAPED) */ - for (i = 0; i < thread_number; i++) { + for (i = 0, cputhreads = 0; i < thread_number; i++) { struct thread_data *td = &threads[i]; + if (td->io_engine == FIO_CPUIO) + cputhreads++; + if (td->runstate != TD_EXITED) continue; @@ -1719,6 +1758,9 @@ static void reap_threads(int *nr_running, int *t_rate, int *m_rate) (*m_rate) -= td->ratemin; (*t_rate) -= td->rate; } + + if (*nr_running == cputhreads) + terminate_threads(TERMINATE_ALL); } static void fio_unpin_memory(void *pinned) diff --git a/fio.h b/fio.h index a87fcef4..ee47599b 100644 --- a/fio.h +++ b/fio.h @@ -131,6 +131,7 @@ enum fio_iotype { FIO_POSIXAIO = 1 << 3, FIO_SGIO = 1 << 4, FIO_SPLICEIO = 1 << 5 | FIO_SYNCIO, + FIO_CPUIO = 1 << 6, }; /* @@ -261,6 +262,12 @@ struct thread_data { unsigned long *file_map; unsigned int num_maps; + /* + * CPU "io" cycle burner + */ + unsigned int cpuload; + unsigned int cpucycle; + /* * bandwidth and latency stats */ @@ -307,13 +314,17 @@ struct thread_data { struct list_head io_log_list; }; -#define td_verror(td, err) \ +#define __td_verror(td, err, msg) \ do { \ int e = (err); \ (td)->error = e; \ - snprintf(td->verror, sizeof(td->verror) - 1, "file:%s:%d, error=%s", __FILE__, __LINE__, strerror(e)); \ + snprintf(td->verror, sizeof(td->verror) - 1, "file:%s:%d, error=%s", __FILE__, __LINE__, (msg)); \ } while (0) + +#define td_verror(td, err) __td_verror((td), (err), strerror((err))) +#define td_vmsg(td, err, msg) __td_verror((td), (err), (msg)) + extern struct io_u *__get_io_u(struct thread_data *); extern void put_io_u(struct thread_data *, struct io_u *); @@ -409,6 +420,7 @@ extern unsigned long utime_since(struct timeval *, struct timeval *); extern unsigned long mtime_since(struct timeval *, struct timeval *); extern unsigned long mtime_since_now(struct timeval *); extern unsigned long time_since_now(struct timeval *); +extern void __usec_sleep(unsigned int); extern void usec_sleep(struct thread_data *, unsigned long); extern void rate_throttle(struct thread_data *, unsigned long, unsigned int); diff --git a/init.c b/init.c index c1e26d3f..1ffd6ff0 100644 --- a/init.c +++ b/init.c @@ -196,9 +196,12 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num) ddir = td->ddir + (!td->sequential << 1) + (td->iomix << 2); 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) + if (!job_add_num) { + if (td->io_engine == FIO_CPUIO) + fprintf(f_out, "%s: ioengine=cpu, cpuload=%u, cpucycle=%u\n", td->name, td->cpuload, td->cpucycle); + else + 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"); } @@ -616,9 +619,13 @@ static int str_ioengine_cb(struct thread_data *td, char *str) strcpy(td->io_engine_name, "splice"); td->io_engine = FIO_SPLICEIO; return 0; + } else if (!strncmp(str, "cpu", 3)) { + strcpy(td->io_engine_name, "cpu"); + td->io_engine = FIO_CPUIO; + return 0; } - log_err("fio: ioengine: { linuxaio, aio, libaio }, posixaio, sync, mmap, sgio, splice\n"); + log_err("fio: ioengine: { linuxaio, aio, libaio }, posixaio, sync, mmap, sgio, splice, cpu\n"); return 1; } @@ -726,6 +733,14 @@ int parse_jobs_ini(char *file, int stonewall_flag) fgetpos(f, &off); continue; } + if (!check_int(p, "cpuload", &td->cpuload)) { + fgetpos(f, &off); + continue; + } + if (!check_int(p, "cpuchunks", &td->cpucycle)) { + fgetpos(f, &off); + continue; + } if (!check_int(p, "thinktime", &td->thinktime)) { fgetpos(f, &off); continue; diff --git a/ioengines.c b/ioengines.c index a78088dd..01492bcf 100644 --- a/ioengines.c +++ b/ioengines.c @@ -918,3 +918,17 @@ int fio_spliceio_init(struct thread_data *td) } #endif /* FIO_HAVE_SPLICE */ + +int fio_cpuio_init(struct thread_data *td) +{ + if (!td->cpuload) { + td_vmsg(td, EINVAL, "cpu thread needs rate"); + return 1; + } else if (td->cpuload > 100) + td->cpuload = 100; + + td->read_iolog = td->write_iolog = 0; + td->fd = -1; + + return 0; +} diff --git a/os.h b/os.h index 1bce2f64..68660b6d 100644 --- a/os.h +++ b/os.h @@ -54,5 +54,6 @@ extern int fio_syncio_init(struct thread_data *); extern int fio_mmapio_init(struct thread_data *); extern int fio_sgio_init(struct thread_data *); extern int fio_spliceio_init(struct thread_data *); +extern int fio_cpuio_init(struct thread_data *); #endif diff --git a/time.c b/time.c index 52462633..c7f898be 100644 --- a/time.c +++ b/time.c @@ -60,7 +60,7 @@ unsigned long time_since_now(struct timeval *s) /* * busy looping version for the last few usec */ -static void __usec_sleep(unsigned int usec) +void __usec_sleep(unsigned int usec) { struct timeval start; -- 2.25.1