Merge ac1e9acc5a
("mm: rearrange madvise code to allow for reuse") into android-mainline
Steps on the way to 5.17-rc1 Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Change-Id: I1666e37e70068565094e721374a7b63d9562579c
This commit is contained in:
commit
f64e8814b0
@ -165,17 +165,15 @@ do_page_fault(unsigned long address, unsigned long mmcsr,
|
||||
BUG();
|
||||
}
|
||||
|
||||
if (flags & FAULT_FLAG_ALLOW_RETRY) {
|
||||
if (fault & VM_FAULT_RETRY) {
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
if (fault & VM_FAULT_RETRY) {
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
|
||||
/* No need to mmap_read_unlock(mm) as we would
|
||||
* have already released it in __lock_page_or_retry
|
||||
* in mm/filemap.c.
|
||||
*/
|
||||
/* No need to mmap_read_unlock(mm) as we would
|
||||
* have already released it in __lock_page_or_retry
|
||||
* in mm/filemap.c.
|
||||
*/
|
||||
|
||||
goto retry;
|
||||
}
|
||||
goto retry;
|
||||
}
|
||||
|
||||
mmap_read_unlock(mm);
|
||||
|
@ -149,8 +149,7 @@ void do_page_fault(unsigned long address, struct pt_regs *regs)
|
||||
/*
|
||||
* Fault retry nuances, mmap_lock already relinquished by core mm
|
||||
*/
|
||||
if (unlikely((fault & VM_FAULT_RETRY) &&
|
||||
(flags & FAULT_FLAG_ALLOW_RETRY))) {
|
||||
if (unlikely(fault & VM_FAULT_RETRY)) {
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
goto retry;
|
||||
}
|
||||
|
@ -322,7 +322,7 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(fault & VM_FAULT_ERROR) && flags & FAULT_FLAG_ALLOW_RETRY) {
|
||||
if (!(fault & VM_FAULT_ERROR)) {
|
||||
if (fault & VM_FAULT_RETRY) {
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
goto retry;
|
||||
|
@ -608,10 +608,8 @@ static int __kprobes do_page_fault(unsigned long far, unsigned int esr,
|
||||
}
|
||||
|
||||
if (fault & VM_FAULT_RETRY) {
|
||||
if (mm_flags & FAULT_FLAG_ALLOW_RETRY) {
|
||||
mm_flags |= FAULT_FLAG_TRIED;
|
||||
goto retry;
|
||||
}
|
||||
mm_flags |= FAULT_FLAG_TRIED;
|
||||
goto retry;
|
||||
}
|
||||
mmap_read_unlock(mm);
|
||||
|
||||
|
@ -98,11 +98,9 @@ void do_page_fault(unsigned long address, long cause, struct pt_regs *regs)
|
||||
|
||||
/* The most common case -- we are done. */
|
||||
if (likely(!(fault & VM_FAULT_ERROR))) {
|
||||
if (flags & FAULT_FLAG_ALLOW_RETRY) {
|
||||
if (fault & VM_FAULT_RETRY) {
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
goto retry;
|
||||
}
|
||||
if (fault & VM_FAULT_RETRY) {
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
mmap_read_unlock(mm);
|
||||
|
@ -156,17 +156,15 @@ ia64_do_page_fault (unsigned long address, unsigned long isr, struct pt_regs *re
|
||||
BUG();
|
||||
}
|
||||
|
||||
if (flags & FAULT_FLAG_ALLOW_RETRY) {
|
||||
if (fault & VM_FAULT_RETRY) {
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
if (fault & VM_FAULT_RETRY) {
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
|
||||
/* No need to mmap_read_unlock(mm) as we would
|
||||
* have already released it in __lock_page_or_retry
|
||||
* in mm/filemap.c.
|
||||
*/
|
||||
/* No need to mmap_read_unlock(mm) as we would
|
||||
* have already released it in __lock_page_or_retry
|
||||
* in mm/filemap.c.
|
||||
*/
|
||||
|
||||
goto retry;
|
||||
}
|
||||
goto retry;
|
||||
}
|
||||
|
||||
mmap_read_unlock(mm);
|
||||
|
@ -153,18 +153,16 @@ int do_page_fault(struct pt_regs *regs, unsigned long address,
|
||||
BUG();
|
||||
}
|
||||
|
||||
if (flags & FAULT_FLAG_ALLOW_RETRY) {
|
||||
if (fault & VM_FAULT_RETRY) {
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
if (fault & VM_FAULT_RETRY) {
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
|
||||
/*
|
||||
* No need to mmap_read_unlock(mm) as we would
|
||||
* have already released it in __lock_page_or_retry
|
||||
* in mm/filemap.c.
|
||||
*/
|
||||
/*
|
||||
* No need to mmap_read_unlock(mm) as we would
|
||||
* have already released it in __lock_page_or_retry
|
||||
* in mm/filemap.c.
|
||||
*/
|
||||
|
||||
goto retry;
|
||||
}
|
||||
goto retry;
|
||||
}
|
||||
|
||||
mmap_read_unlock(mm);
|
||||
|
@ -232,18 +232,16 @@ void do_page_fault(struct pt_regs *regs, unsigned long address,
|
||||
BUG();
|
||||
}
|
||||
|
||||
if (flags & FAULT_FLAG_ALLOW_RETRY) {
|
||||
if (fault & VM_FAULT_RETRY) {
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
if (fault & VM_FAULT_RETRY) {
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
|
||||
/*
|
||||
* No need to mmap_read_unlock(mm) as we would
|
||||
* have already released it in __lock_page_or_retry
|
||||
* in mm/filemap.c.
|
||||
*/
|
||||
/*
|
||||
* No need to mmap_read_unlock(mm) as we would
|
||||
* have already released it in __lock_page_or_retry
|
||||
* in mm/filemap.c.
|
||||
*/
|
||||
|
||||
goto retry;
|
||||
}
|
||||
goto retry;
|
||||
}
|
||||
|
||||
mmap_read_unlock(mm);
|
||||
|
@ -171,18 +171,17 @@ static void __kprobes __do_page_fault(struct pt_regs *regs, unsigned long write,
|
||||
goto do_sigbus;
|
||||
BUG();
|
||||
}
|
||||
if (flags & FAULT_FLAG_ALLOW_RETRY) {
|
||||
if (fault & VM_FAULT_RETRY) {
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
|
||||
/*
|
||||
* No need to mmap_read_unlock(mm) as we would
|
||||
* have already released it in __lock_page_or_retry
|
||||
* in mm/filemap.c.
|
||||
*/
|
||||
if (fault & VM_FAULT_RETRY) {
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
|
||||
goto retry;
|
||||
}
|
||||
/*
|
||||
* No need to mmap_read_unlock(mm) as we would
|
||||
* have already released it in __lock_page_or_retry
|
||||
* in mm/filemap.c.
|
||||
*/
|
||||
|
||||
goto retry;
|
||||
}
|
||||
|
||||
mmap_read_unlock(mm);
|
||||
|
@ -230,16 +230,14 @@ void do_page_fault(unsigned long entry, unsigned long addr,
|
||||
goto bad_area;
|
||||
}
|
||||
|
||||
if (flags & FAULT_FLAG_ALLOW_RETRY) {
|
||||
if (fault & VM_FAULT_RETRY) {
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
if (fault & VM_FAULT_RETRY) {
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
|
||||
/* No need to mmap_read_unlock(mm) as we would
|
||||
* have already released it in __lock_page_or_retry
|
||||
* in mm/filemap.c.
|
||||
*/
|
||||
goto retry;
|
||||
}
|
||||
/* No need to mmap_read_unlock(mm) as we would
|
||||
* have already released it in __lock_page_or_retry
|
||||
* in mm/filemap.c.
|
||||
*/
|
||||
goto retry;
|
||||
}
|
||||
|
||||
mmap_read_unlock(mm);
|
||||
|
@ -149,18 +149,16 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long cause,
|
||||
BUG();
|
||||
}
|
||||
|
||||
if (flags & FAULT_FLAG_ALLOW_RETRY) {
|
||||
if (fault & VM_FAULT_RETRY) {
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
if (fault & VM_FAULT_RETRY) {
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
|
||||
/*
|
||||
* No need to mmap_read_unlock(mm) as we would
|
||||
* have already released it in __lock_page_or_retry
|
||||
* in mm/filemap.c.
|
||||
*/
|
||||
/*
|
||||
* No need to mmap_read_unlock(mm) as we would
|
||||
* have already released it in __lock_page_or_retry
|
||||
* in mm/filemap.c.
|
||||
*/
|
||||
|
||||
goto retry;
|
||||
}
|
||||
goto retry;
|
||||
}
|
||||
|
||||
mmap_read_unlock(mm);
|
||||
|
@ -177,18 +177,16 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long address,
|
||||
BUG();
|
||||
}
|
||||
|
||||
if (flags & FAULT_FLAG_ALLOW_RETRY) {
|
||||
/*RGD modeled on Cris */
|
||||
if (fault & VM_FAULT_RETRY) {
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
/*RGD modeled on Cris */
|
||||
if (fault & VM_FAULT_RETRY) {
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
|
||||
/* No need to mmap_read_unlock(mm) as we would
|
||||
* have already released it in __lock_page_or_retry
|
||||
* in mm/filemap.c.
|
||||
*/
|
||||
/* No need to mmap_read_unlock(mm) as we would
|
||||
* have already released it in __lock_page_or_retry
|
||||
* in mm/filemap.c.
|
||||
*/
|
||||
|
||||
goto retry;
|
||||
}
|
||||
goto retry;
|
||||
}
|
||||
|
||||
mmap_read_unlock(mm);
|
||||
|
@ -324,16 +324,14 @@ void do_page_fault(struct pt_regs *regs, unsigned long code,
|
||||
goto bad_area;
|
||||
BUG();
|
||||
}
|
||||
if (flags & FAULT_FLAG_ALLOW_RETRY) {
|
||||
if (fault & VM_FAULT_RETRY) {
|
||||
/*
|
||||
* No need to mmap_read_unlock(mm) as we would
|
||||
* have already released it in __lock_page_or_retry
|
||||
* in mm/filemap.c.
|
||||
*/
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
goto retry;
|
||||
}
|
||||
if (fault & VM_FAULT_RETRY) {
|
||||
/*
|
||||
* No need to mmap_read_unlock(mm) as we would
|
||||
* have already released it in __lock_page_or_retry
|
||||
* in mm/filemap.c.
|
||||
*/
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
goto retry;
|
||||
}
|
||||
mmap_read_unlock(mm);
|
||||
return;
|
||||
|
@ -517,10 +517,8 @@ static int ___do_page_fault(struct pt_regs *regs, unsigned long address,
|
||||
* case.
|
||||
*/
|
||||
if (unlikely(fault & VM_FAULT_RETRY)) {
|
||||
if (flags & FAULT_FLAG_ALLOW_RETRY) {
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
goto retry;
|
||||
}
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
mmap_read_unlock(current->mm);
|
||||
|
@ -330,7 +330,7 @@ asmlinkage void do_page_fault(struct pt_regs *regs)
|
||||
if (fault_signal_pending(fault, regs))
|
||||
return;
|
||||
|
||||
if (unlikely((fault & VM_FAULT_RETRY) && (flags & FAULT_FLAG_ALLOW_RETRY))) {
|
||||
if (unlikely(fault & VM_FAULT_RETRY)) {
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
|
||||
/*
|
||||
|
@ -452,21 +452,21 @@ static inline vm_fault_t do_exception(struct pt_regs *regs, int access)
|
||||
if (unlikely(fault & VM_FAULT_ERROR))
|
||||
goto out_up;
|
||||
|
||||
if (flags & FAULT_FLAG_ALLOW_RETRY) {
|
||||
if (fault & VM_FAULT_RETRY) {
|
||||
if (IS_ENABLED(CONFIG_PGSTE) && gmap &&
|
||||
(flags & FAULT_FLAG_RETRY_NOWAIT)) {
|
||||
/* FAULT_FLAG_RETRY_NOWAIT has been set,
|
||||
* mmap_lock has not been released */
|
||||
current->thread.gmap_pfault = 1;
|
||||
fault = VM_FAULT_PFAULT;
|
||||
goto out_up;
|
||||
}
|
||||
flags &= ~FAULT_FLAG_RETRY_NOWAIT;
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
mmap_read_lock(mm);
|
||||
goto retry;
|
||||
if (fault & VM_FAULT_RETRY) {
|
||||
if (IS_ENABLED(CONFIG_PGSTE) && gmap &&
|
||||
(flags & FAULT_FLAG_RETRY_NOWAIT)) {
|
||||
/*
|
||||
* FAULT_FLAG_RETRY_NOWAIT has been set, mmap_lock has
|
||||
* not been released
|
||||
*/
|
||||
current->thread.gmap_pfault = 1;
|
||||
fault = VM_FAULT_PFAULT;
|
||||
goto out_up;
|
||||
}
|
||||
flags &= ~FAULT_FLAG_RETRY_NOWAIT;
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
mmap_read_lock(mm);
|
||||
goto retry;
|
||||
}
|
||||
if (IS_ENABLED(CONFIG_PGSTE) && gmap) {
|
||||
address = __gmap_link(gmap, current->thread.gmap_addr,
|
||||
|
@ -485,17 +485,15 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs,
|
||||
if (mm_fault_error(regs, error_code, address, fault))
|
||||
return;
|
||||
|
||||
if (flags & FAULT_FLAG_ALLOW_RETRY) {
|
||||
if (fault & VM_FAULT_RETRY) {
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
if (fault & VM_FAULT_RETRY) {
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
|
||||
/*
|
||||
* No need to mmap_read_unlock(mm) as we would
|
||||
* have already released it in __lock_page_or_retry
|
||||
* in mm/filemap.c.
|
||||
*/
|
||||
goto retry;
|
||||
}
|
||||
/*
|
||||
* No need to mmap_read_unlock(mm) as we would
|
||||
* have already released it in __lock_page_or_retry
|
||||
* in mm/filemap.c.
|
||||
*/
|
||||
goto retry;
|
||||
}
|
||||
|
||||
mmap_read_unlock(mm);
|
||||
|
@ -200,17 +200,15 @@ asmlinkage void do_sparc_fault(struct pt_regs *regs, int text_fault, int write,
|
||||
BUG();
|
||||
}
|
||||
|
||||
if (flags & FAULT_FLAG_ALLOW_RETRY) {
|
||||
if (fault & VM_FAULT_RETRY) {
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
if (fault & VM_FAULT_RETRY) {
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
|
||||
/* No need to mmap_read_unlock(mm) as we would
|
||||
* have already released it in __lock_page_or_retry
|
||||
* in mm/filemap.c.
|
||||
*/
|
||||
/* No need to mmap_read_unlock(mm) as we would
|
||||
* have already released it in __lock_page_or_retry
|
||||
* in mm/filemap.c.
|
||||
*/
|
||||
|
||||
goto retry;
|
||||
}
|
||||
goto retry;
|
||||
}
|
||||
|
||||
mmap_read_unlock(mm);
|
||||
|
@ -437,17 +437,15 @@ asmlinkage void __kprobes do_sparc64_fault(struct pt_regs *regs)
|
||||
BUG();
|
||||
}
|
||||
|
||||
if (flags & FAULT_FLAG_ALLOW_RETRY) {
|
||||
if (fault & VM_FAULT_RETRY) {
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
if (fault & VM_FAULT_RETRY) {
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
|
||||
/* No need to mmap_read_unlock(mm) as we would
|
||||
* have already released it in __lock_page_or_retry
|
||||
* in mm/filemap.c.
|
||||
*/
|
||||
/* No need to mmap_read_unlock(mm) as we would
|
||||
* have already released it in __lock_page_or_retry
|
||||
* in mm/filemap.c.
|
||||
*/
|
||||
|
||||
goto retry;
|
||||
}
|
||||
goto retry;
|
||||
}
|
||||
mmap_read_unlock(mm);
|
||||
|
||||
|
@ -87,12 +87,10 @@ int handle_page_fault(unsigned long address, unsigned long ip,
|
||||
}
|
||||
BUG();
|
||||
}
|
||||
if (flags & FAULT_FLAG_ALLOW_RETRY) {
|
||||
if (fault & VM_FAULT_RETRY) {
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
if (fault & VM_FAULT_RETRY) {
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
|
||||
goto retry;
|
||||
}
|
||||
goto retry;
|
||||
}
|
||||
|
||||
pmd = pmd_off(mm, address);
|
||||
|
@ -1413,8 +1413,7 @@ void do_user_addr_fault(struct pt_regs *regs,
|
||||
* and if there is a fatal signal pending there is no guarantee
|
||||
* that we made any progress. Handle this case first.
|
||||
*/
|
||||
if (unlikely((fault & VM_FAULT_RETRY) &&
|
||||
(flags & FAULT_FLAG_ALLOW_RETRY))) {
|
||||
if (unlikely(fault & VM_FAULT_RETRY)) {
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
goto retry;
|
||||
}
|
||||
|
@ -127,17 +127,16 @@ void do_page_fault(struct pt_regs *regs)
|
||||
goto do_sigbus;
|
||||
BUG();
|
||||
}
|
||||
if (flags & FAULT_FLAG_ALLOW_RETRY) {
|
||||
if (fault & VM_FAULT_RETRY) {
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
|
||||
/* No need to mmap_read_unlock(mm) as we would
|
||||
* have already released it in __lock_page_or_retry
|
||||
* in mm/filemap.c.
|
||||
*/
|
||||
if (fault & VM_FAULT_RETRY) {
|
||||
flags |= FAULT_FLAG_TRIED;
|
||||
|
||||
goto retry;
|
||||
}
|
||||
/* No need to mmap_read_unlock(mm) as we would
|
||||
* have already released it in __lock_page_or_retry
|
||||
* in mm/filemap.c.
|
||||
*/
|
||||
|
||||
goto retry;
|
||||
}
|
||||
|
||||
mmap_read_unlock(mm);
|
||||
|
338
mm/madvise.c
338
mm/madvise.c
@ -63,76 +63,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 region 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;
|
||||
int error;
|
||||
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_WIPEONFORK:
|
||||
/* MADV_WIPEONFORK is only supported on anonymous memory. */
|
||||
if (vma->vm_file || vma->vm_flags & VM_SHARED) {
|
||||
error = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
new_flags |= VM_WIPEONFORK;
|
||||
break;
|
||||
case MADV_KEEPONFORK:
|
||||
new_flags &= ~VM_WIPEONFORK;
|
||||
break;
|
||||
case MADV_DONTDUMP:
|
||||
new_flags |= VM_DONTDUMP;
|
||||
break;
|
||||
case MADV_DODUMP:
|
||||
if (!is_vm_hugetlb_page(vma) && 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_convert_errno;
|
||||
break;
|
||||
case MADV_HUGEPAGE:
|
||||
case MADV_NOHUGEPAGE:
|
||||
error = hugepage_madvise(vma, &new_flags, behavior);
|
||||
if (error)
|
||||
goto out_convert_errno;
|
||||
break;
|
||||
}
|
||||
|
||||
if (new_flags == vma->vm_flags) {
|
||||
*prev = vma;
|
||||
goto out;
|
||||
return 0;
|
||||
}
|
||||
|
||||
pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT);
|
||||
@ -147,23 +91,19 @@ static long madvise_behavior(struct vm_area_struct *vma,
|
||||
*prev = vma;
|
||||
|
||||
if (start != vma->vm_start) {
|
||||
if (unlikely(mm->map_count >= sysctl_max_map_count)) {
|
||||
error = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
if (unlikely(mm->map_count >= sysctl_max_map_count))
|
||||
return -ENOMEM;
|
||||
error = __split_vma(mm, vma, start, 1);
|
||||
if (error)
|
||||
goto out_convert_errno;
|
||||
return error;
|
||||
}
|
||||
|
||||
if (end != vma->vm_end) {
|
||||
if (unlikely(mm->map_count >= sysctl_max_map_count)) {
|
||||
error = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
if (unlikely(mm->map_count >= sysctl_max_map_count))
|
||||
return -ENOMEM;
|
||||
error = __split_vma(mm, vma, end, 0);
|
||||
if (error)
|
||||
goto out_convert_errno;
|
||||
return error;
|
||||
}
|
||||
|
||||
success:
|
||||
@ -172,15 +112,7 @@ static long madvise_behavior(struct vm_area_struct *vma,
|
||||
*/
|
||||
vma->vm_flags = new_flags;
|
||||
|
||||
out_convert_errno:
|
||||
/*
|
||||
* madvise() returns EAGAIN if kernel resources, such as
|
||||
* slab, are temporarily unavailable.
|
||||
*/
|
||||
if (error == -ENOMEM)
|
||||
error = -EAGAIN;
|
||||
out:
|
||||
return error;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SWAP
|
||||
@ -930,6 +862,94 @@ 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;
|
||||
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_COLD:
|
||||
return madvise_cold(vma, prev, start, end);
|
||||
case MADV_PAGEOUT:
|
||||
return madvise_pageout(vma, prev, start, end);
|
||||
case MADV_FREE:
|
||||
case MADV_DONTNEED:
|
||||
return madvise_dontneed_free(vma, prev, start, end, behavior);
|
||||
case MADV_POPULATE_READ:
|
||||
case MADV_POPULATE_WRITE:
|
||||
return madvise_populate(vma, prev, start, end, 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)
|
||||
return -EINVAL;
|
||||
new_flags &= ~VM_DONTCOPY;
|
||||
break;
|
||||
case MADV_WIPEONFORK:
|
||||
/* MADV_WIPEONFORK is only supported on anonymous memory. */
|
||||
if (vma->vm_file || vma->vm_flags & VM_SHARED)
|
||||
return -EINVAL;
|
||||
new_flags |= VM_WIPEONFORK;
|
||||
break;
|
||||
case MADV_KEEPONFORK:
|
||||
new_flags &= ~VM_WIPEONFORK;
|
||||
break;
|
||||
case MADV_DONTDUMP:
|
||||
new_flags |= VM_DONTDUMP;
|
||||
break;
|
||||
case MADV_DODUMP:
|
||||
if (!is_vm_hugetlb_page(vma) && new_flags & VM_SPECIAL)
|
||||
return -EINVAL;
|
||||
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:
|
||||
/*
|
||||
* madvise() returns EAGAIN if kernel resources, such as
|
||||
* slab, are temporarily unavailable.
|
||||
*/
|
||||
if (error == -ENOMEM)
|
||||
error = -EAGAIN;
|
||||
return error;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MEMORY_FAILURE
|
||||
/*
|
||||
* Error injection support for memory error handling.
|
||||
@ -978,30 +998,6 @@ static int madvise_inject_error(int behavior,
|
||||
}
|
||||
#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_COLD:
|
||||
return madvise_cold(vma, prev, start, end);
|
||||
case MADV_PAGEOUT:
|
||||
return madvise_pageout(vma, prev, start, end);
|
||||
case MADV_FREE:
|
||||
case MADV_DONTNEED:
|
||||
return madvise_dontneed_free(vma, prev, start, end, behavior);
|
||||
case MADV_POPULATE_READ:
|
||||
case MADV_POPULATE_WRITE:
|
||||
return madvise_populate(vma, prev, start, end, behavior);
|
||||
default:
|
||||
return madvise_behavior(vma, prev, start, end, behavior);
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
madvise_behavior_valid(int behavior)
|
||||
{
|
||||
@ -1055,6 +1051,73 @@ process_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_lock held for reading or writing.
|
||||
*/
|
||||
static
|
||||
int madvise_walk_vmas(struct mm_struct *mm, 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(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_lock */
|
||||
vma = find_vma(mm, start);
|
||||
}
|
||||
|
||||
return unmapped_error;
|
||||
}
|
||||
|
||||
/*
|
||||
* The madvise(2) system call.
|
||||
*
|
||||
@ -1127,10 +1190,8 @@ process_madvise_behavior_valid(int behavior)
|
||||
*/
|
||||
int do_madvise(struct mm_struct *mm, unsigned long start, size_t len_in, int behavior)
|
||||
{
|
||||
unsigned long end, tmp;
|
||||
struct vm_area_struct *vma, *prev;
|
||||
int unmapped_error = 0;
|
||||
int error = -EINVAL;
|
||||
unsigned long end;
|
||||
int error;
|
||||
int write;
|
||||
size_t len;
|
||||
struct blk_plug plug;
|
||||
@ -1138,23 +1199,22 @@ int do_madvise(struct mm_struct *mm, unsigned long start, size_t len_in, int beh
|
||||
start = untagged_addr(start);
|
||||
|
||||
if (!madvise_behavior_valid(behavior))
|
||||
return error;
|
||||
return -EINVAL;
|
||||
|
||||
if (!PAGE_ALIGNED(start))
|
||||
return error;
|
||||
return -EINVAL;
|
||||
len = PAGE_ALIGN(len_in);
|
||||
|
||||
/* Check to see whether len was rounded up from small -ve to zero */
|
||||
if (len_in && !len)
|
||||
return error;
|
||||
return -EINVAL;
|
||||
|
||||
end = start + len;
|
||||
if (end < start)
|
||||
return error;
|
||||
return -EINVAL;
|
||||
|
||||
error = 0;
|
||||
if (end == start)
|
||||
return error;
|
||||
return 0;
|
||||
|
||||
#ifdef CONFIG_MEMORY_FAILURE
|
||||
if (behavior == MADV_HWPOISON || behavior == MADV_SOFT_OFFLINE)
|
||||
@ -1169,51 +1229,9 @@ int do_madvise(struct mm_struct *mm, unsigned long start, size_t len_in, int beh
|
||||
mmap_read_lock(mm);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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(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_lock */
|
||||
vma = find_vma(mm, start);
|
||||
}
|
||||
out:
|
||||
error = madvise_walk_vmas(mm, start, end, behavior,
|
||||
madvise_vma_behavior);
|
||||
blk_finish_plug(&plug);
|
||||
if (write)
|
||||
mmap_write_unlock(mm);
|
||||
|
Loading…
Reference in New Issue
Block a user