aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexey Kardashevskiy <aik@ozlabs.ru>2014-01-06 18:07:55 +1100
committerEli Qiao <taget@linux.vnet.ibm.com>2014-01-06 15:58:15 +0800
commit2b858ca7c853e7dcc2ed54d6cf30142bf1e44a01 (patch)
tree1fd6f7ad1f08817ef086ac00bec3f47954df8a1a
parent44066fbc5f8b3d8a7f24bf7fc63429af5279c415 (diff)
downloadpowerkvm-2b858ca7c853e7dcc2ed54d6cf30142bf1e44a01.tar.gz
KVM: PPC: vfio kvm device: support spapr tce
In addition to the external VFIO user API, a VFIO KVM device has been introduced recently. sPAPR TCE IOMMU is para-virtualized and the guest does map/unmap via hypercalls which take a logical bus id (LIOBN) as a target IOMMU identifier. LIOBNs are made up and linked to IOMMU groups by the user space. In order to accelerate IOMMU operations in the KVM, we need to tell KVM the information about LIOBN-to-group mapping. For that, a new KVM_DEV_VFIO_GROUP_SET_SPAPR_TCE_LIOBN parameter is added. It accepts a pair of a VFIO group fd and LIOBN. This also adds a new kvm_vfio_find_group_by_liobn() function which receives kvm struct, LIOBN and a callback. As it increases the IOMMU group use counter, the KVMr is required to pass a callback which called when the VFIO group is about to be removed VFIO-KVM tracking so the KVM is able to call iommu_group_put() to release the IOMMU group. The KVM uses kvm_vfio_find_group_by_liobn() once per KVM run and caches the result in kvm_arch. iommu_group_put() for all groups will be called when KVM finishes (in the SPAPR TCE in KVM enablement patch). Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
-rw-r--r--Documentation/virtual/kvm/devices/vfio.txt19
-rw-r--r--arch/powerpc/kvm/Kconfig1
-rw-r--r--arch/powerpc/kvm/Makefile3
-rw-r--r--include/linux/kvm_host.h17
-rw-r--r--include/uapi/linux/kvm.h7
-rw-r--r--virt/kvm/vfio.c120
6 files changed, 164 insertions, 3 deletions
diff --git a/Documentation/virtual/kvm/devices/vfio.txt b/Documentation/virtual/kvm/devices/vfio.txt
index ef51740c67ca13..a25e74a149c34e 100644
--- a/Documentation/virtual/kvm/devices/vfio.txt
+++ b/Documentation/virtual/kvm/devices/vfio.txt
@@ -16,7 +16,22 @@ Groups:
KVM_DEV_VFIO_GROUP attributes:
KVM_DEV_VFIO_GROUP_ADD: Add a VFIO group to VFIO-KVM device tracking
+ kvm_device_attr.addr points to an int32_t file descriptor
+ for the VFIO group.
+
KVM_DEV_VFIO_GROUP_DEL: Remove a VFIO group from VFIO-KVM device tracking
+ kvm_device_attr.addr points to an int32_t file descriptor
+ for the VFIO group.
+
+ KVM_DEV_VFIO_GROUP_SET_SPAPR_TCE_LIOBN: sets a liobn for a VFIO group
+ kvm_device_attr.addr points to a struct:
+ struct kvm_vfio_spapr_tce_liobn {
+ __u32 argsz;
+ __s32 fd;
+ __u32 liobn;
+ };
+ where
+ @argsz is a struct size;
+ @fd is a file descriptor for a VFIO group;
+ @liobn is a logical bus id to be associated with the group.
-For each, kvm_device_attr.addr points to an int32_t file descriptor
-for the VFIO group.
diff --git a/arch/powerpc/kvm/Kconfig b/arch/powerpc/kvm/Kconfig
index 6fbf7141245fab..85cb1569ba0717 100644
--- a/arch/powerpc/kvm/Kconfig
+++ b/arch/powerpc/kvm/Kconfig
@@ -64,6 +64,7 @@ config KVM_BOOK3S_64
select KVM
select SPAPR_TCE_IOMMU if IOMMU_SUPPORT
select KVM_BOOK3S_PR_POSSIBLE if !KVM_BOOK3S_HV_POSSIBLE
+ select KVM_VFIO
---help---
Support running unmodified book3s_64 and book3s_32 guest kernels
in virtual machines on book3s_64 host processors.
diff --git a/arch/powerpc/kvm/Makefile b/arch/powerpc/kvm/Makefile
index 190cebb8bf4ce0..95a781e53eb3b0 100644
--- a/arch/powerpc/kvm/Makefile
+++ b/arch/powerpc/kvm/Makefile
@@ -96,6 +96,9 @@ endif
kvm-book3s_64-objs-$(CONFIG_KVM_XICS) += \
book3s_xics.o
+kvm-book3s_64-objs-$(CONFIG_KVM_VFIO) += \
+ $(addprefix ../../../virt/kvm/, vfio.o)
+
kvm-book3s_64-module-objs += \
../../../virt/kvm/kvm_main.o \
../../../virt/kvm/eventfd.o \
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 785c3d5b213eaf..442e00b386a655 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -1097,5 +1097,22 @@ static inline bool kvm_vcpu_eligible_for_directed_yield(struct kvm_vcpu *vcpu)
}
#endif /* CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT */
+
+#ifdef CONFIG_SPAPR_TCE_IOMMU
+typedef void (*kvm_vfio_spapr_tce_release)(struct kvm *kvm,
+ unsigned long liobn);
+#ifdef CONFIG_KVM_VFIO
+extern struct iommu_group *kvm_vfio_find_group_by_liobn(struct kvm *kvm,
+ unsigned long liobn, kvm_vfio_spapr_tce_release cb);
+
+#else
+static inline struct iommu_group *kvm_vfio_find_group_by_liobn(struct kvm *kvm,
+ unsigned long liobn, ikvm_vfio_ispapr_tce_release cb)
+{
+ return ERR_PTR(-ENODEV);
+}
+#endif
+#endif
+
#endif
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index c2ac2d8cffbc53..7fbca39923c42b 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -850,6 +850,13 @@ struct kvm_device_attr {
#define KVM_DEV_VFIO_GROUP 1
#define KVM_DEV_VFIO_GROUP_ADD 1
#define KVM_DEV_VFIO_GROUP_DEL 2
+#define KVM_DEV_VFIO_GROUP_SET_SPAPR_TCE_LIOBN 3
+
+struct kvm_vfio_spapr_tce_liobn {
+ __u32 argsz;
+ __s32 fd;
+ __u32 liobn;
+};
/*
* ioctls for VM fds
diff --git a/virt/kvm/vfio.c b/virt/kvm/vfio.c
index ca4260e3503713..8233106a145d9e 100644
--- a/virt/kvm/vfio.c
+++ b/virt/kvm/vfio.c
@@ -22,6 +22,12 @@
struct kvm_vfio_group {
struct list_head node;
struct vfio_group *vfio_group;
+#ifdef CONFIG_SPAPR_TCE_IOMMU
+ struct {
+ unsigned long liobn;
+ kvm_vfio_spapr_tce_release cb;
+ } spapr_tce;
+#endif
};
struct kvm_vfio {
@@ -59,6 +65,55 @@ static void kvm_vfio_group_put_external_user(struct vfio_group *vfio_group)
symbol_put(vfio_group_put_external_user);
}
+#ifdef CONFIG_SPAPR_TCE_IOMMU
+struct iommu_group *kvm_vfio_find_group_by_liobn(struct kvm *kvm,
+ unsigned long liobn, kvm_vfio_spapr_tce_release cb)
+{
+ struct kvm_vfio_group *kvg;
+ int group_id;
+ struct iommu_group *grp;
+ struct kvm_vfio *kv = NULL;
+ struct kvm_device *tmp;
+
+ if (!cb)
+ return ERR_PTR(-EINVAL);
+
+ /* Find a VFIO KVM device */
+ list_for_each_entry(tmp, &kvm->devices, vm_node) {
+ if (tmp->ops != &kvm_vfio_ops)
+ continue;
+
+ kv = tmp->private;
+ break;
+ }
+
+ if (!kv)
+ return ERR_PTR(-EIO);
+
+ /* Find a group */
+ mutex_lock(&kv->lock);
+ grp = ERR_PTR(-ENODEV);
+ list_for_each_entry(kvg, &kv->group_list, node) {
+ if (kvg->spapr_tce.liobn != liobn)
+ continue;
+
+ if (kvg->spapr_tce.cb) {
+ grp = ERR_PTR(-EBUSY);
+ break;
+ }
+
+ kvg->spapr_tce.cb = cb;
+ group_id = vfio_external_user_iommu_id(kvg->vfio_group);
+ grp = iommu_group_get_by_id(group_id);
+ break;
+ }
+ mutex_unlock(&kv->lock);
+
+ return grp;
+}
+EXPORT_SYMBOL_GPL(kvm_vfio_find_group_by_liobn);
+#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
@@ -140,7 +195,9 @@ 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;
-
+#ifdef CONFIG_SPAPR_TCE_IOMMU
+ kvg->spapr_tce.liobn = -1;
+#endif
mutex_unlock(&kv->lock);
kvm_vfio_update_coherency(dev);
@@ -170,6 +227,11 @@ static int kvm_vfio_set_group(struct kvm_device *dev, long attr, u64 arg)
continue;
list_del(&kvg->node);
+#ifdef CONFIG_SPAPR_TCE_IOMMU
+ if (kvg->spapr_tce.cb)
+ kvg->spapr_tce.cb(dev->kvm,
+ kvg->spapr_tce.liobn);
+#endif
kvm_vfio_group_put_external_user(kvg->vfio_group);
kfree(kvg);
ret = 0;
@@ -183,6 +245,59 @@ 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_LIOBN: {
+ struct kvm_vfio_spapr_tce_liobn param;
+ unsigned long minsz;
+ struct kvm_vfio *kv = dev->private;
+ struct vfio_group *vfio_group;
+ struct kvm_vfio_group *kvg;
+ struct fd f;
+
+ minsz = offsetofend(struct kvm_vfio_spapr_tce_liobn, liobn);
+
+ if (copy_from_user(&param, (void __user *)arg, minsz))
+ return -EFAULT;
+
+ if (param.argsz < minsz)
+ return -EINVAL;
+
+ f = fdget(param.fd);
+ 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);
+
+ ret = -ENOENT;
+
+ mutex_lock(&kv->lock);
+
+ list_for_each_entry(kvg, &kv->group_list, node) {
+ if (kvg->vfio_group != vfio_group)
+ continue;
+
+ if (kvg->spapr_tce.liobn != -1) {
+ ret = -EBUSY;
+ break;
+ }
+
+ kvg->spapr_tce.liobn = param.liobn;
+ ret = 0;
+ break;
+ }
+
+ mutex_unlock(&kv->lock);
+
+ kvm_vfio_group_put_external_user(vfio_group);
+
+ return ret;
+ }
+#endif /* CONFIG_SPAPR_TCE_IOMMU */
}
return -ENXIO;
@@ -207,6 +322,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_LIOBN:
+#endif
return 0;
}