Intermix Lowmem and Vmalloc
Subsystems (4)
v2 cover Intermix Lowmem and Vmalloc
Hi,
This is v2 of a patch series to allow lowmem and vmalloc to be
intermixed (http://lists.infradead.org/pipermail/linux-arm-kernel/2013-November/210578.html
provides the full decriptions which I've omitted here for brevity)
Nots I forgot to mention before:
- as a side effect of how CMA is setup, the remapped lowmem regions are explicitly printed out in /proc/vmallocinfo which has turned out to be useful for debugging. I couldn't find a good way to support those and the lowmem regions in vmalloc short of adding partial region unmapping.
- I still have a nagging concern about regions that straddle the lowmem/highmem boundary causing problems. I haven't been able to find a concrete issue yet though..
v2: Fixed several comments by Kyungmin Park which led me to discover several issues with the is_vmalloc_addr implementation. is_vmalloc_addr is probably the ugliest part of the entire series and I debated if adding extra vmalloc flags would make it less ugly.
Again, comments or suggestions on how to better accomplish this are welcome.
Thanks,
Laura
RFC PATCH v2 1/4 arm: mm: Add iotable_init_novmreserve
iotable_init is currently used by dma_contiguous_remap to remap CMA memory appropriately. This has the side effect of reserving the area of CMA in the vmalloc tracking structures. This is fine under normal circumstances but it creates conflicts if we want to track lowmem in vmalloc. Since dma_contiguous_remap is only really concerned with the remapping, introduce iotable_init_novmreserve to allow remapping of pages without reserving the virtual address in vmalloc space.
--- arch/arm/include/asm/mach/map.h | 2 ++ arch/arm/mm/dma-mapping.c | 2 +- arch/arm/mm/ioremap.c | 5 +++-- arch/arm/mm/mm.h | 2 +- arch/arm/mm/mmu.c | 17 ++++++++++++++--- 5 files changed, 21 insertions(+), 7 deletions(-)
arch/arm/include/asm/mach/map.h +2 -0
@@ -37,6 +37,7 @@ struct map_desc {#ifdef CONFIG_MMU extern void iotable_init (struct map_desc *,int ); +extern void iotable_init_novmreserve (struct map_desc *,int );extern void vm_reserve_area_early (unsigned long addr,unsigned long size,void *caller);
@@ -56,6 +57,7 @@ extern int ioremap_page(unsigned long virt, unsigned long phys, const struct mem_type *mtype);#else #define iotable_init (map,num) do { } while (0) +#define iotable_init_novmreserve (map,num) do { } while(0)#define vm_reserve_area_early (a,s,c) do { } while (0)#endif
arch/arm/mm/dma-mapping.c +1 -1
@@ -435,7 +435,7 @@ void __init dma_contiguous_remap(void) addr += PMD_SIZE)pmd_clear (pmd_off_k (addr)); -iotable_init (&map,1 ); +iotable_init_novmreserve (&map,1 ); } }
arch/arm/mm/ioremap.c +3 -2
@@ -84,14 +84,15 @@ struct static_vm *find_static_vm_vaddr(void *vaddr)return NULL ; } -void __init add_static_vm_early (struct static_vm *svm) +void __init add_static_vm_early (struct static_vm *svm,bool add_to_vm) {struct static_vm *curr_svm;struct vm_struct *vm;void *vaddr; vm = &svm->vm; -vm_area_add_early (vm); +if (add_to_vm) +vm_area_add_early (vm); vaddr = vm->addr;list_for_each_entry (curr_svm, &static_vmlist, list) {
arch/arm/mm/mm.h +1 -1
@@ -75,7 +75,7 @@ struct static_vm {extern struct list_head static_vmlist;extern struct static_vm *find_static_vm_vaddr (void *vaddr); -extern __init void add_static_vm_early (struct static_vm *svm); +extern __init void add_static_vm_early (struct static_vm *svm,bool add_to_vm);#endif
arch/arm/mm/mmu.c +14 -3
@@ -817,7 +817,8 @@ static void __init create_mapping(struct map_desc *md)/* * Create the architecture specific mappings */ -void __init iotable_init (struct map_desc *io_desc,int nr) +static void __init __iotable_init (struct map_desc *io_desc,int nr, +bool add_to_vm) {struct map_desc *md;struct vm_struct *vm;
@@ -838,10 +839,20 @@ void __init iotable_init(struct map_desc *io_desc, int nr) vm->flags =VM_IOREMAP |VM_ARM_STATIC_MAPPING ; vm->flags |=VM_ARM_MTYPE (md->type); vm->caller = iotable_init; -add_static_vm_early (svm++); +add_static_vm_early (svm++, add_to_vm); } } +void __init iotable_init (struct map_desc *io_desc,int nr) +{ +return __iotable_init (io_desc, nr, true); +} + +void __init iotable_init_novmreserve (struct map_desc *io_desc,int nr) +{ +return __iotable_init (io_desc, nr, false); +} +void __init vm_reserve_area_early (unsigned long addr,unsigned long size,void *caller) {
@@ -855,7 +866,7 @@ void __init vm_reserve_area_early(unsigned long addr, unsigned long size, vm->size = size; vm->flags =VM_IOREMAP |VM_ARM_EMPTY_MAPPING ; vm->caller = caller; -add_static_vm_early (svm); +add_static_vm_early (svm, true); }#ifndef CONFIG_ARM_LPAE
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, hosted by The Linux Foundation
RFC PATCH v2 2/4 arm: mm: Track lowmem in vmalloc
In reply to Laura AbbottRather than always keeping lowmem and vmalloc separate, we can now allow the two to be mixed. This means that all lowmem areas need to be explicitly tracked in vmalloc to avoid over allocating. Additionally, adjust the vmalloc reserve to account for the fact that there may be a hole in the middle consisting of vmalloc.
--- arch/arm/Kconfig | 3 + arch/arm/mm/init.c | 104 ++++++++++++++++++++++++++++++++++++---------------- arch/arm/mm/mm.h | 1 + arch/arm/mm/mmu.c | 23 +++++++++++ 4 files changed, 99 insertions(+), 32 deletions(-)
arch/arm/Kconfig +3 -0
@@ -270,6 +270,9 @@ config GENERIC_BUG def_bool y depends on BUG +config ARCH_TRACKS_VMALLOC + bool + source "init/Kconfig" source "kernel/Kconfig.freezer"
arch/arm/mm/init.c +72 -32
146 lines not on this page · raw message
arch/arm/mm/mm.h +1 -0
@@ -85,6 +85,7 @@ extern phys_addr_t arm_dma_limit;#define arm_dma_limit ((phys_addr_t)~0)#endif +#define MAX_HOLE_ADDRESS (PHYS_OFFSET + 0x10000000)extern phys_addr_t arm_lowmem_limit;void __init bootmem_init (void );
arch/arm/mm/mmu.c +23 -0
@@ -1004,6 +1004,19 @@ void __init sanity_check_meminfo(void)int i, j, highmem =0 ;phys_addr_t vmalloc_limit =__pa (vmalloc_min -1 ) +1 ; +#ifdef CONFIG_ARCH_TRACKS_VMALLOC +unsigned long hole_start; +for (i =0 ; i < (meminfo.nr_banks -1 ); i++) { + hole_start = meminfo.bank[i].start + meminfo.bank[i].size; +if (hole_start != meminfo.bank[i+1 ].start) { +if (hole_start <=MAX_HOLE_ADDRESS ) { + vmalloc_min = (void *) (vmalloc_min + + (meminfo.bank[i+1 ].start - hole_start)); + } + } + } +#endif +for (i =0 , j =0 ; i < meminfo.nr_banks; i++) {struct membank *bank = &meminfo.bank[j];phys_addr_t size_limit;
@@ -1311,6 +1324,7 @@ static void __init map_lowmem(void)phys_addr_t start = reg->base;phys_addr_t end = start + reg->size;struct map_desc map; +struct vm_struct *vm;if (end > arm_lowmem_limit) end = arm_lowmem_limit;
@@ -1323,6 +1337,15 @@ static void __init map_lowmem(void) map.type =MT_MEMORY ;create_mapping (&map); + +#ifdef CONFIG_ARCH_TRACKS_VMALLOC + vm = early_alloc_aligned(sizeof(*vm), __alignof__(*vm)); + vm->addr = (void *)map.virtual; + vm->size = end - start; + vm->flags =VM_LOWMEM ; + vm->caller = map_lowmem; +vm_area_add_early (vm); +#endif } }
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, hosted by The Linux Foundation
RFC PATCH v2 3/4 mm/vmalloc.c: Allow lowmem to be tracked in vmalloc
In reply to Laura Abbottvmalloc is currently assumed to be a completely separate address space from the lowmem region. While this may be true in the general case, there are some instances where lowmem and virtual space intermixing provides gains. One example is needing to steal a large chunk of physical lowmem for another purpose outside the systems usage. Rather than waste the precious lowmem space on a 32-bit system, we can allow the virtual holes created by the physical holes to be used by vmalloc for virtual addressing. Track lowmem allocations in vmalloc to allow mixing of lowmem and vmalloc.
--- include/linux/mm.h | 6 ++++++ include/linux/vmalloc.h | 1 + mm/Kconfig | 11 +++++++++++ mm/vmalloc.c | 35 +++++++++++++++++++++++++++++++++++ 4 files changed, 53 insertions(+), 0 deletions(-)
include/linux/mm.h +6 -0
@@ -308,6 +308,10 @@ unsigned long vmalloc_to_pfn(const void *addr);* On nommu, vmalloc/vfree wrap through kmalloc/kfree directly, so there * is no special casing required. */ + +#ifdef CONFIG_ENABLE_VMALLOC_SAVING +extern int is_vmalloc_addr (const void *x); +#else static inline int is_vmalloc_addr (const void *x) {#ifdef CONFIG_MMU
@@ -318,6 +322,8 @@ static inline int is_vmalloc_addr(const void *x)return 0 ; #endif } +#endif +#ifdef CONFIG_MMU extern int is_vmalloc_or_module_addr (const void *x);#else
include/linux/vmalloc.h +1 -0
@@ -16,6 +16,7 @@ struct vm_area_struct; /* vma defining user mapping in mm_types.h */#define VM_USERMAP 0x00000008/* suitable for remap_vmalloc_range */ #define VM_VPAGES 0x00000010/* buffer for pages was vmalloc'ed */ #define VM_UNINITIALIZED 0x00000020/* vm_struct is not fully initialized */ +#define VM_LOWMEM 0x00000040/* Tracking of direct mapped lowmem */ /* bits [20..32] reserved for arch specific ioremap internals */ /*
mm/Kconfig +11 -0
@@ -519,3 +519,14 @@ config MEM_SOFT_DIRTY it can be cleared by hands. See Documentation/vm/soft-dirty.txt for more details. + +config ENABLE_VMALLOC_SAVING + bool "Intermix lowmem and vmalloc virtual space" + depends on ARCH_TRACKS_VMALLOC + help + Some memory layouts on embedded systems steal large amounts + of lowmem physical memory for purposes outside of the kernel. + Rather than waste the physical and virtual space, allow the + kernel to use the virtual space as vmalloc space. + + If unsure, say N.
mm/vmalloc.c +35 -0
@@ -282,6 +282,38 @@ static unsigned long cached_align;static unsigned long vmap_area_pcpu_hole; +#ifdef CONFIG_ENABLE_VMALLOC_SAVING +int is_vmalloc_addr (const void *x) +{ +struct vmap_area *va; +int ret =0 ; + +spin_lock (&vmap_area_lock); +list_for_each_entry (va, &vmap_area_list, list) { +if (va->flags & (VM_LAZY_FREE |VM_LAZY_FREEING )) +continue ; + +if (!(va->flags &VM_VM_AREA )) +continue ; + +if (va->vm ==NULL ) +continue ; + +if (va->vm->flags &VM_LOWMEM ) +continue ; + +if ((unsigned long )x >= va->va_start && + (unsigned long )x < va->va_end) { + ret =1 ; +break ; + } + } +spin_unlock (&vmap_area_lock); +return ret; +} +EXPORT_SYMBOL (is_vmalloc_addr); +#endif +static struct vmap_area *__find_vmap_area (unsigned long addr) {struct rb_node *n = vmap_area_root.rb_node;
@@ -2628,6 +2660,9 @@ static int s_show(struct seq_file *m, void *p)if (v->flags &VM_VPAGES )seq_printf (m," vpages" ); +if (v->flags &VM_LOWMEM ) +seq_printf (m," lowmem" ); +show_numa_info (m, v);seq_putc (m,'\n' );return 0 ;
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, hosted by The Linux Foundation
RFC PATCH v2 4/4 mm/vmalloc.c: Treat the entire kernel virtual space as vmalloc
In reply to Laura AbbottWith CONFIG_ENABLE_VMALLOC_SAVINGS, all lowmem is tracked in vmalloc. This means that all the kernel virtual address space can be treated as part of the vmalloc region. Allow vm areas to be allocated from the full kernel address range.
--- mm/vmalloc.c | 11 +++++++++++ 1 files changed, 11 insertions(+), 0 deletions(-)
mm/vmalloc.c +11 -0
@@ -1394,16 +1394,27 @@ struct vm_struct *__get_vm_area_caller(unsigned long size, unsigned long flags,*/ struct vm_struct *get_vm_area (unsigned long size,unsigned long flags) { +#ifdef CONFIG_ENABLE_VMALLOC_SAVING +return __get_vm_area_node (size,1 , flags,PAGE_OFFSET ,VMALLOC_END , +NUMA_NO_NODE ,GFP_KERNEL , +__builtin_return_address (0 )); +#else return __get_vm_area_node (size,1 , flags,VMALLOC_START ,VMALLOC_END ,NUMA_NO_NODE ,GFP_KERNEL ,__builtin_return_address (0 )); +#endif }struct vm_struct *get_vm_area_caller (unsigned long size,unsigned long flags,const void *caller) { +#ifdef CONFIG_ENABLE_VMALLOC_SAVING +return __get_vm_area_node (size,1 , flags,PAGE_OFFSET ,VMALLOC_END , +NUMA_NO_NODE ,GFP_KERNEL , caller); +#else return __get_vm_area_node (size,1 , flags,VMALLOC_START ,VMALLOC_END ,NUMA_NO_NODE ,GFP_KERNEL , caller); +#endif }/**
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, hosted by The Linux Foundation