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 | 43 ++++++++++++++++
3 files changed, 122 insertions(+)
diff --git a/src/node_device/node_device_driver.c b/src/node_device/node_device_driver.c
index b47ecba10a..349426757e 100644
--- a/src/node_device/node_device_driver.c
+++ b/src/node_device/node_device_driver.c
@@ -1088,3 +1088,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
+mdevctlEnumerateDevices(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];
+
+ 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 */
+ bool new_device = true;
+ 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 e7317fd67a..b5c1da4ff3 100644
--- a/src/node_device/node_device_driver.h
+++ b/src/node_device/node_device_driver.h
@@ -131,6 +131,9 @@ int
nodeDeviceParseMdevctlJSON(const char *jsonstring,
virNodeDeviceDefPtr **devs);
+int
+mdevctlEnumerateDevices(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 ac7986bc70..e06584a3dc 100644
--- a/src/node_device/node_device_udev.c
+++ b/src/node_device/node_device_udev.c
@@ -1306,6 +1306,45 @@ 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;
+
+ if (VIR_ALLOC_N(ret, nattrs) < 0)
+ return NULL;
+
+ 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 = &src->caps->data.mdev;
+ virNodeDevCapMdevPtr 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)
@@ -1341,6 +1380,8 @@ udevAddOneDevice(struct udev_device *device)
goto cleanup;
if ((obj = virNodeDeviceObjListFindByName(driver->devs, def->name))) {
+ nodeDeviceDefCopyExtraData(def, virNodeDeviceObjGetDef(obj));
+
virNodeDeviceObjEndAPI(&obj);
new_device = false;
}
@@ -1773,6 +1814,8 @@ nodeStateInitializeEnumerate(void *opaque)
/* Populate with known devices */
if (udevEnumerateDevices(udev) != 0)
goto error;
+ if (mdevctlEnumerateDevices() != 0)
+ goto error;
nodeDeviceLock();
driver->initialized = true;
--
2.26.2