Merge tag 'i2c-for-6.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa...
[linux-2.6-microblaze.git] / drivers / vfio / group.c
index fc75c10..610a429 100644 (file)
@@ -160,17 +160,13 @@ out_unlock:
 static void vfio_device_group_get_kvm_safe(struct vfio_device *device)
 {
        spin_lock(&device->group->kvm_ref_lock);
-       if (!device->group->kvm)
-               goto unlock;
-
-       _vfio_device_get_kvm_safe(device, device->group->kvm);
-
-unlock:
+       vfio_device_get_kvm_safe(device, device->group->kvm);
        spin_unlock(&device->group->kvm_ref_lock);
 }
 
-static int vfio_device_group_open(struct vfio_device *device)
+static int vfio_df_group_open(struct vfio_device_file *df)
 {
+       struct vfio_device *device = df->device;
        int ret;
 
        mutex_lock(&device->group->group_lock);
@@ -190,24 +186,62 @@ static int vfio_device_group_open(struct vfio_device *device)
        if (device->open_count == 0)
                vfio_device_group_get_kvm_safe(device);
 
-       ret = vfio_device_open(device, device->group->iommufd);
+       df->iommufd = device->group->iommufd;
+       if (df->iommufd && vfio_device_is_noiommu(device) && device->open_count == 0) {
+               /*
+                * Require no compat ioas to be assigned to proceed.  The basic
+                * statement is that the user cannot have done something that
+                * implies they expected translation to exist
+                */
+               if (!capable(CAP_SYS_RAWIO) ||
+                   vfio_iommufd_device_has_compat_ioas(device, df->iommufd))
+                       ret = -EPERM;
+               else
+                       ret = 0;
+               goto out_put_kvm;
+       }
 
-       if (device->open_count == 0)
-               vfio_device_put_kvm(device);
+       ret = vfio_df_open(df);
+       if (ret)
+               goto out_put_kvm;
+
+       if (df->iommufd && device->open_count == 1) {
+               ret = vfio_iommufd_compat_attach_ioas(device, df->iommufd);
+               if (ret)
+                       goto out_close_device;
+       }
+
+       /*
+        * Paired with smp_load_acquire() in vfio_device_fops::ioctl/
+        * read/write/mmap and vfio_file_has_device_access()
+        */
+       smp_store_release(&df->access_granted, true);
 
        mutex_unlock(&device->dev_set->lock);
+       mutex_unlock(&device->group->group_lock);
+       return 0;
 
+out_close_device:
+       vfio_df_close(df);
+out_put_kvm:
+       df->iommufd = NULL;
+       if (device->open_count == 0)
+               vfio_device_put_kvm(device);
+       mutex_unlock(&device->dev_set->lock);
 out_unlock:
        mutex_unlock(&device->group->group_lock);
        return ret;
 }
 
-void vfio_device_group_close(struct vfio_device *device)
+void vfio_df_group_close(struct vfio_device_file *df)
 {
+       struct vfio_device *device = df->device;
+
        mutex_lock(&device->group->group_lock);
        mutex_lock(&device->dev_set->lock);
 
-       vfio_device_close(device, device->group->iommufd);
+       vfio_df_close(df);
+       df->iommufd = NULL;
 
        if (device->open_count == 0)
                vfio_device_put_kvm(device);
@@ -218,19 +252,28 @@ void vfio_device_group_close(struct vfio_device *device)
 
 static struct file *vfio_device_open_file(struct vfio_device *device)
 {
+       struct vfio_device_file *df;
        struct file *filep;
        int ret;
 
-       ret = vfio_device_group_open(device);
-       if (ret)
+       df = vfio_allocate_device_file(device);
+       if (IS_ERR(df)) {
+               ret = PTR_ERR(df);
                goto err_out;
+       }
+
+       df->group = device->group;
+
+       ret = vfio_df_group_open(df);
+       if (ret)
+               goto err_free;
 
        /*
         * We can't use anon_inode_getfd() because we need to modify
         * the f_mode flags directly to allow more than just ioctls
         */
        filep = anon_inode_getfile("[vfio-device]", &vfio_device_fops,
-                                  device, O_RDWR);
+                                  df, O_RDWR);
        if (IS_ERR(filep)) {
                ret = PTR_ERR(filep);
                goto err_close_device;
@@ -253,7 +296,9 @@ static struct file *vfio_device_open_file(struct vfio_device *device)
        return filep;
 
 err_close_device:
-       vfio_device_group_close(device);
+       vfio_df_group_close(df);
+err_free:
+       kfree(df);
 err_out:
        return ERR_PTR(ret);
 }
@@ -357,6 +402,33 @@ static long vfio_group_fops_unl_ioctl(struct file *filep,
        }
 }
 
+int vfio_device_block_group(struct vfio_device *device)
+{
+       struct vfio_group *group = device->group;
+       int ret = 0;
+
+       mutex_lock(&group->group_lock);
+       if (group->opened_file) {
+               ret = -EBUSY;
+               goto out_unlock;
+       }
+
+       group->cdev_device_open_cnt++;
+
+out_unlock:
+       mutex_unlock(&group->group_lock);
+       return ret;
+}
+
+void vfio_device_unblock_group(struct vfio_device *device)
+{
+       struct vfio_group *group = device->group;
+
+       mutex_lock(&group->group_lock);
+       group->cdev_device_open_cnt--;
+       mutex_unlock(&group->group_lock);
+}
+
 static int vfio_group_fops_open(struct inode *inode, struct file *filep)
 {
        struct vfio_group *group =
@@ -379,6 +451,11 @@ static int vfio_group_fops_open(struct inode *inode, struct file *filep)
                goto out_unlock;
        }
 
+       if (group->cdev_device_open_cnt) {
+               ret = -EBUSY;
+               goto out_unlock;
+       }
+
        /*
         * Do we need multiple instances of the group open?  Seems not.
         */
@@ -453,6 +530,7 @@ static void vfio_group_release(struct device *dev)
        mutex_destroy(&group->device_lock);
        mutex_destroy(&group->group_lock);
        WARN_ON(group->iommu_group);
+       WARN_ON(group->cdev_device_open_cnt);
        ida_free(&vfio.group_ida, MINOR(group->dev.devt));
        kfree(group);
 }
@@ -604,16 +682,6 @@ static struct vfio_group *vfio_group_find_or_alloc(struct device *dev)
        if (!iommu_group)
                return ERR_PTR(-EINVAL);
 
-       /*
-        * VFIO always sets IOMMU_CACHE because we offer no way for userspace to
-        * restore cache coherency. It has to be checked here because it is only
-        * valid for cases where we are using iommu groups.
-        */
-       if (!device_iommu_capable(dev, IOMMU_CAP_CACHE_COHERENCY)) {
-               iommu_group_put(iommu_group);
-               return ERR_PTR(-EINVAL);
-       }
-
        mutex_lock(&vfio.group_lock);
        group = vfio_group_find_from_iommu(iommu_group);
        if (group) {
@@ -745,6 +813,15 @@ bool vfio_device_has_container(struct vfio_device *device)
        return device->group->container;
 }
 
+struct vfio_group *vfio_group_from_file(struct file *file)
+{
+       struct vfio_group *group = file->private_data;
+
+       if (file->f_op != &vfio_group_fops)
+               return NULL;
+       return group;
+}
+
 /**
  * vfio_file_iommu_group - Return the struct iommu_group for the vfio group file
  * @file: VFIO group file
@@ -755,13 +832,13 @@ bool vfio_device_has_container(struct vfio_device *device)
  */
 struct iommu_group *vfio_file_iommu_group(struct file *file)
 {
-       struct vfio_group *group = file->private_data;
+       struct vfio_group *group = vfio_group_from_file(file);
        struct iommu_group *iommu_group = NULL;
 
        if (!IS_ENABLED(CONFIG_SPAPR_TCE_IOMMU))
                return NULL;
 
-       if (!vfio_file_is_group(file))
+       if (!group)
                return NULL;
 
        mutex_lock(&group->group_lock);
@@ -775,33 +852,20 @@ struct iommu_group *vfio_file_iommu_group(struct file *file)
 EXPORT_SYMBOL_GPL(vfio_file_iommu_group);
 
 /**
- * vfio_file_is_group - True if the file is usable with VFIO aPIS
+ * vfio_file_is_group - True if the file is a vfio group file
  * @file: VFIO group file
  */
 bool vfio_file_is_group(struct file *file)
 {
-       return file->f_op == &vfio_group_fops;
+       return vfio_group_from_file(file);
 }
 EXPORT_SYMBOL_GPL(vfio_file_is_group);
 
-/**
- * vfio_file_enforced_coherent - True if the DMA associated with the VFIO file
- *        is always CPU cache coherent
- * @file: VFIO group file
- *
- * Enforced coherency means that the IOMMU ignores things like the PCIe no-snoop
- * bit in DMA transactions. A return of false indicates that the user has
- * rights to access additional instructions such as wbinvd on x86.
- */
-bool vfio_file_enforced_coherent(struct file *file)
+bool vfio_group_enforced_coherent(struct vfio_group *group)
 {
-       struct vfio_group *group = file->private_data;
        struct vfio_device *device;
        bool ret = true;
 
-       if (!vfio_file_is_group(file))
-               return true;
-
        /*
         * If the device does not have IOMMU_CAP_ENFORCE_CACHE_COHERENCY then
         * any domain later attached to it will also not support it. If the cap
@@ -819,28 +883,13 @@ bool vfio_file_enforced_coherent(struct file *file)
        mutex_unlock(&group->device_lock);
        return ret;
 }
-EXPORT_SYMBOL_GPL(vfio_file_enforced_coherent);
 
-/**
- * vfio_file_set_kvm - Link a kvm with VFIO drivers
- * @file: VFIO group file
- * @kvm: KVM to link
- *
- * When a VFIO device is first opened the KVM will be available in
- * device->kvm if one was associated with the group.
- */
-void vfio_file_set_kvm(struct file *file, struct kvm *kvm)
+void vfio_group_set_kvm(struct vfio_group *group, struct kvm *kvm)
 {
-       struct vfio_group *group = file->private_data;
-
-       if (!vfio_file_is_group(file))
-               return;
-
        spin_lock(&group->kvm_ref_lock);
        group->kvm = kvm;
        spin_unlock(&group->kvm_ref_lock);
 }
-EXPORT_SYMBOL_GPL(vfio_file_set_kvm);
 
 /**
  * vfio_file_has_dev - True if the VFIO file is a handle for device
@@ -851,9 +900,9 @@ EXPORT_SYMBOL_GPL(vfio_file_set_kvm);
  */
 bool vfio_file_has_dev(struct file *file, struct vfio_device *device)
 {
-       struct vfio_group *group = file->private_data;
+       struct vfio_group *group = vfio_group_from_file(file);
 
-       if (!vfio_file_is_group(file))
+       if (!group)
                return false;
 
        return group == device->group;