From 27a3f0e91bed0f4dcf0a363e5f5938126d1ff4e5 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 25 Aug 2011 19:10:29 -0400 Subject: ARM: sort the meminfo array earlier The meminfo array has to be sorted before sanity_check_meminfo() in arch/arm/mm/mmu.c is called for it to work properly. This also allows for a simpler find_limits() in arch/arm/mm/init.c. The sort is moved to arch/arm/kernel/setup.c because that's where the meminfo array is populated. Eventually this should be improved upon to make the memory bank parser a bit more robust against problems such as overlapping memory ranges. Signed-off-by: Nicolas Pitre --- arch/arm/mm/init.c | 38 ++++++++------------------------------ 1 file changed, 8 insertions(+), 30 deletions(-) (limited to 'arch/arm/mm') diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index fbdd12ea3a58..d366051e14fe 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include @@ -134,30 +133,18 @@ void show_mem(unsigned int filter) } static void __init find_limits(unsigned long *min, unsigned long *max_low, - unsigned long *max_high) + unsigned long *max_high) { struct meminfo *mi = &meminfo; int i; - *min = -1UL; - *max_low = *max_high = 0; - - for_each_bank (i, mi) { - struct membank *bank = &mi->bank[i]; - unsigned long start, end; - - start = bank_pfn_start(bank); - end = bank_pfn_end(bank); - - if (*min > start) - *min = start; - if (*max_high < end) - *max_high = end; - if (bank->highmem) - continue; - if (*max_low < end) - *max_low = end; - } + /* This assumes the meminfo array is properly sorted */ + *min = bank_pfn_start(&mi->bank[0]); + for_each_bank (i, mi) + if (mi->bank[i].highmem) + break; + *max_low = bank_pfn_end(&mi->bank[i - 1]); + *max_high = bank_pfn_end(&mi->bank[mi->nr_banks - 1]); } static void __init arm_bootmem_init(unsigned long start_pfn, @@ -319,19 +306,10 @@ static void arm_memory_present(void) } #endif -static int __init meminfo_cmp(const void *_a, const void *_b) -{ - const struct membank *a = _a, *b = _b; - long cmp = bank_pfn_start(a) - bank_pfn_start(b); - return cmp < 0 ? -1 : cmp > 0 ? 1 : 0; -} - void __init arm_memblock_init(struct meminfo *mi, struct machine_desc *mdesc) { int i; - sort(&meminfo.bank, meminfo.nr_banks, sizeof(meminfo.bank[0]), meminfo_cmp, NULL); - memblock_init(); for (i = 0; i < mi->nr_banks; i++) memblock_add(mi->bank[i].start, mi->bank[i].size); -- cgit v1.2.3-55-g7522 From 55a8173cfe1c6b489f8f5705282c762aed2e265e Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Sun, 18 Sep 2011 22:40:00 -0400 Subject: ARM: move initialization of the high_memory variable earlier Some upcoming changes must know the VMALLOC_START value, which is based on high_memory, before bootmem_init() is called. The best location to set it is in sanity_check_meminfo() where the needed computation is already done, and in the non MMU case it is trivial to do now that the meminfo array is already sorted at that point. Signed-off-by: Nicolas Pitre --- arch/arm/mm/init.c | 2 -- arch/arm/mm/mmu.c | 1 + arch/arm/mm/nommu.c | 2 ++ 3 files changed, 3 insertions(+), 2 deletions(-) (limited to 'arch/arm/mm') diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index d366051e14fe..786adddf1a86 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -381,8 +381,6 @@ void __init bootmem_init(void) */ arm_bootmem_free(min, max_low, max_high); - high_memory = __va(((phys_addr_t)max_low << PAGE_SHIFT) - 1) + 1; - /* * This doesn't seem to be used by the Linux memory manager any * more, but is used by ll_rw_block. If we can get rid of it, we diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c index dc8c550e6cbd..0aa8b7d5b21d 100644 --- a/arch/arm/mm/mmu.c +++ b/arch/arm/mm/mmu.c @@ -860,6 +860,7 @@ void __init sanity_check_meminfo(void) } #endif meminfo.nr_banks = j; + high_memory = __va(lowmem_limit - 1) + 1; memblock_set_current_limit(lowmem_limit); } diff --git a/arch/arm/mm/nommu.c b/arch/arm/mm/nommu.c index 941a98c9e8aa..9348730a6ba5 100644 --- a/arch/arm/mm/nommu.c +++ b/arch/arm/mm/nommu.c @@ -29,6 +29,8 @@ void __init arm_mm_memblock_reserve(void) void __init sanity_check_meminfo(void) { + phys_addr_t end = bank_phys_end(&meminfo.bank[meminfo.nr_banks - 1]); + high_memory = __va(end - 1) + 1; } /* -- cgit v1.2.3-55-g7522 From 0536bdf33faff4d940ac094c77998cfac368cfff Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 25 Aug 2011 00:35:59 -0400 Subject: ARM: move iotable mappings within the vmalloc region In order to remove the build time variation between different SOCs with regards to VMALLOC_END, the iotable mappings are now allocated inside the vmalloc region. This allows for VMALLOC_END to be identical across all machines. The value for VMALLOC_END is now set to 0xff000000 which is right where the consistent DMA area starts. To accommodate all static mappings on machines with possible highmem usage, the default vmalloc area size is changed to 240 MB so that VMALLOC_START is no higher than 0xf0000000 by default. Signed-off-by: Nicolas Pitre Tested-by: Stephen Warren Tested-by: Kevin Hilman Tested-by: Jamie Iles --- Documentation/arm/memory.txt | 11 +++++----- arch/arm/include/asm/pgtable.h | 8 +------ arch/arm/mm/mmu.c | 49 ++++++++++++++++++++++++++++++------------ 3 files changed, 41 insertions(+), 27 deletions(-) (limited to 'arch/arm/mm') diff --git a/Documentation/arm/memory.txt b/Documentation/arm/memory.txt index 771d48d3b335..208a2d465b92 100644 --- a/Documentation/arm/memory.txt +++ b/Documentation/arm/memory.txt @@ -51,15 +51,14 @@ ffc00000 ffefffff DMA memory mapping region. Memory returned ff000000 ffbfffff Reserved for future expansion of DMA mapping region. -VMALLOC_END feffffff Free for platform use, recommended. - VMALLOC_END must be aligned to a 2MB - boundary. - VMALLOC_START VMALLOC_END-1 vmalloc() / ioremap() space. Memory returned by vmalloc/ioremap will be dynamically placed in this region. - VMALLOC_START may be based upon the value - of the high_memory variable. + Machine specific static mappings are also + located here through iotable_init(). + VMALLOC_START is based upon the value + of the high_memory variable, and VMALLOC_END + is equal to 0xff000000. PAGE_OFFSET high_memory-1 Kernel direct-mapped RAM region. This maps the platforms RAM, and typically diff --git a/arch/arm/include/asm/pgtable.h b/arch/arm/include/asm/pgtable.h index 9451dce3a553..6cdd55cb0b8c 100644 --- a/arch/arm/include/asm/pgtable.h +++ b/arch/arm/include/asm/pgtable.h @@ -21,7 +21,6 @@ #else #include -#include #include #include @@ -33,15 +32,10 @@ * any out-of-bounds memory accesses will hopefully be caught. * The vmalloc() routines leaves a hole of 4kB between each vmalloced * area for the same reason. ;) - * - * Note that platforms may override VMALLOC_START, but they must provide - * VMALLOC_END. VMALLOC_END defines the (exclusive) limit of this space, - * which may not overlap IO space. */ -#ifndef VMALLOC_START #define VMALLOC_OFFSET (8*1024*1024) #define VMALLOC_START (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)) -#endif +#define VMALLOC_END 0xff000000UL #define LIBRARY_TEXT_START 0x0c000000 diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c index 0aa8b7d5b21d..c61481577ae1 100644 --- a/arch/arm/mm/mmu.c +++ b/arch/arm/mm/mmu.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -529,13 +530,18 @@ EXPORT_SYMBOL(phys_mem_access_prot); #define vectors_base() (vectors_high() ? 0xffff0000 : 0) -static void __init *early_alloc(unsigned long sz) +static void __init *early_alloc_aligned(unsigned long sz, unsigned long align) { - void *ptr = __va(memblock_alloc(sz, sz)); + void *ptr = __va(memblock_alloc(sz, align)); memset(ptr, 0, sz); return ptr; } +static void __init *early_alloc(unsigned long sz) +{ + return early_alloc_aligned(sz, sz); +} + static pte_t * __init early_pte_alloc(pmd_t *pmd, unsigned long addr, unsigned long prot) { if (pmd_none(*pmd)) { @@ -685,9 +691,10 @@ static void __init create_mapping(struct map_desc *md) } if ((md->type == MT_DEVICE || md->type == MT_ROM) && - md->virtual >= PAGE_OFFSET && md->virtual < VMALLOC_END) { + md->virtual >= PAGE_OFFSET && + (md->virtual < VMALLOC_START || md->virtual >= VMALLOC_END)) { printk(KERN_WARNING "BUG: mapping for 0x%08llx" - " at 0x%08lx overlaps vmalloc space\n", + " at 0x%08lx out of vmalloc space\n", (long long)__pfn_to_phys((u64)md->pfn), md->virtual); } @@ -729,18 +736,32 @@ static void __init create_mapping(struct map_desc *md) */ void __init iotable_init(struct map_desc *io_desc, int nr) { - int i; + struct map_desc *md; + struct vm_struct *vm; + + if (!nr) + return; - for (i = 0; i < nr; i++) - create_mapping(io_desc + i); + vm = early_alloc_aligned(sizeof(*vm) * nr, __alignof__(*vm)); + + for (md = io_desc; nr; md++, nr--) { + create_mapping(md); + 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->caller = iotable_init; + vm_area_add_early(vm++); + } } -static void * __initdata vmalloc_min = (void *)(VMALLOC_END - SZ_128M); +static void * __initdata vmalloc_min = + (void *)(VMALLOC_END - (240 << 20) - VMALLOC_OFFSET); /* * vmalloc=size forces the vmalloc area to be exactly 'size' * bytes. This can be used to increase (or decrease) the vmalloc - * area - the default is 128m. + * area - the default is 240m. */ static int __init early_vmalloc(char *arg) { @@ -891,10 +912,10 @@ static inline void prepare_page_table(void) /* * Clear out all the kernel space mappings, except for the first - * memory bank, up to the end of the vmalloc region. + * memory bank, up to the vmalloc region. */ for (addr = __phys_to_virt(end); - addr < VMALLOC_END; addr += PMD_SIZE) + addr < VMALLOC_START; addr += PMD_SIZE) pmd_clear(pmd_off_k(addr)); } @@ -921,8 +942,8 @@ void __init arm_mm_memblock_reserve(void) } /* - * Set up device the mappings. Since we clear out the page tables for all - * mappings above VMALLOC_END, we will remove any debug device mappings. + * Set up the device mappings. Since we clear out the page tables for all + * mappings above VMALLOC_START, we will remove any debug device mappings. * This means you have to be careful how you debug this function, or any * called function. This means you can't use any function or debugging * method which may touch any device, otherwise the kernel _will_ crash. @@ -937,7 +958,7 @@ static void __init devicemaps_init(struct machine_desc *mdesc) */ vectors_page = early_alloc(PAGE_SIZE); - for (addr = VMALLOC_END; addr; addr += PMD_SIZE) + for (addr = VMALLOC_START; addr; addr += PMD_SIZE) pmd_clear(pmd_off_k(addr)); /* -- cgit v1.2.3-55-g7522 From 6ee723a6570a897208b76ab3e9a495e9106b2f8c Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 15 Sep 2011 22:12:19 -0400 Subject: ARM: simplify __iounmap() when dealing with section based mapping Firstly, there is no need to have a double pointer here as we're only walking the vmlist and not modifying it. Secondly, for the same reason, we don't need a write lock but only a read lock here, since the lock only protects the coherency of the list nothing else. Lastly, the reason for holding a lock is not what the comment says, so let's remove that misleading piece of information. Signed-off-by: Nicolas Pitre --- arch/arm/mm/ioremap.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) (limited to 'arch/arm/mm') diff --git a/arch/arm/mm/ioremap.c b/arch/arm/mm/ioremap.c index bdb248c4f55c..bc7d9bd766d1 100644 --- a/arch/arm/mm/ioremap.c +++ b/arch/arm/mm/ioremap.c @@ -314,26 +314,24 @@ void __iounmap(volatile void __iomem *io_addr) { void *addr = (void *)(PAGE_MASK & (unsigned long)io_addr); #ifndef CONFIG_SMP - struct vm_struct **p, *tmp; + 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. We need the lock here b/c we need to clear - * all the mappings before the area can be reclaimed - * by someone else. + * such a beast. */ - write_lock(&vmlist_lock); - for (p = &vmlist ; (tmp = *p) ; p = &tmp->next) { - if ((tmp->flags & VM_IOREMAP) && (tmp->addr == addr)) { - if (tmp->flags & VM_ARM_SECTION_MAPPING) { - unmap_area_sections((unsigned long)tmp->addr, - tmp->size); + 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); } break; } } - write_unlock(&vmlist_lock); + read_unlock(&vmlist_lock); #endif vunmap(addr); -- cgit v1.2.3-55-g7522 From 576d2f2525612ecb5af029a76f21f22a3b82563d Mon Sep 17 00:00:00 2001 From: Nicolas Pitre 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 Tested-by: Stephen Warren Tested-by: Kevin Hilman Tested-by: Jamie Iles --- 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(-) (limited to 'arch/arm/mm') diff --git a/arch/arm/mm/ioremap.c b/arch/arm/mm/ioremap.c index bc7d9bd766d1..12c7ad215ce7 100644 --- a/arch/arm/mm/ioremap.c +++ b/arch/arm/mm/ioremap.c @@ -36,12 +36,6 @@ #include #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; @@ -313,26 +329,34 @@ __arm_ioremap_exec(unsigned long phys_addr, size_t size, bool cached) 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 ad7cce3bc431..70f6d3ea4834 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 #ifdef CONFIG_ZONE_DMA diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c index c61481577ae1..27e366af67f9 100644 --- a/arch/arm/mm/mmu.c +++ b/arch/arm/mm/mmu.c @@ -749,7 +749,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++); } -- cgit v1.2.3-55-g7522