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

[S390] make page table upgrade work again

After TASK_SIZE now gives the current size of the address space the
upgrade of a 64 bit process from 3 to 4 levels of page table needs
to use the arch_mmap_check hook to catch large mmap lengths. The
get_unmapped_area* functions need to check for -ENOMEM from the
arch_get_unmapped_area*, upgrade the page table and retry.

Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>

+35 -14
+5
arch/s390/include/asm/mman.h
··· 22 22 #define MCL_CURRENT 1 /* lock all current mappings */ 23 23 #define MCL_FUTURE 2 /* lock all future mappings */ 24 24 25 + #if defined(__KERNEL__) && !defined(__ASSEMBLY__) && defined(CONFIG_64BIT) 26 + int s390_mmap_check(unsigned long addr, unsigned long len); 27 + #define arch_mmap_check(addr,len,flags) s390_mmap_check(addr,len) 28 + #endif 29 + 25 30 #endif /* __S390_MMAN_H__ */
+30 -14
arch/s390/mm/mmap.c
··· 89 89 90 90 #else 91 91 92 + int s390_mmap_check(unsigned long addr, unsigned long len) 93 + { 94 + if (!test_thread_flag(TIF_31BIT) && 95 + len >= TASK_SIZE && TASK_SIZE < (1UL << 53)) 96 + return crst_table_upgrade(current->mm, 1UL << 53); 97 + return 0; 98 + } 99 + 92 100 static unsigned long 93 101 s390_get_unmapped_area(struct file *filp, unsigned long addr, 94 102 unsigned long len, unsigned long pgoff, unsigned long flags) 95 103 { 96 104 struct mm_struct *mm = current->mm; 105 + unsigned long area; 97 106 int rc; 98 107 99 - addr = arch_get_unmapped_area(filp, addr, len, pgoff, flags); 100 - if (addr & ~PAGE_MASK) 101 - return addr; 102 - if (unlikely(mm->context.asce_limit < addr + len)) { 103 - rc = crst_table_upgrade(mm, addr + len); 108 + area = arch_get_unmapped_area(filp, addr, len, pgoff, flags); 109 + if (!(area & ~PAGE_MASK)) 110 + return area; 111 + if (area == -ENOMEM && 112 + !test_thread_flag(TIF_31BIT) && TASK_SIZE < (1UL << 53)) { 113 + /* Upgrade the page table to 4 levels and retry. */ 114 + rc = crst_table_upgrade(mm, 1UL << 53); 104 115 if (rc) 105 116 return (unsigned long) rc; 117 + area = arch_get_unmapped_area(filp, addr, len, pgoff, flags); 106 118 } 107 - return addr; 119 + return area; 108 120 } 109 121 110 122 static unsigned long 111 - s390_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0, 123 + s390_get_unmapped_area_topdown(struct file *filp, const unsigned long addr, 112 124 const unsigned long len, const unsigned long pgoff, 113 125 const unsigned long flags) 114 126 { 115 127 struct mm_struct *mm = current->mm; 116 - unsigned long addr = addr0; 128 + unsigned long area; 117 129 int rc; 118 130 119 - addr = arch_get_unmapped_area_topdown(filp, addr, len, pgoff, flags); 120 - if (addr & ~PAGE_MASK) 121 - return addr; 122 - if (unlikely(mm->context.asce_limit < addr + len)) { 123 - rc = crst_table_upgrade(mm, addr + len); 131 + area = arch_get_unmapped_area_topdown(filp, addr, len, pgoff, flags); 132 + if (!(area & ~PAGE_MASK)) 133 + return area; 134 + if (area == -ENOMEM && 135 + !test_thread_flag(TIF_31BIT) && TASK_SIZE < (1UL << 53)) { 136 + /* Upgrade the page table to 4 levels and retry. */ 137 + rc = crst_table_upgrade(mm, 1UL << 53); 124 138 if (rc) 125 139 return (unsigned long) rc; 140 + area = arch_get_unmapped_area_topdown(filp, addr, len, 141 + pgoff, flags); 126 142 } 127 - return addr; 143 + return area; 128 144 } 129 145 /* 130 146 * This function, called very early during the creation of a new