Messages in this thread Patch in this message | | | From | Nicolin Chen <> | Subject | [PATCH v5 2/4] iommufd: Add IOMMUFD_CMD_SET_DEV_DATA and IOMMUFD_CMD_UNSET_DEV_DATA | Date | Thu, 27 Jul 2023 14:09:41 -0700 |
| |
Add a new pair of ioctls to allow user space to set and unset its iommu- specific device data at the iommu that a passthrough device is behind.
On platforms with SMMUv3, this new uAPIs will be used to forward a user space virtual Stream ID of a passthrough device to link to its physical Stream ID and log into a lookup table, in order for the host kernel to later run sanity on ATC invalidation requests from the user space, with ATC_INV commands that have SID fields (virtual Stream IDs).
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com> --- drivers/iommu/iommufd/device.c | 2 + drivers/iommu/iommufd/iommufd_private.h | 1 + drivers/iommu/iommufd/main.c | 85 +++++++++++++++++++++++++ include/uapi/linux/iommufd.h | 32 ++++++++++ 4 files changed, 120 insertions(+)
diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c index 7f238c583b61..37a234371645 100644 --- a/drivers/iommu/iommufd/device.c +++ b/drivers/iommu/iommufd/device.c @@ -136,6 +136,8 @@ void iommufd_device_destroy(struct iommufd_object *obj) struct iommufd_device *idev = container_of(obj, struct iommufd_device, obj); + if (idev->has_user_data) + dev_iommu_ops(idev->dev)->unset_dev_user_data(idev->dev); iommu_device_release_dma_owner(idev->dev); iommufd_put_group(idev->igroup); if (!iommufd_selftest_is_mock_dev(idev->dev)) diff --git a/drivers/iommu/iommufd/iommufd_private.h b/drivers/iommu/iommufd/iommufd_private.h index 9ae6edea697f..b8abf664d3f8 100644 --- a/drivers/iommu/iommufd/iommufd_private.h +++ b/drivers/iommu/iommufd/iommufd_private.h @@ -313,6 +313,7 @@ struct iommufd_device { /* always the physical device */ struct device *dev; bool enforce_cache_coherency; + bool has_user_data; }; static inline struct iommufd_device * diff --git a/drivers/iommu/iommufd/main.c b/drivers/iommu/iommufd/main.c index 255e8a3c5b0e..d49837397dfa 100644 --- a/drivers/iommu/iommufd/main.c +++ b/drivers/iommu/iommufd/main.c @@ -387,6 +387,85 @@ static int iommufd_option(struct iommufd_ucmd *ucmd) return 0; } +static int iommufd_set_dev_data(struct iommufd_ucmd *ucmd) +{ + struct iommu_set_dev_data *cmd = ucmd->cmd; + struct iommufd_device *idev; + const struct iommu_ops *ops; + void *data = NULL; + int rc; + + if (!cmd->data_uptr || !cmd->data_len) + return -EINVAL; + + idev = iommufd_get_device(ucmd, cmd->dev_id); + if (IS_ERR(idev)) + return PTR_ERR(idev); + + mutex_lock(&idev->igroup->lock); + if (idev->has_user_data) { + rc = -EEXIST; + goto out_unlock; + } + + ops = dev_iommu_ops(idev->dev); + if (!ops->dev_user_data_len || + !ops->set_dev_user_data || + !ops->unset_dev_user_data) { + rc = -EOPNOTSUPP; + goto out_unlock; + } + + data = kzalloc(ops->dev_user_data_len, GFP_KERNEL); + if (!data) { + rc = -ENOMEM; + goto out_unlock; + } + + if (copy_struct_from_user(data, ops->dev_user_data_len, + u64_to_user_ptr(cmd->data_uptr), + cmd->data_len)) { + rc = -EFAULT; + goto out_free_data; + } + + rc = ops->set_dev_user_data(idev->dev, data); + if (rc) + goto out_free_data; + + idev->has_user_data = true; +out_free_data: + kfree(data); +out_unlock: + mutex_unlock(&idev->igroup->lock); + iommufd_put_object(&idev->obj); + return rc; +} + +static int iommufd_unset_dev_data(struct iommufd_ucmd *ucmd) +{ + struct iommu_unset_dev_data *cmd = ucmd->cmd; + struct iommufd_device *idev; + int rc = 0; + + idev = iommufd_get_device(ucmd, cmd->dev_id); + if (IS_ERR(idev)) + return PTR_ERR(idev); + + mutex_lock(&idev->igroup->lock); + if (!idev->has_user_data) { + rc = -ENOENT; + goto out_unlock; + } + + dev_iommu_ops(idev->dev)->unset_dev_user_data(idev->dev); + idev->has_user_data = false; +out_unlock: + mutex_unlock(&idev->igroup->lock); + iommufd_put_object(&idev->obj); + return rc; +} + union ucmd_buffer { struct iommu_destroy destroy; struct iommu_hw_info info; @@ -400,6 +479,8 @@ union ucmd_buffer { struct iommu_ioas_unmap unmap; struct iommu_option option; struct iommu_resv_iova_ranges resv_ranges; + struct iommu_set_dev_data set_dev_data; + struct iommu_unset_dev_data unset_dev_data; struct iommu_vfio_ioas vfio_ioas; #ifdef CONFIG_IOMMUFD_TEST struct iommu_test_cmd test; @@ -446,6 +527,10 @@ static const struct iommufd_ioctl_op iommufd_ioctl_ops[] = { val64), IOCTL_OP(IOMMU_RESV_IOVA_RANGES, iommufd_resv_iova_ranges, struct iommu_resv_iova_ranges, resv_iovas), + IOCTL_OP(IOMMU_SET_DEV_DATA, iommufd_set_dev_data, + struct iommu_set_dev_data, data_len), + IOCTL_OP(IOMMU_UNSET_DEV_DATA, iommufd_unset_dev_data, + struct iommu_unset_dev_data, dev_id), IOCTL_OP(IOMMU_VFIO_IOAS, iommufd_vfio_ioas, struct iommu_vfio_ioas, __reserved), #ifdef CONFIG_IOMMUFD_TEST diff --git a/include/uapi/linux/iommufd.h b/include/uapi/linux/iommufd.h index 034da283cd3a..ede822e5acbb 100644 --- a/include/uapi/linux/iommufd.h +++ b/include/uapi/linux/iommufd.h @@ -49,6 +49,8 @@ enum { IOMMUFD_CMD_GET_HW_INFO, IOMMUFD_CMD_RESV_IOVA_RANGES, IOMMUFD_CMD_HWPT_INVALIDATE, + IOMMUFD_CMD_SET_DEV_DATA, + IOMMUFD_CMD_UNSET_DEV_DATA, }; /** @@ -508,4 +510,34 @@ struct iommu_hwpt_invalidate { __aligned_u64 data_uptr; }; #define IOMMU_HWPT_INVALIDATE _IO(IOMMUFD_TYPE, IOMMUFD_CMD_HWPT_INVALIDATE) + +/** + * struct iommu_set_dev_data - ioctl(IOMMU_SET_DEV_DATA) + * @size: sizeof(struct iommu_set_dev_data) + * @dev_id: The device to set an iommu specific device data + * @data_uptr: User pointer of the device user data + * @data_len: Length of the device user data + * + * The device data must be unset using ioctl(IOMMU_UNSET_DEV_DATA), before + * another ioctl(IOMMU_SET_DEV_DATA) call or before the device itself gets + * unbind'd from the iommufd context. + */ +struct iommu_set_dev_data { + __u32 size; + __u32 dev_id; + __aligned_u64 data_uptr; + __u32 data_len; +}; +#define IOMMU_SET_DEV_DATA _IO(IOMMUFD_TYPE, IOMMUFD_CMD_SET_DEV_DATA) + +/** + * struct iommu_unset_dev_data - ioctl(IOMMU_UNSET_DEV_DATA) + * @size: sizeof(struct iommu_unset_dev_data) + * @dev_id: The device to unset its device user data + */ +struct iommu_unset_dev_data { + __u32 size; + __u32 dev_id; +}; +#define IOMMU_UNSET_DEV_DATA _IO(IOMMUFD_TYPE, IOMMUFD_CMD_UNSET_DEV_DATA) #endif -- 2.41.0
| |