render / rpms / libvirt

Forked from rpms/libvirt 4 months ago
Clone
79b470
From 5f6723e71e3765d1d43bfa9ba1c66e0e05e11a48 Mon Sep 17 00:00:00 2001
79b470
Message-Id: <5f6723e71e3765d1d43bfa9ba1c66e0e05e11a48@dist-git>
79b470
From: Michal Privoznik <mprivozn@redhat.com>
79b470
Date: Mon, 9 Nov 2020 17:22:32 +0100
79b470
Subject: [PATCH] Allow NUMA nodes without vCPUs
79b470
MIME-Version: 1.0
79b470
Content-Type: text/plain; charset=UTF-8
79b470
Content-Transfer-Encoding: 8bit
79b470
79b470
QEMU allows creating NUMA nodes that have memory only.
79b470
These are somehow important for HMAT.
79b470
79b470
With check done in qemuValidateDomainDef() for QEMU 2.7 or newer
79b470
(checked via QEMU_CAPS_NUMA), we can be sure that the vCPUs are
79b470
fully assigned to NUMA nodes in domain XML.
79b470
79b470
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
79b470
Reviewed-by: Daniel Henrique Barboza <danielhb413@gmail.com>
79b470
(cherry picked from commit a26f61ee0cffa421b87ef568002b684dd8025432)
79b470
79b470
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1749518
79b470
79b470
Conflicts:
79b470
- src/qemu/qemu_validate.c: This file doesn't exist in downstream
79b470
yet, so I've moved the change that original patch would do to
79b470
qemu_domain.c where the validator lives.
79b470
79b470
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
79b470
Message-Id: <365508c75e579e9037ad555d6c372068ccd50c95.1604938867.git.mprivozn@redhat.com>
79b470
Reviewed-by: Ján Tomko <jtomko@redhat.com>
79b470
---
79b470
 docs/formatdomain.html.in                     |  2 +
79b470
 docs/schemas/cputypes.rng                     |  8 ++-
79b470
 src/conf/numa_conf.c                          | 59 ++++++++++---------
79b470
 src/libxl/xen_xl.c                            | 10 ++--
79b470
 src/qemu/qemu_command.c                       | 26 ++++----
79b470
 src/qemu/qemu_domain.c                        | 22 +++----
79b470
 tests/qemuxml2argvdata/numatune-no-vcpu.args  | 33 +++++++++++
79b470
 tests/qemuxml2argvdata/numatune-no-vcpu.xml   | 42 +++++++++++++
79b470
 tests/qemuxml2argvtest.c                      |  1 +
79b470
 tests/qemuxml2xmloutdata/numatune-no-vcpu.xml |  1 +
79b470
 tests/qemuxml2xmltest.c                       |  1 +
79b470
 11 files changed, 149 insertions(+), 56 deletions(-)
79b470
 create mode 100644 tests/qemuxml2argvdata/numatune-no-vcpu.args
79b470
 create mode 100644 tests/qemuxml2argvdata/numatune-no-vcpu.xml
79b470
 create mode 120000 tests/qemuxml2xmloutdata/numatune-no-vcpu.xml
79b470
79b470
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
79b470
index 76799f5ffc..4b8d312596 100644
79b470
--- a/docs/formatdomain.html.in
79b470
+++ b/docs/formatdomain.html.in
79b470
@@ -1783,6 +1783,8 @@
79b470
       cpus specifies the CPU or range of CPUs that are
79b470
       part of the node. memory specifies the node memory
79b470
       in kibibytes (i.e. blocks of 1024 bytes).
79b470
+      Since 6.6.0 the cpus attribute
79b470
+      is optional and if omitted a CPU-less NUMA node is created.
79b470
       Since 1.2.11 one can use an additional 
79b470
           href="#elementsMemoryAllocation">unit attribute to
79b470
       define units in which memory is specified.
79b470
diff --git a/docs/schemas/cputypes.rng b/docs/schemas/cputypes.rng
79b470
index e2744acad3..a1682a1003 100644
79b470
--- a/docs/schemas/cputypes.rng
79b470
+++ b/docs/schemas/cputypes.rng
79b470
@@ -115,9 +115,11 @@
79b470
           <ref name="unsignedInt"/>
79b470
         </attribute>
79b470
       </optional>
79b470
-      <attribute name="cpus">
79b470
-        <ref name="cpuset"/>
79b470
-      </attribute>
79b470
+      <optional>
79b470
+        <attribute name="cpus">
79b470
+          <ref name="cpuset"/>
79b470
+        </attribute>
79b470
+      </optional>
79b470
       <attribute name="memory">
79b470
         <ref name="memoryKB"/>
79b470
       </attribute>
79b470
diff --git a/src/conf/numa_conf.c b/src/conf/numa_conf.c
79b470
index c9cc8ac22e..a805336d16 100644
79b470
--- a/src/conf/numa_conf.c
79b470
+++ b/src/conf/numa_conf.c
79b470
@@ -889,32 +889,28 @@ virDomainNumaDefParseXML(virDomainNumaPtr def,
79b470
         }
79b470
         VIR_FREE(tmp);
79b470
 
79b470
-        if (def->mem_nodes[cur_cell].cpumask) {
79b470
+        if (def->mem_nodes[cur_cell].mem) {
79b470
             virReportError(VIR_ERR_XML_ERROR,
79b470
                            _("Duplicate NUMA cell info for cell id '%u'"),
79b470
                            cur_cell);
79b470
             goto cleanup;
79b470
         }
79b470
 
79b470
-        if (!(tmp = virXMLPropString(nodes[i], "cpus"))) {
79b470
-            virReportError(VIR_ERR_XML_ERROR, "%s",
79b470
-                           _("Missing 'cpus' attribute in NUMA cell"));
79b470
-            goto cleanup;
79b470
-        }
79b470
+        if ((tmp = virXMLPropString(nodes[i], "cpus"))) {
79b470
+            g_autoptr(virBitmap) cpumask = NULL;
79b470
 
79b470
-        if (virBitmapParse(tmp, &def->mem_nodes[cur_cell].cpumask,
79b470
-                           VIR_DOMAIN_CPUMASK_LEN) < 0)
79b470
-            goto cleanup;
79b470
+            if (virBitmapParse(tmp, &cpumask, VIR_DOMAIN_CPUMASK_LEN) < 0)
79b470
+                goto cleanup;
79b470
 
79b470
-        if (virBitmapIsAllClear(def->mem_nodes[cur_cell].cpumask)) {
79b470
-            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
79b470
-                          _("NUMA cell %d has no vCPUs assigned"), cur_cell);
79b470
-            goto cleanup;
79b470
+            if (!virBitmapIsAllClear(cpumask))
79b470
+                def->mem_nodes[cur_cell].cpumask = g_steal_pointer(&cpumask);
79b470
+            VIR_FREE(tmp);
79b470
         }
79b470
-        VIR_FREE(tmp);
79b470
 
79b470
         for (j = 0; j < n; j++) {
79b470
-            if (j == cur_cell || !def->mem_nodes[j].cpumask)
79b470
+            if (j == cur_cell ||
79b470
+                !def->mem_nodes[j].cpumask ||
79b470
+                !def->mem_nodes[cur_cell].cpumask)
79b470
                 continue;
79b470
 
79b470
             if (virBitmapOverlaps(def->mem_nodes[j].cpumask,
79b470
@@ -976,7 +972,6 @@ virDomainNumaDefFormatXML(virBufferPtr buf,
79b470
 {
79b470
     virDomainMemoryAccess memAccess;
79b470
     virTristateBool discard;
79b470
-    char *cpustr;
79b470
     size_t ncells = virDomainNumaGetNodeCount(def);
79b470
     size_t i;
79b470
 
79b470
@@ -986,17 +981,22 @@ virDomainNumaDefFormatXML(virBufferPtr buf,
79b470
     virBufferAddLit(buf, "<numa>\n");
79b470
     virBufferAdjustIndent(buf, 2);
79b470
     for (i = 0; i < ncells; i++) {
79b470
+        virBitmapPtr cpumask = virDomainNumaGetNodeCpumask(def, i);
79b470
         int ndistances;
79b470
 
79b470
         memAccess = virDomainNumaGetNodeMemoryAccessMode(def, i);
79b470
         discard = virDomainNumaGetNodeDiscard(def, i);
79b470
 
79b470
-        if (!(cpustr = virBitmapFormat(virDomainNumaGetNodeCpumask(def, i))))
79b470
-            return -1;
79b470
-
79b470
         virBufferAddLit(buf, "
79b470
         virBufferAsprintf(buf, " id='%zu'", i);
79b470
-        virBufferAsprintf(buf, " cpus='%s'", cpustr);
79b470
+
79b470
+        if (cpumask) {
79b470
+            g_autofree char *cpustr = virBitmapFormat(cpumask);
79b470
+
79b470
+            if (!cpustr)
79b470
+                return -1;
79b470
+            virBufferAsprintf(buf, " cpus='%s'", cpustr);
79b470
+        }
79b470
         virBufferAsprintf(buf, " memory='%llu'",
79b470
                           virDomainNumaGetNodeMemorySize(def, i));
79b470
         virBufferAddLit(buf, " unit='KiB'");
79b470
@@ -1032,8 +1032,6 @@ virDomainNumaDefFormatXML(virBufferPtr buf,
79b470
             virBufferAdjustIndent(buf, -2);
79b470
             virBufferAddLit(buf, "</cell>\n");
79b470
         }
79b470
-
79b470
-        VIR_FREE(cpustr);
79b470
     }
79b470
     virBufferAdjustIndent(buf, -2);
79b470
     virBufferAddLit(buf, "</numa>\n");
79b470
@@ -1048,8 +1046,12 @@ virDomainNumaGetCPUCountTotal(virDomainNumaPtr numa)
79b470
     size_t i;
79b470
     unsigned int ret = 0;
79b470
 
79b470
-    for (i = 0; i < numa->nmem_nodes; i++)
79b470
-        ret += virBitmapCountBits(virDomainNumaGetNodeCpumask(numa, i));
79b470
+    for (i = 0; i < numa->nmem_nodes; i++) {
79b470
+        virBitmapPtr cpumask = virDomainNumaGetNodeCpumask(numa, i);
79b470
+
79b470
+        if (cpumask)
79b470
+            ret += virBitmapCountBits(cpumask);
79b470
+    }
79b470
 
79b470
     return ret;
79b470
 }
79b470
@@ -1061,11 +1063,14 @@ virDomainNumaGetMaxCPUID(virDomainNumaPtr numa)
79b470
     unsigned int ret = 0;
79b470
 
79b470
     for (i = 0; i < numa->nmem_nodes; i++) {
79b470
+        virBitmapPtr cpumask = virDomainNumaGetNodeCpumask(numa, i);
79b470
         int bit;
79b470
 
79b470
-        bit = virBitmapLastSetBit(virDomainNumaGetNodeCpumask(numa, i));
79b470
-        if (bit > ret)
79b470
-            ret = bit;
79b470
+        if (cpumask) {
79b470
+            bit = virBitmapLastSetBit(cpumask);
79b470
+            if (bit > ret)
79b470
+                ret = bit;
79b470
+        }
79b470
     }
79b470
 
79b470
     return ret;
79b470
diff --git a/src/libxl/xen_xl.c b/src/libxl/xen_xl.c
79b470
index edea30a86a..752fa925ec 100644
79b470
--- a/src/libxl/xen_xl.c
79b470
+++ b/src/libxl/xen_xl.c
79b470
@@ -1443,19 +1443,21 @@ xenFormatXLVnuma(virConfValuePtr list,
79b470
 {
79b470
     int ret = -1;
79b470
     size_t i;
79b470
-
79b470
     virBuffer buf = VIR_BUFFER_INITIALIZER;
79b470
     virConfValuePtr numaVnode, tmp;
79b470
-
79b470
+    virBitmapPtr cpumask = virDomainNumaGetNodeCpumask(numa, node);
79b470
     size_t nodeSize = virDomainNumaGetNodeMemorySize(numa, node) / 1024;
79b470
-    char *nodeVcpus = virBitmapFormat(virDomainNumaGetNodeCpumask(numa, node));
79b470
+    g_autofree char *nodeVcpus = NULL;
79b470
 
79b470
-    if (VIR_ALLOC(numaVnode) < 0)
79b470
+    if (!cpumask ||
79b470
+        VIR_ALLOC(numaVnode) < 0)
79b470
         goto cleanup;
79b470
 
79b470
     numaVnode->type = VIR_CONF_LIST;
79b470
     numaVnode->list = NULL;
79b470
 
79b470
+    nodeVcpus = virBitmapFormat(cpumask);
79b470
+
79b470
     /* pnode */
79b470
     virBufferAsprintf(&buf, "pnode=%zu", node);
79b470
     xenFormatXLVnode(numaVnode, &buf;;
79b470
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
79b470
index 1a573c2817..ac63d18a42 100644
79b470
--- a/src/qemu/qemu_command.c
79b470
+++ b/src/qemu/qemu_command.c
79b470
@@ -7364,8 +7364,6 @@ qemuBuildNumaCommandLine(virQEMUDriverConfigPtr cfg,
79b470
     size_t i, j;
79b470
     virQEMUCapsPtr qemuCaps = priv->qemuCaps;
79b470
     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
79b470
-    char *cpumask = NULL;
79b470
-    char *tmpmask = NULL;
79b470
     char *next = NULL;
79b470
     virBufferPtr nodeBackends = NULL;
79b470
     bool needBackend = false;
79b470
@@ -7400,9 +7398,7 @@ qemuBuildNumaCommandLine(virQEMUDriverConfigPtr cfg,
79b470
         goto cleanup;
79b470
 
79b470
     for (i = 0; i < ncells; i++) {
79b470
-        VIR_FREE(cpumask);
79b470
-        if (!(cpumask = virBitmapFormat(virDomainNumaGetNodeCpumask(def->numa, i))))
79b470
-            goto cleanup;
79b470
+        virBitmapPtr cpumask = virDomainNumaGetNodeCpumask(def->numa, i);
79b470
 
79b470
         if (needBackend) {
79b470
             virCommandAddArg(cmd, "-object");
79b470
@@ -7412,11 +7408,19 @@ qemuBuildNumaCommandLine(virQEMUDriverConfigPtr cfg,
79b470
         virCommandAddArg(cmd, "-numa");
79b470
         virBufferAsprintf(&buf, "node,nodeid=%zu", i);
79b470
 
79b470
-        for (tmpmask = cpumask; tmpmask; tmpmask = next) {
79b470
-            if ((next = strchr(tmpmask, ',')))
79b470
-                *(next++) = '\0';
79b470
-            virBufferAddLit(&buf, ",cpus=");
79b470
-            virBufferAdd(&buf, tmpmask, -1);
79b470
+        if (cpumask) {
79b470
+            g_autofree char *cpumaskStr = NULL;
79b470
+            char *tmpmask;
79b470
+
79b470
+            if (!(cpumaskStr = virBitmapFormat(cpumask)))
79b470
+                goto cleanup;
79b470
+
79b470
+            for (tmpmask = cpumaskStr; tmpmask; tmpmask = next) {
79b470
+                if ((next = strchr(tmpmask, ',')))
79b470
+                    *(next++) = '\0';
79b470
+                virBufferAddLit(&buf, ",cpus=");
79b470
+                virBufferAdd(&buf, tmpmask, -1);
79b470
+            }
79b470
         }
79b470
 
79b470
         if (needBackend)
79b470
@@ -7447,8 +7451,6 @@ qemuBuildNumaCommandLine(virQEMUDriverConfigPtr cfg,
79b470
     ret = 0;
79b470
 
79b470
  cleanup:
79b470
-    VIR_FREE(cpumask);
79b470
-
79b470
     if (nodeBackends) {
79b470
         for (i = 0; i < ncells; i++)
79b470
             virBufferFreeAndReset(&nodeBackends[i]);
79b470
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
79b470
index 35b536868a..be25790f12 100644
79b470
--- a/src/qemu/qemu_domain.c
79b470
+++ b/src/qemu/qemu_domain.c
79b470
@@ -5373,7 +5373,7 @@ qemuDomainDefValidateNuma(const virDomainDef *def,
79b470
     }
79b470
 
79b470
     for (i = 0; i < ncells; i++) {
79b470
-        g_autofree char * cpumask = NULL;
79b470
+        virBitmapPtr cpumask = virDomainNumaGetNodeCpumask(def->numa, i);
79b470
 
79b470
         if (!hasMemoryCap &&
79b470
             virDomainNumaGetNodeMemoryAccessMode(def->numa, i)) {
79b470
@@ -5383,17 +5383,19 @@ qemuDomainDefValidateNuma(const virDomainDef *def,
79b470
             return -1;
79b470
         }
79b470
 
79b470
-        if (!(cpumask = virBitmapFormat(virDomainNumaGetNodeCpumask(def->numa, i))))
79b470
-            return -1;
79b470
+        if (cpumask) {
79b470
+            g_autofree char * cpumaskStr = NULL;
79b470
+            if (!(cpumaskStr = virBitmapFormat(cpumask)))
79b470
+                return -1;
79b470
 
79b470
-        if (strchr(cpumask, ',') &&
79b470
-            !virQEMUCapsGet(qemuCaps, QEMU_CAPS_NUMA)) {
79b470
-            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
79b470
-                           _("disjoint NUMA cpu ranges are not supported "
79b470
-                             "with this QEMU"));
79b470
-            return -1;
79b470
+            if (strchr(cpumaskStr, ',') &&
79b470
+                !virQEMUCapsGet(qemuCaps, QEMU_CAPS_NUMA)) {
79b470
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
79b470
+                               _("disjoint NUMA cpu ranges are not supported "
79b470
+                                 "with this QEMU"));
79b470
+                return -1;
79b470
+            }
79b470
         }
79b470
-
79b470
     }
79b470
 
79b470
     if (virDomainNumaNodesDistancesAreBeingSet(def->numa) &&
79b470
diff --git a/tests/qemuxml2argvdata/numatune-no-vcpu.args b/tests/qemuxml2argvdata/numatune-no-vcpu.args
79b470
new file mode 100644
79b470
index 0000000000..a1f1ee044e
79b470
--- /dev/null
79b470
+++ b/tests/qemuxml2argvdata/numatune-no-vcpu.args
79b470
@@ -0,0 +1,33 @@
79b470
+LC_ALL=C \
79b470
+PATH=/bin \
79b470
+HOME=/tmp/lib/domain--1-QEMUGuest \
79b470
+USER=test \
79b470
+LOGNAME=test \
79b470
+XDG_DATA_HOME=/tmp/lib/domain--1-QEMUGuest/.local/share \
79b470
+XDG_CACHE_HOME=/tmp/lib/domain--1-QEMUGuest/.cache \
79b470
+XDG_CONFIG_HOME=/tmp/lib/domain--1-QEMUGuest/.config \
79b470
+QEMU_AUDIO_DRV=none \
79b470
+/usr/bin/qemu-system-x86_64 \
79b470
+-name QEMUGuest \
79b470
+-S \
79b470
+-machine pc,accel=tcg,usb=off,dump-guest-core=off \
79b470
+-m 12288 \
79b470
+-realtime mlock=off \
79b470
+-smp 12,sockets=12,cores=1,threads=1 \
79b470
+-numa node,nodeid=0,cpus=0-3,mem=2048 \
79b470
+-numa node,nodeid=1,cpus=4-7,mem=2048 \
79b470
+-numa node,nodeid=2,cpus=8-11,mem=2048 \
79b470
+-numa node,nodeid=3,mem=2048 \
79b470
+-numa node,nodeid=4,mem=2048 \
79b470
+-numa node,nodeid=5,mem=2048 \
79b470
+-uuid c7a5fdb2-cdaf-9455-926a-d65c16db1809 \
79b470
+-display none \
79b470
+-no-user-config \
79b470
+-nodefaults \
79b470
+-chardev socket,id=charmonitor,path=/tmp/lib/domain--1-QEMUGuest/monitor.sock,\
79b470
+server,nowait \
79b470
+-mon chardev=charmonitor,id=monitor,mode=control \
79b470
+-rtc base=utc \
79b470
+-no-shutdown \
79b470
+-usb \
79b470
+-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3
79b470
diff --git a/tests/qemuxml2argvdata/numatune-no-vcpu.xml b/tests/qemuxml2argvdata/numatune-no-vcpu.xml
79b470
new file mode 100644
79b470
index 0000000000..f25a07d7ed
79b470
--- /dev/null
79b470
+++ b/tests/qemuxml2argvdata/numatune-no-vcpu.xml
79b470
@@ -0,0 +1,42 @@
79b470
+<domain type='qemu'>
79b470
+  <name>QEMUGuest</name>
79b470
+  <uuid>c7a5fdb2-cdaf-9455-926a-d65c16db1809</uuid>
79b470
+  <memory unit='KiB'>12582912</memory>
79b470
+  <currentMemory unit='KiB'>12582912</currentMemory>
79b470
+  <vcpu placement='static'>12</vcpu>
79b470
+  <os>
79b470
+    <type arch='x86_64' machine='pc'>hvm</type>
79b470
+    <boot dev='hd'/>
79b470
+  </os>
79b470
+  <features>
79b470
+    <acpi/>
79b470
+    <apic/>
79b470
+    <pae/>
79b470
+  </features>
79b470
+  <cpu>
79b470
+    <numa>
79b470
+      <cell id='0' cpus='0-3' memory='2097152' unit='KiB'/>
79b470
+      <cell id='1' cpus='4-7' memory='2097152' unit='KiB'/>
79b470
+      <cell id='2' cpus='8-11' memory='2097152' unit='KiB'/>
79b470
+      <cell id='3' memory='2097152' unit='KiB'/>
79b470
+      <cell id='4' memory='2097152' unit='KiB'/>
79b470
+      <cell id='5' memory='2097152' unit='KiB'/>
79b470
+    </numa>
79b470
+  </cpu>
79b470
+  <clock offset='utc'/>
79b470
+  <on_poweroff>destroy</on_poweroff>
79b470
+  <on_reboot>restart</on_reboot>
79b470
+  <on_crash>restart</on_crash>
79b470
+  <devices>
79b470
+    <emulator>/usr/bin/qemu-system-x86_64</emulator>
79b470
+    <controller type='usb' index='0'>
79b470
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/>
79b470
+    </controller>
79b470
+    <controller type='pci' index='0' model='pci-root'/>
79b470
+    <input type='mouse' bus='ps2'/>
79b470
+    <input type='keyboard' bus='ps2'/>
79b470
+    <memballoon model='virtio'>
79b470
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
79b470
+    </memballoon>
79b470
+  </devices>
79b470
+</domain>
79b470
diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c
79b470
index ff92af606d..49699e495d 100644
79b470
--- a/tests/qemuxml2argvtest.c
79b470
+++ b/tests/qemuxml2argvtest.c
79b470
@@ -1812,6 +1812,7 @@ mymain(void)
79b470
     DO_TEST_PARSE_ERROR("numatune-memnode-no-memory", NONE);
79b470
 
79b470
     DO_TEST("numatune-distances", QEMU_CAPS_NUMA, QEMU_CAPS_NUMA_DIST);
79b470
+    DO_TEST("numatune-no-vcpu", NONE);
79b470
 
79b470
     DO_TEST("numatune-auto-nodeset-invalid", NONE);
79b470
     DO_TEST("numatune-auto-prefer", QEMU_CAPS_OBJECT_MEMORY_RAM,
79b470
diff --git a/tests/qemuxml2xmloutdata/numatune-no-vcpu.xml b/tests/qemuxml2xmloutdata/numatune-no-vcpu.xml
79b470
new file mode 120000
79b470
index 0000000000..f213032685
79b470
--- /dev/null
79b470
+++ b/tests/qemuxml2xmloutdata/numatune-no-vcpu.xml
79b470
@@ -0,0 +1 @@
79b470
+../qemuxml2argvdata/numatune-no-vcpu.xml
79b470
\ No newline at end of file
79b470
diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c
79b470
index 6c3f5c4a9e..1ddeba30f0 100644
79b470
--- a/tests/qemuxml2xmltest.c
79b470
+++ b/tests/qemuxml2xmltest.c
79b470
@@ -1105,6 +1105,7 @@ mymain(void)
79b470
     DO_TEST("numatune-memnode", QEMU_CAPS_NUMA, QEMU_CAPS_OBJECT_MEMORY_FILE);
79b470
     DO_TEST("numatune-memnode-no-memory", QEMU_CAPS_OBJECT_MEMORY_FILE);
79b470
     DO_TEST("numatune-distances", QEMU_CAPS_NUMA, QEMU_CAPS_NUMA_DIST);
79b470
+    DO_TEST("numatune-no-vcpu", QEMU_CAPS_NUMA);
79b470
 
79b470
     DO_TEST("bios-nvram", NONE);
79b470
     DO_TEST("bios-nvram-os-interleave", NONE);
79b470
-- 
79b470
2.29.2
79b470