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