[libvirt] [PATCH RFC] Add support for QEMU vhost-user feature

Luke Gorrie luke at snabb.co
Wed May 28 08:46:27 UTC 2014


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 at 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




More information about the libvir-list mailing list