The uAPI handlers can call these two functions to set or unset an iommu specific device data. For example, the SMMUv3 driver would get a virtual Stream ID using this interface to tie it to the device's physical Stream ID, for sanity at the device-specific invalidation commands. Signed-off-by: Nicolin Chen <nicolinc@xxxxxxxxxx> --- drivers/iommu/iommufd/device.c | 65 ++++++++++++++++++++++++++++++++++ include/linux/iommufd.h | 4 +++ 2 files changed, 69 insertions(+) diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c index 16fdf4a16643..c4e895239486 100644 --- a/drivers/iommu/iommufd/device.c +++ b/drivers/iommu/iommufd/device.c @@ -610,6 +610,71 @@ iommufd_device_auto_get_domain(struct iommufd_device *idev, return destroy_hwpt; } +/* size of iommu specific device data, indexed by enum hw_info_type. */ +static const size_t iommufd_device_data_size[] = { + [IOMMU_HW_INFO_TYPE_NONE] = 0, + [IOMMU_HW_INFO_TYPE_INTEL_VTD] = 0, +}; + +/** + * iommufd_device_set_data - Set an iommu specific device data + * @idev: device to set data + * @data_uptr: User pointer to an iommu specific device data + * @data_len: Length of the iommu specific device user data + * + * This sets an iommu specific device data from the user space. + */ +int iommufd_device_set_data(struct iommufd_device *idev, + void __user *data_uptr, size_t data_len) +{ + const struct iommu_ops *ops = dev_iommu_ops(idev->dev); + void *data = NULL; + u32 klen = 0; + int rc; + + if (!data_uptr || !data_len) + return -EINVAL; + if (!ops->set_dev_data_user || !ops->unset_dev_data_user) + return -EOPNOTSUPP; + if (ops->hw_info_type >= ARRAY_SIZE(iommufd_device_data_size)) + return -EOPNOTSUPP; + + klen = iommufd_device_data_size[ops->hw_info_type]; + if (!klen) + return -EOPNOTSUPP; + + data = kzalloc(klen, GFP_KERNEL); + if (!data) + return -ENOMEM; + + if (copy_struct_from_user(data, klen, data_uptr, data_len)) { + rc = -EFAULT; + goto out_free_data; + } + + rc = ops->set_dev_data_user(idev->dev, data); +out_free_data: + kfree(data); + return rc; +} +EXPORT_SYMBOL_NS_GPL(iommufd_device_set_data, IOMMUFD); + +/** + * iommufd_device_unset_data - Unset a device's iommu specific data + * @idev: device to unset data + * + * This unsets an iommu specific device data that is previously set from user + * space, calling ops->unset_dev_data_user. + */ +void iommufd_device_unset_data(struct iommufd_device *idev) +{ + const struct iommu_ops *ops = dev_iommu_ops(idev->dev); + + if (ops->unset_dev_data_user) + ops->unset_dev_data_user(idev->dev); +} +EXPORT_SYMBOL_NS_GPL(iommufd_device_unset_data, IOMMUFD); + static int iommufd_device_change_pt(struct iommufd_device *idev, u32 *pt_id, attach_fn do_attach) { diff --git a/include/linux/iommufd.h b/include/linux/iommufd.h index 6752c58226d1..4b3a70faa409 100644 --- a/include/linux/iommufd.h +++ b/include/linux/iommufd.h @@ -25,6 +25,10 @@ int iommufd_device_attach(struct iommufd_device *idev, u32 *pt_id); int iommufd_device_replace(struct iommufd_device *idev, u32 *pt_id); void iommufd_device_detach(struct iommufd_device *idev); +int iommufd_device_set_data(struct iommufd_device *idev, + void __user *data_uptr, size_t data_len); +void iommufd_device_unset_data(struct iommufd_device *idev); + struct iommufd_ctx *iommufd_device_to_ictx(struct iommufd_device *idev); u32 iommufd_device_to_id(struct iommufd_device *idev); -- 2.40.0