aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390
diff options
context:
space:
mode:
authorMartin Schwidefsky <schwidefsky@de.ibm.com>2011-12-27 05:27:07 -0500
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2011-12-27 05:27:10 -0500
commit14045ebf1e1156d966a796cacad91028e01797e5 (patch)
tree75fd97e935a9c337e95dc5202bafbf2634cf170b /arch/s390
parent4999023aa95a00507d3f100ea75510c5c7270f74 (diff)
[S390] add support for physical memory > 4TB
The kernel address space of a 64 bit kernel currently uses a three level page table and the vmemmap array has a fixed address and a fixed maximum size. A three level page table is good enough for systems with less than 3.8TB of memory, for bigger systems four page table levels need to be used. Each page table level costs a bit of performance, use 3 levels for normal systems and 4 levels only for the really big systems. To avoid bloating sparse.o too much set MAX_PHYSMEM_BITS to 46 for a maximum of 64TB of memory. Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'arch/s390')
-rw-r--r--arch/s390/include/asm/pgtable.h23
-rw-r--r--arch/s390/include/asm/sparsemem.h4
-rw-r--r--arch/s390/kernel/setup.c67
-rw-r--r--arch/s390/mm/init.c16
-rw-r--r--arch/s390/mm/pgtable.c11
5 files changed, 68 insertions, 53 deletions
diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h
index 4f289ff0b7fe..011358c1b18e 100644
--- a/arch/s390/include/asm/pgtable.h
+++ b/arch/s390/include/asm/pgtable.h
@@ -128,28 +128,11 @@ static inline int is_zero_pfn(unsigned long pfn)
128 * effect, this also makes sure that 64 bit module code cannot be used 128 * effect, this also makes sure that 64 bit module code cannot be used
129 * as system call address. 129 * as system call address.
130 */ 130 */
131
132extern unsigned long VMALLOC_START; 131extern unsigned long VMALLOC_START;
132extern unsigned long VMALLOC_END;
133extern struct page *vmemmap;
133 134
134#ifndef __s390x__ 135#define VMEM_MAX_PHYS ((unsigned long) vmemmap)
135#define VMALLOC_SIZE (96UL << 20)
136#define VMALLOC_END 0x7e000000UL
137#define VMEM_MAP_END 0x80000000UL
138#else /* __s390x__ */
139#define VMALLOC_SIZE (128UL << 30)
140#define VMALLOC_END 0x3e000000000UL
141#define VMEM_MAP_END 0x40000000000UL
142#endif /* __s390x__ */
143
144/*
145 * VMEM_MAX_PHYS is the highest physical address that can be added to the 1:1
146 * mapping. This needs to be calculated at compile time since the size of the
147 * VMEM_MAP is static but the size of struct page can change.
148 */
149#define VMEM_MAX_PAGES ((VMEM_MAP_END - VMALLOC_END) / sizeof(struct page))
150#define VMEM_MAX_PFN min(VMALLOC_START >> PAGE_SHIFT, VMEM_MAX_PAGES)
151#define VMEM_MAX_PHYS ((VMEM_MAX_PFN << PAGE_SHIFT) & ~((16 << 20) - 1))
152#define vmemmap ((struct page *) VMALLOC_END)
153 136
154/* 137/*
155 * A 31 bit pagetable entry of S390 has following format: 138 * A 31 bit pagetable entry of S390 has following format:
diff --git a/arch/s390/include/asm/sparsemem.h b/arch/s390/include/asm/sparsemem.h
index 545d219e6a2d..0fb34027d3f6 100644
--- a/arch/s390/include/asm/sparsemem.h
+++ b/arch/s390/include/asm/sparsemem.h
@@ -4,8 +4,8 @@
4#ifdef CONFIG_64BIT 4#ifdef CONFIG_64BIT
5 5
6#define SECTION_SIZE_BITS 28 6#define SECTION_SIZE_BITS 28
7#define MAX_PHYSADDR_BITS 42 7#define MAX_PHYSADDR_BITS 46
8#define MAX_PHYSMEM_BITS 42 8#define MAX_PHYSMEM_BITS 46
9 9
10#else 10#else
11 11
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c
index 26b601c2b137..66903eed36e6 100644
--- a/arch/s390/kernel/setup.c
+++ b/arch/s390/kernel/setup.c
@@ -94,6 +94,15 @@ struct mem_chunk __initdata memory_chunk[MEMORY_CHUNKS];
94int __initdata memory_end_set; 94int __initdata memory_end_set;
95unsigned long __initdata memory_end; 95unsigned long __initdata memory_end;
96 96
97unsigned long VMALLOC_START;
98EXPORT_SYMBOL(VMALLOC_START);
99
100unsigned long VMALLOC_END;
101EXPORT_SYMBOL(VMALLOC_END);
102
103struct page *vmemmap;
104EXPORT_SYMBOL(vmemmap);
105
97/* An array with a pointer to the lowcore of every CPU. */ 106/* An array with a pointer to the lowcore of every CPU. */
98struct _lowcore *lowcore_ptr[NR_CPUS]; 107struct _lowcore *lowcore_ptr[NR_CPUS];
99EXPORT_SYMBOL(lowcore_ptr); 108EXPORT_SYMBOL(lowcore_ptr);
@@ -277,6 +286,15 @@ static int __init early_parse_mem(char *p)
277} 286}
278early_param("mem", early_parse_mem); 287early_param("mem", early_parse_mem);
279 288
289static int __init parse_vmalloc(char *arg)
290{
291 if (!arg)
292 return -EINVAL;
293 VMALLOC_END = (memparse(arg, &arg) + PAGE_SIZE - 1) & PAGE_MASK;
294 return 0;
295}
296early_param("vmalloc", parse_vmalloc);
297
280unsigned int user_mode = HOME_SPACE_MODE; 298unsigned int user_mode = HOME_SPACE_MODE;
281EXPORT_SYMBOL_GPL(user_mode); 299EXPORT_SYMBOL_GPL(user_mode);
282 300
@@ -478,8 +496,7 @@ EXPORT_SYMBOL_GPL(real_memory_size);
478 496
479static void __init setup_memory_end(void) 497static void __init setup_memory_end(void)
480{ 498{
481 unsigned long memory_size; 499 unsigned long vmax, vmalloc_size, tmp;
482 unsigned long max_mem;
483 int i; 500 int i;
484 501
485 502
@@ -489,12 +506,9 @@ static void __init setup_memory_end(void)
489 memory_end_set = 1; 506 memory_end_set = 1;
490 } 507 }
491#endif 508#endif
492 memory_size = 0; 509 real_memory_size = 0;
493 memory_end &= PAGE_MASK; 510 memory_end &= PAGE_MASK;
494 511
495 max_mem = memory_end ? min(VMEM_MAX_PHYS, memory_end) : VMEM_MAX_PHYS;
496 memory_end = min(max_mem, memory_end);
497
498 /* 512 /*
499 * Make sure all chunks are MAX_ORDER aligned so we don't need the 513 * Make sure all chunks are MAX_ORDER aligned so we don't need the
500 * extra checks that HOLES_IN_ZONE would require. 514 * extra checks that HOLES_IN_ZONE would require.
@@ -514,23 +528,48 @@ static void __init setup_memory_end(void)
514 chunk->addr = start; 528 chunk->addr = start;
515 chunk->size = end - start; 529 chunk->size = end - start;
516 } 530 }
531 real_memory_size = max(real_memory_size,
532 chunk->addr + chunk->size);
517 } 533 }
518 534
535 /* Choose kernel address space layout: 2, 3, or 4 levels. */
536#ifdef CONFIG_64BIT
537 vmalloc_size = VMALLOC_END ?: 128UL << 30;
538 tmp = (memory_end ?: real_memory_size) / PAGE_SIZE;
539 tmp = tmp * (sizeof(struct page) + PAGE_SIZE) + vmalloc_size;
540 if (tmp <= (1UL << 42))
541 vmax = 1UL << 42; /* 3-level kernel page table */
542 else
543 vmax = 1UL << 53; /* 4-level kernel page table */
544#else
545 vmalloc_size = VMALLOC_END ?: 96UL << 20;
546 vmax = 1UL << 31; /* 2-level kernel page table */
547#endif
548 /* vmalloc area is at the end of the kernel address space. */
549 VMALLOC_END = vmax;
550 VMALLOC_START = vmax - vmalloc_size;
551
552 /* Split remaining virtual space between 1:1 mapping & vmemmap array */
553 tmp = VMALLOC_START / (PAGE_SIZE + sizeof(struct page));
554 tmp = VMALLOC_START - tmp * sizeof(struct page);
555 tmp &= ~((vmax >> 11) - 1); /* align to page table level */
556 tmp = min(tmp, 1UL << MAX_PHYSMEM_BITS);
557 vmemmap = (struct page *) tmp;
558
559 /* Take care that memory_end is set and <= vmemmap */
560 memory_end = min(memory_end ?: real_memory_size, tmp);
561
562 /* Fixup memory chunk array to fit into 0..memory_end */
519 for (i = 0; i < MEMORY_CHUNKS; i++) { 563 for (i = 0; i < MEMORY_CHUNKS; i++) {
520 struct mem_chunk *chunk = &memory_chunk[i]; 564 struct mem_chunk *chunk = &memory_chunk[i];
521 565
522 real_memory_size = max(real_memory_size, 566 if (chunk->addr >= memory_end) {
523 chunk->addr + chunk->size);
524 if (chunk->addr >= max_mem) {
525 memset(chunk, 0, sizeof(*chunk)); 567 memset(chunk, 0, sizeof(*chunk));
526 continue; 568 continue;
527 } 569 }
528 if (chunk->addr + chunk->size > max_mem) 570 if (chunk->addr + chunk->size > memory_end)
529 chunk->size = max_mem - chunk->addr; 571 chunk->size = memory_end - chunk->addr;
530 memory_size = max(memory_size, chunk->addr + chunk->size);
531 } 572 }
532 if (!memory_end)
533 memory_end = memory_size;
534} 573}
535 574
536void *restart_stack __attribute__((__section__(".data"))); 575void *restart_stack __attribute__((__section__(".data")));
diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c
index d4b9fb4d0042..5d633019d8f3 100644
--- a/arch/s390/mm/init.c
+++ b/arch/s390/mm/init.c
@@ -93,18 +93,22 @@ static unsigned long setup_zero_pages(void)
93void __init paging_init(void) 93void __init paging_init(void)
94{ 94{
95 unsigned long max_zone_pfns[MAX_NR_ZONES]; 95 unsigned long max_zone_pfns[MAX_NR_ZONES];
96 unsigned long pgd_type; 96 unsigned long pgd_type, asce_bits;
97 97
98 init_mm.pgd = swapper_pg_dir; 98 init_mm.pgd = swapper_pg_dir;
99 S390_lowcore.kernel_asce = __pa(init_mm.pgd) & PAGE_MASK;
100#ifdef CONFIG_64BIT 99#ifdef CONFIG_64BIT
101 /* A three level page table (4TB) is enough for the kernel space. */ 100 if (VMALLOC_END > (1UL << 42)) {
102 S390_lowcore.kernel_asce |= _ASCE_TYPE_REGION3 | _ASCE_TABLE_LENGTH; 101 asce_bits = _ASCE_TYPE_REGION2 | _ASCE_TABLE_LENGTH;
103 pgd_type = _REGION3_ENTRY_EMPTY; 102 pgd_type = _REGION2_ENTRY_EMPTY;
103 } else {
104 asce_bits = _ASCE_TYPE_REGION3 | _ASCE_TABLE_LENGTH;
105 pgd_type = _REGION3_ENTRY_EMPTY;
106 }
104#else 107#else
105 S390_lowcore.kernel_asce |= _ASCE_TABLE_LENGTH; 108 asce_bits = _ASCE_TABLE_LENGTH;
106 pgd_type = _SEGMENT_ENTRY_EMPTY; 109 pgd_type = _SEGMENT_ENTRY_EMPTY;
107#endif 110#endif
111 S390_lowcore.kernel_asce = (__pa(init_mm.pgd) & PAGE_MASK) | asce_bits;
108 clear_table((unsigned long *) init_mm.pgd, pgd_type, 112 clear_table((unsigned long *) init_mm.pgd, pgd_type,
109 sizeof(unsigned long)*2048); 113 sizeof(unsigned long)*2048);
110 vmem_map_init(); 114 vmem_map_init();
diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c
index dc2269f1821c..9a4d02f64f16 100644
--- a/arch/s390/mm/pgtable.c
+++ b/arch/s390/mm/pgtable.c
@@ -33,17 +33,6 @@
33#define FRAG_MASK 0x03 33#define FRAG_MASK 0x03
34#endif 34#endif
35 35
36unsigned long VMALLOC_START = VMALLOC_END - VMALLOC_SIZE;
37EXPORT_SYMBOL(VMALLOC_START);
38
39static int __init parse_vmalloc(char *arg)
40{
41 if (!arg)
42 return -EINVAL;
43 VMALLOC_START = (VMALLOC_END - memparse(arg, &arg)) & PAGE_MASK;
44 return 0;
45}
46early_param("vmalloc", parse_vmalloc);
47 36
48unsigned long *crst_table_alloc(struct mm_struct *mm) 37unsigned long *crst_table_alloc(struct mm_struct *mm)
49{ 38{