]> pilppa.com Git - linux-2.6-omap-h63xx.git/commitdiff
[S390] dynamic page tables.
authorMartin Schwidefsky <schwidefsky@de.ibm.com>
Sat, 9 Feb 2008 17:24:37 +0000 (18:24 +0100)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Sat, 9 Feb 2008 17:24:41 +0000 (18:24 +0100)
Add support for different number of page table levels dependent
on the highest address used for a process. This will cause a 31 bit
process to use a two level page table instead of the four level page
table that is the default after the pud has been introduced. Likewise
a normal 64 bit process will use three levels instead of four. Only
if a process runs out of the 4 tera bytes which can be addressed with
a three level page table the fourth level is dynamically added. Then
the process can use up to 8 peta byte.

Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
13 files changed:
arch/s390/kernel/binfmt_elf32.c
arch/s390/kernel/traps.c
arch/s390/mm/fault.c
arch/s390/mm/init.c
arch/s390/mm/mmap.c
arch/s390/mm/pgtable.c
include/asm-s390/elf.h
include/asm-s390/mmu.h
include/asm-s390/mmu_context.h
include/asm-s390/pgalloc.h
include/asm-s390/pgtable.h
include/asm-s390/processor.h
include/asm-s390/tlb.h

index f1e40ca00d8d97c93ff630010f0a69becd9af815..3e1c315b736d70a8ab030fdf82b08a0915a4769c 100644 (file)
@@ -134,6 +134,7 @@ static inline int dump_task_fpu(struct task_struct *tsk, elf_fpregset_t *fpregs)
 }
 
 #include <asm/processor.h>
+#include <asm/pgalloc.h>
 #include <linux/module.h>
 #include <linux/elfcore.h>
 #include <linux/binfmts.h>
@@ -183,6 +184,16 @@ struct elf_prpsinfo32
 #undef start_thread
 #define start_thread                    start_thread31 
 
+static inline void start_thread31(struct pt_regs *regs, unsigned long new_psw,
+                                 unsigned long new_stackp)
+{
+       set_fs(USER_DS);
+       regs->psw.mask  = psw_user32_bits;
+       regs->psw.addr  = new_psw;
+       regs->gprs[15]  = new_stackp;
+       crst_table_downgrade(current->mm, 1UL << 31);
+}
+
 MODULE_DESCRIPTION("Binary format loader for compatibility with 32bit Linux for S390 binaries,"
                    " Copyright 2000 IBM Corporation"); 
 MODULE_AUTHOR("Gerhard Tonn <ton@de.ibm.com>");
index a4d29025ddbd9b178197495d955877486b0101f3..60f728aeaf12986ceebbf52c2303805bed0ef378 100644 (file)
@@ -60,6 +60,7 @@ int sysctl_userprocess_debug = 0;
 extern pgm_check_handler_t do_protection_exception;
 extern pgm_check_handler_t do_dat_exception;
 extern pgm_check_handler_t do_monitor_call;
+extern pgm_check_handler_t do_asce_exception;
 
 #define stack_pointer ({ void **sp; asm("la %0,0(15)" : "=&d" (sp)); sp; })
 
@@ -730,7 +731,7 @@ void __init trap_init(void)
         pgm_check_table[0x12] = &translation_exception;
         pgm_check_table[0x13] = &special_op_exception;
 #ifdef CONFIG_64BIT
-        pgm_check_table[0x38] = &do_dat_exception;
+       pgm_check_table[0x38] = &do_asce_exception;
        pgm_check_table[0x39] = &do_dat_exception;
        pgm_check_table[0x3A] = &do_dat_exception;
         pgm_check_table[0x3B] = &do_dat_exception;
index 2456b52ed0687e4ac588fa47ae860cc0973636fa..ed13d429a487d01c4cf927b6d7cfc4a7e519faf9 100644 (file)
@@ -32,6 +32,7 @@
 #include <asm/system.h>
 #include <asm/pgtable.h>
 #include <asm/s390_ext.h>
+#include <asm/mmu_context.h>
 
 #ifndef CONFIG_64BIT
 #define __FAIL_ADDR_MASK 0x7ffff000
@@ -444,6 +445,45 @@ void __kprobes do_dat_exception(struct pt_regs *regs, unsigned long error_code)
        do_exception(regs, error_code & 0xff, 0);
 }
 
+#ifdef CONFIG_64BIT
+void __kprobes do_asce_exception(struct pt_regs *regs, unsigned long error_code)
+{
+       struct mm_struct *mm;
+       struct vm_area_struct *vma;
+       unsigned long address;
+       int space;
+
+       mm = current->mm;
+       address = S390_lowcore.trans_exc_code & __FAIL_ADDR_MASK;
+       space = check_space(current);
+
+       if (unlikely(space == 0 || in_atomic() || !mm))
+               goto no_context;
+
+       local_irq_enable();
+
+       down_read(&mm->mmap_sem);
+       vma = find_vma(mm, address);
+       up_read(&mm->mmap_sem);
+
+       if (vma) {
+               update_mm(mm, current);
+               return;
+       }
+
+       /* User mode accesses just cause a SIGSEGV */
+       if (regs->psw.mask & PSW_MASK_PSTATE) {
+               current->thread.prot_addr = address;
+               current->thread.trap_no = error_code;
+               do_sigsegv(regs, error_code, SEGV_MAPERR, address);
+               return;
+       }
+
+no_context:
+       do_no_context(regs, error_code, address);
+}
+#endif
+
 #ifdef CONFIG_PFAULT 
 /*
  * 'pfault' pseudo page faults routines.
index 248a7101070019aab29d3da1f477e61531af5857..8053245fe259212bedd8075a6560164c5ec977ae 100644 (file)
@@ -112,8 +112,9 @@ void __init paging_init(void)
        init_mm.pgd = swapper_pg_dir;
        S390_lowcore.kernel_asce = __pa(init_mm.pgd) & PAGE_MASK;
 #ifdef CONFIG_64BIT
-       S390_lowcore.kernel_asce |= _ASCE_TYPE_REGION2 | _ASCE_TABLE_LENGTH;
-       pgd_type = _REGION2_ENTRY_EMPTY;
+       /* A three level page table (4TB) is enough for the kernel space. */
+       S390_lowcore.kernel_asce |= _ASCE_TYPE_REGION3 | _ASCE_TABLE_LENGTH;
+       pgd_type = _REGION3_ENTRY_EMPTY;
 #else
        S390_lowcore.kernel_asce |= _ASCE_TABLE_LENGTH;
        pgd_type = _SEGMENT_ENTRY_EMPTY;
index 356257c171de77ef60aba46f0af097b39fc97591..5932a824547a51c370e28f05583a5eef12d9931e 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/personality.h>
 #include <linux/mm.h>
 #include <linux/module.h>
+#include <asm/pgalloc.h>
 
 /*
  * Top of mmap area (just below the process stack).
@@ -62,6 +63,8 @@ static inline int mmap_is_legacy(void)
            current->signal->rlim[RLIMIT_STACK].rlim_cur == RLIM_INFINITY;
 }
 
+#ifndef CONFIG_64BIT
+
 /*
  * This function, called very early during the creation of a new
  * process VM image, sets up which VM layout function to use:
@@ -84,3 +87,65 @@ void arch_pick_mmap_layout(struct mm_struct *mm)
 }
 EXPORT_SYMBOL_GPL(arch_pick_mmap_layout);
 
+#else
+
+static unsigned long
+s390_get_unmapped_area(struct file *filp, unsigned long addr,
+               unsigned long len, unsigned long pgoff, unsigned long flags)
+{
+       struct mm_struct *mm = current->mm;
+       int rc;
+
+       addr = arch_get_unmapped_area(filp, addr, len, pgoff, flags);
+       if (addr & ~PAGE_MASK)
+               return addr;
+       if (unlikely(mm->context.asce_limit < addr + len)) {
+               rc = crst_table_upgrade(mm, addr + len);
+               if (rc)
+                       return (unsigned long) rc;
+       }
+       return addr;
+}
+
+static unsigned long
+s390_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
+                         const unsigned long len, const unsigned long pgoff,
+                         const unsigned long flags)
+{
+       struct mm_struct *mm = current->mm;
+       unsigned long addr = addr0;
+       int rc;
+
+       addr = arch_get_unmapped_area_topdown(filp, addr, len, pgoff, flags);
+       if (addr & ~PAGE_MASK)
+               return addr;
+       if (unlikely(mm->context.asce_limit < addr + len)) {
+               rc = crst_table_upgrade(mm, addr + len);
+               if (rc)
+                       return (unsigned long) rc;
+       }
+       return addr;
+}
+/*
+ * This function, called very early during the creation of a new
+ * process VM image, sets up which VM layout function to use:
+ */
+void arch_pick_mmap_layout(struct mm_struct *mm)
+{
+       /*
+        * Fall back to the standard layout if the personality
+        * bit is set, or if the expected stack growth is unlimited:
+        */
+       if (mmap_is_legacy()) {
+               mm->mmap_base = TASK_UNMAPPED_BASE;
+               mm->get_unmapped_area = s390_get_unmapped_area;
+               mm->unmap_area = arch_unmap_area;
+       } else {
+               mm->mmap_base = mmap_base();
+               mm->get_unmapped_area = s390_get_unmapped_area_topdown;
+               mm->unmap_area = arch_unmap_area_topdown;
+       }
+}
+EXPORT_SYMBOL_GPL(arch_pick_mmap_layout);
+
+#endif
index 809e778930390b38b754807a651e60f2280d75dc..fd072013f88ce11658adab8b7db4818a6479f50b 100644 (file)
@@ -23,6 +23,7 @@
 #include <asm/pgalloc.h>
 #include <asm/tlb.h>
 #include <asm/tlbflush.h>
+#include <asm/mmu_context.h>
 
 #ifndef CONFIG_64BIT
 #define ALLOC_ORDER    1
@@ -70,6 +71,79 @@ void crst_table_free(struct mm_struct *mm, unsigned long *table)
        free_pages((unsigned long) table, ALLOC_ORDER);
 }
 
+#ifdef CONFIG_64BIT
+int crst_table_upgrade(struct mm_struct *mm, unsigned long limit)
+{
+       unsigned long *table, *pgd;
+       unsigned long entry;
+
+       BUG_ON(limit > (1UL << 53));
+repeat:
+       table = crst_table_alloc(mm, mm->context.noexec);
+       if (!table)
+               return -ENOMEM;
+       spin_lock(&mm->page_table_lock);
+       if (mm->context.asce_limit < limit) {
+               pgd = (unsigned long *) mm->pgd;
+               if (mm->context.asce_limit <= (1UL << 31)) {
+                       entry = _REGION3_ENTRY_EMPTY;
+                       mm->context.asce_limit = 1UL << 42;
+                       mm->context.asce_bits = _ASCE_TABLE_LENGTH |
+                                               _ASCE_USER_BITS |
+                                               _ASCE_TYPE_REGION3;
+               } else {
+                       entry = _REGION2_ENTRY_EMPTY;
+                       mm->context.asce_limit = 1UL << 53;
+                       mm->context.asce_bits = _ASCE_TABLE_LENGTH |
+                                               _ASCE_USER_BITS |
+                                               _ASCE_TYPE_REGION2;
+               }
+               crst_table_init(table, entry);
+               pgd_populate(mm, (pgd_t *) table, (pud_t *) pgd);
+               mm->pgd = (pgd_t *) table;
+               table = NULL;
+       }
+       spin_unlock(&mm->page_table_lock);
+       if (table)
+               crst_table_free(mm, table);
+       if (mm->context.asce_limit < limit)
+               goto repeat;
+       update_mm(mm, current);
+       return 0;
+}
+
+void crst_table_downgrade(struct mm_struct *mm, unsigned long limit)
+{
+       pgd_t *pgd;
+
+       if (mm->context.asce_limit <= limit)
+               return;
+       __tlb_flush_mm(mm);
+       while (mm->context.asce_limit > limit) {
+               pgd = mm->pgd;
+               switch (pgd_val(*pgd) & _REGION_ENTRY_TYPE_MASK) {
+               case _REGION_ENTRY_TYPE_R2:
+                       mm->context.asce_limit = 1UL << 42;
+                       mm->context.asce_bits = _ASCE_TABLE_LENGTH |
+                                               _ASCE_USER_BITS |
+                                               _ASCE_TYPE_REGION3;
+                       break;
+               case _REGION_ENTRY_TYPE_R3:
+                       mm->context.asce_limit = 1UL << 31;
+                       mm->context.asce_bits = _ASCE_TABLE_LENGTH |
+                                               _ASCE_USER_BITS |
+                                               _ASCE_TYPE_SEGMENT;
+                       break;
+               default:
+                       BUG();
+               }
+               mm->pgd = (pgd_t *) (pgd_val(*pgd) & _REGION_ENTRY_ORIGIN);
+               crst_table_free(mm, (unsigned long *) pgd);
+       }
+       update_mm(mm, current);
+}
+#endif
+
 /*
  * page table entry allocation/free routines.
  */
index b760cd4de3853be32402624d9b177ff64283591d..b3ac262c4582f94738376cfcefff762cd50a26b1 100644 (file)
@@ -138,7 +138,7 @@ typedef s390_regs elf_gregset_t;
    use of this is to invoke "./ld.so someprog" to test out a new version of
    the loader.  We need to make sure that it is out of the way of the program
    that it will "exec", and that there is sufficient room for the brk.  */
-#define ELF_ET_DYN_BASE                (TASK_SIZE / 3 * 2)
+#define ELF_ET_DYN_BASE                (STACK_TOP / 3 * 2)
 
 /* Wow, the "main" arch needs arch dependent functions too.. :) */
 
index 13ec4215f437078659afb337b0c546903c7225f9..1698e29c5b20fc5b38831bfe0a4b7de80b1487ba 100644 (file)
@@ -5,6 +5,7 @@ typedef struct {
        struct list_head crst_list;
        struct list_head pgtable_list;
        unsigned long asce_bits;
+       unsigned long asce_limit;
        int noexec;
 } mm_context_t;
 
index b3ea3e1999212bfad0a7e0944572ad31a64093c0..b5a34c6f91a932c0841933c6db88da84ed5a7318 100644 (file)
@@ -18,9 +18,11 @@ static inline int init_new_context(struct task_struct *tsk,
 {
        mm->context.asce_bits = _ASCE_TABLE_LENGTH | _ASCE_USER_BITS;
 #ifdef CONFIG_64BIT
-       mm->context.asce_bits |= _ASCE_TYPE_REGION2;
+       mm->context.asce_bits |= _ASCE_TYPE_REGION3;
 #endif
        mm->context.noexec = s390_noexec;
+       mm->context.asce_limit = STACK_TOP_MAX;
+       crst_table_init((unsigned long *) mm->pgd, pgd_entry_type(mm));
        return 0;
 }
 
@@ -47,13 +49,12 @@ static inline void update_mm(struct mm_struct *mm, struct task_struct *tsk)
                /* Load home space page table origin. */
                asm volatile(LCTL_OPCODE" 13,13,%0"
                             : : "m" (S390_lowcore.user_asce) );
+       set_fs(current->thread.mm_segment);
 }
 
 static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next,
                             struct task_struct *tsk)
 {
-       if (unlikely(prev == next))
-               return;
        cpu_set(smp_processor_id(), next->cpu_vm_mask);
        update_mm(next, tsk);
 }
@@ -65,7 +66,6 @@ static inline void activate_mm(struct mm_struct *prev,
                                struct mm_struct *next)
 {
         switch_mm(prev, next, current);
-       set_fs(current->thread.mm_segment);
 }
 
 #endif /* __S390_MMU_CONTEXT_H */
index cc47dd65a49984648acb8ef6e16afd27591c7ba3..f5b2bf3d7c1d4b7a103e3a1089360be7669cb848 100644 (file)
@@ -73,9 +73,16 @@ static inline unsigned long pgd_entry_type(struct mm_struct *mm)
 
 static inline unsigned long pgd_entry_type(struct mm_struct *mm)
 {
+       if (mm->context.asce_limit <= (1UL << 31))
+               return _SEGMENT_ENTRY_EMPTY;
+       if (mm->context.asce_limit <= (1UL << 42))
+               return _REGION3_ENTRY_EMPTY;
        return _REGION2_ENTRY_EMPTY;
 }
 
+int crst_table_upgrade(struct mm_struct *, unsigned long limit);
+void crst_table_downgrade(struct mm_struct *, unsigned long limit);
+
 static inline pud_t *pud_alloc_one(struct mm_struct *mm, unsigned long address)
 {
        unsigned long *table = crst_table_alloc(mm, mm->context.noexec);
@@ -102,12 +109,12 @@ static inline void pgd_populate_kernel(struct mm_struct *mm,
 
 static inline void pgd_populate(struct mm_struct *mm, pgd_t *pgd, pud_t *pud)
 {
-       pgd_t *shadow_pgd = get_shadow_table(pgd);
-       pud_t *shadow_pud = get_shadow_table(pud);
-
-       if (shadow_pgd && shadow_pud)
-               pgd_populate_kernel(mm, shadow_pgd, shadow_pud);
        pgd_populate_kernel(mm, pgd, pud);
+       if (mm->context.noexec) {
+               pgd = get_shadow_table(pgd);
+               pud = get_shadow_table(pud);
+               pgd_populate_kernel(mm, pgd, pud);
+       }
 }
 
 static inline void pud_populate_kernel(struct mm_struct *mm,
@@ -130,14 +137,9 @@ static inline void pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd)
 
 static inline pgd_t *pgd_alloc(struct mm_struct *mm)
 {
-       unsigned long *crst;
-
        INIT_LIST_HEAD(&mm->context.crst_list);
        INIT_LIST_HEAD(&mm->context.pgtable_list);
-       crst = crst_table_alloc(mm, s390_noexec);
-       if (crst)
-               crst_table_init(crst, pgd_entry_type(mm));
-       return (pgd_t *) crst;
+       return (pgd_t *) crst_table_alloc(mm, s390_noexec);
 }
 #define pgd_free(mm, pgd) crst_table_free(mm, (unsigned long *) pgd)
 
index 8f473a7181116198b3df2f7e702d3f9686b57dd8..65154dc9a9e538aea84f867c8aab6ac19cdeba3e 100644 (file)
@@ -421,36 +421,54 @@ static inline int pud_bad(pud_t pud)       { return 0; }
 
 static inline int pgd_present(pgd_t pgd)
 {
+       if ((pgd_val(pgd) & _REGION_ENTRY_TYPE_MASK) < _REGION_ENTRY_TYPE_R2)
+               return 1;
        return (pgd_val(pgd) & _REGION_ENTRY_ORIGIN) != 0UL;
 }
 
 static inline int pgd_none(pgd_t pgd)
 {
+       if ((pgd_val(pgd) & _REGION_ENTRY_TYPE_MASK) < _REGION_ENTRY_TYPE_R2)
+               return 0;
        return (pgd_val(pgd) & _REGION_ENTRY_INV) != 0UL;
 }
 
 static inline int pgd_bad(pgd_t pgd)
 {
+       /*
+        * With dynamic page table levels the pgd can be a region table
+        * entry or a segment table entry. Check for the bit that are
+        * invalid for either table entry.
+        */
        unsigned long mask =
-               ~_REGION_ENTRY_ORIGIN & ~_REGION_ENTRY_INV &
+               ~_SEGMENT_ENTRY_ORIGIN & ~_REGION_ENTRY_INV &
                ~_REGION_ENTRY_TYPE_MASK & ~_REGION_ENTRY_LENGTH;
        return (pgd_val(pgd) & mask) != 0;
 }
 
 static inline int pud_present(pud_t pud)
 {
+       if ((pud_val(pud) & _REGION_ENTRY_TYPE_MASK) < _REGION_ENTRY_TYPE_R3)
+               return 1;
        return (pud_val(pud) & _REGION_ENTRY_ORIGIN) != 0UL;
 }
 
 static inline int pud_none(pud_t pud)
 {
+       if ((pud_val(pud) & _REGION_ENTRY_TYPE_MASK) < _REGION_ENTRY_TYPE_R3)
+               return 0;
        return (pud_val(pud) & _REGION_ENTRY_INV) != 0UL;
 }
 
 static inline int pud_bad(pud_t pud)
 {
+       /*
+        * With dynamic page table levels the pud can be a region table
+        * entry or a segment table entry. Check for the bit that are
+        * invalid for either table entry.
+        */
        unsigned long mask =
-               ~_REGION_ENTRY_ORIGIN & ~_REGION_ENTRY_INV &
+               ~_SEGMENT_ENTRY_ORIGIN & ~_REGION_ENTRY_INV &
                ~_REGION_ENTRY_TYPE_MASK & ~_REGION_ENTRY_LENGTH;
        return (pud_val(pud) & mask) != 0;
 }
@@ -535,7 +553,8 @@ static inline int pte_young(pte_t pte)
 
 static inline void pgd_clear_kernel(pgd_t * pgd)
 {
-       pgd_val(*pgd) = _REGION2_ENTRY_EMPTY;
+       if ((pgd_val(*pgd) & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R2)
+               pgd_val(*pgd) = _REGION2_ENTRY_EMPTY;
 }
 
 static inline void pgd_clear(pgd_t * pgd)
@@ -549,10 +568,11 @@ static inline void pgd_clear(pgd_t * pgd)
 
 static inline void pud_clear_kernel(pud_t *pud)
 {
-       pud_val(*pud) = _REGION3_ENTRY_EMPTY;
+       if ((pud_val(*pud) & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R3)
+               pud_val(*pud) = _REGION3_ENTRY_EMPTY;
 }
 
-static inline void pud_clear(pud_t * pud)
+static inline void pud_clear(pud_t *pud)
 {
        pud_t *shadow = get_shadow_table(pud);
 
@@ -841,13 +861,17 @@ static inline pte_t mk_pte(struct page *page, pgprot_t pgprot)
 
 static inline pud_t *pud_offset(pgd_t *pgd, unsigned long address)
 {
-       pud_t *pud = (pud_t *) pgd_deref(*pgd);
+       pud_t *pud = (pud_t *) pgd;
+       if ((pgd_val(*pgd) & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R2)
+               pud = (pud_t *) pgd_deref(*pgd);
        return pud  + pud_index(address);
 }
 
 static inline pmd_t *pmd_offset(pud_t *pud, unsigned long address)
 {
-       pmd_t *pmd = (pmd_t *) pud_deref(*pud);
+       pmd_t *pmd = (pmd_t *) pud;
+       if ((pud_val(*pud) & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R3)
+               pmd = (pmd_t *) pud_deref(*pud);
        return pmd + pmd_index(address);
 }
 
index 5a21f457d58380ffae6eb195f3b320721d5d49e4..51d88912aa200857bd6b57fee339f41a3a8421d2 100644 (file)
@@ -81,11 +81,12 @@ extern int get_cpu_capability(unsigned int *);
 
 #ifndef __s390x__
 #define STACK_TOP              (1UL << 31)
+#define STACK_TOP_MAX          (1UL << 31)
 #else /* __s390x__ */
-#define STACK_TOP              (1UL << (test_thread_flag(TIF_31BIT) ? 31:53))
+#define STACK_TOP              (1UL << (test_thread_flag(TIF_31BIT) ? 31:42))
+#define STACK_TOP_MAX          (1UL << 42)
 #endif /* __s390x__ */
 
-#define STACK_TOP_MAX          STACK_TOP
 
 #endif
 
@@ -142,8 +143,6 @@ struct stack_frame {
 /*
  * Do necessary setup to start up a new thread.
  */
-#ifndef __s390x__
-
 #define start_thread(regs, new_psw, new_stackp) do {            \
        set_fs(USER_DS);                                        \
        regs->psw.mask  = psw_user_bits;                        \
@@ -151,24 +150,6 @@ struct stack_frame {
         regs->gprs[15]  = new_stackp ;                          \
 } while (0)
 
-#else /* __s390x__ */
-
-#define start_thread(regs, new_psw, new_stackp) do {            \
-       set_fs(USER_DS);                                        \
-       regs->psw.mask  = psw_user_bits;                        \
-        regs->psw.addr  = new_psw;                              \
-        regs->gprs[15]  = new_stackp;                           \
-} while (0)
-
-#define start_thread31(regs, new_psw, new_stackp) do {          \
-       set_fs(USER_DS);                                        \
-       regs->psw.mask  = psw_user32_bits;                      \
-        regs->psw.addr  = new_psw;                              \
-        regs->gprs[15]  = new_stackp;                           \
-} while (0)
-
-#endif /* __s390x__ */
-
 /* Forward declaration, a strange C thing */
 struct task_struct;
 struct mm_struct;
index 9b2ddb7aac49435eff47a32f384468af35ae180c..3d8a96d39d9d6a9a999c0f493911af00e204b605 100644 (file)
@@ -109,10 +109,15 @@ static inline void pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte)
 /*
  * pmd_free_tlb frees a pmd table and clears the CRSTE for the
  * segment table entry from the tlb.
+ * If the mm uses a two level page table the single pmd is freed
+ * as the pgd. pmd_free_tlb checks the asce_limit against 2GB
+ * to avoid the double free of the pmd in this case.
  */
 static inline void pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd)
 {
 #ifdef __s390x__
+       if (tlb->mm->context.asce_limit <= (1UL << 31))
+               return;
        if (!tlb->fullmm) {
                tlb->array[--tlb->nr_pxds] = pmd;
                if (tlb->nr_ptes >= tlb->nr_pxds)
@@ -125,10 +130,15 @@ static inline void pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd)
 /*
  * pud_free_tlb frees a pud table and clears the CRSTE for the
  * region third table entry from the tlb.
+ * If the mm uses a three level page table the single pud is freed
+ * as the pgd. pud_free_tlb checks the asce_limit against 4TB
+ * to avoid the double free of the pud in this case.
  */
 static inline void pud_free_tlb(struct mmu_gather *tlb, pud_t *pud)
 {
 #ifdef __s390x__
+       if (tlb->mm->context.asce_limit <= (1UL << 42))
+               return;
        if (!tlb->fullmm) {
                tlb->array[--tlb->nr_pxds] = pud;
                if (tlb->nr_ptes >= tlb->nr_pxds)