Add support for waiting for other jobs by name
authorAndrey Kuzmin <andrey.v.kuzmin@gmail.com>
Tue, 22 Dec 2015 16:21:25 +0000 (09:21 -0700)
committerJens Axboe <axboe@fb.com>
Tue, 22 Dec 2015 16:21:25 +0000 (09:21 -0700)
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 <axboe@fb.com>
HOWTO
backend.c
cconv.c
examples/waitfor.fio [new file with mode: 0644]
fio.1
fio.h
init.c
libfio.c
options.c
server.h
thread_options.h

diff --git a/HOWTO b/HOWTO
index 482d69651c0362c3afd5863512dc32f7d73f3733..7c24c1b74296aa33cdff97eca9458defd291b6ca 100644 (file)
--- 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.
index 9920e630dc113bff1288ffb9d2f8bf160ac5347c..edc0644a113b259cf2d65cf91f96175a5f50201b 100644 (file)
--- 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 a476aad6376a41c20f94a37f90a87e9d7dff5933..6d8d0b3e336363c8eff7f57b4e18ad68d914ae9b 100644 (file)
--- 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 (file)
index 0000000..95fad00
--- /dev/null
@@ -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 4fe1be27c31cceb3fcf1184fab49c384080b5901..7bdfea3f019f54780b57e5baec828e5708770239 100644 (file)
--- 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 ddc29dbdb038cc40673b2164c3ff0c7a744750f7..66211e9da74d80cafc2398ddf001ed8ff0499a8c 100644 (file)
--- 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 8773138c766af52f8a77397d9bacf53378d14172..991fa1ce3f46f8880345798425018d167568f2b3 100644 (file)
--- 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);
 
        /*
index 6c74852c4f0fb65cdc387f369588a2bd5ed6ca31..c626d15c8a6cd5219b94171b91f4bf7a90ecbbb0 100644 (file)
--- 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)
index 8494713977a215dd9c22110b15edb83cbfd38b85..49d66002602de97baf7899d643cb755c6ec1b204 100644 (file)
--- 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)",
index 5a59d07345f1cdfe4284444599b6faa4ff38917b..9205ae6574a9d1220aa2f7d9c48d25cd65348ea6 100644 (file)
--- 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,
index 6ae0335698c1de41ed60f8b69394106e004e9190..858f307762256695e15752e0a157667f13e00311 100644 (file)
@@ -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];