[libvirt PATCH v2 05/16] nodedev: add ability to list and parse defined mdevs
Erik Skultety
eskultet at redhat.com
Fri Aug 21 11:52:21 UTC 2020
On Tue, Aug 18, 2020 at 09:47:55AM -0500, Jonathon Jongsma wrote:
> 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 other tests verify that we can parse the returned JSON
> and convert it to our own internal node device representations.
>
> Signed-off-by: Jonathon Jongsma <jjongsma at redhat.com>
> ---
> src/node_device/node_device_driver.c | 156 ++++++++++++++++++
> src/node_device/node_device_driver.h | 13 ++
> src/node_device/node_device_udev.c | 19 +--
> .../mdevctl-list-defined.argv | 1 +
> .../mdevctl-list-multiple-parents.json | 59 +++++++
> .../mdevctl-list-multiple-parents.out.xml | 39 +++++
> .../mdevctl-list-multiple.json | 59 +++++++
> .../mdevctl-list-multiple.out.xml | 39 +++++
I see literally zero difference between the respective file pairs ^above. Was
that intentional or they should have tested something else?
> .../mdevctl-list-single-noattr.json | 11 ++
> .../mdevctl-list-single-noattr.out.xml | 8 +
> .../mdevctl-list-single.json | 31 ++++
> .../mdevctl-list-single.out.xml | 14 ++
I'm all for testing, but I feel like ^these single device use cases are both
covered with the multiple ones introduced above, since those include
devices both with attributes as well as without them.
...
> diff --git a/src/node_device/node_device_driver.c b/src/node_device/node_device_driver.c
> index f766fd9f32..3b042e9a45 100644
> --- a/src/node_device/node_device_driver.c
> +++ b/src/node_device/node_device_driver.c
> @@ -812,6 +812,137 @@ virMdevctlStop(virNodeDeviceDefPtr def)
> }
>
>
> +virCommandPtr
> +nodeDeviceGetMdevctlListCommand(bool defined,
> + char **output)
> +{
> + virCommandPtr cmd = virCommandNewArgList(MDEVCTL,
> + "list",
> + "--dumpjson",
> + NULL);
> +
> + if (defined)
> + virCommandAddArg(cmd, "--defined");
> +
> + virCommandSetOutputBuffer(cmd, output);
> +
> + return cmd;
> +}
> +
> +
> +static void mdevGenerateDeviceName(virNodeDeviceDefPtr dev)
^this name generator code movement should IMO be a standalone patch.
> +{
> + nodeDeviceGenerateName(dev, "mdev", dev->caps->data.mdev.uuid, NULL);
> +}
> +
^2 blank lines between functions.
> +int
> +nodeDeviceParseMdevctlJSON(const char *jsonstring,
> + virNodeDeviceDefPtr **devs)
> +{
> + int n;
> + g_autoptr(virJSONValue) json_devicelist = NULL;
> + virNodeDeviceDefPtr *outdevs = NULL;
> + size_t noutdevs = 0;
> + size_t i, j, k, m;
> +
> + json_devicelist = virJSONValueFromString(jsonstring);
> +
> + if (!json_devicelist)
> + goto parsefailure;
> +
> + if (!virJSONValueIsArray(json_devicelist))
> + goto parsefailure;
> +
> + n = virJSONValueArraySize(json_devicelist);
given the following JSON:
[
{
"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"
}
]
}
}
]
}
]
isn't 'n' == 'nparents'? Asking because right now I see O(n^4) complexity and
I'd very much like to optimize it.
> +
> + for (i = 0; i < n; i++) {
> + virJSONValuePtr obj = virJSONValueArrayGet(json_devicelist, i);
> + int nparents;
> +
> + if (!virJSONValueIsObject(obj))
> + goto parsefailure;
> +
> + nparents = virJSONValueObjectKeysNumber(obj);
> +
> + for (j = 0; j < nparents; j++) {
> + const char *parent = virJSONValueObjectGetKey(obj, j);
> + virJSONValuePtr child_array = virJSONValueObjectGetValue(obj, j);
> + int nchildren;
> +
> + if (!virJSONValueIsArray(child_array))
> + goto parsefailure;
> +
> + nchildren = virJSONValueArraySize(child_array);
> +
> + for (k = 0; k < nchildren; k++) {
> + virNodeDevCapMdevPtr mdev;
> + const char *uuid;
> + virJSONValuePtr props;
> + g_autoptr(virNodeDeviceDef) child = g_new0(virNodeDeviceDef, 1);
> + virJSONValuePtr child_obj = virJSONValueArrayGet(child_array, k);
> +
> + /* 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(child_obj) != 1)
> + goto parsefailure;
> +
> + uuid = virJSONValueObjectGetKey(child_obj, 0);
> + props = virJSONValueObjectGetValue(child_obj, 0);
> +
> + child->parent = g_strdup(parent);
> + child->caps = g_new0(virNodeDevCapsDef, 1);
> + child->caps->data.type = VIR_NODE_DEV_CAP_MDEV;
> +
> + mdev = &child->caps->data.mdev;
> + mdev->uuid = g_strdup(uuid);
> + mdev->type =
> + g_strdup(virJSONValueObjectGetString(props, "mdev_type"));
> +
> + virJSONValuePtr attrs = virJSONValueObjectGet(props, "attrs");
> +
> + if (attrs && virJSONValueIsArray(attrs)) {
> + int nattrs = virJSONValueArraySize(attrs);
> +
> + mdev->attributes = g_new0(virMediatedDeviceAttrPtr, nattrs);
> + mdev->nattributes = nattrs;
> +
> + for (m = 0; m < nattrs; m++) {
> + virJSONValuePtr attr = virJSONValueArrayGet(attrs, m);
> + virMediatedDeviceAttrPtr attribute;
> +
> + if (!virJSONValueIsObject(attr) ||
> + virJSONValueObjectKeysNumber(attr) != 1)
> + goto parsefailure;
> +
> + attribute = g_new0(virMediatedDeviceAttr, 1);
> + attribute->name = g_strdup(virJSONValueObjectGetKey(attr, 0));
> + virJSONValuePtr value = virJSONValueObjectGetValue(attr, 0);
> + attribute->value = g_strdup(virJSONValueGetString(value));
> + mdev->attributes[m] = attribute;
> + }
> + }
> + mdevGenerateDeviceName(child);
> +
> + if (VIR_APPEND_ELEMENT(outdevs, noutdevs, child) < 0)
> + child = NULL;
> + }
> + }
> + }
> +
> + *devs = outdevs;
> + return noutdevs;
> +
> + parsefailure:
> + for (i = 0; i < noutdevs; i++)
> + virNodeDeviceDefFree(outdevs[i]);
> + VIR_FREE(outdevs);
> +
> + virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> + _("unable to parse JSON response"));
> + return -1;
> +}
...
> static void
> nodedevTestDriverFree(virNodeDeviceDriverStatePtr drv)
> {
> @@ -284,6 +366,15 @@ mymain(void)
> #define DO_TEST_STOP(uuid) \
> DO_TEST_FULL("mdevctl stop " uuid, testMdevctlStop, uuid)
>
> +#define DO_TEST_LIST_DEFINED() \
> + do { \
> + if (virTestRun("mdevctl list --defined", testMdevctlListDefined, NULL) < 0) \
How about we redefine the DO_TEST_FULL macro so that it doesn't pass a
reference to info on its own but forces the caller to do that? That way you
don't have to do ^this and simply pass NULL safely to DO_TEST_FULL.
Erik
More information about the libvir-list
mailing list