[libvirt PATCH v3 07/21] nodedev: add mdevctl devices to node device list
Erik Skultety
eskultet at redhat.com
Wed Jan 6 10:39:11 UTC 2021
On Wed, Jan 06, 2021 at 11:27:09AM +0100, Erik Skultety wrote:
> On Thu, Dec 24, 2020 at 08:14:31AM -0600, Jonathon Jongsma wrote:
> > At startup, query devices that are defined by 'mdevctl' and add them to
> > the node device list.
> >
> > This adds a complication: we now have two potential sources of
> > information for a node device:
> > - udev for all devices and for activated mediated devices
> > - mdevctl for persistent mediated devices
> >
> > Unfortunately, neither backend returns full information for a mediated
> > device. For example, if a persistent mediated device in the list (with
> > information provided from mdevctl) is 'started', that same device will
> > now be detected by udev. If we simply overwrite the existing device
> > definition with the new one provided by the udev backend, we will lose
> > extra information that was provided by mdevctl (e.g. attributes, etc).
> > To avoid this, make sure to copy the extra information into the new
> > device definition.
> >
> > Signed-off-by: Jonathon Jongsma <jjongsma at redhat.com>
> > ---
> > src/node_device/node_device_driver.c | 76 ++++++++++++++++++++++++++++
> > src/node_device/node_device_driver.h | 3 ++
> > src/node_device/node_device_udev.c | 48 ++++++++++++++++++
> > 3 files changed, 127 insertions(+)
> >
> > diff --git a/src/node_device/node_device_driver.c b/src/node_device/node_device_driver.c
> > index 5309b8abd5..0267005af1 100644
> > --- a/src/node_device/node_device_driver.c
> > +++ b/src/node_device/node_device_driver.c
> > @@ -1144,3 +1144,79 @@ nodeDeviceGenerateName(virNodeDeviceDefPtr def,
> > *(def->name + i) = '_';
> > }
> > }
> > +
> > +
> > +static int
> > +virMdevctlListDefined(virNodeDeviceDefPtr **devs)
> > +{
> > + int status;
> > + g_autofree char *output = NULL;
> > + g_autoptr(virCommand) cmd = nodeDeviceGetMdevctlListCommand(true, &output);
> > +
> > + if (virCommandRun(cmd, &status) < 0)
> > + return -1;
> > +
> > + if (!output)
> > + return -1;
> > +
> > + return nodeDeviceParseMdevctlJSON(output, devs);
> > +}
> > +
> > +
> > +int
> > +nodeDeviceUpdateMediatedDevices(void)
>
> ^This was called mdevctlEnumerateDevices in v2, so I'm wondering why did you
> change the name? I think it was okay the way it was (I double checked that I
> didn't suggest this change in v2 by accident).
>
> > +{
> > + g_autofree virNodeDeviceDefPtr *devs = NULL;
> > + int ndevs;
> > + size_t i;
> > +
> > + if ((ndevs = virMdevctlListDefined(&devs)) < 0) {
> > + virReportSystemError(errno, "%s",
> > + _("failed to query mdevs from mdevctl"));
> > + return -1;
> > + }
> > +
> > + for (i = 0; i < ndevs; i++) {
> > + virNodeDeviceObjPtr obj;
> > + virObjectEventPtr event;
> > + virNodeDeviceDefPtr dev = devs[i];
> > + bool new_device = true;
> > +
> > + dev->driver = g_strdup("vfio_mdev");
> > +
> > + /* If a device defined by mdevctl is already in the list, that means
> > + * that it was found via the normal device discovery process and thus
> > + * is already activated. Active devices contain some additional
> > + * information (e.g. sysfs path) that is not provided by mdevctl, so
> > + * preserve that info */
> > + if ((obj = virNodeDeviceObjListFindByName(driver->devs, dev->name))) {
> > + virNodeDeviceDefPtr olddef = virNodeDeviceObjGetDef(obj);
> > +
> > + /* Copy any data from the existing device */
> > + dev->sysfs_path = g_strdup(olddef->sysfs_path);
> > + dev->parent_sysfs_path = g_strdup(olddef->parent_sysfs_path);
> > + dev->driver = g_strdup(olddef->driver);
> > + dev->devnode = g_strdup(olddef->devnode);
> > + dev->caps->data.mdev.iommuGroupNumber = olddef->caps->data.mdev.iommuGroupNumber;
>
> So, what data other than attributes are coming from mdevctl that we have to
> copy the udev provided data from the old def ^this way?
> When I look at virNodeDeviceDef, If I'm not missing anything, the added value
> from mdevctl are the caps, more specifically, attributes right? If that is true
> I think we should revert the logic and use the function that you introduce
> at the end of the patch that copies extra data and use it here as well.
> To describe my thoughts into more details, at the end of the patch you add
> calls to udevAddOneDevice and nodeStateInitializeEnumerate.
>
> In the former case, we already enumerated the devices, including mdevctl, so
> now when we start an mdev, we get an event from udev and what do we do? We take
> that info and copy the attributes from the existing definition to it, so
> the data flows more or less like udev <- mdevctl.
> In the latter case though, we first enumerated udev devices, then asked mdevctl
> which created a new def ptr for us and we copied the data from udev and then
> redefined the device, so mdevctl <- udev.
> If I'm not missing some crucial information I think the logic can be reverted
> in one of those cases so that we always copy from the same source to the same
> destination to apply a new definition.
>
> > +
> > + virNodeDeviceObjEndAPI(&obj);
> > + new_device = false;
> > + }
> > +
> > + if (!(obj = virNodeDeviceObjListAssignDef(driver->devs, dev))) {
> > + virNodeDeviceDefFree(dev);
> > + return -1;
> > + }
> > +
> > + if (new_device)
> > + event = virNodeDeviceEventLifecycleNew(dev->name,
> > + VIR_NODE_DEVICE_EVENT_CREATED,
> > + 0);
> > + else
> > + event = virNodeDeviceEventUpdateNew(dev->name);
> > + virNodeDeviceObjEndAPI(&obj);
> > + virObjectEventStateQueue(driver->nodeDeviceEventState, event);
> > + }
> > +
> > + return 0;
> > +}
> > diff --git a/src/node_device/node_device_driver.h b/src/node_device/node_device_driver.h
> > index 80ac7c5320..4315f6d6ed 100644
> > --- a/src/node_device/node_device_driver.h
> > +++ b/src/node_device/node_device_driver.h
> > @@ -126,6 +126,9 @@ int
> > nodeDeviceParseMdevctlJSON(const char *jsonstring,
> > virNodeDeviceDefPtr **devs);
> >
> > +int
> > +nodeDeviceUpdateMediatedDevices(void);
> > +
> > void
> > nodeDeviceGenerateName(virNodeDeviceDefPtr def,
> > const char *subsystem,
> > diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c
> > index 632413d046..223ee5a2ff 100644
> > --- a/src/node_device/node_device_udev.c
> > +++ b/src/node_device/node_device_udev.c
> > @@ -1494,6 +1494,50 @@ udevSetParent(struct udev_device *device,
> > return 0;
> > }
> >
> > +static virMediatedDeviceAttrPtr *
> > +virMediatedDeviceAttrsCopy(virMediatedDeviceAttrPtr *attrs,
> > + size_t nattrs)
> > +{
> > + size_t i;
> > + size_t j = 0;
> > + g_autofree virMediatedDeviceAttrPtr *ret = NULL;
> > +
> > + if (nattrs == 0)
> > + return NULL;
> > +
> > + ret = g_new0(virMediatedDeviceAttrPtr, nattrs);
> > +
> > + for (i = 0; i < nattrs; i++) {
> > + virMediatedDeviceAttrPtr attr = virMediatedDeviceAttrNew();
> > + attr->name = g_strdup(attrs[i]->name);
> > + attr->value = g_strdup(attrs[i]->value);
> > + VIR_APPEND_ELEMENT_INPLACE(ret, j, attr);
> > + }
> > +
> > + return g_steal_pointer(ret);
> > +}
> > +
> > +/* An existing device definition may have additional info from mdevctl that is
> > + * not available from udev. Transfer this data to the new definition */
> > +static void
> > +nodeDeviceDefCopyExtraData(virNodeDeviceDefPtr dst,
> > + virNodeDeviceDefPtr src)
>
> ^This name is too vague, we're likely going to only use it with mdevs (and if
> the time comes we need to make it generic, we can easily create a wrapper).
> I'd suggest nodeDeviceDefCopyFromMdevctl or something similar to make it clear
> at first glance what we're trying to copy, because extra data is basically
> "void *".
>
> > +{
> > + virNodeDevCapMdevPtr srcmdev;
> > + virNodeDevCapMdevPtr dstmdev;
> > +
> > + if (dst->caps->data.type != VIR_NODE_DEV_CAP_MDEV)
> > + return;
>
> I think ^this check would be better on the caller side since we're tailoring it
> to mdevs.
>
> > +
> > + srcmdev = &src->caps->data.mdev;
> > + dstmdev = &dst->caps->data.mdev;
> > +
> > + dstmdev->persistent = srcmdev->persistent;
> > + dstmdev->nattributes = srcmdev->nattributes;
> > + dstmdev->attributes = virMediatedDeviceAttrsCopy(srcmdev->attributes,
> > + srcmdev->nattributes);
> > +}
> > +
> >
> > static int
> > udevAddOneDevice(struct udev_device *device)
> > @@ -1527,6 +1571,8 @@ udevAddOneDevice(struct udev_device *device)
> > goto cleanup;
> >
> > if ((obj = virNodeDeviceObjListFindByName(driver->devs, def->name))) {
> > + nodeDeviceDefCopyExtraData(def, virNodeDeviceObjGetDef(obj));
>
> ...the check mentioned above should come here, it'll help the code readability
> instantly to signal that we're not trying to change/copy anything unless the
> device for which udev generated an event is an mdev.
>
> Erik
>
> > +
> > virNodeDeviceObjEndAPI(&obj);
> > new_device = false;
> > }
> > @@ -1953,6 +1999,8 @@ nodeStateInitializeEnumerate(void *opaque)
> > /* Populate with known devices */
> > if (udevEnumerateDevices(udev) != 0)
> > goto error;
Also, I think a one line commentary explaining why we're explicitly
updating/enumerating mdevs from mdevctl would be nice too, something like
"we don't all the data about mdevs from udev, we query mdevctl for the rest"
Erik
> > + if (nodeDeviceUpdateMediatedDevices() != 0)
> > + goto error;
> >
> > nodeDeviceLock();
> > driver->initialized = true;
> > --
> > 2.26.2
> >
>
More information about the libvir-list
mailing list