[PATCH v2 20/27] conf: Introduce virtio-mem <memory/> model

Michal Privoznik mprivozn at redhat.com
Thu Dec 3 12:36:23 UTC 2020


QEMU gained this new virtio-mem model. It's similar to pc-dimm
in a sense that guest uses it as memory, but in a sense very
different from it as it can dynamically change allocation,
without need for hotplug. More specifically, the device has two
attributes more (it has more of course, but these two are
important here):

 1) block-size - the granularity of the device. You can imagine
    the device being divided into blocks, each 'block-size' long.

 2) requested-size - the portion of the device that is in use by
    the guest.

And it all works like this: at guest startup/hotplug both
block-size and requested-size are specified. When sysadmin wants
to give some more memory to the guest, or take some back, they
change the 'requested-size' attribute which is propagated to the
guest where virtio-mem module takes corresponding action.
This means, that 'requested-size' must be a whole number product
of 'block-size' and of course has to be in rage [0, max-size]
(including). The 'max-size' is yet another attribute but if not
set it's "inherited" from corresponding memory-backend-* object.

Therefore, two new elements are introduced under <target/>, to
reflect these attributes:

  <memory model='virtio'>
    <target>
      <size unit='KiB'>4194304</size>
      <node>0</node>
      <block unit='KiB'>2048</block>
      <requested unit='KiB'>524288</requested>
    </target>
    <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/>
  </memory>

The intent here is that <requested/> will be allowed to change
via virDomainUpdateDeviceFlags() API.

Note, QEMU does inform us about success of allocation via an
event - this is covered in next patches.

Signed-off-by: Michal Privoznik <mprivozn at redhat.com>
---
 docs/formatdomain.rst                         |  22 ++++
 docs/schemas/domaincommon.rng                 |  10 ++
 src/conf/domain_conf.c                        | 103 ++++++++++++++++--
 src/conf/domain_conf.h                        |   5 +
 .../memory-hotplug-virtio-mem.xml             |  78 +++++++++++++
 ...emory-hotplug-virtio-mem.x86_64-latest.xml |   1 +
 tests/qemuxml2xmltest.c                       |   1 +
 7 files changed, 213 insertions(+), 7 deletions(-)
 create mode 100644 tests/qemuxml2argvdata/memory-hotplug-virtio-mem.xml
 create mode 120000 tests/qemuxml2xmloutdata/memory-hotplug-virtio-mem.x86_64-latest.xml

diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst
index ca6bc0432e..3990728939 100644
--- a/docs/formatdomain.rst
+++ b/docs/formatdomain.rst
@@ -7187,6 +7187,17 @@ Example: usage of the memory devices
          </label>
        </target>
      </memory>
+     <memory model='virtio'>
+       <source>
+         <path>/tmp/virtio_mem</path>
+       </source>
+       <target>
+         <size unit='KiB'>1048576</size>
+         <node>0</node>
+         <block unit='KiB'>2048</block>
+         <requested unit='KiB'>524288</requested>
+       </target>
+     </memory>
      <memory model='virtio' access='shared'>
        <source>
          <path>/tmp/virtio_pmem</path>
@@ -7300,6 +7311,17 @@ Example: usage of the memory devices
       so other backend types should use the ``readonly`` element. :since:`Since
       5.0.0`
 
+   ``block``
+     The size of an individual block, granularity of division of memory module.
+     Must be power of two and at least equal to size of a transparent hugepage
+     (2MiB on x84_64). The default is hypervisor dependant. This is valid for
+     ``virtio`` model only and mutually exclusive with ``pmem``.
+
+   ``requested``
+     The total size of blocks exposed to the guest. Must respect ``block``
+     granularity. This is valid for ``virtio`` model only and mutually
+     exclusive with ``pmem``.
+
 :anchor:`<a id="elementsIommu"/>`
 
 IOMMU devices
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index b385bae84c..d478b639fa 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -6053,6 +6053,16 @@
         <element name="size">
           <ref name="scaledInteger"/>
         </element>
+        <optional>
+          <element name="block">
+            <ref name="scaledInteger"/>
+          </element>
+        </optional>
+        <optional>
+          <element name="requested">
+            <ref name="scaledInteger"/>
+          </element>
+        </optional>
         <optional>
           <element name="node">
             <ref name="unsignedInt"/>
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 935bea1804..0551f6f266 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -6764,10 +6764,23 @@ virDomainMemoryDefValidate(const virDomainMemoryDef *mem,
                 return -1;
             }
         } else {
-            /* TODO: plain virtio-mem behaves differently then virtio-pmem */
-            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                           _("virtio-mem is not supported yet. <pmem/> is required"));
-            return -1;
+            if (mem->requestedsize > mem->size) {
+                virReportError(VIR_ERR_XML_DETAIL, "%s",
+                               _("requested size must be smaller than @size"));
+                return -1;
+            }
+
+            if (!VIR_IS_POW2(mem->blocksize)) {
+                virReportError(VIR_ERR_XML_DETAIL, "%s",
+                               _("block size must be a power of two"));
+                return -1;
+            }
+
+            if (mem->requestedsize % mem->blocksize != 0) {
+                virReportError(VIR_ERR_XML_DETAIL, "%s",
+                               _("requested size must be an integer multiple of block size"));
+                return -1;
+            }
         }
         break;
 
@@ -16774,9 +16787,25 @@ virDomainMemorySourceDefParseXML(xmlNodePtr node,
     case VIR_DOMAIN_MEMORY_MODEL_VIRTIO:
         def->s.virtio.path = virXPathString("string(./path)", ctxt);
 
-        if (virXPathBoolean("boolean(./pmem)", ctxt))
+        if (virXPathBoolean("boolean(./pmem)", ctxt)) {
             def->s.virtio.pmem = true;
+        } else {
+            if (virDomainParseMemory("./pagesize", "./pagesize/@unit", ctxt,
+                                     &def->s.virtio.pagesize, false, false) < 0)
+                return -1;
 
+            if ((nodemask = virXPathString("string(./nodemask)", ctxt))) {
+                if (virBitmapParse(nodemask, &def->s.virtio.sourceNodes,
+                                   VIR_DOMAIN_CPUMASK_LEN) < 0)
+                    return -1;
+
+                if (virBitmapIsAllClear(def->s.virtio.sourceNodes)) {
+                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                                   _("Invalid value of 'nodemask': %s"), nodemask);
+                    return -1;
+                }
+            }
+        }
         break;
 
     case VIR_DOMAIN_MEMORY_MODEL_NONE:
@@ -16812,7 +16841,8 @@ virDomainMemoryTargetDefParseXML(xmlNodePtr node,
                              &def->size, true, false) < 0)
         return -1;
 
-    if (def->model == VIR_DOMAIN_MEMORY_MODEL_NVDIMM) {
+    switch (def->model) {
+    case VIR_DOMAIN_MEMORY_MODEL_NVDIMM:
         if (virDomainParseMemory("./label/size", "./label/size/@unit", ctxt,
                                  &def->labelsize, false, false) < 0)
             return -1;
@@ -16831,6 +16861,27 @@ virDomainMemoryTargetDefParseXML(xmlNodePtr node,
 
         if (virXPathBoolean("boolean(./readonly)", ctxt))
             def->readonly = true;
+
+        break;
+
+    case VIR_DOMAIN_MEMORY_MODEL_VIRTIO:
+        if (!def->s.virtio.pmem) {
+            if (virDomainParseMemory("./block", "./block/@unit", ctxt,
+                                     &def->blocksize, false, false) < 0)
+                return -1;
+
+            if (virDomainParseMemory("./requested", "./requested/@unit", ctxt,
+                                     &def->requestedsize, false, false) < 0)
+                return -1;
+        }
+
+        break;
+
+    case VIR_DOMAIN_MEMORY_MODEL_NONE:
+    case VIR_DOMAIN_MEMORY_MODEL_DIMM:
+    case VIR_DOMAIN_MEMORY_MODEL_LAST:
+        /* nada */
+        break;
     }
 
     return 0;
@@ -18649,7 +18700,9 @@ virDomainMemoryFindByDefInternal(virDomainDefPtr def,
         /* target info -> always present */
         if (tmp->model != mem->model ||
             tmp->targetNode != mem->targetNode ||
-            tmp->size != mem->size)
+            tmp->size != mem->size ||
+            tmp->blocksize != mem->blocksize ||
+            tmp->requestedsize != mem->requestedsize)
             continue;
 
         switch (mem->model) {
@@ -24255,6 +24308,22 @@ virDomainMemoryDefCheckABIStability(virDomainMemoryDefPtr src,
         return false;
     }
 
+    if (src->blocksize != dst->blocksize) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                       _("Target memory device block size '%llu' doesn't match "
+                         "source memory device block size '%llu'"),
+                       dst->blocksize, src->blocksize);
+        return false;
+    }
+
+    if (src->requestedsize != dst->requestedsize) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                       _("Target memory device requested size '%llu' doesn't match "
+                         "source memory device requested size '%llu'"),
+                       dst->requestedsize, src->requestedsize);
+        return false;
+    }
+
     switch (src->model) {
     case VIR_DOMAIN_MEMORY_MODEL_NVDIMM:
         if (src->labelsize != dst->labelsize) {
@@ -27967,6 +28036,18 @@ virDomainMemorySourceDefFormat(virBufferPtr buf,
 
         if (def->s.virtio.pmem)
             virBufferAddLit(&childBuf, "<pmem/>\n");
+
+        if (def->s.virtio.sourceNodes) {
+            if (!(bitmap = virBitmapFormat(def->s.virtio.sourceNodes)))
+                return -1;
+
+            virBufferAsprintf(&childBuf, "<nodemask>%s</nodemask>\n", bitmap);
+        }
+
+        if (def->s.virtio.pagesize) {
+            virBufferAsprintf(&childBuf, "<pagesize unit='KiB'>%llu</pagesize>\n",
+                              def->s.virtio.pagesize);
+        }
         break;
 
     case VIR_DOMAIN_MEMORY_MODEL_NONE:
@@ -27998,6 +28079,14 @@ virDomainMemoryTargetDefFormat(virBufferPtr buf,
     if (def->readonly)
         virBufferAddLit(&childBuf, "<readonly/>\n");
 
+    if (def->blocksize) {
+        virBufferAsprintf(&childBuf, "<block unit='KiB'>%llu</block>\n",
+                          def->blocksize);
+
+        virBufferAsprintf(&childBuf, "<requested unit='KiB'>%llu</requested>\n",
+                          def->requestedsize);
+    }
+
     virXMLFormatElement(buf, "target", NULL, &childBuf);
 }
 
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index efaa4c5473..f16dc0a029 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -2327,8 +2327,11 @@ struct _virDomainMemoryDef {
             bool pmem;
         } nvdimm; /* VIR_DOMAIN_MEMORY_MODEL_NVDIMM */
         struct {
+            // nodemask + hugepages + no prealloc
             char *path; /* Required for pmem, otherwise optional */
             bool pmem;
+            virBitmapPtr sourceNodes;
+            unsigned long long pagesize; /* kibibytes */
         } virtio; /* VIR_DOMAIN_MEMORY_MODEL_VIRTIO */
     } s;
 
@@ -2337,6 +2340,8 @@ struct _virDomainMemoryDef {
     int targetNode;
     unsigned long long size; /* kibibytes */
     unsigned long long labelsize; /* kibibytes; valid only for NVDIMM */
+    unsigned long long blocksize; /* kibibytes, valid for virtio-mem only */
+    unsigned long long requestedsize; /* kibibytes, valid for virtio-mem only */
     bool readonly; /* valid only for NVDIMM */
 
     /* required for QEMU NVDIMM ppc64 support */
diff --git a/tests/qemuxml2argvdata/memory-hotplug-virtio-mem.xml b/tests/qemuxml2argvdata/memory-hotplug-virtio-mem.xml
new file mode 100644
index 0000000000..0c8d1d970e
--- /dev/null
+++ b/tests/qemuxml2argvdata/memory-hotplug-virtio-mem.xml
@@ -0,0 +1,78 @@
+<domain type='kvm'>
+  <name>QEMUGuest1</name>
+  <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+  <maxMemory slots='16' unit='KiB'>1099511627776</maxMemory>
+  <memory unit='KiB'>8388608</memory>
+  <currentMemory unit='KiB'>8388608</currentMemory>
+  <vcpu placement='static' cpuset='0-1'>2</vcpu>
+  <os>
+    <type arch='x86_64' machine='pc'>hvm</type>
+    <boot dev='hd'/>
+  </os>
+  <cpu mode='custom' match='exact' check='none'>
+    <model fallback='forbid'>qemu64</model>
+    <topology sockets='2' dies='1' cores='1' threads='1'/>
+    <numa>
+      <cell id='0' cpus='0-1' memory='2095104' unit='KiB'/>
+    </numa>
+  </cpu>
+  <clock offset='utc'/>
+  <on_poweroff>destroy</on_poweroff>
+  <on_reboot>restart</on_reboot>
+  <on_crash>destroy</on_crash>
+  <devices>
+    <emulator>/usr/bin/qemu-system-i386</emulator>
+    <disk type='block' device='disk'>
+      <driver name='qemu' type='raw'/>
+      <source dev='/dev/HostVG/QEMUGuest1'/>
+      <target dev='hda' bus='ide'/>
+      <address type='drive' controller='0' bus='0' target='0' unit='0'/>
+    </disk>
+    <controller type='ide' index='0'>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
+    </controller>
+    <controller type='usb' index='0' model='piix3-uhci'>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/>
+    </controller>
+    <controller type='pci' index='0' model='pci-root'/>
+    <input type='mouse' bus='ps2'/>
+    <input type='keyboard' bus='ps2'/>
+    <memballoon model='virtio'>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
+    </memballoon>
+    <memory model='virtio'>
+      <target>
+        <size unit='KiB'>1048576</size>
+        <node>0</node>
+        <block unit='KiB'>2048</block>
+        <requested unit='KiB'>524288</requested>
+      </target>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
+    </memory>
+    <memory model='virtio'>
+      <source>
+        <nodemask>1-3</nodemask>
+        <pagesize unit='KiB'>2048</pagesize>
+      </source>
+      <target>
+        <size unit='KiB'>2097152</size>
+        <node>0</node>
+        <block unit='KiB'>2048</block>
+        <requested unit='KiB'>1048576</requested>
+      </target>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
+    </memory>
+    <memory model='virtio'>
+      <source>
+        <path>/tmp/virtio_mem</path>
+      </source>
+      <target>
+        <size unit='KiB'>4194304</size>
+        <node>0</node>
+        <block unit='KiB'>2048</block>
+        <requested unit='KiB'>524288</requested>
+      </target>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/>
+    </memory>
+  </devices>
+</domain>
diff --git a/tests/qemuxml2xmloutdata/memory-hotplug-virtio-mem.x86_64-latest.xml b/tests/qemuxml2xmloutdata/memory-hotplug-virtio-mem.x86_64-latest.xml
new file mode 120000
index 0000000000..a9d298129c
--- /dev/null
+++ b/tests/qemuxml2xmloutdata/memory-hotplug-virtio-mem.x86_64-latest.xml
@@ -0,0 +1 @@
+../qemuxml2argvdata/memory-hotplug-virtio-mem.xml
\ No newline at end of file
diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c
index e804db8aee..00d8f171bc 100644
--- a/tests/qemuxml2xmltest.c
+++ b/tests/qemuxml2xmltest.c
@@ -1239,6 +1239,7 @@ mymain(void)
     DO_TEST("memory-hotplug-nvdimm-ppc64", QEMU_CAPS_DEVICE_SPAPR_PCI_HOST_BRIDGE,
                                            QEMU_CAPS_DEVICE_NVDIMM);
     DO_TEST_CAPS_LATEST("memory-hotplug-virtio-pmem");
+    DO_TEST_CAPS_LATEST("memory-hotplug-virtio-mem");
 
     DO_TEST("net-udp", NONE);
 
-- 
2.26.2




More information about the libvir-list mailing list