Add documentation as well for client/server.
Get rid of name hash for clients, just pass a cookie back and forth.
Signed-off-by: Jens Axboe <axboe@kernel.dk>
--debug Enable some debugging options (see below)
--output Write output to file
--timeout Runtime in seconds
- --latency-log Generate per-job latency logs
- --bandwidth-log Generate per-job bandwidth logs
+ --latency-log Generate per-job latency logs
+ --bandwidth-log Generate per-job bandwidth logs
--minimal Minimal (terse) output
--version Print version info and exit
--terse-version=type Terse version output format
--help Print this page
- --cmdhelp=cmd Print command help, "all" for all of them
+ --cmdhelp=cmd Print command help, "all" for all of them
--showcmd Turn a job file into command line options
--readonly Turn on safety read-only checks, preventing
- writes
+ writes
--eta=when When ETA estimate should be printed
- May be "always", "never" or "auto"
- --section=name Only run specified section in job file. Multiple
- sections can be specified.
+ May be "always", "never" or "auto"
+ --section=name Only run specified section in job file.
+ Multiple sections can be specified.
--alloc-size=kb Set smalloc pool to this size in kb (def 1024)
--warnings-fatal Fio parser warnings are fatal
--max-jobs Maximum number of threads/processes to support
+ --server=args Start backend server. See Client/Server section.
+ --client=host Connect to specified backend.
Any parameters following the options will be assumed to be job files,
+Client/server
+------------
+
+Normally you would run fio as a stand-alone application on the machine
+where the IO workload should be generated. However, it is also possible to
+run the frontend and backend of fio separately. This makes it possible to
+have a fio server running on the machine(s) where the IO workload should
+be running, while controlling it from another machine.
+
+To start the server, you would do:
+
+fio --server=args
+
+on that machine, where args defines what fio listens to. The arguments
+are of the form 'type:hostname or IP:port'. 'type' is either 'ip' for
+TCP/IP, or 'sock' for a local unix domain socket. 'hostname' is either
+a hostname or IP address, and 'port' is the port to listen to (only valid
+for TCP/IP, not a local socket). Some examples:
+
+1) fio --server
+
+ Start a fio server, listening on all interfaces on the default port (8765).
+
+2) fio --server=ip:hostname:4444
+
+ Start a fio server, listening on IP belonging to hostname and on port 4444.
+
+3) fio --server=:4444
+
+ Start a fio server, listening on all interfaces on port 4444.
+
+4) fio --server=1.2.3.4
+
+ Start a fio server, listening on IP 1.2.3.4 on the default port.
+
+5) fio --server=sock:/tmp/fio.sock
+
+ Start a fio server, listening on the local socket /tmp/fio.sock.
+
+When a server is running, you can connect to it from a client. The client
+is run with:
+
+fio --local-args --client=server --remote-args <job file(s)>
+
+where --local-args are arguments that are local to the client where it is
+running, 'server' is the connect string, and --remote-args and <job file(s)>
+are sent to the server. The 'server' string follows the same format as it
+does on the server side, to allow IP/hostname/socket and port strings.
+You can connect to multiple clients as well, to do that you could run:
+
+fio --client=server2 --client=server2 <job file(s)>
+
+
Platforms
---------
struct fio_client {
struct flist_head list;
- struct flist_head fd_hash_list;
- struct flist_head name_hash_list;
+ struct flist_head hash_list;
struct sockaddr_in addr;
struct sockaddr_un addr_un;
char *hostname;
+ int port;
int fd;
int state;
#define FIO_CLIENT_HASH_BITS 7
#define FIO_CLIENT_HASH_SZ (1 << FIO_CLIENT_HASH_BITS)
#define FIO_CLIENT_HASH_MASK (FIO_CLIENT_HASH_SZ - 1)
-static struct flist_head client_fd_hash[FIO_CLIENT_HASH_SZ];
-static struct flist_head client_name_hash[FIO_CLIENT_HASH_SZ];
+static struct flist_head client_hash[FIO_CLIENT_HASH_SZ];
static int handle_client(struct fio_client *client);
-static void fio_client_add_fd_hash(struct fio_client *client)
+static void fio_client_add_hash(struct fio_client *client)
{
int bucket = hash_long(client->fd, FIO_CLIENT_HASH_BITS);
bucket &= FIO_CLIENT_HASH_MASK;
- flist_add(&client->fd_hash_list, &client_fd_hash[bucket]);
+ flist_add(&client->hash_list, &client_hash[bucket]);
}
-static void fio_client_remove_fd_hash(struct fio_client *client)
+static void fio_client_remove_hash(struct fio_client *client)
{
- if (!flist_empty(&client->fd_hash_list))
- flist_del_init(&client->fd_hash_list);
-}
-
-static void fio_client_add_name_hash(struct fio_client *client)
-{
- int bucket = jhash(client->hostname, strlen(client->hostname), 0);
-
- bucket &= FIO_CLIENT_HASH_MASK;
- flist_add(&client->name_hash_list, &client_name_hash[bucket]);
-}
-
-static void fio_client_remove_name_hash(struct fio_client *client)
-{
- if (!flist_empty(&client->name_hash_list))
- flist_del_init(&client->name_hash_list);
+ if (!flist_empty(&client->hash_list))
+ flist_del_init(&client->hash_list);
}
static void fio_init fio_client_hash_init(void)
{
int i;
- for (i = 0; i < FIO_CLIENT_HASH_SZ; i++) {
- INIT_FLIST_HEAD(&client_fd_hash[i]);
- INIT_FLIST_HEAD(&client_name_hash[i]);
- }
+ for (i = 0; i < FIO_CLIENT_HASH_SZ; i++)
+ INIT_FLIST_HEAD(&client_hash[i]);
}
static struct fio_client *find_client_by_fd(int fd)
struct fio_client *client;
struct flist_head *entry;
- flist_for_each(entry, &client_fd_hash[bucket]) {
- client = flist_entry(entry, struct fio_client, fd_hash_list);
+ flist_for_each(entry, &client_hash[bucket]) {
+ client = flist_entry(entry, struct fio_client, hash_list);
if (client->fd == fd)
return client;
return NULL;
}
-static struct fio_client *find_client_by_name(const char *name)
-{
- int bucket = jhash(name, strlen(name), 0) & FIO_CLIENT_HASH_BITS;
- struct fio_client *client;
- struct flist_head *entry;
-
- flist_for_each(entry, &client_name_hash[bucket]) {
- client = flist_entry(entry, struct fio_client, name_hash_list);
-
- if (!strcmp(name, client->hostname))
- return client;
- }
-
- return NULL;
-}
-
static void remove_client(struct fio_client *client)
{
dprint(FD_NET, "client: removed <%s>\n", client->hostname);
flist_del(&client->list);
- fio_client_remove_fd_hash(client);
- fio_client_remove_name_hash(client);
+ fio_client_remove_hash(client);
free(client->hostname);
if (client->argv)
return 0;
}
-int fio_client_add_cmd_option(const char *hostname, const char *opt)
+int fio_client_add_cmd_option(void *cookie, const char *opt)
{
- struct fio_client *client;
+ struct fio_client *client = cookie;
- if (!hostname || !opt)
+ if (!client || !opt)
return 0;
- client = find_client_by_name(hostname);
- if (!client) {
- log_err("fio: unknown client %s\n", hostname);
- return 1;
- }
-
return __fio_client_add_cmd_option(client, opt);
}
-void fio_client_add(const char *hostname)
+int fio_client_add(const char *hostname, void **cookie)
{
struct fio_client *client;
- dprint(FD_NET, "client: added <%s>\n", hostname);
client = malloc(sizeof(*client));
memset(client, 0, sizeof(*client));
INIT_FLIST_HEAD(&client->list);
- INIT_FLIST_HEAD(&client->fd_hash_list);
- INIT_FLIST_HEAD(&client->name_hash_list);
+ INIT_FLIST_HEAD(&client->hash_list);
+
+ if (fio_server_parse_string(hostname, &client->hostname,
+ &client->is_sock, &client->port,
+ &client->addr.sin_addr))
+ return -1;
- if (!strncmp(hostname, "sock:", 5)) {
- client->hostname = strdup(hostname + 5);
- client->is_sock = 1;
- } else
- client->hostname = strdup(hostname);
+ printf("%s %d %d\n", client->hostname, client->is_sock, client->port);
client->fd = -1;
- fio_client_add_name_hash(client);
-
__fio_client_add_cmd_option(client, "fio");
flist_add(&client->list, &client_list);
nr_clients++;
+ dprint(FD_NET, "client: added <%s>\n", client->hostname);
+ *cookie = client;
+ return 0;
}
static int fio_client_connect_ip(struct fio_client *client)
int fd;
client->addr.sin_family = AF_INET;
- client->addr.sin_port = htons(fio_net_port);
-
- if (inet_aton(client->hostname, &client->addr.sin_addr) != 1) {
- struct hostent *hent;
-
- hent = gethostbyname(client->hostname);
- if (!hent) {
- log_err("fio: gethostbyname: %s\n", strerror(errno));
- log_err("fio: failed looking up hostname %s\n",
- client->hostname);
- return -1;
- }
-
- memcpy(&client->addr.sin_addr, hent->h_addr, 4);
- }
+ client->addr.sin_port = htons(client->port);
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) {
return 1;
client->fd = fd;
- fio_client_add_fd_hash(client);
+ fio_client_add_hash(client);
client->state = Client_connected;
return 0;
}
static void sig_int(int sig)
{
- dprint(FD_NET, "client: got sign %d\n", sig);
+ dprint(FD_NET, "client: got signal %d\n", sig);
fio_clients_terminate();
}
.has_arg = no_argument,
.val = 'D',
},
- {
- .name = (char *) "net-port",
- .has_arg = required_argument,
- .val = 'P',
- },
{
.name = (char *) "client",
.has_arg = required_argument,
" (def 1024)\n");
printf("\t--warnings-fatal Fio parser warnings are fatal\n");
printf("\t--max-jobs\tMaximum number of threads/processes to support\n");
- printf("\t--server\tStart a backend fio server\n");
+ printf("\t--server=args\tStart a backend fio server\n");
printf("\t--client=hostname Talk to remove backend fio server at hostname\n");
- printf("\t--net-port=port\tUse specified port for client/server connection\n");
printf("\nFio was written by Jens Axboe <jens.axboe@oracle.com>");
printf("\n Jens Axboe <jaxboe@fusionio.com>\n");
}
return 0;
}
-int parse_cmd_client(char *client, char *opt)
+int parse_cmd_client(void *client, char *opt)
{
return fio_client_add_cmd_option(client, opt);
}
int c, ini_idx = 0, lidx, ret = 0, do_exit = 0, exit_val = 0;
char *ostr = cmd_optstr;
int daemonize_server = 0;
- char *cur_client = NULL;
+ void *cur_client;
int backend = 0;
/*
break;
}
if (optarg)
- fio_server_add_arg(optarg);
+ fio_server_set_arg(optarg);
is_backend = 1;
backend = 1;
break;
case 'D':
daemonize_server = 1;
break;
- case 'P':
- fio_net_port = atoi(optarg);
- break;
case 'C':
if (is_backend) {
log_err("fio: can't be both client and server\n");
exit_val = 1;
break;
}
- fio_client_add(optarg);
- cur_client = optarg;
+ if (fio_client_add(optarg, &cur_client)) {
+ log_err("fio: failed adding client %s\n", optarg);
+ do_exit++;
+ exit_val = 1;
+ break;
+ }
break;
default:
do_exit++;
#endif
saddr_in.sin_family = AF_INET;
- saddr_in.sin_port = htons(fio_net_port);
if (bind(sk, (struct sockaddr *) &saddr_in, sizeof(saddr_in)) < 0) {
log_err("fio: bind: %s\n", strerror(errno));
static int fio_init_server_connection(void)
{
+ char bind_str[128];
int sk;
dprint(FD_NET, "starting server\n");
if (sk < 0)
return sk;
+ if (!bind_sock)
+ sprintf(bind_str, "%s:%u", inet_ntoa(saddr_in.sin_addr), fio_net_port);
+ else
+ strcpy(bind_str, bind_sock);
+
+ log_info("fio: server listening on %s\n", bind_str);
+
if (listen(sk, 1) < 0) {
log_err("fio: listen: %s\n", strerror(errno));
return -1;
return sk;
}
+int fio_server_parse_string(const char *str, char **ptr, int *is_sock,
+ int *port, struct in_addr *inp)
+{
+ *ptr = NULL;
+ *is_sock = 0;
+ *port = 0;
+
+ if (!strncmp(str, "sock:", 5)) {
+ *ptr = strdup(str + 5);
+ *is_sock = 1;
+ } else {
+ const char *host = str;
+ char *portp;
+ int lport = 0;
+
+ /*
+ * Is it ip:<ip or host>:port
+ */
+ if (!strncmp(host, "ip:", 3))
+ host += 3;
+ else if (host[0] == ':') {
+ /* String is :port */
+ host++;
+ lport = atoi(host);
+ if (!lport || lport > 65535) {
+ log_err("fio: bad server port %u\n", port);
+ return 1;
+ }
+ /* no hostname given, we are done */
+ *port = lport;
+ return 0;
+ }
+
+ /*
+ * If no port seen yet, check if there's a last ':' at the end
+ */
+ if (!lport) {
+ portp = strchr(host, ':');
+ if (portp) {
+ *portp = '\0';
+ portp++;
+ lport = atoi(portp);
+ if (!lport || lport > 65535) {
+ log_err("fio: bad server port %u\n", port);
+ return 1;
+ }
+ }
+ }
+
+ if (lport)
+ *port = lport;
+
+ *ptr = strdup(host);
+
+ if (inet_aton(host, inp) != 1) {
+ struct hostent *hent;
+
+ hent = gethostbyname(host);
+ if (!hent) {
+ printf("FAIL\n");
+ free(*ptr);
+ *ptr = NULL;
+ return 1;
+ }
+
+ memcpy(inp, hent->h_addr, 4);
+ }
+ }
+
+ if (*port == 0)
+ *port = fio_net_port;
+
+ return 0;
+}
+
/*
* Server arg should be one of:
*
*/
static int fio_handle_server_arg(void)
{
+ int unused;
+
saddr_in.sin_addr.s_addr = htonl(INADDR_ANY);
+ saddr_in.sin_port = htons(fio_net_port);
if (!fio_server_arg)
return 0;
- if (!strncmp(fio_server_arg, "sock:", 5)) {
- bind_sock = fio_server_arg + 5;
- return 0;
- } else {
- char *host = fio_server_arg;
-
- if (!strncmp(host, "ip:", 3))
- host += 3;
- if (inet_aton(host, &saddr_in.sin_addr) != 1) {
- struct hostent *hent;
-
- hent = gethostbyname(host);
- if (hent)
- memcpy(&saddr_in.sin_addr, hent->h_addr, 4);
- }
- return 0;
- }
+ return fio_server_parse_string(fio_server_arg, &bind_sock, &unused,
+ &fio_net_port, &saddr_in.sin_addr);
}
static int fio_server(void)
free(fio_server_arg);
fio_server_arg = NULL;
}
+ if (bind_sock)
+ free(bind_sock);
return ret;
}
return fio_server();
}
-void fio_server_add_arg(const char *arg)
+void fio_server_set_arg(const char *arg)
{
fio_server_arg = strdup(arg);
}
extern int fio_server_log(const char *format, ...);
extern int fio_net_send_cmd(int, uint16_t, const void *, off_t);
extern int fio_net_send_simple_cmd(int sk, uint16_t opcode, uint64_t serial);
-extern void fio_server_add_arg(const char *);
+extern void fio_server_set_arg(const char *);
+extern int fio_server_parse_string(const char *, char **, int *, int *, struct in_addr *);
struct thread_stat;
struct group_run_stats;
extern int fio_clients_connect(void);
extern int fio_clients_send_ini(const char *);
extern int fio_handle_clients(void);
-extern void fio_client_add(const char *);
-extern int fio_client_add_cmd_option(const char *, const char *);
+extern int fio_client_add(const char *, void **);
+extern int fio_client_add_cmd_option(void *, const char *);
extern int fio_recv_data(int sk, void *p, unsigned int len);
extern int fio_send_data(int sk, const void *p, unsigned int len);