diff --git a/SOURCES/libvirt-cgroup-use-virCgroupSetCpuShares-instead-of-virCgroupSetupCpuShares.patch b/SOURCES/libvirt-cgroup-use-virCgroupSetCpuShares-instead-of-virCgroupSetupCpuShares.patch
new file mode 100644
index 0000000..9eec89b
--- /dev/null
+++ b/SOURCES/libvirt-cgroup-use-virCgroupSetCpuShares-instead-of-virCgroupSetupCpuShares.patch
@@ -0,0 +1,146 @@
+From 8d08db00d403ddd17cb51d972842c6d13a122d57 Mon Sep 17 00:00:00 2001
+Message-Id: <8d08db00d403ddd17cb51d972842c6d13a122d57@dist-git>
+From: Pavel Hrdina Since 7.2.0 QEMU/KVM only
++ When used together with
++ List of mandatory attributes:
++ ESX
and VMWare
hypervisor drivers, however,
+ the i686
arch will always be chosen even on an
+ x86_64
host. Since 0.0.1
++ firmware
firmware
attribute of
++ os
element the type
attribute must
++ have the same value.
++
++
++ type
(accepted values are bios
++ and efi
) same as the firmware
++ attribute of os
element.
++
++ When using firmware auto-selection there are different features
++ enabled in the firmwares. The list of features can be used to
++ limit what firmware should be automatically selected for the VM.
++ The list of features can be specified using zero or more
++ feature
elements. Libvirt will take into consideration
++ only the listed features and ignore the rest when selecting the firmware.
++
++
feature
enabled
(accepted values are yes
++ and no
) is used to tell libvirt if the feature
++ must be enabled or not in the automatically selected firmware
++ name
the name of the feature, the list of the features:
++ enrolled-keys
whether the selected nvram template
++ has default certificate enrolled. Firmware with Secure Boot
++ feature but without enrolled keys will successfully boot
++ non-signed binaries as well. Valid only for firmwares with
++ Secure Boot feature.
++ secure-boot
whether the firmware implements
++ UEFI Secure boot feature.
++ loader
loader
tag refers to a firmware blob,
+ which is specified by absolute path,
+diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
+index 6671ef3dfa..b7f6a6b494 100644
+--- a/docs/schemas/domaincommon.rng
++++ b/docs/schemas/domaincommon.rng
+@@ -268,6 +268,29 @@
+
+
+
++ firmware
Since 7.2.0 QEMU/KVM only
+-
+- When used together with firmware
attribute of
+- os
element the type
attribute must
+- have the same value.
+-
+- List of mandatory attributes: +-
type
(accepted values are bios
+- and efi
) same as the firmware
+- attribute of os
element.
+-
+ When using firmware auto-selection there are different features
+ enabled in the firmwares. The list of features can be used to
+diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
+index b7f6a6b494..ec8167e588 100644
+--- a/docs/schemas/domaincommon.rng
++++ b/docs/schemas/domaincommon.rng
+@@ -270,13 +270,7 @@
+
+ type
should be
+ pflash
. Moreover, some firmwares may
+ implement the Secure boot feature. Attribute
+- secure
can be used then to control it.
++ secure
can be used to tell the hypervisor that the
++ firmware is capable of Secure Boot feature. It cannot be used to
++ enable or disable the feature itself in the firmware.
+ Since 2.1.0
nvram
quota
as any
+ negative value indicates that the domain has infinite bandwidth for
+ vCPU threads, which means that it is not bandwidth controlled. The value
+- should be in range [1000, 18446744073709551] or less than 0. A quota
++ should be in range [1000, 17592186044415] or less than 0. A quota
+ with value 0 means no value. You can use this feature to ensure that all
+ vCPUs run at the same speed.
+ Only QEMU driver support since 0.9.4, LXC since
+@@ -894,7 +894,7 @@
+ domain. A domain with global_quota
as any negative
+ value indicates that the domain has infinite bandwidth, which means that
+ it is not bandwidth controlled. The value should be in range
+- [1000, 18446744073709551] or less than 0. A global_quota
++ [1000, 17592186044415] or less than 0. A global_quota
+ with value 0 means no value.
+ Only QEMU driver support since 1.3.3
+ emulator_quota
as any negative
+ value indicates that the domain has infinite bandwidth for emulator threads
+ (those excluding vCPUs), which means that it is not bandwidth controlled.
+- The value should be in range [1000, 18446744073709551] or less than 0. A
++ The value should be in range [1000, 17592186044415] or less than 0. A
+ quota with value 0 means no value.
+ Only QEMU driver support since 0.10.0
+ iothread_quota
as any negative value indicates that the
+ domain IOThreads have infinite bandwidth, which means that it is
+ not bandwidth controlled. The value should be in range
+- [1000, 18446744073709551] or less than 0. An iothread_quota
++ [1000, 17592186044415] or less than 0. An iothread_quota
+ with value 0 means no value. You can use this feature to ensure that
+ all IOThreads run at the same speed.
+ Only QEMU driver support since 2.1.0
+diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst
+index 0804465d44..a5b95c1123 100644
+--- a/docs/manpages/virsh.rst
++++ b/docs/manpages/virsh.rst
+@@ -3715,7 +3715,7 @@ XEN_CREDIT scheduler.
+ ``Note``: The vcpu_period, emulator_period, and iothread_period parameters
+ have a valid value range of 1000-1000000 or 0, and the vcpu_quota,
+ emulator_quota, and iothread_quota parameters have a valid value range of
+-1000-18446744073709551 or less than 0. The value 0 for
++1000-17592186044415 or less than 0. The value 0 for
+ either parameter is the same as not specifying that parameter.
+
+
+diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
+index 4a42cb9b40..6671ef3dfa 100644
+--- a/docs/schemas/domaincommon.rng
++++ b/docs/schemas/domaincommon.rng
+@@ -6649,7 +6649,7 @@
+ auto
is set to yes
, libvirt
+ will assign a free CID automatically on domain startup.
+- Since 4.4.0
++ Since 4.4.0
++ The optional driver
element allows to specify virtio options, see
++ Virtio-specific options for more details.
++ Since 7.1.0
+
+ + ... +diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng +index 9fda5f17e0..4a42cb9b40 100644 +--- a/docs/schemas/domaincommon.rng ++++ b/docs/schemas/domaincommon.rng +@@ -4685,6 +4685,11 @@ ++ +++ + ++++ + + + +diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c +index c5a0442c6f..166c3e48d2 100644 +--- a/src/conf/domain_conf.c ++++ b/src/conf/domain_conf.c +@@ -2392,6 +2392,7 @@ virDomainVsockDefFree(virDomainVsockDefPtr vsock) + + virObjectUnref(vsock->privateData); + virDomainDeviceInfoClear(&vsock->info); ++ VIR_FREE(vsock->virtio); + VIR_FREE(vsock); + } + +@@ -6504,6 +6505,15 @@ virDomainMemoryDefValidate(const virDomainMemoryDef *mem) + } + + ++static bool ++virDomainVsockIsVirtioModel(const virDomainVsockDef *vsock) ++{ ++ return (vsock->model == VIR_DOMAIN_VSOCK_MODEL_VIRTIO || ++ vsock->model == VIR_DOMAIN_VSOCK_MODEL_VIRTIO_TRANSITIONAL || ++ vsock->model == VIR_DOMAIN_VSOCK_MODEL_VIRTIO_NON_TRANSITIONAL); ++} ++ ++ + static int + virDomainVsockDefValidate(const virDomainVsockDef *vsock) + { +@@ -6513,6 +6523,10 @@ virDomainVsockDefValidate(const virDomainVsockDef *vsock) + return -1; + } + ++ if (!virDomainVsockIsVirtioModel(vsock) && ++ virDomainCheckVirtioOptions(vsock->virtio) < 0) ++ return -1; ++ + return 0; + } + +@@ -16649,6 +16663,11 @@ virDomainVsockDefParseXML(virDomainXMLOptionPtr xmlopt, + if (virDomainDeviceInfoParseXML(xmlopt, node, &vsock->info, flags) < 0) + return NULL; + ++ if (virDomainVirtioOptionsParseXML(virXPathNode("./driver", ctxt), ++ &vsock->virtio) < 0) ++ return NULL; ++ ++ + return g_steal_pointer(&vsock); + } + +@@ -23350,6 +23369,10 @@ virDomainVsockDefCheckABIStability(virDomainVsockDefPtr src, + return false; + } + ++ if (src->virtio && dst->virtio && ++ !virDomainVirtioOptionsCheckABIStability(src->virtio, dst->virtio)) ++ return false; ++ + if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) + return false; + +@@ -28364,6 +28387,7 @@ virDomainVsockDefFormat(virBufferPtr buf, + g_auto(virBuffer) childBuf = VIR_BUFFER_INIT_CHILD(buf); + g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER; + g_auto(virBuffer) cidAttrBuf = VIR_BUFFER_INITIALIZER; ++ g_auto(virBuffer) drvAttrBuf = VIR_BUFFER_INITIALIZER; + + if (vsock->model) { + virBufferAsprintf(&attrBuf, " model='%s'", +@@ -28381,6 +28405,9 @@ virDomainVsockDefFormat(virBufferPtr buf, + if (virDomainDeviceInfoFormat(&childBuf, &vsock->info, 0) < 0) + return -1; + ++ virDomainVirtioOptionsFormat(&drvAttrBuf, vsock->virtio); ++ ++ virXMLFormatElement(&childBuf, "driver", &drvAttrBuf, NULL); + virXMLFormatElement(buf, "vsock", &attrBuf, &childBuf); + + return 0; +diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h +index 118077edaa..3aed1fb22a 100644 +--- a/src/conf/domain_conf.h ++++ b/src/conf/domain_conf.h +@@ -2389,6 +2389,7 @@ struct _virDomainVsockDef { + virTristateBool auto_cid; + + virDomainDeviceInfo info; ++ virDomainVirtioOptionsPtr virtio; + }; + + struct _virDomainVirtioOptions { +diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c +index 67d7334b0f..998c3c90f8 100644 +--- a/src/qemu/qemu_command.c ++++ b/src/qemu/qemu_command.c +@@ -9965,6 +9965,10 @@ qemuBuildVsockDevStr(virDomainDefPtr def, + virBufferAsprintf(&buf, ",id=%s", vsock->info.alias); + virBufferAsprintf(&buf, ",guest-cid=%u", vsock->guest_cid); + virBufferAsprintf(&buf, ",vhostfd=%s%u", fdprefix, priv->vhostfd); ++ ++ if (qemuBuildVirtioOptionsStr(&buf, vsock->virtio, qemuCaps) < 0) ++ return NULL; ++ + if (qemuBuildDeviceAddressStr(&buf, def, &vsock->info, qemuCaps) < 0) + return NULL; + +diff --git a/tests/qemuxml2argvdata/vhost-vsock-ccw-iommu.s390x-latest.args b/tests/qemuxml2argvdata/vhost-vsock-ccw-iommu.s390x-latest.args +new file mode 100644 +index 0000000000..78eede78d3 +--- /dev/null ++++ b/tests/qemuxml2argvdata/vhost-vsock-ccw-iommu.s390x-latest.args +@@ -0,0 +1,37 @@ ++LC_ALL=C \ ++PATH=/bin \ ++HOME=/tmp/lib/domain--1-QEMUGuest1 \ ++USER=test \ ++LOGNAME=test \ ++XDG_DATA_HOME=/tmp/lib/domain--1-QEMUGuest1/.local/share \ ++XDG_CACHE_HOME=/tmp/lib/domain--1-QEMUGuest1/.cache \ ++XDG_CONFIG_HOME=/tmp/lib/domain--1-QEMUGuest1/.config \ ++QEMU_AUDIO_DRV=none \ ++/usr/bin/qemu-system-s390x \ ++-name guest=QEMUGuest1,debug-threads=on \ ++-S \ ++-object secret,id=masterKey0,format=raw,\ ++file=/tmp/lib/domain--1-QEMUGuest1/master-key.aes \ ++-machine s390-ccw-virtio,accel=tcg,usb=off,dump-guest-core=off \ ++-cpu qemu \ ++-m 214 \ ++-overcommit mem-lock=off \ ++-smp 1,sockets=1,cores=1,threads=1 \ ++-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \ ++-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 \ ++-boot strict=on \ ++-drive file=/dev/HostVG/QEMUGuest1,format=raw,if=none,id=drive-virtio-disk0 \ ++-device virtio-blk-ccw,scsi=off,devno=fe.0.0000,drive=drive-virtio-disk0,\ ++id=virtio-disk0,bootindex=1 \ ++-device virtio-balloon-ccw,id=balloon0,devno=fe.0.0001 \ ++-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,\ ++resourcecontrol=deny \ ++-device vhost-vsock-ccw,id=vsock0,guest-cid=4,vhostfd=6789,iommu_platform=on,\ ++devno=fe.0.0002 \ ++-msg timestamp=on +diff --git a/tests/qemuxml2argvdata/vhost-vsock-ccw-iommu.xml b/tests/qemuxml2argvdata/vhost-vsock-ccw-iommu.xml +new file mode 100644 +index 0000000000..dbfe082a6f +--- /dev/null ++++ b/tests/qemuxml2argvdata/vhost-vsock-ccw-iommu.xml +@@ -0,0 +1,37 @@ ++++ ++ ++++ +diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c +index 629f5ac100..a22e3ba157 100644 +--- a/tests/qemuxml2argvtest.c ++++ b/tests/qemuxml2argvtest.c +@@ -3056,6 +3056,7 @@ mymain(void) + DO_TEST_CAPS_LATEST("vhost-vsock-auto"); + DO_TEST_CAPS_ARCH_LATEST("vhost-vsock-ccw", "s390x"); + DO_TEST_CAPS_ARCH_LATEST("vhost-vsock-ccw-auto", "s390x"); ++ DO_TEST_CAPS_ARCH_LATEST("vhost-vsock-ccw-iommu", "s390x"); + + DO_TEST_CAPS_VER("launch-security-sev", "2.12.0"); + +diff --git a/tests/qemuxml2xmloutdata/vhost-vsock-ccw-iommu.s390x-latest.xml b/tests/qemuxml2xmloutdata/vhost-vsock-ccw-iommu.s390x-latest.xml +new file mode 120000 +index 0000000000..78971a8ef9 +--- /dev/null ++++ b/tests/qemuxml2xmloutdata/vhost-vsock-ccw-iommu.s390x-latest.xml +@@ -0,0 +1 @@ ++../qemuxml2argvdata/vhost-vsock-ccw-iommu.xml +\ No newline at end of file +diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c +index 60efcac6c8..461b5bc68f 100644 +--- a/tests/qemuxml2xmltest.c ++++ b/tests/qemuxml2xmltest.c +@@ -1433,6 +1433,8 @@ mymain(void) + QEMU_CAPS_CCW); + DO_TEST("vhost-vsock-ccw-auto", QEMU_CAPS_DEVICE_VHOST_VSOCK, + QEMU_CAPS_CCW); ++ DO_TEST_CAPS_ARCH_LATEST("vhost-vsock-ccw-iommu", "s390x"); ++ + + DO_TEST_CAPS_LATEST("vhost-user-fs-fd-memory"); + DO_TEST_CAPS_LATEST("vhost-user-fs-hugepages"); +-- +2.30.0 + diff --git a/SOURCES/libvirt-qemu-implement-support-for-firmware-auto-selection-feature-filtering.patch b/SOURCES/libvirt-qemu-implement-support-for-firmware-auto-selection-feature-filtering.patch new file mode 100644 index 0000000..f780cd6 --- /dev/null +++ b/SOURCES/libvirt-qemu-implement-support-for-firmware-auto-selection-feature-filtering.patch @@ -0,0 +1,248 @@ +From d1c5d166a891a2abf408a5879b95bded23b45825 Mon Sep 17 00:00:00 2001 +Message-Id:QEMUGuest1 ++c7a5fdbd-edaf-9455-926a-d65c16db1809 ++219136 ++219136 ++1 ++++ ++hvm ++++ ++ ++qemu ++++ destroy ++restart ++destroy ++++ ++/usr/bin/qemu-system-s390x ++++ ++++ ++ ++ ++ ++ ++ ++ ++++ ++ ++++ ++ ++ +From: Pavel Hrdina +Date: Fri, 21 May 2021 14:16:12 +0200 +Subject: [PATCH] qemu: implement support for firmware auto-selection feature + filtering + +Signed-off-by: Pavel Hrdina +Reviewed-by: Michal Privoznik +(cherry picked from commit c91fa273062ec388385bf8cc081117c78c2f7af5) + +Conflicts: + tests/qemuxml2argvdata/os-firmware-efi-no-enrolled-keys.x86_64-latest.args + - missing upstream commits: + d96fb5cb31b870e1539bd8ee95fb27dbe461a357 + 43c9c0859f2d53321ccc646ab905beec0740490b + 88957116c9d3cb4705380c3702c9d4315fb500bb + + tests/qemuxml2argvdata/os-firmware-efi-no-enrolled-keys.xml + - missing upstream commits: + e88367095f3cad2cf80a687fd599dfaeb3073841 + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1929357 + +Signed-off-by: Pavel Hrdina +Message-Id: +Reviewed-by: Michal Privoznik +--- + src/qemu/qemu_firmware.c | 40 +++++++++++++++ + ...re-efi-no-enrolled-keys.x86_64-latest.args | 47 ++++++++++++++++++ + .../os-firmware-efi-no-enrolled-keys.xml | 49 +++++++++++++++++++ + tests/qemuxml2argvtest.c | 1 + + ...are-efi-no-enrolled-keys.x86_64-latest.xml | 1 + + tests/qemuxml2xmltest.c | 1 + + 6 files changed, 139 insertions(+) + create mode 100644 tests/qemuxml2argvdata/os-firmware-efi-no-enrolled-keys.x86_64-latest.args + create mode 100644 tests/qemuxml2argvdata/os-firmware-efi-no-enrolled-keys.xml + create mode 120000 tests/qemuxml2xmloutdata/os-firmware-efi-no-enrolled-keys.x86_64-latest.xml + +diff --git a/src/qemu/qemu_firmware.c b/src/qemu/qemu_firmware.c +index 8ef515ca57..e875e355c7 100644 +--- a/src/qemu/qemu_firmware.c ++++ b/src/qemu/qemu_firmware.c +@@ -952,6 +952,10 @@ qemuFirmwareMatchDomain(const virDomainDef *def, + bool supportsS4 = false; + bool requiresSMM = false; + bool supportsSEV = false; ++ bool supportsSecureBoot = false; ++ bool hasEnrolledKeys = false; ++ int reqSecureBoot; ++ int reqEnrolledKeys; + + want = qemuFirmwareOSInterfaceTypeFromOsDefFirmware(def->os.firmware); + +@@ -1001,7 +1005,13 @@ qemuFirmwareMatchDomain(const virDomainDef *def, + break; + + case QEMU_FIRMWARE_FEATURE_SECURE_BOOT: ++ supportsSecureBoot = true; ++ break; ++ + case QEMU_FIRMWARE_FEATURE_ENROLLED_KEYS: ++ hasEnrolledKeys = true; ++ break; ++ + case QEMU_FIRMWARE_FEATURE_VERBOSE_DYNAMIC: + case QEMU_FIRMWARE_FEATURE_VERBOSE_STATIC: + case QEMU_FIRMWARE_FEATURE_NONE: +@@ -1022,6 +1032,36 @@ qemuFirmwareMatchDomain(const virDomainDef *def, + return false; + } + ++ if (def->os.firmwareFeatures) { ++ reqSecureBoot = def->os.firmwareFeatures[VIR_DOMAIN_OS_DEF_FIRMWARE_FEATURE_SECURE_BOOT]; ++ if (reqSecureBoot != VIR_TRISTATE_BOOL_ABSENT) { ++ if (reqSecureBoot == VIR_TRISTATE_BOOL_YES && !supportsSecureBoot) { ++ VIR_DEBUG("User requested Secure Boot, firmware '%s' doesn't support it", ++ path); ++ return false; ++ } ++ ++ if (reqSecureBoot == VIR_TRISTATE_BOOL_NO && supportsSecureBoot) { ++ VIR_DEBUG("User refused Secure Boot, firmware '%s' supports it", path); ++ return false; ++ } ++ } ++ ++ reqEnrolledKeys = def->os.firmwareFeatures[VIR_DOMAIN_OS_DEF_FIRMWARE_FEATURE_ENROLLED_KEYS]; ++ if (reqEnrolledKeys != VIR_TRISTATE_BOOL_ABSENT) { ++ if (reqEnrolledKeys == VIR_TRISTATE_BOOL_YES && !hasEnrolledKeys) { ++ VIR_DEBUG("User requested Enrolled keys, firmware '%s' doesn't have them", ++ path); ++ return false; ++ } ++ ++ if (reqEnrolledKeys == VIR_TRISTATE_BOOL_NO && hasEnrolledKeys) { ++ VIR_DEBUG("User refused Enrolled keys, firmware '%s' has them", path); ++ return false; ++ } ++ } ++ } ++ + if (def->os.loader && + def->os.loader->secure == VIR_TRISTATE_BOOL_YES && + !requiresSMM) { +diff --git a/tests/qemuxml2argvdata/os-firmware-efi-no-enrolled-keys.x86_64-latest.args b/tests/qemuxml2argvdata/os-firmware-efi-no-enrolled-keys.x86_64-latest.args +new file mode 100644 +index 0000000000..c3c838fb1a +--- /dev/null ++++ b/tests/qemuxml2argvdata/os-firmware-efi-no-enrolled-keys.x86_64-latest.args +@@ -0,0 +1,47 @@ ++LC_ALL=C \ ++PATH=/bin \ ++HOME=/tmp/lib/domain--1-fedora \ ++USER=test \ ++LOGNAME=test \ ++XDG_DATA_HOME=/tmp/lib/domain--1-fedora/.local/share \ ++XDG_CACHE_HOME=/tmp/lib/domain--1-fedora/.cache \ ++XDG_CONFIG_HOME=/tmp/lib/domain--1-fedora/.config \ ++QEMU_AUDIO_DRV=none \ ++/usr/bin/qemu-system-x86_64 \ ++-name guest=fedora,debug-threads=on \ ++-S \ ++-object secret,id=masterKey0,format=raw,\ ++file=/tmp/lib/domain--1-fedora/master-key.aes \ ++-blockdev '{"driver":"file","filename":"/usr/share/OVMF/OVMF_CODE.fd",\ ++"node-name":"libvirt-pflash0-storage","auto-read-only":true,\ ++"discard":"unmap"}' \ ++-blockdev '{"node-name":"libvirt-pflash0-format","read-only":true,\ ++"driver":"raw","file":"libvirt-pflash0-storage"}' \ ++-blockdev '{"driver":"file",\ ++"filename":"/var/lib/libvirt/qemu/nvram/fedora_VARS.fd",\ ++"node-name":"libvirt-pflash1-storage","auto-read-only":true,\ ++"discard":"unmap"}' \ ++-blockdev '{"node-name":"libvirt-pflash1-format","read-only":false,\ ++"driver":"raw","file":"libvirt-pflash1-storage"}' \ ++-machine pc-q35-4.0,accel=kvm,usb=off,dump-guest-core=off,\ ++pflash0=libvirt-pflash0-format,pflash1=libvirt-pflash1-format \ ++-cpu qemu64 \ ++-m 8 \ ++-overcommit mem-lock=off \ ++-smp 1,sockets=1,cores=1,threads=1 \ ++-uuid 63840878-0deb-4095-97e6-fc444d9bc9fa \ ++-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 \ ++-boot strict=on \ ++-device pcie-root-port,port=0x8,chassis=1,id=pci.1,bus=pcie.0,multifunction=on,\ ++addr=0x1 \ ++-device pcie-root-port,port=0x9,chassis=2,id=pci.2,bus=pcie.0,addr=0x1.0x1 \ ++-device qemu-xhci,id=usb,bus=pci.1,addr=0x0 \ ++-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,\ ++resourcecontrol=deny \ ++-msg timestamp=on +diff --git a/tests/qemuxml2argvdata/os-firmware-efi-no-enrolled-keys.xml b/tests/qemuxml2argvdata/os-firmware-efi-no-enrolled-keys.xml +new file mode 100644 +index 0000000000..7f8f57a859 +--- /dev/null ++++ b/tests/qemuxml2argvdata/os-firmware-efi-no-enrolled-keys.xml +@@ -0,0 +1,49 @@ ++ ++ +diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c +index bc04bea692..5e16d7fd31 100644 +--- a/tests/qemuxml2argvtest.c ++++ b/tests/qemuxml2argvtest.c +@@ -3094,6 +3094,7 @@ mymain(void) + DO_TEST_CAPS_LATEST("os-firmware-bios"); + DO_TEST_CAPS_LATEST("os-firmware-efi"); + DO_TEST_CAPS_LATEST("os-firmware-efi-secboot"); ++ DO_TEST_CAPS_LATEST("os-firmware-efi-no-enrolled-keys"); + DO_TEST_CAPS_LATEST_PARSE_ERROR("os-firmware-invalid-type"); + DO_TEST_CAPS_ARCH_LATEST("aarch64-os-firmware-efi", "aarch64"); + +diff --git a/tests/qemuxml2xmloutdata/os-firmware-efi-no-enrolled-keys.x86_64-latest.xml b/tests/qemuxml2xmloutdata/os-firmware-efi-no-enrolled-keys.x86_64-latest.xml +new file mode 120000 +index 0000000000..902ccb783b +--- /dev/null ++++ b/tests/qemuxml2xmloutdata/os-firmware-efi-no-enrolled-keys.x86_64-latest.xml +@@ -0,0 +1 @@ ++../qemuxml2argvdata/os-firmware-efi-no-enrolled-keys.xml +\ No newline at end of file +diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c +index 461b5bc68f..9e5747290a 100644 +--- a/tests/qemuxml2xmltest.c ++++ b/tests/qemuxml2xmltest.c +@@ -1122,6 +1122,7 @@ mymain(void) + DO_TEST_CAPS_LATEST("os-firmware-bios"); + DO_TEST_CAPS_LATEST("os-firmware-efi"); + DO_TEST_CAPS_LATEST("os-firmware-efi-secboot"); ++ DO_TEST_CAPS_LATEST("os-firmware-efi-no-enrolled-keys"); + + DO_TEST("aarch64-aavmf-virtio-mmio", + QEMU_CAPS_DEVICE_VIRTIO_MMIO, +-- +2.31.1 + diff --git a/SOURCES/libvirt-qemu_firmware-don-t-error-out-for-unknown-firmware-features.patch b/SOURCES/libvirt-qemu_firmware-don-t-error-out-for-unknown-firmware-features.patch new file mode 100644 index 0000000..6cab919 --- /dev/null +++ b/SOURCES/libvirt-qemu_firmware-don-t-error-out-for-unknown-firmware-features.patch @@ -0,0 +1,68 @@ +From c8ede44db2e94444e5a8ee38e21eda2b42717879 Mon Sep 17 00:00:00 2001 +Message-Id:fedora ++63840878-0deb-4095-97e6-fc444d9bc9fa ++8192 ++8192 ++1 ++++ ++hvm ++++ ++++ ++ ++ ++++ ++ ++ ++ ++qemu64 ++++ destroy ++restart ++destroy ++++ ++/usr/bin/qemu-system-x86_64 ++++ ++ ++++ ++ ++ ++ ++++ ++ ++ ++ ++ ++++ ++ ++ ++ ++++ +From: Pavel Hrdina +Date: Tue, 18 May 2021 15:03:02 +0200 +Subject: [PATCH] qemu_firmware: don't error out for unknown firmware features +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +When QEMU introduces new firmware features libvirt will fail until we +list that feature in our code as well which doesn't sound right. + +We should simply ignore the new feature until we add a proper support +for it. + +Reported-by: Laszlo Ersek +Signed-off-by: Pavel Hrdina +Reviewed-by: Peter Krempa +Reviewed-by: Daniel P. Berrangé +(cherry picked from commit 61d95a1073833ec4323c1ef28e71e913c55aa7b9) + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1961562 + +Signed-off-by: Pavel Hrdina +Message-Id: <8989d70d49d8a720532a8c25e3e73d9b3bf2a495.1621342722.git.phrdina@redhat.com> +Reviewed-by: Michal Privoznik +--- + src/qemu/qemu_firmware.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/src/qemu/qemu_firmware.c b/src/qemu/qemu_firmware.c +index c84d03f0a8..8ef515ca57 100644 +--- a/src/qemu/qemu_firmware.c ++++ b/src/qemu/qemu_firmware.c +@@ -570,6 +570,7 @@ qemuFirmwareFeatureParse(const char *path, + virJSONValuePtr featuresJSON; + g_autoptr(qemuFirmwareFeature) features = NULL; + size_t nfeatures; ++ size_t nparsed = 0; + size_t i; + + if (!(featuresJSON = virJSONValueObjectGetArray(doc, "features"))) { +@@ -590,17 +591,16 @@ qemuFirmwareFeatureParse(const char *path, + int tmp; + + if ((tmp = qemuFirmwareFeatureTypeFromString(tmpStr)) <= 0) { +- virReportError(VIR_ERR_INTERNAL_ERROR, +- _("unknown feature %s"), +- tmpStr); +- return -1; ++ VIR_DEBUG("ignoring unknown QEMU firmware feature '%s'", tmpStr); ++ continue; + } + +- features[i] = tmp; ++ features[nparsed] = tmp; ++ nparsed++; + } + + fw->features = g_steal_pointer(&features); +- fw->nfeatures = nfeatures; ++ fw->nfeatures = nparsed; + return 0; + } + +-- +2.31.1 + diff --git a/SOURCES/libvirt-security-fix-SELinux-label-generation-logic.patch b/SOURCES/libvirt-security-fix-SELinux-label-generation-logic.patch new file mode 100644 index 0000000..d32275b --- /dev/null +++ b/SOURCES/libvirt-security-fix-SELinux-label-generation-logic.patch @@ -0,0 +1,58 @@ +From 0f7c8a271f07b3f9aff07dd814d7bec80ddac362 Mon Sep 17 00:00:00 2001 +Message-Id: <0f7c8a271f07b3f9aff07dd814d7bec80ddac362@dist-git> +From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= +Date: Wed, 28 Jul 2021 14:59:00 +0200 +Subject: [PATCH] security: fix SELinux label generation logic +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +A process can access a file if the set of MCS categories +for the file is equal-to *or* a subset-of, the set of +MCS categories for the process. + +If there are two VMs: + + a) svirt_t:s0:c117 + b) svirt_t:s0:c117,c720 + +Then VM (b) is able to access files labelled for VM (a). + +IOW, we must discard case where the categories are equal +because that is a subset of many other valid category pairs. + +Fixes: https://gitlab.com/libvirt/libvirt/-/issues/153 +CVE-2021-3631 +Reviewed-by: Peter Krempa +Signed-off-by: Daniel P. Berrangé +(cherry picked from commit 15073504dbb624d3f6c911e85557019d3620fdb2) +Message-Id: <38c6a7b570b8eb2114d9f1ff0c84a8346e01472f.1627476632.git.pkrempa@redhat.com> +Reviewed-by: Ján Tomko +--- + src/security/security_selinux.c | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c +index 985c7eda1a..93fae831ca 100644 +--- a/src/security/security_selinux.c ++++ b/src/security/security_selinux.c +@@ -391,7 +391,15 @@ virSecuritySELinuxMCSFind(virSecurityManagerPtr mgr, + VIR_DEBUG("Try cat %s:c%d,c%d", sens, c1 + catMin, c2 + catMin); + + if (c1 == c2) { +- mcs = g_strdup_printf("%s:c%d", sens, catMin + c1); ++ /* ++ * A process can access a file if the set of MCS categories ++ * for the file is equal-to *or* a subset-of, the set of ++ * MCS categories for the process. ++ * ++ * IOW, we must discard case where the categories are equal ++ * because that is a subset of other category pairs. ++ */ ++ continue; + } else { + if (c1 > c2) { + int t = c1; +-- +2.32.0 + diff --git a/SOURCES/libvirt-storage_driver-Unlock-object-on-ACL-fail-in-storagePoolLookupByTargetPath.patch b/SOURCES/libvirt-storage_driver-Unlock-object-on-ACL-fail-in-storagePoolLookupByTargetPath.patch new file mode 100644 index 0000000..370de09 --- /dev/null +++ b/SOURCES/libvirt-storage_driver-Unlock-object-on-ACL-fail-in-storagePoolLookupByTargetPath.patch @@ -0,0 +1,44 @@ +From b794a0e4e657defe9a491eb20adf61eafa443ca3 Mon Sep 17 00:00:00 2001 +Message-Id: +From: Peter Krempa +Date: Wed, 28 Jul 2021 14:59:01 +0200 +Subject: [PATCH] storage_driver: Unlock object on ACL fail in + storagePoolLookupByTargetPath +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +'virStoragePoolObjListSearch' returns a locked and refed object, thus we +must release it on ACL permission failure. + +Fixes: 7aa0e8c0cb8 +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1984318 +Signed-off-by: Peter Krempa +Reviewed-by: Michal Privoznik +(cherry picked from commit 447f69dec47e1b0bd15ecd7cd49a9fd3b050fb87) +CVE-2021-3667 +Message-Id: +Reviewed-by: Ján Tomko +--- + src/storage/storage_driver.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c +index 0bb116cf08..4f0b8c1218 100644 +--- a/src/storage/storage_driver.c ++++ b/src/storage/storage_driver.c +@@ -1733,8 +1733,10 @@ storagePoolLookupByTargetPath(virConnectPtr conn, + storagePoolLookupByTargetPathCallback, + cleanpath))) { + def = virStoragePoolObjGetDef(obj); +- if (virStoragePoolLookupByTargetPathEnsureACL(conn, def) < 0) ++ if (virStoragePoolLookupByTargetPathEnsureACL(conn, def) < 0) { ++ virStoragePoolObjEndAPI(&obj); + return NULL; ++ } + + pool = virGetStoragePool(conn, def->name, def->uuid, NULL, NULL); + virStoragePoolObjEndAPI(&obj); +-- +2.32.0 + diff --git a/SOURCES/libvirt-tests-add-cgroup-nested-tests.patch b/SOURCES/libvirt-tests-add-cgroup-nested-tests.patch new file mode 100644 index 0000000..300ab47 --- /dev/null +++ b/SOURCES/libvirt-tests-add-cgroup-nested-tests.patch @@ -0,0 +1,226 @@ +From c94691d796682d951ffa8fb3a4fcb985aae17d9b Mon Sep 17 00:00:00 2001 +Message-Id: +From: Pavel Hrdina +Date: Fri, 19 Feb 2021 13:34:00 +0100 +Subject: [PATCH] tests: add cgroup nested tests +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Pavel Hrdina +Reviewed-by: Michal Privoznik +(cherry picked from commit 85099c339346e41f457234e8ad831841aef1d5e3) + +Conflicts: + tests/vircgrouptest.c + - missing upstream g_autofree rewrite + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1798463 + +Signed-off-by: Pavel Hrdina +Message-Id: +Reviewed-by: Ján Tomko +--- + tests/vircgroupdata/systemd-legacy.cgroups | 12 +++ + tests/vircgroupdata/systemd-legacy.mounts | 11 +++ + .../vircgroupdata/systemd-legacy.self.cgroup | 11 +++ + tests/vircgroupdata/systemd-unified.cgroups | 13 +++ + tests/vircgroupdata/systemd-unified.mounts | 1 + + .../vircgroupdata/systemd-unified.self.cgroup | 1 + + tests/vircgrouptest.c | 82 +++++++++++++++++++ + 7 files changed, 131 insertions(+) + create mode 100644 tests/vircgroupdata/systemd-legacy.cgroups + create mode 100644 tests/vircgroupdata/systemd-legacy.mounts + create mode 100644 tests/vircgroupdata/systemd-legacy.self.cgroup + create mode 100644 tests/vircgroupdata/systemd-unified.cgroups + create mode 100644 tests/vircgroupdata/systemd-unified.mounts + create mode 100644 tests/vircgroupdata/systemd-unified.self.cgroup + +diff --git a/tests/vircgroupdata/systemd-legacy.cgroups b/tests/vircgroupdata/systemd-legacy.cgroups +new file mode 100644 +index 0000000000..444354e3c8 +--- /dev/null ++++ b/tests/vircgroupdata/systemd-legacy.cgroups +@@ -0,0 +1,12 @@ ++#subsys_name hierarchy num_cgroups enabled ++blkio 1 1 1 ++cpu 2 1 1 ++cpuacct 3 1 1 ++cpuset 4 1 1 ++devices 5 1 1 ++freezer 6 1 1 ++hugetlb 7 1 1 ++memory 8 1 1 ++net_cls 9 1 1 ++perf_event 10 1 1 ++pids 11 1 1 +diff --git a/tests/vircgroupdata/systemd-legacy.mounts b/tests/vircgroupdata/systemd-legacy.mounts +new file mode 100644 +index 0000000000..23462e9e68 +--- /dev/null ++++ b/tests/vircgroupdata/systemd-legacy.mounts +@@ -0,0 +1,11 @@ ++cgroup /not/really/sys/fs/cgroup/blkio cgroup rw,seclabel,nosuid,nodev,noexec,relatime,blkio 0 0 ++cgroup /not/really/sys/fs/cgroup/cpu cgroup rw,seclabel,nosuid,nodev,noexec,relatime,cpu 0 0 ++cgroup /not/really/sys/fs/cgroup/cpuacct cgroup rw,seclabel,nosuid,nodev,noexec,relatime,cpuacct 0 0 ++cgroup /not/really/sys/fs/cgroup/cpuset cgroup rw,seclabel,nosuid,nodev,noexec,relatime,cpuset 0 0 ++cgroup /not/really/sys/fs/cgroup/devices cgroup rw,seclabel,nosuid,nodev,noexec,relatime,devices 0 0 ++cgroup /not/really/sys/fs/cgroup/freezer cgroup rw,seclabel,nosuid,nodev,noexec,relatime,freezer 0 0 ++cgroup /not/really/sys/fs/cgroup/hugetlb cgroup rw,seclabel,nosuid,nodev,noexec,relatime,hugetlb 0 0 ++cgroup /not/really/sys/fs/cgroup/memory cgroup rw,seclabel,nosuid,nodev,noexec,relatime,memory 0 0 ++cgroup /not/really/sys/fs/cgroup/net_cls cgroup rw,seclabel,nosuid,nodev,noexec,relatime,net_cls 0 0 ++cgroup /not/really/sys/fs/cgroup/perf_event cgroup rw,seclabel,nosuid,nodev,noexec,relatime,perf_event 0 0 ++cgroup /not/really/sys/fs/cgroup/pids cgroup rw,seclabel,nosuid,nodev,noexec,relatime,pids 0 0 +diff --git a/tests/vircgroupdata/systemd-legacy.self.cgroup b/tests/vircgroupdata/systemd-legacy.self.cgroup +new file mode 100644 +index 0000000000..5c133a3c08 +--- /dev/null ++++ b/tests/vircgroupdata/systemd-legacy.self.cgroup +@@ -0,0 +1,11 @@ ++1:blkio:/libvirt ++2:cpu:/libvirt/emulator ++3:cpuacct:/libvirt/emulator ++4:cpuset:/libvirt/emulator ++5:devices:/libvirt ++6:freezer:/libvirt ++7:hugetlb:/ ++8:memory:/libvirt ++9:net_cls:/libvirt ++10:perf_event:/libvirt ++11:pids:/ +diff --git a/tests/vircgroupdata/systemd-unified.cgroups b/tests/vircgroupdata/systemd-unified.cgroups +new file mode 100644 +index 0000000000..e0d8a3561c +--- /dev/null ++++ b/tests/vircgroupdata/systemd-unified.cgroups +@@ -0,0 +1,13 @@ ++#subsys_name hierarchy num_cgroups enabled ++cpuset 0 1 1 ++cpu 0 1 1 ++cpuacct 0 1 1 ++blkio 0 1 1 ++memory 0 1 1 ++devices 0 1 1 ++freezer 0 1 1 ++net_cls 0 1 1 ++perf_event 0 1 1 ++net_prio 0 1 1 ++hugetlb 0 1 1 ++pids 0 1 1 +diff --git a/tests/vircgroupdata/systemd-unified.mounts b/tests/vircgroupdata/systemd-unified.mounts +new file mode 100644 +index 0000000000..8225f37f45 +--- /dev/null ++++ b/tests/vircgroupdata/systemd-unified.mounts +@@ -0,0 +1 @@ ++cgroup2 /not/really/sys/fs/cgroup cgroup2 rw,seclabel,nosuid,nodev,noexec,relatime,nsdelegate 0 0 +diff --git a/tests/vircgroupdata/systemd-unified.self.cgroup b/tests/vircgroupdata/systemd-unified.self.cgroup +new file mode 100644 +index 0000000000..6007ce7e18 +--- /dev/null ++++ b/tests/vircgroupdata/systemd-unified.self.cgroup +@@ -0,0 +1 @@ ++0::/libvirt/emulator +diff --git a/tests/vircgrouptest.c b/tests/vircgrouptest.c +index 2d6f52fb6e..aebb90c16c 100644 +--- a/tests/vircgrouptest.c ++++ b/tests/vircgrouptest.c +@@ -636,6 +636,74 @@ static int testCgroupNewForSelfHybrid(const void *args G_GNUC_UNUSED) + } + + ++static int testCgroupNewForSelfSystemdLegacy(const void *args G_GNUC_UNUSED) ++{ ++ virCgroupPtr cgroup = NULL; ++ int ret = -1; ++ const char *empty[VIR_CGROUP_CONTROLLER_LAST] = { 0 }; ++ const char *mounts[VIR_CGROUP_CONTROLLER_LAST] = { ++ [VIR_CGROUP_CONTROLLER_BLKIO] = "/not/really/sys/fs/cgroup/blkio", ++ [VIR_CGROUP_CONTROLLER_CPU] = "/not/really/sys/fs/cgroup/cpu", ++ [VIR_CGROUP_CONTROLLER_CPUACCT] = "/not/really/sys/fs/cgroup/cpuacct", ++ [VIR_CGROUP_CONTROLLER_CPUSET] = "/not/really/sys/fs/cgroup/cpuset", ++ [VIR_CGROUP_CONTROLLER_DEVICES] = "/not/really/sys/fs/cgroup/devices", ++ [VIR_CGROUP_CONTROLLER_FREEZER] = "/not/really/sys/fs/cgroup/freezer", ++ [VIR_CGROUP_CONTROLLER_MEMORY] = "/not/really/sys/fs/cgroup/memory", ++ [VIR_CGROUP_CONTROLLER_NET_CLS] = "/not/really/sys/fs/cgroup/net_cls", ++ [VIR_CGROUP_CONTROLLER_PERF_EVENT] = "/not/really/sys/fs/cgroup/perf_event", ++ }; ++ const char *placement[VIR_CGROUP_CONTROLLER_LAST] = { ++ [VIR_CGROUP_CONTROLLER_BLKIO] = "", ++ [VIR_CGROUP_CONTROLLER_CPU] = "", ++ [VIR_CGROUP_CONTROLLER_CPUACCT] = "", ++ [VIR_CGROUP_CONTROLLER_CPUSET] = "", ++ [VIR_CGROUP_CONTROLLER_DEVICES] = "", ++ [VIR_CGROUP_CONTROLLER_FREEZER] = "", ++ [VIR_CGROUP_CONTROLLER_MEMORY] = "", ++ [VIR_CGROUP_CONTROLLER_NET_CLS] = "", ++ [VIR_CGROUP_CONTROLLER_PERF_EVENT] = "", ++ }; ++ ++ if (virCgroupNewSelf(&cgroup) < 0) { ++ fprintf(stderr, "Cannot create cgroup for self\n"); ++ goto cleanup; ++ } ++ ++ ret = validateCgroup(cgroup, "", mounts, empty, placement, NULL, NULL, 0); ++ ++ cleanup: ++ virCgroupFree(&cgroup); ++ return ret; ++} ++ ++ ++static int testCgroupNewForSelfSystemdUnified(const void *args G_GNUC_UNUSED) ++{ ++ virCgroupPtr cgroup = NULL; ++ int ret = -1; ++ const char *empty[VIR_CGROUP_CONTROLLER_LAST] = { 0 }; ++ unsigned int controllers = ++ (1 << VIR_CGROUP_CONTROLLER_CPU) | ++ (1 << VIR_CGROUP_CONTROLLER_CPUACCT) | ++ (1 << VIR_CGROUP_CONTROLLER_MEMORY) | ++ (1 << VIR_CGROUP_CONTROLLER_DEVICES) | ++ (1 << VIR_CGROUP_CONTROLLER_BLKIO); ++ ++ if (virCgroupNewSelf(&cgroup) < 0) { ++ fprintf(stderr, "Cannot create cgroup for self\n"); ++ goto cleanup; ++ } ++ ++ ret = validateCgroup(cgroup, "", empty, empty, empty, ++ "/not/really/sys/fs/cgroup", "", ++ controllers); ++ ++ cleanup: ++ virCgroupFree(&cgroup); ++ return ret; ++} ++ ++ + static int testCgroupAvailable(const void *args) + { + bool got = virCgroupAvailable(); +@@ -1125,6 +1193,20 @@ mymain(void) + ret = -1; + cleanupFakeFS(fakerootdir); + ++ fakerootdir = initFakeFS("legacy", "systemd-legacy"); ++ if (virTestRun("New cgroup for self (systemd-legacy)", ++ testCgroupNewForSelfSystemdLegacy, NULL) < 0) { ++ ret = -1; ++ } ++ cleanupFakeFS(fakerootdir); ++ ++ fakerootdir = initFakeFS("unified", "systemd-unified"); ++ if (virTestRun("New cgroup for self (systemd-unified)", ++ testCgroupNewForSelfSystemdUnified, NULL) < 0) { ++ ret = -1; ++ } ++ cleanupFakeFS(fakerootdir); ++ + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; + } + +-- +2.30.0 + diff --git a/SOURCES/libvirt-vircgroup-correctly-free-nested-virCgroupPtr.patch b/SOURCES/libvirt-vircgroup-correctly-free-nested-virCgroupPtr.patch new file mode 100644 index 0000000..e72b84d --- /dev/null +++ b/SOURCES/libvirt-vircgroup-correctly-free-nested-virCgroupPtr.patch @@ -0,0 +1,45 @@ +From 7cdf83f2e699a9c9b8cafbc09dbd21d2cb3a3b45 Mon Sep 17 00:00:00 2001 +Message-Id: <7cdf83f2e699a9c9b8cafbc09dbd21d2cb3a3b45@dist-git> +From: Pavel Hrdina +Date: Fri, 19 Feb 2021 13:34:01 +0100 +Subject: [PATCH] vircgroup: correctly free nested virCgroupPtr +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Fixes: 184245f53b94fc84f727eb6e8a2aa52df02d69c0 + +Signed-off-by: Pavel Hrdina +Reviewed-by: Daniel Henrique Barboza +(cherry picked from commit 6a1f5e8a4f3184bb54b9dcaa3afcf8c97adccb62) + +Conflicts: + src/util/vircgroup.c + - missing upstream g_free rewrite + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1798463 + +Signed-off-by: Pavel Hrdina +Message-Id: +Reviewed-by: Ján Tomko +--- + src/util/vircgroup.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/util/vircgroup.c b/src/util/vircgroup.c +index d0f867ba7f..0a6404e97c 100644 +--- a/src/util/vircgroup.c ++++ b/src/util/vircgroup.c +@@ -3711,7 +3711,8 @@ virCgroupFree(virCgroupPtr *group) + VIR_FREE((*group)->unified.mountPoint); + VIR_FREE((*group)->unified.placement); + VIR_FREE((*group)->unitName); +- VIR_FREE((*group)->nested); ++ ++ virCgroupFree(&(*group)->nested); + + VIR_FREE((*group)->path); + VIR_FREE(*group); +-- +2.30.0 + diff --git a/SOURCES/libvirt-vircgroup-enforce-range-limit-for-cpu.shares.patch b/SOURCES/libvirt-vircgroup-enforce-range-limit-for-cpu.shares.patch new file mode 100644 index 0000000..d800b77 --- /dev/null +++ b/SOURCES/libvirt-vircgroup-enforce-range-limit-for-cpu.shares.patch @@ -0,0 +1,147 @@ +From c82c32f60579d148f37064e5156e857fa3c84c2f Mon Sep 17 00:00:00 2001 +Message-Id: +From: Pavel Hrdina +Date: Thu, 4 Mar 2021 12:57:57 +0100 +Subject: [PATCH] vircgroup: enforce range limit for cpu.shares +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Before the conversion to using systemd DBus API to set the cpu.shares +there was some magic conversion done by kernel which was documented in +virsh manpage as well. Now systemd errors out if the value is out of +range. + +Since we enforce the range for other cpu cgroup attributes 'quota' and +'period' it makes sense to do the same for 'shares' as well. + +Signed-off-by: Pavel Hrdina +Reviewed-by: Michal Privoznik +(cherry picked from commit 1d9d9961ada6c2d0b9facae0ef8be4f459cf7fc9) + +Conflicts: + docs/formatdomain.rst + src/conf/domain_validate.c + - both are not present in downstream + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1798463 + +Signed-off-by: Pavel Hrdina +Message-Id: <79b9ef9f98b3ab35061f8c4e4acf7b6861d28055.1614858616.git.phrdina@redhat.com> +Reviewed-by: Ján Tomko +--- + docs/formatdomain.html.in | 1 + + docs/manpages/virsh.rst | 5 +---- + src/conf/domain_conf.c | 10 ++++++++++ + src/util/vircgroup.h | 2 ++ + src/util/vircgroupv1.c | 10 ++++++++++ + src/util/vircgroupv2.c | 10 ++++++++++ + 6 files changed, 34 insertions(+), 4 deletions(-) + +diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in +index 4341e256a8..7ac9523684 100644 +--- a/docs/formatdomain.html.in ++++ b/docs/formatdomain.html.in +@@ -854,6 +854,7 @@ + it's a relative measure based on the setting of other VM, + e.g. A VM configured with value + 2048 will get twice as much CPU time as a VM configured with value 1024. ++ The value should be in range [2, 262144]. + Since 0.9.0 + + +diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst +index a5b95c1123..01e1c01912 100644 +--- a/docs/manpages/virsh.rst ++++ b/docs/manpages/virsh.rst +@@ -3704,10 +3704,7 @@ If *--live* is specified, set scheduler information of a running guest. + If *--config* is specified, affect the next boot of a persistent guest. + If *--current* is specified, affect the current guest state. + +-``Note``: The cpu_shares parameter has a valid value range of 0-262144; Negative +-values are wrapped to positive, and larger values are capped at the maximum. +-Therefore, -1 is a useful shorthand for 262144. On the Linux kernel, the +-values 0 and 1 are automatically converted to a minimal value of 2. ++``Note``: The cpu_shares parameter has a valid value range of 2-262144. + + ``Note``: The weight and cap parameters are defined only for the + XEN_CREDIT scheduler. +diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c +index 9f6cdb0de8..444657c9a1 100644 +--- a/src/conf/domain_conf.c ++++ b/src/conf/domain_conf.c +@@ -7026,6 +7026,16 @@ virDomainDefLifecycleActionValidate(const virDomainDef *def) + static int + virDomainDefCputuneValidate(const virDomainDef *def) + { ++ if (def->cputune.shares > 0 && ++ (def->cputune.shares < VIR_CGROUP_CPU_SHARES_MIN || ++ def->cputune.shares > VIR_CGROUP_CPU_SHARES_MAX)) { ++ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, ++ _("Value of cputune 'shares' must be in range [%llu, %llu]"), ++ VIR_CGROUP_CPU_SHARES_MIN, ++ VIR_CGROUP_CPU_SHARES_MAX); ++ return -1; ++ } ++ + CPUTUNE_VALIDATE_PERIOD(period); + CPUTUNE_VALIDATE_PERIOD(global_period); + CPUTUNE_VALIDATE_PERIOD(emulator_period); +diff --git a/src/util/vircgroup.h b/src/util/vircgroup.h +index 1c6edea0be..938cfdfbe3 100644 +--- a/src/util/vircgroup.h ++++ b/src/util/vircgroup.h +@@ -243,6 +243,8 @@ virCgroupGetDomainTotalCpuStats(virCgroupPtr group, + int virCgroupSetCpuShares(virCgroupPtr group, unsigned long long shares); + int virCgroupGetCpuShares(virCgroupPtr group, unsigned long long *shares); + ++#define VIR_CGROUP_CPU_SHARES_MIN 2LL ++#define VIR_CGROUP_CPU_SHARES_MAX 262144LL + #define VIR_CGROUP_CPU_PERIOD_MIN 1000LL + #define VIR_CGROUP_CPU_PERIOD_MAX 1000000LL + #define VIR_CGROUP_CPU_QUOTA_MIN 1000LL +diff --git a/src/util/vircgroupv1.c b/src/util/vircgroupv1.c +index 49a2cb023e..d417446447 100644 +--- a/src/util/vircgroupv1.c ++++ b/src/util/vircgroupv1.c +@@ -1901,6 +1901,16 @@ static int + virCgroupV1SetCpuShares(virCgroupPtr group, + unsigned long long shares) + { ++ if (shares < VIR_CGROUP_CPU_SHARES_MIN || ++ shares > VIR_CGROUP_CPU_SHARES_MAX) { ++ virReportError(VIR_ERR_INVALID_ARG, ++ _("shares '%llu' must be in range [%llu, %llu]"), ++ shares, ++ VIR_CGROUP_CPU_SHARES_MIN, ++ VIR_CGROUP_CPU_SHARES_MAX); ++ return -1; ++ } ++ + if (group->unitName) { + return virCgroupSetValueDBus(group->unitName, "CPUShares", + "t", shares); +diff --git a/src/util/vircgroupv2.c b/src/util/vircgroupv2.c +index a14fc669fb..079fe6a8ec 100644 +--- a/src/util/vircgroupv2.c ++++ b/src/util/vircgroupv2.c +@@ -1499,6 +1499,16 @@ static int + virCgroupV2SetCpuShares(virCgroupPtr group, + unsigned long long shares) + { ++ if (shares < VIR_CGROUP_CPU_SHARES_MIN || ++ shares > VIR_CGROUP_CPU_SHARES_MAX) { ++ virReportError(VIR_ERR_INVALID_ARG, ++ _("shares '%llu' must be in range [%llu, %llu]"), ++ shares, ++ VIR_CGROUP_CPU_SHARES_MIN, ++ VIR_CGROUP_CPU_SHARES_MAX); ++ return -1; ++ } ++ + if (group->unitName) { + return virCgroupSetValueDBus(group->unitName, "CPUWeight", + "t", shares); +-- +2.30.0 + diff --git a/SOURCES/libvirt-vircgroup-introduce-nested-cgroup-to-properly-work-with-systemd.patch b/SOURCES/libvirt-vircgroup-introduce-nested-cgroup-to-properly-work-with-systemd.patch new file mode 100644 index 0000000..435dc6a --- /dev/null +++ b/SOURCES/libvirt-vircgroup-introduce-nested-cgroup-to-properly-work-with-systemd.patch @@ -0,0 +1,879 @@ +From 2593f2e4626fbb6dfef2317bceea4d1b8275f9d8 Mon Sep 17 00:00:00 2001 +Message-Id: <2593f2e4626fbb6dfef2317bceea4d1b8275f9d8@dist-git> +From: Pavel Hrdina period
+Date: Fri, 19 Feb 2021 13:33:59 +0100 +Subject: [PATCH] vircgroup: introduce nested cgroup to properly work with + systemd +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +When running on host with systemd we register VMs with machined. +In this case systemd creates the root VM cgroup for us. This has some +implications where one of them is that systemd owns all files inside +the root VM cgroup and we should not touch them. + +We already use DBus calls for some of the APIs but for the remaining +ones we will continue accessing the files directly. Systemd doesn't +support threaded cgroups so we need to do this. + +The reason why we don't use DBus for most of the APIs is that we already +have a code that works with files and we would have to check if systemd +supports each API. + +This change introduces new topology on systemd hosts: + +$ROOT + | + +- machine.slice + | + +- machine-qemu\x2d1\x2dvm1.scope + | + +- libvirt + | + +- emulator + +- vcpu0 + +- vcpu0 + +compared to the previous topology: + +$ROOT + | + +- machine.slice + | + +- machine-qemu\x2d1\x2dvm1.scope + | + +- emulator + +- vcpu0 + +- vcpu0 + +Signed-off-by: Pavel Hrdina +Reviewed-by: Michal Privoznik +(cherry picked from commit 184245f53b94fc84f727eb6e8a2aa52df02d69c0) + +Conflicts: + src/util/vircgroup.c + - missing upstream g_free and g_autofree rewrite + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1798463 + +Signed-off-by: Pavel Hrdina +Message-Id: <51312c8b520e4ed794f8cd8a77b77c228387bb15.1613737828.git.phrdina@redhat.com> +Reviewed-by: Ján Tomko +--- + docs/cgroups.html.in | 29 +++-- + src/util/vircgroup.c | 256 +++++++++++++++++++++++++++++++-------- + src/util/vircgrouppriv.h | 4 + + src/util/vircgroupv1.c | 15 ++- + src/util/vircgroupv2.c | 6 + + 5 files changed, 245 insertions(+), 65 deletions(-) + +diff --git a/docs/cgroups.html.in b/docs/cgroups.html.in +index 78dede1bba..412a9360ff 100644 +--- a/docs/cgroups.html.in ++++ b/docs/cgroups.html.in +@@ -117,21 +117,27 @@ $ROOT + | + +- machine-qemu\x2d1\x2dvm1.scope + | | +- | +- emulator +- | +- vcpu0 +- | +- vcpu1 ++ | +- libvirt ++ | | ++ | +- emulator ++ | +- vcpu0 ++ | +- vcpu1 + | + +- machine-qemu\x2d2\x2dvm2.scope + | | +- | +- emulator +- | +- vcpu0 +- | +- vcpu1 ++ | +- libvirt ++ | | ++ | +- emulator ++ | +- vcpu0 ++ | +- vcpu1 + | + +- machine-qemu\x2d3\x2dvm3.scope + | | +- | +- emulator +- | +- vcpu0 +- | +- vcpu1 ++ | +- libvirt ++ | | ++ | +- emulator ++ | +- vcpu0 ++ | +- vcpu1 + | + +- machine-engineering.slice + | | +@@ -148,6 +154,11 @@ $ROOT + +- machine-lxc\x2d33333\x2dcontainer3.scope +
++ Prior libvirt 7.1.0 the topology doesn't have extra
++ libvirt
directory.
++
+diff --git a/src/util/vircgroup.c b/src/util/vircgroup.c
+index 8f5bcd94f4..d0f867ba7f 100644
+--- a/src/util/vircgroup.c
++++ b/src/util/vircgroup.c
+@@ -639,6 +639,22 @@ virCgroupMakeGroup(virCgroupPtr parent,
+ }
+
+
++static bool
++virCgroupExists(virCgroupPtr group)
++{
++ size_t i;
++
++ for (i = 0; i < VIR_CGROUP_BACKEND_TYPE_LAST; i++) {
++ if (group->backends[i] &&
++ !group->backends[i]->exists(group)) {
++ return false;
++ }
++ }
++
++ return true;
++}
++
++
+ /**
+ * virCgroupNew:
+ * @path: path for the new group
+@@ -695,10 +711,11 @@ virCgroupAddTaskInternal(virCgroupPtr group,
+ unsigned int flags)
+ {
+ size_t i;
++ virCgroupPtr parent = virCgroupGetNested(group);
+
+ for (i = 0; i < VIR_CGROUP_BACKEND_TYPE_LAST; i++) {
+- if (group->backends[i] &&
+- group->backends[i]->addTask(group, pid, flags) < 0) {
++ if (parent->backends[i] &&
++ parent->backends[i]->addTask(parent, pid, flags) < 0) {
+ return -1;
+ }
+ }
+@@ -871,6 +888,30 @@ virCgroupNewPartition(const char *path,
+ }
+
+
++static int
++virCgroupNewNested(virCgroupPtr parent,
++ int controllers,
++ bool create,
++ pid_t pid,
++ virCgroupPtr *nested)
++{
++ virCgroupPtr new = NULL;
++
++ if (virCgroupNew(-1, "libvirt", parent, controllers, &new) < 0)
++ return -1;
++
++ if (create) {
++ if (virCgroupMakeGroup(parent, new, create, pid, VIR_CGROUP_NONE) < 0) {
++ virCgroupFree(&new);
++ return -1;
++ }
++ }
++
++ *nested = g_steal_pointer(&new);
++ return 0;
++}
++
++
+ /**
+ * virCgroupNewSelf:
+ *
+@@ -954,6 +995,7 @@ virCgroupNewThread(virCgroupPtr domain,
+ virCgroupPtr *group)
+ {
+ g_autofree char *name = NULL;
++ virCgroupPtr parent = NULL;
+ int controllers;
+
+ switch (nameval) {
+@@ -976,10 +1018,12 @@ virCgroupNewThread(virCgroupPtr domain,
+ (1 << VIR_CGROUP_CONTROLLER_CPUACCT) |
+ (1 << VIR_CGROUP_CONTROLLER_CPUSET));
+
+- if (virCgroupNew(-1, name, domain, controllers, group) < 0)
++ parent = virCgroupGetNested(domain);
++
++ if (virCgroupNew(-1, name, parent, controllers, group) < 0)
+ return -1;
+
+- if (virCgroupMakeGroup(domain, *group, create, -1, VIR_CGROUP_THREAD) < 0) {
++ if (virCgroupMakeGroup(parent, *group, create, -1, VIR_CGROUP_THREAD) < 0) {
+ virCgroupFree(group);
+ return -1;
+ }
+@@ -1009,6 +1053,7 @@ virCgroupNewDetectMachine(const char *name,
+ virCgroupPtr *group)
+ {
+ size_t i;
++ virCgroupPtr nested = NULL;
+
+ if (virCgroupNewDetect(pid, controllers, group) < 0) {
+ if (virCgroupNewIgnoreError())
+@@ -1032,6 +1077,14 @@ virCgroupNewDetectMachine(const char *name,
+ if (virSystemdHasMachined() == 0 && !(*group)->unitName)
+ return -1;
+
++ if (virCgroupNewNested((*group), controllers, false, -1, &nested) < 0)
++ return -1;
++
++ if (virCgroupExists(nested))
++ (*group)->nested = g_steal_pointer(&nested);
++
++ virCgroupFree(&nested);
++
+ return 0;
+ }
+
+@@ -1107,6 +1160,7 @@ virCgroupNewMachineSystemd(const char *name,
+ {
+ int rv;
+ virCgroupPtr init;
++ virCgroupPtr nested = NULL;
+ g_autofree char *path = NULL;
+ size_t i;
+
+@@ -1157,6 +1211,13 @@ virCgroupNewMachineSystemd(const char *name,
+ return -1;
+ }
+
++ if (virCgroupNewNested((*group), controllers, true, pidleader, &nested) < 0) {
++ virCgroupFree(group);
++ return -1;
++ }
++
++ (*group)->nested = nested;
++
+ if (virCgroupAddProcess(*group, pidleader) < 0) {
+ virErrorPtr saved;
+
+@@ -1349,7 +1410,9 @@ virCgroupGetBlkioIoServiced(virCgroupPtr group,
+ long long *requests_read,
+ long long *requests_write)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_BLKIO,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_BLKIO,
+ getBlkioIoServiced, -1,
+ bytes_read, bytes_write,
+ requests_read, requests_write);
+@@ -1376,7 +1439,9 @@ virCgroupGetBlkioIoDeviceServiced(virCgroupPtr group,
+ long long *requests_read,
+ long long *requests_write)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_BLKIO,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_BLKIO,
+ getBlkioIoDeviceServiced, -1,
+ path, bytes_read, bytes_write,
+ requests_read, requests_write);
+@@ -1427,7 +1492,9 @@ virCgroupSetBlkioDeviceReadIops(virCgroupPtr group,
+ const char *path,
+ unsigned int riops)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_BLKIO,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_BLKIO,
+ setBlkioDeviceReadIops, -1, path, riops);
+ }
+
+@@ -1445,7 +1512,9 @@ virCgroupSetBlkioDeviceWriteIops(virCgroupPtr group,
+ const char *path,
+ unsigned int wiops)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_BLKIO,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_BLKIO,
+ setBlkioDeviceWriteIops, -1, path, wiops);
+ }
+
+@@ -1463,7 +1532,9 @@ virCgroupSetBlkioDeviceReadBps(virCgroupPtr group,
+ const char *path,
+ unsigned long long rbps)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_BLKIO,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_BLKIO,
+ setBlkioDeviceReadBps, -1, path, rbps);
+ }
+
+@@ -1480,7 +1551,9 @@ virCgroupSetBlkioDeviceWriteBps(virCgroupPtr group,
+ const char *path,
+ unsigned long long wbps)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_BLKIO,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_BLKIO,
+ setBlkioDeviceWriteBps, -1, path, wbps);
+ }
+
+@@ -1516,7 +1589,9 @@ virCgroupGetBlkioDeviceReadIops(virCgroupPtr group,
+ const char *path,
+ unsigned int *riops)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_BLKIO,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_BLKIO,
+ getBlkioDeviceReadIops, -1, path, riops);
+ }
+
+@@ -1533,7 +1608,9 @@ virCgroupGetBlkioDeviceWriteIops(virCgroupPtr group,
+ const char *path,
+ unsigned int *wiops)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_BLKIO,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_BLKIO,
+ getBlkioDeviceWriteIops, -1, path, wiops);
+ }
+
+@@ -1550,7 +1627,9 @@ virCgroupGetBlkioDeviceReadBps(virCgroupPtr group,
+ const char *path,
+ unsigned long long *rbps)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_BLKIO,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_BLKIO,
+ getBlkioDeviceReadBps, -1, path, rbps);
+ }
+
+@@ -1567,7 +1646,9 @@ virCgroupGetBlkioDeviceWriteBps(virCgroupPtr group,
+ const char *path,
+ unsigned long long *wbps)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_BLKIO,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_BLKIO,
+ getBlkioDeviceWriteBps, -1, path, wbps);
+ }
+
+@@ -1600,7 +1681,9 @@ virCgroupGetBlkioDeviceWeight(virCgroupPtr group,
+ int
+ virCgroupSetMemory(virCgroupPtr group, unsigned long long kb)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_MEMORY,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_MEMORY,
+ setMemory, -1, kb);
+ }
+
+@@ -1627,7 +1710,9 @@ virCgroupGetMemoryStat(virCgroupPtr group,
+ unsigned long long *inactiveFile,
+ unsigned long long *unevictable)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_MEMORY,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_MEMORY,
+ getMemoryStat, -1, cache,
+ activeAnon, inactiveAnon,
+ activeFile, inactiveFile,
+@@ -1646,7 +1731,9 @@ virCgroupGetMemoryStat(virCgroupPtr group,
+ int
+ virCgroupGetMemoryUsage(virCgroupPtr group, unsigned long *kb)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_MEMORY,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_MEMORY,
+ getMemoryUsage, -1, kb);
+ }
+
+@@ -1662,7 +1749,9 @@ virCgroupGetMemoryUsage(virCgroupPtr group, unsigned long *kb)
+ int
+ virCgroupSetMemoryHardLimit(virCgroupPtr group, unsigned long long kb)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_MEMORY,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_MEMORY,
+ setMemoryHardLimit, -1, kb);
+ }
+
+@@ -1678,7 +1767,9 @@ virCgroupSetMemoryHardLimit(virCgroupPtr group, unsigned long long kb)
+ int
+ virCgroupGetMemoryHardLimit(virCgroupPtr group, unsigned long long *kb)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_MEMORY,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_MEMORY,
+ getMemoryHardLimit, -1, kb);
+ }
+
+@@ -1694,7 +1785,9 @@ virCgroupGetMemoryHardLimit(virCgroupPtr group, unsigned long long *kb)
+ int
+ virCgroupSetMemorySoftLimit(virCgroupPtr group, unsigned long long kb)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_MEMORY,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_MEMORY,
+ setMemorySoftLimit, -1, kb);
+ }
+
+@@ -1710,7 +1803,9 @@ virCgroupSetMemorySoftLimit(virCgroupPtr group, unsigned long long kb)
+ int
+ virCgroupGetMemorySoftLimit(virCgroupPtr group, unsigned long long *kb)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_MEMORY,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_MEMORY,
+ getMemorySoftLimit, -1, kb);
+ }
+
+@@ -1726,7 +1821,9 @@ virCgroupGetMemorySoftLimit(virCgroupPtr group, unsigned long long *kb)
+ int
+ virCgroupSetMemSwapHardLimit(virCgroupPtr group, unsigned long long kb)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_MEMORY,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_MEMORY,
+ setMemSwapHardLimit, -1, kb);
+ }
+
+@@ -1742,7 +1839,9 @@ virCgroupSetMemSwapHardLimit(virCgroupPtr group, unsigned long long kb)
+ int
+ virCgroupGetMemSwapHardLimit(virCgroupPtr group, unsigned long long *kb)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_MEMORY,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_MEMORY,
+ getMemSwapHardLimit, -1, kb);
+ }
+
+@@ -1758,7 +1857,9 @@ virCgroupGetMemSwapHardLimit(virCgroupPtr group, unsigned long long *kb)
+ int
+ virCgroupGetMemSwapUsage(virCgroupPtr group, unsigned long long *kb)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_MEMORY,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_MEMORY,
+ getMemSwapUsage, -1, kb);
+ }
+
+@@ -1774,7 +1875,9 @@ virCgroupGetMemSwapUsage(virCgroupPtr group, unsigned long long *kb)
+ int
+ virCgroupSetCpusetMems(virCgroupPtr group, const char *mems)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_CPUSET,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_CPUSET,
+ setCpusetMems, -1, mems);
+ }
+
+@@ -1790,7 +1893,9 @@ virCgroupSetCpusetMems(virCgroupPtr group, const char *mems)
+ int
+ virCgroupGetCpusetMems(virCgroupPtr group, char **mems)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_CPUSET,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_CPUSET,
+ getCpusetMems, -1, mems);
+ }
+
+@@ -1806,7 +1911,9 @@ virCgroupGetCpusetMems(virCgroupPtr group, char **mems)
+ int
+ virCgroupSetCpusetMemoryMigrate(virCgroupPtr group, bool migrate)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_CPUSET,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_CPUSET,
+ setCpusetMemoryMigrate, -1, migrate);
+ }
+
+@@ -1822,7 +1929,9 @@ virCgroupSetCpusetMemoryMigrate(virCgroupPtr group, bool migrate)
+ int
+ virCgroupGetCpusetMemoryMigrate(virCgroupPtr group, bool *migrate)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_CPUSET,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_CPUSET,
+ getCpusetMemoryMigrate, -1, migrate);
+ }
+
+@@ -1838,7 +1947,9 @@ virCgroupGetCpusetMemoryMigrate(virCgroupPtr group, bool *migrate)
+ int
+ virCgroupSetCpusetCpus(virCgroupPtr group, const char *cpus)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_CPUSET,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_CPUSET,
+ setCpusetCpus, -1, cpus);
+ }
+
+@@ -1854,7 +1965,9 @@ virCgroupSetCpusetCpus(virCgroupPtr group, const char *cpus)
+ int
+ virCgroupGetCpusetCpus(virCgroupPtr group, char **cpus)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_CPUSET,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_CPUSET,
+ getCpusetCpus, -1, cpus);
+ }
+
+@@ -1869,7 +1982,9 @@ virCgroupGetCpusetCpus(virCgroupPtr group, char **cpus)
+ int
+ virCgroupDenyAllDevices(virCgroupPtr group)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_DEVICES,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_DEVICES,
+ denyAllDevices, -1);
+ }
+
+@@ -1890,7 +2005,9 @@ virCgroupDenyAllDevices(virCgroupPtr group)
+ int
+ virCgroupAllowAllDevices(virCgroupPtr group, int perms)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_DEVICES,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_DEVICES,
+ allowAllDevices, -1, perms);
+ }
+
+@@ -1910,7 +2027,9 @@ int
+ virCgroupAllowDevice(virCgroupPtr group, char type, int major, int minor,
+ int perms)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_DEVICES,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_DEVICES,
+ allowDevice, -1, type, major, minor, perms);
+ }
+
+@@ -1936,6 +2055,7 @@ virCgroupAllowDevicePath(virCgroupPtr group,
+ bool ignoreEacces)
+ {
+ struct stat sb;
++ virCgroupPtr parent = virCgroupGetNested(group);
+
+ if (stat(path, &sb) < 0) {
+ if (errno == EACCES && ignoreEacces)
+@@ -1950,7 +2070,7 @@ virCgroupAllowDevicePath(virCgroupPtr group,
+ if (!S_ISCHR(sb.st_mode) && !S_ISBLK(sb.st_mode))
+ return 1;
+
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_DEVICES,
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_DEVICES,
+ allowDevice, -1,
+ S_ISCHR(sb.st_mode) ? 'c' : 'b',
+ major(sb.st_rdev),
+@@ -1974,7 +2094,9 @@ int
+ virCgroupDenyDevice(virCgroupPtr group, char type, int major, int minor,
+ int perms)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_DEVICES,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_DEVICES,
+ denyDevice, -1, type, major, minor, perms);
+ }
+
+@@ -2000,6 +2122,7 @@ virCgroupDenyDevicePath(virCgroupPtr group,
+ bool ignoreEacces)
+ {
+ struct stat sb;
++ virCgroupPtr parent = virCgroupGetNested(group);
+
+ if (stat(path, &sb) < 0) {
+ if (errno == EACCES && ignoreEacces)
+@@ -2014,7 +2137,7 @@ virCgroupDenyDevicePath(virCgroupPtr group,
+ if (!S_ISCHR(sb.st_mode) && !S_ISBLK(sb.st_mode))
+ return 1;
+
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_DEVICES,
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_DEVICES,
+ denyDevice, -1,
+ S_ISCHR(sb.st_mode) ? 'c' : 'b',
+ major(sb.st_rdev),
+@@ -2282,7 +2405,9 @@ virCgroupGetCpuShares(virCgroupPtr group, unsigned long long *shares)
+ int
+ virCgroupSetCpuCfsPeriod(virCgroupPtr group, unsigned long long cfs_period)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_CPU,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_CPU,
+ setCpuCfsPeriod, -1, cfs_period);
+ }
+
+@@ -2298,7 +2423,9 @@ virCgroupSetCpuCfsPeriod(virCgroupPtr group, unsigned long long cfs_period)
+ int
+ virCgroupGetCpuCfsPeriod(virCgroupPtr group, unsigned long long *cfs_period)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_CPU,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_CPU,
+ getCpuCfsPeriod, -1, cfs_period);
+ }
+
+@@ -2315,7 +2442,9 @@ virCgroupGetCpuCfsPeriod(virCgroupPtr group, unsigned long long *cfs_period)
+ int
+ virCgroupSetCpuCfsQuota(virCgroupPtr group, long long cfs_quota)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_CPU,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_CPU,
+ setCpuCfsQuota, -1, cfs_quota);
+ }
+
+@@ -2323,7 +2452,9 @@ virCgroupSetCpuCfsQuota(virCgroupPtr group, long long cfs_quota)
+ int
+ virCgroupGetCpuacctPercpuUsage(virCgroupPtr group, char **usage)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_CPUACCT,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_CPUACCT,
+ getCpuacctPercpuUsage, -1, usage);
+ }
+
+@@ -2669,7 +2800,9 @@ virCgroupKillPainfully(virCgroupPtr group)
+ int
+ virCgroupGetCpuCfsQuota(virCgroupPtr group, long long *cfs_quota)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_CPU,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_CPU,
+ getCpuCfsQuota, -1, cfs_quota);
+ }
+
+@@ -2677,7 +2810,9 @@ virCgroupGetCpuCfsQuota(virCgroupPtr group, long long *cfs_quota)
+ int
+ virCgroupGetCpuacctUsage(virCgroupPtr group, unsigned long long *usage)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_CPUACCT,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_CPUACCT,
+ getCpuacctUsage, -1, usage);
+ }
+
+@@ -2686,7 +2821,9 @@ int
+ virCgroupGetCpuacctStat(virCgroupPtr group, unsigned long long *user,
+ unsigned long long *sys)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_CPUACCT,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_CPUACCT,
+ getCpuacctStat, -1, user, sys);
+ }
+
+@@ -2694,7 +2831,9 @@ virCgroupGetCpuacctStat(virCgroupPtr group, unsigned long long *user,
+ int
+ virCgroupSetFreezerState(virCgroupPtr group, const char *state)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_FREEZER,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_FREEZER,
+ setFreezerState, -1, state);
+ }
+
+@@ -2702,7 +2841,9 @@ virCgroupSetFreezerState(virCgroupPtr group, const char *state)
+ int
+ virCgroupGetFreezerState(virCgroupPtr group, char **state)
+ {
+- VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_FREEZER,
++ virCgroupPtr parent = virCgroupGetNested(group);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_FREEZER,
+ getFreezerState, -1, state);
+ }
+
+@@ -2712,10 +2853,11 @@ virCgroupBindMount(virCgroupPtr group, const char *oldroot,
+ const char *mountopts)
+ {
+ size_t i;
++ virCgroupPtr parent = virCgroupGetNested(group);
+
+ for (i = 0; i < VIR_CGROUP_BACKEND_TYPE_LAST; i++) {
+- if (group->backends[i] &&
+- group->backends[i]->bindMount(group, oldroot, mountopts) < 0) {
++ if (parent->backends[i] &&
++ parent->backends[i]->bindMount(parent, oldroot, mountopts) < 0) {
+ return -1;
+ }
+ }
+@@ -2730,10 +2872,11 @@ int virCgroupSetOwner(virCgroupPtr cgroup,
+ int controllers)
+ {
+ size_t i;
++ virCgroupPtr parent = virCgroupGetNested(cgroup);
+
+ for (i = 0; i < VIR_CGROUP_BACKEND_TYPE_LAST; i++) {
+- if (cgroup->backends[i] &&
+- cgroup->backends[i]->setOwner(cgroup, uid, gid, controllers) < 0) {
++ if (parent->backends[i] &&
++ parent->backends[i]->setOwner(parent, uid, gid, controllers) < 0) {
+ return -1;
+ }
+ }
+@@ -2752,7 +2895,9 @@ int virCgroupSetOwner(virCgroupPtr cgroup,
+ bool
+ virCgroupSupportsCpuBW(virCgroupPtr cgroup)
+ {
+- VIR_CGROUP_BACKEND_CALL(cgroup, VIR_CGROUP_CONTROLLER_CPU,
++ virCgroupPtr parent = virCgroupGetNested(cgroup);
++
++ VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_CPU,
+ supportsCpuBW, false);
+ }
+
+@@ -2760,10 +2905,11 @@ int
+ virCgroupHasEmptyTasks(virCgroupPtr cgroup, int controller)
+ {
+ size_t i;
++ virCgroupPtr parent = virCgroupGetNested(cgroup);
+
+ for (i = 0; i < VIR_CGROUP_BACKEND_TYPE_LAST; i++) {
+- if (cgroup->backends[i]) {
+- int rc = cgroup->backends[i]->hasEmptyTasks(cgroup, controller);
++ if (parent->backends[i]) {
++ int rc = parent->backends[i]->hasEmptyTasks(parent, controller);
+ if (rc <= 0)
+ return rc;
+ }
+@@ -3565,6 +3711,7 @@ virCgroupFree(virCgroupPtr *group)
+ VIR_FREE((*group)->unified.mountPoint);
+ VIR_FREE((*group)->unified.placement);
+ VIR_FREE((*group)->unitName);
++ VIR_FREE((*group)->nested);
+
+ VIR_FREE((*group)->path);
+ VIR_FREE(*group);
+@@ -3577,9 +3724,12 @@ virCgroupDelThread(virCgroupPtr cgroup,
+ int idx)
+ {
+ virCgroupPtr new_cgroup = NULL;
++ virCgroupPtr parent = NULL;
+
+ if (cgroup) {
+- if (virCgroupNewThread(cgroup, nameval, idx, false, &new_cgroup) < 0)
++ parent = virCgroupGetNested(cgroup);
++
++ if (virCgroupNewThread(parent, nameval, idx, false, &new_cgroup) < 0)
+ return -1;
+
+ /* Remove the offlined cgroup */
+diff --git a/src/util/vircgrouppriv.h b/src/util/vircgrouppriv.h
+index b4a9e0b379..104d74e4d7 100644
+--- a/src/util/vircgrouppriv.h
++++ b/src/util/vircgrouppriv.h
+@@ -69,8 +69,12 @@ struct _virCgroup {
+ virCgroupV2Controller unified;
+
+ char *unitName;
++ virCgroupPtr nested;
+ };
+
++#define virCgroupGetNested(cgroup) \
++ (cgroup->nested ? cgroup->nested : cgroup)
++
+ #define virCgroupSetValueDBus(unitName, key, ...) \
+ ({ \
+ int __ret = -1; \
+diff --git a/src/util/vircgroupv1.c b/src/util/vircgroupv1.c
+index 57d617cb69..49a2cb023e 100644
+--- a/src/util/vircgroupv1.c
++++ b/src/util/vircgroupv1.c
+@@ -338,6 +338,8 @@ virCgroupV1DetectPlacement(virCgroupPtr group,
+
+ for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
+ const char *typestr = virCgroupV1ControllerTypeToString(i);
++ g_autofree char* placement = NULL;
++ char *tmp = NULL;
+
+ if (!virCgroupV1MountOptsMatchController(controllers, typestr))
+ continue;
+@@ -348,17 +350,24 @@ virCgroupV1DetectPlacement(virCgroupPtr group,
+ if (group->legacy[i].placement)
+ continue;
+
++ /* On systemd we create a nested cgroup for some cgroup tasks
++ * but the placement should point to the root cgroup. */
++ placement = g_strdup(selfpath);
++ tmp = g_strrstr(placement, "/libvirt");
++ if (tmp)
++ *tmp = '\0';
++
+ /*
+ * selfpath == "/" + path="" -> "/"
+ * selfpath == "/libvirt.service" + path == "" -> "/libvirt.service"
+ * selfpath == "/libvirt.service" + path == "foo" -> "/libvirt.service/foo"
+ */
+ if (i == VIR_CGROUP_CONTROLLER_SYSTEMD) {
+- group->legacy[i].placement = g_strdup(selfpath);
++ group->legacy[i].placement = g_strdup(placement);
+ } else {
+- bool delim = STREQ(selfpath, "/") || STREQ(path, "");
++ bool delim = STREQ(placement, "/") || STREQ(path, "");
+
+- group->legacy[i].placement = g_strdup_printf("%s%s%s", selfpath,
++ group->legacy[i].placement = g_strdup_printf("%s%s%s", placement,
+ delim ? "" : "/",
+ path);
+ }
+diff --git a/src/util/vircgroupv2.c b/src/util/vircgroupv2.c
+index d15e2354cf..a14fc669fb 100644
+--- a/src/util/vircgroupv2.c
++++ b/src/util/vircgroupv2.c
+@@ -210,6 +210,12 @@ virCgroupV2DetectPlacement(virCgroupPtr group,
+ if (tmp)
+ *tmp = '\0';
+
++ /* On systemd we create a nested cgroup for some cgroup tasks
++ * but the placement should point to the root cgroup. */
++ tmp = g_strrstr(placement, "/libvirt");
++ if (tmp)
++ *tmp = '\0';
++
+ /*
+ * selfpath == "/" + path="" -> "/"
+ * selfpath == "/libvirt.service" + path == "" -> "/libvirt.service"
+--
+2.30.0
+
diff --git a/SOURCES/libvirt-vircgroup-introduce-virCgroupV1Exists-and-virCgroupV2Exists.patch b/SOURCES/libvirt-vircgroup-introduce-virCgroupV1Exists-and-virCgroupV2Exists.patch
new file mode 100644
index 0000000..c73bf43
--- /dev/null
+++ b/SOURCES/libvirt-vircgroup-introduce-virCgroupV1Exists-and-virCgroupV2Exists.patch
@@ -0,0 +1,129 @@
+From f835b834d7922bed1ccda35885e42ab7c3f4a70f Mon Sep 17 00:00:00 2001
+Message-Id: