Weidong,
There seems to be line wrapping issues with the patch.
Please resend it.
Thanks,
Ben
On Thu, 2008-07-03 at 17:05 +0800, Han, Weidong wrote:
> 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;
--
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