.middle = vma_, \
.next = (vma_) ? NULL : (map_)->next, \
.state = VMA_MERGE_START, \
- .merge_flags = VMG_FLAG_DEFAULT, \
}
static inline bool is_mergeable_vma(struct vma_merge_struct *vmg, bool merge_next)
memset(vp, 0, sizeof(struct vma_prepare));
vp->vma = vma;
vp->anon_vma = vma->anon_vma;
- vp->remove = remove;
- vp->remove2 = remove2;
+ vp->remove = remove ? remove : remove2;
+ vp->remove2 = remove ? remove2 : NULL;
vp->adj_next = next;
if (!vp->anon_vma && next)
vp->anon_vma = next->anon_vma;
vp->file = vma->vm_file;
if (vp->file)
vp->mapping = vma->vm_file->f_mapping;
-
}
/*
}
#endif /* CONFIG_DEBUG_VM_MAPLE_TREE */
-/* Actually perform the VMA merge operation. */
-static int commit_merge(struct vma_merge_struct *vmg,
- struct vm_area_struct *adjust,
- struct vm_area_struct *remove,
- struct vm_area_struct *remove2,
- long adj_start,
- bool expanded)
+/*
+ * Actually perform the VMA merge operation.
+ *
+ * On success, returns the merged VMA. Otherwise returns NULL.
+ */
+static struct vm_area_struct *commit_merge(struct vma_merge_struct *vmg,
+ long adj_start)
{
struct vma_prepare vp;
+ struct vm_area_struct *remove = NULL;
+ struct vm_area_struct *remove2 = NULL;
+ struct vm_area_struct *adjust = NULL;
+ /*
+ * In all cases but that of merge right, shrink next, we write
+ * vmg->target to the maple tree and return this as the merged VMA.
+ */
+ bool merge_target = adj_start >= 0;
+
+ if (vmg->__remove_middle)
+ remove = vmg->middle;
+ if (vmg->__remove_next)
+ remove2 = vmg->next;
+
+ if (adj_start > 0)
+ adjust = vmg->middle;
+ else if (adj_start < 0)
+ adjust = vmg->next;
init_multi_vma_prep(&vp, vmg->target, adjust, remove, remove2);
VM_WARN_ON(vp.anon_vma && adjust && adjust->anon_vma &&
vp.anon_vma != adjust->anon_vma);
- if (expanded) {
+ if (merge_target) {
/* Note: vma iterator must be pointing to 'start'. */
vma_iter_config(vmg->vmi, vmg->start, vmg->end);
} else {
}
if (vma_iter_prealloc(vmg->vmi, vmg->target))
- return -ENOMEM;
+ return NULL;
vma_prepare(&vp);
vma_adjust_trans_huge(vmg->target, vmg->start, vmg->end, adj_start);
vma_set_range(vmg->target, vmg->start, vmg->end, vmg->pgoff);
- if (expanded)
+ if (merge_target)
vma_iter_store(vmg->vmi, vmg->target);
if (adj_start) {
adjust->vm_start += adj_start;
adjust->vm_pgoff += PHYS_PFN(adj_start);
- if (adj_start < 0) {
- WARN_ON(expanded);
+
+ if (!merge_target)
vma_iter_store(vmg->vmi, adjust);
- }
}
vma_complete(&vp, vmg->vmi, vmg->target->vm_mm);
- return 0;
+ return merge_target ? vmg->target : vmg->next;
}
/* We can only remove VMAs when merging if they do not have a close hook. */
struct vm_area_struct *prev = vmg->prev;
struct vm_area_struct *next, *res;
struct vm_area_struct *anon_dup = NULL;
- struct vm_area_struct *adjust = NULL;
unsigned long start = vmg->start;
unsigned long end = vmg->end;
bool left_side = middle && start == middle->vm_start;
bool right_side = middle && end == middle->vm_end;
int err = 0;
long adj_start = 0;
- bool merge_will_delete_middle, merge_will_delete_next;
bool merge_left, merge_right, merge_both;
- bool expanded;
mmap_assert_write_locked(vmg->mm);
VM_WARN_ON_VMG(!middle, vmg); /* We are modifying a VMA, so caller must specify. */
merge_both = merge_left && merge_right;
/* If we span the entire VMA, a merge implies it will be deleted. */
- merge_will_delete_middle = left_side && right_side;
+ vmg->__remove_middle = left_side && right_side;
/*
* If we need to remove middle in its entirety but are unable to do so,
* we have no sensible recourse but to abort the merge.
*/
- if (merge_will_delete_middle && !can_merge_remove_vma(middle))
+ if (vmg->__remove_middle && !can_merge_remove_vma(middle))
return NULL;
/*
* If we merge both VMAs, then next is also deleted. This implies
* merge_will_delete_vma also.
*/
- merge_will_delete_next = merge_both;
+ vmg->__remove_next = merge_both;
/*
* If we cannot delete next, then we can reduce the operation to merging
* prev and middle (thereby deleting middle).
*/
- if (merge_will_delete_next && !can_merge_remove_vma(next)) {
- merge_will_delete_next = false;
+ if (vmg->__remove_next && !can_merge_remove_vma(next)) {
+ vmg->__remove_next = false;
merge_right = false;
merge_both = false;
}
vmg->start = prev->vm_start;
vmg->pgoff = prev->vm_pgoff;
- if (!merge_will_delete_middle) {
- adjust = middle;
+ /*
+ * We both expand prev and shrink middle.
+ */
+ if (!vmg->__remove_middle)
adj_start = vmg->end - middle->vm_start;
- }
err = dup_anon_vma(prev, middle, &anon_dup);
} else { /* merge_right */
/* If we are offset into a VMA, then prev must be middle. */
VM_WARN_ON_VMG(vmg->start > middle->vm_start && prev && middle != prev, vmg);
- if (merge_will_delete_middle) {
+ if (vmg->__remove_middle) {
vmg->target = next;
vmg->end = next->vm_end;
vmg->pgoff = next->vm_pgoff - pglen;
vmg->end = start;
vmg->pgoff = middle->vm_pgoff;
- adjust = next;
adj_start = -(middle->vm_end - start);
}
if (err)
goto abort;
- /*
- * In nearly all cases, we expand vmg->middle. There is one exception -
- * merge_right where we partially span the VMA. In this case we shrink
- * the end of vmg->middle and adjust the start of vmg->next accordingly.
- */
- expanded = !merge_right || merge_will_delete_middle;
-
- if (commit_merge(vmg, adjust,
- merge_will_delete_middle ? middle : NULL,
- merge_will_delete_next ? next : NULL,
- adj_start, expanded)) {
+ res = commit_merge(vmg, adj_start);
+ if (!res) {
if (anon_dup)
unlink_anon_vmas(anon_dup);
return NULL;
}
- res = merge_left ? prev : next;
khugepaged_enter_vma(res, vmg->flags);
-
vmg->state = VMA_MERGE_SUCCESS;
return res;
struct vm_area_struct *next = vmg->next;
unsigned long end = vmg->end;
bool can_merge_left, can_merge_right;
- bool just_expand = vmg->merge_flags & VMG_FLAG_JUST_EXPAND;
mmap_assert_write_locked(vmg->mm);
VM_WARN_ON_VMG(vmg->middle, vmg);
return NULL;
can_merge_left = can_vma_merge_left(vmg);
- can_merge_right = !just_expand && can_vma_merge_right(vmg, can_merge_left);
+ can_merge_right = !vmg->just_expand && can_vma_merge_right(vmg, can_merge_left);
/* If we can merge with the next VMA, adjust vmg accordingly. */
if (can_merge_right) {
vmg->end = end;
/* In expand-only case we are already positioned at prev. */
- if (!just_expand) {
+ if (!vmg->just_expand) {
/* Equivalent to going to the previous range. */
vma_prev(vmg->vmi);
}
middle->vm_end > vmg->end, vmg);
vmg->target = middle;
- if (commit_merge(vmg, NULL, remove_next ? next : NULL, NULL, 0, true))
+ if (remove_next)
+ vmg->__remove_next = true;
+
+ if (!commit_merge(vmg, 0))
goto nomem;
return 0;
vmg.prev = vma;
/* vmi is positioned at prev, which this mode expects. */
- vmg.merge_flags = VMG_FLAG_JUST_EXPAND;
+ vmg.just_expand = true;
if (vma_merge_new_range(&vmg))
goto out;
VMA_MERGE_SUCCESS,
};
-enum vma_merge_flags {
- VMG_FLAG_DEFAULT = 0,
- /*
- * If we can expand, simply do so. We know there is nothing to merge to
- * the right. Does not reset state upon failure to merge. The VMA
- * iterator is assumed to be positioned at the previous VMA, rather than
- * at the gap.
- */
- VMG_FLAG_JUST_EXPAND = 1 << 0,
-};
-
/*
* Describes a VMA merge operation and is threaded throughout it.
*
struct mempolicy *policy;
struct vm_userfaultfd_ctx uffd_ctx;
struct anon_vma_name *anon_name;
- enum vma_merge_flags merge_flags;
enum vma_merge_state state;
+
+ /* Flags which callers can use to modify merge behaviour: */
+
+ /*
+ * If we can expand, simply do so. We know there is nothing to merge to
+ * the right. Does not reset state upon failure to merge. The VMA
+ * iterator is assumed to be positioned at the previous VMA, rather than
+ * at the gap.
+ */
+ bool just_expand :1;
+
+ /* Internal flags set during merge process: */
+
+ /*
+ * Internal flag used during the merge operation to indicate we will
+ * remove vmg->middle.
+ */
+ bool __remove_middle :1;
+ /*
+ * Internal flag used during the merge operationr to indicate we will
+ * remove vmg->next.
+ */
+ bool __remove_next :1;
+
};
static inline bool vmg_nomem(struct vma_merge_struct *vmg)
.flags = flags_, \
.pgoff = pgoff_, \
.state = VMA_MERGE_START, \
- .merge_flags = VMG_FLAG_DEFAULT, \
}
#define VMG_VMA_STATE(name, vmi_, prev_, vma_, start_, end_) \
.uffd_ctx = vma_->vm_userfaultfd_ctx, \
.anon_name = anon_vma_name(vma_), \
.state = VMA_MERGE_START, \
- .merge_flags = VMG_FLAG_DEFAULT, \
}
#ifdef CONFIG_DEBUG_VM_MAPLE_TREE