vhost-user is a networking backend based on unix domain sockets
instead of tap devices and ioctl(). This makes it possible for
userspace networking stacks (vswitches) to provide vhost-networking to
guests.
Signed-off-by: Luke Gorrie <luke(a)snabb.co>
---
docs/schemas/domaincommon.rng | 23 ++++++++
src/conf/domain_conf.c | 42 ++++++++++++++
src/conf/domain_conf.h | 5 ++
src/lxc/lxc_process.c | 1 +
src/qemu/qemu_command.c | 66 +++++++++++++++++++++-
src/uml/uml_conf.c | 5 ++
src/xenxs/xen_sxpr.c | 1 +
tests/qemuxml2argvdata/qemuxml2argv-hugepages.args | 2 +-
8 files changed, 142 insertions(+), 3 deletions(-)
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index af67123..30e7daf 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -1966,6 +1966,29 @@
<ref name="interface-options"/>
</interleave>
</group>
+ <!-- vhostuser interface: specify unix socket path, socket mode -->
+ <group>
+ <attribute name="type">
+ <value>vhostuser</value>
+ </attribute>
+ <interleave>
+ <optional>
+ <element name="socket">
+ <attribute name="path">
+ <ref name="absFilePath"/>
+ </attribute>
+ <attribute name="mode">
+ <choice>
+ <value>server</value>
+ <value>client</value>
+ </choice>
+ </attribute>
+ <empty/>
+ </element>
+ </optional>
+ <ref name="interface-options"/>
+ </interleave>
+ </group>
<group>
<attribute name="type">
<value>network</value>
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index f1df092..b47554a 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -348,6 +348,7 @@ VIR_ENUM_IMPL(virDomainFSWrpolicy, VIR_DOMAIN_FS_WRPOLICY_LAST,
VIR_ENUM_IMPL(virDomainNet, VIR_DOMAIN_NET_TYPE_LAST,
"user",
"ethernet",
+ "vhostuser",
"server",
"client",
"mcast",
@@ -1333,6 +1334,11 @@ void virDomainNetDefFree(virDomainNetDefPtr def)
VIR_FREE(def->data.ethernet.ipaddr);
break;
+ case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
+ VIR_FREE(def->data.vhostuser.socket);
+ VIR_FREE(def->data.vhostuser.mode);
+ break;
+
case VIR_DOMAIN_NET_TYPE_SERVER:
case VIR_DOMAIN_NET_TYPE_CLIENT:
case VIR_DOMAIN_NET_TYPE_MCAST:
@@ -6579,6 +6585,8 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt,
char *script = NULL;
char *address = NULL;
char *port = NULL;
+ char *vhostuser_socket = NULL;
+ char *vhostuser_mode = NULL;
char *model = NULL;
char *backend = NULL;
char *txmode = NULL;
@@ -6636,6 +6644,11 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt,
xmlStrEqual(cur->name, BAD_CAST "source")) {
dev = virXMLPropString(cur, "dev");
mode = virXMLPropString(cur, "mode");
+ } else if (!vhostuser_socket && !vhostuser_mode &&
+ def->type == VIR_DOMAIN_NET_TYPE_VHOSTUSER &&
+ xmlStrEqual(cur->name, BAD_CAST "socket")) {
+ vhostuser_socket = virXMLPropString(cur, "path");
+ vhostuser_mode = virXMLPropString(cur, "mode");
} else if (!def->virtPortProfile
&& xmlStrEqual(cur->name, BAD_CAST
"virtualport")) {
if (def->type == VIR_DOMAIN_NET_TYPE_NETWORK) {
@@ -6804,6 +6817,25 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt,
}
break;
+ case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
+ if (vhostuser_socket == NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("No <socket> 'path' attribute "
+ "specified with <interface
type='vhostuser'/>"));
+ goto error;
+ }
+ if (vhostuser_mode == NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("No <socket> 'mode' attribute "
+ "specified with <interface
type='vhostuser'/>"));
+ goto error;
+ }
+ def->data.vhostuser.socket = vhostuser_socket;
+ def->data.vhostuser.mode = vhostuser_mode;
+ vhostuser_socket = NULL;
+ vhostuser_mode = NULL;
+ break;
+
case VIR_DOMAIN_NET_TYPE_BRIDGE:
if (bridge == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
@@ -7038,6 +7070,8 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt,
VIR_FREE(portgroup);
VIR_FREE(address);
VIR_FREE(port);
+ VIR_FREE(vhostuser_socket);
+ VIR_FREE(vhostuser_mode);
VIR_FREE(ifname);
VIR_FREE(dev);
virDomainActualNetDefFree(actual);
@@ -15764,6 +15798,14 @@ virDomainNetDefFormat(virBufferPtr buf,
def->data.ethernet.ipaddr);
break;
+ case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
+ virBufferEscapeString(buf, "<socket path='%s'",
+ def->data.vhostuser.socket);
+ virBufferEscapeString(buf, " mode='%s'",
+ def->data.vhostuser.mode);
+ virBufferAddLit(buf, "/>\n");
+ break;
+
case VIR_DOMAIN_NET_TYPE_BRIDGE:
virBufferEscapeString(buf, "<source
bridge='%s'/>\n",
def->data.bridge.brname);
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 8f17c74..b5ea2f6 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -791,6 +791,7 @@ struct _virDomainFSDef {
enum virDomainNetType {
VIR_DOMAIN_NET_TYPE_USER,
VIR_DOMAIN_NET_TYPE_ETHERNET,
+ VIR_DOMAIN_NET_TYPE_VHOSTUSER,
VIR_DOMAIN_NET_TYPE_SERVER,
VIR_DOMAIN_NET_TYPE_CLIENT,
VIR_DOMAIN_NET_TYPE_MCAST,
@@ -877,6 +878,10 @@ struct _virDomainNetDef {
char *ipaddr;
} ethernet;
struct {
+ char *socket;
+ char *mode;
+ } vhostuser;
+ struct {
char *address;
int port;
} socket; /* any of NET_CLIENT or NET_SERVER or NET_MCAST */
diff --git a/src/lxc/lxc_process.c b/src/lxc/lxc_process.c
index 0aef13a..854f65d 100644
--- a/src/lxc/lxc_process.c
+++ b/src/lxc/lxc_process.c
@@ -437,6 +437,7 @@ static int virLXCProcessSetupInterfaces(virConnectPtr conn,
case VIR_DOMAIN_NET_TYPE_USER:
case VIR_DOMAIN_NET_TYPE_ETHERNET:
+ case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
case VIR_DOMAIN_NET_TYPE_SERVER:
case VIR_DOMAIN_NET_TYPE_CLIENT:
case VIR_DOMAIN_NET_TYPE_MCAST:
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 1d5bce6..755bf9d 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -7007,6 +7007,62 @@ qemuBuildGraphicsCommandLine(virQEMUDriverConfigPtr cfg,
}
static int
+qemuBuildVhostuserCommandLine(virCommandPtr cmd,
+ virDomainDefPtr def,
+ virDomainNetDefPtr net,
+ virQEMUCapsPtr qemuCaps)
+{
+ virBuffer buf1 = VIR_BUFFER_INITIALIZER;
+ virBuffer buf2 = VIR_BUFFER_INITIALIZER;
+
+ char* nic = NULL;
+
+ if (!qemuDomainSupportsNetdev(def, qemuCaps, net)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("Netdev support unavailable"));
+ goto error;
+ }
+
+ if (!qemuDomainSupportsNicdev(def, qemuCaps, net)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("Nicdev support unavailable"));
+ goto error;
+ }
+
+ virBufferAsprintf(&buf1, "socket,id=char%s,path=%s%s",
+ net->info.alias, net->data.vhostuser.socket,
+ STRCASEEQ(net->data.vhostuser.mode, "server") ? ",server" :
"");
+ virBufferAsprintf(&buf2, "type=vhost-user,id=host%s,chardev=char%s",
+ net->info.alias, net->info.alias);
+
+ if (virBufferError(&buf1) || virBufferError(&buf2)) {
+ virReportOOMError();
+ goto error;
+ }
+
+ virCommandAddArgList(cmd, "-chardev", virBufferContentAndReset(&buf1),
+ NULL);
+ virCommandAddArgList(cmd, "-netdev", virBufferContentAndReset(&buf2),
+ NULL);
+
+ if (!(nic = qemuBuildNicDevStr(def, net, -1, 0, 0, qemuCaps))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("Error generating NIC -device
string"));
+ goto error;
+ }
+
+ virCommandAddArgList(cmd, "-device", nic, NULL);
+
+ return 0;
+
+error:
+ virBufferFreeAndReset(&buf1);
+ virBufferFreeAndReset(&buf2);
+
+ return -1;
+}
+
+static int
qemuBuildInterfaceCommandLine(virCommandPtr cmd,
virQEMUDriverPtr driver,
virConnectPtr conn,
@@ -7029,6 +7085,10 @@ qemuBuildInterfaceCommandLine(virCommandPtr cmd,
int actualType = virDomainNetGetActualType(net);
size_t i;
+ if (actualType == VIR_DOMAIN_NET_TYPE_VHOSTUSER) {
+ return qemuBuildVhostuserCommandLine(cmd, def, net, qemuCaps);
+ }
+
if (actualType == VIR_DOMAIN_NET_TYPE_HOSTDEV) {
/* NET_TYPE_HOSTDEV devices are really hostdev devices, so
* their commandlines are constructed with other hostdevs.
@@ -7374,8 +7434,10 @@ qemuBuildCommandLine(virConnectPtr conn,
def->emulator);
goto error;
}
- virCommandAddArgList(cmd, "-mem-prealloc", "-mem-path",
- cfg->hugepagePath, NULL);
+ virCommandAddArg(cmd, "-mem-path");
+ virCommandAddArgFormat(cmd, "%s,prealloc=on,share=%s",
+ cfg->hugepagePath,
+ def->mem.nosharepages ? "off" :
"on");
}
if (def->mem.locked && !virQEMUCapsGet(qemuCaps, QEMU_CAPS_MLOCK)) {
diff --git a/src/uml/uml_conf.c b/src/uml/uml_conf.c
index 464d56d..d33d9b4 100644
--- a/src/uml/uml_conf.c
+++ b/src/uml/uml_conf.c
@@ -182,6 +182,11 @@ umlBuildCommandLineNet(virConnectPtr conn,
}
break;
+ case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("vhostuser networking type not supported"));
+ goto error;
+
case VIR_DOMAIN_NET_TYPE_SERVER:
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("TCP server networking type not supported"));
diff --git a/src/xenxs/xen_sxpr.c b/src/xenxs/xen_sxpr.c
index 29316a4..ca26694 100644
--- a/src/xenxs/xen_sxpr.c
+++ b/src/xenxs/xen_sxpr.c
@@ -1933,6 +1933,7 @@ xenFormatSxprNet(virConnectPtr conn,
virBufferEscapeSexpr(buf, "(ip '%s')",
def->data.ethernet.ipaddr);
break;
+ case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
case VIR_DOMAIN_NET_TYPE_USER:
case VIR_DOMAIN_NET_TYPE_SERVER:
case VIR_DOMAIN_NET_TYPE_CLIENT:
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-hugepages.args
b/tests/qemuxml2argvdata/qemuxml2argv-hugepages.args
index d42d9fc..5f8df71 100644
--- a/tests/qemuxml2argvdata/qemuxml2argv-hugepages.args
+++ b/tests/qemuxml2argvdata/qemuxml2argv-hugepages.args
@@ -1,5 +1,5 @@
LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=none \
/usr/bin/qemu -S -M \
-pc -m 214 -mem-prealloc -mem-path /dev/hugepages/libvirt/qemu -smp 1 \
+pc -m 214 -mem-path /dev/hugepages/libvirt/qemu,prealloc=on,share=on -smp 1 \
-nographic -monitor unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -usb -hda \
/dev/HostVG/QEMUGuest1 -net none -serial none -parallel none
--
1.9.1