As of v9.4.0-rc2~5 it is possible to specify guest address where a virtio-mem/virtio-pmem memory device is mapped to. What that commit forgot to introduce was a check for overlaps. And yes, this is technically an O(n^2) algorithm, as virDomainMemoryDefValidate() is called over each memory device and after this, virDomainMemoryDefValidate() also iterates over each memory device. But given there's usually only a handful of such devices, and this runs only when parsing domain XML I guess code readability wins over some less obvious solution. Resolves: https://issues.redhat.com/browse/RHEL-4452 Signed-off-by: Michal Privoznik <mprivozn@xxxxxxxxxx> --- src/conf/domain_validate.c | 47 +++++++++++++++++ ...rtio-mem-overlap-address.x86_64-latest.err | 1 + ...ory-hotplug-virtio-mem-overlap-address.xml | 50 +++++++++++++++++++ tests/qemuxml2argvtest.c | 1 + 4 files changed, 99 insertions(+) create mode 100644 tests/qemuxml2argvdata/memory-hotplug-virtio-mem-overlap-address.x86_64-latest.err create mode 100644 tests/qemuxml2argvdata/memory-hotplug-virtio-mem-overlap-address.xml diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c index d14559cd73..6962fe76bf 100644 --- a/src/conf/domain_validate.c +++ b/src/conf/domain_validate.c @@ -2223,6 +2223,9 @@ virDomainMemoryDefValidate(const virDomainMemoryDef *mem, { const long pagesize = virGetSystemPageSize(); unsigned long long thpSize; + unsigned long long thisStart = 0; + unsigned long long thisEnd = 0; + size_t i; /* Guest NUMA nodes are continuous and indexed from zero. */ if (mem->targetNode != -1) { @@ -2304,6 +2307,7 @@ virDomainMemoryDefValidate(const virDomainMemoryDef *mem, pagesize); return -1; } + thisStart = mem->target.virtio_pmem.address; break; case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_MEM: @@ -2347,6 +2351,7 @@ virDomainMemoryDefValidate(const virDomainMemoryDef *mem, _("memory device address must be aligned to blocksize")); return -1; } + thisStart = mem->target.virtio_mem.address; break; case VIR_DOMAIN_MEMORY_MODEL_DIMM: @@ -2368,6 +2373,48 @@ virDomainMemoryDefValidate(const virDomainMemoryDef *mem, return -1; } + if (thisStart == 0) { + return 0; + } + + /* thisStart and thisEnd are in bytes, mem->size in kibibytes */ + thisEnd = thisStart + mem->size * 1024; + + for (i = 0; i < def->nmems; i++) { + const virDomainMemoryDef *other = def->mems[i]; + unsigned long long otherStart = 0; + + if (other == mem) + continue; + + switch (other->model) { + case VIR_DOMAIN_MEMORY_MODEL_NONE: + case VIR_DOMAIN_MEMORY_MODEL_DIMM: + case VIR_DOMAIN_MEMORY_MODEL_NVDIMM: + case VIR_DOMAIN_MEMORY_MODEL_SGX_EPC: + case VIR_DOMAIN_MEMORY_MODEL_LAST: + continue; + break; + + case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_PMEM: + otherStart = other->target.virtio_pmem.address; + break; + case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_MEM: + otherStart = other->target.virtio_mem.address; + break; + } + + if (otherStart == 0) + continue; + + if (thisStart <= otherStart && thisEnd > otherStart) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("memory device address [0x%1$llx:0x%2$llx] overlaps with other memory device (0x%3$llx)"), + thisStart, thisEnd, otherStart); + return -1; + } + } + return 0; } diff --git a/tests/qemuxml2argvdata/memory-hotplug-virtio-mem-overlap-address.x86_64-latest.err b/tests/qemuxml2argvdata/memory-hotplug-virtio-mem-overlap-address.x86_64-latest.err new file mode 100644 index 0000000000..36d5b8a6e6 --- /dev/null +++ b/tests/qemuxml2argvdata/memory-hotplug-virtio-mem-overlap-address.x86_64-latest.err @@ -0,0 +1 @@ +unsupported configuration: memory device address [0x140000000:0x180000000] overlaps with other memory device (0x170000000) diff --git a/tests/qemuxml2argvdata/memory-hotplug-virtio-mem-overlap-address.xml b/tests/qemuxml2argvdata/memory-hotplug-virtio-mem-overlap-address.xml new file mode 100644 index 0000000000..65999ccd99 --- /dev/null +++ b/tests/qemuxml2argvdata/memory-hotplug-virtio-mem-overlap-address.xml @@ -0,0 +1,50 @@ +<domain type='kvm'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <maxMemory 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-x86_64</emulator> + <memory model='virtio-mem'> + <target> + <size unit='KiB'>1048576</size> + <node>0</node> + <block unit='KiB'>2048</block> + <requested unit='KiB'>524288</requested> + <address base='0x140000000'/> + </target> + <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/> + </memory> + <memory model='virtio-mem'> + <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> + <address base='0x170000000'/> + </target> + <address type='pci' domain='0x0000' bus='0x01' slot='0x01' function='0x0'/> + </memory> + </devices> +</domain> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 2195d8efa3..a454dcb205 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -2114,6 +2114,7 @@ mymain(void) DO_TEST_CAPS_LATEST("memory-hotplug-virtio-pmem"); DO_TEST_CAPS_LATEST("memory-hotplug-virtio-mem"); DO_TEST_CAPS_LATEST("memory-hotplug-multiple"); + DO_TEST_CAPS_LATEST_PARSE_ERROR("memory-hotplug-virtio-mem-overlap-address"); DO_TEST_CAPS_ARCH_LATEST("machine-aeskeywrap-on-caps", "s390x"); DO_TEST_CAPS_ARCH_LATEST("machine-aeskeywrap-on-cap", "s390x"); -- 2.41.0