kvm.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] [RESEND] VT-d: Support multiple device assignment to one guest
@ 2008-10-06  6:38 Han, Weidong
  2008-10-07 10:04 ` Zhang, Xiantao
  2008-10-07 13:29 ` Avi Kivity
  0 siblings, 2 replies; 20+ messages in thread
From: Han, Weidong @ 2008-10-06  6:38 UTC (permalink / raw)
  To: Avi Kivity; +Cc: kvm, Amit Shah, Kay, Allen M, Yang, Sheng, benami, muli

[-- Attachment #1: Type: text/plain, Size: 14461 bytes --]

[Rebased the patch due to my mmio's patch (commit: 0d679782) was checked
in]

>From 9e68fc762358cc44cfec3968ac5ec65324ce04d7 Mon Sep 17 00:00:00 2001
From: Weidong Han <weidong.han@intel.com>
Date: Mon, 6 Oct 2008 14:02:18 +0800
Subject: [PATCH] Support multiple device assignment to one guest

Current VT-d patches in kvm only support one device assignment to one
guest due to dmar_domain is per device.

In order to support multiple device assignemnt, this patch wraps
dmar_domain with a reference count (kvm_vtd_domain), and also adds a
pointer in kvm_assigned_dev_kernel to link to a kvm_vtd_domain.

Each dmar_domain owns one VT-d page table, in order to reduce page
tables and improve IOTLB utility, the devices assigned to the same guest
and under the same IOMMU share the same kvm_vtd_domain.

Signed-off-by: Weidong Han <weidong.han@intel.com>
---
 arch/x86/kvm/vtd.c          |  198
+++++++++++++++++++++++++++----------------
 arch/x86/kvm/x86.c          |   22 +++--
 drivers/pci/intel-iommu.c   |   16 ++++
 include/asm-x86/kvm_host.h  |    1 -
 include/linux/intel-iommu.h |    1 +
 include/linux/kvm_host.h    |   21 +++++
 6 files changed, 177 insertions(+), 82 deletions(-)

diff --git a/arch/x86/kvm/vtd.c b/arch/x86/kvm/vtd.c
index a770874..7552f92 100644
--- a/arch/x86/kvm/vtd.c
+++ b/arch/x86/kvm/vtd.c
@@ -27,19 +27,40 @@
 #include <linux/dmar.h>
 #include <linux/intel-iommu.h>
 
-static int kvm_iommu_unmap_memslots(struct kvm *kvm);
+static void kvm_iommu_put_domain_pages(struct dmar_domain *domain,
+                                       gfn_t base_gfn, unsigned long
npages)
+{
+        gfn_t gfn = base_gfn;
+        pfn_t pfn;
+        int i;
+
+        for (i = 0; i < npages; i++) {
+                pfn = (pfn_t)intel_iommu_iova_to_pfn(domain,
+                                                     gfn_to_gpa(gfn));
+                kvm_release_pfn_clean(pfn);
+                gfn++;
+        }
+}
+
 static void kvm_iommu_put_pages(struct kvm *kvm,
-				gfn_t base_gfn, unsigned long npages);
+                                gfn_t base_gfn, unsigned long npages)
+{
+        struct kvm_assigned_dev_kernel *assigned_dev;
 
-int kvm_iommu_map_pages(struct kvm *kvm,
-			gfn_t base_gfn, unsigned long npages)
+        list_for_each_entry(assigned_dev, &kvm->arch.assigned_dev_head,
list) {
+
kvm_iommu_put_domain_pages(assigned_dev->vtd_domain->domain,
+                                           base_gfn, npages);
+        }
+}
+
+static int kvm_iommu_map_domain_pages(struct kvm *kvm,
+                                      struct dmar_domain *domain,
+                                      gfn_t base_gfn, unsigned long
npages)
 {
 	gfn_t gfn = base_gfn;
 	pfn_t pfn;
 	int i, r = 0;
-	struct dmar_domain *domain = kvm->arch.intel_iommu_domain;
 
-	/* check if iommu exists and in use */
 	if (!domain)
 		return 0;
 
@@ -58,7 +79,7 @@ int kvm_iommu_map_pages(struct kvm *kvm,
 					     DMA_PTE_READ |
 					     DMA_PTE_WRITE);
 		if (r) {
-			printk(KERN_ERR "kvm_iommu_map_pages:"
+			printk(KERN_ERR "kvm_iommu_map_domain_pages:"
 			       "iommu failed to map pfn=%lx\n", pfn);
 			goto unmap_pages;
 		}
@@ -67,18 +88,40 @@ int kvm_iommu_map_pages(struct kvm *kvm,
 	return 0;
 
 unmap_pages:
-	kvm_iommu_put_pages(kvm, base_gfn, i);
+	kvm_iommu_put_domain_pages(domain, base_gfn, i);
 	return r;
 }
 
-static int kvm_iommu_map_memslots(struct kvm *kvm)
+int kvm_iommu_map_pages(struct kvm *kvm,
+			gfn_t base_gfn, unsigned long npages)
+{
+	int r = 0;
+        struct kvm_assigned_dev_kernel *assigned_dev;
+
+        list_for_each_entry(assigned_dev, &kvm->arch.assigned_dev_head,
list) {
+		r = kvm_iommu_map_domain_pages(kvm,
+			assigned_dev->vtd_domain->domain,
+			base_gfn, npages);
+		if (r)
+			goto unmap_pages;
+	}
+
+	return 0;
+
+unmap_pages:
+	kvm_iommu_put_pages(kvm, base_gfn, npages);
+	return r;
+}
+
+static int kvm_iommu_map_domain_memslots(struct kvm *kvm,
+					 struct dmar_domain *domain)
 {
 	int i, r;
 
 	down_read(&kvm->slots_lock);
 	for (i = 0; i < kvm->nmemslots; i++) {
-		r = kvm_iommu_map_pages(kvm, kvm->memslots[i].base_gfn,
-					kvm->memslots[i].npages);
+		r = kvm_iommu_map_domain_pages(kvm, domain,
+			 kvm->memslots[i].base_gfn,
kvm->memslots[i].npages);
 		if (r)
 			break;
 	}
@@ -86,10 +129,23 @@ static int kvm_iommu_map_memslots(struct kvm *kvm)
 	return r;
 }
 
+static void kvm_iommu_unmap_domain_memslots(struct kvm *kvm,
+					    struct dmar_domain *domain)
+{
+	int i;
+	down_read(&kvm->slots_lock);
+	for (i = 0; i < kvm->nmemslots; i++) {
+		kvm_iommu_put_domain_pages(domain,
+			kvm->memslots[i].base_gfn,
kvm->memslots[i].npages);
+	}
+	up_read(&kvm->slots_lock);
+}
+
 int kvm_iommu_map_guest(struct kvm *kvm,
 			struct kvm_assigned_dev_kernel *assigned_dev)
 {
 	struct pci_dev *pdev = NULL;
+	struct kvm_vtd_domain *vtd_dom = NULL;
 	int r;
 
 	if (!intel_iommu_found()) {
@@ -103,89 +159,83 @@ int kvm_iommu_map_guest(struct kvm *kvm,
 	       PCI_FUNC(assigned_dev->host_devfn));
 
 	pdev = assigned_dev->dev;
+	vtd_dom = assigned_dev->vtd_domain;
 
-	if (pdev == NULL) {
-		if (kvm->arch.intel_iommu_domain) {
-
intel_iommu_domain_exit(kvm->arch.intel_iommu_domain);
-			kvm->arch.intel_iommu_domain = NULL;
-		}
-		return -ENODEV;
-	}
-
-	kvm->arch.intel_iommu_domain = intel_iommu_domain_alloc(pdev);
-	if (!kvm->arch.intel_iommu_domain)
-		return -ENODEV;
-
-	r = kvm_iommu_map_memslots(kvm);
-	if (r)
-		goto out_unmap;
-
-	intel_iommu_detach_dev(kvm->arch.intel_iommu_domain,
+	intel_iommu_detach_dev(vtd_dom->domain,
 			       pdev->bus->number, pdev->devfn);
 
-	r = intel_iommu_context_mapping(kvm->arch.intel_iommu_domain,
-					pdev);
+	r = intel_iommu_context_mapping(vtd_dom->domain, pdev);
 	if (r) {
 		printk(KERN_ERR "Domain context map for %s failed",
 		       pci_name(pdev));
-		goto out_unmap;
+		return r;
 	}
-	return 0;
 
-out_unmap:
-	kvm_iommu_unmap_memslots(kvm);
-	return r;
+	return 0;
 }
 
-static void kvm_iommu_put_pages(struct kvm *kvm,
-			       gfn_t base_gfn, unsigned long npages)
+int kvm_iommu_unmap_guest(struct kvm *kvm)
 {
-	gfn_t gfn = base_gfn;
-	pfn_t pfn;
-	struct dmar_domain *domain = kvm->arch.intel_iommu_domain;
-	int i;
+        struct kvm_assigned_dev_kernel *entry;
 
-	for (i = 0; i < npages; i++) {
-		pfn = (pfn_t)intel_iommu_iova_to_pfn(domain,
-						     gfn_to_gpa(gfn));
-		kvm_release_pfn_clean(pfn);
-		gfn++;
-	}
+        list_for_each_entry(entry, &kvm->arch.assigned_dev_head, list)
{
+                printk(KERN_DEBUG "VT-d unmap: host bdf = %x:%x:%x\n",
+                       entry->host_busnr,
+                       PCI_SLOT(entry->host_devfn),
+                       PCI_FUNC(entry->host_devfn));
+
+                /* detach kvm dmar domain */
+                intel_iommu_detach_dev(entry->vtd_domain->domain,
+				       entry->host_busnr,
+                                       entry->host_devfn);
+        }
+
+        return 0;
 }
 
-static int kvm_iommu_unmap_memslots(struct kvm *kvm)
+struct kvm_vtd_domain *get_kvm_vtd_domain(struct kvm *kvm,
+		 			  struct pci_dev *pdev)
 {
-	int i;
-	down_read(&kvm->slots_lock);
-	for (i = 0; i < kvm->nmemslots; i++) {
-		kvm_iommu_put_pages(kvm, kvm->memslots[i].base_gfn,
-				    kvm->memslots[i].npages);
+	struct kvm_vtd_domain *vtd_dom = NULL;
+	struct intel_iommu *iommu = NULL;
+	struct kvm_assigned_dev_kernel *assigned_dev;
+
+	iommu = intel_iommu_device_get_iommu(pdev);
+
+	list_for_each_entry(assigned_dev, &kvm->arch.assigned_dev_head,
list) {
+		if (iommu == assigned_dev->vtd_domain->domain->iommu)
+			return assigned_dev->vtd_domain;
 	}
-	up_read(&kvm->slots_lock);
 
-	return 0;
-}
+	vtd_dom = kzalloc(sizeof(struct kvm_vtd_domain), GFP_KERNEL);
+	if (!vtd_dom)
+		return NULL;
 
-int kvm_iommu_unmap_guest(struct kvm *kvm)
-{
-	struct kvm_assigned_dev_kernel *entry;
-	struct dmar_domain *domain = kvm->arch.intel_iommu_domain;
+	vtd_dom->domain = intel_iommu_domain_alloc(pdev);
+	if (!vtd_dom->domain) {
+		kfree(vtd_dom);
+		return NULL;
+	}
 
-	/* check if iommu exists and in use */
-	if (!domain)
-		return 0;
+	if (kvm_iommu_map_domain_memslots(kvm, vtd_dom->domain)) {
+		kvm_iommu_unmap_domain_memslots(kvm, vtd_dom->domain);
+		intel_iommu_domain_exit(vtd_dom->domain);
+		kfree(vtd_dom);
+		return NULL;
+	}
 
-	list_for_each_entry(entry, &kvm->arch.assigned_dev_head, list) {
-		printk(KERN_DEBUG "VT-d unmap: host bdf = %x:%x:%x\n",
-		       entry->host_busnr,
-		       PCI_SLOT(entry->host_devfn),
-		       PCI_FUNC(entry->host_devfn));
+	return vtd_dom;
+}
 
-		/* detach kvm dmar domain */
-		intel_iommu_detach_dev(domain, entry->host_busnr,
-				       entry->host_devfn);
+void release_kvm_vtd_domain(struct kvm *kvm, struct kvm_vtd_domain
*vtd_dom)
+{
+	if (vtd_dom == NULL)
+		return;
+
+	if (--vtd_dom->dev_count == 0) {
+		kvm_iommu_unmap_domain_memslots(kvm, vtd_dom->domain);
+		intel_iommu_domain_exit(vtd_dom->domain);
+		kfree(vtd_dom);
+		vtd_dom = NULL;
 	}
-	kvm_iommu_unmap_memslots(kvm);
-	intel_iommu_domain_exit(domain);
-	return 0;
 }
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 675fcc1..a1d6ede 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -188,6 +188,8 @@ static void kvm_free_assigned_device(struct kvm
*kvm,
 	pci_disable_device(assigned_dev->dev);
 	pci_dev_put(assigned_dev->dev);
 
+	release_kvm_vtd_domain(kvm, assigned_dev->vtd_domain);
+
 	list_del(&assigned_dev->list);
 	kfree(assigned_dev);
 }
@@ -280,7 +282,10 @@ static int kvm_vm_ioctl_assign_device(struct kvm
*kvm,
 	match = kvm_find_assigned_dev(&kvm->arch.assigned_dev_head,
 				      assigned_dev->assigned_dev_id);
 	if (match) {
-		/* device already assigned */
+		printk(KERN_ERR "%s: device (%x:%x.%x) is already
assigned\n",
+		       __func__, match->host_busnr,
+		       PCI_SLOT(match->host_devfn),
+		       PCI_FUNC(match->host_devfn));
 		r = -EINVAL;
 		goto out;
 	}
@@ -317,21 +322,24 @@ static int kvm_vm_ioctl_assign_device(struct kvm
*kvm,
 
 	match->kvm = kvm;
 
-	list_add(&match->list, &kvm->arch.assigned_dev_head);
-
 	if (assigned_dev->flags & KVM_DEV_ASSIGN_ENABLE_IOMMU) {
+		match->vtd_domain = get_kvm_vtd_domain(kvm, dev);
+		if (match->vtd_domain == NULL)
+			goto out_disable;
+		match->vtd_domain->dev_count++;
+
 		r = kvm_iommu_map_guest(kvm, match);
 		if (r)
-			goto out_list_del;
+			goto out_disable;
 	}
+	
+	list_add(&match->list, &kvm->arch.assigned_dev_head);
 
 out:
 	mutex_unlock(&kvm->lock);
 	return r;
-out_list_del:
-	list_del(&match->list);
-	pci_release_regions(dev);
 out_disable:
+	pci_release_regions(dev);
 	pci_disable_device(dev);
 out_put:
 	pci_dev_put(dev);
diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c
index 089ba3f..ccb2fd3 100644
--- a/drivers/pci/intel-iommu.c
+++ b/drivers/pci/intel-iommu.c
@@ -2561,3 +2561,19 @@ u64 intel_iommu_iova_to_pfn(struct dmar_domain
*domain, u64 iova)
 	return pfn >> PAGE_SHIFT_4K;
 }
 EXPORT_SYMBOL_GPL(intel_iommu_iova_to_pfn);
+
+struct intel_iommu *intel_iommu_device_get_iommu(struct pci_dev *pdev)
+{
+	struct dmar_drhd_unit *drhd;
+
+	drhd = dmar_find_matched_drhd_unit(pdev);
+	if (!drhd) {
+		printk(KERN_ERR "%s: cannot find drhd for %x:%x.%x\n",
+		       __func__, pdev->bus->number,
+		       PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
+		return NULL;
+	}
+
+	return drhd->iommu;
+}
+EXPORT_SYMBOL_GPL(intel_iommu_device_get_iommu);
diff --git a/include/asm-x86/kvm_host.h b/include/asm-x86/kvm_host.h
index 53995a8..a48dcfe 100644
--- a/include/asm-x86/kvm_host.h
+++ b/include/asm-x86/kvm_host.h
@@ -351,7 +351,6 @@ struct kvm_arch{
 	 */
 	struct list_head active_mmu_pages;
 	struct list_head assigned_dev_head;
-	struct dmar_domain *intel_iommu_domain;
 	struct kvm_pic *vpic;
 	struct kvm_ioapic *vioapic;
 	struct kvm_pit *vpit;
diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h
index 5fa9d26..7801aa4 100644
--- a/include/linux/intel-iommu.h
+++ b/include/linux/intel-iommu.h
@@ -350,6 +350,7 @@ int intel_iommu_page_mapping(struct dmar_domain
*domain, dma_addr_t iova,
 void intel_iommu_detach_dev(struct dmar_domain *domain, u8 bus, u8
devfn);
 struct dmar_domain *intel_iommu_find_domain(struct pci_dev *pdev);
 u64 intel_iommu_iova_to_pfn(struct dmar_domain *domain, u64 iova);
+struct intel_iommu *intel_iommu_device_get_iommu(struct pci_dev *pdev);
 
 #ifdef CONFIG_DMAR
 int intel_iommu_found(void);
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 73b7c52..7a3e1b6 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -293,6 +293,11 @@ struct kvm_irq_ack_notifier {
 	void (*irq_acked)(struct kvm_irq_ack_notifier *kian);
 };
 
+struct kvm_vtd_domain {
+	int dev_count;			/* number of assigned devices */
+	struct dmar_domain *domain;
+};
+
 struct kvm_assigned_dev_kernel {
 	struct kvm_irq_ack_notifier ack_notifier;
 	struct work_struct interrupt_work;
@@ -305,6 +310,7 @@ struct kvm_assigned_dev_kernel {
 	int irq_requested;
 	struct pci_dev *dev;
 	struct kvm *kvm;
+	struct kvm_vtd_domain *vtd_domain;
 };
 
 #ifdef CONFIG_DMAR
@@ -313,6 +319,9 @@ int kvm_iommu_map_pages(struct kvm *kvm, gfn_t
base_gfn,
 int kvm_iommu_map_guest(struct kvm *kvm,
 			struct kvm_assigned_dev_kernel *assigned_dev);
 int kvm_iommu_unmap_guest(struct kvm *kvm);
+struct kvm_vtd_domain *get_kvm_vtd_domain(struct kvm *kvm,
+					  struct pci_dev *pdev);
+void release_kvm_vtd_domain(struct kvm *kvm, struct kvm_vtd_domain
*vtd_dom);
 #else /* CONFIG_DMAR */
 static inline int kvm_iommu_map_pages(struct kvm *kvm,
 				      gfn_t base_gfn,
@@ -332,6 +341,18 @@ static inline int kvm_iommu_unmap_guest(struct kvm
*kvm)
 {
 	return 0;
 }
+
+static inline struct kvm_vtd_domain *get_kvm_vtd_domain(struct kvm
*kvm,
+					  		struct pci_dev
*pdev)
+{
+	return NULL;
+}
+
+static inline void release_kvm_vtd_domain(struct kvm *kvm,
+					  struct kvm_vtd_domain
*vtd_dom)
+{
+	return;
+}
 #endif /* CONFIG_DMAR */
 
 static inline void kvm_guest_enter(void)
-- 
1.5.1

[-- Attachment #2: 0001-Support-multiple-device-assignment-to-one-guest-v2.patch --]
[-- Type: application/octet-stream, Size: 13911 bytes --]

From 9e68fc762358cc44cfec3968ac5ec65324ce04d7 Mon Sep 17 00:00:00 2001
From: Weidong Han <weidong.han@intel.com>
Date: Mon, 6 Oct 2008 14:02:18 +0800
Subject: [PATCH] Support multiple device assignment to one guest

Current VT-d patches in kvm only support one device assignment to one guest due to dmar_domain is per device.

In order to support multiple device assignemnt, this patch wraps dmar_domain with a reference count (kvm_vtd_domain), and also adds a pointer in kvm_assigned_dev_kernel to link to a kvm_vtd_domain.

Each dmar_domain owns one VT-d page table, in order to reduce page tables and improve IOTLB utility, the devices assigned to the same guest and under the same IOMMU share the same kvm_vtd_domain.

Signed-off-by: Weidong Han <weidong.han@intel.com>
---
 arch/x86/kvm/vtd.c          |  198 +++++++++++++++++++++++++++----------------
 arch/x86/kvm/x86.c          |   22 +++--
 drivers/pci/intel-iommu.c   |   16 ++++
 include/asm-x86/kvm_host.h  |    1 -
 include/linux/intel-iommu.h |    1 +
 include/linux/kvm_host.h    |   21 +++++
 6 files changed, 177 insertions(+), 82 deletions(-)

diff --git a/arch/x86/kvm/vtd.c b/arch/x86/kvm/vtd.c
index a770874..7552f92 100644
--- a/arch/x86/kvm/vtd.c
+++ b/arch/x86/kvm/vtd.c
@@ -27,19 +27,40 @@
 #include <linux/dmar.h>
 #include <linux/intel-iommu.h>
 
-static int kvm_iommu_unmap_memslots(struct kvm *kvm);
+static void kvm_iommu_put_domain_pages(struct dmar_domain *domain,
+                                       gfn_t base_gfn, unsigned long npages)
+{
+        gfn_t gfn = base_gfn;
+        pfn_t pfn;
+        int i;
+
+        for (i = 0; i < npages; i++) {
+                pfn = (pfn_t)intel_iommu_iova_to_pfn(domain,
+                                                     gfn_to_gpa(gfn));
+                kvm_release_pfn_clean(pfn);
+                gfn++;
+        }
+}
+
 static void kvm_iommu_put_pages(struct kvm *kvm,
-				gfn_t base_gfn, unsigned long npages);
+                                gfn_t base_gfn, unsigned long npages)
+{
+        struct kvm_assigned_dev_kernel *assigned_dev;
 
-int kvm_iommu_map_pages(struct kvm *kvm,
-			gfn_t base_gfn, unsigned long npages)
+        list_for_each_entry(assigned_dev, &kvm->arch.assigned_dev_head, list) {
+                kvm_iommu_put_domain_pages(assigned_dev->vtd_domain->domain,
+                                           base_gfn, npages);
+        }
+}
+
+static int kvm_iommu_map_domain_pages(struct kvm *kvm,
+                                      struct dmar_domain *domain,
+                                      gfn_t base_gfn, unsigned long npages)
 {
 	gfn_t gfn = base_gfn;
 	pfn_t pfn;
 	int i, r = 0;
-	struct dmar_domain *domain = kvm->arch.intel_iommu_domain;
 
-	/* check if iommu exists and in use */
 	if (!domain)
 		return 0;
 
@@ -58,7 +79,7 @@ int kvm_iommu_map_pages(struct kvm *kvm,
 					     DMA_PTE_READ |
 					     DMA_PTE_WRITE);
 		if (r) {
-			printk(KERN_ERR "kvm_iommu_map_pages:"
+			printk(KERN_ERR "kvm_iommu_map_domain_pages:"
 			       "iommu failed to map pfn=%lx\n", pfn);
 			goto unmap_pages;
 		}
@@ -67,18 +88,40 @@ int kvm_iommu_map_pages(struct kvm *kvm,
 	return 0;
 
 unmap_pages:
-	kvm_iommu_put_pages(kvm, base_gfn, i);
+	kvm_iommu_put_domain_pages(domain, base_gfn, i);
 	return r;
 }
 
-static int kvm_iommu_map_memslots(struct kvm *kvm)
+int kvm_iommu_map_pages(struct kvm *kvm,
+			gfn_t base_gfn, unsigned long npages)
+{
+	int r = 0;
+        struct kvm_assigned_dev_kernel *assigned_dev;
+
+        list_for_each_entry(assigned_dev, &kvm->arch.assigned_dev_head, list) {
+		r = kvm_iommu_map_domain_pages(kvm,
+			assigned_dev->vtd_domain->domain,
+			base_gfn, npages);
+		if (r)
+			goto unmap_pages;
+	}
+
+	return 0;
+
+unmap_pages:
+	kvm_iommu_put_pages(kvm, base_gfn, npages);
+	return r;
+}
+
+static int kvm_iommu_map_domain_memslots(struct kvm *kvm,
+					 struct dmar_domain *domain)
 {
 	int i, r;
 
 	down_read(&kvm->slots_lock);
 	for (i = 0; i < kvm->nmemslots; i++) {
-		r = kvm_iommu_map_pages(kvm, kvm->memslots[i].base_gfn,
-					kvm->memslots[i].npages);
+		r = kvm_iommu_map_domain_pages(kvm, domain,
+			 kvm->memslots[i].base_gfn, kvm->memslots[i].npages);
 		if (r)
 			break;
 	}
@@ -86,10 +129,23 @@ static int kvm_iommu_map_memslots(struct kvm *kvm)
 	return r;
 }
 
+static void kvm_iommu_unmap_domain_memslots(struct kvm *kvm,
+					    struct dmar_domain *domain)
+{
+	int i;
+	down_read(&kvm->slots_lock);
+	for (i = 0; i < kvm->nmemslots; i++) {
+		kvm_iommu_put_domain_pages(domain,
+			kvm->memslots[i].base_gfn, kvm->memslots[i].npages);
+	}
+	up_read(&kvm->slots_lock);
+}
+
 int kvm_iommu_map_guest(struct kvm *kvm,
 			struct kvm_assigned_dev_kernel *assigned_dev)
 {
 	struct pci_dev *pdev = NULL;
+	struct kvm_vtd_domain *vtd_dom = NULL;
 	int r;
 
 	if (!intel_iommu_found()) {
@@ -103,89 +159,83 @@ int kvm_iommu_map_guest(struct kvm *kvm,
 	       PCI_FUNC(assigned_dev->host_devfn));
 
 	pdev = assigned_dev->dev;
+	vtd_dom = assigned_dev->vtd_domain;
 
-	if (pdev == NULL) {
-		if (kvm->arch.intel_iommu_domain) {
-			intel_iommu_domain_exit(kvm->arch.intel_iommu_domain);
-			kvm->arch.intel_iommu_domain = NULL;
-		}
-		return -ENODEV;
-	}
-
-	kvm->arch.intel_iommu_domain = intel_iommu_domain_alloc(pdev);
-	if (!kvm->arch.intel_iommu_domain)
-		return -ENODEV;
-
-	r = kvm_iommu_map_memslots(kvm);
-	if (r)
-		goto out_unmap;
-
-	intel_iommu_detach_dev(kvm->arch.intel_iommu_domain,
+	intel_iommu_detach_dev(vtd_dom->domain,
 			       pdev->bus->number, pdev->devfn);
 
-	r = intel_iommu_context_mapping(kvm->arch.intel_iommu_domain,
-					pdev);
+	r = intel_iommu_context_mapping(vtd_dom->domain, pdev);
 	if (r) {
 		printk(KERN_ERR "Domain context map for %s failed",
 		       pci_name(pdev));
-		goto out_unmap;
+		return r;
 	}
-	return 0;
 
-out_unmap:
-	kvm_iommu_unmap_memslots(kvm);
-	return r;
+	return 0;
 }
 
-static void kvm_iommu_put_pages(struct kvm *kvm,
-			       gfn_t base_gfn, unsigned long npages)
+int kvm_iommu_unmap_guest(struct kvm *kvm)
 {
-	gfn_t gfn = base_gfn;
-	pfn_t pfn;
-	struct dmar_domain *domain = kvm->arch.intel_iommu_domain;
-	int i;
+        struct kvm_assigned_dev_kernel *entry;
 
-	for (i = 0; i < npages; i++) {
-		pfn = (pfn_t)intel_iommu_iova_to_pfn(domain,
-						     gfn_to_gpa(gfn));
-		kvm_release_pfn_clean(pfn);
-		gfn++;
-	}
+        list_for_each_entry(entry, &kvm->arch.assigned_dev_head, list) {
+                printk(KERN_DEBUG "VT-d unmap: host bdf = %x:%x:%x\n",
+                       entry->host_busnr,
+                       PCI_SLOT(entry->host_devfn),
+                       PCI_FUNC(entry->host_devfn));
+
+                /* detach kvm dmar domain */
+                intel_iommu_detach_dev(entry->vtd_domain->domain,
+				       entry->host_busnr,
+                                       entry->host_devfn);
+        }
+
+        return 0;
 }
 
-static int kvm_iommu_unmap_memslots(struct kvm *kvm)
+struct kvm_vtd_domain *get_kvm_vtd_domain(struct kvm *kvm,
+		 			  struct pci_dev *pdev)
 {
-	int i;
-	down_read(&kvm->slots_lock);
-	for (i = 0; i < kvm->nmemslots; i++) {
-		kvm_iommu_put_pages(kvm, kvm->memslots[i].base_gfn,
-				    kvm->memslots[i].npages);
+	struct kvm_vtd_domain *vtd_dom = NULL;
+	struct intel_iommu *iommu = NULL;
+	struct kvm_assigned_dev_kernel *assigned_dev;
+
+	iommu = intel_iommu_device_get_iommu(pdev);
+
+	list_for_each_entry(assigned_dev, &kvm->arch.assigned_dev_head, list) {
+		if (iommu == assigned_dev->vtd_domain->domain->iommu)
+			return assigned_dev->vtd_domain;
 	}
-	up_read(&kvm->slots_lock);
 
-	return 0;
-}
+	vtd_dom = kzalloc(sizeof(struct kvm_vtd_domain), GFP_KERNEL);
+	if (!vtd_dom)
+		return NULL;
 
-int kvm_iommu_unmap_guest(struct kvm *kvm)
-{
-	struct kvm_assigned_dev_kernel *entry;
-	struct dmar_domain *domain = kvm->arch.intel_iommu_domain;
+	vtd_dom->domain = intel_iommu_domain_alloc(pdev);
+	if (!vtd_dom->domain) {
+		kfree(vtd_dom);
+		return NULL;
+	}
 
-	/* check if iommu exists and in use */
-	if (!domain)
-		return 0;
+	if (kvm_iommu_map_domain_memslots(kvm, vtd_dom->domain)) {
+		kvm_iommu_unmap_domain_memslots(kvm, vtd_dom->domain);
+		intel_iommu_domain_exit(vtd_dom->domain);
+		kfree(vtd_dom);
+		return NULL;
+	}
 
-	list_for_each_entry(entry, &kvm->arch.assigned_dev_head, list) {
-		printk(KERN_DEBUG "VT-d unmap: host bdf = %x:%x:%x\n",
-		       entry->host_busnr,
-		       PCI_SLOT(entry->host_devfn),
-		       PCI_FUNC(entry->host_devfn));
+	return vtd_dom;
+}
 
-		/* detach kvm dmar domain */
-		intel_iommu_detach_dev(domain, entry->host_busnr,
-				       entry->host_devfn);
+void release_kvm_vtd_domain(struct kvm *kvm, struct kvm_vtd_domain *vtd_dom)
+{
+	if (vtd_dom == NULL)
+		return;
+
+	if (--vtd_dom->dev_count == 0) {
+		kvm_iommu_unmap_domain_memslots(kvm, vtd_dom->domain);
+		intel_iommu_domain_exit(vtd_dom->domain);
+		kfree(vtd_dom);
+		vtd_dom = NULL;
 	}
-	kvm_iommu_unmap_memslots(kvm);
-	intel_iommu_domain_exit(domain);
-	return 0;
 }
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 675fcc1..a1d6ede 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -188,6 +188,8 @@ static void kvm_free_assigned_device(struct kvm *kvm,
 	pci_disable_device(assigned_dev->dev);
 	pci_dev_put(assigned_dev->dev);
 
+	release_kvm_vtd_domain(kvm, assigned_dev->vtd_domain);
+
 	list_del(&assigned_dev->list);
 	kfree(assigned_dev);
 }
@@ -280,7 +282,10 @@ static int kvm_vm_ioctl_assign_device(struct kvm *kvm,
 	match = kvm_find_assigned_dev(&kvm->arch.assigned_dev_head,
 				      assigned_dev->assigned_dev_id);
 	if (match) {
-		/* device already assigned */
+		printk(KERN_ERR "%s: device (%x:%x.%x) is already assigned\n",
+		       __func__, match->host_busnr,
+		       PCI_SLOT(match->host_devfn),
+		       PCI_FUNC(match->host_devfn));
 		r = -EINVAL;
 		goto out;
 	}
@@ -317,21 +322,24 @@ static int kvm_vm_ioctl_assign_device(struct kvm *kvm,
 
 	match->kvm = kvm;
 
-	list_add(&match->list, &kvm->arch.assigned_dev_head);
-
 	if (assigned_dev->flags & KVM_DEV_ASSIGN_ENABLE_IOMMU) {
+		match->vtd_domain = get_kvm_vtd_domain(kvm, dev);
+		if (match->vtd_domain == NULL)
+			goto out_disable;
+		match->vtd_domain->dev_count++;
+
 		r = kvm_iommu_map_guest(kvm, match);
 		if (r)
-			goto out_list_del;
+			goto out_disable;
 	}
+	
+	list_add(&match->list, &kvm->arch.assigned_dev_head);
 
 out:
 	mutex_unlock(&kvm->lock);
 	return r;
-out_list_del:
-	list_del(&match->list);
-	pci_release_regions(dev);
 out_disable:
+	pci_release_regions(dev);
 	pci_disable_device(dev);
 out_put:
 	pci_dev_put(dev);
diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c
index 089ba3f..ccb2fd3 100644
--- a/drivers/pci/intel-iommu.c
+++ b/drivers/pci/intel-iommu.c
@@ -2561,3 +2561,19 @@ u64 intel_iommu_iova_to_pfn(struct dmar_domain *domain, u64 iova)
 	return pfn >> PAGE_SHIFT_4K;
 }
 EXPORT_SYMBOL_GPL(intel_iommu_iova_to_pfn);
+
+struct intel_iommu *intel_iommu_device_get_iommu(struct pci_dev *pdev)
+{
+	struct dmar_drhd_unit *drhd;
+
+	drhd = dmar_find_matched_drhd_unit(pdev);
+	if (!drhd) {
+		printk(KERN_ERR "%s: cannot find drhd for %x:%x.%x\n",
+		       __func__, pdev->bus->number,
+		       PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
+		return NULL;
+	}
+
+	return drhd->iommu;
+}
+EXPORT_SYMBOL_GPL(intel_iommu_device_get_iommu);
diff --git a/include/asm-x86/kvm_host.h b/include/asm-x86/kvm_host.h
index 53995a8..a48dcfe 100644
--- a/include/asm-x86/kvm_host.h
+++ b/include/asm-x86/kvm_host.h
@@ -351,7 +351,6 @@ struct kvm_arch{
 	 */
 	struct list_head active_mmu_pages;
 	struct list_head assigned_dev_head;
-	struct dmar_domain *intel_iommu_domain;
 	struct kvm_pic *vpic;
 	struct kvm_ioapic *vioapic;
 	struct kvm_pit *vpit;
diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h
index 5fa9d26..7801aa4 100644
--- a/include/linux/intel-iommu.h
+++ b/include/linux/intel-iommu.h
@@ -350,6 +350,7 @@ int intel_iommu_page_mapping(struct dmar_domain *domain, dma_addr_t iova,
 void intel_iommu_detach_dev(struct dmar_domain *domain, u8 bus, u8 devfn);
 struct dmar_domain *intel_iommu_find_domain(struct pci_dev *pdev);
 u64 intel_iommu_iova_to_pfn(struct dmar_domain *domain, u64 iova);
+struct intel_iommu *intel_iommu_device_get_iommu(struct pci_dev *pdev);
 
 #ifdef CONFIG_DMAR
 int intel_iommu_found(void);
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 73b7c52..7a3e1b6 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -293,6 +293,11 @@ struct kvm_irq_ack_notifier {
 	void (*irq_acked)(struct kvm_irq_ack_notifier *kian);
 };
 
+struct kvm_vtd_domain {
+	int dev_count;			/* number of assigned devices */
+	struct dmar_domain *domain;
+};
+
 struct kvm_assigned_dev_kernel {
 	struct kvm_irq_ack_notifier ack_notifier;
 	struct work_struct interrupt_work;
@@ -305,6 +310,7 @@ struct kvm_assigned_dev_kernel {
 	int irq_requested;
 	struct pci_dev *dev;
 	struct kvm *kvm;
+	struct kvm_vtd_domain *vtd_domain;
 };
 
 #ifdef CONFIG_DMAR
@@ -313,6 +319,9 @@ int kvm_iommu_map_pages(struct kvm *kvm, gfn_t base_gfn,
 int kvm_iommu_map_guest(struct kvm *kvm,
 			struct kvm_assigned_dev_kernel *assigned_dev);
 int kvm_iommu_unmap_guest(struct kvm *kvm);
+struct kvm_vtd_domain *get_kvm_vtd_domain(struct kvm *kvm,
+					  struct pci_dev *pdev);
+void release_kvm_vtd_domain(struct kvm *kvm, struct kvm_vtd_domain *vtd_dom);
 #else /* CONFIG_DMAR */
 static inline int kvm_iommu_map_pages(struct kvm *kvm,
 				      gfn_t base_gfn,
@@ -332,6 +341,18 @@ static inline int kvm_iommu_unmap_guest(struct kvm *kvm)
 {
 	return 0;
 }
+
+static inline struct kvm_vtd_domain *get_kvm_vtd_domain(struct kvm *kvm,
+					  		struct pci_dev *pdev)
+{
+	return NULL;
+}
+
+static inline void release_kvm_vtd_domain(struct kvm *kvm,
+					  struct kvm_vtd_domain *vtd_dom)
+{
+	return;
+}
 #endif /* CONFIG_DMAR */
 
 static inline void kvm_guest_enter(void)
-- 
1.5.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2008-10-29 10:26 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-10-06  6:38 [PATCH] [RESEND] VT-d: Support multiple device assignment to one guest Han, Weidong
2008-10-07 10:04 ` Zhang, Xiantao
2008-10-07 13:59   ` Avi Kivity
2008-10-08  1:58     ` Zhang, Xiantao
2008-10-07 13:29 ` Avi Kivity
2008-10-08  5:40   ` Han, Weidong
2008-10-08 10:32     ` Avi Kivity
2008-10-08 15:06       ` Han, Weidong
2008-10-08 19:49         ` Avi Kivity
2008-10-09  6:11           ` Han, Weidong
2008-10-09  8:31             ` Avi Kivity
2008-10-09  9:25               ` Han, Weidong
2008-10-09 12:50                 ` Avi Kivity
2008-10-09 14:31                   ` Han, Weidong
     [not found]                   ` <0122C7C995D32147B66BF4F440D3016301CB08EF@pdsmsx415.ccr.corp.intel.com>
2008-10-10  5:50                     ` Han, Weidong
2008-10-10  6:40                       ` Avi Kivity
2008-10-10  7:22                         ` Han, Weidong
2008-10-10  7:32                           ` Avi Kivity
2008-10-10  7:50                             ` Han, Weidong
2008-10-29 10:25   ` Joerg Roedel

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).