render / rpms / libvirt

Forked from rpms/libvirt 7 months ago
Clone
8c03ec
From 2593f2e4626fbb6dfef2317bceea4d1b8275f9d8 Mon Sep 17 00:00:00 2001
8c03ec
Message-Id: <2593f2e4626fbb6dfef2317bceea4d1b8275f9d8@dist-git>
8c03ec
From: Pavel Hrdina <phrdina@redhat.com>
8c03ec
Date: Fri, 19 Feb 2021 13:33:59 +0100
8c03ec
Subject: [PATCH] vircgroup: introduce nested cgroup to properly work with
8c03ec
 systemd
8c03ec
MIME-Version: 1.0
8c03ec
Content-Type: text/plain; charset=UTF-8
8c03ec
Content-Transfer-Encoding: 8bit
8c03ec
8c03ec
When running on host with systemd we register VMs with machined.
8c03ec
In this case systemd creates the root VM cgroup for us. This has some
8c03ec
implications where one of them is that systemd owns all files inside
8c03ec
the root VM cgroup and we should not touch them.
8c03ec
8c03ec
We already use DBus calls for some of the APIs but for the remaining
8c03ec
ones we will continue accessing the files directly. Systemd doesn't
8c03ec
support threaded cgroups so we need to do this.
8c03ec
8c03ec
The reason why we don't use DBus for most of the APIs is that we already
8c03ec
have a code that works with files and we would have to check if systemd
8c03ec
supports each API.
8c03ec
8c03ec
This change introduces new topology on systemd hosts:
8c03ec
8c03ec
$ROOT
8c03ec
  |
8c03ec
  +- machine.slice
8c03ec
     |
8c03ec
     +- machine-qemu\x2d1\x2dvm1.scope
8c03ec
        |
8c03ec
        +- libvirt
8c03ec
           |
8c03ec
           +- emulator
8c03ec
           +- vcpu0
8c03ec
           +- vcpu0
8c03ec
8c03ec
compared to the previous topology:
8c03ec
8c03ec
$ROOT
8c03ec
  |
8c03ec
  +- machine.slice
8c03ec
     |
8c03ec
     +- machine-qemu\x2d1\x2dvm1.scope
8c03ec
        |
8c03ec
        +- emulator
8c03ec
        +- vcpu0
8c03ec
        +- vcpu0
8c03ec
8c03ec
Signed-off-by: Pavel Hrdina <phrdina@redhat.com>
8c03ec
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
8c03ec
(cherry picked from commit 184245f53b94fc84f727eb6e8a2aa52df02d69c0)
8c03ec
8c03ec
Conflicts:
8c03ec
    src/util/vircgroup.c
8c03ec
        - missing upstream g_free and g_autofree rewrite
8c03ec
8c03ec
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1798463
8c03ec
8c03ec
Signed-off-by: Pavel Hrdina <phrdina@redhat.com>
8c03ec
Message-Id: <51312c8b520e4ed794f8cd8a77b77c228387bb15.1613737828.git.phrdina@redhat.com>
8c03ec
Reviewed-by: Ján Tomko <jtomko@redhat.com>
8c03ec
---
8c03ec
 docs/cgroups.html.in     |  29 +++--
8c03ec
 src/util/vircgroup.c     | 256 +++++++++++++++++++++++++++++++--------
8c03ec
 src/util/vircgrouppriv.h |   4 +
8c03ec
 src/util/vircgroupv1.c   |  15 ++-
8c03ec
 src/util/vircgroupv2.c   |   6 +
8c03ec
 5 files changed, 245 insertions(+), 65 deletions(-)
8c03ec
8c03ec
diff --git a/docs/cgroups.html.in b/docs/cgroups.html.in
8c03ec
index 78dede1bba..412a9360ff 100644
8c03ec
--- a/docs/cgroups.html.in
8c03ec
+++ b/docs/cgroups.html.in
8c03ec
@@ -117,21 +117,27 @@ $ROOT
8c03ec
       |
8c03ec
       +- machine-qemu\x2d1\x2dvm1.scope
8c03ec
       |   |
8c03ec
-      |   +- emulator
8c03ec
-      |   +- vcpu0
8c03ec
-      |   +- vcpu1
8c03ec
+      |   +- libvirt
8c03ec
+      |       |
8c03ec
+      |       +- emulator
8c03ec
+      |       +- vcpu0
8c03ec
+      |       +- vcpu1
8c03ec
       |
8c03ec
       +- machine-qemu\x2d2\x2dvm2.scope
8c03ec
       |   |
8c03ec
-      |   +- emulator
8c03ec
-      |   +- vcpu0
8c03ec
-      |   +- vcpu1
8c03ec
+      |   +- libvirt
8c03ec
+      |       |
8c03ec
+      |       +- emulator
8c03ec
+      |       +- vcpu0
8c03ec
+      |       +- vcpu1
8c03ec
       |
8c03ec
       +- machine-qemu\x2d3\x2dvm3.scope
8c03ec
       |   |
8c03ec
-      |   +- emulator
8c03ec
-      |   +- vcpu0
8c03ec
-      |   +- vcpu1
8c03ec
+      |   +- libvirt
8c03ec
+      |       |
8c03ec
+      |       +- emulator
8c03ec
+      |       +- vcpu0
8c03ec
+      |       +- vcpu1
8c03ec
       |
8c03ec
       +- machine-engineering.slice
8c03ec
       |   |
8c03ec
@@ -148,6 +154,11 @@ $ROOT
8c03ec
           +- machine-lxc\x2d33333\x2dcontainer3.scope
8c03ec
     
8c03ec
 
8c03ec
+    

8c03ec
+      Prior libvirt 7.1.0 the topology doesn't have extra
8c03ec
+      libvirt directory.
8c03ec
+    

8c03ec
+
8c03ec
     

Non-systemd cgroups layout

8c03ec
 
8c03ec
     

8c03ec
diff --git a/src/util/vircgroup.c b/src/util/vircgroup.c
8c03ec
index 8f5bcd94f4..d0f867ba7f 100644
8c03ec
--- a/src/util/vircgroup.c
8c03ec
+++ b/src/util/vircgroup.c
8c03ec
@@ -639,6 +639,22 @@ virCgroupMakeGroup(virCgroupPtr parent,
8c03ec
 }
8c03ec
 
8c03ec
 
8c03ec
+static bool
8c03ec
+virCgroupExists(virCgroupPtr group)
8c03ec
+{
8c03ec
+    size_t i;
8c03ec
+
8c03ec
+    for (i = 0; i < VIR_CGROUP_BACKEND_TYPE_LAST; i++) {
8c03ec
+        if (group->backends[i] &&
8c03ec
+            !group->backends[i]->exists(group)) {
8c03ec
+            return false;
8c03ec
+        }
8c03ec
+    }
8c03ec
+
8c03ec
+    return true;
8c03ec
+}
8c03ec
+
8c03ec
+
8c03ec
 /**
8c03ec
  * virCgroupNew:
8c03ec
  * @path: path for the new group
8c03ec
@@ -695,10 +711,11 @@ virCgroupAddTaskInternal(virCgroupPtr group,
8c03ec
                          unsigned int flags)
8c03ec
 {
8c03ec
     size_t i;
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
 
8c03ec
     for (i = 0; i < VIR_CGROUP_BACKEND_TYPE_LAST; i++) {
8c03ec
-        if (group->backends[i] &&
8c03ec
-            group->backends[i]->addTask(group, pid, flags) < 0) {
8c03ec
+        if (parent->backends[i] &&
8c03ec
+            parent->backends[i]->addTask(parent, pid, flags) < 0) {
8c03ec
             return -1;
8c03ec
         }
8c03ec
     }
8c03ec
@@ -871,6 +888,30 @@ virCgroupNewPartition(const char *path,
8c03ec
 }
8c03ec
 
8c03ec
 
8c03ec
+static int
8c03ec
+virCgroupNewNested(virCgroupPtr parent,
8c03ec
+                   int controllers,
8c03ec
+                   bool create,
8c03ec
+                   pid_t pid,
8c03ec
+                   virCgroupPtr *nested)
8c03ec
+{
8c03ec
+    virCgroupPtr new = NULL;
8c03ec
+
8c03ec
+    if (virCgroupNew(-1, "libvirt", parent, controllers, &new) < 0)
8c03ec
+        return -1;
8c03ec
+
8c03ec
+    if (create) {
8c03ec
+        if (virCgroupMakeGroup(parent, new, create, pid, VIR_CGROUP_NONE) < 0) {
8c03ec
+            virCgroupFree(&new;;
8c03ec
+            return -1;
8c03ec
+        }
8c03ec
+    }
8c03ec
+
8c03ec
+    *nested = g_steal_pointer(&new;;
8c03ec
+    return 0;
8c03ec
+}
8c03ec
+
8c03ec
+
8c03ec
 /**
8c03ec
 * virCgroupNewSelf:
8c03ec
 *
8c03ec
@@ -954,6 +995,7 @@ virCgroupNewThread(virCgroupPtr domain,
8c03ec
                    virCgroupPtr *group)
8c03ec
 {
8c03ec
     g_autofree char *name = NULL;
8c03ec
+    virCgroupPtr parent = NULL;
8c03ec
     int controllers;
8c03ec
 
8c03ec
     switch (nameval) {
8c03ec
@@ -976,10 +1018,12 @@ virCgroupNewThread(virCgroupPtr domain,
8c03ec
                    (1 << VIR_CGROUP_CONTROLLER_CPUACCT) |
8c03ec
                    (1 << VIR_CGROUP_CONTROLLER_CPUSET));
8c03ec
 
8c03ec
-    if (virCgroupNew(-1, name, domain, controllers, group) < 0)
8c03ec
+    parent = virCgroupGetNested(domain);
8c03ec
+
8c03ec
+    if (virCgroupNew(-1, name, parent, controllers, group) < 0)
8c03ec
         return -1;
8c03ec
 
8c03ec
-    if (virCgroupMakeGroup(domain, *group, create, -1, VIR_CGROUP_THREAD) < 0) {
8c03ec
+    if (virCgroupMakeGroup(parent, *group, create, -1, VIR_CGROUP_THREAD) < 0) {
8c03ec
         virCgroupFree(group);
8c03ec
         return -1;
8c03ec
     }
8c03ec
@@ -1009,6 +1053,7 @@ virCgroupNewDetectMachine(const char *name,
8c03ec
                           virCgroupPtr *group)
8c03ec
 {
8c03ec
     size_t i;
8c03ec
+    virCgroupPtr nested = NULL;
8c03ec
 
8c03ec
     if (virCgroupNewDetect(pid, controllers, group) < 0) {
8c03ec
         if (virCgroupNewIgnoreError())
8c03ec
@@ -1032,6 +1077,14 @@ virCgroupNewDetectMachine(const char *name,
8c03ec
     if (virSystemdHasMachined() == 0 && !(*group)->unitName)
8c03ec
         return -1;
8c03ec
 
8c03ec
+    if (virCgroupNewNested((*group), controllers, false, -1, &nested) < 0)
8c03ec
+        return -1;
8c03ec
+
8c03ec
+    if (virCgroupExists(nested))
8c03ec
+        (*group)->nested = g_steal_pointer(&nested);
8c03ec
+
8c03ec
+    virCgroupFree(&nested);
8c03ec
+
8c03ec
     return 0;
8c03ec
 }
8c03ec
 
8c03ec
@@ -1107,6 +1160,7 @@ virCgroupNewMachineSystemd(const char *name,
8c03ec
 {
8c03ec
     int rv;
8c03ec
     virCgroupPtr init;
8c03ec
+    virCgroupPtr nested = NULL;
8c03ec
     g_autofree char *path = NULL;
8c03ec
     size_t i;
8c03ec
 
8c03ec
@@ -1157,6 +1211,13 @@ virCgroupNewMachineSystemd(const char *name,
8c03ec
         return -1;
8c03ec
     }
8c03ec
 
8c03ec
+    if (virCgroupNewNested((*group), controllers, true, pidleader, &nested) < 0) {
8c03ec
+        virCgroupFree(group);
8c03ec
+        return -1;
8c03ec
+    }
8c03ec
+
8c03ec
+    (*group)->nested = nested;
8c03ec
+
8c03ec
     if (virCgroupAddProcess(*group, pidleader) < 0) {
8c03ec
         virErrorPtr saved;
8c03ec
 
8c03ec
@@ -1349,7 +1410,9 @@ virCgroupGetBlkioIoServiced(virCgroupPtr group,
8c03ec
                             long long *requests_read,
8c03ec
                             long long *requests_write)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_BLKIO,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_BLKIO,
8c03ec
                             getBlkioIoServiced, -1,
8c03ec
                             bytes_read, bytes_write,
8c03ec
                             requests_read, requests_write);
8c03ec
@@ -1376,7 +1439,9 @@ virCgroupGetBlkioIoDeviceServiced(virCgroupPtr group,
8c03ec
                                   long long *requests_read,
8c03ec
                                   long long *requests_write)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_BLKIO,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_BLKIO,
8c03ec
                             getBlkioIoDeviceServiced, -1,
8c03ec
                             path, bytes_read, bytes_write,
8c03ec
                             requests_read, requests_write);
8c03ec
@@ -1427,7 +1492,9 @@ virCgroupSetBlkioDeviceReadIops(virCgroupPtr group,
8c03ec
                                 const char *path,
8c03ec
                                 unsigned int riops)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_BLKIO,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_BLKIO,
8c03ec
                             setBlkioDeviceReadIops, -1, path, riops);
8c03ec
 }
8c03ec
 
8c03ec
@@ -1445,7 +1512,9 @@ virCgroupSetBlkioDeviceWriteIops(virCgroupPtr group,
8c03ec
                                  const char *path,
8c03ec
                                  unsigned int wiops)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_BLKIO,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_BLKIO,
8c03ec
                             setBlkioDeviceWriteIops, -1, path, wiops);
8c03ec
 }
8c03ec
 
8c03ec
@@ -1463,7 +1532,9 @@ virCgroupSetBlkioDeviceReadBps(virCgroupPtr group,
8c03ec
                                const char *path,
8c03ec
                                unsigned long long rbps)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_BLKIO,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_BLKIO,
8c03ec
                             setBlkioDeviceReadBps, -1, path, rbps);
8c03ec
 }
8c03ec
 
8c03ec
@@ -1480,7 +1551,9 @@ virCgroupSetBlkioDeviceWriteBps(virCgroupPtr group,
8c03ec
                                 const char *path,
8c03ec
                                 unsigned long long wbps)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_BLKIO,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_BLKIO,
8c03ec
                             setBlkioDeviceWriteBps, -1, path, wbps);
8c03ec
 }
8c03ec
 
8c03ec
@@ -1516,7 +1589,9 @@ virCgroupGetBlkioDeviceReadIops(virCgroupPtr group,
8c03ec
                                 const char *path,
8c03ec
                                 unsigned int *riops)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_BLKIO,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_BLKIO,
8c03ec
                             getBlkioDeviceReadIops, -1, path, riops);
8c03ec
 }
8c03ec
 
8c03ec
@@ -1533,7 +1608,9 @@ virCgroupGetBlkioDeviceWriteIops(virCgroupPtr group,
8c03ec
                                  const char *path,
8c03ec
                                  unsigned int *wiops)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_BLKIO,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_BLKIO,
8c03ec
                             getBlkioDeviceWriteIops, -1, path, wiops);
8c03ec
 }
8c03ec
 
8c03ec
@@ -1550,7 +1627,9 @@ virCgroupGetBlkioDeviceReadBps(virCgroupPtr group,
8c03ec
                                const char *path,
8c03ec
                                unsigned long long *rbps)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_BLKIO,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_BLKIO,
8c03ec
                             getBlkioDeviceReadBps, -1, path, rbps);
8c03ec
 }
8c03ec
 
8c03ec
@@ -1567,7 +1646,9 @@ virCgroupGetBlkioDeviceWriteBps(virCgroupPtr group,
8c03ec
                                 const char *path,
8c03ec
                                 unsigned long long *wbps)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_BLKIO,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_BLKIO,
8c03ec
                             getBlkioDeviceWriteBps, -1, path, wbps);
8c03ec
 }
8c03ec
 
8c03ec
@@ -1600,7 +1681,9 @@ virCgroupGetBlkioDeviceWeight(virCgroupPtr group,
8c03ec
 int
8c03ec
 virCgroupSetMemory(virCgroupPtr group, unsigned long long kb)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_MEMORY,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_MEMORY,
8c03ec
                             setMemory, -1, kb);
8c03ec
 }
8c03ec
 
8c03ec
@@ -1627,7 +1710,9 @@ virCgroupGetMemoryStat(virCgroupPtr group,
8c03ec
                        unsigned long long *inactiveFile,
8c03ec
                        unsigned long long *unevictable)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_MEMORY,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_MEMORY,
8c03ec
                             getMemoryStat, -1, cache,
8c03ec
                             activeAnon, inactiveAnon,
8c03ec
                             activeFile, inactiveFile,
8c03ec
@@ -1646,7 +1731,9 @@ virCgroupGetMemoryStat(virCgroupPtr group,
8c03ec
 int
8c03ec
 virCgroupGetMemoryUsage(virCgroupPtr group, unsigned long *kb)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_MEMORY,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_MEMORY,
8c03ec
                             getMemoryUsage, -1, kb);
8c03ec
 }
8c03ec
 
8c03ec
@@ -1662,7 +1749,9 @@ virCgroupGetMemoryUsage(virCgroupPtr group, unsigned long *kb)
8c03ec
 int
8c03ec
 virCgroupSetMemoryHardLimit(virCgroupPtr group, unsigned long long kb)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_MEMORY,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_MEMORY,
8c03ec
                             setMemoryHardLimit, -1, kb);
8c03ec
 }
8c03ec
 
8c03ec
@@ -1678,7 +1767,9 @@ virCgroupSetMemoryHardLimit(virCgroupPtr group, unsigned long long kb)
8c03ec
 int
8c03ec
 virCgroupGetMemoryHardLimit(virCgroupPtr group, unsigned long long *kb)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_MEMORY,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_MEMORY,
8c03ec
                             getMemoryHardLimit, -1, kb);
8c03ec
 }
8c03ec
 
8c03ec
@@ -1694,7 +1785,9 @@ virCgroupGetMemoryHardLimit(virCgroupPtr group, unsigned long long *kb)
8c03ec
 int
8c03ec
 virCgroupSetMemorySoftLimit(virCgroupPtr group, unsigned long long kb)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_MEMORY,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_MEMORY,
8c03ec
                             setMemorySoftLimit, -1, kb);
8c03ec
 }
8c03ec
 
8c03ec
@@ -1710,7 +1803,9 @@ virCgroupSetMemorySoftLimit(virCgroupPtr group, unsigned long long kb)
8c03ec
 int
8c03ec
 virCgroupGetMemorySoftLimit(virCgroupPtr group, unsigned long long *kb)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_MEMORY,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_MEMORY,
8c03ec
                             getMemorySoftLimit, -1, kb);
8c03ec
 }
8c03ec
 
8c03ec
@@ -1726,7 +1821,9 @@ virCgroupGetMemorySoftLimit(virCgroupPtr group, unsigned long long *kb)
8c03ec
 int
8c03ec
 virCgroupSetMemSwapHardLimit(virCgroupPtr group, unsigned long long kb)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_MEMORY,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_MEMORY,
8c03ec
                             setMemSwapHardLimit, -1, kb);
8c03ec
 }
8c03ec
 
8c03ec
@@ -1742,7 +1839,9 @@ virCgroupSetMemSwapHardLimit(virCgroupPtr group, unsigned long long kb)
8c03ec
 int
8c03ec
 virCgroupGetMemSwapHardLimit(virCgroupPtr group, unsigned long long *kb)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_MEMORY,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_MEMORY,
8c03ec
                             getMemSwapHardLimit, -1, kb);
8c03ec
 }
8c03ec
 
8c03ec
@@ -1758,7 +1857,9 @@ virCgroupGetMemSwapHardLimit(virCgroupPtr group, unsigned long long *kb)
8c03ec
 int
8c03ec
 virCgroupGetMemSwapUsage(virCgroupPtr group, unsigned long long *kb)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_MEMORY,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_MEMORY,
8c03ec
                             getMemSwapUsage, -1, kb);
8c03ec
 }
8c03ec
 
8c03ec
@@ -1774,7 +1875,9 @@ virCgroupGetMemSwapUsage(virCgroupPtr group, unsigned long long *kb)
8c03ec
 int
8c03ec
 virCgroupSetCpusetMems(virCgroupPtr group, const char *mems)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_CPUSET,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_CPUSET,
8c03ec
                             setCpusetMems, -1, mems);
8c03ec
 }
8c03ec
 
8c03ec
@@ -1790,7 +1893,9 @@ virCgroupSetCpusetMems(virCgroupPtr group, const char *mems)
8c03ec
 int
8c03ec
 virCgroupGetCpusetMems(virCgroupPtr group, char **mems)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_CPUSET,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_CPUSET,
8c03ec
                             getCpusetMems, -1, mems);
8c03ec
 }
8c03ec
 
8c03ec
@@ -1806,7 +1911,9 @@ virCgroupGetCpusetMems(virCgroupPtr group, char **mems)
8c03ec
 int
8c03ec
 virCgroupSetCpusetMemoryMigrate(virCgroupPtr group, bool migrate)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_CPUSET,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_CPUSET,
8c03ec
                             setCpusetMemoryMigrate, -1, migrate);
8c03ec
 }
8c03ec
 
8c03ec
@@ -1822,7 +1929,9 @@ virCgroupSetCpusetMemoryMigrate(virCgroupPtr group, bool migrate)
8c03ec
 int
8c03ec
 virCgroupGetCpusetMemoryMigrate(virCgroupPtr group, bool *migrate)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_CPUSET,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_CPUSET,
8c03ec
                             getCpusetMemoryMigrate, -1, migrate);
8c03ec
 }
8c03ec
 
8c03ec
@@ -1838,7 +1947,9 @@ virCgroupGetCpusetMemoryMigrate(virCgroupPtr group, bool *migrate)
8c03ec
 int
8c03ec
 virCgroupSetCpusetCpus(virCgroupPtr group, const char *cpus)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_CPUSET,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_CPUSET,
8c03ec
                             setCpusetCpus, -1, cpus);
8c03ec
 }
8c03ec
 
8c03ec
@@ -1854,7 +1965,9 @@ virCgroupSetCpusetCpus(virCgroupPtr group, const char *cpus)
8c03ec
 int
8c03ec
 virCgroupGetCpusetCpus(virCgroupPtr group, char **cpus)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_CPUSET,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_CPUSET,
8c03ec
                             getCpusetCpus, -1, cpus);
8c03ec
 }
8c03ec
 
8c03ec
@@ -1869,7 +1982,9 @@ virCgroupGetCpusetCpus(virCgroupPtr group, char **cpus)
8c03ec
 int
8c03ec
 virCgroupDenyAllDevices(virCgroupPtr group)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_DEVICES,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_DEVICES,
8c03ec
                             denyAllDevices, -1);
8c03ec
 }
8c03ec
 
8c03ec
@@ -1890,7 +2005,9 @@ virCgroupDenyAllDevices(virCgroupPtr group)
8c03ec
 int
8c03ec
 virCgroupAllowAllDevices(virCgroupPtr group, int perms)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_DEVICES,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_DEVICES,
8c03ec
                             allowAllDevices, -1, perms);
8c03ec
 }
8c03ec
 
8c03ec
@@ -1910,7 +2027,9 @@ int
8c03ec
 virCgroupAllowDevice(virCgroupPtr group, char type, int major, int minor,
8c03ec
                      int perms)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_DEVICES,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_DEVICES,
8c03ec
                             allowDevice, -1, type, major, minor, perms);
8c03ec
 }
8c03ec
 
8c03ec
@@ -1936,6 +2055,7 @@ virCgroupAllowDevicePath(virCgroupPtr group,
8c03ec
                          bool ignoreEacces)
8c03ec
 {
8c03ec
     struct stat sb;
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
 
8c03ec
     if (stat(path, &sb) < 0) {
8c03ec
         if (errno == EACCES && ignoreEacces)
8c03ec
@@ -1950,7 +2070,7 @@ virCgroupAllowDevicePath(virCgroupPtr group,
8c03ec
     if (!S_ISCHR(sb.st_mode) && !S_ISBLK(sb.st_mode))
8c03ec
         return 1;
8c03ec
 
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_DEVICES,
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_DEVICES,
8c03ec
                             allowDevice, -1,
8c03ec
                             S_ISCHR(sb.st_mode) ? 'c' : 'b',
8c03ec
                             major(sb.st_rdev),
8c03ec
@@ -1974,7 +2094,9 @@ int
8c03ec
 virCgroupDenyDevice(virCgroupPtr group, char type, int major, int minor,
8c03ec
                     int perms)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_DEVICES,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_DEVICES,
8c03ec
                             denyDevice, -1, type, major, minor, perms);
8c03ec
 }
8c03ec
 
8c03ec
@@ -2000,6 +2122,7 @@ virCgroupDenyDevicePath(virCgroupPtr group,
8c03ec
                         bool ignoreEacces)
8c03ec
 {
8c03ec
     struct stat sb;
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
 
8c03ec
     if (stat(path, &sb) < 0) {
8c03ec
         if (errno == EACCES && ignoreEacces)
8c03ec
@@ -2014,7 +2137,7 @@ virCgroupDenyDevicePath(virCgroupPtr group,
8c03ec
     if (!S_ISCHR(sb.st_mode) && !S_ISBLK(sb.st_mode))
8c03ec
         return 1;
8c03ec
 
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_DEVICES,
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_DEVICES,
8c03ec
                             denyDevice, -1,
8c03ec
                             S_ISCHR(sb.st_mode) ? 'c' : 'b',
8c03ec
                             major(sb.st_rdev),
8c03ec
@@ -2282,7 +2405,9 @@ virCgroupGetCpuShares(virCgroupPtr group, unsigned long long *shares)
8c03ec
 int
8c03ec
 virCgroupSetCpuCfsPeriod(virCgroupPtr group, unsigned long long cfs_period)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_CPU,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_CPU,
8c03ec
                             setCpuCfsPeriod, -1, cfs_period);
8c03ec
 }
8c03ec
 
8c03ec
@@ -2298,7 +2423,9 @@ virCgroupSetCpuCfsPeriod(virCgroupPtr group, unsigned long long cfs_period)
8c03ec
 int
8c03ec
 virCgroupGetCpuCfsPeriod(virCgroupPtr group, unsigned long long *cfs_period)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_CPU,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_CPU,
8c03ec
                             getCpuCfsPeriod, -1, cfs_period);
8c03ec
 }
8c03ec
 
8c03ec
@@ -2315,7 +2442,9 @@ virCgroupGetCpuCfsPeriod(virCgroupPtr group, unsigned long long *cfs_period)
8c03ec
 int
8c03ec
 virCgroupSetCpuCfsQuota(virCgroupPtr group, long long cfs_quota)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_CPU,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_CPU,
8c03ec
                             setCpuCfsQuota, -1, cfs_quota);
8c03ec
 }
8c03ec
 
8c03ec
@@ -2323,7 +2452,9 @@ virCgroupSetCpuCfsQuota(virCgroupPtr group, long long cfs_quota)
8c03ec
 int
8c03ec
 virCgroupGetCpuacctPercpuUsage(virCgroupPtr group, char **usage)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_CPUACCT,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_CPUACCT,
8c03ec
                             getCpuacctPercpuUsage, -1, usage);
8c03ec
 }
8c03ec
 
8c03ec
@@ -2669,7 +2800,9 @@ virCgroupKillPainfully(virCgroupPtr group)
8c03ec
 int
8c03ec
 virCgroupGetCpuCfsQuota(virCgroupPtr group, long long *cfs_quota)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_CPU,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_CPU,
8c03ec
                             getCpuCfsQuota, -1, cfs_quota);
8c03ec
 }
8c03ec
 
8c03ec
@@ -2677,7 +2810,9 @@ virCgroupGetCpuCfsQuota(virCgroupPtr group, long long *cfs_quota)
8c03ec
 int
8c03ec
 virCgroupGetCpuacctUsage(virCgroupPtr group, unsigned long long *usage)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_CPUACCT,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_CPUACCT,
8c03ec
                             getCpuacctUsage, -1, usage);
8c03ec
 }
8c03ec
 
8c03ec
@@ -2686,7 +2821,9 @@ int
8c03ec
 virCgroupGetCpuacctStat(virCgroupPtr group, unsigned long long *user,
8c03ec
                         unsigned long long *sys)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_CPUACCT,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_CPUACCT,
8c03ec
                             getCpuacctStat, -1, user, sys);
8c03ec
 }
8c03ec
 
8c03ec
@@ -2694,7 +2831,9 @@ virCgroupGetCpuacctStat(virCgroupPtr group, unsigned long long *user,
8c03ec
 int
8c03ec
 virCgroupSetFreezerState(virCgroupPtr group, const char *state)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_FREEZER,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_FREEZER,
8c03ec
                             setFreezerState, -1, state);
8c03ec
 }
8c03ec
 
8c03ec
@@ -2702,7 +2841,9 @@ virCgroupSetFreezerState(virCgroupPtr group, const char *state)
8c03ec
 int
8c03ec
 virCgroupGetFreezerState(virCgroupPtr group, char **state)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(group, VIR_CGROUP_CONTROLLER_FREEZER,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_FREEZER,
8c03ec
                             getFreezerState, -1, state);
8c03ec
 }
8c03ec
 
8c03ec
@@ -2712,10 +2853,11 @@ virCgroupBindMount(virCgroupPtr group, const char *oldroot,
8c03ec
                    const char *mountopts)
8c03ec
 {
8c03ec
     size_t i;
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(group);
8c03ec
 
8c03ec
     for (i = 0; i < VIR_CGROUP_BACKEND_TYPE_LAST; i++) {
8c03ec
-        if (group->backends[i] &&
8c03ec
-            group->backends[i]->bindMount(group, oldroot, mountopts) < 0) {
8c03ec
+        if (parent->backends[i] &&
8c03ec
+            parent->backends[i]->bindMount(parent, oldroot, mountopts) < 0) {
8c03ec
             return -1;
8c03ec
         }
8c03ec
     }
8c03ec
@@ -2730,10 +2872,11 @@ int virCgroupSetOwner(virCgroupPtr cgroup,
8c03ec
                       int controllers)
8c03ec
 {
8c03ec
     size_t i;
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(cgroup);
8c03ec
 
8c03ec
     for (i = 0; i < VIR_CGROUP_BACKEND_TYPE_LAST; i++) {
8c03ec
-        if (cgroup->backends[i] &&
8c03ec
-            cgroup->backends[i]->setOwner(cgroup, uid, gid, controllers) < 0) {
8c03ec
+        if (parent->backends[i] &&
8c03ec
+            parent->backends[i]->setOwner(parent, uid, gid, controllers) < 0) {
8c03ec
             return -1;
8c03ec
         }
8c03ec
     }
8c03ec
@@ -2752,7 +2895,9 @@ int virCgroupSetOwner(virCgroupPtr cgroup,
8c03ec
 bool
8c03ec
 virCgroupSupportsCpuBW(virCgroupPtr cgroup)
8c03ec
 {
8c03ec
-    VIR_CGROUP_BACKEND_CALL(cgroup, VIR_CGROUP_CONTROLLER_CPU,
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(cgroup);
8c03ec
+
8c03ec
+    VIR_CGROUP_BACKEND_CALL(parent, VIR_CGROUP_CONTROLLER_CPU,
8c03ec
                             supportsCpuBW, false);
8c03ec
 }
8c03ec
 
8c03ec
@@ -2760,10 +2905,11 @@ int
8c03ec
 virCgroupHasEmptyTasks(virCgroupPtr cgroup, int controller)
8c03ec
 {
8c03ec
     size_t i;
8c03ec
+    virCgroupPtr parent = virCgroupGetNested(cgroup);
8c03ec
 
8c03ec
     for (i = 0; i < VIR_CGROUP_BACKEND_TYPE_LAST; i++) {
8c03ec
-        if (cgroup->backends[i]) {
8c03ec
-            int rc = cgroup->backends[i]->hasEmptyTasks(cgroup, controller);
8c03ec
+        if (parent->backends[i]) {
8c03ec
+            int rc = parent->backends[i]->hasEmptyTasks(parent, controller);
8c03ec
             if (rc <= 0)
8c03ec
                 return rc;
8c03ec
         }
8c03ec
@@ -3565,6 +3711,7 @@ virCgroupFree(virCgroupPtr *group)
8c03ec
     VIR_FREE((*group)->unified.mountPoint);
8c03ec
     VIR_FREE((*group)->unified.placement);
8c03ec
     VIR_FREE((*group)->unitName);
8c03ec
+    VIR_FREE((*group)->nested);
8c03ec
 
8c03ec
     VIR_FREE((*group)->path);
8c03ec
     VIR_FREE(*group);
8c03ec
@@ -3577,9 +3724,12 @@ virCgroupDelThread(virCgroupPtr cgroup,
8c03ec
                    int idx)
8c03ec
 {
8c03ec
     virCgroupPtr new_cgroup = NULL;
8c03ec
+    virCgroupPtr parent = NULL;
8c03ec
 
8c03ec
     if (cgroup) {
8c03ec
-        if (virCgroupNewThread(cgroup, nameval, idx, false, &new_cgroup) < 0)
8c03ec
+        parent = virCgroupGetNested(cgroup);
8c03ec
+
8c03ec
+        if (virCgroupNewThread(parent, nameval, idx, false, &new_cgroup) < 0)
8c03ec
             return -1;
8c03ec
 
8c03ec
         /* Remove the offlined cgroup */
8c03ec
diff --git a/src/util/vircgrouppriv.h b/src/util/vircgrouppriv.h
8c03ec
index b4a9e0b379..104d74e4d7 100644
8c03ec
--- a/src/util/vircgrouppriv.h
8c03ec
+++ b/src/util/vircgrouppriv.h
8c03ec
@@ -69,8 +69,12 @@ struct _virCgroup {
8c03ec
     virCgroupV2Controller unified;
8c03ec
 
8c03ec
     char *unitName;
8c03ec
+    virCgroupPtr nested;
8c03ec
 };
8c03ec
 
8c03ec
+#define virCgroupGetNested(cgroup) \
8c03ec
+    (cgroup->nested ? cgroup->nested : cgroup)
8c03ec
+
8c03ec
 #define virCgroupSetValueDBus(unitName, key, ...) \
8c03ec
     ({ \
8c03ec
         int __ret = -1; \
8c03ec
diff --git a/src/util/vircgroupv1.c b/src/util/vircgroupv1.c
8c03ec
index 57d617cb69..49a2cb023e 100644
8c03ec
--- a/src/util/vircgroupv1.c
8c03ec
+++ b/src/util/vircgroupv1.c
8c03ec
@@ -338,6 +338,8 @@ virCgroupV1DetectPlacement(virCgroupPtr group,
8c03ec
 
8c03ec
     for (i = 0; i < VIR_CGROUP_CONTROLLER_LAST; i++) {
8c03ec
         const char *typestr = virCgroupV1ControllerTypeToString(i);
8c03ec
+        g_autofree char* placement = NULL;
8c03ec
+        char *tmp = NULL;
8c03ec
 
8c03ec
         if (!virCgroupV1MountOptsMatchController(controllers, typestr))
8c03ec
             continue;
8c03ec
@@ -348,17 +350,24 @@ virCgroupV1DetectPlacement(virCgroupPtr group,
8c03ec
         if (group->legacy[i].placement)
8c03ec
             continue;
8c03ec
 
8c03ec
+        /* On systemd we create a nested cgroup for some cgroup tasks
8c03ec
+         * but the placement should point to the root cgroup. */
8c03ec
+        placement = g_strdup(selfpath);
8c03ec
+        tmp = g_strrstr(placement, "/libvirt");
8c03ec
+        if (tmp)
8c03ec
+            *tmp = '\0';
8c03ec
+
8c03ec
         /*
8c03ec
          * selfpath == "/" + path="" -> "/"
8c03ec
          * selfpath == "/libvirt.service" + path == "" -> "/libvirt.service"
8c03ec
          * selfpath == "/libvirt.service" + path == "foo" -> "/libvirt.service/foo"
8c03ec
          */
8c03ec
         if (i == VIR_CGROUP_CONTROLLER_SYSTEMD) {
8c03ec
-            group->legacy[i].placement = g_strdup(selfpath);
8c03ec
+            group->legacy[i].placement = g_strdup(placement);
8c03ec
         } else {
8c03ec
-            bool delim = STREQ(selfpath, "/") || STREQ(path, "");
8c03ec
+            bool delim = STREQ(placement, "/") || STREQ(path, "");
8c03ec
 
8c03ec
-            group->legacy[i].placement = g_strdup_printf("%s%s%s", selfpath,
8c03ec
+            group->legacy[i].placement = g_strdup_printf("%s%s%s", placement,
8c03ec
                                                          delim ? "" : "/",
8c03ec
                                                          path);
8c03ec
         }
8c03ec
diff --git a/src/util/vircgroupv2.c b/src/util/vircgroupv2.c
8c03ec
index d15e2354cf..a14fc669fb 100644
8c03ec
--- a/src/util/vircgroupv2.c
8c03ec
+++ b/src/util/vircgroupv2.c
8c03ec
@@ -210,6 +210,12 @@ virCgroupV2DetectPlacement(virCgroupPtr group,
8c03ec
     if (tmp)
8c03ec
         *tmp = '\0';
8c03ec
 
8c03ec
+    /* On systemd we create a nested cgroup for some cgroup tasks
8c03ec
+     * but the placement should point to the root cgroup. */
8c03ec
+    tmp = g_strrstr(placement, "/libvirt");
8c03ec
+    if (tmp)
8c03ec
+        *tmp = '\0';
8c03ec
+
8c03ec
     /*
8c03ec
      * selfpath == "/" + path="" -> "/"
8c03ec
      * selfpath == "/libvirt.service" + path == "" -> "/libvirt.service"
8c03ec
-- 
8c03ec
2.30.0
8c03ec