From: Andrey Kuzmin Date: Tue, 22 Dec 2015 16:21:25 +0000 (-0700) Subject: Add support for waiting for other jobs by name X-Git-Tag: fio-2.3~6 X-Git-Url: https://git.kernel.dk/?p=fio.git;a=commitdiff_plain;h=9cc8cb91d355d7e47d5b930b352087ec71f85f85;hp=b5f4ef37543b46f9ef44cabf691a92d6592fb286 Add support for waiting for other jobs by name We have stonewall to barrier against previous jobs before a new job runs. This adds support for 'wait_for', which allows the user to explicitly wait for a given job before starting. Signed-off-by: Jens Axboe --- diff --git a/HOWTO b/HOWTO index 482d6965..7c24c1b7 100644 --- a/HOWTO +++ b/HOWTO @@ -305,6 +305,16 @@ name=str ASCII name of the job. This may be used to override the special purpose of also signaling the start of a new job. +wait_for=str Specifies the name of the already defined job to wait + for. Single waitee name only may be specified. If set, the job + won't be started until all workers of the waitee job are done. + + Wait_for operates on the job name basis, so there are a few + limitations. First, the waitee must be defined prior to the + waiter job (meaning no forward references). Second, if a job + is being referenced as a waitee, it must have a unique name + (no duplicate waitees). + description=str Text description of the job. Doesn't do anything except dump this text description when this job is run. It's not parsed. diff --git a/backend.c b/backend.c index 9920e630..edc0644a 100644 --- a/backend.c +++ b/backend.c @@ -1978,6 +1978,32 @@ mounted: return true; } +static bool waitee_running(struct thread_data *me) +{ + const char *waitee = me->o.wait_for; + const char *self = me->o.name; + struct thread_data *td; + int i; + + if (!waitee) + return false; + + for_each_td(td, i) { + if (!strcmp(td->o.name, self) || strcmp(td->o.name, waitee)) + continue; + + if (td->runstate < TD_EXITED) { + dprint(FD_PROCESS, "%s fenced by %s(%s)\n", + self, td->o.name, + runstate_to_name(td->runstate)); + return true; + } + } + + dprint(FD_PROCESS, "%s: %s completed, can run\n", self, waitee); + return false; +} + /* * Main function for kicking off and reaping jobs, as needed. */ @@ -2101,6 +2127,12 @@ reap: break; } + if (waitee_running(td)) { + dprint(FD_PROCESS, "%s: waiting for %s\n", + td->o.name, td->o.wait_for); + break; + } + init_disk_util(td); td->rusage_sem = fio_mutex_init(FIO_MUTEX_LOCKED); diff --git a/cconv.c b/cconv.c index a476aad6..6d8d0b3e 100644 --- a/cconv.c +++ b/cconv.c @@ -25,6 +25,7 @@ static void free_thread_options_to_cpu(struct thread_options *o) { free(o->description); free(o->name); + free(o->wait_for); free(o->directory); free(o->filename); free(o->filename_format); @@ -54,6 +55,7 @@ void convert_thread_options_to_cpu(struct thread_options *o, string_to_cpu(&o->description, top->description); string_to_cpu(&o->name, top->name); + string_to_cpu(&o->wait_for, top->wait_for); string_to_cpu(&o->directory, top->directory); string_to_cpu(&o->filename, top->filename); string_to_cpu(&o->filename_format, top->filename_format); @@ -276,6 +278,7 @@ void convert_thread_options_to_net(struct thread_options_pack *top, string_to_net(top->description, o->description); string_to_net(top->name, o->name); + string_to_net(top->wait_for, o->wait_for); string_to_net(top->directory, o->directory); string_to_net(top->filename, o->filename); string_to_net(top->filename_format, o->filename_format); diff --git a/examples/waitfor.fio b/examples/waitfor.fio new file mode 100644 index 00000000..95fad005 --- /dev/null +++ b/examples/waitfor.fio @@ -0,0 +1,35 @@ +[global] +threads=1 +group_reporting=1 +filename=/tmp/data +filesize=128m + +[writers] +rw=write +bs=128k +numjobs=4 +runtime=10 + +[readers] +new_group +wait_for=writers +rw=randread +bs=4k +numjobs=4 +runtime=10 + +[writers2] +new_group +wait_for=readers +rw=randwrite +bs=4k +numjobs=4 +runtime=10 + +[readers2] +new_group +wait_for=writers2 +rw=randread +bs=4k +numjobs=4 +runtime=10 diff --git a/fio.1 b/fio.1 index 4fe1be27..7bdfea3f 100644 --- a/fio.1 +++ b/fio.1 @@ -180,6 +180,14 @@ a ':' character. May be used to override the job name. On the command line, this parameter has the special purpose of signalling the start of a new job. .TP +.BI wait_for \fR=\fPstr +Specifies the name of the already defined job to wait for. Single waitee name +only may be specified. If set, the job won't be started until all workers of +the waitee job are done. Wait_for operates on the job name basis, so there are +a few limitations. First, the waitee must be defined prior to the waiter job +(meaning no forward references). Second, if a job is being referenced as a +waitee, it must have a unique name (no duplicate waitees). +.TP .BI description \fR=\fPstr Human-readable description of the job. It is printed when the job is run, but otherwise has no special purpose. diff --git a/fio.h b/fio.h index ddc29dbd..66211e9d 100644 --- a/fio.h +++ b/fio.h @@ -542,6 +542,7 @@ enum { extern void td_set_runstate(struct thread_data *, int); extern int td_bump_runstate(struct thread_data *, int); extern void td_restore_runstate(struct thread_data *, int); +extern const char *runstate_to_name(int runstate); /* * Allow 60 seconds for a job to quit on its own, otherwise reap with diff --git a/init.c b/init.c index 8773138c..991fa1ce 100644 --- a/init.c +++ b/init.c @@ -1217,6 +1217,49 @@ static void gen_log_name(char *name, size_t size, const char *logtype, snprintf(name, size, "%s_%s.%s", logname, logtype, suf); } +static int check_waitees(char *waitee) +{ + struct thread_data *td; + int i, ret = 0; + + for_each_td(td, i) { + if (td->subjob_number) + continue; + + ret += !strcmp(td->o.name, waitee); + } + + return ret; +} + +static bool wait_for_ok(const char *jobname, struct thread_options *o) +{ + int nw; + + if (!o->wait_for) + return true; + + if (!strcmp(jobname, o->wait_for)) { + log_err("%s: a job cannot wait for itself (wait_for=%s).\n", + jobname, o->wait_for); + return false; + } + + if (!(nw = check_waitees(o->wait_for))) { + log_err("%s: waitee job %s unknown.\n", jobname, o->wait_for); + return false; + } + + if (nw > 1) { + log_err("%s: multiple waitees %s found,\n" + "please avoid duplicates when using wait_for option.\n", + jobname, o->wait_for); + return false; + } + + return true; +} + /* * Adds a job to the list of things todo. Sanitizes the various options * to make sure we don't have conflicts, and initializes various @@ -1273,6 +1316,12 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num, if (fixup_options(td)) goto err; + /* + * Belongs to fixup_options, but o->name is not necessarily set as yet + */ + if (!wait_for_ok(jobname, o)) + goto err; + flow_init_job(td); /* diff --git a/libfio.c b/libfio.c index 6c74852c..c626d15c 100644 --- a/libfio.c +++ b/libfio.c @@ -190,7 +190,7 @@ static const char *td_runstates[] = { "REAPED", }; -static const char *runstate_to_name(int runstate) +const char *runstate_to_name(int runstate) { compiletime_assert(TD_LAST == 12, "td runstate list"); if (runstate >= 0 && runstate < TD_LAST) diff --git a/options.c b/options.c index 84947139..49d66002 100644 --- a/options.c +++ b/options.c @@ -1228,6 +1228,15 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .category = FIO_OPT_C_GENERAL, .group = FIO_OPT_G_DESC, }, + { + .name = "wait_for", + .lname = "Waitee name", + .type = FIO_OPT_STR_STORE, + .off1 = td_var_offset(wait_for), + .help = "Name of the job this one wants to wait for before starting", + .category = FIO_OPT_C_GENERAL, + .group = FIO_OPT_G_DESC, + }, { .name = "filename", .lname = "Filename(s)", diff --git a/server.h b/server.h index 5a59d073..9205ae65 100644 --- a/server.h +++ b/server.h @@ -38,7 +38,7 @@ struct fio_net_cmd_reply { }; enum { - FIO_SERVER_VER = 50, + FIO_SERVER_VER = 51, FIO_SERVER_MAX_FRAGMENT_PDU = 1024, FIO_SERVER_MAX_CMD_MB = 2048, diff --git a/thread_options.h b/thread_options.h index 6ae03356..858f3077 100644 --- a/thread_options.h +++ b/thread_options.h @@ -40,6 +40,7 @@ struct thread_options { uint64_t set_options[NR_OPTS_SZ]; char *description; char *name; + char *wait_for; char *directory; char *filename; char *filename_format; @@ -289,6 +290,7 @@ struct thread_options_pack { uint64_t set_options[NR_OPTS_SZ]; uint8_t description[FIO_TOP_STR_MAX]; uint8_t name[FIO_TOP_STR_MAX]; + uint8_t wait_for[FIO_TOP_STR_MAX]; uint8_t directory[FIO_TOP_STR_MAX]; uint8_t filename[FIO_TOP_STR_MAX]; uint8_t filename_format[FIO_TOP_STR_MAX];