| From 5be0e66269ddfd8ca3dee6512e573b1f461a6faf Mon Sep 17 00:00:00 2001 |
| From: Nicolas Pitre <nicolas.pitre@linaro.org> |
| Date: Fri, 16 Sep 2011 01:14:23 -0400 |
| Subject: ARM: add generic ioremap optimization by reusing static mappings |
| |
| Now that we have all the static mappings from iotable_init() located |
| in the vmalloc area, it is trivial to optimize ioremap by reusing those |
| static mappings when the requested physical area fits in one of them, |
| and so in a generic way for all platforms. |
| |
| Signed-off-by: Nicolas Pitre <nicolas.pitre@linaro.org> |
| Tested-by: Stephen Warren <swarren@nvidia.com> |
| Tested-by: Kevin Hilman <khilman@ti.com> |
| Tested-by: Jamie Iles <jamie@jamieiles.com> |
| (cherry picked from commit 576d2f2525612ecb5af029a76f21f22a3b82563d) |
| |
| Signed-off-by: Simon Horman <horms@verge.net.au> |
| --- |
| arch/arm/mm/ioremap.c | 72 ++++++++++++++++++++++++++++++++----------------- |
| arch/arm/mm/mm.h | 14 ++++++++++ |
| arch/arm/mm/mmu.c | 3 ++- |
| 3 files changed, 64 insertions(+), 25 deletions(-) |
| |
| diff --git a/arch/arm/mm/ioremap.c b/arch/arm/mm/ioremap.c |
| index 1ddcd8a..b5900f2 100644 |
| --- a/arch/arm/mm/ioremap.c |
| +++ b/arch/arm/mm/ioremap.c |
| @@ -36,12 +36,6 @@ |
| #include <asm/mach/map.h> |
| #include "mm.h" |
| |
| -/* |
| - * Used by ioremap() and iounmap() code to mark (super)section-mapped |
| - * I/O regions in vm_struct->flags field. |
| - */ |
| -#define VM_ARM_SECTION_MAPPING 0x80000000 |
| - |
| int ioremap_page(unsigned long virt, unsigned long phys, |
| const struct mem_type *mtype) |
| { |
| @@ -201,12 +195,6 @@ void __iomem * __arm_ioremap_pfn_caller(unsigned long pfn, |
| if (pfn >= 0x100000 && (__pfn_to_phys(pfn) & ~SUPERSECTION_MASK)) |
| return NULL; |
| |
| - /* |
| - * Don't allow RAM to be mapped - this causes problems with ARMv6+ |
| - */ |
| - if (WARN_ON(pfn_valid(pfn))) |
| - return NULL; |
| - |
| type = get_mem_type(mtype); |
| if (!type) |
| return NULL; |
| @@ -216,6 +204,34 @@ void __iomem * __arm_ioremap_pfn_caller(unsigned long pfn, |
| */ |
| size = PAGE_ALIGN(offset + size); |
| |
| + /* |
| + * Try to reuse one of the static mapping whenever possible. |
| + */ |
| + read_lock(&vmlist_lock); |
| + for (area = vmlist; area; area = area->next) { |
| + if (!size || (sizeof(phys_addr_t) == 4 && pfn >= 0x100000)) |
| + break; |
| + if (!(area->flags & VM_ARM_STATIC_MAPPING)) |
| + continue; |
| + if ((area->flags & VM_ARM_MTYPE_MASK) != VM_ARM_MTYPE(mtype)) |
| + continue; |
| + if (__phys_to_pfn(area->phys_addr) > pfn || |
| + __pfn_to_phys(pfn) + size-1 > area->phys_addr + area->size-1) |
| + continue; |
| + /* we can drop the lock here as we know *area is static */ |
| + read_unlock(&vmlist_lock); |
| + addr = (unsigned long)area->addr; |
| + addr += __pfn_to_phys(pfn) - area->phys_addr; |
| + return (void __iomem *) (offset + addr); |
| + } |
| + read_unlock(&vmlist_lock); |
| + |
| + /* |
| + * Don't allow RAM to be mapped - this causes problems with ARMv6+ |
| + */ |
| + if (WARN_ON(pfn_valid(pfn))) |
| + return NULL; |
| + |
| area = get_vm_area_caller(size, VM_IOREMAP, caller); |
| if (!area) |
| return NULL; |
| @@ -292,26 +308,34 @@ EXPORT_SYMBOL(__arm_ioremap); |
| void __iounmap(volatile void __iomem *io_addr) |
| { |
| void *addr = (void *)(PAGE_MASK & (unsigned long)io_addr); |
| -#ifndef CONFIG_SMP |
| struct vm_struct *vm; |
| |
| - /* |
| - * If this is a section based mapping we need to handle it |
| - * specially as the VM subsystem does not know how to handle |
| - * such a beast. |
| - */ |
| read_lock(&vmlist_lock); |
| for (vm = vmlist; vm; vm = vm->next) { |
| - if ((vm->flags & VM_IOREMAP) && (vm->addr == addr)) { |
| - if (vm->flags & VM_ARM_SECTION_MAPPING) { |
| - unmap_area_sections((unsigned long)vm->addr, |
| - vm->size); |
| - } |
| + if (vm->addr > addr) |
| break; |
| + if (!(vm->flags & VM_IOREMAP)) |
| + continue; |
| + /* If this is a static mapping we must leave it alone */ |
| + if ((vm->flags & VM_ARM_STATIC_MAPPING) && |
| + (vm->addr <= addr) && (vm->addr + vm->size > addr)) { |
| + read_unlock(&vmlist_lock); |
| + return; |
| } |
| +#ifndef CONFIG_SMP |
| + /* |
| + * If this is a section based mapping we need to handle it |
| + * specially as the VM subsystem does not know how to handle |
| + * such a beast. |
| + */ |
| + if ((vm->addr == addr) && |
| + (vm->flags & VM_ARM_SECTION_MAPPING)) { |
| + unmap_area_sections((unsigned long)vm->addr, vm->size); |
| + break; |
| + } |
| +#endif |
| } |
| read_unlock(&vmlist_lock); |
| -#endif |
| |
| vunmap(addr); |
| } |
| diff --git a/arch/arm/mm/mm.h b/arch/arm/mm/mm.h |
| index 5b3d7d5..26f3a2c 100644 |
| --- a/arch/arm/mm/mm.h |
| +++ b/arch/arm/mm/mm.h |
| @@ -21,6 +21,20 @@ const struct mem_type *get_mem_type(unsigned int type); |
| |
| extern void __flush_dcache_page(struct address_space *mapping, struct page *page); |
| |
| +/* |
| + * ARM specific vm_struct->flags bits. |
| + */ |
| + |
| +/* (super)section-mapped I/O regions used by ioremap()/iounmap() */ |
| +#define VM_ARM_SECTION_MAPPING 0x80000000 |
| + |
| +/* permanent static mappings from iotable_init() */ |
| +#define VM_ARM_STATIC_MAPPING 0x40000000 |
| + |
| +/* mapping type (attributes) for permanent static mappings */ |
| +#define VM_ARM_MTYPE(mt) ((mt) << 20) |
| +#define VM_ARM_MTYPE_MASK (0x1f << 20) |
| + |
| #endif |
| |
| void __init bootmem_init(void); |
| diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c |
| index 6a98c4a..ce0e427 100644 |
| --- a/arch/arm/mm/mmu.c |
| +++ b/arch/arm/mm/mmu.c |
| @@ -741,7 +741,8 @@ void __init iotable_init(struct map_desc *io_desc, int nr) |
| vm->addr = (void *)(md->virtual & PAGE_MASK); |
| vm->size = PAGE_ALIGN(md->length + (md->virtual & ~PAGE_MASK)); |
| vm->phys_addr = __pfn_to_phys(md->pfn); |
| - vm->flags = VM_IOREMAP; |
| + vm->flags = VM_IOREMAP | VM_ARM_STATIC_MAPPING; |
| + vm->flags |= VM_ARM_MTYPE(md->type); |
| vm->caller = iotable_init; |
| vm_area_add_early(vm++); |
| } |
| -- |
| 1.7.10.1.362.g242cab3 |
| |