summaryrefslogtreecommitdiff
path: root/mm/vma.c
diff options
context:
space:
mode:
Diffstat (limited to 'mm/vma.c')
-rw-r--r--mm/vma.c43
1 files changed, 35 insertions, 8 deletions
diff --git a/mm/vma.c b/mm/vma.c
index 44c6ff8c5183..65ce1c3c9b00 100644
--- a/mm/vma.c
+++ b/mm/vma.c
@@ -2612,6 +2612,29 @@ static int __mmap_new_file_vma(struct mmap_state *map,
return 0;
}
+static bool map_is_dev_zero(const struct mmap_state *map)
+{
+ const struct file *file = map->file;
+ const struct inode *inode = file_inode(file);
+
+ if (!S_ISCHR(inode->i_mode))
+ return false;
+ return imajor(inode) == MEM_MAJOR && iminor(inode) == DEVZERO_MINOR;
+}
+
+static bool map_is_private(const struct mmap_state *map)
+{
+ return !vma_flags_test(&map->vma_flags, VMA_SHARED_BIT);
+}
+
+static bool map_is_anon(const struct mmap_state *map)
+{
+ if (!map_is_private(map))
+ return false;
+
+ return !map->file || map_is_dev_zero(map);
+}
+
/*
* __mmap_new_vma() - Allocate a new VMA for the region, as merging was not
* possible.
@@ -2625,8 +2648,7 @@ static int __mmap_new_file_vma(struct mmap_state *map,
static int __mmap_new_vma(struct mmap_state *map, struct vm_area_struct **vmap,
struct mmap_action *action)
{
- const bool is_anon = !map->file &&
- !vma_flags_test(&map->vma_flags, VMA_SHARED_BIT);
+ const bool is_anon = map_is_anon(map);
struct vma_iterator *vmi = map->vmi;
int error = 0;
struct vm_area_struct *vma;
@@ -2642,7 +2664,7 @@ static int __mmap_new_vma(struct mmap_state *map, struct vm_area_struct **vmap,
vma_iter_config(vmi, map->addr, map->end);
- if (is_anon)
+ if (is_anon && !map->file)
vma_set_anonymous(vma);
vma_set_range(vma, map->addr, map->end, map->pgoff, map->virt_pgoff);
@@ -2660,6 +2682,10 @@ static int __mmap_new_vma(struct mmap_state *map, struct vm_area_struct **vmap,
else if (!is_anon)
error = shmem_zero_setup(vma);
+ /* Temporary MAP_PRIVATE-/dev/zero workaround. */
+ if (is_anon && map->file)
+ vma_set_anonymous(vma);
+
if (error)
goto free_iter_vma;
@@ -2768,6 +2794,10 @@ static int call_mmap_prepare(struct mmap_state *map,
if (err)
return err;
+ /* Hooks cannot mark themselves anonymous. */
+ if (!desc->vm_ops)
+ return -EINVAL;
+
err = call_action_prepare(map, desc);
if (err)
return err;
@@ -2790,10 +2820,7 @@ static int call_mmap_prepare(struct mmap_state *map,
static void set_vma_user_defined_fields(struct vm_area_struct *vma,
struct mmap_state *map)
{
- if (map->vm_ops)
- vma->vm_ops = map->vm_ops;
- else /* Only /dev/zero should do this. */
- vma_set_anonymous(vma);
+ vma->vm_ops = map->vm_ops;
vma->vm_private_data = map->vm_private_data;
}
@@ -2873,7 +2900,7 @@ static unsigned long __mmap_region(struct file *file, unsigned long addr,
allocated_new = true;
}
- if (have_mmap_prepare)
+ if (have_mmap_prepare && !map_is_anon(&map))
set_vma_user_defined_fields(vma, &map);
__mmap_complete(&map, vma);