Skip to content

Commit

Permalink
Add support for VSOCK to engine/net.c
Browse files Browse the repository at this point in the history
* configure: add option to enable/disable vsock support

* engines/net.c: add vsock support

The  VSOCK  address family facilitates communication between virtual
machines and the host they are running on.

The addressing is formed by 2 integers: <CID, port>
- CID: Context ID, it is the ID assigned to the VM
  0, 1, 2 CIDs are reserved:
  0 - hypervisor CID (rarely used)
  1 - local communication (loopback)
  2 - host CID (the guest can always reach the host using CID=2)

- port: port number on 32bit to reach a specific process

* examples: add 3 simple job files for vsock (one sender, one receiver
  and one that uses vsock loopback interface similar to
  examples/netio.fio)

* fio.1: add vsock to supported protocols together with the required
  parameters

* HOWTO.rst: add vsock to supported protocols together with the required
  parameters

Signed-off-by: Marco Pinna <marco.pinn95@gmail.com>
  • Loading branch information
MPinna committed Feb 12, 2024
1 parent 625b155 commit 80cc242
Show file tree
Hide file tree
Showing 7 changed files with 217 additions and 6 deletions.
7 changes: 5 additions & 2 deletions HOWTO.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2626,10 +2626,13 @@ with the caveat that when used on the command line, they must come after the
User datagram protocol V6.
**unix**
UNIX domain socket.
**vsock**
VSOCK protocol.

When the protocol is TCP or UDP, the port must also be given, as well as the
hostname if the job is a TCP listener or UDP reader. For unix sockets, the
When the protocol is TCP, UDP or VSOCK, the port must also be given, as well as the
hostname if the job is a TCP or VSOCK listener or UDP reader. For unix sockets, the
normal :option:`filename` option should be used and the port is invalid.
When the protocol is VSOCK, the :option:`hostname` is the CID of the remote VM.

.. option:: listen : [netsplice] [net]

Expand Down
22 changes: 22 additions & 0 deletions configure
Original file line number Diff line number Diff line change
Expand Up @@ -1728,6 +1728,25 @@ elif compile_prog "" "-lws2_32" "TCP_NODELAY"; then
fi
print_config "TCP_NODELAY" "$tcp_nodelay"

##########################################
# Check whether we have vsock
if test "$vsock" != "yes" ; then
vsock="no"
fi
cat > $TMPC << EOF
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/vm_sockets.h>
int main(int argc, char **argv)
{
return socket(AF_VSOCK, SOCK_STREAM, 0);
}
EOF
if compile_prog "" "" "vsock"; then
vsock="yes"
fi
print_config "vsock" "$vsock"

##########################################
# Check whether we have SO_SNDBUF
if test "$window_size" != "yes" ; then
Expand Down Expand Up @@ -3192,6 +3211,9 @@ fi
if test "$ipv6" = "yes" ; then
output_sym "CONFIG_IPV6"
fi
if test "$vsock" = "yes"; then
output_sym "CONFIG_VSOCK"
fi
if test "$http" = "yes" ; then
output_sym "CONFIG_HTTP"
fi
Expand Down
132 changes: 130 additions & 2 deletions engines/net.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@
#include <sys/socket.h>
#include <sys/un.h>

#ifdef CONFIG_VSOCK
#include <linux/vm_sockets.h>
#else
struct sockaddr_vm {
};
#ifndef AF_VSOCK
#define AF_VSOCK -1
#endif
#endif

#include "../fio.h"
#include "../verify.h"
#include "../optgroup.h"
Expand All @@ -30,6 +40,7 @@ struct netio_data {
struct sockaddr_in addr;
struct sockaddr_in6 addr6;
struct sockaddr_un addr_un;
struct sockaddr_vm addr_vm;
uint64_t udp_send_seq;
uint64_t udp_recv_seq;
};
Expand Down Expand Up @@ -69,6 +80,7 @@ enum {
FIO_TYPE_UNIX = 3,
FIO_TYPE_TCP_V6 = 4,
FIO_TYPE_UDP_V6 = 5,
FIO_TYPE_VSOCK_STREAM = 6,
};

static int str_hostname_cb(void *data, const char *input);
Expand Down Expand Up @@ -126,6 +138,10 @@ static struct fio_option options[] = {
.oval = FIO_TYPE_UNIX,
.help = "UNIX domain socket",
},
{ .ival = "vsock",
.oval = FIO_TYPE_VSOCK_STREAM,
.help = "Virtual socket",
},
},
.category = FIO_OPT_C_ENGINE,
.group = FIO_OPT_G_NETIO,
Expand Down Expand Up @@ -223,6 +239,11 @@ static inline int is_ipv6(struct netio_options *o)
return o->proto == FIO_TYPE_UDP_V6 || o->proto == FIO_TYPE_TCP_V6;
}

static inline int is_vsock(struct netio_options *o)
{
return o->proto == FIO_TYPE_VSOCK_STREAM;
}

static int set_window_size(struct thread_data *td, int fd)
{
#ifdef CONFIG_NET_WINDOWSIZE
Expand Down Expand Up @@ -732,6 +753,9 @@ static int fio_netio_connect(struct thread_data *td, struct fio_file *f)
} else if (o->proto == FIO_TYPE_UNIX) {
domain = AF_UNIX;
type = SOCK_STREAM;
} else if (is_vsock(o)) {
domain = AF_VSOCK;
type = SOCK_STREAM;
} else {
log_err("fio: bad network type %d\n", o->proto);
f->fd = -1;
Expand Down Expand Up @@ -809,7 +833,14 @@ static int fio_netio_connect(struct thread_data *td, struct fio_file *f)
close(f->fd);
return 1;
}
} else if (is_vsock(o)) {
socklen_t len = sizeof(nd->addr_vm);

if (connect(f->fd, (struct sockaddr *) &nd->addr_vm, len) < 0) {
td_verror(td, errno, "connect");
close(f->fd);
return 1;
}
} else {
struct sockaddr_un *addr = &nd->addr_un;
socklen_t len;
Expand Down Expand Up @@ -849,6 +880,9 @@ static int fio_netio_accept(struct thread_data *td, struct fio_file *f)
if (o->proto == FIO_TYPE_TCP) {
socklen = sizeof(nd->addr);
f->fd = accept(nd->listenfd, (struct sockaddr *) &nd->addr, &socklen);
} else if (is_vsock(o)) {
socklen = sizeof(nd->addr_vm);
f->fd = accept(nd->listenfd, (struct sockaddr *) &nd->addr_vm, &socklen);
} else {
socklen = sizeof(nd->addr6);
f->fd = accept(nd->listenfd, (struct sockaddr *) &nd->addr6, &socklen);
Expand Down Expand Up @@ -890,6 +924,9 @@ static void fio_netio_send_close(struct thread_data *td, struct fio_file *f)
if (is_ipv6(o)) {
to = (struct sockaddr *) &nd->addr6;
len = sizeof(nd->addr6);
} else if (is_vsock(o)) {
to = NULL;
len = 0;
} else {
to = (struct sockaddr *) &nd->addr;
len = sizeof(nd->addr);
Expand Down Expand Up @@ -960,6 +997,9 @@ static int fio_netio_send_open(struct thread_data *td, struct fio_file *f)
if (is_ipv6(o)) {
len = sizeof(nd->addr6);
to = (struct sockaddr *) &nd->addr6;
} else if (is_vsock(o)) {
len = sizeof(nd->addr_vm);
to = (struct sockaddr *) &nd->addr_vm;
} else {
len = sizeof(nd->addr);
to = (struct sockaddr *) &nd->addr;
Expand Down Expand Up @@ -1023,13 +1063,17 @@ static int fio_fill_addr(struct thread_data *td, const char *host, int af,

memset(&hints, 0, sizeof(hints));

if (is_tcp(o))
if (is_tcp(o) || is_vsock(o))
hints.ai_socktype = SOCK_STREAM;
else
hints.ai_socktype = SOCK_DGRAM;

if (is_ipv6(o))
hints.ai_family = AF_INET6;
#ifdef CONFIG_VSOCK
else if (is_vsock(o))
hints.ai_family = AF_VSOCK;
#endif
else
hints.ai_family = AF_INET;

Expand Down Expand Up @@ -1110,12 +1154,50 @@ static int fio_netio_setup_connect_unix(struct thread_data *td,
return 0;
}

static int fio_netio_setup_connect_vsock(struct thread_data *td,
const char *host, unsigned short port)
{
#ifdef CONFIG_VSOCK
struct netio_data *nd = td->io_ops_data;
struct sockaddr_vm *addr = &nd->addr_vm;
int cid;

if (!host) {
log_err("fio: connect with no host to connect to.\n");
if (td_read(td))
log_err("fio: did you forget to set 'listen'?\n");

td_verror(td, EINVAL, "no hostname= set");
return 1;
}

addr->svm_family = AF_VSOCK;
addr->svm_port = port;

if (host) {
cid = atoi(host);
if (cid < 0 || cid > UINT32_MAX) {
log_err("fio: invalid CID %d\n", cid);
return 1;
}
addr->svm_cid = cid;
}

return 0;
#else
td_verror(td, -EINVAL, "vsock not supported");
return 1;
#endif
}

static int fio_netio_setup_connect(struct thread_data *td)
{
struct netio_options *o = td->eo;

if (is_udp(o) || is_tcp(o))
return fio_netio_setup_connect_inet(td, td->o.filename,o->port);
else if (is_vsock(o))
return fio_netio_setup_connect_vsock(td, td->o.filename, o->port);
else
return fio_netio_setup_connect_unix(td, td->o.filename);
}
Expand Down Expand Up @@ -1268,6 +1350,47 @@ static int fio_netio_setup_listen_inet(struct thread_data *td, short port)
return 0;
}

static int fio_netio_setup_listen_vsock(struct thread_data *td, short port, int type)
{
#ifdef CONFIG_VSOCK
struct netio_data *nd = td->io_ops_data;
struct sockaddr_vm *addr = &nd->addr_vm;
int fd, opt;
socklen_t len;

fd = socket(AF_VSOCK, type, 0);
if (fd < 0) {
td_verror(td, errno, "socket");
return 1;
}

opt = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *) &opt, sizeof(opt)) < 0) {
td_verror(td, errno, "setsockopt");
close(fd);
return 1;
}

len = sizeof(*addr);

nd->addr_vm.svm_family = AF_VSOCK;
nd->addr_vm.svm_cid = VMADDR_CID_ANY;
nd->addr_vm.svm_port = port;

if (bind(fd, (struct sockaddr *) addr, len) < 0) {
td_verror(td, errno, "bind");
close(fd);
return 1;
}

nd->listenfd = fd;
return 0;
#else
td_verror(td, -EINVAL, "vsock not supported");
return -1;
#endif
}

static int fio_netio_setup_listen(struct thread_data *td)
{
struct netio_data *nd = td->io_ops_data;
Expand All @@ -1276,6 +1399,8 @@ static int fio_netio_setup_listen(struct thread_data *td)

if (is_udp(o) || is_tcp(o))
ret = fio_netio_setup_listen_inet(td, o->port);
else if (is_vsock(o))
ret = fio_netio_setup_listen_vsock(td, o->port, SOCK_STREAM);
else
ret = fio_netio_setup_listen_unix(td, td->o.filename);

Expand Down Expand Up @@ -1311,14 +1436,17 @@ static int fio_netio_init(struct thread_data *td)
if (o->proto == FIO_TYPE_UNIX && o->port) {
log_err("fio: network IO port not valid with unix socket\n");
return 1;
} else if (is_vsock(o) && !o->port) {
log_err("fio: network IO requires port for vsock\n");
return 1;
} else if (o->proto != FIO_TYPE_UNIX && !o->port) {
log_err("fio: network IO requires port for tcp or udp\n");
return 1;
}

o->port += td->subjob_number;

if (!is_tcp(o)) {
if (!is_tcp(o) && !is_vsock(o)) {
if (o->listen) {
log_err("fio: listen only valid for TCP proto IO\n");
return 1;
Expand Down
22 changes: 22 additions & 0 deletions examples/netio_vsock.fio
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Example network vsock job, just defines two clients that send/recv data
[global]
ioengine=net

port=8888
protocol=vsock
bs=4k
size=100g

#set the below option to enable end-to-end data integrity tests
#verify=md5

[receiver]
listen
rw=read

[sender]
# 1 (VMADDR_CID_LOCAL) is the well-known address
# for local communication (loopback)
hostname=1
startdelay=1
rw=write
14 changes: 14 additions & 0 deletions examples/netio_vsock_receiver.fio
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Example network vsock job, just defines a receiver
[global]
ioengine=net
port=8888
protocol=vsock
bs=4k
size=100g

#set the below option to enable end-to-end data integrity tests
#verify=md5

[receiver]
listen
rw=read
17 changes: 17 additions & 0 deletions examples/netio_vsock_sender.fio
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Example network vsock job, just defines a sender
[global]
ioengine=net
port=8888
protocol=vsock
bs=4k
size=100g

#set the below option to enable end-to-end data integrity tests
#verify=md5

[sender]
# set the 'hostname' option to the CID of the listening domain
hostname=3
startdelay=1
rw=write

9 changes: 7 additions & 2 deletions fio.1
Original file line number Diff line number Diff line change
Expand Up @@ -2376,11 +2376,16 @@ User datagram protocol V6.
.TP
.B unix
UNIX domain socket.
.TP
.B vsock
VSOCK protocol.
.RE
.P
When the protocol is TCP or UDP, the port must also be given, as well as the
hostname if the job is a TCP listener or UDP reader. For unix sockets, the
When the protocol is TCP, UDP or VSOCK, the port must also be given, as well as the
hostname if the job is a TCP or VSOCK listener or UDP reader. For unix sockets, the
normal \fBfilename\fR option should be used and the port is invalid.
When the protocol is VSOCK, the \fBhostname\fR is the CID of the remote VM.

.RE
.TP
.BI (netsplice,net)listen
Expand Down

0 comments on commit 80cc242

Please sign in to comment.