Blame SOURCES/libvirt-vircgroup-introduce-nested-cgroup-to-properly-work-with-systemd.patch

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

397dc2
+      Prior libvirt 7.1.0 the topology doesn't have extra
397dc2
+      libvirt directory.
397dc2
+    

397dc2
+
397dc2
     

Non-systemd cgroups layout

397dc2
 
397dc2
     

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