* [PATCHv2 1/2] mm: rearrange madvise code to allow for reuse
@ 2013-10-01 20:21 Colin Cross
[not found] ` <1380658901-11666-2-git-send-email-ccross@android.com>
0 siblings, 1 reply; 2+ messages in thread
From: Colin Cross @ 2013-10-01 20:21 UTC (permalink / raw
To: linux-kernel, Pekka Enberg, Dave Hansen, Peter Zijlstra,
Ingo Molnar, Oleg Nesterov, Eric W. Biederman, Jan Glauber
Cc: Colin Cross, Andrew Morton, Sasha Levin, Rasmus Villemoes,
David Rientjes, open list:MEMORY MANAGEMENT
This patch refactors the madvise syscall to allow for parts of it
to be reused by a prctl syscall that affects vmas.
Move the code that walks vmas in a virtual address range into a
function that takes a function pointer as a parameter. The only
caller for now is sys_madvise, which uses it to call
madvise_vma_behavior on each vma, but the next patch will add
an additional caller.
Move handling all vma behaviors inside madvise_behavior, and
rename it to madvise_vma_behavior.
Move the code that updates the flags on a vma, including splitting
or merging the vma as necessary, into a new function called
madvise_update_vma. The next patch will add support for updating
a new anon_name field as well.
Signed-off-by: Colin Cross <ccross@android.com>
---
mm/madvise.c | 272 +++++++++++++++++++++++++++++++++--------------------------
1 file changed, 151 insertions(+), 121 deletions(-)
diff --git a/mm/madvise.c b/mm/madvise.c
index 7055883..b8820fd 100644
--- a/mm/madvise.c
+++ b/mm/madvise.c
@@ -39,65 +39,20 @@ static int madvise_need_mmap_write(int behavior)
}
/*
- * We can potentially split a vm area into separate
- * areas, each area with its own behavior.
+ * Update the vm_flags on regiion of a vma, splitting it or merging it as
+ * necessary. Must be called with mmap_sem held for writing;
*/
-static long madvise_behavior(struct vm_area_struct * vma,
- struct vm_area_struct **prev,
- unsigned long start, unsigned long end, int behavior)
+static int madvise_update_vma(struct vm_area_struct *vma,
+ struct vm_area_struct **prev, unsigned long start,
+ unsigned long end, unsigned long new_flags)
{
struct mm_struct * mm = vma->vm_mm;
- int error = 0;
pgoff_t pgoff;
- unsigned long new_flags = vma->vm_flags;
-
- switch (behavior) {
- case MADV_NORMAL:
- new_flags = new_flags & ~VM_RAND_READ & ~VM_SEQ_READ;
- break;
- case MADV_SEQUENTIAL:
- new_flags = (new_flags & ~VM_RAND_READ) | VM_SEQ_READ;
- break;
- case MADV_RANDOM:
- new_flags = (new_flags & ~VM_SEQ_READ) | VM_RAND_READ;
- break;
- case MADV_DONTFORK:
- new_flags |= VM_DONTCOPY;
- break;
- case MADV_DOFORK:
- if (vma->vm_flags & VM_IO) {
- error = -EINVAL;
- goto out;
- }
- new_flags &= ~VM_DONTCOPY;
- break;
- case MADV_DONTDUMP:
- new_flags |= VM_DONTDUMP;
- break;
- case MADV_DODUMP:
- if (new_flags & VM_SPECIAL) {
- error = -EINVAL;
- goto out;
- }
- new_flags &= ~VM_DONTDUMP;
- break;
- case MADV_MERGEABLE:
- case MADV_UNMERGEABLE:
- error = ksm_madvise(vma, start, end, behavior, &new_flags);
- if (error)
- goto out;
- break;
- case MADV_HUGEPAGE:
- case MADV_NOHUGEPAGE:
- error = hugepage_madvise(vma, &new_flags, behavior);
- if (error)
- goto out;
- break;
- }
+ int error;
if (new_flags == vma->vm_flags) {
*prev = vma;
- goto out;
+ return 0;
}
pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT);
@@ -113,13 +68,13 @@ static long madvise_behavior(struct vm_area_struct * vma,
if (start != vma->vm_start) {
error = split_vma(mm, vma, start, 1);
if (error)
- goto out;
+ return error;
}
if (end != vma->vm_end) {
error = split_vma(mm, vma, end, 0);
if (error)
- goto out;
+ return error;
}
success:
@@ -128,10 +83,7 @@ success:
*/
vma->vm_flags = new_flags;
-out:
- if (error == -ENOMEM)
- error = -EAGAIN;
- return error;
+ return 0;
}
#ifdef CONFIG_SWAP
@@ -337,6 +289,77 @@ static long madvise_remove(struct vm_area_struct *vma,
return error;
}
+/*
+ * Apply an madvise behavior to a region of a vma. madvise_update_vma
+ * will handle splitting a vm area into separate areas, each area with its own
+ * behavior.
+ */
+static int madvise_vma_behavior(struct vm_area_struct *vma,
+ struct vm_area_struct **prev,
+ unsigned long start, unsigned long end,
+ unsigned long behavior)
+{
+ int error = 0;
+ unsigned long new_flags = vma->vm_flags;
+
+ switch (behavior) {
+ case MADV_REMOVE:
+ return madvise_remove(vma, prev, start, end);
+ case MADV_WILLNEED:
+ return madvise_willneed(vma, prev, start, end);
+ case MADV_DONTNEED:
+ return madvise_dontneed(vma, prev, start, end);
+ case MADV_NORMAL:
+ new_flags = new_flags & ~VM_RAND_READ & ~VM_SEQ_READ;
+ break;
+ case MADV_SEQUENTIAL:
+ new_flags = (new_flags & ~VM_RAND_READ) | VM_SEQ_READ;
+ break;
+ case MADV_RANDOM:
+ new_flags = (new_flags & ~VM_SEQ_READ) | VM_RAND_READ;
+ break;
+ case MADV_DONTFORK:
+ new_flags |= VM_DONTCOPY;
+ break;
+ case MADV_DOFORK:
+ if (vma->vm_flags & VM_IO) {
+ error = -EINVAL;
+ goto out;
+ }
+ new_flags &= ~VM_DONTCOPY;
+ break;
+ case MADV_DONTDUMP:
+ new_flags |= VM_DONTDUMP;
+ break;
+ case MADV_DODUMP:
+ if (new_flags & VM_SPECIAL) {
+ error = -EINVAL;
+ goto out;
+ }
+ new_flags &= ~VM_DONTDUMP;
+ break;
+ case MADV_MERGEABLE:
+ case MADV_UNMERGEABLE:
+ error = ksm_madvise(vma, start, end, behavior, &new_flags);
+ if (error)
+ goto out;
+ break;
+ case MADV_HUGEPAGE:
+ case MADV_NOHUGEPAGE:
+ error = hugepage_madvise(vma, &new_flags, behavior);
+ if (error)
+ goto out;
+ break;
+ }
+
+ error = madvise_update_vma(vma, prev, start, end, new_flags);
+
+out:
+ if (error == -ENOMEM)
+ error = -EAGAIN;
+ return error;
+}
+
#ifdef CONFIG_MEMORY_FAILURE
/*
* Error injection support for memory error handling.
@@ -369,22 +392,6 @@ static int madvise_hwpoison(int bhv, unsigned long start, unsigned long end)
}
#endif
-static long
-madvise_vma(struct vm_area_struct *vma, struct vm_area_struct **prev,
- unsigned long start, unsigned long end, int behavior)
-{
- switch (behavior) {
- case MADV_REMOVE:
- return madvise_remove(vma, prev, start, end);
- case MADV_WILLNEED:
- return madvise_willneed(vma, prev, start, end);
- case MADV_DONTNEED:
- return madvise_dontneed(vma, prev, start, end);
- default:
- return madvise_behavior(vma, prev, start, end, behavior);
- }
-}
-
static int
madvise_behavior_valid(int behavior)
{
@@ -415,6 +422,73 @@ madvise_behavior_valid(int behavior)
}
/*
+ * Walk the vmas in range [start,end), and call the visit function on each one.
+ * The visit function will get start and end parameters that cover the overlap
+ * between the current vma and the original range. Any unmapped regions in the
+ * original range will result in this function returning -ENOMEM while still
+ * calling the visit function on all of the existing vmas in the range.
+ * Must be called with the mmap_sem held for reading or writing.
+ */
+static
+int madvise_walk_vmas(unsigned long start, unsigned long end,
+ unsigned long arg,
+ int (*visit)(struct vm_area_struct *vma,
+ struct vm_area_struct **prev, unsigned long start,
+ unsigned long end, unsigned long arg))
+{
+ struct vm_area_struct *vma;
+ struct vm_area_struct *prev;
+ unsigned long tmp;
+ int unmapped_error = 0;
+
+ /*
+ * If the interval [start,end) covers some unmapped address
+ * ranges, just ignore them, but return -ENOMEM at the end.
+ * - different from the way of handling in mlock etc.
+ */
+ vma = find_vma_prev(current->mm, start, &prev);
+ if (vma && start > vma->vm_start)
+ prev = vma;
+
+ for (;;) {
+ int error;
+
+ /* Still start < end. */
+ if (!vma)
+ return -ENOMEM;
+
+ /* Here start < (end|vma->vm_end). */
+ if (start < vma->vm_start) {
+ unmapped_error = -ENOMEM;
+ start = vma->vm_start;
+ if (start >= end)
+ break;
+ }
+
+ /* Here vma->vm_start <= start < (end|vma->vm_end) */
+ tmp = vma->vm_end;
+ if (end < tmp)
+ tmp = end;
+
+ /* Here vma->vm_start <= start < tmp <= (end|vma->vm_end). */
+ error = visit(vma, &prev, start, tmp, arg);
+ if (error)
+ return error;
+ start = tmp;
+ if (prev && start < prev->vm_end)
+ start = prev->vm_end;
+ if (start >= end)
+ break;
+ if (prev)
+ vma = prev->vm_next;
+ else /* madvise_remove dropped mmap_sem */
+ vma = find_vma(current->mm, start);
+ }
+
+ return unmapped_error;
+}
+
+/*
* The madvise(2) system call.
*
* Applications can use madvise() to advise the kernel how it should
@@ -458,9 +532,7 @@ madvise_behavior_valid(int behavior)
*/
SYSCALL_DEFINE3(madvise, unsigned long, start, size_t, len_in, int, behavior)
{
- unsigned long end, tmp;
- struct vm_area_struct * vma, *prev;
- int unmapped_error = 0;
+ unsigned long end;
int error = -EINVAL;
int write;
size_t len;
@@ -495,52 +567,10 @@ SYSCALL_DEFINE3(madvise, unsigned long, start, size_t, len_in, int, behavior)
else
down_read(¤t->mm->mmap_sem);
- /*
- * If the interval [start,end) covers some unmapped address
- * ranges, just ignore them, but return -ENOMEM at the end.
- * - different from the way of handling in mlock etc.
- */
- vma = find_vma_prev(current->mm, start, &prev);
- if (vma && start > vma->vm_start)
- prev = vma;
-
blk_start_plug(&plug);
- for (;;) {
- /* Still start < end. */
- error = -ENOMEM;
- if (!vma)
- goto out;
-
- /* Here start < (end|vma->vm_end). */
- if (start < vma->vm_start) {
- unmapped_error = -ENOMEM;
- start = vma->vm_start;
- if (start >= end)
- goto out;
- }
-
- /* Here vma->vm_start <= start < (end|vma->vm_end) */
- tmp = vma->vm_end;
- if (end < tmp)
- tmp = end;
-
- /* Here vma->vm_start <= start < tmp <= (end|vma->vm_end). */
- error = madvise_vma(vma, &prev, start, tmp, behavior);
- if (error)
- goto out;
- start = tmp;
- if (prev && start < prev->vm_end)
- start = prev->vm_end;
- error = unmapped_error;
- if (start >= end)
- goto out;
- if (prev)
- vma = prev->vm_next;
- else /* madvise_remove dropped mmap_sem */
- vma = find_vma(current->mm, start);
- }
-out:
+ error = madvise_walk_vmas(start, end, behavior, madvise_vma_behavior);
blk_finish_plug(&plug);
+
if (write)
up_write(¤t->mm->mmap_sem);
else
--
1.8.4
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCHv2 2/2] mm: add a field to store names for private anonymous memory
[not found] ` <1380658901-11666-2-git-send-email-ccross@android.com>
@ 2013-10-02 0:37 ` KOSAKI Motohiro
0 siblings, 0 replies; 2+ messages in thread
From: KOSAKI Motohiro @ 2013-10-02 0:37 UTC (permalink / raw
To: ccross
Cc: linux-kernel, penberg, dave.hansen, peterz, mingo, oleg, ebiederm,
jan.glauber, rob, akpm, gorcunov, rientjes, dave, keescook, xemul,
liwanp, walken, mgorman, riel, jiang.liu, khlebnikov, paulmck,
dhowells, arnd, davej, holt, rafael.j.wysocki, shli, sasha.levin,
kosaki.motohiro, hughd, hannes, a.p.zijlstra, linux-doc, linux-mm
> +static void seq_print_vma_name(struct seq_file *m, struct vm_area_struct *vma)
> +{
> + const char __user *name = vma_get_anon_name(vma);
> + struct mm_struct *mm = vma->vm_mm;
> +
> + unsigned long page_start_vaddr;
> + unsigned long page_offset;
> + unsigned long num_pages;
> + unsigned long max_len = NAME_MAX;
> + int i;
> +
> + page_start_vaddr = (unsigned long)name & PAGE_MASK;
> + page_offset = (unsigned long)name - page_start_vaddr;
> + num_pages = DIV_ROUND_UP(page_offset + max_len, PAGE_SIZE);
> +
> + seq_puts(m, "[anon:");
> +
> + for (i = 0; i < num_pages; i++) {
> + int len;
> + int write_len;
> + const char *kaddr;
> + long pages_pinned;
> + struct page *page;
> +
> + pages_pinned = get_user_pages(current, mm, page_start_vaddr,
> + 1, 0, 0, &page, NULL);
> + if (pages_pinned < 1) {
> + seq_puts(m, "<fault>]");
> + return;
> + }
> +
> + kaddr = (const char *)kmap(page);
> + len = min(max_len, PAGE_SIZE - page_offset);
You can't show the name if the name is placed in end of page.
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2013-10-02 0:37 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-10-01 20:21 [PATCHv2 1/2] mm: rearrange madvise code to allow for reuse Colin Cross
[not found] ` <1380658901-11666-2-git-send-email-ccross@android.com>
2013-10-02 0:37 ` [PATCHv2 2/2] mm: add a field to store names for private anonymous memory KOSAKI Motohiro
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).