[RFC PATCH 2/4] qemu_capabilities: Added new capability ebpf_rss_fds for QEMU.

Andrew Melnychenko andrew at daynix.com
Mon Oct 9 06:16:12 UTC 2023


Also, added logic for retrieving eBPF objects from QEMU.
eBPF objects stored in the hash table during runtime.
eBPF objects cached encoded in base64 in the .xml cache file.

Signed-off-by: Andrew Melnychenko <andrew at daynix.com>
---
 src/qemu/qemu_capabilities.c | 181 +++++++++++++++++++++++++++++++++++
 src/qemu/qemu_capabilities.h |   4 +
 2 files changed, 185 insertions(+)

diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
index 3a1bfbf74d..d9b5d6fb22 100644
--- a/src/qemu/qemu_capabilities.c
+++ b/src/qemu/qemu_capabilities.c
@@ -698,6 +698,7 @@ VIR_ENUM_IMPL(virQEMUCaps,
               /* 450 */
               "run-with.async-teardown", /* QEMU_CAPS_RUN_WITH_ASYNC_TEARDOWN */
               "virtio-blk-vhost-vdpa", /* QEMU_CAPS_DEVICE_VIRTIO_BLK_VHOST_VDPA */
+              "virtio-net.ebpf_rss_fds", /* QEMU_CAPS_VIRTIO_NET_EBPF_RSS_FDS */
     );
 
 
@@ -789,6 +790,9 @@ struct _virQEMUCaps {
     virQEMUCapsAccel kvm;
     virQEMUCapsAccel hvf;
     virQEMUCapsAccel tcg;
+
+    /* Hash of ebpf objects virQEMUEbpfOBjectData */
+    GHashTable *ebpfObjects;
 };
 
 struct virQEMUCapsSearchData {
@@ -796,6 +800,18 @@ struct virQEMUCapsSearchData {
     const char *binaryFilter;
 };
 
+struct virQEMUEbpfOBjectData {
+    void *ebpfData;
+    size_t ebpfSize;
+};
+
+void virQEMUEbpfOBjectDataDestroy(gpointer data) {
+    if (!data)
+        return;
+    struct virQEMUEbpfOBjectData *object = data;
+    g_free(object->ebpfData);
+    g_free(data);
+}
 
 static virClass *virQEMUCapsClass;
 static void virQEMUCapsDispose(void *obj);
@@ -836,6 +852,19 @@ const char *virQEMUCapsArchToString(virArch arch)
 }
 
 
+const void *
+virQEMUCapsGetEbpf(virQEMUCaps *qemuCaps, const char *id, size_t *size)
+{
+    struct virQEMUEbpfOBjectData *object = virHashLookup(qemuCaps->ebpfObjects, id);
+
+    if (!object)
+        return NULL;
+
+    *size = object->ebpfSize;
+    return object->ebpfData;
+}
+
+
 /* Checks whether a domain with @guest arch can run natively on @host.
  */
 bool
@@ -1426,6 +1455,7 @@ static struct virQEMUCapsDevicePropsFlags virQEMUCapsDevicePropsVirtioBlk[] = {
 static struct virQEMUCapsDevicePropsFlags virQEMUCapsDevicePropsVirtioNet[] = {
     { "acpi-index", QEMU_CAPS_ACPI_INDEX, NULL },
     { "rss", QEMU_CAPS_VIRTIO_NET_RSS, NULL },
+    { "ebpf_rss_fds", QEMU_CAPS_VIRTIO_NET_EBPF_RSS_FDS, NULL },
 };
 
 static struct virQEMUCapsDevicePropsFlags virQEMUCapsDevicePropsPCIeRootPort[] = {
@@ -1804,6 +1834,8 @@ virQEMUCapsNew(void)
     qemuCaps->invalidation = true;
     qemuCaps->flags = virBitmapNew(QEMU_CAPS_LAST);
 
+    qemuCaps->ebpfObjects = virHashNew(virQEMUEbpfOBjectDataDestroy);
+
     return qemuCaps;
 }
 
@@ -1984,6 +2016,8 @@ virQEMUCaps *virQEMUCapsNewCopy(virQEMUCaps *qemuCaps)
     ret->hypervCapabilities = g_memdup(qemuCaps->hypervCapabilities,
                                        sizeof(virDomainCapsFeatureHyperv));
 
+    ret->ebpfObjects = g_hash_table_ref(qemuCaps->ebpfObjects);
+
     return g_steal_pointer(&ret);
 }
 
@@ -2026,6 +2060,8 @@ void virQEMUCapsDispose(void *obj)
 
     g_free(qemuCaps->hypervCapabilities);
 
+    g_hash_table_unref(qemuCaps->ebpfObjects);
+
     virQEMUCapsAccelClear(&qemuCaps->kvm);
     virQEMUCapsAccelClear(&qemuCaps->hvf);
     virQEMUCapsAccelClear(&qemuCaps->tcg);
@@ -4541,6 +4577,46 @@ virQEMUCapsValidateArch(virQEMUCaps *qemuCaps, xmlXPathContextPtr ctxt)
 }
 
 
+static int
+virQEMUCapsParseEbpfObjects(virQEMUCaps *qemuCaps, xmlXPathContextPtr ctxt)
+{
+    g_autofree xmlNodePtr *nodes = NULL;
+    size_t i;
+    int n;
+
+    if ((n = virXPathNodeSet("./ebpf", ctxt, &nodes)) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("failed to parse qemu cached eBPF object"));
+        return -1;
+    }
+
+    for (i = 0; i < n; i++) {
+        g_autofree char *id = NULL;
+        g_autofree char *ebpf = NULL;
+        struct virQEMUEbpfOBjectData *ebpfObject = NULL;
+
+        if (!(id = virXMLPropString(nodes[i], "id"))) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("missing eBPF object ID in QEMU capabilities cache"));
+            return -1;
+        }
+
+        if (!(ebpf = virXMLNodeContentString(nodes[i]))) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s %s",
+                           _("can't get eBPF base64 data from cache for ID: "), id);
+            return -1;
+        }
+
+        ebpfObject = g_malloc(sizeof(*ebpfObject));
+        ebpfObject->ebpfData = g_base64_decode(ebpf, &ebpfObject->ebpfSize);
+
+        virHashAddEntry(qemuCaps->ebpfObjects, id, ebpfObject);
+    }
+
+    return 0;
+}
+
+
 /*
  * Parsing a doc that looks like
  *
@@ -4688,6 +4764,8 @@ virQEMUCapsLoadCache(virArch hostArch,
     if (skipInvalidation)
         qemuCaps->invalidation = false;
 
+    virQEMUCapsParseEbpfObjects(qemuCaps, ctxt);
+
     return 0;
 }
 
@@ -4925,6 +5003,32 @@ virQEMUCapsFormatHypervCapabilities(virQEMUCaps *qemuCaps,
 }
 
 
+static int
+virQEMUCapsFormatEbpfObjectsIterator(void *payload, const char *name, void *opaque)
+{
+    virBuffer *buf = opaque;
+    struct virQEMUEbpfOBjectData *object = payload;
+    g_autofree char *objectBase64 = NULL;
+
+    if (!buf || !object)
+        return 0;
+
+    objectBase64 = g_base64_encode(object->ebpfData, object->ebpfSize);
+    if (!objectBase64)
+        return 0;
+
+    virBufferAsprintf(buf, "<ebpf id='%s'>\n", name);
+    virBufferAdjustIndent(buf, 2);
+
+    virBufferAddStr(buf, objectBase64);
+    virBufferAddLit(buf, "\n");
+
+    virBufferAdjustIndent(buf, -2);
+    virBufferAddLit(buf, "</ebpf>\n");
+
+    return 0;
+}
+
 char *
 virQEMUCapsFormatCache(virQEMUCaps *qemuCaps)
 {
@@ -5015,6 +5119,8 @@ virQEMUCapsFormatCache(virQEMUCaps *qemuCaps)
     if (qemuCaps->kvmSupportsSecureGuest)
         virBufferAddLit(&buf, "<kvmSupportsSecureGuest/>\n");
 
+    virHashForEach(qemuCaps->ebpfObjects, virQEMUCapsFormatEbpfObjectsIterator, &buf);
+
     virBufferAdjustIndent(&buf, -2);
     virBufferAddLit(&buf, "</qemuCaps>\n");
 
@@ -5437,6 +5543,79 @@ virQEMUCapsInitProcessCaps(virQEMUCaps *qemuCaps)
 }
 
 
+static int
+virQEMUCapsProbeQMPEbpfObject(virQEMUCaps *qemuCaps, const char *id,
+                          qemuMonitor *mon)
+{
+    void *ebpfObject = NULL;
+    size_t size = 0;
+
+    if (!id)
+        return -1;
+
+    ebpfObject = qemuMonitorGetEbpf(mon, id, &size);
+    if(ebpfObject == NULL)
+        return -1;
+
+    struct virQEMUEbpfOBjectData *object = g_malloc(sizeof(*object));
+    object->ebpfData = ebpfObject;
+    object->ebpfSize = size;
+
+    virHashAddEntry(qemuCaps->ebpfObjects, id, object);
+
+    return 0;
+}
+
+static void virQEMUCapsProbeQMPSchemaEbpf(virQEMUCaps *qemuCaps, GHashTable* schema, qemuMonitor *mon) {
+    if (schema == NULL)
+        return;
+
+    virJSONValue *requestSchema = virHashLookup(schema, "request-ebpf");
+    if (!requestSchema)
+        return;
+
+    const char *argType = virJSONValueObjectGetString(requestSchema, "arg-type");
+    if (!argType)
+        return;
+
+    virJSONValue *argSchema = virHashLookup(schema, argType);
+    if (!argSchema)
+        return;
+
+    /* Get member id type*/
+    virJSONValue *members = virJSONValueObjectGetArray(argSchema, "members");
+    if (!members)
+        return;
+
+    /* Find "id" type */
+    virJSONValue *idSchema = NULL;
+    for (size_t i = 0; (idSchema = virJSONValueArrayGet(members, i)) != NULL; ++i) {
+        const char *name = virJSONValueObjectGetString(idSchema, "name");
+        if (strncmp(name, "id", 3) == 0)
+            break;
+    }
+
+    if (!idSchema)
+        return;
+
+    const char *ebpfIds = virJSONValueObjectGetString(idSchema, "type");
+    virJSONValue *ebpfIdsSchema = virHashLookup(schema, ebpfIds);
+    if (!ebpfIdsSchema)
+        return;
+
+    virJSONValue *ebpfIdsArray = virJSONValueObjectGetArray(ebpfIdsSchema, "values");
+    if (!ebpfIdsArray)
+        return;
+
+    /* Try to request every eBPF */
+    virJSONValue *id = NULL;
+    for (size_t i = 0; (id = virJSONValueArrayGet(ebpfIdsArray, i)) != NULL; ++i) {
+        const char *name = virJSONValueGetString(id);
+        virQEMUCapsProbeQMPEbpfObject(qemuCaps, name, mon);
+    }
+}
+
+
 static int
 virQEMUCapsProbeQMPSchemaCapabilities(virQEMUCaps *qemuCaps,
                                       qemuMonitor *mon)
@@ -5466,6 +5645,8 @@ virQEMUCapsProbeQMPSchemaCapabilities(virQEMUCaps *qemuCaps,
             virQEMUCapsSet(qemuCaps, cmd->flag);
     }
 
+    virQEMUCapsProbeQMPSchemaEbpf(qemuCaps, schema, mon);
+
     return 0;
 }
 
diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h
index 3c4f7f625b..164dc003d0 100644
--- a/src/qemu/qemu_capabilities.h
+++ b/src/qemu/qemu_capabilities.h
@@ -677,6 +677,7 @@ typedef enum { /* virQEMUCapsFlags grouping marker for syntax-check */
     /* 450 */
     QEMU_CAPS_RUN_WITH_ASYNC_TEARDOWN, /* asynchronous teardown -run-with async-teardown=on|off */
     QEMU_CAPS_DEVICE_VIRTIO_BLK_VHOST_VDPA, /* virtio-blk-vhost-vdpa block driver */
+    QEMU_CAPS_VIRTIO_NET_EBPF_RSS_FDS, /* virtio-net ebpf_rss_fds feature */
 
     QEMU_CAPS_LAST /* this must always be the last item */
 } virQEMUCapsFlags;
@@ -895,3 +896,6 @@ int
 virQEMUCapsProbeQMPMachineTypes(virQEMUCaps *qemuCaps,
                                 virDomainVirtType virtType,
                                 qemuMonitor *mon);
+
+const void *
+virQEMUCapsGetEbpf(virQEMUCaps *qemuCaps, const char *id, size_t *size);
-- 
2.42.0



More information about the libvir-list mailing list