Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ARC: support HIGHMEM even without PAE40

Initial HIGHMEM support on ARC was introduced for PAE40 where the low
memory (0x8000_0000 based) and high memory (0x1_0000_0000) were
physically contiguous. So CONFIG_FLATMEM sufficed (despite a peipheral
hole in the middle, which wasted a bit of struct page memory, but things
worked).

However w/o PAE, highmem was not possible and we could only reach
~1.75GB of DDR. Now there is a use case to access ~4GB of DDR w/o PAE40
The idea is to have low memory at canonical 0x8000_0000 and highmem
at 0 so enire 4GB address space is available for physical addressing
This needs additional platform/interconnect mapping to convert
the non contiguous physical addresses into linear bus adresses.

From Linux point of view, non contiguous divide means FLATMEM no
longer works and DISCONTIGMEM is needed to track the pfns in the 2
regions.

This scheme would also work for PAE40, only better in that we don't
waste struct page memory for the peripheral hole.

The DT description will be something like

memory {
...
reg = <0x80000000 0x200000000 /* 512MB: lowmem */
0x00000000 0x10000000>; /* 256MB: highmem */
}

Signed-off-by: Noam Camus <noamc@ezchip.com>
Signed-off-by: Vineet Gupta <vgupta@synopsys.com>

+98 -18
+13
arch/arc/Kconfig
··· 58 58 config RWSEM_GENERIC_SPINLOCK 59 59 def_bool y 60 60 61 + config ARCH_DISCONTIGMEM_ENABLE 62 + def_bool y 63 + 61 64 config ARCH_FLATMEM_ENABLE 62 65 def_bool y 63 66 ··· 350 347 351 348 endchoice 352 349 350 + config NODES_SHIFT 351 + int "Maximum NUMA Nodes (as a power of 2)" 352 + default "1" if !DISCONTIGMEM 353 + default "2" if DISCONTIGMEM 354 + depends on NEED_MULTIPLE_NODES 355 + ---help--- 356 + Accessing memory beyond 1GB (with or w/o PAE) requires 2 memory 357 + zones. 358 + 353 359 if ISA_ARCOMPACT 354 360 355 361 config ARC_COMPACT_IRQ_LEVELS ··· 467 455 468 456 config HIGHMEM 469 457 bool "High Memory Support" 458 + select DISCONTIGMEM 470 459 help 471 460 With ARC 2G:2G address split, only upper 2G is directly addressable by 472 461 kernel. Enable this to potentially allow access to rest of 2G and PAE
+43
arch/arc/include/asm/mmzone.h
··· 1 + /* 2 + * Copyright (C) 2016 Synopsys, Inc. (www.synopsys.com) 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License version 2 as 6 + * published by the Free Software Foundation. 7 + */ 8 + 9 + #ifndef _ASM_ARC_MMZONE_H 10 + #define _ASM_ARC_MMZONE_H 11 + 12 + #ifdef CONFIG_DISCONTIGMEM 13 + 14 + extern struct pglist_data node_data[]; 15 + #define NODE_DATA(nid) (&node_data[nid]) 16 + 17 + static inline int pfn_to_nid(unsigned long pfn) 18 + { 19 + int is_end_low = 1; 20 + 21 + if (IS_ENABLED(CONFIG_ARC_HAS_PAE40)) 22 + is_end_low = pfn <= virt_to_pfn(0xFFFFFFFFUL); 23 + 24 + /* 25 + * node 0: lowmem: 0x8000_0000 to 0xFFFF_FFFF 26 + * node 1: HIGHMEM w/o PAE40: 0x0 to 0x7FFF_FFFF 27 + * HIGHMEM with PAE40: 0x1_0000_0000 to ... 28 + */ 29 + if (pfn >= ARCH_PFN_OFFSET && is_end_low) 30 + return 0; 31 + 32 + return 1; 33 + } 34 + 35 + static inline int pfn_valid(unsigned long pfn) 36 + { 37 + int nid = pfn_to_nid(pfn); 38 + 39 + return (pfn <= node_end_pfn(nid)); 40 + } 41 + #endif /* CONFIG_DISCONTIGMEM */ 42 + 43 + #endif
+3 -3
arch/arc/include/asm/page.h
··· 83 83 84 84 #define ARCH_PFN_OFFSET virt_to_pfn(CONFIG_LINUX_LINK_BASE) 85 85 86 + #ifdef CONFIG_FLATMEM 86 87 #define pfn_valid(pfn) (((pfn) - ARCH_PFN_OFFSET) < max_mapnr) 88 + #endif 87 89 88 90 /* 89 91 * __pa, __va, virt_to_page (ALERT: deprecated, don't use them) ··· 97 95 #define __pa(vaddr) ((unsigned long)(vaddr)) 98 96 #define __va(paddr) ((void *)((unsigned long)(paddr))) 99 97 100 - #define virt_to_page(kaddr) \ 101 - (mem_map + virt_to_pfn((kaddr) - CONFIG_LINUX_LINK_BASE)) 102 - 98 + #define virt_to_page(kaddr) pfn_to_page(virt_to_pfn(kaddr)) 103 99 #define virt_addr_valid(kaddr) pfn_valid(virt_to_pfn(kaddr)) 104 100 105 101 /* Default Permissions for stack/heaps pages (Non Executable) */
+39 -15
arch/arc/mm/init.c
··· 30 30 static unsigned long low_mem_sz; 31 31 32 32 #ifdef CONFIG_HIGHMEM 33 - static unsigned long min_high_pfn; 33 + static unsigned long min_high_pfn, max_high_pfn; 34 34 static u64 high_mem_start; 35 35 static u64 high_mem_sz; 36 + #endif 37 + 38 + #ifdef CONFIG_DISCONTIGMEM 39 + struct pglist_data node_data[MAX_NUMNODES] __read_mostly; 40 + EXPORT_SYMBOL(node_data); 36 41 #endif 37 42 38 43 /* User can over-ride above with "mem=nnn[KkMm]" in cmdline */ ··· 114 109 /* Last usable page of low mem */ 115 110 max_low_pfn = max_pfn = PFN_DOWN(low_mem_start + low_mem_sz); 116 111 117 - #ifdef CONFIG_HIGHMEM 118 - min_high_pfn = PFN_DOWN(high_mem_start); 119 - max_pfn = PFN_DOWN(high_mem_start + high_mem_sz); 112 + #ifdef CONFIG_FLATMEM 113 + /* pfn_valid() uses this */ 114 + max_mapnr = max_low_pfn - min_low_pfn; 120 115 #endif 121 - 122 - max_mapnr = max_pfn - min_low_pfn; 123 116 124 117 /*------------- bootmem allocator setup -----------------------*/ 125 118 ··· 132 129 * the crash 133 130 */ 134 131 135 - memblock_add(low_mem_start, low_mem_sz); 132 + memblock_add_node(low_mem_start, low_mem_sz, 0); 136 133 memblock_reserve(low_mem_start, __pa(_end) - low_mem_start); 137 134 138 135 #ifdef CONFIG_BLK_DEV_INITRD ··· 152 149 zones_size[ZONE_NORMAL] = max_low_pfn - min_low_pfn; 153 150 zones_holes[ZONE_NORMAL] = 0; 154 151 155 - #ifdef CONFIG_HIGHMEM 156 - zones_size[ZONE_HIGHMEM] = max_pfn - max_low_pfn; 157 - 158 - /* This handles the peripheral address space hole */ 159 - zones_holes[ZONE_HIGHMEM] = min_high_pfn - max_low_pfn; 160 - #endif 161 - 162 152 /* 163 153 * We can't use the helper free_area_init(zones[]) because it uses 164 154 * PAGE_OFFSET to compute the @min_low_pfn which would be wrong ··· 164 168 zones_holes); /* holes */ 165 169 166 170 #ifdef CONFIG_HIGHMEM 171 + /* 172 + * Populate a new node with highmem 173 + * 174 + * On ARC (w/o PAE) HIGHMEM addresses are actually smaller (0 based) 175 + * than addresses in normal ala low memory (0x8000_0000 based). 176 + * Even with PAE, the huge peripheral space hole would waste a lot of 177 + * mem with single mem_map[]. This warrants a mem_map per region design. 178 + * Thus HIGHMEM on ARC is imlemented with DISCONTIGMEM. 179 + * 180 + * DISCONTIGMEM in turns requires multiple nodes. node 0 above is 181 + * populated with normal memory zone while node 1 only has highmem 182 + */ 183 + node_set_online(1); 184 + 185 + min_high_pfn = PFN_DOWN(high_mem_start); 186 + max_high_pfn = PFN_DOWN(high_mem_start + high_mem_sz); 187 + 188 + zones_size[ZONE_NORMAL] = 0; 189 + zones_holes[ZONE_NORMAL] = 0; 190 + 191 + zones_size[ZONE_HIGHMEM] = max_high_pfn - min_high_pfn; 192 + zones_holes[ZONE_HIGHMEM] = 0; 193 + 194 + free_area_init_node(1, /* node-id */ 195 + zones_size, /* num pages per zone */ 196 + min_high_pfn, /* first pfn of node */ 197 + zones_holes); /* holes */ 198 + 167 199 high_memory = (void *)(min_high_pfn << PAGE_SHIFT); 168 200 kmap_init(); 169 201 #endif ··· 209 185 unsigned long tmp; 210 186 211 187 reset_all_zones_managed_pages(); 212 - for (tmp = min_high_pfn; tmp < max_pfn; tmp++) 188 + for (tmp = min_high_pfn; tmp < max_high_pfn; tmp++) 213 189 free_highmem_page(pfn_to_page(tmp)); 214 190 #endif 215 191