[RFC] KVM: Balloon support for device assignment

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Eran Borovik <borove@xxxxxxxxxx>

This patch adds modifications to allow correct
balloon operation when a virtual guest uses a direct assigned device.
The modifications include a new interface between qemu and kvm to allow
mapping and unmapping the pages from the IOMMU as well as pinning and unpinning as needed.

Signed-off-by: Eran Borovik <borove@xxxxxxxxxx>
---
 include/linux/kvm.h      |    3 ++
 include/linux/kvm_host.h |    4 ++
 virt/kvm/iommu.c         |   86 +++++++++++++++++++++++++++++++++++++++++++--
 virt/kvm/kvm_main.c      |    9 +++++
 4 files changed, 98 insertions(+), 4 deletions(-)

diff --git a/include/linux/kvm.h b/include/linux/kvm.h
index f8f8900..567f5f8 100644
--- a/include/linux/kvm.h
+++ b/include/linux/kvm.h
@@ -514,6 +514,9 @@ struct kvm_irqfd {
 					struct kvm_userspace_memory_region)
 #define KVM_SET_TSS_ADDR          _IO(KVMIO, 0x47)
 #define KVM_SET_IDENTITY_MAP_ADDR _IOW(KVMIO, 0x48, __u64)
+#define KVM_IOMMU_UNMAP_PAGE  _IOW(KVMIO, 0x49, __u64)
+#define KVM_IOMMU_MAP_PAGE  _IOW(KVMIO, 0x50, __u64)
+
 /* Device model IOC */
 #define KVM_CREATE_IRQCHIP	  _IO(KVMIO,  0x60)
 #define KVM_IRQ_LINE		  _IOW(KVMIO, 0x61, struct kvm_irq_level)
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index b7bbb5d..ad904ec 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -411,6 +411,10 @@ int kvm_assign_device(struct kvm *kvm,
 		      struct kvm_assigned_dev_kernel *assigned_dev);
 int kvm_deassign_device(struct kvm *kvm,
 			struct kvm_assigned_dev_kernel *assigned_dev);
+void kvm_iommu_unmap_page(struct kvm *kvm,
+			  gfn_t base_gfn);
+int kvm_iommu_map_page(struct kvm *kvm,
+		       gfn_t base_gfn);
 #else /* CONFIG_IOMMU_API */
 static inline int kvm_iommu_map_pages(struct kvm *kvm,
 				      gfn_t base_gfn,
diff --git a/virt/kvm/iommu.c b/virt/kvm/iommu.c
index 1514758..54cfd33 100644
--- a/virt/kvm/iommu.c
+++ b/virt/kvm/iommu.c
@@ -190,23 +190,101 @@ static void kvm_iommu_put_pages(struct kvm *kvm,
 	gfn_t gfn = base_gfn;
 	pfn_t pfn;
 	struct iommu_domain *domain = kvm->arch.iommu_domain;
-	unsigned long i;
+	unsigned long i, iommu_pages;
 	u64 phys;
 
 	/* check if iommu exists and in use */
 	if (!domain)
 		return;
 
-	for (i = 0; i < npages; i++) {
+	for (i = 0, iommu_pages = 0; i < npages; i++, gfn++) {
 		phys = iommu_iova_to_phys(domain, gfn_to_gpa(gfn));
+
+		/*Because of ballooning, there can be holes in the
+		  range. In that case, we simply unmap everything
+		  till now, and continue forward.
+		*/
+		if (!phys) {
+
+			/*No consecutive IOMMU pages here*/
+			if (iommu_pages == 0)
+				continue;
+			iommu_unmap_range(domain,
+					   gfn_to_gpa(base_gfn),
+					   PAGE_SIZE*iommu_pages);
+
+			/*Reset consequtive iommu range counters*/
+			base_gfn = gfn + 1;
+			iommu_pages = 0;
+			continue;
+		}
 		pfn = phys >> PAGE_SHIFT;
 		kvm_release_pfn_clean(pfn);
-		gfn++;
+		++iommu_pages;
 	}
 
-	iommu_unmap_range(domain, gfn_to_gpa(base_gfn), PAGE_SIZE * npages);
+	/*Unmap the last iommu range if any*/
+	if (iommu_pages != 0)
+		iommu_unmap_range(domain,
+				   gfn_to_gpa(base_gfn),
+				   PAGE_SIZE * iommu_pages);
+}
+
+/*Called to map a page from IOMMU */
+int kvm_iommu_map_page(struct kvm *kvm,
+		       gfn_t base_gfn)
+{
+	gfn_t gfn = base_gfn;
+	pfn_t pfn;
+	struct iommu_domain *domain = kvm->arch.iommu_domain;
+	u64 phys;
+	int rc;
+	int flags;
+
+	/* check if iommu exists and in use */
+	if (!domain)
+		return 0;
+	phys = iommu_iova_to_phys(domain, gfn_to_gpa(gfn));
+
+	/*Verify addres is not mapped already*/
+	if (phys)
+		return 0;
+	flags = IOMMU_READ | IOMMU_WRITE;
+	if (kvm->arch.iommu_flags & KVM_IOMMU_CACHE_COHERENCY)
+		flags |= IOMMU_CACHE;
+	pfn = gfn_to_pfn(kvm, gfn);
+	rc = iommu_map_range(domain,
+			    gfn_to_gpa(gfn),
+			    pfn_to_hpa(pfn),
+			    PAGE_SIZE, flags);
+	return rc;
+}
+
+
+
+/*Called to unmap a page from IOMMU */
+void kvm_iommu_unmap_page(struct kvm *kvm,
+			  gfn_t base_gfn)
+{
+	gfn_t gfn = base_gfn;
+	pfn_t pfn;
+	struct iommu_domain *domain = kvm->arch.iommu_domain;
+	u64 phys;
+
+	/* check if iommu exists and in use */
+	if (!domain)
+		return;
+	phys = iommu_iova_to_phys(domain, gfn_to_gpa(gfn));
+
+	/*Verify addres is mapped*/
+	if (!phys)
+		return;
+	pfn = phys >> PAGE_SHIFT;
+	kvm_release_pfn_clean(pfn);
+	iommu_unmap_range(domain, gfn_to_gpa(gfn), PAGE_SIZE);
 }
 
+
 static int kvm_iommu_unmap_memslots(struct kvm *kvm)
 {
 	int i;
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 7495ce3..560a3ff 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -2184,6 +2184,15 @@ static long kvm_vm_ioctl(struct file *filp,
 		r = -EOPNOTSUPP;
 		break;
 	}
+	case KVM_IOMMU_MAP_PAGE: {
+		r = kvm_iommu_map_page(kvm, arg);
+		break;
+	}
+	case KVM_IOMMU_UNMAP_PAGE:{
+		kvm_iommu_unmap_page(kvm, arg);
+		r = 0;
+		break;
+	}
 #ifdef KVM_CAP_ASSIGN_DEV_IRQ
 	case KVM_ASSIGN_DEV_IRQ: {
 		struct kvm_assigned_irq assigned_irq;
-- 
1.6.0.4

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux