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(a)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)
+{
+ 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;
+
+ 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)
+{
+ virNodeDevCapMdevPtr srcmdev;
+ virNodeDevCapMdevPtr dstmdev;
+
+ if (dst->caps->data.type != VIR_NODE_DEV_CAP_MDEV)
+ return;
+
+ 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));
+
virNodeDeviceObjEndAPI(&obj);
new_device = false;
}
@@ -1953,6 +1999,8 @@ nodeStateInitializeEnumerate(void *opaque)
/* Populate with known devices */
if (udevEnumerateDevices(udev) != 0)
goto error;
+ if (nodeDeviceUpdateMediatedDevices() != 0)
+ goto error;
nodeDeviceLock();
driver->initialized = true;
--
2.26.2