]> xenbits.xensource.com Git - libvirt.git/commitdiff
vircgroup: introduce virCgroupV2DevicesPrepareProg
authorPavel Hrdina <phrdina@redhat.com>
Mon, 24 Jun 2019 12:34:51 +0000 (14:34 +0200)
committerPavel Hrdina <phrdina@redhat.com>
Fri, 15 Nov 2019 11:58:33 +0000 (12:58 +0100)
This function will be called for every virCgroup(Allow|Deny)* API in
order to prepare BPF program for guest.  Since libvirtd can be restarted
at any point we will first try to detect existing progam, if there is
none we will create a new empty BPF program and lastly if we don't have
any space left in the existing BPF map we will create a new copy of the
BPF map with more space and attach a new program with that map into the
guest cgroup.

This solution allows us to start with reasonably small BPF map consuming
only small amount of memory and if needed we can easily extend the BPF
map if there is a lot of host devices used in guest or if user wants to
hot-plug a lot of devices once the guest is running.

Since there is no way how to reallocate existing BPF map we need to
create a new copy if we run out of space in current BPF map.

This overcomes all the limitations in BPF:

    - map used in program has to be created before the program is loaded
      into kernel

    - once map is created you cannot change its size

    - you cannot replace map in existing program

    - you cannot use an array of maps because it can store FD to maps
      of one specific size so we would not be able to use it to overcome
      the second issue

Signed-off-by: Pavel Hrdina <phrdina@redhat.com>
Reviewed-by: Ján Tomko <jtomko@redhat.com>
src/libvirt_private.syms
src/util/vircgroupv2devices.c
src/util/vircgroupv2devices.h

index b75956c4e1c8d7f2b27b10b1bc560553cdf54c12..2ec9f067302779a32cddbab99ab7a4c76990c9b3 100644 (file)
@@ -1721,6 +1721,7 @@ virCgroupV2DevicesAttachProg;
 virCgroupV2DevicesAvailable;
 virCgroupV2DevicesCreateProg;
 virCgroupV2DevicesDetectProg;
+virCgroupV2DevicesPrepareProg;
 
 # util/virclosecallbacks.h
 virCloseCallbacksGet;
index 330e40c63aa5b541390603c53ed59b2650423fd4..7e537f5368589ed7e66c63729d3f3a3dc182d68e 100644 (file)
@@ -446,6 +446,51 @@ virCgroupV2DevicesCreateMap(size_t size)
 }
 
 
+static int
+virCgroupV2DevicesReallocMap(int mapfd,
+                             size_t size)
+{
+    uint64_t key = 0;
+    uint64_t prevKey = 0;
+    int rc;
+    int ret = -1;
+    VIR_AUTOCLOSE newmapfd = virCgroupV2DevicesCreateMap(size);
+
+    VIR_DEBUG("realloc devices map mapfd:%d, size:%lu", mapfd, size);
+
+    if (newmapfd < 0)
+        return -1;
+
+    while ((rc = virBPFGetNextElem(mapfd, &prevKey, &key)) == 0) {
+        uint32_t val = 0;
+
+        if (virBPFLookupElem(mapfd, &key, &val) < 0) {
+            virReportSystemError(errno, "%s",
+                                 _("failed to lookup device in old map"));
+            return -1;
+        }
+
+        if (virBPFUpdateElem(newmapfd, &key, &val) < 0) {
+            virReportSystemError(errno, "%s",
+                                 _("failed to add device into new map"));
+            return -1;
+        }
+
+        prevKey = key;
+    }
+
+    if (rc < 0 && errno != ENOENT) {
+        virReportSystemError(errno, "%s",
+                             _("failed to copy all device rules"));
+        return -1;
+    }
+
+    ret = newmapfd;
+    newmapfd = -1;
+    return ret;
+}
+
+
 int
 virCgroupV2DevicesCreateProg(virCgroupPtr group)
 {
@@ -466,6 +511,33 @@ virCgroupV2DevicesCreateProg(virCgroupPtr group)
     mapfd = -1;
     return 0;
 }
+
+
+int
+virCgroupV2DevicesPrepareProg(virCgroupPtr group)
+{
+    if (virCgroupV2DevicesDetectProg(group) < 0)
+        return -1;
+
+    if (virCgroupV2DevicesCreateProg(group) < 0)
+        return -1;
+
+    if (group->unified.devices.count >= group->unified.devices.max) {
+        size_t max = group->unified.devices.max * 2;
+        int newmapfd = virCgroupV2DevicesReallocMap(group->unified.devices.mapfd,
+                                                    max);
+
+        if (newmapfd < 0)
+            return -1;
+
+        if (virCgroupV2DevicesAttachProg(group, newmapfd, max) < 0) {
+            VIR_FORCE_CLOSE(newmapfd);
+            return -1;
+        }
+    }
+
+    return 0;
+}
 #else /* !HAVE_DECL_BPF_CGROUP_DEVICE */
 bool
 virCgroupV2DevicesAvailable(virCgroupPtr group G_GNUC_UNUSED)
@@ -504,4 +576,14 @@ virCgroupV2DevicesCreateProg(virCgroupPtr group G_GNUC_UNUSED)
                            "with this kernel"));
     return -1;
 }
+
+
+int
+virCgroupV2DevicesPrepareProg(virCgroupPtr group G_GNUC_UNUSED)
+{
+    virReportSystemError(ENOSYS, "%s",
+                         _("cgroups v2 BPF devices not supported "
+                           "with this kernel"));
+    return -1;
+}
 #endif /* !HAVE_DECL_BPF_CGROUP_DEVICE */
index db1316d072e2686a178d16b70eac6c7e67d2e2ae..357ed0c8bf3038297e27387aa7d4439747a4bf54 100644 (file)
@@ -33,3 +33,6 @@ virCgroupV2DevicesDetectProg(virCgroupPtr group);
 
 int
 virCgroupV2DevicesCreateProg(virCgroupPtr group);
+
+int
+virCgroupV2DevicesPrepareProg(virCgroupPtr group);