| From dd7c08083e6cf0c7ed8fd21153f52861ac465250 Mon Sep 17 00:00:00 2001 |
| From: "Kirill A. Shutemov" <kirill@shutemov.name> |
| Date: Mon, 13 Jan 2020 16:29:13 -0800 |
| Subject: [PATCH] mm/shmem.c: thp, shmem: fix conflict of above-47bit hint |
| address and PMD alignment |
| |
| commit 991589974d9c9ecb24ee3799ec8c415c730598a2 upstream. |
| |
| Shmem/tmpfs tries to provide THP-friendly mappings if huge pages are |
| enabled. But it doesn't work well with above-47bit hint address. |
| |
| Normally, the kernel doesn't create userspace mappings above 47-bit, |
| even if the machine allows this (such as with 5-level paging on x86-64). |
| Not all user space is ready to handle wide addresses. It's known that |
| at least some JIT compilers use higher bits in pointers to encode their |
| information. |
| |
| Userspace can ask for allocation from full address space by specifying |
| hint address (with or without MAP_FIXED) above 47-bits. If the |
| application doesn't need a particular address, but wants to allocate |
| from whole address space it can specify -1 as a hint address. |
| |
| Unfortunately, this trick breaks THP alignment in shmem/tmp: |
| shmem_get_unmapped_area() would not try to allocate PMD-aligned area if |
| *any* hint address specified. |
| |
| This can be fixed by requesting the aligned area if the we failed to |
| allocated at user-specified hint address. The request with inflated |
| length will also take the user-specified hint address. This way we will |
| not lose an allocation request from the full address space. |
| |
| [kirill@shutemov.name: fold in a fixup] |
| Link: http://lkml.kernel.org/r/20191223231309.t6bh5hkbmokihpfu@box |
| Link: http://lkml.kernel.org/r/20191220142548.7118-3-kirill.shutemov@linux.intel.com |
| Fixes: b569bab78d8d ("x86/mm: Prepare to expose larger address space to userspace") |
| Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com> |
| Cc: "Willhalm, Thomas" <thomas.willhalm@intel.com> |
| Cc: Dan Williams <dan.j.williams@intel.com> |
| Cc: "Bruggeman, Otto G" <otto.g.bruggeman@intel.com> |
| Cc: "Aneesh Kumar K . V" <aneesh.kumar@linux.vnet.ibm.com> |
| Cc: <stable@vger.kernel.org> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/mm/shmem.c b/mm/shmem.c |
| index 9fedf2805291..826bc0f6b879 100644 |
| --- a/mm/shmem.c |
| +++ b/mm/shmem.c |
| @@ -2089,9 +2089,10 @@ unsigned long shmem_get_unmapped_area(struct file *file, |
| /* |
| * Our priority is to support MAP_SHARED mapped hugely; |
| * and support MAP_PRIVATE mapped hugely too, until it is COWed. |
| - * But if caller specified an address hint, respect that as before. |
| + * But if caller specified an address hint and we allocated area there |
| + * successfully, respect that as before. |
| */ |
| - if (uaddr) |
| + if (uaddr == addr) |
| return addr; |
| |
| if (shmem_huge != SHMEM_HUGE_FORCE) { |
| @@ -2125,7 +2126,7 @@ unsigned long shmem_get_unmapped_area(struct file *file, |
| if (inflated_len < len) |
| return addr; |
| |
| - inflated_addr = get_area(NULL, 0, inflated_len, 0, flags); |
| + inflated_addr = get_area(NULL, uaddr, inflated_len, 0, flags); |
| if (IS_ERR_VALUE(inflated_addr)) |
| return addr; |
| if (inflated_addr & ~PAGE_MASK) |
| -- |
| 2.7.4 |
| |