[PATCH RFC v2 3/3] vfio: Add dev_data_len/uptr in struct vfio_device_bind_iommufd

From: Nicolin Chen
Date: Thu Apr 20 2023 - 03:49:06 EST


This allows user space to pass in an iommu specific device data with the
VFIO_DEVICE_BIND_IOMMUFD ioctl. The data is not mandatory. But it must be
provided if a non-zero data_len is passed along.

Signed-off-by: Nicolin Chen <nicolinc@xxxxxxxxxx>
---
drivers/vfio/device_cdev.c | 19 +++++++++++++++++--
drivers/vfio/iommufd.c | 13 +++++++++++++
include/linux/vfio.h | 2 ++
include/uapi/linux/vfio.h | 13 +++++++++++++
4 files changed, 45 insertions(+), 2 deletions(-)

diff --git a/drivers/vfio/device_cdev.c b/drivers/vfio/device_cdev.c
index b5824179cd48..70241d9c0fa9 100644
--- a/drivers/vfio/device_cdev.c
+++ b/drivers/vfio/device_cdev.c
@@ -104,9 +104,10 @@ static struct iommufd_ctx *vfio_get_iommufd_from_fd(int fd)
long vfio_device_ioctl_bind_iommufd(struct vfio_device_file *df,
struct vfio_device_bind_iommufd __user *arg)
{
+ uint32_t mask = VFIO_DEVICE_BIND_IOMMUFD_FLAG_DATA;
struct vfio_device *device = df->device;
struct vfio_device_bind_iommufd bind;
- unsigned long minsz;
+ unsigned long minsz, datasz;
bool is_noiommu;
int ret;

@@ -117,9 +118,23 @@ long vfio_device_ioctl_bind_iommufd(struct vfio_device_file *df,
if (copy_from_user(&bind, arg, minsz))
return -EFAULT;

- if (bind.argsz < minsz || bind.flags)
+ if (bind.argsz < minsz || bind.flags & ~mask)
return -EINVAL;

+ if (bind.flags & VFIO_DEVICE_BIND_IOMMUFD_FLAG_DATA) {
+ datasz = offsetofend(struct vfio_device_bind_iommufd,
+ dev_data_len);
+ if (bind.argsz < datasz)
+ return -EINVAL;
+ if (copy_from_user((void *)&bind + minsz,
+ (void __user *)arg + minsz, datasz - minsz))
+ return -EFAULT;
+ if (!bind.dev_data_uptr ^ !bind.dev_data_len)
+ return -EINVAL;
+ device->user_data = u64_to_user_ptr(bind.dev_data_uptr);
+ device->user_data_len = bind.dev_data_len;
+ }
+
/* BIND_IOMMUFD only allowed for cdev fds */
if (df->group)
return -EINVAL;
diff --git a/drivers/vfio/iommufd.c b/drivers/vfio/iommufd.c
index 16d6aac06180..0b985ccffcbe 100644
--- a/drivers/vfio/iommufd.c
+++ b/drivers/vfio/iommufd.c
@@ -91,6 +91,16 @@ int vfio_iommufd_physical_bind(struct vfio_device *vdev,
idev = iommufd_device_bind(ictx, vdev->dev, out_device_id);
if (IS_ERR(idev))
return PTR_ERR(idev);
+
+ if (vdev->user_data) {
+ int rc = iommufd_device_set_data(idev, vdev->user_data,
+ vdev->user_data_len);
+ if (rc) {
+ iommufd_device_unbind(idev);
+ return rc;
+ }
+ }
+
vdev->iommufd_device = idev;
return 0;
}
@@ -104,8 +114,11 @@ void vfio_iommufd_physical_unbind(struct vfio_device *vdev)
iommufd_device_detach(vdev->iommufd_device);
vdev->iommufd_attached = false;
}
+ iommufd_device_unset_data(vdev->iommufd_device);
iommufd_device_unbind(vdev->iommufd_device);
vdev->iommufd_device = NULL;
+ vdev->user_data = NULL;
+ vdev->user_data_len = 0;
}
EXPORT_SYMBOL_GPL(vfio_iommufd_physical_unbind);

diff --git a/include/linux/vfio.h b/include/linux/vfio.h
index 46b313f8bfaf..e4bb63801472 100644
--- a/include/linux/vfio.h
+++ b/include/linux/vfio.h
@@ -68,6 +68,8 @@ struct vfio_device {
#if IS_ENABLED(CONFIG_IOMMUFD)
struct iommufd_device *iommufd_device;
bool iommufd_attached;
+ void *user_data;
+ u32 user_data_len;
#endif
bool noiommu;
};
diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h
index 17c5a1dadd08..9cec823c2829 100644
--- a/include/uapi/linux/vfio.h
+++ b/include/uapi/linux/vfio.h
@@ -212,15 +212,28 @@ struct vfio_group_status {
* as long as the input @iommufd is valid. Otherwise, it is
* meaningless. devid is a handle for this device and can be
* used in IOMMUFD commands.
+ * @dev_data_uptr: User pointer of the device user data.
+ * @dev_data_len: Length of the device user data.
+ *
+ * A device user data is an iommu specific structure that must be defined in
+ * the include/uapi/linux/iommufd.h file. On some platform enabling the iommu
+ * nested translation configuration, a device behind the iommu, while working
+ * in a guest VM, needs to provide the host kernel a certain virtual ID in the
+ * guest VM. For example, ARM SMMUv3 requires a virtual Stream ID to sanity a
+ * cache invalidation command from the user space. User space wanting to pass a
+ * user data must set VFIO_DEVICE_BIND_IOMMUFD_FLAG_DATA flag.
*
* Return: 0 on success, -errno on failure.
*/
struct vfio_device_bind_iommufd {
__u32 argsz;
__u32 flags;
+#define VFIO_DEVICE_BIND_IOMMUFD_FLAG_DATA (1 << 0)
__s32 iommufd;
#define VFIO_NOIOMMU_FD (-1)
__u32 out_devid;
+ __aligned_u64 dev_data_uptr;
+ __u32 dev_data_len;
};

#define VFIO_DEVICE_BIND_IOMMUFD _IO(VFIO_TYPE, VFIO_BASE + 19)
--
2.40.0