From: Joerg Roedel <jroedel@xxxxxxx> This allows to to ioremap_nocache and iounmap in the same path, so we don't need to collect all pointers returned from ioremap to free them after the spinlock is released. Tested-by: Baoquan He <bhe@xxxxxxxxxx> Signed-off-by: Joerg Roedel <jroedel@xxxxxxx> --- drivers/iommu/intel-iommu.c | 127 +++++++++++++++++++++++--------------------- 1 file changed, 66 insertions(+), 61 deletions(-) diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index c0b72e8..2602b33 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -380,17 +380,11 @@ struct iommu_remapped_entry { void __iomem *mem; }; static LIST_HEAD(__iommu_remapped_mem); -static DEFINE_MUTEX(__iommu_mem_list_lock); -/* ======================================================================== +/* * Copy iommu translation tables from old kernel into new kernel. * Entry to this set of functions is: intel_iommu_load_translation_tables() - * ------------------------------------------------------------------------ */ - -static int copy_root_entry_table(struct intel_iommu *iommu, - struct root_entry *old_re); - static int intel_iommu_load_translation_tables(struct intel_iommu *iommu); static void unmap_device_dma(struct dmar_domain *domain, @@ -4958,68 +4952,41 @@ static struct context_entry *device_to_existing_context_entry( /* * Copy memory from a physically-addressed area into a virtually-addressed area */ -static int __iommu_load_from_oldmem(void *to, unsigned long from, - unsigned long size) +static int copy_from_oldmem_phys(void *to, phys_addr_t from, size_t size) { - unsigned long pfn; /* Page Frame Number */ - size_t csize = (size_t)size; /* Num(bytes to copy) */ - unsigned long offset; /* Lower 12 bits of to */ void __iomem *virt_mem; - struct iommu_remapped_entry *mapped; + unsigned long offset; + unsigned long pfn; - pfn = from >> VTD_PAGE_SHIFT; + pfn = from >> VTD_PAGE_SHIFT; offset = from & (~VTD_PAGE_MASK); if (page_is_ram(pfn)) { - memcpy(to, pfn_to_kaddr(pfn) + offset, csize); - } else{ - - mapped = kzalloc(sizeof(struct iommu_remapped_entry), - GFP_KERNEL); - if (!mapped) - return -ENOMEM; - + memcpy(to, pfn_to_kaddr(pfn) + offset, size); + } else { virt_mem = ioremap_cache((unsigned long)from, size); - if (!virt_mem) { - kfree(mapped); + if (!virt_mem) return -ENOMEM; - } + memcpy(to, virt_mem, size); - mutex_lock(&__iommu_mem_list_lock); - mapped->mem = virt_mem; - list_add_tail(&mapped->list, &__iommu_remapped_mem); - mutex_unlock(&__iommu_mem_list_lock); + iounmap(virt_mem); } - return size; -} -/* - * Free the mapped memory for ioremap; - */ -static int __iommu_free_mapped_mem(void) -{ - struct iommu_remapped_entry *mem_entry, *tmp; - - mutex_lock(&__iommu_mem_list_lock); - list_for_each_entry_safe(mem_entry, tmp, &__iommu_remapped_mem, list) { - iounmap(mem_entry->mem); - list_del(&mem_entry->list); - kfree(mem_entry); - } - mutex_unlock(&__iommu_mem_list_lock); - return 0; + return size; } /* - * Load root entry tables from old kernel. + * Load context entry tables from old kernel. */ -static int copy_root_entry_table(struct intel_iommu *iommu, - struct root_entry *old_re) +static int copy_context_tables(struct intel_iommu *iommu, + struct context_entry **tbl, + struct root_entry *old_re) { struct context_entry *context_new_virt; - unsigned long context_old_phys; + phys_addr_t context_old_phys; u32 bus; + int ret; for (bus = 0; bus < 256; bus++, old_re++) { if (!root_present(old_re)) @@ -5030,22 +4997,48 @@ static int copy_root_entry_table(struct intel_iommu *iommu, if (!context_old_phys) continue; + ret = -ENOMEM; context_new_virt = alloc_pgtable_page(iommu->node); - if (!context_new_virt) - return -ENOMEM; - - __iommu_load_from_oldmem(context_new_virt, - context_old_phys, - VTD_PAGE_SIZE); + goto out_err; + + ret = copy_from_oldmem_phys(context_new_virt, context_old_phys, + VTD_PAGE_SIZE); + if (ret != VTD_PAGE_SIZE) { + pr_err("Failed to copy context table for bus %d from physical address 0x%llx\n", + bus, context_old_phys); + free_pgtable_page(context_new_virt); + continue; + } __iommu_flush_cache(iommu, context_new_virt, VTD_PAGE_SIZE); - set_root_value(&iommu->root_entry[bus], - virt_to_phys(context_new_virt)); + tbl[bus] = context_new_virt; } return 0; + +out_err: + for (bus = 0; bus < 256; bus++) { + free_pgtable_page(tbl[bus]); + tbl[bus] = NULL; + } + + return ret; +} + +static void update_root_entry_table(struct intel_iommu *iommu, + struct context_entry **tbl) +{ + struct root_entry *re = iommu->root_entry; + u32 bus; + + for (bus = 0; bus < 256; bus++, re++) { + if (!tbl[bus]) + continue; + + set_root_value(re, virt_to_phys(tbl[bus])); + } } /* @@ -5054,6 +5047,7 @@ static int copy_root_entry_table(struct intel_iommu *iommu, */ static int intel_iommu_load_translation_tables(struct intel_iommu *iommu) { + struct context_entry **ctxt_tbls; struct root_entry *old_re; phys_addr_t old_re_phys; unsigned long flags; @@ -5074,16 +5068,27 @@ static int intel_iommu_load_translation_tables(struct intel_iommu *iommu) return -ENOMEM; } + ret = -ENOMEM; + ctxt_tbls = kzalloc(256 * sizeof(void *), GFP_KERNEL); + if (!ctxt_tbls) + goto out_unmap; + + ret = copy_context_tables(iommu, ctxt_tbls, old_re); + if (ret) + goto out_free; + spin_lock_irqsave(&iommu->lock, flags); - ret = copy_root_entry_table(iommu, old_re); + update_root_entry_table(iommu, ctxt_tbls); __iommu_flush_cache(iommu, iommu->root_entry, PAGE_SIZE); spin_unlock_irqrestore(&iommu->lock, flags); - iounmap(old_re); +out_free: + kfree(ctxt_tbls); - __iommu_free_mapped_mem(); +out_unmap: + iounmap(old_re); return ret; } -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-pci" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html