[RFC 2/3] mm: allow munmap related functions to understand gfp_mask

From: Michal Hocko
Date: Tue Apr 28 2015 - 08:12:34 EST


__split_vma path requires to allocate a memory. Later patch in the
series will require to change standard GFP_KERNEL allocation to use
__GFP_NOFAIL. In order to do that all the allocation paths down this
path should understand gfp requirements of the caller.

This involves vma_dup_policy and vma_adjust which have _gfp variant now
and anon_vma_clone got just a new parameter because it doesn't have
many callers.

The patch doesn't have any runtime effects but it makes the code
slightly larger:
text data bss dec hex filename
511480 74147 44440 630067 99d33 mm/built-in.o.before
511560 74147 44440 630147 99d83 mm/built-in.o.after

Signed-off-by: Michal Hocko <mhocko@xxxxxxx>
---
include/linux/mempolicy.h | 17 ++++++++++++++---
include/linux/mm.h | 10 ++++++++--
include/linux/rmap.h | 2 +-
mm/mempolicy.c | 9 +++++----
mm/mmap.c | 43 +++++++++++++++++++++++++------------------
mm/nommu.c | 24 ++++++++++++++++++------
mm/rmap.c | 7 ++++---
7 files changed, 75 insertions(+), 37 deletions(-)

diff --git a/include/linux/mempolicy.h b/include/linux/mempolicy.h
index 3d385c81c153..a42585c09d4e 100644
--- a/include/linux/mempolicy.h
+++ b/include/linux/mempolicy.h
@@ -82,11 +82,12 @@ static inline void mpol_cond_put(struct mempolicy *pol)
__mpol_put(pol);
}

-extern struct mempolicy *__mpol_dup(struct mempolicy *pol);
+extern struct mempolicy *mpol_dup_gfp(struct mempolicy *pol,
+ gfp_t gfp_mask);
static inline struct mempolicy *mpol_dup(struct mempolicy *pol)
{
if (pol)
- pol = __mpol_dup(pol);
+ pol = mpol_dup_gfp(pol, GFP_KERNEL);
return pol;
}

@@ -125,7 +126,12 @@ struct shared_policy {
spinlock_t lock;
};

-int vma_dup_policy(struct vm_area_struct *src, struct vm_area_struct *dst);
+int vma_dup_policy_gfp(struct vm_area_struct *src,
+ struct vm_area_struct *dst, gfp_t gfp_mask);
+static inline int
+vma_dup_policy(struct vm_area_struct *src, struct vm_area_struct *dst) {
+ return vma_dup_policy_gfp(src, dst, GFP_KERNEL);
+}
void mpol_shared_policy_init(struct shared_policy *sp, struct mempolicy *mpol);
int mpol_set_shared_policy(struct shared_policy *info,
struct vm_area_struct *vma,
@@ -235,6 +241,11 @@ vma_dup_policy(struct vm_area_struct *src, struct vm_area_struct *dst)
{
return 0;
}
+static inline int
+vma_dup_policy_gfp(struct vm_area_struct *src,
+ struct vm_area_struct *dst, gfp_t gfp_mask) {
+ return 0;
+}

static inline void numa_policy_init(void)
{
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 5d20fba62081..723032a2273f 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1752,8 +1752,14 @@ void anon_vma_interval_tree_verify(struct anon_vma_chain *node);

/* mmap.c */
extern int __vm_enough_memory(struct mm_struct *mm, long pages, int cap_sys_admin);
-extern int vma_adjust(struct vm_area_struct *vma, unsigned long start,
- unsigned long end, pgoff_t pgoff, struct vm_area_struct *insert);
+extern int vma_adjust_gfp(struct vm_area_struct *vma, unsigned long start,
+ unsigned long end, pgoff_t pgoff, struct vm_area_struct *insert,
+ gfp_t gfp_mask);
+static inline int
+vma_adjust(struct vm_area_struct *vma, unsigned long start,
+ unsigned long end, pgoff_t pgoff, struct vm_area_struct *insert) {
+ return vma_adjust_gfp(vma, start, end, pgoff, insert, GFP_KERNEL);
+}
extern struct vm_area_struct *vma_merge(struct mm_struct *,
struct vm_area_struct *prev, unsigned long addr, unsigned long end,
unsigned long vm_flags, struct anon_vma *, struct file *, pgoff_t,
diff --git a/include/linux/rmap.h b/include/linux/rmap.h
index bf36b6e644c4..23d210b84431 100644
--- a/include/linux/rmap.h
+++ b/include/linux/rmap.h
@@ -147,7 +147,7 @@ static inline void anon_vma_unlock_read(struct anon_vma *anon_vma)
void anon_vma_init(void); /* create anon_vma_cachep */
int anon_vma_prepare(struct vm_area_struct *);
void unlink_anon_vmas(struct vm_area_struct *);
-int anon_vma_clone(struct vm_area_struct *, struct vm_area_struct *);
+int anon_vma_clone(struct vm_area_struct *, struct vm_area_struct *, gfp_t);
int anon_vma_fork(struct vm_area_struct *, struct vm_area_struct *);

static inline void anon_vma_merge(struct vm_area_struct *vma,
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index ede26291d4aa..9002d0a15d74 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -2060,9 +2060,10 @@ retry_cpuset:
}
EXPORT_SYMBOL(alloc_pages_current);

-int vma_dup_policy(struct vm_area_struct *src, struct vm_area_struct *dst)
+int vma_dup_policy_gfp(struct vm_area_struct *src, struct vm_area_struct *dst,
+ gfp_t gfp_mask)
{
- struct mempolicy *pol = mpol_dup(vma_policy(src));
+ struct mempolicy *pol = mpol_dup_gfp(vma_policy(src), gfp_mask);

if (IS_ERR(pol))
return PTR_ERR(pol);
@@ -2082,9 +2083,9 @@ int vma_dup_policy(struct vm_area_struct *src, struct vm_area_struct *dst)
*/

/* Slow path of a mempolicy duplicate */
-struct mempolicy *__mpol_dup(struct mempolicy *old)
+struct mempolicy *mpol_dup_gfp(struct mempolicy *old, gfp_t gfp_mask)
{
- struct mempolicy *new = kmem_cache_alloc(policy_cache, GFP_KERNEL);
+ struct mempolicy *new = kmem_cache_alloc(policy_cache, gfp_mask);

if (!new)
return ERR_PTR(-ENOMEM);
diff --git a/mm/mmap.c b/mm/mmap.c
index bb50cacc3ea5..4882008dac83 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -719,8 +719,9 @@ __vma_unlink(struct mm_struct *mm, struct vm_area_struct *vma,
* are necessary. The "insert" vma (if any) is to be inserted
* before we drop the necessary locks.
*/
-int vma_adjust(struct vm_area_struct *vma, unsigned long start,
- unsigned long end, pgoff_t pgoff, struct vm_area_struct *insert)
+int vma_adjust_gfp(struct vm_area_struct *vma, unsigned long start,
+ unsigned long end, pgoff_t pgoff, struct vm_area_struct *insert,
+ gfp_t gfp_mask)
{
struct mm_struct *mm = vma->vm_mm;
struct vm_area_struct *next = vma->vm_next;
@@ -773,7 +774,7 @@ again: remove_next = 1 + (end > next->vm_end);
int error;

importer->anon_vma = exporter->anon_vma;
- error = anon_vma_clone(importer, exporter);
+ error = anon_vma_clone(importer, exporter, gfp_mask);
if (error)
return error;
}
@@ -2435,11 +2436,11 @@ detach_vmas_to_be_unmapped(struct mm_struct *mm, struct vm_area_struct *vma,
}

/*
- * __split_vma() bypasses sysctl_max_map_count checking. We use this on the
+ * __split_vma_gfp() bypasses sysctl_max_map_count checking. We use this on the
* munmap path where it doesn't make sense to fail.
*/
-static int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma,
- unsigned long addr, int new_below)
+static int __split_vma_gfp(struct mm_struct *mm, struct vm_area_struct *vma,
+ unsigned long addr, int new_below, gfp_t gfp_mask)
{
struct vm_area_struct *new;
int err = -ENOMEM;
@@ -2448,7 +2449,7 @@ static int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma,
~(huge_page_mask(hstate_vma(vma)))))
return -EINVAL;

- new = kmem_cache_alloc(vm_area_cachep, GFP_KERNEL);
+ new = kmem_cache_alloc(vm_area_cachep, gfp_mask);
if (!new)
goto out_err;

@@ -2464,11 +2465,11 @@ static int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma,
new->vm_pgoff += ((addr - vma->vm_start) >> PAGE_SHIFT);
}

- err = vma_dup_policy(vma, new);
+ err = vma_dup_policy_gfp(vma, new, gfp_mask);
if (err)
goto out_free_vma;

- err = anon_vma_clone(new, vma);
+ err = anon_vma_clone(new, vma, gfp_mask);
if (err)
goto out_free_mpol;

@@ -2479,10 +2480,10 @@ static int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma,
new->vm_ops->open(new);

if (new_below)
- err = vma_adjust(vma, addr, vma->vm_end, vma->vm_pgoff +
- ((addr - new->vm_start) >> PAGE_SHIFT), new);
+ err = vma_adjust_gfp(vma, addr, vma->vm_end, vma->vm_pgoff +
+ ((addr - new->vm_start) >> PAGE_SHIFT), new, gfp_mask);
else
- err = vma_adjust(vma, vma->vm_start, addr, vma->vm_pgoff, new);
+ err = vma_adjust_gfp(vma, vma->vm_start, addr, vma->vm_pgoff, new, gfp_mask);

/* Success. */
if (!err)
@@ -2512,7 +2513,7 @@ int split_vma(struct mm_struct *mm, struct vm_area_struct *vma,
if (mm->map_count >= sysctl_max_map_count)
return -ENOMEM;

- return __split_vma(mm, vma, addr, new_below);
+ return __split_vma_gfp(mm, vma, addr, new_below, GFP_KERNEL);
}

/* Munmap is split into 2 main parts -- this part which finds
@@ -2520,7 +2521,8 @@ int split_vma(struct mm_struct *mm, struct vm_area_struct *vma,
* work. This now handles partial unmappings.
* Jeremy Fitzhardinge <jeremy@xxxxxxxx>
*/
-int do_munmap(struct mm_struct *mm, unsigned long start, size_t len)
+static int __do_munmap_gfp(struct mm_struct *mm, unsigned long start, size_t len,
+ gfp_t gfp_mask)
{
unsigned long end;
struct vm_area_struct *vma, *prev, *last;
@@ -2562,7 +2564,7 @@ int do_munmap(struct mm_struct *mm, unsigned long start, size_t len)
if (end < vma->vm_end && mm->map_count >= sysctl_max_map_count)
return -ENOMEM;

- error = __split_vma(mm, vma, start, 0);
+ error = __split_vma_gfp(mm, vma, start, 0, gfp_mask);
if (error)
return error;
prev = vma;
@@ -2571,7 +2573,7 @@ int do_munmap(struct mm_struct *mm, unsigned long start, size_t len)
/* Does it split the last one? */
last = find_vma(mm, end);
if (last && end > last->vm_start) {
- int error = __split_vma(mm, last, end, 1);
+ int error = __split_vma_gfp(mm, last, end, 1, gfp_mask);
if (error)
return error;
}
@@ -2605,6 +2607,11 @@ int do_munmap(struct mm_struct *mm, unsigned long start, size_t len)
return 0;
}

+int do_munmap(struct mm_struct *mm, unsigned long start, size_t len)
+{
+ return __do_munmap_gfp(mm, start, len, GFP_KERNEL);
+}
+
int vm_munmap(unsigned long start, size_t len)
{
int ret;
@@ -2943,10 +2950,10 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap,
new_vma->vm_start = addr;
new_vma->vm_end = addr + len;
new_vma->vm_pgoff = pgoff;
- if (vma_dup_policy(vma, new_vma))
+ if (vma_dup_policy_gfp(vma, new_vma, GFP_KERNEL))
goto out_free_vma;
INIT_LIST_HEAD(&new_vma->anon_vma_chain);
- if (anon_vma_clone(new_vma, vma))
+ if (anon_vma_clone(new_vma, vma, GFP_KERNEL))
goto out_free_mempol;
if (new_vma->vm_file)
get_file(new_vma->vm_file);
diff --git a/mm/nommu.c b/mm/nommu.c
index 3fba2dc97c44..f1e7b41a2031 100644
--- a/mm/nommu.c
+++ b/mm/nommu.c
@@ -1556,8 +1556,8 @@ SYSCALL_DEFINE1(old_mmap, struct mmap_arg_struct __user *, arg)
* split a vma into two pieces at address 'addr', a new vma is allocated either
* for the first part or the tail.
*/
-int split_vma(struct mm_struct *mm, struct vm_area_struct *vma,
- unsigned long addr, int new_below)
+static int __split_vma_gfp(struct mm_struct *mm, struct vm_area_struct *vma,
+ unsigned long addr, int new_below, gfp_t gfp_mask)
{
struct vm_area_struct *new;
struct vm_region *region;
@@ -1573,11 +1573,11 @@ int split_vma(struct mm_struct *mm, struct vm_area_struct *vma,
if (mm->map_count >= sysctl_max_map_count)
return -ENOMEM;

- region = kmem_cache_alloc(vm_region_jar, GFP_KERNEL);
+ region = kmem_cache_alloc(vm_region_jar, gfp_mask);
if (!region)
return -ENOMEM;

- new = kmem_cache_alloc(vm_area_cachep, GFP_KERNEL);
+ new = kmem_cache_alloc(vm_area_cachep, gfp_mask);
if (!new) {
kmem_cache_free(vm_region_jar, region);
return -ENOMEM;
@@ -1618,6 +1618,12 @@ int split_vma(struct mm_struct *mm, struct vm_area_struct *vma,
return 0;
}

+int split_vma(struct mm_struct *mm, struct vm_area_struct *vma,
+ unsigned long addr, int new_below)
+{
+ return __split_vma_gfp(mm, vma, addr, new_below, GFP_KERNEL);
+}
+
/*
* shrink a VMA by removing the specified chunk from either the beginning or
* the end
@@ -1663,7 +1669,8 @@ static int shrink_vma(struct mm_struct *mm,
* - under NOMMU conditions the chunk to be unmapped must be backed by a single
* VMA, though it need not cover the whole VMA
*/
-int do_munmap(struct mm_struct *mm, unsigned long start, size_t len)
+static int __do_munmap_gfp(struct mm_struct *mm, unsigned long start, size_t len
+ gfp_t gfp_mask)
{
struct vm_area_struct *vma;
unsigned long end;
@@ -1722,7 +1729,7 @@ int do_munmap(struct mm_struct *mm, unsigned long start, size_t len)
return -EINVAL;
}
if (start != vma->vm_start && end != vma->vm_end) {
- ret = split_vma(mm, vma, start, 1);
+ ret = __split_vma_gfp(mm, vma, start, 1, gfp_mask);
if (ret < 0) {
kleave(" = %d [split]", ret);
return ret;
@@ -1737,6 +1744,11 @@ erase_whole_vma:
kleave(" = 0");
return 0;
}
+
+int do_munmap(struct mm_struct *mm, unsigned long start, size_t len)
+{
+ return __do_munmap_gfp(mm, start, len, GFP_KERNEL);
+}
EXPORT_SYMBOL(do_munmap);

int vm_munmap(unsigned long addr, size_t len)
diff --git a/mm/rmap.c b/mm/rmap.c
index dad23a43e42c..e10101940031 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -249,7 +249,8 @@ static inline void unlock_anon_vma_root(struct anon_vma *root)
* good chance of avoiding scanning the whole hierarchy when it searches where
* page is mapped.
*/
-int anon_vma_clone(struct vm_area_struct *dst, struct vm_area_struct *src)
+int anon_vma_clone(struct vm_area_struct *dst, struct vm_area_struct *src,
+ gfp_t gfp_mask)
{
struct anon_vma_chain *avc, *pavc;
struct anon_vma *root = NULL;
@@ -261,7 +262,7 @@ int anon_vma_clone(struct vm_area_struct *dst, struct vm_area_struct *src)
if (unlikely(!avc)) {
unlock_anon_vma_root(root);
root = NULL;
- avc = anon_vma_chain_alloc(GFP_KERNEL);
+ avc = anon_vma_chain_alloc(gfp_mask);
if (!avc)
goto enomem_failure;
}
@@ -320,7 +321,7 @@ int anon_vma_fork(struct vm_area_struct *vma, struct vm_area_struct *pvma)
* First, attach the new VMA to the parent VMA's anon_vmas,
* so rmap can find non-COWed pages in child processes.
*/
- error = anon_vma_clone(vma, pvma);
+ error = anon_vma_clone(vma, pvma, GFP_KERNEL);
if (error)
return error;

--
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/