[RFC PATCH v3 2/6] qemu_capabilities: Added logic for retrieving eBPF objects from QEMU.

Andrew Melnychenko posted 6 patches 6 months, 2 weeks ago
There is a newer version of this series
[RFC PATCH v3 2/6] qemu_capabilities: Added logic for retrieving eBPF objects from QEMU.
Posted by Andrew Melnychenko 6 months, 2 weeks ago
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@daynix.com>
---
 src/qemu/qemu_capabilities.c | 122 +++++++++++++++++++++++++++++++++++
 src/qemu/qemu_capabilities.h |   3 +
 2 files changed, 125 insertions(+)

diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
index 45525db803..09bb6ca36e 100644
--- a/src/qemu/qemu_capabilities.c
+++ b/src/qemu/qemu_capabilities.c
@@ -799,6 +799,9 @@ struct _virQEMUCaps {
     virQEMUCapsAccel kvm;
     virQEMUCapsAccel hvf;
     virQEMUCapsAccel tcg;
+
+    /* Hash of ebpf objects encoded in base64 */
+    GHashTable *ebpfObjects;
 };
 
 struct virQEMUCapsSearchData {
@@ -846,6 +849,13 @@ const char *virQEMUCapsArchToString(virArch arch)
 }
 
 
+const char *
+virQEMUCapsGetEbpf(virQEMUCaps *qemuCaps, const char *id)
+{
+    return virHashLookup(qemuCaps->ebpfObjects, id);
+}
+
+
 /* Checks whether a domain with @guest arch can run natively on @host.
  */
 bool
@@ -1823,6 +1833,8 @@ virQEMUCapsNew(void)
     qemuCaps->invalidation = true;
     qemuCaps->flags = virBitmapNew(QEMU_CAPS_LAST);
 
+    qemuCaps->ebpfObjects = virHashNew(g_free);
+
     return qemuCaps;
 }
 
@@ -1965,6 +1977,9 @@ virQEMUCaps *virQEMUCapsNewCopy(virQEMUCaps *qemuCaps)
 {
     g_autoptr(virQEMUCaps) ret = virQEMUCapsNewBinary(qemuCaps->binary);
     size_t i;
+    GHashTableIter iter;
+    const char *key;
+    const char *value;
 
     ret->invalidation = qemuCaps->invalidation;
     ret->kvmSupportsNesting = qemuCaps->kvmSupportsNesting;
@@ -2003,6 +2018,12 @@ virQEMUCaps *virQEMUCapsNewCopy(virQEMUCaps *qemuCaps)
     ret->hypervCapabilities = g_memdup(qemuCaps->hypervCapabilities,
                                        sizeof(virDomainCapsFeatureHyperv));
 
+    ret->ebpfObjects = virHashNew(g_free);
+    g_hash_table_iter_init(&iter, qemuCaps->ebpfObjects);
+    while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &value)) {
+        g_hash_table_insert(ret->ebpfObjects, g_strdup(key), g_strdup(value));
+    }
+
     return g_steal_pointer(&ret);
 }
 
@@ -2045,6 +2066,8 @@ void virQEMUCapsDispose(void *obj)
 
     g_free(qemuCaps->hypervCapabilities);
 
+    g_hash_table_destroy(qemuCaps->ebpfObjects);
+
     virQEMUCapsAccelClear(&qemuCaps->kvm);
     virQEMUCapsAccelClear(&qemuCaps->hvf);
     virQEMUCapsAccelClear(&qemuCaps->tcg);
@@ -4560,6 +4583,40 @@ 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/object", 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;
+
+        if (!(id = virXMLPropStringRequired(nodes[i], "id")))
+            return -1;
+
+        if (!(ebpf = virXMLPropStringRequired(nodes[i], "data")))
+            return -1;
+
+        if (virHashAddEntry(qemuCaps->ebpfObjects, id, ebpf) < 0)
+            return -1;
+
+        /* steal the ebpf if it was added to the hash without issues */
+        g_steal_pointer(&ebpf);
+    }
+
+    return 0;
+}
+
+
 /*
  * Parsing a doc that looks like
  *
@@ -4707,6 +4764,9 @@ virQEMUCapsLoadCache(virArch hostArch,
     if (skipInvalidation)
         qemuCaps->invalidation = false;
 
+    if (virQEMUCapsParseEbpfObjects(qemuCaps, ctxt) < 0)
+        return -1;
+
     return 0;
 }
 
@@ -4944,6 +5004,16 @@ virQEMUCapsFormatHypervCapabilities(virQEMUCaps *qemuCaps,
 }
 
 
+static int
+virQEMUCapsFormatEbpfObjectsIterator(void *payload, const char *name, void *opaque)
+{
+    virBuffer *buf = opaque;
+
+    virBufferAsprintf(buf, "<object id='%s' data='%s'/>\n", name, (const char *)payload);
+
+    return 0;
+}
+
 char *
 virQEMUCapsFormatCache(virQEMUCaps *qemuCaps)
 {
@@ -5034,6 +5104,14 @@ virQEMUCapsFormatCache(virQEMUCaps *qemuCaps)
     if (qemuCaps->kvmSupportsSecureGuest)
         virBufferAddLit(&buf, "<kvmSupportsSecureGuest/>\n");
 
+    if (virHashSize(qemuCaps->ebpfObjects) > 0) {
+        virBufferAddLit(&buf, "<ebpf>\n");
+        virBufferAdjustIndent(&buf, 2);
+        virHashForEachSorted(qemuCaps->ebpfObjects, virQEMUCapsFormatEbpfObjectsIterator, &buf);
+        virBufferAdjustIndent(&buf, -2);
+        virBufferAddLit(&buf, "</ebpf>\n");
+    }
+
     virBufferAdjustIndent(&buf, -2);
     virBufferAddLit(&buf, "</qemuCaps>\n");
 
@@ -5456,6 +5534,47 @@ virQEMUCapsInitProcessCaps(virQEMUCaps *qemuCaps)
 }
 
 
+static int
+virQEMUCapsProbeQMPEbpfObject(virQEMUCaps *qemuCaps, const char *id, qemuMonitor *mon)
+{
+    const char *ebpfObject = NULL;
+
+    ebpfObject = qemuMonitorGetEbpf(mon, id);
+    if (ebpfObject == NULL)
+        return -1;
+
+    return virHashAddEntry(qemuCaps->ebpfObjects, id, (void *)ebpfObject);
+}
+
+
+static int
+virQEMUCapsProbeQMPSchemaEbpf(virQEMUCaps *qemuCaps, GHashTable *schema, qemuMonitor *mon)
+{
+    virJSONValue *ebpfIdsArray;
+    virJSONValue *ebpfIdsSchema;
+    size_t i;
+
+    if (virQEMUQAPISchemaPathGet("request-ebpf/arg-type/id", schema, &ebpfIdsSchema) != 1)
+        return 0;
+
+    if (!(ebpfIdsArray = virJSONValueObjectGetArray(ebpfIdsSchema, "values"))) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("malformed QMP schema of 'request-ebpf'"));
+        return -1;
+    }
+
+    /* Try to request every eBPF */
+    for (i = 0; i < virJSONValueArraySize(ebpfIdsArray); i++) {
+        virJSONValue *id = virJSONValueArrayGet(ebpfIdsArray, i);
+
+        if (virQEMUCapsProbeQMPEbpfObject(qemuCaps, virJSONValueGetString(id), mon) < 0)
+            return -1;
+    }
+
+    return 0;
+}
+
+
 static int
 virQEMUCapsProbeQMPSchemaCapabilities(virQEMUCaps *qemuCaps,
                                       qemuMonitor *mon)
@@ -5489,6 +5608,9 @@ virQEMUCapsProbeQMPSchemaCapabilities(virQEMUCaps *qemuCaps,
         virQEMUQAPISchemaPathExists("block-stream/arg-type/backing-mask-protocol", schema))
         virQEMUCapsSet(qemuCaps, QEMU_CAPS_BLOCKJOB_BACKING_MASK_PROTOCOL);
 
+    if (virQEMUCapsProbeQMPSchemaEbpf(qemuCaps, schema, mon) < 0)
+        return -1;
+
     return 0;
 }
 
diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h
index 00b4066e9a..371ea19bd0 100644
--- a/src/qemu/qemu_capabilities.h
+++ b/src/qemu/qemu_capabilities.h
@@ -906,3 +906,6 @@ int
 virQEMUCapsProbeQMPMachineTypes(virQEMUCaps *qemuCaps,
                                 virDomainVirtType virtType,
                                 qemuMonitor *mon);
+
+const char *
+virQEMUCapsGetEbpf(virQEMUCaps *qemuCaps, const char *id);
-- 
2.44.0
_______________________________________________
Devel mailing list -- devel@lists.libvirt.org
To unsubscribe send an email to devel-leave@lists.libvirt.org
Re: [RFC PATCH v3 2/6] qemu_capabilities: Added logic for retrieving eBPF objects from QEMU.
Posted by Michal Prívozník 6 months, 1 week ago
On 5/12/24 21:45, Andrew Melnychenko wrote:
> 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@daynix.com>
> ---
>  src/qemu/qemu_capabilities.c | 122 +++++++++++++++++++++++++++++++++++
>  src/qemu/qemu_capabilities.h |   3 +
>  2 files changed, 125 insertions(+)

'meson test' fails after this one.

> 
> diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
> index 45525db803..09bb6ca36e 100644
> --- a/src/qemu/qemu_capabilities.c
> +++ b/src/qemu/qemu_capabilities.c
> @@ -799,6 +799,9 @@ struct _virQEMUCaps {
>      virQEMUCapsAccel kvm;
>      virQEMUCapsAccel hvf;
>      virQEMUCapsAccel tcg;
> +
> +    /* Hash of ebpf objects encoded in base64 */
> +    GHashTable *ebpfObjects;
>  };
>  
>  struct virQEMUCapsSearchData {
> @@ -846,6 +849,13 @@ const char *virQEMUCapsArchToString(virArch arch)
>  }
>  
>  
> +const char *
> +virQEMUCapsGetEbpf(virQEMUCaps *qemuCaps, const char *id)
> +{
> +    return virHashLookup(qemuCaps->ebpfObjects, id);
> +}
> +
> +
>  /* Checks whether a domain with @guest arch can run natively on @host.
>   */
>  bool
> @@ -1823,6 +1833,8 @@ virQEMUCapsNew(void)
>      qemuCaps->invalidation = true;
>      qemuCaps->flags = virBitmapNew(QEMU_CAPS_LAST);
>  
> +    qemuCaps->ebpfObjects = virHashNew(g_free);
> +
>      return qemuCaps;
>  }
>  
> @@ -1965,6 +1977,9 @@ virQEMUCaps *virQEMUCapsNewCopy(virQEMUCaps *qemuCaps)
>  {
>      g_autoptr(virQEMUCaps) ret = virQEMUCapsNewBinary(qemuCaps->binary);
>      size_t i;
> +    GHashTableIter iter;
> +    const char *key;
> +    const char *value;
>  
>      ret->invalidation = qemuCaps->invalidation;
>      ret->kvmSupportsNesting = qemuCaps->kvmSupportsNesting;
> @@ -2003,6 +2018,12 @@ virQEMUCaps *virQEMUCapsNewCopy(virQEMUCaps *qemuCaps)
>      ret->hypervCapabilities = g_memdup(qemuCaps->hypervCapabilities,
>                                         sizeof(virDomainCapsFeatureHyperv));
>  
> +    ret->ebpfObjects = virHashNew(g_free);
> +    g_hash_table_iter_init(&iter, qemuCaps->ebpfObjects);
> +    while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &value)) {
> +        g_hash_table_insert(ret->ebpfObjects, g_strdup(key), g_strdup(value));
> +    }
> +

I'd move this into a separate function for code estetics purpose.

>      return g_steal_pointer(&ret);
>  }

Michal