]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blobdiff - virt/kvm/vfio.c
UBUNTU: Start new release
[mirror_ubuntu-zesty-kernel.git] / virt / kvm / vfio.c
index d32f239eb47133ae3f57ab0ed52a71d5c0478b8e..37d9118fd84be07bc119d40efe58b8edc1f2a889 100644 (file)
 #include <linux/vfio.h>
 #include "vfio.h"
 
+#ifdef CONFIG_SPAPR_TCE_IOMMU
+#include <asm/kvm_ppc.h>
+#endif
+
 struct kvm_vfio_group {
        struct list_head node;
        struct vfio_group *vfio_group;
@@ -89,6 +93,47 @@ static bool kvm_vfio_group_is_coherent(struct vfio_group *vfio_group)
        return ret > 0;
 }
 
+#ifdef CONFIG_SPAPR_TCE_IOMMU
+static int kvm_vfio_external_user_iommu_id(struct vfio_group *vfio_group)
+{
+       int (*fn)(struct vfio_group *);
+       int ret = -EINVAL;
+
+       fn = symbol_get(vfio_external_user_iommu_id);
+       if (!fn)
+               return ret;
+
+       ret = fn(vfio_group);
+
+       symbol_put(vfio_external_user_iommu_id);
+
+       return ret;
+}
+
+static struct iommu_group *kvm_vfio_group_get_iommu_group(
+               struct vfio_group *group)
+{
+       int group_id = kvm_vfio_external_user_iommu_id(group);
+
+       if (group_id < 0)
+               return NULL;
+
+       return iommu_group_get_by_id(group_id);
+}
+
+static void kvm_spapr_tce_release_vfio_group(struct kvm *kvm,
+               struct vfio_group *vfio_group)
+{
+       struct iommu_group *grp = kvm_vfio_group_get_iommu_group(vfio_group);
+
+       if (WARN_ON_ONCE(!grp))
+               return;
+
+       kvm_spapr_tce_release_iommu_group(kvm, grp);
+       iommu_group_put(grp);
+}
+#endif
+
 /*
  * 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
@@ -211,6 +256,9 @@ static int kvm_vfio_set_group(struct kvm_device *dev, long attr, u64 arg)
 
                mutex_unlock(&kv->lock);
 
+#ifdef CONFIG_SPAPR_TCE_IOMMU
+               kvm_spapr_tce_release_vfio_group(dev->kvm, vfio_group);
+#endif
                kvm_vfio_group_set_kvm(vfio_group, NULL);
 
                kvm_vfio_group_put_external_user(vfio_group);
@@ -218,6 +266,57 @@ static int kvm_vfio_set_group(struct kvm_device *dev, long attr, u64 arg)
                kvm_vfio_update_coherency(dev);
 
                return ret;
+
+#ifdef CONFIG_SPAPR_TCE_IOMMU
+       case KVM_DEV_VFIO_GROUP_SET_SPAPR_TCE: {
+               struct kvm_vfio_spapr_tce param;
+               struct kvm_vfio *kv = dev->private;
+               struct vfio_group *vfio_group;
+               struct kvm_vfio_group *kvg;
+               struct fd f;
+               struct iommu_group *grp;
+
+               if (copy_from_user(&param, (void __user *)arg,
+                               sizeof(struct kvm_vfio_spapr_tce)))
+                       return -EFAULT;
+
+               f = fdget(param.groupfd);
+               if (!f.file)
+                       return -EBADF;
+
+               vfio_group = kvm_vfio_group_get_external_user(f.file);
+               fdput(f);
+
+               if (IS_ERR(vfio_group))
+                       return PTR_ERR(vfio_group);
+
+               grp = kvm_vfio_group_get_iommu_group(vfio_group);
+               if (WARN_ON_ONCE(!grp)) {
+                       kvm_vfio_group_put_external_user(vfio_group);
+                       return -EIO;
+               }
+
+               ret = -ENOENT;
+
+               mutex_lock(&kv->lock);
+
+               list_for_each_entry(kvg, &kv->group_list, node) {
+                       if (kvg->vfio_group != vfio_group)
+                               continue;
+
+                       ret = kvm_spapr_tce_attach_iommu_group(dev->kvm,
+                                       param.tablefd, grp);
+                       break;
+               }
+
+               mutex_unlock(&kv->lock);
+
+               iommu_group_put(grp);
+               kvm_vfio_group_put_external_user(vfio_group);
+
+               return ret;
+       }
+#endif /* CONFIG_SPAPR_TCE_IOMMU */
        }
 
        return -ENXIO;
@@ -242,6 +341,9 @@ static int kvm_vfio_has_attr(struct kvm_device *dev,
                switch (attr->attr) {
                case KVM_DEV_VFIO_GROUP_ADD:
                case KVM_DEV_VFIO_GROUP_DEL:
+#ifdef CONFIG_SPAPR_TCE_IOMMU
+               case KVM_DEV_VFIO_GROUP_SET_SPAPR_TCE:
+#endif
                        return 0;
                }
 
@@ -257,6 +359,9 @@ static void kvm_vfio_destroy(struct kvm_device *dev)
        struct kvm_vfio_group *kvg, *tmp;
 
        list_for_each_entry_safe(kvg, tmp, &kv->group_list, node) {
+#ifdef CONFIG_SPAPR_TCE_IOMMU
+               kvm_spapr_tce_release_vfio_group(dev->kvm, kvg->vfio_group);
+#endif
                kvm_vfio_group_set_kvm(kvg->vfio_group, NULL);
                kvm_vfio_group_put_external_user(kvg->vfio_group);
                list_del(&kvg->node);