Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0a27a14a authored by Nick Piggin's avatar Nick Piggin Committed by Linus Torvalds
Browse files

mm: madvise avoid exclusive mmap_sem



Avoid down_write of the mmap_sem in madvise when we can help it.

Acked-by: default avatarHugh Dickins <hugh@veritas.com>
Signed-off-by: default avatarNick Piggin <npiggin@suse.de>
Cc: Rik van Riel <riel@redhat.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent b4169525
Loading
Loading
Loading
Loading
+29 −4
Original line number Diff line number Diff line
@@ -11,6 +11,24 @@
#include <linux/mempolicy.h>
#include <linux/hugetlb.h>

/*
 * Any behaviour which results in changes to the vma->vm_flags needs to
 * take mmap_sem for writing. Others, which simply traverse vmas, need
 * to only take it for reading.
 */
static int madvise_need_mmap_write(int behavior)
{
	switch (behavior) {
	case MADV_REMOVE:
	case MADV_WILLNEED:
	case MADV_DONTNEED:
		return 0;
	default:
		/* be safe, default to 1. list exceptions explicitly */
		return 1;
	}
}

/*
 * We can potentially split a vm area into separate
 * areas, each area with its own behavior.
@@ -183,9 +201,9 @@ static long madvise_remove(struct vm_area_struct *vma,
			+ ((loff_t)vma->vm_pgoff << PAGE_SHIFT);

	/* vmtruncate_range needs to take i_mutex and i_alloc_sem */
	up_write(&current->mm->mmap_sem);
	up_read(&current->mm->mmap_sem);
	error = vmtruncate_range(mapping->host, offset, endoff);
	down_write(&current->mm->mmap_sem);
	down_read(&current->mm->mmap_sem);
	return error;
}

@@ -270,7 +288,10 @@ asmlinkage long sys_madvise(unsigned long start, size_t len_in, int behavior)
	int error = -EINVAL;
	size_t len;

	if (madvise_need_mmap_write(behavior))
		down_write(&current->mm->mmap_sem);
	else
		down_read(&current->mm->mmap_sem);

	if (start & ~PAGE_MASK)
		goto out;
@@ -332,6 +353,10 @@ asmlinkage long sys_madvise(unsigned long start, size_t len_in, int behavior)
			vma = find_vma(current->mm, start);
	}
out:
	if (madvise_need_mmap_write(behavior))
		up_write(&current->mm->mmap_sem);
	else
		up_read(&current->mm->mmap_sem);

	return error;
}