diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c index 364bb489bd..d5bfc098b2 100644 --- a/src/conf/node_device_conf.c +++ b/src/conf/node_device_conf.c @@ -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__ */ diff --git a/src/conf/node_device_conf.h b/src/conf/node_device_conf.h index a234b4147b..21622c62ac 100644 --- a/src/conf/node_device_conf.h +++ b/src/conf/node_device_conf.h @@ -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); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 8b8d0c7566..ac2802095e 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -870,6 +870,7 @@ virNodeDeviceDefParseNode; virNodeDeviceDefParseString; virNodeDeviceGetAPMatrixDynamicCaps; virNodeDeviceGetCSSDynamicCaps; +virNodeDeviceGetMdevParentDynamicCaps; virNodeDeviceGetPCIDynamicCaps; virNodeDeviceGetSCSIHostCaps; virNodeDeviceGetSCSITargetCaps; @@ -2736,6 +2737,7 @@ virMediatedDeviceListStealIndex; virMediatedDeviceModelTypeFromString; virMediatedDeviceModelTypeToString; virMediatedDeviceNew; +virMediatedDeviceParentGetAddress; virMediatedDeviceSetUsedBy; virMediatedDeviceTypeFree; virMediatedDeviceTypeReadAttrs; diff --git a/src/node_device/node_device_driver.c b/src/node_device/node_device_driver.c index 0178ae8b66..fa3cfcf24c 100644 --- a/src/node_device/node_device_driver.c +++ b/src/node_device/node_device_driver.c @@ -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: diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c index 3d69bdedae..e76c1a36b8 100644 --- a/src/node_device/node_device_udev.c +++ b/src/node_device/node_device_udev.c @@ -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: diff --git a/src/util/virmdev.c b/src/util/virmdev.c index eb566982b1..41d4cef8b9 100644 --- a/src/util/virmdev.c +++ b/src/util/virmdev.c @@ -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 diff --git a/src/util/virmdev.h b/src/util/virmdev.h index 4d3655a091..bc8306d0e1 100644 --- a/src/util/virmdev.h +++ b/src/util/virmdev.h @@ -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);