diff options
Diffstat (limited to 'mm/mremap.c')
| -rw-r--r-- | mm/mremap.c | 33 |
1 files changed, 25 insertions, 8 deletions
diff --git a/mm/mremap.c b/mm/mremap.c index db7e773d0884..11a8321a90b8 100644 --- a/mm/mremap.c +++ b/mm/mremap.c @@ -1339,6 +1339,13 @@ static int remap_is_valid(struct vma_remap_struct *vrm) (vma->vm_flags & (VM_DONTEXPAND | VM_PFNMAP))) return -EINVAL; + /* + * We permit crossing of boundaries for the range being unmapped due to + * a shrink. + */ + if (vrm->remap_type == MREMAP_SHRINK) + old_len = new_len; + /* We can't remap across vm area boundaries */ if (old_len > vma->vm_end - addr) return -EFAULT; @@ -1443,10 +1450,6 @@ static unsigned long mremap_to(struct vma_remap_struct *vrm) vrm->old_len = vrm->new_len; } - err = remap_is_valid(vrm); - if (err) - return err; - /* MREMAP_DONTUNMAP expands by old_len since old_len == new_len */ if (vrm->flags & MREMAP_DONTUNMAP) { vm_flags_t vm_flags = vrm->vma->vm_flags; @@ -1635,10 +1638,6 @@ static unsigned long expand_vma(struct vma_remap_struct *vrm) { unsigned long err; - err = remap_is_valid(vrm); - if (err) - return err; - /* * [addr, old_len) spans precisely to the end of the VMA, so try to * expand it in-place. @@ -1705,6 +1704,21 @@ static unsigned long mremap_at(struct vma_remap_struct *vrm) return -EINVAL; } +/* + * Will this operation result in the VMA being expanded or moved and thus need + * to map a new portion of virtual address space? + */ +static bool vrm_will_map_new(struct vma_remap_struct *vrm) +{ + if (vrm->remap_type == MREMAP_EXPAND) + return true; + + if (vrm_implies_new_addr(vrm)) + return true; + + return false; +} + static int check_prep_vma(struct vma_remap_struct *vrm) { struct vm_area_struct *vma = vrm->vma; @@ -1726,6 +1740,9 @@ static int check_prep_vma(struct vma_remap_struct *vrm) if (!vrm_implies_new_addr(vrm)) vrm->new_addr = vrm->addr; + if (vrm_will_map_new(vrm)) + return remap_is_valid(vrm); + return 0; } |