This adds ioctl for userspace to bind device cdev fd to iommufd.
VFIO_DEVICE_BIND_IOMMUFD: bind device to an iommufd, hence gain DMA
control provided by the iommufd. open_device
op is called after bind_iommufd op.
Tested-by: Nicolin Chen
Tested-by: Matthew Rosato
Tested-by: Yanting Jiang
Tested-by: Shameer Kolothum
Tested-by: Terrence Xu
Tested-by: Zhenzhong Duan
Signed-off-by: Yi Liu
---
drivers/vfio/device_cdev.c | 107 +
drivers/vfio/vfio.h| 13 +
drivers/vfio/vfio_main.c | 5 ++
include/linux/vfio.h | 5 +-
include/uapi/linux/vfio.h | 27 ++
5 files changed, 155 insertions(+), 2 deletions(-)
diff --git a/drivers/vfio/device_cdev.c b/drivers/vfio/device_cdev.c
index bf1032d00107..f40784dd5561 100644
--- a/drivers/vfio/device_cdev.c
+++ b/drivers/vfio/device_cdev.c
@@ -3,6 +3,7 @@
* Copyright (c) 2023 Intel Corporation.
*/
#include
+#include
#include "vfio.h"
@@ -45,6 +46,112 @@ int vfio_device_fops_cdev_open(struct inode *inode, struct
file *filep)
return ret;
}
+static void vfio_df_get_kvm_safe(struct vfio_device_file *df)
+{
+ spin_lock(>kvm_ref_lock);
+ vfio_device_get_kvm_safe(df->device, df->kvm);
+ spin_unlock(>kvm_ref_lock);
+}
+
+long vfio_df_ioctl_bind_iommufd(struct vfio_device_file *df,
+ struct vfio_device_bind_iommufd __user *arg)
+{
+ struct vfio_device *device = df->device;
+ struct vfio_device_bind_iommufd bind;
+ unsigned long minsz;
+ int ret;
+
+ static_assert(__same_type(arg->out_devid, df->devid));
+
+ minsz = offsetofend(struct vfio_device_bind_iommufd, out_devid);
+
+ if (copy_from_user(, arg, minsz))
+ return -EFAULT;
+
+ if (bind.argsz < minsz || bind.flags || bind.iommufd < 0)
+ return -EINVAL;
+
+ /* BIND_IOMMUFD only allowed for cdev fds */
+ if (df->group)
+ return -EINVAL;
+
+ ret = vfio_device_block_group(device);
+ if (ret)
+ return ret;
+
+ mutex_lock(>dev_set->lock);
+ /* one device cannot be bound twice */
+ if (df->access_granted) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ df->iommufd = iommufd_ctx_from_fd(bind.iommufd);
+ if (IS_ERR(df->iommufd)) {
+ ret = PTR_ERR(df->iommufd);
+ df->iommufd = NULL;
+ goto out_unlock;
+ }
+
+ /*
+* Before the device open, get the KVM pointer currently
+* associated with the device file (if there is) and obtain
+* a reference. This reference is held until device closed.
+* Save the pointer in the device for use by drivers.
+*/
+ vfio_df_get_kvm_safe(df);
+
+ ret = vfio_df_open(df);
+ if (ret)
+ goto out_put_kvm;
+
+ ret = copy_to_user(>out_devid, >devid,
+ sizeof(df->devid)) ? -EFAULT : 0;
+ if (ret)
+ goto out_close_device;
+
+ device->cdev_opened = true;
+ /*
+* Paired with smp_load_acquire() in vfio_device_fops::ioctl/
+* read/write/mmap
+*/
+ smp_store_release(>access_granted, true);
+ mutex_unlock(>dev_set->lock);
+ return 0;
+
+out_close_device:
+ vfio_df_close(df);
+out_put_kvm:
+ vfio_device_put_kvm(device);
+ iommufd_ctx_put(df->iommufd);
+ df->iommufd = NULL;
+out_unlock:
+ mutex_unlock(>dev_set->lock);
+ vfio_device_unblock_group(device);
+ return ret;
+}
+
+void vfio_df_unbind_iommufd(struct vfio_device_file *df)
+{
+ struct vfio_device *device = df->device;
+
+ /*
+* In the time of close, there is no contention with another one
+* changing this flag. So read df->access_granted without lock
+* and no smp_load_acquire() is ok.
+*/
+ if (!df->access_granted)
+ return;
+
+ mutex_lock(>dev_set->lock);
+ vfio_df_close(df);
+ vfio_device_put_kvm(device);
+ iommufd_ctx_put(df->iommufd);
+ device->cdev_opened = false;
+ mutex_unlock(>dev_set->lock);
+ vfio_device_unblock_group(device);
+}
+
static char *vfio_device_devnode(const struct device *dev, umode_t *mode)
{
return kasprintf(GFP_KERNEL, "vfio/devices/%s", dev_name(dev));
diff --git a/drivers/vfio/vfio.h b/drivers/vfio/vfio.h
index c2aa65382592..b6d4ba1ef2b8 100644
--- a/drivers/vfio/vfio.h
+++ b/drivers/vfio/vfio.h
@@ -287,6 +287,9 @@ static inline void vfio_device_del(struct vfio_device
*device)
}
int vfio_device_fops_cdev_open(struct inode *inode, struct file *filep);
+long vfio_df_ioctl_bind_iommufd(struct vfio_device_file *df,
+ struct vfio_device_bind_iommufd __user *arg);
+void vfio_df_unbind_iommufd(struct vfio_device_file *df);
int