From nobody Mon Feb 9 23:39:33 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of redhat.com designates 63.128.21.124 as permitted sender) client-ip=63.128.21.124; envelope-from=libvir-list-bounces@redhat.com; helo=us-smtp-delivery-124.mimecast.com; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of redhat.com designates 63.128.21.124 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1608819322; cv=none; d=zohomail.com; s=zohoarc; b=UYaEmetfBMHFDjW8Bgg7dkDWK0GFGrI1RVzeQj3ki8J0q8m342dUb5k47QRm7qamvssUONEEavERnGqmB2aR3rGk656LYHN9SDJKzOSJ5qxfpslCeX7+0DiDwUPCX1VmChy6pNjTIhqbzOkJsOgcIXCaM5mMqqwet1uTV/Dp/0g= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1608819322; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=Ao4Tb+tjv3WxuZBqcDy/EUd3f17q5/uMmni64J/Dy4c=; b=SAqsohZquIymE1cU0ZqIh3ISfBA4uWm9rOe7m6jv7hysV9aRnfBjhelJxAAhKvw4jxnrAtdP/+4TpOfMCPAH9Qug87129eZ2TxSxpRtD1lQYrkFHKpgtguFb3wLccQgJnfJ3siRNMunpxQgM7s7nTwwwPK0gXp5/pyg1OmqCoGA= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of redhat.com designates 63.128.21.124 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass header.from= (p=none dis=none) header.from= Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [63.128.21.124]) by mx.zohomail.com with SMTPS id 160881932208893.07629235557943; Thu, 24 Dec 2020 06:15:22 -0800 (PST) Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-336-PeLAU9h4OF2Mpd0tz4oLAw-1; Thu, 24 Dec 2020 09:15:18 -0500 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 23042190D34A; Thu, 24 Dec 2020 14:15:13 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.21]) by smtp.corp.redhat.com (Postfix) with ESMTPS id A594660C47; Thu, 24 Dec 2020 14:15:12 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id 721865002C; Thu, 24 Dec 2020 14:15:12 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id 0BOEEqjU005640 for ; Thu, 24 Dec 2020 09:14:52 -0500 Received: by smtp.corp.redhat.com (Postfix) id 1F8AA57; Thu, 24 Dec 2020 14:14:52 +0000 (UTC) Received: from himantopus.redhat.com (ovpn-112-15.phx2.redhat.com [10.3.112.15]) by smtp.corp.redhat.com (Postfix) with ESMTPS id C1ED25C1A3; Thu, 24 Dec 2020 14:14:51 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1608819320; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references:list-id:list-help: list-unsubscribe:list-subscribe:list-post; bh=Ao4Tb+tjv3WxuZBqcDy/EUd3f17q5/uMmni64J/Dy4c=; b=HYw172bU01qumKfNm7r/uQtcbCr4g1awSAdA+eZ7BwNXKhQ1FCy3ygsg4VuQP+febMEK+6 /CLOgb6QcaJtqDQlrLI+dgoS1VX0+lITVuOBeKjt83xuaJoZZB2SErXXz2+0KCe560/EzD BLwxW5XefK4jJ/Ur+XLkdCDzBCGHvTI= X-MC-Unique: PeLAU9h4OF2Mpd0tz4oLAw-1 From: Jonathon Jongsma To: libvir-list@redhat.com Subject: [libvirt PATCH v3 05/21] nodedev: add ability to list and parse defined mdevs Date: Thu, 24 Dec 2020 08:14:29 -0600 Message-Id: <20201224141445.163819-6-jjongsma@redhat.com> In-Reply-To: <20201224141445.163819-1-jjongsma@redhat.com> References: <20201224141445.163819-1-jjongsma@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 X-loop: libvir-list@redhat.com Cc: eskultet@redhat.com X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=libvir-list-bounces@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @redhat.com) Content-Type: text/plain; charset="utf-8" This adds some internal API to query for persistent mediated devices that are defined by mdevctl. Following commits will make use of this information. This just provides the infrastructure and tests for this feature. One test verifies that we are executing mdevctl with the proper arguments, and the other test verifies that we can parse the returned JSON and convert it to our own internal node device representations. Signed-off-by: Jonathon Jongsma Reviewed-by: Erik Skultety --- src/node_device/node_device_driver.c | 150 ++++++++++++++++++ src/node_device/node_device_driver.h | 7 + .../mdevctl-list-defined.argv | 1 + .../mdevctl-list-multiple.json | 59 +++++++ .../mdevctl-list-multiple.out.xml | 39 +++++ tests/nodedevmdevctltest.c | 95 ++++++++++- 6 files changed, 349 insertions(+), 2 deletions(-) create mode 100644 tests/nodedevmdevctldata/mdevctl-list-defined.argv create mode 100644 tests/nodedevmdevctldata/mdevctl-list-multiple.json create mode 100644 tests/nodedevmdevctldata/mdevctl-list-multiple.out.xml diff --git a/src/node_device/node_device_driver.c b/src/node_device/node_de= vice_driver.c index 6143459618..bbd373e32e 100644 --- a/src/node_device/node_device_driver.c +++ b/src/node_device/node_device_driver.c @@ -853,6 +853,156 @@ virMdevctlStop(virNodeDeviceDefPtr def) } =20 =20 +virCommandPtr +nodeDeviceGetMdevctlListCommand(bool defined, + char **output) +{ + virCommandPtr cmd =3D virCommandNewArgList(MDEVCTL, + "list", + "--dumpjson", + NULL); + + if (defined) + virCommandAddArg(cmd, "--defined"); + + virCommandSetOutputBuffer(cmd, output); + + return cmd; +} + + +static void mdevGenerateDeviceName(virNodeDeviceDefPtr dev) +{ + nodeDeviceGenerateName(dev, "mdev", dev->caps->data.mdev.uuid, NULL); +} + + +static virNodeDeviceDefPtr +nodeDeviceParseMdevctlChildDevice(const char *parent, + virJSONValuePtr json) +{ + virNodeDevCapMdevPtr mdev; + const char *uuid; + virJSONValuePtr props, attrs; + g_autoptr(virNodeDeviceDef) child =3D g_new0(virNodeDeviceDef, 1); + + /* the child object should have a single key equal to its uuid. + * The value is an object describing the properties of the mdev */ + if (virJSONValueObjectKeysNumber(json) !=3D 1) + return NULL; + + uuid =3D virJSONValueObjectGetKey(json, 0); + props =3D virJSONValueObjectGetValue(json, 0); + + child->parent =3D g_strdup(parent); + child->caps =3D g_new0(virNodeDevCapsDef, 1); + child->caps->data.type =3D VIR_NODE_DEV_CAP_MDEV; + + mdev =3D &child->caps->data.mdev; + mdev->uuid =3D g_strdup(uuid); + mdev->type =3D + g_strdup(virJSONValueObjectGetString(props, "mdev_type")); + + attrs =3D virJSONValueObjectGet(props, "attrs"); + + if (attrs && virJSONValueIsArray(attrs)) { + size_t i; + int nattrs =3D virJSONValueArraySize(attrs); + + mdev->attributes =3D g_new0(virMediatedDeviceAttrPtr, nattrs); + mdev->nattributes =3D nattrs; + + for (i =3D 0; i < nattrs; i++) { + virJSONValuePtr attr =3D virJSONValueArrayGet(attrs, i); + virMediatedDeviceAttrPtr attribute; + virJSONValuePtr value; + + if (!virJSONValueIsObject(attr) || + virJSONValueObjectKeysNumber(attr) !=3D 1) + return NULL; + + attribute =3D g_new0(virMediatedDeviceAttr, 1); + attribute->name =3D g_strdup(virJSONValueObjectGetKey(attr, 0)= ); + value =3D virJSONValueObjectGetValue(attr, 0); + attribute->value =3D g_strdup(virJSONValueGetString(value)); + mdev->attributes[i] =3D attribute; + } + } + mdevGenerateDeviceName(child); + + return g_steal_pointer(&child); +} + + +int +nodeDeviceParseMdevctlJSON(const char *jsonstring, + virNodeDeviceDefPtr **devs) +{ + int n; + g_autoptr(virJSONValue) json_devicelist =3D NULL; + virNodeDeviceDefPtr *outdevs =3D NULL; + size_t noutdevs =3D 0; + size_t i, j; + + json_devicelist =3D virJSONValueFromString(jsonstring); + + if (!json_devicelist) + goto parsefailure; + + if (!virJSONValueIsArray(json_devicelist)) + goto parsefailure; + + n =3D virJSONValueArraySize(json_devicelist); + + for (i =3D 0; i < n; i++) { + virJSONValuePtr obj =3D virJSONValueArrayGet(json_devicelist, i); + const char *parent; + virJSONValuePtr child_array; + int nchildren; + + if (!virJSONValueIsObject(obj)) + goto parsefailure; + + /* mdevctl returns an array of objects. Each object is a parent d= evice + * object containing a single key-value pair which maps from the n= ame + * of the parent device to an array of child devices */ + if (virJSONValueObjectKeysNumber(obj) !=3D 1) + goto parsefailure; + + parent =3D virJSONValueObjectGetKey(obj, 0); + child_array =3D virJSONValueObjectGetValue(obj, 0); + + if (!virJSONValueIsArray(child_array)) + goto parsefailure; + + nchildren =3D virJSONValueArraySize(child_array); + + for (j =3D 0; j < nchildren; j++) { + g_autoptr(virNodeDeviceDef) child =3D NULL; + virJSONValuePtr child_obj =3D virJSONValueArrayGet(child_array= , j); + + if (!(child =3D nodeDeviceParseMdevctlChildDevice(parent, chil= d_obj))) + goto parsefailure; + + if (VIR_APPEND_ELEMENT(outdevs, noutdevs, child) < 0) + goto parsefailure; + } + } + + *devs =3D outdevs; + return noutdevs; + + parsefailure: + for (i =3D 0; i < noutdevs; i++) + virNodeDeviceDefFree(outdevs[i]); + VIR_FREE(outdevs); + + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("unable to parse JSON response")); + return -1; +} + + int nodeDeviceDestroy(virNodeDevicePtr device) { diff --git a/src/node_device/node_device_driver.h b/src/node_device/node_de= vice_driver.h index 02baf56dab..80ac7c5320 100644 --- a/src/node_device/node_device_driver.h +++ b/src/node_device/node_device_driver.h @@ -119,6 +119,13 @@ nodeDeviceGetMdevctlStartCommand(virNodeDeviceDefPtr d= ef, virCommandPtr nodeDeviceGetMdevctlStopCommand(const char *uuid); =20 +virCommandPtr +nodeDeviceGetMdevctlListCommand(bool defined, char **output); + +int +nodeDeviceParseMdevctlJSON(const char *jsonstring, + virNodeDeviceDefPtr **devs); + void nodeDeviceGenerateName(virNodeDeviceDefPtr def, const char *subsystem, diff --git a/tests/nodedevmdevctldata/mdevctl-list-defined.argv b/tests/nod= edevmdevctldata/mdevctl-list-defined.argv new file mode 100644 index 0000000000..72b5906e9e --- /dev/null +++ b/tests/nodedevmdevctldata/mdevctl-list-defined.argv @@ -0,0 +1 @@ +$MDEVCTL_BINARY$ list --dumpjson --defined diff --git a/tests/nodedevmdevctldata/mdevctl-list-multiple.json b/tests/no= dedevmdevctldata/mdevctl-list-multiple.json new file mode 100644 index 0000000000..eefcd90c62 --- /dev/null +++ b/tests/nodedevmdevctldata/mdevctl-list-multiple.json @@ -0,0 +1,59 @@ +[ + { + "0000:00:02.0": [ + { + "200f228a-c80a-4d50-bfb7-f5a0e4e34045": { + "mdev_type": "i915-GVTg_V5_4", + "start": "manual" + } + }, + { + "de807ffc-1923-4d5f-b6c9-b20ecebc6d4b": { + "mdev_type": "i915-GVTg_V5_4", + "start": "auto" + } + }, + { + "435722ea-5f43-468a-874f-da34f1217f13": { + "mdev_type": "i915-GVTg_V5_8", + "start": "manual", + "attrs": [ + { + "testattr": "42" + } + ] + } + } + ] + }, + { + "matrix": [ + { "783e6dbb-ea0e-411f-94e2-717eaad438bf": { + "mdev_type": "vfio_ap-passthrough", + "start": "manual", + "attrs": [ + { + "assign_adapter": "5" + }, + { + "assign_adapter": "6" + }, + { + "assign_domain": "0xab" + }, + { + "assign_control_domain": "0xab" + }, + { + "assign_domain": "4" + }, + { + "assign_control_domain": "4" + } + ] + } + } + ] + } +] + diff --git a/tests/nodedevmdevctldata/mdevctl-list-multiple.out.xml b/tests= /nodedevmdevctldata/mdevctl-list-multiple.out.xml new file mode 100644 index 0000000000..543ad916b7 --- /dev/null +++ b/tests/nodedevmdevctldata/mdevctl-list-multiple.out.xml @@ -0,0 +1,39 @@ + + mdev_200f228a_c80a_4d50_bfb7_f5a0e4e34045 + 0000:00:02.0 + + + + + + + mdev_de807ffc_1923_4d5f_b6c9_b20ecebc6d4b + 0000:00:02.0 + + + + + + + mdev_435722ea_5f43_468a_874f_da34f1217f13 + 0000:00:02.0 + + + + + + + + mdev_783e6dbb_ea0e_411f_94e2_717eaad438bf + matrix + + + + + + + + + + + diff --git a/tests/nodedevmdevctltest.c b/tests/nodedevmdevctltest.c index 1d3cb00400..8bc464d59f 100644 --- a/tests/nodedevmdevctltest.c +++ b/tests/nodedevmdevctltest.c @@ -143,6 +143,87 @@ testMdevctlStop(const void *data) return ret; } =20 +static int +testMdevctlListDefined(const void *data G_GNUC_UNUSED) +{ + virBuffer buf =3D VIR_BUFFER_INITIALIZER; + const char *actualCmdline =3D NULL; + int ret =3D -1; + g_autoptr(virCommand) cmd =3D NULL; + g_autofree char *output =3D NULL; + g_autofree char *cmdlinefile =3D + g_strdup_printf("%s/nodedevmdevctldata/mdevctl-list-defined.argv", + abs_srcdir); + + cmd =3D nodeDeviceGetMdevctlListCommand(true, &output); + + if (!cmd) + goto cleanup; + + virCommandSetDryRun(&buf, NULL, NULL); + if (virCommandRun(cmd, NULL) < 0) + goto cleanup; + + if (!(actualCmdline =3D virBufferCurrentContent(&buf))) + goto cleanup; + + if (nodedevCompareToFile(actualCmdline, cmdlinefile) < 0) + goto cleanup; + + ret =3D 0; + + cleanup: + virBufferFreeAndReset(&buf); + virCommandSetDryRun(NULL, NULL, NULL); + return ret; +} + +static int +testMdevctlParse(const void *data) +{ + g_autofree char *buf =3D NULL; + const char *filename =3D data; + g_autofree char *jsonfile =3D g_strdup_printf("%s/nodedevmdevctldata/%= s.json", + abs_srcdir, filename); + g_autofree char *xmloutfile =3D g_strdup_printf("%s/nodedevmdevctldata= /%s.out.xml", + abs_srcdir, filename); + int ret =3D -1; + int nmdevs =3D 0; + virNodeDeviceDefPtr *mdevs =3D NULL; + virBuffer xmloutbuf =3D VIR_BUFFER_INITIALIZER; + size_t i; + + if (virFileReadAll(jsonfile, 1024*1024, &buf) < 0) { + VIR_TEST_DEBUG("Unable to read file %s", jsonfile); + return -1; + } + + if ((nmdevs =3D nodeDeviceParseMdevctlJSON(buf, &mdevs)) < 0) { + VIR_TEST_DEBUG("Unable to parse json for %s", filename); + return -1; + } + + for (i =3D 0; i < nmdevs; i++) { + g_autofree char *devxml =3D virNodeDeviceDefFormat(mdevs[i]); + if (!devxml) + goto cleanup; + virBufferAddStr(&xmloutbuf, devxml); + } + + if (nodedevCompareToFile(virBufferCurrentContent(&xmloutbuf), xmloutfi= le) < 0) + goto cleanup; + + ret =3D 0; + + cleanup: + virBufferFreeAndReset(&xmloutbuf); + for (i =3D 0; i < nmdevs; i++) + virNodeDeviceDefFree(mdevs[i]); + g_free(mdevs); + + return ret; +} + static void nodedevTestDriverFree(virNodeDeviceDriverStatePtr drv) { @@ -263,13 +344,13 @@ mymain(void) } =20 #define DO_TEST_FULL(desc, func, info) \ - if (virTestRun(desc, func, &info) < 0) \ + if (virTestRun(desc, func, info) < 0) \ ret =3D -1; =20 #define DO_TEST_START_FULL(virt_type, create, filename) \ do { \ struct startTestInfo info =3D { virt_type, create, filename }; \ - DO_TEST_FULL("mdevctl start " filename, testMdevctlStartHelper, in= fo); \ + DO_TEST_FULL("mdevctl start " filename, testMdevctlStartHelper, &i= nfo); \ } \ while (0) =20 @@ -279,6 +360,12 @@ mymain(void) #define DO_TEST_STOP(uuid) \ DO_TEST_FULL("mdevctl stop " uuid, testMdevctlStop, uuid) =20 +#define DO_TEST_LIST_DEFINED() \ + DO_TEST_FULL("mdevctl list --defined", testMdevctlListDefined, NULL) + +#define DO_TEST_PARSE_JSON(filename) \ + DO_TEST_FULL("parse mdevctl json " filename, testMdevctlParse, filenam= e) + /* Test mdevctl start commands */ DO_TEST_START("mdev_d069d019_36ea_4111_8f0a_8c9a70e21366"); DO_TEST_START("mdev_fedc4916_1ca8_49ac_b176_871d16c13076"); @@ -287,6 +374,10 @@ mymain(void) /* Test mdevctl stop command, pass an arbitrary uuid */ DO_TEST_STOP("e2451f73-c95b-4124-b900-e008af37c576"); =20 + DO_TEST_LIST_DEFINED(); + + DO_TEST_PARSE_JSON("mdevctl-list-multiple"); + done: nodedevTestDriverFree(driver); =20 --=20 2.26.2