summaryrefslogtreecommitdiffstats
path: root/drivers/char/mem.c
diff options
context:
space:
mode:
authorHugh Dickins <hughd@google.com>2016-07-26 18:26:15 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2016-07-26 19:19:19 -0400
commitc01d5b300774d130a24d787825b01eb24e6e20cb (patch)
treee56b92aa5cf8f25ab812da30db5b36a6126e877b /drivers/char/mem.c
parent5a6e75f8110c97e2a5488894d4e922187e6cb343 (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 'drivers/char/mem.c')
-rw-r--r--drivers/char/mem.c24
1 files changed, 24 insertions, 0 deletions
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index d633974e7f8b..a33163dbb913 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -22,6 +22,7 @@
22#include <linux/device.h> 22#include <linux/device.h>
23#include <linux/highmem.h> 23#include <linux/highmem.h>
24#include <linux/backing-dev.h> 24#include <linux/backing-dev.h>
25#include <linux/shmem_fs.h>
25#include <linux/splice.h> 26#include <linux/splice.h>
26#include <linux/pfn.h> 27#include <linux/pfn.h>
27#include <linux/export.h> 28#include <linux/export.h>
@@ -657,6 +658,28 @@ static int mmap_zero(struct file *file, struct vm_area_struct *vma)
657 return 0; 658 return 0;
658} 659}
659 660
661static unsigned long get_unmapped_area_zero(struct file *file,
662 unsigned long addr, unsigned long len,
663 unsigned long pgoff, unsigned long flags)
664{
665#ifdef CONFIG_MMU
666 if (flags & MAP_SHARED) {
667 /*
668 * mmap_zero() will call shmem_zero_setup() to create a file,
669 * so use shmem's get_unmapped_area in case it can be huge;
670 * and pass NULL for file as in mmap.c's get_unmapped_area(),
671 * so as not to confuse shmem with our handle on "/dev/zero".
672 */
673 return shmem_get_unmapped_area(NULL, addr, len, pgoff, flags);
674 }
675
676 /* Otherwise flags & MAP_PRIVATE: with no shmem object beneath it */
677 return current->mm->get_unmapped_area(file, addr, len, pgoff, flags);
678#else
679 return -ENOSYS;
680#endif
681}
682
660static ssize_t write_full(struct file *file, const char __user *buf, 683static ssize_t write_full(struct file *file, const char __user *buf,
661 size_t count, loff_t *ppos) 684 size_t count, loff_t *ppos)
662{ 685{
@@ -764,6 +787,7 @@ static const struct file_operations zero_fops = {
764 .read_iter = read_iter_zero, 787 .read_iter = read_iter_zero,
765 .write_iter = write_iter_zero, 788 .write_iter = write_iter_zero,
766 .mmap = mmap_zero, 789 .mmap = mmap_zero,
790 .get_unmapped_area = get_unmapped_area_zero,
767#ifndef CONFIG_MMU 791#ifndef CONFIG_MMU
768 .mmap_capabilities = zero_mmap_capabilities, 792 .mmap_capabilities = zero_mmap_capabilities,
769#endif 793#endif