diff options
| -rw-r--r-- | arch/s390/kernel/early.c | 16 | ||||
| -rw-r--r-- | arch/s390/kernel/head64.S | 2 | ||||
| -rw-r--r-- | arch/s390/kernel/setup.c | 10 | ||||
| -rw-r--r-- | arch/s390/mm/Makefile | 2 | ||||
| -rw-r--r-- | arch/s390/mm/fault.c | 3 | ||||
| -rw-r--r-- | arch/s390/mm/hugetlbpage.c | 134 | ||||
| -rw-r--r-- | arch/s390/mm/init.c | 23 | ||||
| -rw-r--r-- | arch/s390/mm/vmem.c | 55 | ||||
| -rw-r--r-- | fs/Kconfig | 3 | ||||
| -rw-r--r-- | include/asm-s390/hugetlb.h | 183 | ||||
| -rw-r--r-- | include/asm-s390/page.h | 29 | ||||
| -rw-r--r-- | include/asm-s390/pgtable.h | 12 | ||||
| -rw-r--r-- | include/asm-s390/setup.h | 6 | ||||
| -rw-r--r-- | include/asm-s390/tlbflush.h | 1 |
14 files changed, 437 insertions, 42 deletions
diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c index bd188f6bd0e2..d0e09684b9ce 100644 --- a/arch/s390/kernel/early.c +++ b/arch/s390/kernel/early.c | |||
| @@ -268,6 +268,19 @@ static noinline __init void setup_lowcore_early(void) | |||
| 268 | s390_base_pgm_handler_fn = early_pgm_check_handler; | 268 | s390_base_pgm_handler_fn = early_pgm_check_handler; |
| 269 | } | 269 | } |
| 270 | 270 | ||
| 271 | static noinline __init void setup_hpage(void) | ||
| 272 | { | ||
| 273 | #ifndef CONFIG_DEBUG_PAGEALLOC | ||
| 274 | unsigned int facilities; | ||
| 275 | |||
| 276 | facilities = stfl(); | ||
| 277 | if (!(facilities & (1UL << 23)) || !(facilities & (1UL << 29))) | ||
| 278 | return; | ||
| 279 | machine_flags |= MACHINE_FLAG_HPAGE; | ||
| 280 | __ctl_set_bit(0, 23); | ||
| 281 | #endif | ||
| 282 | } | ||
| 283 | |||
| 271 | static __init void detect_mvpg(void) | 284 | static __init void detect_mvpg(void) |
| 272 | { | 285 | { |
| 273 | #ifndef CONFIG_64BIT | 286 | #ifndef CONFIG_64BIT |
| @@ -360,6 +373,8 @@ static __init void detect_machine_facilities(void) | |||
| 360 | facilities = stfl(); | 373 | facilities = stfl(); |
| 361 | if (facilities & (1 << 28)) | 374 | if (facilities & (1 << 28)) |
| 362 | machine_flags |= MACHINE_FLAG_IDTE; | 375 | machine_flags |= MACHINE_FLAG_IDTE; |
| 376 | if (facilities & (1 << 23)) | ||
| 377 | machine_flags |= MACHINE_FLAG_PFMF; | ||
| 363 | if (facilities & (1 << 4)) | 378 | if (facilities & (1 << 4)) |
| 364 | machine_flags |= MACHINE_FLAG_MVCOS; | 379 | machine_flags |= MACHINE_FLAG_MVCOS; |
| 365 | #endif | 380 | #endif |
| @@ -388,6 +403,7 @@ void __init startup_init(void) | |||
| 388 | detect_diag9c(); | 403 | detect_diag9c(); |
| 389 | detect_diag44(); | 404 | detect_diag44(); |
| 390 | detect_machine_facilities(); | 405 | detect_machine_facilities(); |
| 406 | setup_hpage(); | ||
| 391 | sclp_read_info_early(); | 407 | sclp_read_info_early(); |
| 392 | sclp_facilities_detect(); | 408 | sclp_facilities_detect(); |
| 393 | memsize = sclp_memory_detect(); | 409 | memsize = sclp_memory_detect(); |
diff --git a/arch/s390/kernel/head64.S b/arch/s390/kernel/head64.S index 9c2c6f7d37e7..1d06961e87b3 100644 --- a/arch/s390/kernel/head64.S +++ b/arch/s390/kernel/head64.S | |||
| @@ -129,7 +129,7 @@ startup_continue: | |||
| 129 | # virtual and never return ... | 129 | # virtual and never return ... |
| 130 | .align 16 | 130 | .align 16 |
| 131 | .Lentry:.quad 0x0000000180000000,_stext | 131 | .Lentry:.quad 0x0000000180000000,_stext |
| 132 | .Lctl: .quad 0x04b50002 # cr0: various things | 132 | .Lctl: .quad 0x04350002 # cr0: various things |
| 133 | .quad 0 # cr1: primary space segment table | 133 | .quad 0 # cr1: primary space segment table |
| 134 | .quad .Lduct # cr2: dispatchable unit control table | 134 | .quad .Lduct # cr2: dispatchable unit control table |
| 135 | .quad 0 # cr3: instruction authorization | 135 | .quad 0 # cr3: instruction authorization |
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 694c6546ce64..2bc70b6e876a 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c | |||
| @@ -749,6 +749,9 @@ static void __init setup_hwcaps(void) | |||
| 749 | elf_hwcap |= 1UL << 6; | 749 | elf_hwcap |= 1UL << 6; |
| 750 | } | 750 | } |
| 751 | 751 | ||
| 752 | if (MACHINE_HAS_HPAGE) | ||
| 753 | elf_hwcap |= 1UL << 7; | ||
| 754 | |||
| 752 | switch (cpuinfo->cpu_id.machine) { | 755 | switch (cpuinfo->cpu_id.machine) { |
| 753 | case 0x9672: | 756 | case 0x9672: |
| 754 | #if !defined(CONFIG_64BIT) | 757 | #if !defined(CONFIG_64BIT) |
| @@ -872,8 +875,9 @@ void __cpuinit print_cpu_info(struct cpuinfo_S390 *cpuinfo) | |||
| 872 | 875 | ||
| 873 | static int show_cpuinfo(struct seq_file *m, void *v) | 876 | static int show_cpuinfo(struct seq_file *m, void *v) |
| 874 | { | 877 | { |
| 875 | static const char *hwcap_str[7] = { | 878 | static const char *hwcap_str[8] = { |
| 876 | "esan3", "zarch", "stfle", "msa", "ldisp", "eimm", "dfp" | 879 | "esan3", "zarch", "stfle", "msa", "ldisp", "eimm", "dfp", |
| 880 | "edat" | ||
| 877 | }; | 881 | }; |
| 878 | struct cpuinfo_S390 *cpuinfo; | 882 | struct cpuinfo_S390 *cpuinfo; |
| 879 | unsigned long n = (unsigned long) v - 1; | 883 | unsigned long n = (unsigned long) v - 1; |
| @@ -888,7 +892,7 @@ static int show_cpuinfo(struct seq_file *m, void *v) | |||
| 888 | num_online_cpus(), loops_per_jiffy/(500000/HZ), | 892 | num_online_cpus(), loops_per_jiffy/(500000/HZ), |
| 889 | (loops_per_jiffy/(5000/HZ))%100); | 893 | (loops_per_jiffy/(5000/HZ))%100); |
| 890 | seq_puts(m, "features\t: "); | 894 | seq_puts(m, "features\t: "); |
| 891 | for (i = 0; i < 7; i++) | 895 | for (i = 0; i < 8; i++) |
| 892 | if (hwcap_str[i] && (elf_hwcap & (1UL << i))) | 896 | if (hwcap_str[i] && (elf_hwcap & (1UL << i))) |
| 893 | seq_printf(m, "%s ", hwcap_str[i]); | 897 | seq_printf(m, "%s ", hwcap_str[i]); |
| 894 | seq_puts(m, "\n"); | 898 | seq_puts(m, "\n"); |
diff --git a/arch/s390/mm/Makefile b/arch/s390/mm/Makefile index 66401930f83e..fb988a48a754 100644 --- a/arch/s390/mm/Makefile +++ b/arch/s390/mm/Makefile | |||
| @@ -4,4 +4,4 @@ | |||
| 4 | 4 | ||
| 5 | obj-y := init.o fault.o extmem.o mmap.o vmem.o pgtable.o | 5 | obj-y := init.o fault.o extmem.o mmap.o vmem.o pgtable.o |
| 6 | obj-$(CONFIG_CMM) += cmm.o | 6 | obj-$(CONFIG_CMM) += cmm.o |
| 7 | 7 | obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o | |
diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index 2650f46001d0..4d537205e83c 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c | |||
| @@ -28,6 +28,7 @@ | |||
| 28 | #include <linux/hardirq.h> | 28 | #include <linux/hardirq.h> |
| 29 | #include <linux/kprobes.h> | 29 | #include <linux/kprobes.h> |
| 30 | #include <linux/uaccess.h> | 30 | #include <linux/uaccess.h> |
| 31 | #include <linux/hugetlb.h> | ||
| 31 | #include <asm/system.h> | 32 | #include <asm/system.h> |
| 32 | #include <asm/pgtable.h> | 33 | #include <asm/pgtable.h> |
| 33 | #include <asm/s390_ext.h> | 34 | #include <asm/s390_ext.h> |
| @@ -367,6 +368,8 @@ good_area: | |||
| 367 | } | 368 | } |
| 368 | 369 | ||
| 369 | survive: | 370 | survive: |
| 371 | if (is_vm_hugetlb_page(vma)) | ||
| 372 | address &= HPAGE_MASK; | ||
| 370 | /* | 373 | /* |
| 371 | * If for any reason at all we couldn't handle the fault, | 374 | * If for any reason at all we couldn't handle the fault, |
| 372 | * make sure we exit gracefully rather than endlessly redo | 375 | * make sure we exit gracefully rather than endlessly redo |
diff --git a/arch/s390/mm/hugetlbpage.c b/arch/s390/mm/hugetlbpage.c new file mode 100644 index 000000000000..f4b6124fdb75 --- /dev/null +++ b/arch/s390/mm/hugetlbpage.c | |||
| @@ -0,0 +1,134 @@ | |||
| 1 | /* | ||
| 2 | * IBM System z Huge TLB Page Support for Kernel. | ||
| 3 | * | ||
| 4 | * Copyright 2007 IBM Corp. | ||
| 5 | * Author(s): Gerald Schaefer <gerald.schaefer@de.ibm.com> | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include <linux/mm.h> | ||
| 9 | #include <linux/hugetlb.h> | ||
| 10 | |||
| 11 | |||
| 12 | void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, | ||
| 13 | pte_t *pteptr, pte_t pteval) | ||
| 14 | { | ||
| 15 | pmd_t *pmdp = (pmd_t *) pteptr; | ||
| 16 | pte_t shadow_pteval = pteval; | ||
| 17 | unsigned long mask; | ||
| 18 | |||
| 19 | if (!MACHINE_HAS_HPAGE) { | ||
| 20 | pteptr = (pte_t *) pte_page(pteval)[1].index; | ||
| 21 | mask = pte_val(pteval) & | ||
| 22 | (_SEGMENT_ENTRY_INV | _SEGMENT_ENTRY_RO); | ||
| 23 | pte_val(pteval) = (_SEGMENT_ENTRY + __pa(pteptr)) | mask; | ||
| 24 | if (mm->context.noexec) { | ||
| 25 | pteptr += PTRS_PER_PTE; | ||
| 26 | pte_val(shadow_pteval) = | ||
| 27 | (_SEGMENT_ENTRY + __pa(pteptr)) | mask; | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | pmd_val(*pmdp) = pte_val(pteval); | ||
| 32 | if (mm->context.noexec) { | ||
| 33 | pmdp = get_shadow_table(pmdp); | ||
| 34 | pmd_val(*pmdp) = pte_val(shadow_pteval); | ||
| 35 | } | ||
| 36 | } | ||
| 37 | |||
| 38 | int arch_prepare_hugepage(struct page *page) | ||
| 39 | { | ||
| 40 | unsigned long addr = page_to_phys(page); | ||
| 41 | pte_t pte; | ||
| 42 | pte_t *ptep; | ||
| 43 | int i; | ||
| 44 | |||
| 45 | if (MACHINE_HAS_HPAGE) | ||
| 46 | return 0; | ||
| 47 | |||
| 48 | ptep = (pte_t *) pte_alloc_one(&init_mm, address); | ||
| 49 | if (!ptep) | ||
| 50 | return -ENOMEM; | ||
| 51 | |||
| 52 | pte = mk_pte(page, PAGE_RW); | ||
| 53 | for (i = 0; i < PTRS_PER_PTE; i++) { | ||
| 54 | set_pte_at(&init_mm, addr + i * PAGE_SIZE, ptep + i, pte); | ||
| 55 | pte_val(pte) += PAGE_SIZE; | ||
| 56 | } | ||
| 57 | page[1].index = (unsigned long) ptep; | ||
| 58 | return 0; | ||
| 59 | } | ||
| 60 | |||
| 61 | void arch_release_hugepage(struct page *page) | ||
| 62 | { | ||
| 63 | pte_t *ptep; | ||
| 64 | |||
| 65 | if (MACHINE_HAS_HPAGE) | ||
| 66 | return; | ||
| 67 | |||
| 68 | ptep = (pte_t *) page[1].index; | ||
| 69 | if (!ptep) | ||
| 70 | return; | ||
| 71 | pte_free(&init_mm, ptep); | ||
| 72 | page[1].index = 0; | ||
| 73 | } | ||
| 74 | |||
| 75 | pte_t *huge_pte_alloc(struct mm_struct *mm, unsigned long addr) | ||
| 76 | { | ||
| 77 | pgd_t *pgdp; | ||
| 78 | pud_t *pudp; | ||
| 79 | pmd_t *pmdp = NULL; | ||
| 80 | |||
| 81 | pgdp = pgd_offset(mm, addr); | ||
| 82 | pudp = pud_alloc(mm, pgdp, addr); | ||
| 83 | if (pudp) | ||
| 84 | pmdp = pmd_alloc(mm, pudp, addr); | ||
| 85 | return (pte_t *) pmdp; | ||
| 86 | } | ||
| 87 | |||
| 88 | pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr) | ||
| 89 | { | ||
| 90 | pgd_t *pgdp; | ||
| 91 | pud_t *pudp; | ||
| 92 | pmd_t *pmdp = NULL; | ||
| 93 | |||
| 94 | pgdp = pgd_offset(mm, addr); | ||
| 95 | if (pgd_present(*pgdp)) { | ||
| 96 | pudp = pud_offset(pgdp, addr); | ||
| 97 | if (pud_present(*pudp)) | ||
| 98 | pmdp = pmd_offset(pudp, addr); | ||
| 99 | } | ||
| 100 | return (pte_t *) pmdp; | ||
| 101 | } | ||
| 102 | |||
| 103 | int huge_pmd_unshare(struct mm_struct *mm, unsigned long *addr, pte_t *ptep) | ||
| 104 | { | ||
| 105 | return 0; | ||
| 106 | } | ||
| 107 | |||
| 108 | struct page *follow_huge_addr(struct mm_struct *mm, unsigned long address, | ||
| 109 | int write) | ||
| 110 | { | ||
| 111 | return ERR_PTR(-EINVAL); | ||
| 112 | } | ||
| 113 | |||
| 114 | int pmd_huge(pmd_t pmd) | ||
| 115 | { | ||
| 116 | if (!MACHINE_HAS_HPAGE) | ||
| 117 | return 0; | ||
| 118 | |||
| 119 | return !!(pmd_val(pmd) & _SEGMENT_ENTRY_LARGE); | ||
| 120 | } | ||
| 121 | |||
| 122 | struct page *follow_huge_pmd(struct mm_struct *mm, unsigned long address, | ||
| 123 | pmd_t *pmdp, int write) | ||
| 124 | { | ||
| 125 | struct page *page; | ||
| 126 | |||
| 127 | if (!MACHINE_HAS_HPAGE) | ||
| 128 | return NULL; | ||
| 129 | |||
| 130 | page = pmd_page(*pmdp); | ||
| 131 | if (page) | ||
| 132 | page += ((address & ~HPAGE_MASK) >> PAGE_SHIFT); | ||
| 133 | return page; | ||
| 134 | } | ||
diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c index 202c952a29b4..acc92f46a096 100644 --- a/arch/s390/mm/init.c +++ b/arch/s390/mm/init.c | |||
| @@ -77,28 +77,6 @@ void show_mem(void) | |||
| 77 | printk("%lu pages pagetables\n", global_page_state(NR_PAGETABLE)); | 77 | printk("%lu pages pagetables\n", global_page_state(NR_PAGETABLE)); |
| 78 | } | 78 | } |
| 79 | 79 | ||
| 80 | static void __init setup_ro_region(void) | ||
| 81 | { | ||
| 82 | pgd_t *pgd; | ||
| 83 | pud_t *pud; | ||
| 84 | pmd_t *pmd; | ||
| 85 | pte_t *pte; | ||
| 86 | pte_t new_pte; | ||
| 87 | unsigned long address, end; | ||
| 88 | |||
| 89 | address = ((unsigned long)&_stext) & PAGE_MASK; | ||
| 90 | end = PFN_ALIGN((unsigned long)&_eshared); | ||
| 91 | |||
| 92 | for (; address < end; address += PAGE_SIZE) { | ||
| 93 | pgd = pgd_offset_k(address); | ||
| 94 | pud = pud_offset(pgd, address); | ||
| 95 | pmd = pmd_offset(pud, address); | ||
| 96 | pte = pte_offset_kernel(pmd, address); | ||
| 97 | new_pte = mk_pte_phys(address, __pgprot(_PAGE_RO)); | ||
| 98 | *pte = new_pte; | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | /* | 80 | /* |
| 103 | * paging_init() sets up the page tables | 81 | * paging_init() sets up the page tables |
| 104 | */ | 82 | */ |
| @@ -121,7 +99,6 @@ void __init paging_init(void) | |||
| 121 | clear_table((unsigned long *) init_mm.pgd, pgd_type, | 99 | clear_table((unsigned long *) init_mm.pgd, pgd_type, |
| 122 | sizeof(unsigned long)*2048); | 100 | sizeof(unsigned long)*2048); |
| 123 | vmem_map_init(); | 101 | vmem_map_init(); |
| 124 | setup_ro_region(); | ||
| 125 | 102 | ||
| 126 | /* enable virtual mapping in kernel mode */ | 103 | /* enable virtual mapping in kernel mode */ |
| 127 | __ctl_load(S390_lowcore.kernel_asce, 1, 1); | 104 | __ctl_load(S390_lowcore.kernel_asce, 1, 1); |
diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c index 3ffc0211dc85..97bce6c97574 100644 --- a/arch/s390/mm/vmem.c +++ b/arch/s390/mm/vmem.c | |||
| @@ -10,10 +10,12 @@ | |||
| 10 | #include <linux/mm.h> | 10 | #include <linux/mm.h> |
| 11 | #include <linux/module.h> | 11 | #include <linux/module.h> |
| 12 | #include <linux/list.h> | 12 | #include <linux/list.h> |
| 13 | #include <linux/hugetlb.h> | ||
| 13 | #include <asm/pgalloc.h> | 14 | #include <asm/pgalloc.h> |
| 14 | #include <asm/pgtable.h> | 15 | #include <asm/pgtable.h> |
| 15 | #include <asm/setup.h> | 16 | #include <asm/setup.h> |
| 16 | #include <asm/tlbflush.h> | 17 | #include <asm/tlbflush.h> |
| 18 | #include <asm/sections.h> | ||
| 17 | 19 | ||
| 18 | static DEFINE_MUTEX(vmem_mutex); | 20 | static DEFINE_MUTEX(vmem_mutex); |
| 19 | 21 | ||
| @@ -113,7 +115,7 @@ static pte_t __init_refok *vmem_pte_alloc(void) | |||
| 113 | /* | 115 | /* |
| 114 | * Add a physical memory range to the 1:1 mapping. | 116 | * Add a physical memory range to the 1:1 mapping. |
| 115 | */ | 117 | */ |
| 116 | static int vmem_add_range(unsigned long start, unsigned long size) | 118 | static int vmem_add_range(unsigned long start, unsigned long size, int ro) |
| 117 | { | 119 | { |
| 118 | unsigned long address; | 120 | unsigned long address; |
| 119 | pgd_t *pg_dir; | 121 | pgd_t *pg_dir; |
| @@ -140,7 +142,19 @@ static int vmem_add_range(unsigned long start, unsigned long size) | |||
| 140 | pud_populate_kernel(&init_mm, pu_dir, pm_dir); | 142 | pud_populate_kernel(&init_mm, pu_dir, pm_dir); |
| 141 | } | 143 | } |
| 142 | 144 | ||
| 145 | pte = mk_pte_phys(address, __pgprot(ro ? _PAGE_RO : 0)); | ||
| 143 | pm_dir = pmd_offset(pu_dir, address); | 146 | pm_dir = pmd_offset(pu_dir, address); |
| 147 | |||
| 148 | #ifdef __s390x__ | ||
| 149 | if (MACHINE_HAS_HPAGE && !(address & ~HPAGE_MASK) && | ||
| 150 | (address + HPAGE_SIZE <= start + size) && | ||
| 151 | (address >= HPAGE_SIZE)) { | ||
| 152 | pte_val(pte) |= _SEGMENT_ENTRY_LARGE; | ||
| 153 | pmd_val(*pm_dir) = pte_val(pte); | ||
| 154 | address += HPAGE_SIZE - PAGE_SIZE; | ||
| 155 | continue; | ||
| 156 | } | ||
| 157 | #endif | ||
| 144 | if (pmd_none(*pm_dir)) { | 158 | if (pmd_none(*pm_dir)) { |
| 145 | pt_dir = vmem_pte_alloc(); | 159 | pt_dir = vmem_pte_alloc(); |
| 146 | if (!pt_dir) | 160 | if (!pt_dir) |
| @@ -149,7 +163,6 @@ static int vmem_add_range(unsigned long start, unsigned long size) | |||
| 149 | } | 163 | } |
| 150 | 164 | ||
| 151 | pt_dir = pte_offset_kernel(pm_dir, address); | 165 | pt_dir = pte_offset_kernel(pm_dir, address); |
| 152 | pte = pfn_pte(address >> PAGE_SHIFT, PAGE_KERNEL); | ||
| 153 | *pt_dir = pte; | 166 | *pt_dir = pte; |
| 154 | } | 167 | } |
| 155 | ret = 0; | 168 | ret = 0; |
| @@ -180,6 +193,13 @@ static void vmem_remove_range(unsigned long start, unsigned long size) | |||
| 180 | pm_dir = pmd_offset(pu_dir, address); | 193 | pm_dir = pmd_offset(pu_dir, address); |
| 181 | if (pmd_none(*pm_dir)) | 194 | if (pmd_none(*pm_dir)) |
| 182 | continue; | 195 | continue; |
| 196 | |||
| 197 | if (pmd_huge(*pm_dir)) { | ||
| 198 | pmd_clear_kernel(pm_dir); | ||
| 199 | address += HPAGE_SIZE - PAGE_SIZE; | ||
| 200 | continue; | ||
| 201 | } | ||
| 202 | |||
| 183 | pt_dir = pte_offset_kernel(pm_dir, address); | 203 | pt_dir = pte_offset_kernel(pm_dir, address); |
| 184 | *pt_dir = pte; | 204 | *pt_dir = pte; |
| 185 | } | 205 | } |
| @@ -248,14 +268,14 @@ out: | |||
| 248 | return ret; | 268 | return ret; |
| 249 | } | 269 | } |
| 250 | 270 | ||
| 251 | static int vmem_add_mem(unsigned long start, unsigned long size) | 271 | static int vmem_add_mem(unsigned long start, unsigned long size, int ro) |
| 252 | { | 272 | { |
| 253 | int ret; | 273 | int ret; |
| 254 | 274 | ||
| 255 | ret = vmem_add_mem_map(start, size); | 275 | ret = vmem_add_mem_map(start, size); |
| 256 | if (ret) | 276 | if (ret) |
| 257 | return ret; | 277 | return ret; |
| 258 | return vmem_add_range(start, size); | 278 | return vmem_add_range(start, size, ro); |
| 259 | } | 279 | } |
| 260 | 280 | ||
| 261 | /* | 281 | /* |
| @@ -338,7 +358,7 @@ int add_shared_memory(unsigned long start, unsigned long size) | |||
| 338 | if (ret) | 358 | if (ret) |
| 339 | goto out_free; | 359 | goto out_free; |
| 340 | 360 | ||
| 341 | ret = vmem_add_mem(start, size); | 361 | ret = vmem_add_mem(start, size, 0); |
| 342 | if (ret) | 362 | if (ret) |
| 343 | goto out_remove; | 363 | goto out_remove; |
| 344 | 364 | ||
| @@ -374,14 +394,35 @@ out: | |||
| 374 | */ | 394 | */ |
| 375 | void __init vmem_map_init(void) | 395 | void __init vmem_map_init(void) |
| 376 | { | 396 | { |
| 397 | unsigned long ro_start, ro_end; | ||
| 398 | unsigned long start, end; | ||
| 377 | int i; | 399 | int i; |
| 378 | 400 | ||
| 379 | INIT_LIST_HEAD(&init_mm.context.crst_list); | 401 | INIT_LIST_HEAD(&init_mm.context.crst_list); |
| 380 | INIT_LIST_HEAD(&init_mm.context.pgtable_list); | 402 | INIT_LIST_HEAD(&init_mm.context.pgtable_list); |
| 381 | init_mm.context.noexec = 0; | 403 | init_mm.context.noexec = 0; |
| 382 | NODE_DATA(0)->node_mem_map = VMEM_MAP; | 404 | NODE_DATA(0)->node_mem_map = VMEM_MAP; |
| 383 | for (i = 0; i < MEMORY_CHUNKS && memory_chunk[i].size > 0; i++) | 405 | ro_start = ((unsigned long)&_stext) & PAGE_MASK; |
| 384 | vmem_add_mem(memory_chunk[i].addr, memory_chunk[i].size); | 406 | ro_end = PFN_ALIGN((unsigned long)&_eshared); |
| 407 | for (i = 0; i < MEMORY_CHUNKS && memory_chunk[i].size > 0; i++) { | ||
| 408 | start = memory_chunk[i].addr; | ||
| 409 | end = memory_chunk[i].addr + memory_chunk[i].size; | ||
| 410 | if (start >= ro_end || end <= ro_start) | ||
| 411 | vmem_add_mem(start, end - start, 0); | ||
| 412 | else if (start >= ro_start && end <= ro_end) | ||
| 413 | vmem_add_mem(start, end - start, 1); | ||
| 414 | else if (start >= ro_start) { | ||
| 415 | vmem_add_mem(start, ro_end - start, 1); | ||
| 416 | vmem_add_mem(ro_end, end - ro_end, 0); | ||
| 417 | } else if (end < ro_end) { | ||
| 418 | vmem_add_mem(start, ro_start - start, 0); | ||
| 419 | vmem_add_mem(ro_start, end - ro_start, 1); | ||
| 420 | } else { | ||
| 421 | vmem_add_mem(start, ro_start - start, 0); | ||
| 422 | vmem_add_mem(ro_start, ro_end - ro_start, 1); | ||
| 423 | vmem_add_mem(ro_end, end - ro_end, 0); | ||
| 424 | } | ||
| 425 | } | ||
| 385 | } | 426 | } |
| 386 | 427 | ||
| 387 | /* | 428 | /* |
diff --git a/fs/Kconfig b/fs/Kconfig index 2e43d46f65d6..cf12c403b8c7 100644 --- a/fs/Kconfig +++ b/fs/Kconfig | |||
| @@ -1005,7 +1005,8 @@ config TMPFS_POSIX_ACL | |||
| 1005 | 1005 | ||
| 1006 | config HUGETLBFS | 1006 | config HUGETLBFS |
| 1007 | bool "HugeTLB file system support" | 1007 | bool "HugeTLB file system support" |
| 1008 | depends on X86 || IA64 || PPC64 || SPARC64 || (SUPERH && MMU) || BROKEN | 1008 | depends on X86 || IA64 || PPC64 || SPARC64 || (SUPERH && MMU) || \ |
| 1009 | (S390 && 64BIT) || BROKEN | ||
| 1009 | help | 1010 | help |
| 1010 | hugetlbfs is a filesystem backing for HugeTLB pages, based on | 1011 | hugetlbfs is a filesystem backing for HugeTLB pages, based on |
| 1011 | ramfs. For architectures that support it, say Y here and read | 1012 | ramfs. For architectures that support it, say Y here and read |
diff --git a/include/asm-s390/hugetlb.h b/include/asm-s390/hugetlb.h new file mode 100644 index 000000000000..600a776f8f75 --- /dev/null +++ b/include/asm-s390/hugetlb.h | |||
| @@ -0,0 +1,183 @@ | |||
| 1 | /* | ||
| 2 | * IBM System z Huge TLB Page Support for Kernel. | ||
| 3 | * | ||
| 4 | * Copyright IBM Corp. 2008 | ||
| 5 | * Author(s): Gerald Schaefer <gerald.schaefer@de.ibm.com> | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef _ASM_S390_HUGETLB_H | ||
| 9 | #define _ASM_S390_HUGETLB_H | ||
| 10 | |||
| 11 | #include <asm/page.h> | ||
| 12 | #include <asm/pgtable.h> | ||
| 13 | |||
| 14 | |||
| 15 | #define is_hugepage_only_range(mm, addr, len) 0 | ||
| 16 | #define hugetlb_free_pgd_range free_pgd_range | ||
| 17 | |||
| 18 | void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, | ||
| 19 | pte_t *ptep, pte_t pte); | ||
| 20 | |||
| 21 | /* | ||
| 22 | * If the arch doesn't supply something else, assume that hugepage | ||
| 23 | * size aligned regions are ok without further preparation. | ||
| 24 | */ | ||
| 25 | static inline int prepare_hugepage_range(unsigned long addr, unsigned long len) | ||
| 26 | { | ||
| 27 | if (len & ~HPAGE_MASK) | ||
| 28 | return -EINVAL; | ||
| 29 | if (addr & ~HPAGE_MASK) | ||
| 30 | return -EINVAL; | ||
| 31 | return 0; | ||
| 32 | } | ||
| 33 | |||
| 34 | #define hugetlb_prefault_arch_hook(mm) do { } while (0) | ||
| 35 | |||
| 36 | int arch_prepare_hugepage(struct page *page); | ||
| 37 | void arch_release_hugepage(struct page *page); | ||
| 38 | |||
| 39 | static inline pte_t pte_mkhuge(pte_t pte) | ||
| 40 | { | ||
| 41 | /* | ||
| 42 | * PROT_NONE needs to be remapped from the pte type to the ste type. | ||
| 43 | * The HW invalid bit is also different for pte and ste. The pte | ||
| 44 | * invalid bit happens to be the same as the ste _SEGMENT_ENTRY_LARGE | ||
| 45 | * bit, so we don't have to clear it. | ||
| 46 | */ | ||
| 47 | if (pte_val(pte) & _PAGE_INVALID) { | ||
| 48 | if (pte_val(pte) & _PAGE_SWT) | ||
| 49 | pte_val(pte) |= _HPAGE_TYPE_NONE; | ||
| 50 | pte_val(pte) |= _SEGMENT_ENTRY_INV; | ||
| 51 | } | ||
| 52 | /* | ||
| 53 | * Clear SW pte bits SWT and SWX, there are no SW bits in a segment | ||
| 54 | * table entry. | ||
| 55 | */ | ||
| 56 | pte_val(pte) &= ~(_PAGE_SWT | _PAGE_SWX); | ||
| 57 | /* | ||
| 58 | * Also set the change-override bit because we don't need dirty bit | ||
| 59 | * tracking for hugetlbfs pages. | ||
| 60 | */ | ||
| 61 | pte_val(pte) |= (_SEGMENT_ENTRY_LARGE | _SEGMENT_ENTRY_CO); | ||
| 62 | return pte; | ||
| 63 | } | ||
| 64 | |||
| 65 | static inline pte_t huge_pte_wrprotect(pte_t pte) | ||
| 66 | { | ||
| 67 | pte_val(pte) |= _PAGE_RO; | ||
| 68 | return pte; | ||
| 69 | } | ||
| 70 | |||
| 71 | static inline int huge_pte_none(pte_t pte) | ||
| 72 | { | ||
| 73 | return (pte_val(pte) & _SEGMENT_ENTRY_INV) && | ||
| 74 | !(pte_val(pte) & _SEGMENT_ENTRY_RO); | ||
| 75 | } | ||
| 76 | |||
| 77 | static inline pte_t huge_ptep_get(pte_t *ptep) | ||
| 78 | { | ||
| 79 | pte_t pte = *ptep; | ||
| 80 | unsigned long mask; | ||
| 81 | |||
| 82 | if (!MACHINE_HAS_HPAGE) { | ||
| 83 | ptep = (pte_t *) (pte_val(pte) & _SEGMENT_ENTRY_ORIGIN); | ||
| 84 | if (ptep) { | ||
| 85 | mask = pte_val(pte) & | ||
| 86 | (_SEGMENT_ENTRY_INV | _SEGMENT_ENTRY_RO); | ||
| 87 | pte = pte_mkhuge(*ptep); | ||
| 88 | pte_val(pte) |= mask; | ||
| 89 | } | ||
| 90 | } | ||
| 91 | return pte; | ||
| 92 | } | ||
| 93 | |||
| 94 | static inline pte_t huge_ptep_get_and_clear(struct mm_struct *mm, | ||
| 95 | unsigned long addr, pte_t *ptep) | ||
| 96 | { | ||
| 97 | pte_t pte = huge_ptep_get(ptep); | ||
| 98 | |||
| 99 | pmd_clear((pmd_t *) ptep); | ||
| 100 | return pte; | ||
| 101 | } | ||
| 102 | |||
| 103 | static inline void __pmd_csp(pmd_t *pmdp) | ||
| 104 | { | ||
| 105 | register unsigned long reg2 asm("2") = pmd_val(*pmdp); | ||
| 106 | register unsigned long reg3 asm("3") = pmd_val(*pmdp) | | ||
| 107 | _SEGMENT_ENTRY_INV; | ||
| 108 | register unsigned long reg4 asm("4") = ((unsigned long) pmdp) + 5; | ||
| 109 | |||
| 110 | asm volatile( | ||
| 111 | " csp %1,%3" | ||
| 112 | : "=m" (*pmdp) | ||
| 113 | : "d" (reg2), "d" (reg3), "d" (reg4), "m" (*pmdp) : "cc"); | ||
| 114 | pmd_val(*pmdp) = _SEGMENT_ENTRY_INV | _SEGMENT_ENTRY; | ||
| 115 | } | ||
| 116 | |||
| 117 | static inline void __pmd_idte(unsigned long address, pmd_t *pmdp) | ||
| 118 | { | ||
| 119 | unsigned long sto = (unsigned long) pmdp - | ||
| 120 | pmd_index(address) * sizeof(pmd_t); | ||
| 121 | |||
| 122 | if (!(pmd_val(*pmdp) & _SEGMENT_ENTRY_INV)) { | ||
| 123 | asm volatile( | ||
| 124 | " .insn rrf,0xb98e0000,%2,%3,0,0" | ||
| 125 | : "=m" (*pmdp) | ||
| 126 | : "m" (*pmdp), "a" (sto), | ||
| 127 | "a" ((address & HPAGE_MASK)) | ||
| 128 | ); | ||
| 129 | } | ||
| 130 | pmd_val(*pmdp) = _SEGMENT_ENTRY_INV | _SEGMENT_ENTRY; | ||
| 131 | } | ||
| 132 | |||
| 133 | static inline void huge_ptep_invalidate(struct mm_struct *mm, | ||
| 134 | unsigned long address, pte_t *ptep) | ||
| 135 | { | ||
| 136 | pmd_t *pmdp = (pmd_t *) ptep; | ||
| 137 | |||
| 138 | if (!MACHINE_HAS_IDTE) { | ||
| 139 | __pmd_csp(pmdp); | ||
| 140 | if (mm->context.noexec) { | ||
| 141 | pmdp = get_shadow_table(pmdp); | ||
| 142 | __pmd_csp(pmdp); | ||
| 143 | } | ||
| 144 | return; | ||
| 145 | } | ||
| 146 | |||
| 147 | __pmd_idte(address, pmdp); | ||
| 148 | if (mm->context.noexec) { | ||
| 149 | pmdp = get_shadow_table(pmdp); | ||
| 150 | __pmd_idte(address, pmdp); | ||
| 151 | } | ||
| 152 | return; | ||
| 153 | } | ||
| 154 | |||
| 155 | #define huge_ptep_set_access_flags(__vma, __addr, __ptep, __entry, __dirty) \ | ||
| 156 | ({ \ | ||
| 157 | int __changed = !pte_same(huge_ptep_get(__ptep), __entry); \ | ||
| 158 | if (__changed) { \ | ||
| 159 | huge_ptep_invalidate((__vma)->vm_mm, __addr, __ptep); \ | ||
| 160 | set_huge_pte_at((__vma)->vm_mm, __addr, __ptep, __entry); \ | ||
| 161 | } \ | ||
| 162 | __changed; \ | ||
| 163 | }) | ||
| 164 | |||
| 165 | #define huge_ptep_set_wrprotect(__mm, __addr, __ptep) \ | ||
| 166 | ({ \ | ||
| 167 | pte_t __pte = huge_ptep_get(__ptep); \ | ||
| 168 | if (pte_write(__pte)) { \ | ||
| 169 | if (atomic_read(&(__mm)->mm_users) > 1 || \ | ||
| 170 | (__mm) != current->active_mm) \ | ||
| 171 | huge_ptep_invalidate(__mm, __addr, __ptep); \ | ||
| 172 | set_huge_pte_at(__mm, __addr, __ptep, \ | ||
| 173 | huge_pte_wrprotect(__pte)); \ | ||
| 174 | } \ | ||
| 175 | }) | ||
| 176 | |||
| 177 | static inline void huge_ptep_clear_flush(struct vm_area_struct *vma, | ||
| 178 | unsigned long address, pte_t *ptep) | ||
| 179 | { | ||
| 180 | huge_ptep_invalidate(vma->vm_mm, address, ptep); | ||
| 181 | } | ||
| 182 | |||
| 183 | #endif /* _ASM_S390_HUGETLB_H */ | ||
diff --git a/include/asm-s390/page.h b/include/asm-s390/page.h index fe7f92b6ae6d..b01e6fc9a295 100644 --- a/include/asm-s390/page.h +++ b/include/asm-s390/page.h | |||
| @@ -19,17 +19,34 @@ | |||
| 19 | #define PAGE_DEFAULT_ACC 0 | 19 | #define PAGE_DEFAULT_ACC 0 |
| 20 | #define PAGE_DEFAULT_KEY (PAGE_DEFAULT_ACC << 4) | 20 | #define PAGE_DEFAULT_KEY (PAGE_DEFAULT_ACC << 4) |
| 21 | 21 | ||
| 22 | #define HPAGE_SHIFT 20 | ||
| 23 | #define HPAGE_SIZE (1UL << HPAGE_SHIFT) | ||
| 24 | #define HPAGE_MASK (~(HPAGE_SIZE - 1)) | ||
| 25 | #define HUGETLB_PAGE_ORDER (HPAGE_SHIFT - PAGE_SHIFT) | ||
| 26 | |||
| 27 | #define ARCH_HAS_SETCLEAR_HUGE_PTE | ||
| 28 | #define ARCH_HAS_HUGE_PTE_TYPE | ||
| 29 | #define ARCH_HAS_PREPARE_HUGEPAGE | ||
| 30 | #define ARCH_HAS_HUGEPAGE_CLEAR_FLUSH | ||
| 31 | |||
| 22 | #include <asm/setup.h> | 32 | #include <asm/setup.h> |
| 23 | #ifndef __ASSEMBLY__ | 33 | #ifndef __ASSEMBLY__ |
| 24 | 34 | ||
| 25 | static inline void clear_page(void *page) | 35 | static inline void clear_page(void *page) |
| 26 | { | 36 | { |
| 27 | register unsigned long reg1 asm ("1") = 0; | 37 | if (MACHINE_HAS_PFMF) { |
| 28 | register void *reg2 asm ("2") = page; | 38 | asm volatile( |
| 29 | register unsigned long reg3 asm ("3") = 4096; | 39 | " .insn rre,0xb9af0000,%0,%1" |
| 30 | asm volatile( | 40 | : : "d" (0x10000), "a" (page) : "memory", "cc"); |
| 31 | " mvcl 2,0" | 41 | } else { |
| 32 | : "+d" (reg2), "+d" (reg3) : "d" (reg1) : "memory", "cc"); | 42 | register unsigned long reg1 asm ("1") = 0; |
| 43 | register void *reg2 asm ("2") = page; | ||
| 44 | register unsigned long reg3 asm ("3") = 4096; | ||
| 45 | asm volatile( | ||
| 46 | " mvcl 2,0" | ||
| 47 | : "+d" (reg2), "+d" (reg3) : "d" (reg1) | ||
| 48 | : "memory", "cc"); | ||
| 49 | } | ||
| 33 | } | 50 | } |
| 34 | 51 | ||
| 35 | static inline void copy_page(void *to, void *from) | 52 | static inline void copy_page(void *to, void *from) |
diff --git a/include/asm-s390/pgtable.h b/include/asm-s390/pgtable.h index f8347ce9c5a1..fd336f2e2a7a 100644 --- a/include/asm-s390/pgtable.h +++ b/include/asm-s390/pgtable.h | |||
| @@ -234,6 +234,15 @@ extern char empty_zero_page[PAGE_SIZE]; | |||
| 234 | #define _PAGE_TYPE_EX_RW 0x002 | 234 | #define _PAGE_TYPE_EX_RW 0x002 |
| 235 | 235 | ||
| 236 | /* | 236 | /* |
| 237 | * Only four types for huge pages, using the invalid bit and protection bit | ||
| 238 | * of a segment table entry. | ||
| 239 | */ | ||
| 240 | #define _HPAGE_TYPE_EMPTY 0x020 /* _SEGMENT_ENTRY_INV */ | ||
| 241 | #define _HPAGE_TYPE_NONE 0x220 | ||
| 242 | #define _HPAGE_TYPE_RO 0x200 /* _SEGMENT_ENTRY_RO */ | ||
| 243 | #define _HPAGE_TYPE_RW 0x000 | ||
| 244 | |||
| 245 | /* | ||
| 237 | * PTE type bits are rather complicated. handle_pte_fault uses pte_present, | 246 | * PTE type bits are rather complicated. handle_pte_fault uses pte_present, |
| 238 | * pte_none and pte_file to find out the pte type WITHOUT holding the page | 247 | * pte_none and pte_file to find out the pte type WITHOUT holding the page |
| 239 | * table lock. ptep_clear_flush on the other hand uses ptep_clear_flush to | 248 | * table lock. ptep_clear_flush on the other hand uses ptep_clear_flush to |
| @@ -325,6 +334,9 @@ extern char empty_zero_page[PAGE_SIZE]; | |||
| 325 | #define _SEGMENT_ENTRY (0) | 334 | #define _SEGMENT_ENTRY (0) |
| 326 | #define _SEGMENT_ENTRY_EMPTY (_SEGMENT_ENTRY_INV) | 335 | #define _SEGMENT_ENTRY_EMPTY (_SEGMENT_ENTRY_INV) |
| 327 | 336 | ||
| 337 | #define _SEGMENT_ENTRY_LARGE 0x400 /* STE-format control, large page */ | ||
| 338 | #define _SEGMENT_ENTRY_CO 0x100 /* change-recording override */ | ||
| 339 | |||
| 328 | #endif /* __s390x__ */ | 340 | #endif /* __s390x__ */ |
| 329 | 341 | ||
| 330 | /* | 342 | /* |
diff --git a/include/asm-s390/setup.h b/include/asm-s390/setup.h index 3a9e458fd8c3..ba69674012a7 100644 --- a/include/asm-s390/setup.h +++ b/include/asm-s390/setup.h | |||
| @@ -69,6 +69,8 @@ extern unsigned long machine_flags; | |||
| 69 | #define MACHINE_FLAG_DIAG9C (1UL << 7) | 69 | #define MACHINE_FLAG_DIAG9C (1UL << 7) |
| 70 | #define MACHINE_FLAG_MVCOS (1UL << 8) | 70 | #define MACHINE_FLAG_MVCOS (1UL << 8) |
| 71 | #define MACHINE_FLAG_KVM (1UL << 9) | 71 | #define MACHINE_FLAG_KVM (1UL << 9) |
| 72 | #define MACHINE_FLAG_HPAGE (1UL << 10) | ||
| 73 | #define MACHINE_FLAG_PFMF (1UL << 11) | ||
| 72 | 74 | ||
| 73 | #define MACHINE_IS_VM (machine_flags & MACHINE_FLAG_VM) | 75 | #define MACHINE_IS_VM (machine_flags & MACHINE_FLAG_VM) |
| 74 | #define MACHINE_IS_KVM (machine_flags & MACHINE_FLAG_KVM) | 76 | #define MACHINE_IS_KVM (machine_flags & MACHINE_FLAG_KVM) |
| @@ -82,6 +84,8 @@ extern unsigned long machine_flags; | |||
| 82 | #define MACHINE_HAS_DIAG44 (1) | 84 | #define MACHINE_HAS_DIAG44 (1) |
| 83 | #define MACHINE_HAS_MVPG (machine_flags & MACHINE_FLAG_MVPG) | 85 | #define MACHINE_HAS_MVPG (machine_flags & MACHINE_FLAG_MVPG) |
| 84 | #define MACHINE_HAS_MVCOS (0) | 86 | #define MACHINE_HAS_MVCOS (0) |
| 87 | #define MACHINE_HAS_HPAGE (0) | ||
| 88 | #define MACHINE_HAS_PFMF (0) | ||
| 85 | #else /* __s390x__ */ | 89 | #else /* __s390x__ */ |
| 86 | #define MACHINE_HAS_IEEE (1) | 90 | #define MACHINE_HAS_IEEE (1) |
| 87 | #define MACHINE_HAS_CSP (1) | 91 | #define MACHINE_HAS_CSP (1) |
| @@ -89,6 +93,8 @@ extern unsigned long machine_flags; | |||
| 89 | #define MACHINE_HAS_DIAG44 (machine_flags & MACHINE_FLAG_DIAG44) | 93 | #define MACHINE_HAS_DIAG44 (machine_flags & MACHINE_FLAG_DIAG44) |
| 90 | #define MACHINE_HAS_MVPG (1) | 94 | #define MACHINE_HAS_MVPG (1) |
| 91 | #define MACHINE_HAS_MVCOS (machine_flags & MACHINE_FLAG_MVCOS) | 95 | #define MACHINE_HAS_MVCOS (machine_flags & MACHINE_FLAG_MVCOS) |
| 96 | #define MACHINE_HAS_HPAGE (machine_flags & MACHINE_FLAG_HPAGE) | ||
| 97 | #define MACHINE_HAS_PFMF (machine_flags & MACHINE_FLAG_PFMF) | ||
| 92 | #endif /* __s390x__ */ | 98 | #endif /* __s390x__ */ |
| 93 | 99 | ||
| 94 | #define MACHINE_HAS_SCLP (!MACHINE_IS_P390) | 100 | #define MACHINE_HAS_SCLP (!MACHINE_IS_P390) |
diff --git a/include/asm-s390/tlbflush.h b/include/asm-s390/tlbflush.h index 9e57a93d7de1..d60394b9745e 100644 --- a/include/asm-s390/tlbflush.h +++ b/include/asm-s390/tlbflush.h | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | #define _S390_TLBFLUSH_H | 2 | #define _S390_TLBFLUSH_H |
| 3 | 3 | ||
| 4 | #include <linux/mm.h> | 4 | #include <linux/mm.h> |
| 5 | #include <linux/sched.h> | ||
| 5 | #include <asm/processor.h> | 6 | #include <asm/processor.h> |
| 6 | #include <asm/pgalloc.h> | 7 | #include <asm/pgalloc.h> |
| 7 | 8 | ||
