The iommu_hw_info can output via the out_data_type field the vendor data type from a driver, but this only allows driver to report one data type.
Now, with SMMUv3 having a Tegra241 CMDQV implementation, it has two sets of types and data structs to report.
One way to support that is to use the same type field bidirectionally.
Rename "out_data_type" to simply "data_type", to allow an input for user space to request for a specific type and to get the corresponding data.
For backward compatibility, since the ioctl handler has never checked an input value, add a new IOMMU_HW_INFO_FLAG_INPUT_TYPE to switch between the old output-only field and the new bidirectional field.
Signed-off-by: Nicolin Chen nicolinc@nvidia.com --- include/uapi/linux/iommufd.h | 22 +++++++++++++++++----- drivers/iommu/iommufd/device.c | 16 +++++++++------- 2 files changed, 26 insertions(+), 12 deletions(-)
diff --git a/include/uapi/linux/iommufd.h b/include/uapi/linux/iommufd.h index 1fc546acb231..7bcd3912180a 100644 --- a/include/uapi/linux/iommufd.h +++ b/include/uapi/linux/iommufd.h @@ -628,6 +628,15 @@ enum iommufd_hw_capabilities { IOMMU_HW_CAP_PCI_PASID_PRIV = 1 << 2, };
+/** + * enum iommufd_hw_info_flags - Flags for iommu_hw_info + * @IOMMU_HW_INFO_FLAG_INPUT_TYPE: If set, @data_type carries an input type for + * user space to request for a specific info + */ +enum iommufd_hw_info_flags { + IOMMU_HW_INFO_FLAG_INPUT_TYPE = 1 << 0, +}; + /** * struct iommu_hw_info - ioctl(IOMMU_GET_HW_INFO) * @size: sizeof(struct iommu_hw_info) @@ -637,8 +646,11 @@ enum iommufd_hw_capabilities { * data that kernel supports * @data_uptr: User pointer to a user-space buffer used by the kernel to fill * the iommu type specific hardware information data - * @out_data_type: Output the iommu hardware info type as defined in the enum - * iommu_hw_info_type. + * @data_type: Output the iommu hardware info type as defined in the enum + * iommu_hw_info_type. If IOMMU_HW_INFO_FLAG_INPUT_TYPE is set, an + * input type via @data_type will be valid, requesting for the info + * data to the given type. Otherwise, any input value carried via + * @data_type will be seen as IOMMU_HW_INFO_TYPE_DEFAULT * @out_capabilities: Output the generic iommu capability info type as defined * in the enum iommu_hw_capabilities. * @out_max_pasid_log2: Output the width of PASIDs. 0 means no PASID support. @@ -657,8 +669,8 @@ enum iommufd_hw_capabilities { * user buffer is larger than the data that kernel has. Otherwise, kernel only * fills the buffer using the given length in @data_len. If the ioctl succeeds, * @data_len will be updated to the length that kernel actually supports, - * @out_data_type will be filled to decode the data filled in the buffer - * pointed by @data_uptr. Input @data_len == zero is allowed. + * @data_type will be filled to decode the data filled in the buffer pointed by + * @data_uptr. Input @data_len == zero is allowed. */ struct iommu_hw_info { __u32 size; @@ -666,7 +678,7 @@ struct iommu_hw_info { __u32 dev_id; __u32 data_len; __aligned_u64 data_uptr; - __u32 out_data_type; + __u32 data_type; __u8 out_max_pasid_log2; __u8 __reserved[3]; __aligned_u64 out_capabilities; diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c index 50337183eb1c..68e8b8e36907 100644 --- a/drivers/iommu/iommufd/device.c +++ b/drivers/iommu/iommufd/device.c @@ -1352,6 +1352,7 @@ EXPORT_SYMBOL_NS_GPL(iommufd_access_rw, "IOMMUFD");
int iommufd_get_hw_info(struct iommufd_ucmd *ucmd) { + const u32 SUPPORTED_FLAGS = IOMMU_HW_INFO_FLAG_INPUT_TYPE; struct iommu_hw_info *cmd = ucmd->cmd; void __user *user_ptr = u64_to_user_ptr(cmd->data_uptr); const struct iommu_ops *ops; @@ -1361,12 +1362,14 @@ int iommufd_get_hw_info(struct iommufd_ucmd *ucmd) void *data; int rc;
- if (cmd->flags || cmd->__reserved[0] || cmd->__reserved[1] || - cmd->__reserved[2]) + if (cmd->flags & ~SUPPORTED_FLAGS) + return -EOPNOTSUPP; + if (cmd->__reserved[0] || cmd->__reserved[1] || cmd->__reserved[2]) return -EOPNOTSUPP;
/* Clear the type field since drivers don't support a random input */ - cmd->out_data_type = IOMMU_HW_INFO_TYPE_DEFAULT; + if (!(cmd->flags & IOMMU_HW_INFO_FLAG_INPUT_TYPE)) + cmd->data_type = IOMMU_HW_INFO_TYPE_DEFAULT;
idev = iommufd_get_device(ucmd, cmd->dev_id); if (IS_ERR(idev)) @@ -1374,7 +1377,7 @@ int iommufd_get_hw_info(struct iommufd_ucmd *ucmd)
ops = dev_iommu_ops(idev->dev); if (ops->hw_info) { - data = ops->hw_info(idev->dev, &data_len, &cmd->out_data_type); + data = ops->hw_info(idev->dev, &data_len, &cmd->data_type); if (IS_ERR(data)) { rc = PTR_ERR(data); goto out_put; @@ -1384,13 +1387,12 @@ int iommufd_get_hw_info(struct iommufd_ucmd *ucmd) * drivers that have hw_info callback should have a unique * iommu_hw_info_type. */ - if (WARN_ON_ONCE(cmd->out_data_type == - IOMMU_HW_INFO_TYPE_NONE)) { + if (WARN_ON_ONCE(cmd->data_type == IOMMU_HW_INFO_TYPE_NONE)) { rc = -ENODEV; goto out_free; } } else { - cmd->out_data_type = IOMMU_HW_INFO_TYPE_NONE; + cmd->data_type = IOMMU_HW_INFO_TYPE_NONE; data_len = 0; data = NULL; }