Add a new pair of ioctls to allow user space to set and unset its iommu-
specific device data for a passthrough device that's behind the iommu.
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 | 81 +++++++++++++++++++++++++
drivers/iommu/iommufd/iommufd_private.h | 3 +
drivers/iommu/iommufd/main.c | 4 ++
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 c649a3403797..9480cd36a8bd 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 (WARN_ON(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))
@@ -726,6 +728,85 @@ void iommufd_device_detach(struct iommufd_device *idev)
}
EXPORT_SYMBOL_NS_GPL(iommufd_device_detach, IOMMUFD);
+int iommufd_device_set_data(struct iommufd_ucmd *ucmd)
+{
+ struct iommu_device_set_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;
+}
+
+int iommufd_device_unset_data(struct iommufd_ucmd *ucmd)
+{
+ struct iommu_device_unset_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;
+}
+
void iommufd_access_destroy_object(struct iommufd_object *obj)
{
struct iommufd_access *access =
diff --git a/drivers/iommu/iommufd/iommufd_private.h b/drivers/iommu/iommufd/iommufd_private.h
index 35d1294b2f06..98eaef9c41d3 100644
--- a/drivers/iommu/iommufd/iommufd_private.h
+++ b/drivers/iommu/iommufd/iommufd_private.h
@@ -309,6 +309,7 @@ struct iommufd_device {
/* always the physical device */
struct device *dev;
bool enforce_cache_coherency;
+ bool has_user_data;
};
static inline struct iommufd_device *
@@ -321,6 +322,8 @@ iommufd_get_device(struct iommufd_ucmd *ucmd, u32 id)
void iommufd_device_destroy(struct iommufd_object *obj);
int iommufd_device_get_hw_info(struct iommufd_ucmd *ucmd);
+int iommufd_device_set_data(struct iommufd_ucmd *ucmd);
+int iommufd_device_unset_data(struct iommufd_ucmd *ucmd);
struct iommufd_access {
struct iommufd_object obj;
diff --git a/drivers/iommu/iommufd/main.c b/drivers/iommu/iommufd/main.c
index b84b22487b7d..bd4753512990 100644
--- a/drivers/iommu/iommufd/main.c
+++ b/drivers/iommu/iommufd/main.c
@@ -326,6 +326,10 @@ static const struct iommufd_ioctl_op iommufd_ioctl_ops[] = {
val64),
IOCTL_OP(IOMMU_VFIO_IOAS, iommufd_vfio_ioas, struct iommu_vfio_ioas,
__reserved),
+ IOCTL_OP(IOMMU_DEVICE_SET_DATA, iommufd_device_set_data,
+ struct iommu_device_set_data, data_len),
+ IOCTL_OP(IOMMU_DEVICE_UNSET_DATA, iommufd_device_unset_data,
+ struct iommu_device_unset_data, dev_id),
#ifdef CONFIG_IOMMUFD_TEST
IOCTL_OP(IOMMU_TEST_CMD, iommufd_test, struct iommu_test_cmd, last),
#endif
diff --git a/include/uapi/linux/iommufd.h b/include/uapi/linux/iommufd.h
index ac023ea6040a..b39b0e2e46b1 100644
--- a/include/uapi/linux/iommufd.h
+++ b/include/uapi/linux/iommufd.h
@@ -51,6 +51,8 @@ enum {
IOMMUFD_CMD_HWPT_ALLOC,
IOMMUFD_CMD_DEVICE_GET_HW_INFO,
IOMMUFD_CMD_HWPT_INVALIDATE,
+ IOMMUFD_CMD_DEVICE_SET_DATA,
+ IOMMUFD_CMD_DEVICE_UNSET_DATA,
};
/**
@@ -626,4 +628,34 @@ struct iommu_hwpt_invalidate {
__aligned_u64 data_uptr;
};
#define IOMMU_HWPT_INVALIDATE _IO(IOMMUFD_TYPE, IOMMUFD_CMD_HWPT_INVALIDATE)
+
+/**
+ * struct iommu_device_set_data - ioctl(IOMMU_DEVICE_SET_DATA)
+ * @size: sizeof(struct iommu_device_set_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_DEVICE_UNSET_DATA), before
+ * another ioctl(IOMMU_DEVICE_SET_DATA) call or before the device itself gets
+ * unbind'd from the iommufd context.
+ */
+struct iommu_device_set_data {
+ __u32 size;
+ __u32 dev_id;
+ __aligned_u64 data_uptr;
+ __u32 data_len;
+};
+#define IOMMU_DEVICE_SET_DATA _IO(IOMMUFD_TYPE, IOMMUFD_CMD_DEVICE_SET_DATA)
+
+/**
+ * struct iommu_device_unset_data - ioctl(IOMMU_DEVICE_UNSET_DATA)
+ * @size: sizeof(struct iommu_device_unset_data)
+ * @dev_id: The device to unset its device user data
+ */
+struct iommu_device_unset_data {
+ __u32 size;
+ __u32 dev_id;
+};
+#define IOMMU_DEVICE_UNSET_DATA _IO(IOMMUFD_TYPE, IOMMUFD_CMD_DEVICE_UNSET_DATA)
#endif
--
2.40.0
On 4/23/23 3:40 PM, Nicolin Chen wrote: > Add a new pair of ioctls to allow user space to set and unset its iommu- > specific device data for a passthrough device that's behind the iommu. > > 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 | 81 +++++++++++++++++++++++++ > drivers/iommu/iommufd/iommufd_private.h | 3 + > drivers/iommu/iommufd/main.c | 4 ++ > 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 c649a3403797..9480cd36a8bd 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 (WARN_ON(idev->has_user_data)) > + dev_iommu_ops(idev->dev)->unset_dev_user_data(idev->dev); Do you really need this WARN_ON()? The user space application can easily trigger this kernel WARN() by setting the user data and forgetting to unset it. Best regards, baolu
Hi Baolu, On Mon, Apr 24, 2023 at 10:44:08AM +0800, Baolu Lu wrote: > On 4/23/23 3:40 PM, Nicolin Chen wrote: > > Add a new pair of ioctls to allow user space to set and unset its iommu- > > specific device data for a passthrough device that's behind the iommu. > > > > 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 | 81 +++++++++++++++++++++++++ > > drivers/iommu/iommufd/iommufd_private.h | 3 + > > drivers/iommu/iommufd/main.c | 4 ++ > > 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 c649a3403797..9480cd36a8bd 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 (WARN_ON(idev->has_user_data)) > > + dev_iommu_ops(idev->dev)->unset_dev_user_data(idev->dev); > > Do you really need this WARN_ON()? The user space application can easily > trigger this kernel WARN() by setting the user data and forgetting to > unset it. I can drop that, since it's a user triggerable one. Thanks Nic
© 2016 - 2025 Red Hat, Inc.