s390/mm: downgrade page table after fork of a 31 bit process
Martin Schwidefsky [Thu, 26 Jul 2012 06:53:06 +0000 (08:53 +0200)]
commit 0f6f281b731d20bfe75c13f85d33f3f05b440222 upstream.

The downgrade of the 4 level page table created by init_new_context is
currently done only in start_thread31. If a 31 bit process forks the
new mm uses a 4 level page table, including the task size of 2<<42
that goes along with it. This is incorrect as now a 31 bit process
can map memory beyond 2GB. Define arch_dup_mmap to do the downgrade
after fork.

Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

arch/s390/include/asm/mmu_context.h
arch/s390/include/asm/processor.h
arch/s390/mm/mmap.c
arch/s390/mm/pgtable.c

index 5d09e40..5d211f7 100644 (file)
@@ -13,7 +13,6 @@
 #include <asm/uaccess.h>
 #include <asm/tlbflush.h>
 #include <asm/ctl_reg.h>
-#include <asm-generic/mm_hooks.h>
 
 static inline int init_new_context(struct task_struct *tsk,
                                   struct mm_struct *mm)
@@ -93,4 +92,17 @@ static inline void activate_mm(struct mm_struct *prev,
         switch_mm(prev, next, current);
 }
 
+static inline void arch_dup_mmap(struct mm_struct *oldmm,
+                                struct mm_struct *mm)
+{
+#ifdef CONFIG_64BIT
+       if (oldmm->context.asce_limit < mm->context.asce_limit)
+               crst_table_downgrade(mm, oldmm->context.asce_limit);
+#endif
+}
+
+static inline void arch_exit_mmap(struct mm_struct *mm)
+{
+}
+
 #endif /* __S390_MMU_CONTEXT_H */
index d499b30..8b6f62e 100644 (file)
@@ -129,7 +129,9 @@ struct stack_frame {
        regs->psw.mask  = psw_user_bits | PSW_MASK_BA;                  \
        regs->psw.addr  = new_psw | PSW_ADDR_AMODE;                     \
        regs->gprs[15]  = new_stackp;                                   \
+       __tlb_flush_mm(current->mm);                                    \
        crst_table_downgrade(current->mm, 1UL << 31);                   \
+       update_mm(current->mm, current);                                \
 } while (0)
 
 /* Forward declaration, a strange C thing */
index 2857c48..a64fe53 100644 (file)
@@ -105,9 +105,15 @@ void arch_pick_mmap_layout(struct mm_struct *mm)
 
 int s390_mmap_check(unsigned long addr, unsigned long len)
 {
+       int rc;
+
        if (!is_compat_task() &&
-           len >= TASK_SIZE && TASK_SIZE < (1UL << 53))
-               return crst_table_upgrade(current->mm, 1UL << 53);
+           len >= TASK_SIZE && TASK_SIZE < (1UL << 53)) {
+               rc = crst_table_upgrade(current->mm, 1UL << 53);
+               if (rc)
+                       return rc;
+               update_mm(current->mm, current);
+       }
        return 0;
 }
 
@@ -127,6 +133,7 @@ s390_get_unmapped_area(struct file *filp, unsigned long addr,
                rc = crst_table_upgrade(mm, 1UL << 53);
                if (rc)
                        return (unsigned long) rc;
+               update_mm(mm, current);
                area = arch_get_unmapped_area(filp, addr, len, pgoff, flags);
        }
        return area;
@@ -149,6 +156,7 @@ s390_get_unmapped_area_topdown(struct file *filp, const unsigned long addr,
                rc = crst_table_upgrade(mm, 1UL << 53);
                if (rc)
                        return (unsigned long) rc;
+               update_mm(mm, current);
                area = arch_get_unmapped_area_topdown(filp, addr, len,
                                                      pgoff, flags);
        }
index 6e765bf..87f0efd 100644 (file)
@@ -85,7 +85,6 @@ repeat:
                crst_table_free(mm, table);
        if (mm->context.asce_limit < limit)
                goto repeat;
-       update_mm(mm, current);
        return 0;
 }
 
@@ -93,9 +92,6 @@ 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) {
@@ -118,7 +114,6 @@ void crst_table_downgrade(struct mm_struct *mm, unsigned long limit)
                mm->task_size = mm->context.asce_limit;
                crst_table_free(mm, (unsigned long *) pgd);
        }
-       update_mm(mm, current);
 }
 #endif