6ae9ed
From c9fb2f1ef7a756909c705df0c6a323af826be139 Mon Sep 17 00:00:00 2001
6ae9ed
Message-Id: <c9fb2f1ef7a756909c705df0c6a323af826be139@dist-git>
6ae9ed
From: Peter Krempa <pkrempa@redhat.com>
6ae9ed
Date: Wed, 24 Aug 2016 16:11:33 -0400
6ae9ed
Subject: [PATCH] conf: Add XML for individual vCPU hotplug
6ae9ed
6ae9ed
https://bugzilla.redhat.com/show_bug.cgi?id=1097930
6ae9ed
https://bugzilla.redhat.com/show_bug.cgi?id=1224341
6ae9ed
6ae9ed
Individual vCPU hotplug requires us to track the state of any vCPU. To
6ae9ed
allow this add the following XML:
6ae9ed
6ae9ed
<domain>
6ae9ed
  ...
6ae9ed
  <vcpu current='2'>3</vcpu>
6ae9ed
  <vcpus>
6ae9ed
    <vcpu id='0' enabled='yes' hotpluggable='no' order='1'/>
6ae9ed
    <vcpu id='1' enabled='yes' hotpluggable='yes' order='2'/>
6ae9ed
    <vcpu id='1' enabled='no' hotpluggable='yes'/>
6ae9ed
  </vcpus>
6ae9ed
  ...
6ae9ed
6ae9ed
The 'enabled' attribute allows to control the state of the vcpu.
6ae9ed
'hotpluggable' controls whether given vcpu can be hotplugged and 'order'
6ae9ed
allows to specify the order to add the vcpus.
6ae9ed
6ae9ed
(cherry picked from commit 5847bc5c64f205a2f00315a2434e8a7bd48c5508)
6ae9ed
---
6ae9ed
 docs/formatdomain.html.in                          |  40 ++++++
6ae9ed
 docs/schemas/domaincommon.rng                      |  25 ++++
6ae9ed
 src/conf/domain_conf.c                             | 154 ++++++++++++++++++++-
6ae9ed
 src/conf/domain_conf.h                             |   6 +
6ae9ed
 src/qemu/qemu_domain.c                             |  11 +-
6ae9ed
 .../generic-vcpus-individual.xml                   |  23 +++
6ae9ed
 tests/genericxml2xmltest.c                         |   2 +
6ae9ed
 tests/testutils.c                                  |   4 +-
6ae9ed
 8 files changed, 261 insertions(+), 4 deletions(-)
6ae9ed
 create mode 100644 tests/genericxml2xmlindata/generic-vcpus-individual.xml
6ae9ed
6ae9ed
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
6ae9ed
index b74057f..252f91c 100644
6ae9ed
--- a/docs/formatdomain.html.in
6ae9ed
+++ b/docs/formatdomain.html.in
6ae9ed
@@ -489,6 +489,10 @@
6ae9ed
 <domain>
6ae9ed
   ...
6ae9ed
   <vcpu placement='static' cpuset="1-4,^3,6" current="1">2</vcpu>
6ae9ed
+  <vcpus>
6ae9ed
+    <vcpu id='0' enabled='yes' hotpluggable='no' order='1'/>
6ae9ed
+    <vcpu id='1' enabled='no' hotpluggable='yes'/>
6ae9ed
+  </vcpus>
6ae9ed
   ...
6ae9ed
 </domain>
6ae9ed
 
6ae9ed
@@ -542,6 +546,42 @@
6ae9ed
          
6ae9ed
         
6ae9ed
       
6ae9ed
+      
vcpus
6ae9ed
+      
6ae9ed
+        The vcpus element allows to control state of individual vcpus.
6ae9ed
+
6ae9ed
+        The id attribute specifies the vCPU id as used by libvirt
6ae9ed
+        in other places such as vcpu pinning, scheduler information and NUMA
6ae9ed
+        assignment. Note that the vcpu ID as seen in the guest may differ from
6ae9ed
+        libvirt ID in certain cases. Valid IDs are from 0 to the maximum vcpu
6ae9ed
+        count as set by the vcpu element minus 1.
6ae9ed
+
6ae9ed
+        The enabled attribute allows to control the state of the
6ae9ed
+        vcpu. Valid values are yes and no.
6ae9ed
+
6ae9ed
+        hotpluggable controls whether given vcpu can be hotplugged
6ae9ed
+        and hotunplugged in cases when the cpu is enabled at boot. Note that
6ae9ed
+        all disabled vcpus must be hotpluggable. Valid values are
6ae9ed
+        yes and no.
6ae9ed
+
6ae9ed
+        order allows to specify the order to add the vcpus. For
6ae9ed
+        hypervisors/platforms that require to insert multiple vcpus at once
6ae9ed
+        the order may be be duplicated accross all vcpus that need to be
6ae9ed
+        enabled at once. Specifying order is not necessary, vcpus are then
6ae9ed
+        added in an arbitrary order.
6ae9ed
+
6ae9ed
+        Note that hypervisors may create hotpluggable vcpus differently from
6ae9ed
+        boot vcpus thus special initialization may be necessary.
6ae9ed
+
6ae9ed
+        Hypervisors may require that vcpus enabled on boot which are not
6ae9ed
+        hotpluggable are clustered at the beginning starting with ID 0. It may
6ae9ed
+        be also required that vcpu 0 is always present and non-hotpluggable.
6ae9ed
+
6ae9ed
+        Note that providing state for individual cpus may be necessary to enable
6ae9ed
+        support of addressable vCPU hotplug and this feature may not be
6ae9ed
+        supported by all hypervisors.
6ae9ed
+        Since 2.2.0 (QEMU only)
6ae9ed
+      
6ae9ed
     
6ae9ed
 
6ae9ed
     

IOThreads Allocation

6ae9ed
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
6ae9ed
index 497597c..bf4d795 100644
6ae9ed
--- a/docs/schemas/domaincommon.rng
6ae9ed
+++ b/docs/schemas/domaincommon.rng
6ae9ed
@@ -583,6 +583,31 @@
6ae9ed
       </optional>
6ae9ed
 
6ae9ed
       <optional>
6ae9ed
+        <element name="vcpus">
6ae9ed
+          <zeroOrMore>
6ae9ed
+            <element name="vcpu">
6ae9ed
+              <attribute name="id">
6ae9ed
+                <ref name="unsignedInt"/>
6ae9ed
+              </attribute>
6ae9ed
+              <attribute name="enabled">
6ae9ed
+                <ref name="virYesNo"/>
6ae9ed
+              </attribute>
6ae9ed
+              <optional>
6ae9ed
+                <attribute name="hotpluggable">
6ae9ed
+                  <ref name="virYesNo"/>
6ae9ed
+                </attribute>
6ae9ed
+              </optional>
6ae9ed
+              <optional>
6ae9ed
+                <attribute name="order">
6ae9ed
+                  <ref name="unsignedInt"/>
6ae9ed
+                </attribute>
6ae9ed
+              </optional>
6ae9ed
+            </element>
6ae9ed
+          </zeroOrMore>
6ae9ed
+        </element>
6ae9ed
+      </optional>
6ae9ed
+
6ae9ed
+      <optional>
6ae9ed
         <element name="iothreads">
6ae9ed
           <ref name="unsignedInt"/>
6ae9ed
         </element>
6ae9ed
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
6ae9ed
index 4e703d9..d3a0400 100644
6ae9ed
--- a/src/conf/domain_conf.c
6ae9ed
+++ b/src/conf/domain_conf.c
6ae9ed
@@ -4425,6 +4425,13 @@ virDomainDefPostParseCheckFeatures(virDomainDefPtr def,
6ae9ed
         }
6ae9ed
     }
6ae9ed
 
6ae9ed
+    if (UNSUPPORTED(VIR_DOMAIN_DEF_FEATURE_INDIVIDUAL_VCPUS) &&
6ae9ed
+        def->individualvcpus) {
6ae9ed
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
6ae9ed
+                       _("individual CPU state configuration is not supported"));
6ae9ed
+        return -1;
6ae9ed
+    }
6ae9ed
+
6ae9ed
     return 0;
6ae9ed
 }
6ae9ed
 
6ae9ed
@@ -4499,6 +4506,43 @@ virDomainDefPostParseDeviceIterator(virDomainDefPtr def,
6ae9ed
 
6ae9ed
 
6ae9ed
 static int
6ae9ed
+virDomainVcpuDefPostParse(virDomainDefPtr def)
6ae9ed
+{
6ae9ed
+    virDomainVcpuDefPtr vcpu;
6ae9ed
+    size_t maxvcpus = virDomainDefGetVcpusMax(def);
6ae9ed
+    size_t i;
6ae9ed
+
6ae9ed
+    for (i = 0; i < maxvcpus; i++) {
6ae9ed
+        vcpu = virDomainDefGetVcpu(def, i);
6ae9ed
+
6ae9ed
+        switch (vcpu->hotpluggable) {
6ae9ed
+        case VIR_TRISTATE_BOOL_ABSENT:
6ae9ed
+            if (vcpu->online)
6ae9ed
+                vcpu->hotpluggable = VIR_TRISTATE_BOOL_NO;
6ae9ed
+            else
6ae9ed
+                vcpu->hotpluggable = VIR_TRISTATE_BOOL_YES;
6ae9ed
+            break;
6ae9ed
+
6ae9ed
+        case VIR_TRISTATE_BOOL_NO:
6ae9ed
+            if (!vcpu->online) {
6ae9ed
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
6ae9ed
+                               _("vcpu '%zu' is both offline and not "
6ae9ed
+                                 "hotpluggable"), i);
6ae9ed
+                return -1;
6ae9ed
+            }
6ae9ed
+            break;
6ae9ed
+
6ae9ed
+        case VIR_TRISTATE_BOOL_YES:
6ae9ed
+        case VIR_TRISTATE_BOOL_LAST:
6ae9ed
+            break;
6ae9ed
+        }
6ae9ed
+    }
6ae9ed
+
6ae9ed
+    return 0;
6ae9ed
+}
6ae9ed
+
6ae9ed
+
6ae9ed
+static int
6ae9ed
 virDomainDefPostParseInternal(virDomainDefPtr def,
6ae9ed
                               struct virDomainDefPostParseDeviceIteratorData *data)
6ae9ed
 {
6ae9ed
@@ -4509,6 +4553,9 @@ virDomainDefPostParseInternal(virDomainDefPtr def,
6ae9ed
         return -1;
6ae9ed
     }
6ae9ed
 
6ae9ed
+    if (virDomainVcpuDefPostParse(def) < 0)
6ae9ed
+        return -1;
6ae9ed
+
6ae9ed
     if (virDomainDefPostParseMemory(def, data->parseFlags) < 0)
6ae9ed
         return -1;
6ae9ed
 
6ae9ed
@@ -15590,6 +15637,8 @@ virDomainVcpuParse(virDomainDefPtr def,
6ae9ed
                    virDomainXMLOptionPtr xmlopt)
6ae9ed
 {
6ae9ed
     int n;
6ae9ed
+    xmlNodePtr *nodes = NULL;
6ae9ed
+    size_t i;
6ae9ed
     char *tmp = NULL;
6ae9ed
     unsigned int maxvcpus;
6ae9ed
     unsigned int vcpus;
6ae9ed
@@ -15618,8 +15667,6 @@ virDomainVcpuParse(virDomainDefPtr def,
6ae9ed
         vcpus = maxvcpus;
6ae9ed
     }
6ae9ed
 
6ae9ed
-    if (virDomainDefSetVcpus(def, vcpus) < 0)
6ae9ed
-        goto cleanup;
6ae9ed
 
6ae9ed
     tmp = virXPathString("string(./vcpu[1]/@placement)", ctxt);
6ae9ed
     if (tmp) {
6ae9ed
@@ -15651,9 +15698,82 @@ virDomainVcpuParse(virDomainDefPtr def,
6ae9ed
         }
6ae9ed
     }
6ae9ed
 
6ae9ed
+    if ((n = virXPathNodeSet("./vcpus/vcpu", ctxt, &nodes)) < 0)
6ae9ed
+        goto cleanup;
6ae9ed
+
6ae9ed
+    if (n) {
6ae9ed
+        /* if individual vcpu states are provided take them as master */
6ae9ed
+        def->individualvcpus = true;
6ae9ed
+
6ae9ed
+        for (i = 0; i < n; i++) {
6ae9ed
+            virDomainVcpuDefPtr vcpu;
6ae9ed
+            int state;
6ae9ed
+            unsigned int id;
6ae9ed
+            unsigned int order;
6ae9ed
+
6ae9ed
+            if (!(tmp = virXMLPropString(nodes[i], "id")) ||
6ae9ed
+                virStrToLong_uip(tmp, NULL, 10, &id) < 0) {
6ae9ed
+                virReportError(VIR_ERR_XML_ERROR, "%s",
6ae9ed
+                               _("missing or invalid vcpu id"));
6ae9ed
+                goto cleanup;
6ae9ed
+            }
6ae9ed
+
6ae9ed
+            VIR_FREE(tmp);
6ae9ed
+
6ae9ed
+            if (id >= def->maxvcpus) {
6ae9ed
+                virReportError(VIR_ERR_XML_ERROR,
6ae9ed
+                               _("vcpu id '%u' is out of range of maximum "
6ae9ed
+                                 "vcpu count"), id);
6ae9ed
+                goto cleanup;
6ae9ed
+            }
6ae9ed
+
6ae9ed
+            vcpu = virDomainDefGetVcpu(def, id);
6ae9ed
+
6ae9ed
+            if (!(tmp = virXMLPropString(nodes[i], "enabled"))) {
6ae9ed
+                virReportError(VIR_ERR_XML_ERROR, "%s",
6ae9ed
+                               _("missing vcpu enabled state"));
6ae9ed
+                goto cleanup;
6ae9ed
+            }
6ae9ed
+
6ae9ed
+            if ((state = virTristateBoolTypeFromString(tmp)) < 0) {
6ae9ed
+                virReportError(VIR_ERR_XML_ERROR,
6ae9ed
+                               _("invalid vcpu 'enabled' value '%s'"), tmp);
6ae9ed
+                goto cleanup;
6ae9ed
+            }
6ae9ed
+            VIR_FREE(tmp);
6ae9ed
+
6ae9ed
+            vcpu->online = state == VIR_TRISTATE_BOOL_YES;
6ae9ed
+
6ae9ed
+            if ((tmp = virXMLPropString(nodes[i], "hotpluggable"))) {
6ae9ed
+                int hotpluggable;
6ae9ed
+                if ((hotpluggable = virTristateBoolTypeFromString(tmp)) < 0) {
6ae9ed
+                    virReportError(VIR_ERR_XML_ERROR,
6ae9ed
+                                   _("invalid vcpu 'hotpluggable' value '%s'"), tmp);
6ae9ed
+                    goto cleanup;
6ae9ed
+                }
6ae9ed
+                vcpu->hotpluggable = hotpluggable;
6ae9ed
+                VIR_FREE(tmp);
6ae9ed
+            }
6ae9ed
+
6ae9ed
+            if ((tmp = virXMLPropString(nodes[i], "order"))) {
6ae9ed
+                if (virStrToLong_uip(tmp, NULL, 10, &order) < 0) {
6ae9ed
+                    virReportError(VIR_ERR_XML_ERROR, "%s",
6ae9ed
+                                   _("invalid vcpu order"));
6ae9ed
+                    goto cleanup;
6ae9ed
+                }
6ae9ed
+                vcpu->order = order;
6ae9ed
+                VIR_FREE(tmp);
6ae9ed
+            }
6ae9ed
+        }
6ae9ed
+    } else {
6ae9ed
+        if (virDomainDefSetVcpus(def, vcpus) < 0)
6ae9ed
+            goto cleanup;
6ae9ed
+    }
6ae9ed
+
6ae9ed
     ret = 0;
6ae9ed
 
6ae9ed
  cleanup:
6ae9ed
+    VIR_FREE(nodes);
6ae9ed
     VIR_FREE(tmp);
6ae9ed
 
6ae9ed
     return ret;
6ae9ed
@@ -18679,6 +18799,13 @@ virDomainDefVcpuCheckAbiStability(virDomainDefPtr src,
6ae9ed
                              "destination definitions"), i);
6ae9ed
             return false;
6ae9ed
         }
6ae9ed
+
6ae9ed
+        if (svcpu->order != dvcpu->order) {
6ae9ed
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
6ae9ed
+                           _("vcpu enable order of vCPU '%zu' differs between "
6ae9ed
+                             "source and destination definitions"), i);
6ae9ed
+            return false;
6ae9ed
+        }
6ae9ed
     }
6ae9ed
 
6ae9ed
     return true;
6ae9ed
@@ -22921,6 +23048,8 @@ static int
6ae9ed
 virDomainCpuDefFormat(virBufferPtr buf,
6ae9ed
                       const virDomainDef *def)
6ae9ed
 {
6ae9ed
+    virDomainVcpuDefPtr vcpu;
6ae9ed
+    size_t i;
6ae9ed
     char *cpumask = NULL;
6ae9ed
     int ret = -1;
6ae9ed
 
6ae9ed
@@ -22937,6 +23066,27 @@ virDomainCpuDefFormat(virBufferPtr buf,
6ae9ed
         virBufferAsprintf(buf, " current='%u'", virDomainDefGetVcpus(def));
6ae9ed
     virBufferAsprintf(buf, ">%u</vcpu>\n", virDomainDefGetVcpusMax(def));
6ae9ed
 
6ae9ed
+    if (def->individualvcpus) {
6ae9ed
+        virBufferAddLit(buf, "<vcpus>\n");
6ae9ed
+        virBufferAdjustIndent(buf, 2);
6ae9ed
+        for (i = 0; i < def->maxvcpus; i++) {
6ae9ed
+            vcpu = def->vcpus[i];
6ae9ed
+
6ae9ed
+            virBufferAsprintf(buf, "
6ae9ed
+                              i, vcpu->online ? "yes" : "no");
6ae9ed
+            if (vcpu->hotpluggable)
6ae9ed
+                virBufferAsprintf(buf, " hotpluggable='%s'",
6ae9ed
+                                  virTristateBoolTypeToString(vcpu->hotpluggable));
6ae9ed
+
6ae9ed
+            if (vcpu->order != 0)
6ae9ed
+                virBufferAsprintf(buf, " order='%d'", vcpu->order);
6ae9ed
+
6ae9ed
+            virBufferAddLit(buf, "/>\n");
6ae9ed
+        }
6ae9ed
+        virBufferAdjustIndent(buf, -2);
6ae9ed
+        virBufferAddLit(buf, "</vcpus>\n");
6ae9ed
+    }
6ae9ed
+
6ae9ed
     ret = 0;
6ae9ed
 
6ae9ed
  cleanup:
6ae9ed
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
6ae9ed
index e7ff401..34da54c 100644
6ae9ed
--- a/src/conf/domain_conf.h
6ae9ed
+++ b/src/conf/domain_conf.h
6ae9ed
@@ -2040,6 +2040,9 @@ typedef virDomainVcpuDef *virDomainVcpuDefPtr;
6ae9ed
 
6ae9ed
 struct _virDomainVcpuDef {
6ae9ed
     bool online;
6ae9ed
+    virTristateBool hotpluggable;
6ae9ed
+    unsigned int order;
6ae9ed
+
6ae9ed
     virBitmapPtr cpumask;
6ae9ed
 
6ae9ed
     virDomainThreadSchedParam sched;
6ae9ed
@@ -2136,6 +2139,8 @@ struct _virDomainDef {
6ae9ed
 
6ae9ed
     virDomainVcpuDefPtr *vcpus;
6ae9ed
     size_t maxvcpus;
6ae9ed
+    /* set if the vcpu definition was specified individually */
6ae9ed
+    bool individualvcpus;
6ae9ed
     int placement_mode;
6ae9ed
     virBitmapPtr cpumask;
6ae9ed
 
6ae9ed
@@ -2338,6 +2343,7 @@ typedef enum {
6ae9ed
     VIR_DOMAIN_DEF_FEATURE_MEMORY_HOTPLUG = (1 << 1),
6ae9ed
     VIR_DOMAIN_DEF_FEATURE_OFFLINE_VCPUPIN = (1 << 2),
6ae9ed
     VIR_DOMAIN_DEF_FEATURE_NAME_SLASH = (1 << 3),
6ae9ed
+    VIR_DOMAIN_DEF_FEATURE_INDIVIDUAL_VCPUS = (1 << 4),
6ae9ed
 } virDomainDefFeatures;
6ae9ed
 
6ae9ed
 
6ae9ed
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
6ae9ed
index add8379..7a71bfb 100644
6ae9ed
--- a/src/qemu/qemu_domain.c
6ae9ed
+++ b/src/qemu/qemu_domain.c
6ae9ed
@@ -5796,8 +5796,17 @@ qemuDomainRefreshVcpuInfo(virQEMUDriverPtr driver,
6ae9ed
         VIR_STEAL_PTR(vcpupriv->alias, info[i].alias);
6ae9ed
         vcpupriv->enable_id = info[i].id;
6ae9ed
 
6ae9ed
-        if (hotplug && state)
6ae9ed
+        if (hotplug && state) {
6ae9ed
             vcpu->online = !!info[i].qom_path;
6ae9ed
+
6ae9ed
+            /* mark cpus that don't have an alias as non-hotpluggable */
6ae9ed
+            if (vcpu->online) {
6ae9ed
+                if (vcpupriv->alias)
6ae9ed
+                    vcpu->hotpluggable = VIR_TRISTATE_BOOL_YES;
6ae9ed
+                else
6ae9ed
+                    vcpu->hotpluggable = VIR_TRISTATE_BOOL_NO;
6ae9ed
+            }
6ae9ed
+        }
6ae9ed
     }
6ae9ed
 
6ae9ed
     ret = 0;
6ae9ed
diff --git a/tests/genericxml2xmlindata/generic-vcpus-individual.xml b/tests/genericxml2xmlindata/generic-vcpus-individual.xml
6ae9ed
new file mode 100644
6ae9ed
index 0000000..cbcf8fd
6ae9ed
--- /dev/null
6ae9ed
+++ b/tests/genericxml2xmlindata/generic-vcpus-individual.xml
6ae9ed
@@ -0,0 +1,23 @@
6ae9ed
+<domain type='qemu'>
6ae9ed
+  <name>foobar</name>
6ae9ed
+  <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
6ae9ed
+  <memory unit='KiB'>219136</memory>
6ae9ed
+  <currentMemory unit='KiB'>219136</currentMemory>
6ae9ed
+  <vcpu placement='static' current='2'>4</vcpu>
6ae9ed
+  <vcpus>
6ae9ed
+    <vcpu id='0' enabled='no' hotpluggable='yes' order='1'/>
6ae9ed
+    <vcpu id='1' enabled='yes' hotpluggable='no'/>
6ae9ed
+    <vcpu id='2' enabled='no' hotpluggable='yes' order='2'/>
6ae9ed
+    <vcpu id='3' enabled='yes' hotpluggable='no'/>
6ae9ed
+  </vcpus>
6ae9ed
+  <os>
6ae9ed
+    <type arch='i686' machine='pc'>hvm</type>
6ae9ed
+    <boot dev='hd'/>
6ae9ed
+  </os>
6ae9ed
+  <clock offset='utc'/>
6ae9ed
+  <on_poweroff>destroy</on_poweroff>
6ae9ed
+  <on_reboot>restart</on_reboot>
6ae9ed
+  <on_crash>destroy</on_crash>
6ae9ed
+  <devices>
6ae9ed
+  </devices>
6ae9ed
+</domain>
6ae9ed
diff --git a/tests/genericxml2xmltest.c b/tests/genericxml2xmltest.c
6ae9ed
index a487727..2ea2396 100644
6ae9ed
--- a/tests/genericxml2xmltest.c
6ae9ed
+++ b/tests/genericxml2xmltest.c
6ae9ed
@@ -97,6 +97,8 @@ mymain(void)
6ae9ed
 
6ae9ed
     DO_TEST("perf");
6ae9ed
 
6ae9ed
+    DO_TEST("vcpus-individual");
6ae9ed
+
6ae9ed
     virObjectUnref(caps);
6ae9ed
     virObjectUnref(xmlopt);
6ae9ed
 
6ae9ed
diff --git a/tests/testutils.c b/tests/testutils.c
6ae9ed
index be61e4d..2daad43 100644
6ae9ed
--- a/tests/testutils.c
6ae9ed
+++ b/tests/testutils.c
6ae9ed
@@ -1075,7 +1075,9 @@ virCapsPtr virTestGenericCapsInit(void)
6ae9ed
     return NULL;
6ae9ed
 }
6ae9ed
 
6ae9ed
-static virDomainDefParserConfig virTestGenericDomainDefParserConfig;
6ae9ed
+static virDomainDefParserConfig virTestGenericDomainDefParserConfig = {
6ae9ed
+    .features = VIR_DOMAIN_DEF_FEATURE_INDIVIDUAL_VCPUS,
6ae9ed
+};
6ae9ed
 static virDomainXMLPrivateDataCallbacks virTestGenericPrivateDataCallbacks;
6ae9ed
 
6ae9ed
 virDomainXMLOptionPtr virTestGenericDomainXMLConfInit(void)
6ae9ed
-- 
6ae9ed
2.10.0
6ae9ed