MALI: rockchip: linux: upgrade to DDK r13p0-00rel0
[firefly-linux-kernel-4.4.55.git] / virt / kvm / vfio.c
index 475487e238e1ef5fefd64893836b1afbacb97215..1dd087da6f31ae2f38c70042213ee6e5159cee10 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/slab.h>
 #include <linux/uaccess.h>
 #include <linux/vfio.h>
+#include "vfio.h"
 
 struct kvm_vfio_group {
        struct list_head node;
@@ -27,6 +28,7 @@ struct kvm_vfio_group {
 struct kvm_vfio {
        struct list_head group_list;
        struct mutex lock;
+       bool noncoherent;
 };
 
 static struct vfio_group *kvm_vfio_group_get_external_user(struct file *filep)
@@ -58,19 +60,69 @@ static void kvm_vfio_group_put_external_user(struct vfio_group *vfio_group)
        symbol_put(vfio_group_put_external_user);
 }
 
+static bool kvm_vfio_group_is_coherent(struct vfio_group *vfio_group)
+{
+       long (*fn)(struct vfio_group *, unsigned long);
+       long ret;
+
+       fn = symbol_get(vfio_external_check_extension);
+       if (!fn)
+               return false;
+
+       ret = fn(vfio_group, VFIO_DMA_CC_IOMMU);
+
+       symbol_put(vfio_external_check_extension);
+
+       return ret > 0;
+}
+
+/*
+ * Groups can use the same or different IOMMU domains.  If the same then
+ * adding a new group may change the coherency of groups we've previously
+ * been told about.  We don't want to care about any of that so we retest
+ * each group and bail as soon as we find one that's noncoherent.  This
+ * means we only ever [un]register_noncoherent_dma once for the whole device.
+ */
+static void kvm_vfio_update_coherency(struct kvm_device *dev)
+{
+       struct kvm_vfio *kv = dev->private;
+       bool noncoherent = false;
+       struct kvm_vfio_group *kvg;
+
+       mutex_lock(&kv->lock);
+
+       list_for_each_entry(kvg, &kv->group_list, node) {
+               if (!kvm_vfio_group_is_coherent(kvg->vfio_group)) {
+                       noncoherent = true;
+                       break;
+               }
+       }
+
+       if (noncoherent != kv->noncoherent) {
+               kv->noncoherent = noncoherent;
+
+               if (kv->noncoherent)
+                       kvm_arch_register_noncoherent_dma(dev->kvm);
+               else
+                       kvm_arch_unregister_noncoherent_dma(dev->kvm);
+       }
+
+       mutex_unlock(&kv->lock);
+}
+
 static int kvm_vfio_set_group(struct kvm_device *dev, long attr, u64 arg)
 {
        struct kvm_vfio *kv = dev->private;
        struct vfio_group *vfio_group;
        struct kvm_vfio_group *kvg;
-       void __user *argp = (void __user *)arg;
+       int32_t __user *argp = (int32_t __user *)(unsigned long)arg;
        struct fd f;
        int32_t fd;
        int ret;
 
        switch (attr) {
        case KVM_DEV_VFIO_GROUP_ADD:
-               if (get_user(fd, (int32_t __user *)argp))
+               if (get_user(fd, argp))
                        return -EFAULT;
 
                f = fdget(fd);
@@ -103,12 +155,16 @@ static int kvm_vfio_set_group(struct kvm_device *dev, long attr, u64 arg)
                list_add_tail(&kvg->node, &kv->group_list);
                kvg->vfio_group = vfio_group;
 
+               kvm_arch_start_assignment(dev->kvm);
+
                mutex_unlock(&kv->lock);
 
+               kvm_vfio_update_coherency(dev);
+
                return 0;
 
        case KVM_DEV_VFIO_GROUP_DEL:
-               if (get_user(fd, (int32_t __user *)argp))
+               if (get_user(fd, argp))
                        return -EFAULT;
 
                f = fdget(fd);
@@ -136,10 +192,14 @@ static int kvm_vfio_set_group(struct kvm_device *dev, long attr, u64 arg)
                        break;
                }
 
+               kvm_arch_end_assignment(dev->kvm);
+
                mutex_unlock(&kv->lock);
 
                kvm_vfio_group_put_external_user(vfio_group);
 
+               kvm_vfio_update_coherency(dev);
+
                return ret;
        }
 
@@ -183,8 +243,11 @@ static void kvm_vfio_destroy(struct kvm_device *dev)
                kvm_vfio_group_put_external_user(kvg->vfio_group);
                list_del(&kvg->node);
                kfree(kvg);
+               kvm_arch_end_assignment(dev->kvm);
        }
 
+       kvm_vfio_update_coherency(dev);
+
        kfree(kv);
        kfree(dev); /* alloc by kvm_ioctl_create_device, free by .destroy */
 }
@@ -221,8 +284,12 @@ static int kvm_vfio_create(struct kvm_device *dev, u32 type)
        return 0;
 }
 
-static int __init kvm_vfio_ops_init(void)
+int kvm_vfio_ops_init(void)
 {
        return kvm_register_device_ops(&kvm_vfio_ops, KVM_DEV_TYPE_VFIO);
 }
-module_init(kvm_vfio_ops_init);
+
+void kvm_vfio_ops_exit(void)
+{
+       kvm_unregister_device_ops(KVM_DEV_TYPE_VFIO);
+}