]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
nodedev: support 'mtty' device for testing
authorJonathon Jongsma <jjongsma@redhat.com>
Fri, 15 Jul 2022 19:10:30 +0000 (14:10 -0500)
committerJonathon Jongsma <jjongsma@redhat.com>
Wed, 10 Aug 2022 16:19:03 +0000 (11:19 -0500)
It would be nice to be able to test the mediated device capabilities
without having physical hardware which supports it. The 'mtty' kernel
module presents a virtual parent device which is capable of creating
'fake' mediated devices, and as such it would be useful for testing.

However, the 'mtty' device is not part of an existing device subsystem
(e.g. PCI, etc), so libvirt ignores it and it does not get added to the
node device list. And because it does not get added to the node device
list, it cannot be used to create child mdevs using `virsh
nodedev-create`.

There is already a node device type capability
VIR_NODE_DEV_CAP_MDEV_TYPES that indicates whether a device supports
creating child mediated devices, but libvirt assumes that this is a
nested capability (in other words, it assumes that the primary
capability of a device is something like PCI). If we allow this
MDEV_TYPES capability to be a primary device capability, then we can
support virtual devices like 'mtty' as a parent for mediated devices.

See https://bugzilla.redhat.com/show_bug.cgi?id=2107031

Signed-off-by: Jonathon Jongsma <jjongsma@redhat.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
src/conf/node_device_conf.c
src/conf/node_device_conf.h
src/libvirt_private.syms
src/node_device/node_device_driver.c
src/node_device/node_device_udev.c
src/util/virmdev.c
src/util/virmdev.h

index 364bb489bd313edd8bd07bde0780bdf6887f8873..d5bfc098b2113c159d6e5512e8703116efeb153b 100644 (file)
@@ -772,6 +772,10 @@ virNodeDeviceDefFormat(const virNodeDeviceDef *def)
                                                 data->ap_matrix.nmdev_types);
             break;
         case VIR_NODE_DEV_CAP_MDEV_TYPES:
+            virNodeDeviceCapMdevTypesFormat(&buf,
+                                            data->mdev_parent.mdev_types,
+                                            data->mdev_parent.nmdev_types);
+            break;
         case VIR_NODE_DEV_CAP_FC_HOST:
         case VIR_NODE_DEV_CAP_VPORTS:
         case VIR_NODE_DEV_CAP_VPD:
@@ -2674,6 +2678,11 @@ virNodeDevCapsDefFree(virNodeDevCapsDef *caps)
         g_free(data->ap_matrix.mdev_types);
         break;
     case VIR_NODE_DEV_CAP_MDEV_TYPES:
+        for (i = 0; i < data->mdev_parent.nmdev_types; i++)
+            virMediatedDeviceTypeFree(data->mdev_parent.mdev_types[i]);
+        g_free(data->mdev_parent.mdev_types);
+        g_free(data->mdev_parent.address);
+        break;
     case VIR_NODE_DEV_CAP_DRM:
     case VIR_NODE_DEV_CAP_FC_HOST:
     case VIR_NODE_DEV_CAP_VPORTS:
@@ -2729,6 +2738,11 @@ virNodeDeviceUpdateCaps(virNodeDeviceDef *def)
                                                     &cap->data.ap_matrix) < 0)
                 return -1;
             break;
+        case VIR_NODE_DEV_CAP_MDEV_TYPES:
+            if (virNodeDeviceGetMdevParentDynamicCaps(def->sysfs_path,
+                                                      &cap->data.mdev_parent) < 0)
+                return -1;
+            break;
 
             /* all types that (supposedly) don't require any updates
              * relative to what's in the cache.
@@ -2742,7 +2756,6 @@ virNodeDeviceUpdateCaps(virNodeDeviceDef *def)
         case VIR_NODE_DEV_CAP_FC_HOST:
         case VIR_NODE_DEV_CAP_VPORTS:
         case VIR_NODE_DEV_CAP_SCSI_GENERIC:
-        case VIR_NODE_DEV_CAP_MDEV_TYPES:
         case VIR_NODE_DEV_CAP_MDEV:
         case VIR_NODE_DEV_CAP_CCW_DEV:
         case VIR_NODE_DEV_CAP_VDPA:
@@ -3193,6 +3206,24 @@ virNodeDeviceGetAPMatrixDynamicCaps(const char *sysfsPath,
     return 0;
 }
 
+/* virNodeDeviceGetMdevParentDynamicCaps() get info that is stored in sysfs
+ * about devices related to this device, i.e. things that can change
+ * without this device itself changing. These must be refreshed
+ * anytime full XML of the device is requested, because they can
+ * change with no corresponding notification from the kernel/udev.
+ */
+int
+virNodeDeviceGetMdevParentDynamicCaps(const char *sysfsPath,
+                                      virNodeDevCapMdevParent *mdev_parent)
+{
+    if (virNodeDeviceGetMdevTypesCaps(sysfsPath,
+                                      &mdev_parent->mdev_types,
+                                      &mdev_parent->nmdev_types) < 0)
+        return -1;
+    return 0;
+}
+
+
 #else
 
 int
@@ -3229,4 +3260,12 @@ virNodeDeviceGetAPMatrixDynamicCaps(const char *sysfsPath G_GNUC_UNUSED,
     return -1;
 }
 
+int
+virNodeDeviceGetMdevParentDynamicCaps(const char *sysfsPath G_GNUC_UNUSED,
+                                      virNodeDevCapMdevParent *mdev_parent G_GNUC_UNUSED)
+{
+    return -1;
+}
+
+
 #endif /* __linux__ */
index a234b4147bdb37e29af13dab8777cda1476ecf95..21622c62acf33ae211ea38ed7b3bf3ddd5cd3a2c 100644 (file)
@@ -306,6 +306,14 @@ struct _virNodeDevCapAPMatrix {
     size_t nmdev_types;
 };
 
+
+typedef struct _virNodeDevCapMdevParent virNodeDevCapMdevParent;
+struct _virNodeDevCapMdevParent {
+    virMediatedDeviceType **mdev_types;
+    size_t nmdev_types;
+    char *address;
+};
+
 typedef struct _virNodeDevCapData virNodeDevCapData;
 struct _virNodeDevCapData {
     virNodeDevCapType type;
@@ -327,6 +335,7 @@ struct _virNodeDevCapData {
         virNodeDevCapAPCard ap_card;
         virNodeDevCapAPQueue ap_queue;
         virNodeDevCapAPMatrix ap_matrix;
+        virNodeDevCapMdevParent mdev_parent;
     };
 };
 
@@ -453,6 +462,10 @@ int
 virNodeDeviceGetAPMatrixDynamicCaps(const char *sysfsPath,
                                     virNodeDevCapAPMatrix *ap_matrix);
 
+int
+virNodeDeviceGetMdevParentDynamicCaps(const char *sysfsPath,
+                                      virNodeDevCapMdevParent *mdev_parent);
+
 int
 virNodeDeviceUpdateCaps(virNodeDeviceDef *def);
 
index 8b8d0c75667fee716c359edc8c13cce0e2ab0de4..ac2802095eba8a8b3a4c342b4e0e3c627a87f032 100644 (file)
@@ -870,6 +870,7 @@ virNodeDeviceDefParseNode;
 virNodeDeviceDefParseString;
 virNodeDeviceGetAPMatrixDynamicCaps;
 virNodeDeviceGetCSSDynamicCaps;
+virNodeDeviceGetMdevParentDynamicCaps;
 virNodeDeviceGetPCIDynamicCaps;
 virNodeDeviceGetSCSIHostCaps;
 virNodeDeviceGetSCSITargetCaps;
@@ -2736,6 +2737,7 @@ virMediatedDeviceListStealIndex;
 virMediatedDeviceModelTypeFromString;
 virMediatedDeviceModelTypeToString;
 virMediatedDeviceNew;
+virMediatedDeviceParentGetAddress;
 virMediatedDeviceSetUsedBy;
 virMediatedDeviceTypeFree;
 virMediatedDeviceTypeReadAttrs;
index 0178ae8b665a8d8f8c427d6f76302c698e621984..fa3cfcf24c6d9e54c03ab1ca48c00ace14141128 100644 (file)
@@ -672,6 +672,10 @@ nodeDeviceObjFormatAddress(virNodeDeviceObj *obj)
             addr = g_strdup(caps->data.ap_matrix.addr);
             break;
 
+        case VIR_NODE_DEV_CAP_MDEV_TYPES:
+            addr = g_strdup(caps->data.mdev_parent.address);
+            break;
+
         case VIR_NODE_DEV_CAP_SYSTEM:
         case VIR_NODE_DEV_CAP_USB_DEV:
         case VIR_NODE_DEV_CAP_USB_INTERFACE:
@@ -684,7 +688,6 @@ nodeDeviceObjFormatAddress(virNodeDeviceObj *obj)
         case VIR_NODE_DEV_CAP_VPORTS:
         case VIR_NODE_DEV_CAP_SCSI_GENERIC:
         case VIR_NODE_DEV_CAP_DRM:
-        case VIR_NODE_DEV_CAP_MDEV_TYPES:
         case VIR_NODE_DEV_CAP_MDEV:
         case VIR_NODE_DEV_CAP_CCW_DEV:
         case VIR_NODE_DEV_CAP_VDPA:
index 3d69bdedae45a3c7fc9789f377cd387861db20b2..e76c1a36b81810e414b6cfae944ca8803eb45a50 100644 (file)
@@ -457,6 +457,28 @@ udevProcessPCI(struct udev_device *device,
 }
 
 
+static int
+udevProcessMdevParent(struct udev_device *device,
+                      virNodeDeviceDef *def)
+{
+    virNodeDevCapMdevParent *mdev_parent = &def->caps->data.mdev_parent;
+
+    udevGenerateDeviceName(device, def, NULL);
+
+    if (virMediatedDeviceParentGetAddress(def->sysfs_path, &mdev_parent->address) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Unable to find address for mdev parent device '%s'"),
+                       def->name);
+        return -1;
+    }
+
+    if (virNodeDeviceGetMdevParentDynamicCaps(def->sysfs_path, mdev_parent) < 0)
+        return -1;
+
+    return 0;
+}
+
+
 static int
 drmGetMinorType(int minor)
 {
@@ -1349,6 +1371,8 @@ udevGetDeviceType(struct udev_device *device,
             *type = VIR_NODE_DEV_CAP_VDPA;
         else if (STREQ_NULLABLE(subsystem, "matrix"))
             *type = VIR_NODE_DEV_CAP_AP_MATRIX;
+        else if (STREQ_NULLABLE(subsystem, "mtty"))
+            *type = VIR_NODE_DEV_CAP_MDEV_TYPES;
 
         VIR_FREE(subsystem);
     }
@@ -1404,6 +1428,7 @@ udevGetDeviceDetails(struct udev_device *device,
     case VIR_NODE_DEV_CAP_AP_MATRIX:
         return udevProcessAPMatrix(device, def);
     case VIR_NODE_DEV_CAP_MDEV_TYPES:
+        return udevProcessMdevParent(device, def);
     case VIR_NODE_DEV_CAP_VPD:
     case VIR_NODE_DEV_CAP_SYSTEM:
     case VIR_NODE_DEV_CAP_FC_HOST:
index eb566982b19be5292c49a46870ff700e952d335e..41d4cef8b9add15c9916739fc4a9991d7a2139f6 100644 (file)
@@ -520,6 +520,34 @@ void virMediatedDeviceAttrFree(virMediatedDeviceAttr *attr)
 }
 
 
+#define MDEV_BUS_DIR "/sys/class/mdev_bus"
+
+
+int
+virMediatedDeviceParentGetAddress(const char *sysfspath,
+                                  char **address)
+{
+    g_autoptr(DIR) dir = NULL;
+    struct dirent *entry;
+    if (virDirOpen(&dir, MDEV_BUS_DIR) < 0)
+        return -1;
+
+    /* check if one of the links in /sys/class/mdev_bus/ points at the sysfs
+     * path for this device. If so, the link name is treated as the 'address'
+     * for the mdev parent */
+    while (virDirRead(dir, &entry, MDEV_BUS_DIR) > 0) {
+        g_autofree char *tmppath = g_strdup_printf("%s/%s", MDEV_BUS_DIR,
+                                                   entry->d_name);
+
+        if (virFileLinkPointsTo(tmppath, sysfspath)) {
+            *address = g_strdup(entry->d_name);
+            return 0;
+        }
+    }
+
+    return -1;
+}
+
 #ifdef __linux__
 
 ssize_t
index 4d3655a091fa831d356d4c613368cbd47ba62f7c..bc8306d0e1585e4eb773f4b9b281eb3057acbd93 100644 (file)
@@ -149,5 +149,9 @@ virMediatedDeviceGetMdevTypes(const char *sysfspath,
                               virMediatedDeviceType ***types,
                               size_t *ntypes);
 
+int
+virMediatedDeviceParentGetAddress(const char *sysfspath,
+                                  char **address);
+
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(virMediatedDevice, virMediatedDeviceFree);
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(virMediatedDeviceType, virMediatedDeviceTypeFree);