Multiple devices may under different IOMMUs, so one dmar_domain cannot
cover this. This patch abstracts a structure kvm_vtd_domain, which let
devices under the same IOMMU share the same dmar_domain. Then use a
kvm_vtd_domain list to contain the all assigned devices. 

This patch is on top of vtd branch of amit's tree. I will rebase it if
VT-d patches sent by Ben are merged into KVM main tree.

Signed-off-by: Weidong Han <[EMAIL PROTECTED]>
---
 arch/x86/kvm/vtd.c         |  260
+++++++++++++++++++++++++++++---------------
 arch/x86/kvm/x86.c         |    4 +-
 include/asm-x86/kvm_host.h |   14 ++-
 3 files changed, 185 insertions(+), 93 deletions(-)

diff --git a/arch/x86/kvm/vtd.c b/arch/x86/kvm/vtd.c
index 4387c25..2365509 100644
--- a/arch/x86/kvm/vtd.c
+++ b/arch/x86/kvm/vtd.c
@@ -26,29 +26,32 @@
 #include <linux/intel-iommu.h>
 #include "vtd.h"
 
-int kvm_iommu_map_pages(struct kvm *kvm,
-       gfn_t base_gfn, unsigned long npages)
+DEFINE_SPINLOCK(kvm_vtd_domain_lock);
+
+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;
        struct page *page;
        int i, rc;
 
-       if (!kvm->arch.intel_iommu_domain)
+       if (!domain)
                return -EFAULT;
 
-       printk(KERN_DEBUG "kvm_iommu_map_page: gpa = %lx\n",
+       printk(KERN_DEBUG "kvm_iommu_map_domain_page: gpa = %lx\n",
                gfn << PAGE_SHIFT);
-       printk(KERN_DEBUG "kvm_iommu_map_page: hpa = %lx\n",
+       printk(KERN_DEBUG "kvm_iommu_map_domain_page: hpa = %lx\n",
                gfn_to_pfn(kvm, base_gfn) << PAGE_SHIFT);
-       printk(KERN_DEBUG "kvm_iommu_map_page: size = %lx\n",
+       printk(KERN_DEBUG "kvm_iommu_map_domain_page: size = %lx\n",
                npages*PAGE_SIZE);
 
        for (i = 0; i < npages; i++) {
                pfn = gfn_to_pfn(kvm, gfn);
                if (pfn_valid(pfn)) {
                        rc = kvm_intel_iommu_page_mapping(
-                               kvm->arch.intel_iommu_domain,
+                               domain,
                                gfn << PAGE_SHIFT, pfn << PAGE_SHIFT,
                                PAGE_SIZE, DMA_PTE_READ |
DMA_PTE_WRITE);
                        if (rc) {
@@ -64,25 +67,146 @@ int kvm_iommu_map_pages(struct kvm *kvm,
        }
        return 0;
 }
+
+int kvm_iommu_map_pages(struct kvm *kvm,
+                       gfn_t base_gfn, unsigned long npages)
+{
+       struct kvm_vtd_domain *vtd_dom = NULL;
+
+       list_for_each_entry(vtd_dom, &kvm->arch.vtd_domains, list)
+               kvm_iommu_map_domain_pages(kvm, vtd_dom->domain,
+                                          base_gfn, npages);
+
+       return 0;
+}
 EXPORT_SYMBOL_GPL(kvm_iommu_map_pages);
 
-static int kvm_iommu_map_memslots(struct kvm *kvm)
+static int kvm_iommu_put_domain_pages(struct kvm *kvm,
+                                     struct dmar_domain *domain,
+                                     gfn_t base_gfn, unsigned long
npages)
+{
+       gfn_t gfn = base_gfn;
+       struct page *page;
+       int i;
+
+       if (!domain)
+               return -EFAULT;
+
+       printk(KERN_DEBUG "kvm_iommu_put_domain_pages: gpa = %lx\n",
+               gfn << PAGE_SHIFT);
+       printk(KERN_DEBUG "kvm_iommu_put_domain_pages: hpa = %lx\n",
+               gfn_to_pfn(kvm, gfn) << PAGE_SHIFT);
+       printk(KERN_DEBUG "kvm_iommu_put_domain_pages: size = %lx\n",
+               npages*PAGE_SIZE);
+
+       for (i = 0; i < npages; i++) {
+               page = gfn_to_page(kvm, gfn);
+               put_page(page);
+               gfn++;
+       }
+       return 0;
+}
+
+int kvm_iommu_put_pages(struct kvm *kvm,
+                       gfn_t base_gfn, unsigned long npages)
+{
+       struct kvm_vtd_domain *vtd_dom = NULL;
+
+       list_for_each_entry(vtd_dom, &kvm->arch.vtd_domains, list)
+               kvm_iommu_put_domain_pages(kvm, vtd_dom->domain,
+                                          base_gfn, npages);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(kvm_iommu_put_pages);
+
+static int kvm_iommu_map_domain_memslots(struct kvm *kvm,
+                                        struct dmar_domain *domain)
 {
        int i, rc;
        for (i = 0; i < kvm->nmemslots; i++) {
-               rc = kvm_iommu_map_pages(kvm, kvm->memslots[i].base_gfn,
-                               kvm->memslots[i].npages);
+               rc = kvm_iommu_map_domain_pages(kvm, domain,
+                        kvm->memslots[i].base_gfn,
kvm->memslots[i].npages);
                if (rc)
                        return rc;
        }
        return 0;
 }
 
-static int kvm_iommu_unmap_memslots(struct kvm *kvm);
+static int kvm_iommu_unmap_domain_memslots(struct kvm *kvm,
+                                          struct dmar_domain *domain)
+{
+       int i, rc;
+       for (i = 0; i < kvm->nmemslots; i++) {
+               rc = kvm_iommu_put_domain_pages(kvm, domain,
+                       kvm->memslots[i].base_gfn,
kvm->memslots[i].npages);
+               if (rc)
+                       return rc;
+       }
+       return 0;
+}
+
+static struct kvm_vtd_domain *find_kvm_vtd_domain(struct kvm *kvm,
+                                                 struct pci_dev *pdev)
+{
+       struct kvm_vtd_domain *vtd_dom;
+       struct kvm_vtd_pt_dev_list *vtd_dev;
+
+       list_for_each_entry(vtd_dom, &kvm->arch.vtd_domains, list) {
+               list_for_each_entry(vtd_dev, &vtd_dom->pci_pt_devices,
list) {
+                       if (vtd_dev->dev_info.busnr == pdev->bus->number
&&
+                           vtd_dev->dev_info.devfn == pdev->devfn)
+                               return vtd_dom;
+               }
+       }
+
+       return NULL;
+}
+
+static struct kvm_vtd_domain *get_kvm_vtd_domain(struct kvm *kvm,
+                                                struct pci_dev *pdev)
+{
+       struct kvm_vtd_domain *vtd_dom;
+
+       vtd_dom = find_kvm_vtd_domain(kvm, pdev);
+       if (!vtd_dom) {
+               vtd_dom = kzalloc(sizeof(struct kvm_vtd_domain),
GFP_KERNEL);
+               if (!vtd_dom)
+                       return NULL;
+
+               INIT_LIST_HEAD(&vtd_dom->pci_pt_devices);
+               if (!vtd_dom->domain) {
+                       vtd_dom->domain =
kvm_intel_iommu_domain_alloc(pdev);
+                       if (!vtd_dom->domain) {
+                               kfree(vtd_dom);
+                               return NULL;
+                       }
+
+                       if (kvm_iommu_map_domain_memslots(kvm,
+                                                 vtd_dom->domain)) {
+                               kvm_iommu_unmap_domain_memslots(kvm,
+                                       vtd_dom->domain);
+
kvm_intel_iommu_domain_exit(vtd_dom->domain);
+                               kfree(vtd_dom);
+                               return NULL;
+                       }
+               }
+
+               spin_lock(&kvm_vtd_domain_lock);
+               list_add(&vtd_dom->list, &kvm->arch.vtd_domains);
+               spin_unlock(&kvm_vtd_domain_lock);
+       }
+
+       return vtd_dom;
+}
+
 int kvm_iommu_map_guest(struct kvm *kvm,
-       struct kvm_pci_passthrough_dev *pci_pt_dev)
+                       struct kvm_pci_passthrough_dev *pci_pt_dev)
 {
        struct pci_dev *pdev = NULL;
+       struct kvm_vtd_domain *vtd_dom = NULL;
+       struct kvm_vtd_pt_dev_list *vtd_dev = NULL;
+
 
        printk(KERN_DEBUG "kvm_iommu_map_guest: host bdf = %x:%x:%x\n",
                pci_pt_dev->host.busnr,
@@ -94,104 +218,62 @@ int kvm_iommu_map_guest(struct kvm *kvm,
                        (pdev->devfn == pci_pt_dev->host.devfn))
                        goto found;
        }
-       if (kvm->arch.intel_iommu_domain) {
-
kvm_intel_iommu_domain_exit(kvm->arch.intel_iommu_domain);
-               kvm->arch.intel_iommu_domain = NULL;
-       }
        return -ENODEV;
 found:
-       kvm->arch.intel_iommu_domain =
kvm_intel_iommu_domain_alloc(pdev);
-       if (kvm->arch.intel_iommu_domain == NULL)
-               printk(KERN_ERR "kvm_iommu_map_guest: domain ==
NULL\n");
-       else
-               printk("kvm_iommu_map_guest: domain = %p\n",
-                      kvm->arch.intel_iommu_domain);
-
-       if (kvm_iommu_map_memslots(kvm)) {
-               kvm_iommu_unmap_memslots(kvm);
+       vtd_dom = get_kvm_vtd_domain(kvm, pdev);
+       if (!vtd_dom)
                return -EFAULT;
-       }
 
-       if (kvm_intel_iommu_context_mapping(
-               kvm->arch.intel_iommu_domain, pdev)) {
-               printk(KERN_ERR "Domain context map for %s failed",
-                      pci_name(pdev));
-               return -EFAULT;
-       }
-       return 0;
-}
-EXPORT_SYMBOL_GPL(kvm_iommu_map_guest);
+       vtd_dev = kzalloc(sizeof(struct kvm_vtd_pt_dev_list),
GFP_KERNEL);
+       if (!vtd_dev)
+               return -ENOMEM;
+       vtd_dev->dev_info.busnr = pdev->bus->number;
+       vtd_dev->dev_info.devfn = pdev->devfn;
 
-static int kvm_iommu_put_pages(struct kvm *kvm,
-       gfn_t base_gfn, unsigned long npages)
-{
-       gfn_t gfn = base_gfn;
-       struct page *page;
-       int i;
+       spin_lock(&kvm_vtd_domain_lock);
+       list_add(&vtd_dev->list, &vtd_dom->pci_pt_devices);
+       spin_unlock(&kvm_vtd_domain_lock);
 
-       if (!kvm->arch.intel_iommu_domain)
+       /* detach device, because it may be used by host */
+       kvm_intel_iommu_detach_dev(vtd_dom->domain,
+                                  pdev->bus->number, pdev->devfn);
+       if (kvm_intel_iommu_context_mapping(vtd_dom->domain, pdev)) {
+               printk(KERN_ERR "Domain context map for %s failed",
+                      pci_name(pdev));
                return -EFAULT;
-
-       printk(KERN_DEBUG "kvm_iommu_put_pages: gpa = %lx\n",
-               gfn << PAGE_SHIFT);
-       printk(KERN_DEBUG "kvm_iommu_put_pages: hpa = %lx\n",
-               gfn_to_pfn(kvm, gfn) << PAGE_SHIFT);
-       printk(KERN_DEBUG "kvm_iommu_put_pages: size = %lx\n",
-               npages*PAGE_SIZE);
-
-       for (i = 0; i < npages; i++) {
-               page = gfn_to_page(kvm, gfn);
-               put_page(page);
-               gfn++;
        }
-       return 0;
-}
 
-static int kvm_iommu_unmap_memslots(struct kvm *kvm)
-{
-       int i, rc;
-       for (i = 0; i < kvm->nmemslots; i++) {
-               rc = kvm_iommu_put_pages(kvm, kvm->memslots[i].base_gfn,
-                               kvm->memslots[i].npages);
-               if (rc)
-                       return rc;
-       }
        return 0;
 }
+EXPORT_SYMBOL_GPL(kvm_iommu_map_guest);
 
 int kvm_iommu_unmap_guest(struct kvm *kvm)
 {
-       struct kvm_pci_pt_dev_list *entry;
-       struct pci_dev *pdev = NULL;
+       struct kvm_vtd_domain *vtd_dom;
+       struct kvm_vtd_pt_dev_list *vtd_dev;
+       struct list_head *pdev1, *pdev2;
 
-       if (!kvm->arch.intel_iommu_domain)
+       if (list_empty(&kvm->arch.vtd_domains))
                return 0;
 
-       list_for_each_entry(entry, &kvm->arch.pci_pt_dev_head, list) {
-               printk(KERN_DEBUG "kvm_iommu_unmap_guest: %x:%x:%x\n",
-                       entry->pt_dev.host.busnr,
-                       PCI_SLOT(entry->pt_dev.host.devfn),
-                       PCI_FUNC(entry->pt_dev.host.devfn));
+       spin_lock(&kvm_vtd_domain_lock);
 
-               for_each_pci_dev(pdev) {
-                       if ((pdev->bus->number ==
entry->pt_dev.host.busnr) &&
-                               (pdev->devfn ==
entry->pt_dev.host.devfn))
-                               goto found;
-               }
-               return -ENODEV;
-found:
-               if (pdev == NULL) {
-                       printk("kvm_iommu_unmap_guest: pdev == NULL\n");
-                       return -EFAULT;
+       list_for_each_entry(vtd_dom, &kvm->arch.vtd_domains, list) {
+               list_for_each_safe(pdev1, pdev2,
&vtd_dom->pci_pt_devices) {
+                       vtd_dev = list_entry(pdev1,
+                               struct kvm_vtd_pt_dev_list, list);
+                       kvm_intel_iommu_detach_dev(vtd_dom->domain,
+                               vtd_dev->dev_info.busnr,
+                               vtd_dev->dev_info.devfn);
+                       list_del(&vtd_dev->list);
+                       kfree(vtd_dev);
                }
 
-               /* detach kvm dmar domain */
-               kvm_intel_iommu_detach_dev(
-                               kvm->arch.intel_iommu_domain,
-                               pdev->bus->number, pdev->devfn);
+               kvm_iommu_unmap_domain_memslots(kvm, vtd_dom->domain);
+               kvm_intel_iommu_domain_exit(vtd_dom->domain);
        }
-       kvm_iommu_unmap_memslots(kvm);
-       kvm_intel_iommu_domain_exit(kvm->arch.intel_iommu_domain);
+
+       spin_unlock(&kvm_vtd_domain_lock);
        return 0;
 }
 EXPORT_SYMBOL_GPL(kvm_iommu_unmap_guest);
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 37e3a66..1bc41d9 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -386,9 +386,6 @@ static void kvm_free_pci_passthrough(struct kvm
*kvm)
                kfree(pci_pt_dev);
        }
        write_unlock_irqrestore(&kvm_pci_pt_lock, flags);
-
-       if (kvm_intel_iommu_found())
-               kvm->arch.intel_iommu_domain = NULL;
 }
 
 unsigned long segment_base(u16 selector)
@@ -4251,6 +4248,7 @@ struct  kvm *kvm_arch_create_vm(void)
 
        INIT_LIST_HEAD(&kvm->arch.active_mmu_pages);
        INIT_LIST_HEAD(&kvm->arch.pci_pt_dev_head);
+       INIT_LIST_HEAD(&kvm->arch.vtd_domains);
 
        return kvm;
 }
diff --git a/include/asm-x86/kvm_host.h b/include/asm-x86/kvm_host.h
index a2f1e3e..00b43a3 100644
--- a/include/asm-x86/kvm_host.h
+++ b/include/asm-x86/kvm_host.h
@@ -343,6 +343,18 @@ struct kvm_pci_pt_dev_list {
        struct kvm_pci_passthrough_dev_kernel pt_dev;
 };
 
+struct kvm_vtd_pt_dev_list {
+       struct list_head list;
+       struct kvm_pci_pt_info dev_info;
+};
+
+/* devices under the same iommu uses the same dmar_domain */
+struct kvm_vtd_domain {
+       struct list_head list;
+       struct list_head pci_pt_devices; /* assigned devices */
+       struct dmar_domain *domain;      /* pointer to domain */
+};
+
 struct kvm_arch{
        int naliases;
        struct kvm_mem_alias aliases[KVM_ALIAS_SLOTS];
@@ -356,7 +368,7 @@ struct kvm_arch{
         */
        struct list_head active_mmu_pages;
        struct list_head pci_pt_dev_head;
-       struct dmar_domain *intel_iommu_domain;
+       struct list_head vtd_domains;
        struct kvm_pic *vpic;
        struct kvm_ioapic *vioapic;
        struct kvm_pit *vpit;
-- 
1.5.1
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to