qemu: add memfd source type

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@redhat.com>
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
This commit is contained in:
Marc-André Lureau 2018-11-15 15:55:53 +04:00 committed by Michal Privoznik
parent a6fd5b596a
commit 24b74d187c
9 changed files with 140 additions and 27 deletions

View File

@ -1126,7 +1126,7 @@
&lt;/hugepages&gt;
&lt;nosharepages/&gt;
&lt;locked/&gt;
&lt;source type="file|anonymous"/&gt;
&lt;source type="file|anonymous|memfd"/&gt;
&lt;access mode="shared|private"/&gt;
&lt;allocation mode="immediate|ondemand"/&gt;
&lt;discard/&gt;
@ -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

View File

@ -655,6 +655,7 @@
<choice>
<value>file</value>
<value>anonymous</value>
<value>memfd</value>
</choice>
</attribute>
</element>

View File

@ -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",

View File

@ -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;

View File

@ -3176,6 +3176,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
@ -3195,7 +3215,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,
@ -3321,7 +3341,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) {
@ -3359,21 +3391,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";
}
@ -3403,7 +3422,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;
@ -3421,6 +3442,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;
@ -7654,7 +7681,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"));
@ -7680,7 +7708,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)

View File

@ -3952,7 +3952,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;
@ -3974,6 +3975,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 &&
@ -4139,7 +4147,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;

View File

@ -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

View File

@ -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>

View File

@ -2974,6 +2974,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);