Separate the act of adding and starting a job

CMD_JOB or CMD_JOB_LINE only adds the necessary jobs, which will
automatically return the job option info to the client. Let the
client do what it wants with the options, then have the client
send a CMD_RUN command to actually start the jobs.

Next we'll have a command for passing back edited options, so that
the client can update existing options.

This adds a new button to gfio, to note this split of add and run
as well.

Signed-off-by: Jens Axboe <axboe@kernel.dk>
diff --git a/client.c b/client.c
index a77dc93..10dc53b 100644
--- a/client.c
+++ b/client.c
@@ -40,15 +40,6 @@
 
 static struct timeval eta_tv;
 
-enum {
-	Client_created		= 0,
-	Client_connected	= 1,
-	Client_started		= 2,
-	Client_running		= 3,
-	Client_stopped		= 4,
-	Client_exited		= 5,
-};
-
 static FLIST_HEAD(client_list);
 static FLIST_HEAD(eta_list);
 
@@ -469,6 +460,33 @@
 	return !nr_clients;
 }
 
+int fio_start_client(struct fio_client *client)
+{
+	dprint(FD_NET, "client: start %s\n", client->hostname);
+	return fio_net_send_simple_cmd(client->fd, FIO_NET_CMD_RUN, 0, NULL);
+}
+
+int fio_start_all_clients(void)
+{
+	struct fio_client *client;
+	struct flist_head *entry, *tmp;
+	int ret;
+
+	dprint(FD_NET, "client: start all\n");
+
+	flist_for_each_safe(entry, tmp, &client_list) {
+		client = flist_entry(entry, struct fio_client, list);
+
+		ret = fio_start_client(client);
+		if (ret) {
+			remove_client(client);
+			continue;
+		}
+	}
+
+	return flist_empty(&client_list);
+}
+
 /*
  * Send file contents to server backend. We could use sendfile(), but to remain
  * more portable lets just read/write the darn thing.
@@ -891,8 +909,8 @@
 	if (!cmd)
 		return 0;
 
-	dprint(FD_NET, "client: got cmd op %s from %s\n",
-				fio_server_op(cmd->opcode), client->hostname);
+	dprint(FD_NET, "client: got cmd op %s from %s (pdu=%u)\n",
+		fio_server_op(cmd->opcode), client->hostname, cmd->pdu_len);
 
 	switch (cmd->opcode) {
 	case FIO_NET_CMD_QUIT:
diff --git a/client.h b/client.h
index eccac92..9157aff 100644
--- a/client.h
+++ b/client.h
@@ -11,6 +11,15 @@
 struct fio_net_cmd;
 struct client_ops;
 
+enum {
+	Client_created		= 0,
+	Client_connected	= 1,
+	Client_started		= 2,
+	Client_running		= 3,
+	Client_stopped		= 4,
+	Client_exited		= 5,
+};
+
 struct fio_client {
 	struct flist_head list;
 	struct flist_head hash_list;
@@ -93,6 +102,8 @@
 };
 
 extern int fio_clients_connect(void);
+extern int fio_start_client(struct fio_client *);
+extern int fio_start_all_clients(void);
 extern int fio_clients_send_ini(const char *);
 extern int fio_handle_clients(struct client_ops *);
 extern int fio_client_add(struct client_ops *, const char *, void **);
diff --git a/fio.c b/fio.c
index d628cfc..9a6c31a 100644
--- a/fio.c
+++ b/fio.c
@@ -45,8 +45,10 @@
 	if (parse_options(argc, argv))
 		return 1;
 
-	if (nr_clients)
+	if (nr_clients) {
+		if (fio_start_all_clients())
+			return 1;
 		return fio_handle_clients(&fio_client_ops);
-	else
+	} else
 		return fio_backend();
 }
diff --git a/gfio.c b/gfio.c
index 4521f75..877438a 100644
--- a/gfio.c
+++ b/gfio.c
@@ -44,6 +44,7 @@
 
 static void connect_clicked(GtkWidget *widget, gpointer data);
 static void start_job_clicked(GtkWidget *widget, gpointer data);
+static void send_clicked(GtkWidget *widget, gpointer data);
 
 static struct button_spec {
 	const char *buttontext;
@@ -52,10 +53,11 @@
 	const int start_insensitive;
 } buttonspeclist[] = {
 #define CONNECT_BUTTON 0
-#define START_JOB_BUTTON 1
+#define SEND_BUTTON 1
+#define START_JOB_BUTTON 2
 	{ "Connect", connect_clicked, "Connect to host", 0 },
-	{ "Start Job",
-		start_job_clicked,
+	{ "Send", send_clicked, "Send job description to host", 1 },
+	{ "Start Job", start_job_clicked,
 		"Send current fio job to fio server to be executed", 1 },
 };
 
@@ -114,13 +116,14 @@
 
 	struct graph *iops_graph;
 	struct graph *bandwidth_graph;
-	struct fio_client *client;
+	struct gfio_client *client;
 	int nr_job_files;
 	char **job_files;
 } ui;
 
 struct gfio_client {
 	struct gui *ui;
+	struct fio_client *client;
 	GtkWidget *results_widget;
 	GtkWidget *disk_util_frame;
 	GtkWidget *err_entry;
@@ -223,13 +226,14 @@
 static void gfio_set_connected(struct gui *ui, int connected)
 {
 	if (connected) {
-		gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
+		gtk_widget_set_sensitive(ui->button[SEND_BUTTON], 1);
 		ui->connected = 1;
 		gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Disconnect");
 		gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 1);
 	} else {
 		ui->connected = 0;
 		gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Connect");
+		gtk_widget_set_sensitive(ui->button[SEND_BUTTON], 0);
 		gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
 		gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 1);
 	}
@@ -1321,15 +1325,6 @@
 	return ret;
 }
 
-static void start_job_thread(struct gui *ui)
-{
-	if (send_job_files(ui)) {
-		printf("Yeah, I didn't really like those options too much.\n");
-		gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
-		return;
-	}
-}
-
 static void *server_thread(void *arg)
 {
 	is_backend = 1;
@@ -1352,9 +1347,10 @@
                 gpointer data)
 {
 	struct gui *ui = data;
+	struct gfio_client *gc = ui->client;
 
 	gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
-	start_job_thread(ui);
+	fio_net_send_simple_cmd(gc->client->fd, FIO_NET_CMD_RUN, 0, NULL);
 }
 
 static void file_open(GtkWidget *w, gpointer data);
@@ -1371,6 +1367,7 @@
 		if (!fio_clients_connect()) {
 			pthread_create(&ui->t, NULL, job_thread, NULL);
 			gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 0);
+			gtk_widget_set_sensitive(ui->button[SEND_BUTTON], 1);
 		}
 	} else {
 		fio_clients_terminate();
@@ -1379,6 +1376,19 @@
 	}
 }
 
+static void send_clicked(GtkWidget *widget, gpointer data)
+{
+	struct gui *ui = data;
+
+	if (send_job_files(ui)) {
+		printf("Yeah, I didn't really like those options too much.\n");
+		gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
+	}
+
+	gtk_widget_set_sensitive(ui->button[SEND_BUTTON], 0);
+	gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
+}
+
 static void add_button(struct gui *ui, int i, GtkWidget *buttonbox,
 			struct button_spec *buttonspec)
 {
@@ -1575,6 +1585,9 @@
 	gc = malloc(sizeof(*gc));
 	memset(gc, 0, sizeof(*gc));
 	gc->ui = ui;
+	gc->client = client;
+
+	ui->client = gc;
 
 	client->client_data = gc;
 }
diff --git a/server.c b/server.c
index dfbe337..1085180 100644
--- a/server.c
+++ b/server.c
@@ -51,8 +51,9 @@
 	"START",
 	"STOP",
 	"DISK_UTIL",
-	"RUN",
+	"SERVER_START",
 	"ADD_JOB",
+	"CMD_RUN"
 };
 
 const char *fio_server_op(unsigned int op)
@@ -68,7 +69,7 @@
 
 int fio_send_data(int sk, const void *p, unsigned int len)
 {
-	assert(len <= sizeof(struct fio_net_cmd) + FIO_SERVER_MAX_PDU);
+	assert(len <= sizeof(struct fio_net_cmd) + FIO_SERVER_MAX_FRAGMENT_PDU);
 
 	do {
 		int ret = send(sk, p, len, 0);
@@ -146,7 +147,7 @@
 		return 1;
 	}
 
-	if (cmd->pdu_len > FIO_SERVER_MAX_PDU) {
+	if (cmd->pdu_len > FIO_SERVER_MAX_FRAGMENT_PDU) {
 		log_err("fio: command payload too large: %u\n", cmd->pdu_len);
 		return 1;
 	}
@@ -262,8 +263,8 @@
 
 	do {
 		this_len = size;
-		if (this_len > FIO_SERVER_MAX_PDU)
-			this_len = FIO_SERVER_MAX_PDU;
+		if (this_len > FIO_SERVER_MAX_FRAGMENT_PDU)
+			this_len = FIO_SERVER_MAX_FRAGMENT_PDU;
 
 		if (!cmd || cur_len < sizeof(*cmd) + this_len) {
 			if (cmd)
@@ -339,21 +340,11 @@
 	return fio_net_send_simple_cmd(server_fd, FIO_NET_CMD_QUIT, 0, NULL);
 }
 
-static int handle_job_cmd(struct fio_net_cmd *cmd)
+static int handle_run_cmd(struct fio_net_cmd *cmd)
 {
-	char *buf = (char *) cmd->payload;
-	struct cmd_start_pdu spdu;
 	struct cmd_end_pdu epdu;
 	int ret;
 
-	if (parse_jobs_ini(buf, 1, 0)) {
-		fio_server_send_quit_cmd();
-		return -1;
-	}
-
-	spdu.jobs = cpu_to_le32(thread_number);
-	fio_net_send_cmd(server_fd, FIO_NET_CMD_START, &spdu, sizeof(spdu), 0);
-
 	ret = fio_backend();
 
 	epdu.error = ret;
@@ -365,14 +356,30 @@
 	return ret;
 }
 
+static int handle_job_cmd(struct fio_net_cmd *cmd)
+{
+	char *buf = (char *) cmd->payload;
+	struct cmd_start_pdu spdu;
+
+	if (parse_jobs_ini(buf, 1, 0)) {
+		fio_server_send_quit_cmd();
+		return -1;
+	}
+
+	spdu.jobs = cpu_to_le32(thread_number);
+	fio_net_send_cmd(server_fd, FIO_NET_CMD_START, &spdu, sizeof(spdu), 0);
+	return 0;
+}
+
 static int handle_jobline_cmd(struct fio_net_cmd *cmd)
 {
 	void *pdu = cmd->payload;
 	struct cmd_single_line_pdu *cslp;
 	struct cmd_line_pdu *clp;
 	unsigned long offset;
+	struct cmd_start_pdu spdu;
 	char **argv;
-	int ret, i;
+	int i;
 
 	clp = pdu;
 	clp->lines = le16_to_cpu(clp->lines);
@@ -397,13 +404,9 @@
 
 	free(argv);
 
-	fio_net_send_simple_cmd(server_fd, FIO_NET_CMD_START, 0, NULL);
-
-	ret = fio_backend();
-	fio_server_send_quit_cmd();
-	reset_fio_state();
-	first_cmd_check = 0;
-	return ret;
+	spdu.jobs = cpu_to_le32(thread_number);
+	fio_net_send_cmd(server_fd, FIO_NET_CMD_START, &spdu, sizeof(spdu), 0);
+	return 0;
 }
 
 static int handle_probe_cmd(struct fio_net_cmd *cmd)
@@ -498,6 +501,9 @@
 	case FIO_NET_CMD_SEND_ETA:
 		ret = handle_send_eta_cmd(cmd);
 		break;
+	case FIO_NET_CMD_RUN:
+		ret = handle_run_cmd(cmd);
+		break;
 	default:
 		log_err("fio: unknown opcode: %s\n",fio_server_op(cmd->opcode));
 		ret = 1;
diff --git a/server.h b/server.h
index bc044e9..7a801bf 100644
--- a/server.h
+++ b/server.h
@@ -38,9 +38,9 @@
 };
 
 enum {
-	FIO_SERVER_VER			= 9,
+	FIO_SERVER_VER			= 10,
 
-	FIO_SERVER_MAX_PDU		= 1024,
+	FIO_SERVER_MAX_FRAGMENT_PDU	= 1024,
 
 	FIO_NET_CMD_QUIT		= 1,
 	FIO_NET_CMD_EXIT		= 2,
@@ -57,7 +57,8 @@
 	FIO_NET_CMD_DU			= 13,
 	FIO_NET_CMD_SERVER_START	= 14,
 	FIO_NET_CMD_ADD_JOB		= 15,
-	FIO_NET_CMD_NR			= 16,
+	FIO_NET_CMD_RUN			= 16,
+	FIO_NET_CMD_NR			= 17,
 
 	FIO_NET_CMD_F_MORE		= 1UL << 0,