Skip to contentSkip to conversation

Intermix Lowmem and Vmalloc

RFC PATCH Superseded Laura Abbott 1 participant 5 messages Nov 12, 2013

Subsystems (4)

v2 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

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

#1 lore raw

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
index 2fe141f..02e3509 100644
@@ -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
index 7f9b179..bf80e43 100644
@@ -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
index f123d6e..ad92d4f 100644
@@ -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
index d5a4e9a..27a3680 100644
@@ -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
index 53cdbd3..b83ed88 100644
@@ -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
#2 lore raw

RFC PATCH v2 2/4 arm: mm: Track lowmem in vmalloc

In reply to Laura Abbott

Rather 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
index 051fce4..1f36664 100644
@@ -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
index 27a3680..f484e52 100644
@@ -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
index b83ed88..ed2a4fa 100644
@@ -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
#3 lore raw

RFC PATCH v2 3/4 mm/vmalloc.c: Allow lowmem to be tracked in vmalloc

In reply to Laura Abbott

vmalloc 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
index f022460..f2da420 100644
@@ -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
index 4b8a891..e0c8c49 100644
@@ -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
index 8028dcc..b3c459d 100644
@@ -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
index 13a5495..2ec9ac7 100644
@@ -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
#4 lore raw

RFC PATCH v2 4/4 mm/vmalloc.c: Treat the entire kernel virtual space as vmalloc

In reply to Laura Abbott

With 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
index 2ec9ac7..31644b6 100644
@@ -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