+ struct client_eta *eta;
+ int skipped = 0;
+
+ dprint(FD_NET, "client: request eta (%d)\n", nr_clients);
+
+ eta = malloc(sizeof(*eta));
+ memset(&eta->eta, 0, sizeof(eta->eta));
+ eta->pending = nr_clients;
+
+ flist_for_each(entry, &client_list) {
+ client = flist_entry(entry, struct fio_client, list);
+
+ if (!flist_empty(&client->eta_list)) {
+ skipped++;
+ continue;
+ }
+ if (client->state != Client_running)
+ continue;
+
+ assert(!client->eta_in_flight);
+ flist_add_tail(&client->eta_list, &eta_list);
+ client->eta_in_flight = eta;
+ fio_net_send_simple_cmd(client->fd, FIO_NET_CMD_SEND_ETA,
+ (uintptr_t) eta, &client->cmd_list);
+ }
+
+ while (skipped--)
+ fio_client_dec_jobs_eta(eta, ops->eta);
+
+ dprint(FD_NET, "client: requested eta tag %p\n", eta);
+}
+
+static int client_check_cmd_timeout(struct fio_client *client,
+ struct timeval *now)
+{
+ struct fio_net_int_cmd *cmd;
+ struct flist_head *entry, *tmp;
+ int ret = 0;
+
+ flist_for_each_safe(entry, tmp, &client->cmd_list) {
+ cmd = flist_entry(entry, struct fio_net_int_cmd, list);
+
+ if (mtime_since(&cmd->tv, now) < FIO_NET_CLIENT_TIMEOUT)
+ continue;
+
+ log_err("fio: client %s, timeout on cmd %s\n", client->hostname,
+ fio_server_op(cmd->cmd.opcode));
+ flist_del(&cmd->list);
+ free(cmd);
+ ret = 1;
+ }
+
+ return flist_empty(&client->cmd_list) && ret;
+}
+
+static int fio_check_clients_timed_out(void)
+{
+ struct fio_client *client;
+ struct flist_head *entry, *tmp;
+ struct timeval tv;
+ int ret = 0;
+
+ gettimeofday(&tv, NULL);
+
+ flist_for_each_safe(entry, tmp, &client_list) {
+ client = flist_entry(entry, struct fio_client, list);
+
+ if (flist_empty(&client->cmd_list))
+ continue;
+
+ if (!client_check_cmd_timeout(client, &tv))
+ continue;
+
+ if (client->ops->timed_out)
+ client->ops->timed_out(client);
+ else
+ log_err("fio: client %s timed out\n", client->hostname);
+
+ remove_client(client);
+ ret = 1;
+ }
+
+ return ret;
+}
+
+int fio_handle_clients(struct client_ops *ops)
+{