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@xxxxxxxxxx> --- 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