diff options
author | Hugh Dickins <hughd@google.com> | 2016-07-26 18:26:15 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-07-26 19:19:19 -0400 |
commit | c01d5b300774d130a24d787825b01eb24e6e20cb (patch) | |
tree | e56b92aa5cf8f25ab812da30db5b36a6126e877b /mm/mmap.c | |
parent | 5a6e75f8110c97e2a5488894d4e922187e6cb343 (diff) |
shmem: get_unmapped_area align huge page
Provide a shmem_get_unmapped_area method in file_operations, called at
mmap time to decide the mapping address. It could be conditional on
CONFIG_TRANSPARENT_HUGEPAGE, but save #ifdefs in other places by making
it unconditional.
shmem_get_unmapped_area() first calls the usual mm->get_unmapped_area
(which we treat as a black box, highly dependent on architecture and
config and executable layout). Lots of conditions, and in most cases it
just goes with the address that chose; but when our huge stars are
rightly aligned, yet that did not provide a suitable address, go back to
ask for a larger arena, within which to align the mapping suitably.
There have to be some direct calls to shmem_get_unmapped_area(), not via
the file_operations: because of the way shmem_zero_setup() is called to
create a shmem object late in the mmap sequence, when MAP_SHARED is
requested with MAP_ANONYMOUS or /dev/zero. Though this only matters
when /proc/sys/vm/shmem_huge has been set.
Link: http://lkml.kernel.org/r/1466021202-61880-29-git-send-email-kirill.shutemov@linux.intel.com
Signed-off-by: Hugh Dickins <hughd@google.com>
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/mmap.c')
-rw-r--r-- | mm/mmap.c | 16 |
1 files changed, 14 insertions, 2 deletions
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/personality.h> | 25 | #include <linux/personality.h> |
26 | #include <linux/security.h> | 26 | #include <linux/security.h> |
27 | #include <linux/hugetlb.h> | 27 | #include <linux/hugetlb.h> |
28 | #include <linux/shmem_fs.h> | ||
28 | #include <linux/profile.h> | 29 | #include <linux/profile.h> |
29 | #include <linux/export.h> | 30 | #include <linux/export.h> |
30 | #include <linux/mount.h> | 31 | #include <linux/mount.h> |
@@ -1897,8 +1898,19 @@ get_unmapped_area(struct file *file, unsigned long addr, unsigned long len, | |||
1897 | return -ENOMEM; | 1898 | return -ENOMEM; |
1898 | 1899 | ||
1899 | get_area = current->mm->get_unmapped_area; | 1900 | get_area = current->mm->get_unmapped_area; |
1900 | if (file && file->f_op->get_unmapped_area) | 1901 | if (file) { |
1901 | get_area = file->f_op->get_unmapped_area; | 1902 | if (file->f_op->get_unmapped_area) |
1903 | get_area = file->f_op->get_unmapped_area; | ||
1904 | } else if (flags & MAP_SHARED) { | ||
1905 | /* | ||
1906 | * mmap_region() will call shmem_zero_setup() to create a file, | ||
1907 | * so use shmem's get_unmapped_area in case it can be huge. | ||
1908 | * do_mmap_pgoff() will clear pgoff, so match alignment. | ||
1909 | */ | ||
1910 | pgoff = 0; | ||
1911 | get_area = shmem_get_unmapped_area; | ||
1912 | } | ||
1913 | |||
1902 | addr = get_area(file, addr, len, pgoff, flags); | 1914 | addr = get_area(file, addr, len, pgoff, flags); |
1903 | if (IS_ERR_VALUE(addr)) | 1915 | if (IS_ERR_VALUE(addr)) |
1904 | return addr; | 1916 | return addr; |