From: Marc-André Lureau <marcandre.lureau(a)redhat.com>
Add a new memoryBacking source type "memfd", supported by QEMU (when
the apability is available).
A memfd is a specialized anonymous memory kind. As such, an anonymous
source type could be automatically using a memfd. However, there are
some complications when migrating from different memory backends in
qemu (mainly due to the internal object naming at this point, but
there could be more). For now, it is simpler and safer to simply
introduce a new source type "memfd". Eventually, the "anonymous" type
could learn to use memfd transparently in a seperate change.
The main benefits are that it doesn't need to create filesystem files,
and it also enforces sealing, providing a bit more safety.
Signed-off-by: Marc-André Lureau <marcandre.lureau(a)redhat.com>
---
docs/formatdomain.html.in | 9 +--
docs/schemas/domaincommon.rng | 1 +
src/conf/domain_conf.c | 3 +-
src/conf/domain_conf.h | 1 +
src/qemu/qemu_command.c | 69 +++++++++++++------
src/qemu/qemu_domain.c | 12 +++-
.../memfd-memory-numa.x86_64-latest.args | 34 +++++++++
tests/qemuxml2argvdata/memfd-memory-numa.xml | 36 ++++++++++
tests/qemuxml2argvtest.c | 2 +
9 files changed, 140 insertions(+), 27 deletions(-)
create mode 100644 tests/qemuxml2argvdata/memfd-memory-numa.x86_64-latest.args
create mode 100644 tests/qemuxml2argvdata/memfd-memory-numa.xml
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index 1f12ab5b42..eeee1f6d40 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -1099,7 +1099,7 @@
</hugepages>
<nosharepages/>
<locked/>
- <source type="file|anonymous"/>
+ <source type="file|anonymous|memfd"/>
<access mode="shared|private"/>
<allocation mode="immediate|ondemand"/>
<discard/>
@@ -1150,9 +1150,10 @@
suitable for the specific environment at the same time to mitigate
the risks described above. <span class="since">Since
1.0.6</span></dd>
<dt><code>source</code></dt>
- <dd>Using the <code>type</code> attribute, it's possible to
provide
- "file" to utilize file memorybacking or keep the default
- "anonymous".</dd>
+ <dd>Using the <code>type</code> attribute, it's possible to
+ provide "file" to utilize file memorybacking or keep the
+ default "anonymous". <span class="since">Since
4.8.0</span>,
+ you may choose "memfd" backing. (QEMU/KVM only)</dd>
<dt><code>access</code></dt>
<dd>Using the <code>mode</code> attribute, specify if the memory
is
to be "shared" or "private". This can be overridden per numa
node by
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index 099a949cf8..4b431b4188 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -655,6 +655,7 @@
<choice>
<value>file</value>
<value>anonymous</value>
+ <value>memfd</value>
</choice>
</attribute>
</element>
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 1ee43950ae..648015b5b5 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -894,7 +894,8 @@ VIR_ENUM_IMPL(virDomainDiskMirrorState,
VIR_DOMAIN_DISK_MIRROR_STATE_LAST,
VIR_ENUM_IMPL(virDomainMemorySource, VIR_DOMAIN_MEMORY_SOURCE_LAST,
"none",
"file",
- "anonymous")
+ "anonymous",
+ "memfd")
VIR_ENUM_IMPL(virDomainMemoryAllocation, VIR_DOMAIN_MEMORY_ALLOCATION_LAST,
"none",
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index e30a4b2fe7..00dacef3af 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -607,6 +607,7 @@ typedef enum {
VIR_DOMAIN_MEMORY_SOURCE_NONE = 0, /* No memory source defined */
VIR_DOMAIN_MEMORY_SOURCE_FILE, /* Memory source is set as file */
VIR_DOMAIN_MEMORY_SOURCE_ANONYMOUS, /* Memory source is set as anonymous */
+ VIR_DOMAIN_MEMORY_SOURCE_MEMFD, /* Memory source is set as memfd */
VIR_DOMAIN_MEMORY_SOURCE_LAST,
} virDomainMemorySource;
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 0a353f87ba..b7481b622d 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -3114,6 +3114,26 @@ qemuBuildControllerDevCommandLine(virCommandPtr cmd,
}
+static int
+qemuBuildMemoryBackendPropsShare(virJSONValuePtr props,
+ virDomainMemoryAccess memAccess)
+{
+ switch (memAccess) {
+ case VIR_DOMAIN_MEMORY_ACCESS_SHARED:
+ return virJSONValueObjectAdd(props, "b:share", true, NULL);
+
+ case VIR_DOMAIN_MEMORY_ACCESS_PRIVATE:
+ return virJSONValueObjectAdd(props, "b:share", false, NULL);
+
+ case VIR_DOMAIN_MEMORY_ACCESS_DEFAULT:
+ case VIR_DOMAIN_MEMORY_ACCESS_LAST:
+ break;
+ }
+
+ return 0;
+}
+
+
/**
* qemuBuildMemoryBackendProps:
* @backendProps: [out] constructed object
@@ -3133,7 +3153,7 @@ qemuBuildControllerDevCommandLine(virCommandPtr cmd,
* configuration value of 1 is returned. This behaviour can be suppressed by
* setting @force to true in which case 0 would be returned.
*
- * Then, if one of the two memory-backend-* should be used, the @qemuCaps is
+ * Then, if one of the three memory-backend-* should be used, the @qemuCaps is
* consulted to check if qemu does support it.
*
* Returns: 0 on success,
@@ -3259,7 +3279,19 @@ qemuBuildMemoryBackendProps(virJSONValuePtr *backendProps,
if (!(props = virJSONValueNewObject()))
return -1;
- if (useHugepage || mem->nvdimmPath || memAccess ||
+ if (def->mem.source == VIR_DOMAIN_MEMORY_SOURCE_MEMFD) {
+ backendType = "memory-backend-memfd";
+
+ if (useHugepage &&
+ (virJSONValueObjectAdd(props, "b:hugetlb", useHugepage, NULL) <
0 ||
+ virJSONValueObjectAdd(props, "U:hugetlbsize", pagesize <<
10, NULL) < 0)) {
+ goto cleanup;
+ }
+
+ if (qemuBuildMemoryBackendPropsShare(props, memAccess) < 0)
+ goto cleanup;
+
+ } else if (useHugepage || mem->nvdimmPath || memAccess ||
def->mem.source == VIR_DOMAIN_MEMORY_SOURCE_FILE) {
if (mem->nvdimmPath) {
@@ -3297,21 +3329,8 @@ qemuBuildMemoryBackendProps(virJSONValuePtr *backendProps,
goto cleanup;
}
- switch (memAccess) {
- case VIR_DOMAIN_MEMORY_ACCESS_SHARED:
- if (virJSONValueObjectAdd(props, "b:share", true, NULL) < 0)
- goto cleanup;
- break;
-
- case VIR_DOMAIN_MEMORY_ACCESS_PRIVATE:
- if (virJSONValueObjectAdd(props, "b:share", false, NULL) < 0)
- goto cleanup;
- break;
-
- case VIR_DOMAIN_MEMORY_ACCESS_DEFAULT:
- case VIR_DOMAIN_MEMORY_ACCESS_LAST:
- break;
- }
+ if (qemuBuildMemoryBackendPropsShare(props, memAccess) < 0)
+ goto cleanup;
} else {
backendType = "memory-backend-ram";
}
@@ -3341,7 +3360,9 @@ qemuBuildMemoryBackendProps(virJSONValuePtr *backendProps,
if (!needHugepage && !mem->sourceNodes && !nodeSpecified
&&
!mem->nvdimmPath &&
memAccess == VIR_DOMAIN_MEMORY_ACCESS_DEFAULT &&
- def->mem.source != VIR_DOMAIN_MEMORY_SOURCE_FILE && !force) {
+ def->mem.source != VIR_DOMAIN_MEMORY_SOURCE_FILE &&
+ def->mem.source != VIR_DOMAIN_MEMORY_SOURCE_MEMFD &&
+ !force) {
/* report back that using the new backend is not necessary
* to achieve the desired configuration */
ret = 1;
@@ -3359,6 +3380,12 @@ qemuBuildMemoryBackendProps(virJSONValuePtr *backendProps,
_("this qemu doesn't support the "
"memory-backend-ram object"));
goto cleanup;
+ } else if (STREQ(backendType, "memory-backend-memory") &&
+ !virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_MEMFD)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("this qemu doesn't support the "
+ "memory-backend-memfd object"));
+ goto cleanup;
}
ret = 0;
@@ -7567,7 +7594,8 @@ qemuBuildNumaArgStr(virQEMUDriverConfigPtr cfg,
if (virDomainNumatuneHasPerNodeBinding(def->numa) &&
!(virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_RAM) ||
- virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_FILE))) {
+ virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_FILE) ||
+ virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_MEMFD))) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Per-node memory binding is not supported "
"with this QEMU"));
@@ -7593,7 +7621,8 @@ qemuBuildNumaArgStr(virQEMUDriverConfigPtr cfg,
* need to check which approach to use */
for (i = 0; i < ncells; i++) {
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_RAM) ||
- virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_FILE)) {
+ virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_FILE) ||
+ virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_MEMFD)) {
if ((rc = qemuBuildMemoryCellBackendStr(def, cfg, i, priv,
&nodeBackends[i])) < 0)
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index 2fd8a2a268..4983669a34 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -3949,7 +3949,8 @@ qemuDomainDefValidateFeatures(const virDomainDef *def,
static int
-qemuDomainDefValidateMemory(const virDomainDef *def)
+qemuDomainDefValidateMemory(const virDomainDef *def,
+ virQEMUCapsPtr qemuCaps)
{
const long system_page_size = virGetSystemPageSizeKB();
const virDomainMemtune *mem = &def->mem;
@@ -3971,6 +3972,13 @@ qemuDomainDefValidateMemory(const virDomainDef *def)
return -1;
}
+ if (mem->source == VIR_DOMAIN_MEMORY_SOURCE_MEMFD &&
+ !virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_MEMFD_HUGETLB)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("hugepages is not support with memfd memory
source"));
+ return -1;
+ }
+
/* We can't guarantee any other mem.access
* if no guest NUMA nodes are defined. */
if (mem->hugepages[0].size != system_page_size &&
@@ -4110,7 +4118,7 @@ qemuDomainDefValidate(const virDomainDef *def,
if (qemuDomainDefValidateFeatures(def, qemuCaps) < 0)
goto cleanup;
- if (qemuDomainDefValidateMemory(def) < 0)
+ if (qemuDomainDefValidateMemory(def, qemuCaps) < 0)
goto cleanup;
ret = 0;
diff --git a/tests/qemuxml2argvdata/memfd-memory-numa.x86_64-latest.args
b/tests/qemuxml2argvdata/memfd-memory-numa.x86_64-latest.args
new file mode 100644
index 0000000000..d0f4057e01
--- /dev/null
+++ b/tests/qemuxml2argvdata/memfd-memory-numa.x86_64-latest.args
@@ -0,0 +1,34 @@
+LC_ALL=C \
+PATH=/bin \
+HOME=/home/test \
+USER=test \
+LOGNAME=test \
+QEMU_AUDIO_DRV=none \
+/usr/bin/qemu-system-x86_64 \
+-name guest=instance-00000092,debug-threads=on \
+-S \
+-object secret,id=masterKey0,format=raw,\
+file=/tmp/lib/domain--1-instance-00000092/master-key.aes \
+-machine pc-i440fx-wily,accel=kvm,usb=off,dump-guest-core=off \
+-m 14336 \
+-mem-prealloc \
+-realtime mlock=off \
+-smp 8,sockets=1,cores=8,threads=1 \
+-object memory-backend-memfd,id=ram-node0,hugetlb=yes,hugetlbsize=2097152,\
+share=yes,size=15032385536,host-nodes=3,policy=preferred \
+-numa node,nodeid=0,cpus=0-7,memdev=ram-node0 \
+-uuid 126f2720-6f8e-45ab-a886-ec9277079a67 \
+-display none \
+-no-user-config \
+-nodefaults \
+-chardev socket,id=charmonitor,fd=1729,server,nowait \
+-mon chardev=charmonitor,id=monitor,mode=control \
+-rtc base=utc \
+-no-shutdown \
+-no-acpi \
+-boot strict=on \
+-device piix3-usb-uhci,id=usb,bus=pci.0,addr=0x1.0x2 \
+-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x2 \
+-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,\
+resourcecontrol=deny \
+-msg timestamp=on
diff --git a/tests/qemuxml2argvdata/memfd-memory-numa.xml
b/tests/qemuxml2argvdata/memfd-memory-numa.xml
new file mode 100644
index 0000000000..8416a990fa
--- /dev/null
+++ b/tests/qemuxml2argvdata/memfd-memory-numa.xml
@@ -0,0 +1,36 @@
+ <domain type='kvm' id='56'>
+ <name>instance-00000092</name>
+ <uuid>126f2720-6f8e-45ab-a886-ec9277079a67</uuid>
+ <memory unit='KiB'>14680064</memory>
+ <currentMemory unit='KiB'>14680064</currentMemory>
+ <memoryBacking>
+ <hugepages>
+ <page size="2" unit="M"/>
+ </hugepages>
+ <source type='memfd'/>
+ <access mode='shared'/>
+ <allocation mode='immediate'/>
+ </memoryBacking>
+ <numatune>
+ <memnode cellid='0' mode='preferred' nodeset='3'/>
+ </numatune>
+ <vcpu placement='static'>8</vcpu>
+ <os>
+ <type arch='x86_64'
machine='pc-i440fx-wily'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <cpu>
+ <topology sockets='1' cores='8' threads='1'/>
+ <numa>
+ <cell id='0' cpus='0-7' memory='14680064'
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-x86_64</emulator>
+ <memballoon model='virtio'/>
+ </devices>
+ </domain>
diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c
index 3d84cb346a..79938a973c 100644
--- a/tests/qemuxml2argvtest.c
+++ b/tests/qemuxml2argvtest.c
@@ -2945,6 +2945,8 @@ mymain(void)
DO_TEST("fd-memory-no-numa-topology", QEMU_CAPS_OBJECT_MEMORY_FILE,
QEMU_CAPS_KVM);
+ DO_TEST_CAPS_LATEST("memfd-memory-numa");
+
DO_TEST("cpu-check-none", QEMU_CAPS_KVM);
DO_TEST("cpu-check-partial", QEMU_CAPS_KVM);
DO_TEST("cpu-check-full", QEMU_CAPS_KVM);
--
2.19.0