From: Marc-André Lureau <marcandre.lureau(a)redhat.com>
Add a new memoryBacking source type "memfd", supported by QEMU (when
the capability 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 separate 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 295f9ff93e..e7f4ad4060 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -1126,7 +1126,7 @@
</hugepages>
<nosharepages/>
<locked/>
- <source type="file|anonymous"/>
+ <source type="file|anonymous|memfd"/>
<access mode="shared|private"/>
<allocation mode="immediate|ondemand"/>
<discard/>
@@ -1177,9 +1177,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.10.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 cad189513a..bfa76c4db3 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 6c15781dc0..bc82dc3504 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -898,7 +898,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 c167f8c43c..467785cd83 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 d543ada2a1..5b13e3fd86 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;
@@ -7566,7 +7593,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"));
@@ -7592,7 +7620,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 c374219aab..0f3f4948b8 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -3951,7 +3951,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;
@@ -3973,6 +3974,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 &&
@@ -4112,7 +4120,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 39a7f1f53c..d3b71d39df 100644
--- a/tests/qemuxml2argvtest.c
+++ b/tests/qemuxml2argvtest.c
@@ -2952,6 +2952,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.1.708.g4ede3d42df